aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2006-01-12 12:08:06 +0100
committerJames Bottomley <jejb@mulgrave.(none)>2006-01-12 12:03:50 -0600
commit11668bb673c41ec169a85d0b52c538a1c11d29e1 (patch)
tree423c30e22eea305d5c059c6463bdd226c1734bf4 /drivers
parentba62cd2d01e401faa5d5a25fa8e990d0b1a1996a (diff)
[SCSI] aic79xx: Sequencer update
Update sequencer code to Adaptec version 2.0.12-6.3.9. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.h12
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.reg60
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq241
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c665
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_inline.h6
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c62
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg.h_shipped646
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped507
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped1250
9 files changed, 1911 insertions, 1538 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h
index ac3d07a2a28..2cfdbef447d 100644
--- a/drivers/scsi/aic7xxx/aic79xx.h
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -75,8 +75,7 @@ struct scb_platform_data;
#define INITIATOR_WILDCARD (~0)
#define SCB_LIST_NULL 0xFF00
#define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL))
-#define QOUTFIFO_ENTRY_VALID 0x8000
-#define QOUTFIFO_ENTRY_VALID_LE (ahd_htole16(0x8000))
+#define QOUTFIFO_ENTRY_VALID 0x80
#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
#define SCSIID_TARGET(ahd, scsiid) \
@@ -1053,6 +1052,13 @@ typedef uint8_t ahd_mode_state;
typedef void ahd_callback_t (void *);
+struct ahd_completion
+{
+ uint16_t tag;
+ uint8_t sg_status;
+ uint8_t valid_tag;
+};
+
struct ahd_softc {
bus_space_tag_t tags[2];
bus_space_handle_t bshs[2];
@@ -1142,11 +1148,11 @@ struct ahd_softc {
struct seeprom_config *seep_config;
/* Command Queues */
+ struct ahd_completion *qoutfifo;
uint16_t qoutfifonext;
uint16_t qoutfifonext_valid_tag;
uint16_t qinfifonext;
uint16_t qinfifo[AHD_SCB_MAX];
- uint16_t *qoutfifo;
/*
* Our qfreeze count. The sequencer compares
diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg
index cca58edc864..3a3204703b1 100644
--- a/drivers/scsi/aic7xxx/aic79xx.reg
+++ b/drivers/scsi/aic7xxx/aic79xx.reg
@@ -39,7 +39,7 @@
*
* $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#76 $"
/*
* This file is processed by the aic7xxx_asm utility for use in assembling
@@ -65,13 +65,6 @@ VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $"
mvi MODE_PTR, MK_MODE(src, dst); \
}
-#define TOGGLE_DFF_MODE \
- if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
- call toggle_dff_mode_work_around; \
- } else { \
- xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); \
- }
-
#define RESTORE_MODE(mode) \
if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
mov mode call set_mode_work_around; \
@@ -1199,7 +1192,7 @@ register TARGPCISTAT {
/*
* LQ Packet In
- * The last LQ Packet received
+ * The last LQ Packet recieved
*/
register LQIN {
address 0x020
@@ -3542,10 +3535,34 @@ scratch_ram {
COMPLETE_DMA_SCB_HEAD {
size 2
}
- /* Counting semaphore to prevent new select-outs */
+ /*
+ * tail of list of SCBs that have
+ * completed but need to be uploaded
+ * to the host prior to being completed.
+ */
+ COMPLETE_DMA_SCB_TAIL {
+ size 2
+ }
+ /*
+ * head of list of SCBs that have
+ * been uploaded to the host, but cannot
+ * be completed until the QFREEZE is in
+ * full effect (i.e. no selections pending).
+ */
+ COMPLETE_ON_QFREEZE_HEAD {
+ size 2
+ }
+ /*
+ * Counting semaphore to prevent new select-outs
+ * The queue is frozen so long as the sequencer
+ * and kernel freeze counts differ.
+ */
QFREEZE_COUNT {
size 2
}
+ KERNEL_QFREEZE_COUNT {
+ size 2
+ }
/*
* Mode to restore on legacy idle loop exit.
*/
@@ -3625,6 +3642,17 @@ scratch_ram {
size 1
}
/*
+ * Kernel and sequencer offsets into the queue of
+ * incoming target mode command descriptors. The
+ * queue is full when the KERNEL_TQINPOS == TQINPOS.
+ */
+ KERNEL_TQINPOS {
+ size 1
+ }
+ TQINPOS {
+ size 1
+ }
+ /*
* Base address of our shared data with the kernel driver in host
* memory. This includes the qoutfifo and target mode
* incoming command queue.
@@ -3639,17 +3667,6 @@ scratch_ram {
QOUTFIFO_NEXT_ADDR {
size 4
}
- /*
- * Kernel and sequencer offsets into the queue of
- * incoming target mode command descriptors. The
- * queue is full when the KERNEL_TQINPOS == TQINPOS.
- */
- KERNEL_TQINPOS {
- size 1
- }
- TQINPOS {
- size 1
- }
ARG_1 {
size 1
mask SEND_MSG 0x80
@@ -3951,6 +3968,7 @@ const SG_PREFETCH_ADDR_MASK download
const SG_SIZEOF download
const PKT_OVERRUN_BUFOFFSET download
const SCB_TRANSFER_SIZE download
+const CACHELINE_MASK download
/*
* BIOS SCB offsets
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
index 65339bc1ca9..bef1f9d369b 100644
--- a/drivers/scsi/aic7xxx/aic79xx.seq
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -40,7 +40,7 @@
* $FreeBSD$
*/
-VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#99 $"
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#119 $"
PATCH_ARG_LIST = "struct ahd_softc *ahd"
PREFIX = "ahd_"
@@ -68,13 +68,47 @@ no_error_set:
}
SET_MODE(M_SCSI, M_SCSI)
test SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus;
- test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz idle_loop_checkbus;
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jz check_waiting_list;
+ /*
+ * If the kernel has caught up with us, thaw the queue.
+ */
+ mov A, KERNEL_QFREEZE_COUNT;
+ cmp QFREEZE_COUNT, A jne check_frozen_completions;
+ mov A, KERNEL_QFREEZE_COUNT[1];
+ cmp QFREEZE_COUNT[1], A jne check_frozen_completions;
+ and SEQ_FLAGS2, ~SELECTOUT_QFROZEN;
+ jmp check_waiting_list;
+check_frozen_completions:
+ test SSTAT0, SELDO|SELINGO jnz idle_loop_checkbus;
+BEGIN_CRITICAL;
+ /*
+ * If we have completions stalled waiting for the qfreeze
+ * to take effect, move them over to the complete_scb list
+ * now that no selections are pending.
+ */
+ cmp COMPLETE_ON_QFREEZE_HEAD[1],SCB_LIST_NULL je idle_loop_checkbus;
+ /*
+ * Find the end of the qfreeze list. The first element has
+ * to be treated specially.
+ */
+ bmov SCBPTR, COMPLETE_ON_QFREEZE_HEAD, 2;
+ cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je join_lists;
+ /*
+ * Now the normal loop.
+ */
+ bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
+ cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . - 1;
+join_lists:
+ bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+ bmov COMPLETE_SCB_HEAD, COMPLETE_ON_QFREEZE_HEAD, 2;
+ mvi COMPLETE_ON_QFREEZE_HEAD[1], SCB_LIST_NULL;
+ jmp idle_loop_checkbus;
+check_waiting_list:
cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
/*
* ENSELO is cleared by a SELDO, so we must test for SELDO
* one last time.
*/
-BEGIN_CRITICAL;
test SSTAT0, SELDO jnz select_out;
END_CRITICAL;
call start_selection;
@@ -90,6 +124,13 @@ idle_loop_check_nonpackreq:
test SSTAT2, NONPACKREQ jz . + 2;
call unexpected_nonpkt_phase_find_ctxt;
if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+ /*
+ * On Rev A. hardware, the busy LED is only
+ * turned on automaically during selections
+ * and re-selections. Make the LED status
+ * more useful by forcing it to be on so
+ * long as one of our data FIFOs is active.
+ */
and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
@@ -101,9 +142,9 @@ idle_loop_check_nonpackreq:
call idle_loop_cchan;
jmp idle_loop;
-BEGIN_CRITICAL;
idle_loop_gsfifo:
SET_MODE(M_SCSI, M_SCSI)
+BEGIN_CRITICAL;
idle_loop_gsfifo_in_scsi_mode:
test LQISTAT2, LQIGSAVAIL jz return;
/*
@@ -152,11 +193,15 @@ END_CRITICAL;
idle_loop_service_fifos:
SET_MODE(M_DFF0, M_DFF0)
+BEGIN_CRITICAL;
test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
call longjmp;
+END_CRITICAL;
idle_loop_next_fifo:
SET_MODE(M_DFF1, M_DFF1)
+BEGIN_CRITICAL;
test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
+END_CRITICAL;
return:
ret;
@@ -170,7 +215,6 @@ BEGIN_CRITICAL;
test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
test CCSCBCTL, CCSCBDONE jz return;
-END_CRITICAL;
/* FALLTHROUGH */
scbdma_tohost_done:
test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
@@ -180,26 +224,18 @@ scbdma_tohost_done:
* bad SCSI status (currently only for underruns), we
* queue the SCB for normal completion. Otherwise, we
* wait until any select-out activity has halted, and
- * then notify the host so that the transaction can be
- * dealt with.
+ * then queue the completion.
*/
- test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
and CCSCBCTL, ~(CCARREN|CCSCBEN);
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . + 2;
+ mvi COMPLETE_DMA_SCB_TAIL[1], SCB_LIST_NULL;
+ test SCB_SCSI_STATUS, 0xff jz scbdma_queue_completion;
+ bmov SCB_NEXT_COMPLETE, COMPLETE_ON_QFREEZE_HEAD, 2;
+ bmov COMPLETE_ON_QFREEZE_HEAD, SCBPTR, 2 ret;
+scbdma_queue_completion:
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
-scbdma_notify_host:
- SET_MODE(M_SCSI, M_SCSI)
- test SCSISEQ0, ENSELO jnz return;
- test SSTAT0, (SELDO|SELINGO) jnz return;
- SET_MODE(M_CCHAN, M_CCHAN)
- /*
- * Remove SCB and notify host.
- */
- and CCSCBCTL, ~(CCARREN|CCSCBEN);
- bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
- SET_SEQINTCODE(BAD_SCB_STATUS)
- ret;
fill_qoutfifo_dmadone:
and CCSCBCTL, ~(CCARREN|CCSCBEN);
call qoutfifo_updated;
@@ -208,6 +244,7 @@ fill_qoutfifo_dmadone:
test QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4;
xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
+END_CRITICAL;
qoutfifo_updated:
/*
@@ -324,14 +361,15 @@ fill_qoutfifo:
* Keep track of the SCBs we are dmaing just
* in case the DMA fails or is aborted.
*/
- mov A, QOUTFIFO_ENTRY_VALID_TAG;
bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
mvi CCSCBCTL, CCSCBRESET;
bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+ mov A, QOUTFIFO_NEXT_ADDR;
bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
fill_qoutfifo_loop:
- mov CCSCBRAM, SCBPTR;
- or CCSCBRAM, A, SCBPTR[1];
+ bmov CCSCBRAM, SCBPTR, 2;
+ mov CCSCBRAM, SCB_SGPTR[0];
+ mov CCSCBRAM, QOUTFIFO_ENTRY_VALID_TAG;
mov NONE, SDSCB_QOFF;
inc INT_COALESCING_CMDCOUNT;
add CMDS_PENDING, -1;
@@ -339,6 +377,18 @@ fill_qoutfifo_loop:
cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
test QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done;
+ /*
+ * Don't cross an ADB or Cachline boundary when DMA'ing
+ * completion entries. In PCI mode, at least in 32/33
+ * configurations, the SCB DMA engine may lose its place
+ * in the data-stream should the target force a retry on
+ * something other than an 8byte aligned boundary. In
+ * PCI-X mode, we do this to avoid split transactions since
+ * many chipsets seem to be unable to format proper split
+ * completions to continue the data transfer.
+ */
+ add SINDEX, A, CCSCBADDR;
+ test SINDEX, CACHELINE_MASK jz fill_qoutfifo_done;
bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
jmp fill_qoutfifo_loop;
fill_qoutfifo_done:
@@ -354,7 +404,6 @@ dma_complete_scb:
bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
bmov SCBHADDR, SCB_BUSADDR, 4;
mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb;
-END_CRITICAL;
/*
* Either post or fetch an SCB from host memory. The caller
@@ -371,9 +420,19 @@ dma_scb:
mvi SCBHCNT, SCB_TRANSFER_SIZE;
mov CCSCBCTL, SINDEX ret;
-BEGIN_CRITICAL;
setjmp:
- bmov LONGJMP_ADDR, STACK, 2 ret;
+ /*
+ * At least on the A, a return in the same
+ * instruction as the bmov results in a return
+ * to the caller, not to the new address at the
+ * top of the stack. Since we want the latter
+ * (we use setjmp to register a handler from an
+ * interrupt context but not invoke that handler
+ * until we return to our idle loop), use a
+ * separate ret instruction.
+ */
+ bmov LONGJMP_ADDR, STACK, 2;
+ ret;
setjmp_inline:
bmov LONGJMP_ADDR, STACK, 2;
longjmp:
@@ -392,11 +451,6 @@ set_mode_work_around:
mvi SEQINTCTL, INTVEC1DSL;
mov MODE_PTR, SINDEX;
clr SEQINTCTL ret;
-
-toggle_dff_mode_work_around:
- mvi SEQINTCTL, INTVEC1DSL;
- xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
- clr SEQINTCTL ret;
}
@@ -490,6 +544,21 @@ allocate_fifo1:
SET_SRC_MODE M_SCSI;
SET_DST_MODE M_SCSI;
select_in:
+ if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+ /*
+ * On Rev A. hardware, the busy LED is only
+ * turned on automaically during selections
+ * and re-selections. Make the LED status
+ * more useful by forcing it to be on from
+ * the point of selection until our idle
+ * loop determines that neither of our FIFOs
+ * are busy. This handles the non-packetized
+ * case nicely as we will not return to the
+ * idle loop until the busfree at the end of
+ * each transaction.
+ */
+ or SBLKCTL, DIAGLEDEN|DIAGLEDON;
+ }
if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
/*
* Test to ensure that the bus has not
@@ -528,6 +597,21 @@ SET_SRC_MODE M_SCSI;
SET_DST_MODE M_SCSI;
select_out:
BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+ /*
+ * On Rev A. hardware, the busy LED is only
+ * turned on automaically during selections
+ * and re-selections. Make the LED status
+ * more useful by forcing it to be on from
+ * the point of re-selection until our idle
+ * loop determines that neither of our FIFOs
+ * are busy. This handles the non-packetized
+ * case nicely as we will not return to the
+ * idle loop until the busfree at the end of
+ * each transaction.
+ */
+ or SBLKCTL, DIAGLEDEN|DIAGLEDON;
+ }
/* Clear out all SCBs that have been successfully sent. */
if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
/*
@@ -1000,15 +1084,9 @@ not_found_ITloop:
/*
* We received a "command complete" message. Put the SCB on the complete
* queue and trigger a completion interrupt via the idle loop. Before doing
- * so, check to see if there
- * is a residual or the status byte is something other than STATUS_GOOD (0).
- * In either of these conditions, we upload the SCB back to the host so it can
- * process this information. In the case of a non zero status byte, we
- * additionally interrupt the kernel driver synchronously, allowing it to
- * decide if sense should be retrieved. If the kernel driver wishes to request
- * sense, it will fill the kernel SCB with a request sense command, requeue
- * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting
- * RETURN_1 to SEND_SENSE.
+ * so, check to see if there is a residual or the status byte is something
+ * other than STATUS_GOOD (0). In either of these conditions, we upload the
+ * SCB back to the host so it can process this information.
*/
mesgin_complete:
@@ -1053,6 +1131,7 @@ complete_nomsg:
call queue_scb_completion;
jmp await_busfree;
+BEGIN_CRITICAL;
freeze_queue:
/* Cancel any pending select-out. */
test SSTAT0, SELDO|SELINGO jnz . + 2;
@@ -1063,6 +1142,7 @@ freeze_queue:
adc QFREEZE_COUNT[1], A;
or SEQ_FLAGS2, SELECTOUT_QFROZEN;
mov A, ACCUM_SAVE ret;
+END_CRITICAL;
/*
* Complete the current FIFO's SCB if data for this same
@@ -1085,8 +1165,10 @@ queue_scb_completion:
test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
complete:
+BEGIN_CRITICAL;
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+END_CRITICAL;
bad_status:
cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
call freeze_queue;
@@ -1097,9 +1179,18 @@ upload_scb:
* it on the host.
*/
bmov SCB_TAG, SCBPTR, 2;
- bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2;
+BEGIN_CRITICAL;
+ or SCB_SGPTR, SG_STATUS_VALID;
+ mvi SCB_NEXT_COMPLETE[1], SCB_LIST_NULL;
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne add_dma_scb_tail;
bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
- or SCB_SGPTR, SG_STATUS_VALID ret;
+ bmov COMPLETE_DMA_SCB_TAIL, SCBPTR, 2 ret;
+add_dma_scb_tail:
+ bmov REG0, SCBPTR, 2;
+ bmov SCBPTR, COMPLETE_DMA_SCB_TAIL, 2;
+ bmov SCB_NEXT_COMPLETE, REG0, 2;
+ bmov COMPLETE_DMA_SCB_TAIL, REG0, 2 ret;
+END_CRITICAL;
/*
* Is it a disconnect message? Set a flag in the SCB to remind us
@@ -1146,8 +1237,18 @@ SET_DST_MODE M_DFF1;
await_busfree_clrchn:
mvi DFFSXFRCTL, CLRCHN;
await_busfree_not_m_dff:
- call clear_target_state;
+ /* clear target specific flags */
+ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT;
test SSTAT1,REQINIT|BUSFREE jz .;
+ /*
+ * We only set BUSFREE status once either a new
+ * phase has been detected or we are really
+ * BUSFREE. This allows the driver to know
+ * that we are active on the bus even though
+ * no identified transaction exists should a
+ * timeout occur while awaiting busfree.
+ */
+ mvi LASTPHASE, P_BUSFREE;
test SSTAT1, BUSFREE jnz idle_loop;
SET_SEQINTCODE(MISSED_BUSFREE)
@@ -1202,11 +1303,6 @@ msgin_rdptrs_get_fifo:
call allocate_fifo;
jmp mesgin_done;
-clear_target_state:
- mvi LASTPHASE, P_BUSFREE;
- /* clear target specific flags */
- mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
-
phase_lock:
if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) {
/*
@@ -1298,6 +1394,47 @@ service_fifo:
test CCSGCTL, CCSGENACK jnz return;
/*
+ * Should the other FIFO get the S/G cache first? If
+ * both FIFOs have been allocated since we last checked
+ * any FIFO, it is important that we service a FIFO
+ * that is not actively on the bus first. This guarantees
+ * that a FIFO will be freed to handle snapshot requests for
+ * any FIFO that is still on the bus. Chips with RTI do not
+ * perform snapshots, so don't bother with this test there.
+ */
+ if ((ahd->features & AHD_RTI) == 0) {
+ /*
+ * If we're not still receiving SCSI data,
+ * it is safe to allocate the S/G cache to
+ * this FIFO.
+ */
+ test DFCNTRL, SCSIEN jz idle_sgfetch_start;
+
+ /*
+ * Switch to the other FIFO. Non-RTI chips
+ * also have the "set mode" bug, so we must
+ * disable interrupts during the switch.
+ */
+ mvi SEQINTCTL, INTVEC1DSL;
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+
+ /*
+ * If the other FIFO needs loading, then it
+ * must not have claimed the S/G cache yet
+ * (SG_CACHE_AVAIL would have been cleared in
+ * the orginal FIFO mode and we test this above).
+ * Return to the idle loop so we can process the
+ * FIFO not currently on the bus first.
+ */
+ test SG_STATE, LOADING_NEEDED jz idle_sgfetch_okay;
+ clr SEQINTCTL ret;
+idle_sgfetch_okay:
+ xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+ clr SEQINTCTL;
+ }
+
+idle_sgfetch_start:
+ /*
* We fetch a "cacheline aligned" and sized amount of data
* so we don't end up referencing a non-existant page.
* Cacheline aligned is in quotes because the kernel will
@@ -1308,7 +1445,7 @@ service_fifo:
mvi SGHCNT, SG_PREFETCH_CNT;
if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) {
/*
- * Need two instruction between "touches" of SGHADDR.
+ * Need two instructions between "touches" of SGHADDR.
*/
nop;
}
@@ -1658,7 +1795,7 @@ export seq_isr:
* savepointer in the current FIFO. We do this so that
* a pending CTXTDONE or SAVEPTR is visible in the active
* FIFO. This status is the only way we can detect if we
- * have lost the race (e.g. host paused us) and our attepts
+ * have lost the race (e.g. host paused us) and our attempts
* to disable the channel occurred after all REQs were
* already seen and acked (REQINIT never comes true).
*/
@@ -1667,7 +1804,7 @@ export seq_isr:
test DFCNTRL, DIRECTION jz interrupt_return;
and DFCNTRL, ~SCSIEN;
snapshot_wait_data_valid:
- test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz snapshot_data_valid;
+ test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz interrupt_return;
test SSTAT1, REQINIT jz snapshot_wait_data_valid;
snapshot_data_valid:
or DFCNTRL, SCSIEN;
@@ -1834,7 +1971,6 @@ pkt_saveptrs_check_status:
dec SCB_FIFO_USE_COUNT;
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret;
-END_CRITICAL;
/*
* LAST_SEG_DONE status has been seen in the current FIFO.
@@ -1843,7 +1979,6 @@ END_CRITICAL;
* Check for overrun and see if we can complete this command.
*/
pkt_last_seg_done:
-BEGIN_CRITICAL;
/*
* Mark transfer as completed.
*/
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 35b51e1979f..db8f5ce99ee 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -37,9 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
- * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#202 $
- *
- * $FreeBSD$
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#247 $
*/
#ifdef __linux__
@@ -332,6 +330,14 @@ ahd_restart(struct ahd_softc *ahd)
ahd_outb(ahd, SCSISEQ1,
ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+ /*
+ * Clear any pending sequencer interrupt. It is no
+ * longer relevant since we're resetting the Program
+ * Counter.
+ */
+ ahd_outb(ahd, CLRINT, CLRSEQINT);
+
ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
ahd_unpause(ahd);
}
@@ -373,13 +379,7 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
saved_modes = ahd_save_modes(ahd);
/*
- * Complete any SCBs that just finished being
- * DMA'ed into the qoutfifo.
- */
- ahd_run_qoutfifo(ahd);
-
- /*
- * Flush the good status FIFO for compelted packetized commands.
+ * Flush the good status FIFO for completed packetized commands.
*/
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
saved_scbptr = ahd_get_scbptr(ahd);
@@ -400,22 +400,33 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
* the host before completing the command.
*/
fifo_mode = 0;
+rescan_fifos:
for (i = 0; i < 2; i++) {
/* Toggle to the other mode. */
fifo_mode ^= 1;
ahd_set_modes(ahd, fifo_mode, fifo_mode);
+
if (ahd_scb_active_in_fifo(ahd, scb) == 0)
continue;
ahd_run_data_fifo(ahd, scb);
/*
- * Clearing this transaction in this FIFO may
- * cause a CFG4DATA for this same transaction
- * to assert in the other FIFO. Make sure we
- * loop one more time and check the other FIFO.
+ * Running this FIFO may cause a CFG4DATA for
+ * this same transaction to assert in the other
+ * FIFO or a new snapshot SAVEPTRS interrupt
+ * in this FIFO. Even running a FIFO may not
+ * clear the transaction if we are still waiting
+ * for data to drain to the host. We must loop
+ * until the transaction is not active in either
+ * FIFO just to be sure. Reset our loop counter
+ * so we will visit both FIFOs again before
+ * declaring this transaction finished. We
+ * also delay a bit so that status has a chance
+ * to change before we look at this FIFO again.
*/
- i = 0;
+ ahd_delay(200);
+ goto rescan_fifos;
}
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_set_scbptr(ahd, scbid);
@@ -428,19 +439,28 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
/*
* The transfer completed with a residual.
* Place this SCB on the complete DMA list
- * so that we Update our in-core copy of the
+ * so that we update our in-core copy of the
* SCB before completing the command.
*/
ahd_outb(ahd, SCB_SCSI_STATUS, 0);
ahd_outb(ahd, SCB_SGPTR,
ahd_inb_scbram(ahd, SCB_SGPTR)
| SG_STATUS_VALID);
- ahd_outw(ahd, SCB_TAG, SCB_GET_TAG(scb));
+ ahd_outw(ahd, SCB_TAG, scbid);
+ ahd_outw(ahd, SCB_NEXT_COMPLETE, SCB_LIST_NULL);
comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
- ahd_outw(ahd, SCB_NEXT_COMPLETE, comp_head);
- if (SCBID_IS_NULL(comp_head))
- ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD,
- SCB_GET_TAG(scb));
+ if (SCBID_IS_NULL(comp_head)) {
+ ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, scbid);
+ ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
+ } else {
+ u_int tail;
+
+ tail = ahd_inw(ahd, COMPLETE_DMA_SCB_TAIL);
+ ahd_set_scbptr(ahd, tail);
+ ahd_outw(ahd, SCB_NEXT_COMPLETE, scbid);
+ ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
+ ahd_set_scbptr(ahd, scbid);
+ }
} else
ahd_complete_scb(ahd, scb);
}
@@ -464,9 +484,22 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
break;
ahd_delay(200);
}
- if ((ccscbctl & CCSCBDIR) != 0)
+ /*
+ * We leave the sequencer to cleanup in the case of DMA's to
+ * update the qoutfifo. In all other cases (DMA's to the
+ * chip or a push of an SCB from the COMPLETE_DMA_SCB list),
+ * we disable the DMA engine so that the sequencer will not
+ * attempt to handle the DMA completion.
+ */
+ if ((ccscbctl & CCSCBDIR) != 0 || (ccscbctl & ARRDONE) != 0)
ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));
+ /*
+ * Complete any SCBs that just finished
+ * being DMA'ed into the qoutfifo.
+ */
+ ahd_run_qoutfifo(ahd);
+
saved_scbptr = ahd_get_scbptr(ahd);
/*
* Manually update/complete any completed SCBs that are waiting to be
@@ -493,6 +526,24 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
scbid = next_scbid;
}
ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+ ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, SCB_LIST_NULL);
+
+ scbid = ahd_inw(ahd, COMPLETE_ON_QFREEZE_HEAD);
+ while (!SCBID_IS_NULL(scbid)) {
+
+ ahd_set_scbptr(ahd, scbid);
+ next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+ scb = ahd_lookup_scb(ahd, scbid);
+ if (scb == NULL) {
+ printf("%s: Warning - Complete Qfrz SCB %d invalid\n",
+ ahd_name(ahd), scbid);
+ continue;
+ }
+
+ ahd_complete_scb(ahd, scb);
+ scbid = next_scbid;
+ }
+ ahd_outw(ahd, COMPLETE_ON_QFREEZE_HEAD, SCB_LIST_NULL);
scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);
while (!SCBID_IS_NULL(scbid)) {
@@ -557,150 +608,146 @@ ahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb)
{
u_int seqintsrc;
- while (1) {
- seqintsrc = ahd_inb(ahd, SEQINTSRC);
- if ((seqintsrc & CFG4DATA) != 0) {
- uint32_t datacnt;
- uint32_t sgptr;
-
- /*
- * Clear full residual flag.
- */
- sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;
- ahd_outb(ahd, SCB_SGPTR, sgptr);
+ seqintsrc = ahd_inb(ahd, SEQINTSRC);
+ if ((seqintsrc & CFG4DATA) != 0) {
+ uint32_t datacnt;
+ uint32_t sgptr;
- /*
- * Load datacnt and address.
- */
- datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);
- if ((datacnt & AHD_DMA_LAST_SEG) != 0) {
- sgptr |= LAST_SEG;
- ahd_outb(ahd, SG_STATE, 0);
- } else
- ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
- ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));
- ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);
- ahd_outb(ahd, SG_CACHE_PRE, sgptr);
- ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+ /*
+ * Clear full residual flag.
+ */
+ sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;
+ ahd_outb(ahd, SCB_SGPTR, sgptr);
- /*
- * Initialize Residual Fields.
- */
- ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);
- ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);
+ /*
+ * Load datacnt and address.
+ */
+ datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);
+ if ((datacnt & AHD_DMA_LAST_SEG) != 0) {
+ sgptr |= LAST_SEG;
+ ahd_outb(ahd, SG_STATE, 0);
+ } else
+ ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
+ ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));
+ ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);
+ ahd_outb(ahd, SG_CACHE_PRE, sgptr);
+ ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
- /*
- * Mark the SCB as having a FIFO in use.
- */
- ahd_outb(ahd, SCB_FIFO_USE_COUNT,
- ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);
+ /*
+ * Initialize Residual Fields.
+ */
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);
- /*
- * Install a "fake" handler for this FIFO.
- */
- ahd_outw(ahd, LONGJMP_ADDR, 0);
+ /*
+ * Mark the SCB as having a FIFO in use.
+ */
+ ahd_outb(ahd, SCB_FIFO_USE_COUNT,
+ ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);
- /*
- * Notify the hardware that we have satisfied
- * this sequencer interrupt.
- */
- ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);
- } else if ((seqintsrc & SAVEPTRS) != 0) {
- uint32_t sgptr;
- uint32_t resid;
+ /*
+ * Install a "fake" handler for this FIFO.
+ */
+ ahd_outw(ahd, LONGJMP_ADDR, 0);
- if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {
- /*
- * Snapshot Save Pointers. Clear
- * the snapshot and continue.
- */
- ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
- continue;
- }
+ /*
+ * Notify the hardware that we have satisfied
+ * this sequencer interrupt.
+ */
+ ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);
+ } else if ((seqintsrc & SAVEPTRS) != 0) {
+ uint32_t sgptr;
+ uint32_t resid;
+ if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {
/*
- * Disable S/G fetch so the DMA engine
- * is available to future users.
+ * Snapshot Save Pointers. All that
+ * is necessary to clear the snapshot
+ * is a CLRCHN.
*/
- if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
- ahd_outb(ahd, CCSGCTL, 0);
- ahd_outb(ahd, SG_STATE, 0);
+ goto clrchn;
+ }
- /*
- * Flush the data FIFO. Strickly only
- * necessary for Rev A parts.
- */
- ahd_outb(ahd, DFCNTRL,
- ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);
+ /*
+ * Disable S/G fetch so the DMA engine
+ * is available to future users.
+ */
+ if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+ ahd_outb(ahd, CCSGCTL, 0);
+ ahd_outb(ahd, SG_STATE, 0);
- /*
- * Calculate residual.
- */
- sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
- resid = ahd_inl(ahd, SHCNT);
- resid |=
- ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;
- ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);
- if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {
- /*
- * Must back up to the correct S/G element.
- * Typically this just means resetting our
- * low byte to the offset in the SG_CACHE,
- * but if we wrapped, we have to correct
- * the other bytes of the sgptr too.
- */
- if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0
- && (sgptr & 0x80) == 0)
- sgptr -= 0x100;
- sgptr &= ~0xFF;
- sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)
- & SG_ADDR_MASK;
- ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
- ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);
- } else if ((resid & AHD_SG_LEN_MASK) == 0) {
- ahd_outb(ahd, SCB_RESIDUAL_SGPTR,
- sgptr | SG_LIST_NULL);
- }
- /*
- * Save Pointers.
- */
- ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));
- ahd_outl(ahd, SCB_DATACNT, resid);
- ahd_outl(ahd, SCB_SGPTR, sgptr);
- ahd_outb(ahd, CLRSEQINTSRC, CLRSAVEPTRS);
- ahd_outb(ahd, SEQIMODE,
- ahd_inb(ahd, SEQIMODE) | ENSAVEPTRS);
- /*
- * If the data is to the SCSI bus, we are
- * done, otherwise wait for FIFOEMP.
- */
- if ((ahd_inb(ahd, DFCNTRL) & DIRECTION) != 0)
- break;
- } else if ((ahd_inb(ahd, SG_STATE) & LOADING_NEEDED) != 0) {
- uint32_t sgptr;
- uint64_t data_addr;
- uint32_t data_len;
- u_int dfcntrl;
+ /*
+ * Flush the data FIFO. Strickly only
+ * necessary for Rev A parts.
+ */
+ ahd_outb(ahd, DFCNTRL, ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);
+ /*
+ * Calculate residual.
+ */
+ sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+ resid = ahd_inl(ahd, SHCNT);
+ resid |= ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;
+ ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);
+ if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {
/*
- * Disable S/G fetch so the DMA engine
- * is available to future users.
+ * Must back up to the correct S/G element.
+ * Typically this just means resetting our
+ * low byte to the offset in the SG_CACHE,
+ * but if we wrapped, we have to correct
+ * the other bytes of the sgptr too.
*/
- if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) {
- ahd_outb(ahd, CCSGCTL, 0);
- ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
- }
+ if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0
+ && (sgptr & 0x80) == 0)
+ sgptr -= 0x100;
+ sgptr &= ~0xFF;
+ sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)
+ & SG_ADDR_MASK;
+ ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+ ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);
+ } else if ((resid & AHD_SG_LEN_MASK) == 0) {
+ ahd_outb(ahd, SCB_RESIDUAL_SGPTR,
+ sgptr | SG_LIST_NULL);
+ }
+ /*
+ * Save Pointers.
+ */
+ ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));
+ ahd_outl(ahd, SCB_DATACNT, resid);
+ ahd_outl(ahd, SCB_SG