summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx/aic79xx.seq
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/aic7xxx/aic79xx.seq
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/aic7xxx/aic79xx.seq')
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq2058
1 files changed, 2058 insertions, 0 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644
index 000000000000..65339bc1ca99
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -0,0 +1,2058 @@
+/*
+ * Adaptec U320 device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * 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, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#99 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+PREFIX = "ahd_"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+restart:
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ test SEQINTCODE, 0xFF jz idle_loop;
+ SET_SEQINTCODE(NO_SEQINT)
+}
+
+idle_loop:
+
+ if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+ /*
+ * Convert ERROR status into a sequencer
+ * interrupt to handle the case of an
+ * interrupt collision on the hardware
+ * setting of HWERR.
+ */
+ test ERROR, 0xFF jz no_error_set;
+ SET_SEQINTCODE(SAW_HWERR)
+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;
+ 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;
+idle_loop_checkbus:
+BEGIN_CRITICAL;
+ test SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+ test SSTAT0, SELDI jnz select_in;
+ test SCSIPHASE, ~DATA_PHASE_MASK jz idle_loop_check_nonpackreq;
+ test SCSISIGO, ATNO jz idle_loop_check_nonpackreq;
+ call unexpected_nonpkt_phase_find_ctxt;
+idle_loop_check_nonpackreq:
+ test SSTAT2, NONPACKREQ jz . + 2;
+ call unexpected_nonpkt_phase_find_ctxt;
+ if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
+ and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
+ jmp . + 2;
+ or SBLKCTL, DIAGLEDEN|DIAGLEDON;
+ }
+ call idle_loop_gsfifo_in_scsi_mode;
+ call idle_loop_service_fifos;
+ call idle_loop_cchan;
+ jmp idle_loop;
+
+BEGIN_CRITICAL;
+idle_loop_gsfifo:
+ SET_MODE(M_SCSI, M_SCSI)
+idle_loop_gsfifo_in_scsi_mode:
+ test LQISTAT2, LQIGSAVAIL jz return;
+ /*
+ * We have received good status for this transaction. There may
+ * still be data in our FIFOs draining to the host. Complete
+ * the SCB only if all data has transferred to the host.
+ */
+good_status_IU_done:
+ bmov SCBPTR, GSFIFO, 2;
+ clr SCB_SCSI_STATUS;
+ /*
+ * If a command completed before an attempted task management
+ * function completed, notify the host after disabling any
+ * pending select-outs.
+ */
+ test SCB_TASK_MANAGEMENT, 0xFF jz gsfifo_complete_normally;
+ test SSTAT0, SELDO|SELINGO jnz . + 2;
+ and SCSISEQ0, ~ENSELO;
+ SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
+gsfifo_complete_normally:
+ or SCB_CONTROL, STATUS_RCVD;
+
+ /*
+ * Since this status did not consume a FIFO, we have to
+ * be a bit more dilligent in how we check for FIFOs pertaining
+ * to this transaction. There are two states that a FIFO still
+ * transferring data may be in.
+ *
+ * 1) Configured and draining to the host, with a FIFO handler.
+ * 2) Pending cfg4data, fifo not empty.
+ *
+ * Case 1 can be detected by noticing a non-zero FIFO active
+ * count in the SCB. In this case, we allow the routine servicing
+ * the FIFO to complete the SCB.
+ *
+ * Case 2 implies either a pending or yet to occur save data
+ * pointers for this same context in the other FIFO. So, if
+ * we detect case 1, we will properly defer the post of the SCB
+ * and achieve the desired result. The pending cfg4data will
+ * notice that status has been received and complete the SCB.
+ */
+ test SCB_FIFO_USE_COUNT, 0xFF jnz idle_loop_gsfifo_in_scsi_mode;
+ call complete;
+END_CRITICAL;
+ jmp idle_loop_gsfifo_in_scsi_mode;
+
+idle_loop_service_fifos:
+ SET_MODE(M_DFF0, M_DFF0)
+ test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
+ call longjmp;
+idle_loop_next_fifo:
+ SET_MODE(M_DFF1, M_DFF1)
+ test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
+return:
+ ret;
+
+idle_loop_cchan:
+ SET_MODE(M_CCHAN, M_CCHAN)
+ test QOFF_CTLSTA, HS_MAILBOX_ACT jz hs_mailbox_empty;
+ or QOFF_CTLSTA, HS_MAILBOX_ACT;
+ mov LOCAL_HS_MAILBOX, HS_MAILBOX;
+hs_mailbox_empty:
+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;
+ /*
+ * An SCB has been succesfully uploaded to the host.
+ * If the SCB was uploaded for some reason other than
+ * 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.
+ */
+ test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ 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;
+ mvi COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL;
+ bmov QOUTFIFO_NEXT_ADDR, SCBHADDR, 4;
+ 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;
+
+qoutfifo_updated:
+ /*
+ * If there are more commands waiting to be dma'ed
+ * to the host, always coalesce. Otherwise honor the
+ * host's wishes.
+ */
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+ test LOCAL_HS_MAILBOX, ENINT_COALESCE jz issue_cmdcmplt;
+
+ /*
+ * If we have relatively few commands outstanding, don't
+ * bother waiting for another command to complete.
+ */
+ test CMDS_PENDING[1], 0xFF jnz coalesce_by_count;
+ /* Add -1 so that jnc means <= not just < */
+ add A, -1, INT_COALESCING_MINCMDS;
+ add NONE, A, CMDS_PENDING;
+ jnc issue_cmdcmplt;
+
+ /*
+ * If coalescing, only coalesce up to the limit
+ * provided by the host driver.
+ */
+coalesce_by_count:
+ mov A, INT_COALESCING_MAXCMDS;
+ add NONE, A, INT_COALESCING_CMDCOUNT;
+ jc issue_cmdcmplt;
+ /*
+ * If the timer is not currently active,
+ * fire it up.
+ */
+ test INTCTL, SWTMINTMASK jz return;
+ bmov SWTIMER, INT_COALESCING_TIMER, 2;
+ mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+ or INTCTL, SWTMINTEN|SWTIMER_START;
+ and INTCTL, ~SWTMINTMASK ret;
+
+issue_cmdcmplt:
+ mvi INTSTAT, CMDCMPLT;
+ clr INT_COALESCING_CMDCOUNT;
+ or INTCTL, SWTMINTMASK ret;
+
+BEGIN_CRITICAL;
+fetch_new_scb_inprog:
+ test CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+ and CCSCBCTL, ~(CCARREN|CCSCBEN);
+ bmov REG0, SCBPTR, 2;
+ clr A;
+ add CMDS_PENDING, 1;
+ adc CMDS_PENDING[1], A;
+ if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+ /*
+ * "Short Luns" are not placed into outgoing LQ
+ * packets in the correct byte order. Use a full
+ * sized lun field instead and fill it with the
+ * one byte of lun information we support.
+ */
+ mov SCB_PKT_LUN[6], SCB_LUN;
+ }
+ /*
+ * The FIFO use count field is shared with the
+ * tag set by the host so that our SCB dma engine
+ * knows the correct location to store the SCB.
+ * Set it to zero before processing the SCB.
+ */
+ clr SCB_FIFO_USE_COUNT;
+ /* Update the next SCB address to download. */
+ bmov NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
+ mvi SCB_NEXT[1], SCB_LIST_NULL;
+ mvi SCB_NEXT2[1], SCB_LIST_NULL;
+ /* Increment our position in the QINFIFO. */
+ mov NONE, SNSCB_QOFF;
+ /*
+ * SCBs that want to send messages are always
+ * queued independently. This ensures that they
+ * are at the head of the SCB list to select out
+ * to a target and we will see the MK_MESSAGE flag.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz first_new_target_scb;
+ shr SINDEX, 3, SCB_SCSIID;
+ and SINDEX, ~0x1;
+ mvi SINDEX[1], (WAITING_SCB_TAILS >> 8);
+ bmov DINDEX, SINDEX, 2;
+ bmov SCBPTR, SINDIR, 2;
+ bmov DINDIR, REG0, 2;
+ cmp SCBPTR[1], SCB_LIST_NULL je first_new_target_scb;
+ bmov SCB_NEXT, REG0, 2 ret;
+first_new_target_scb:
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2 ret;
+first_new_scb:
+ bmov WAITING_TID_HEAD, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2 ret;
+END_CRITICAL;
+
+scbdma_idle:
+ /*
+ * Give precedence to downloading new SCBs to execute
+ * unless select-outs are currently frozen.
+ */
+ test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2;
+BEGIN_CRITICAL;
+ test QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb;
+ cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb;
+ cmp COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return;
+ /* FALLTHROUGH */
+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;
+ bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
+fill_qoutfifo_loop:
+ mov CCSCBRAM, SCBPTR;
+ or CCSCBRAM, A, SCBPTR[1];
+ mov NONE, SDSCB_QOFF;
+ inc INT_COALESCING_CMDCOUNT;
+ add CMDS_PENDING, -1;
+ adc CMDS_PENDING[1], -1;
+ 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;
+ bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
+ jmp fill_qoutfifo_loop;
+fill_qoutfifo_done:
+ mov SCBHCNT, CCSCBADDR;
+ mvi CCSCBCTL, CCSCBEN|CCSCBRESET;
+ bmov COMPLETE_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+ mvi SCB_NEXT_COMPLETE[1], SCB_LIST_NULL ret;
+
+fetch_new_scb:
+ bmov SCBHADDR, NEXT_QUEUED_SCB_ADDR, 4;
+ mvi CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET jmp dma_scb;
+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
+ * is responsible for polling for transfer completion.
+ *
+ * Prerequisits: Mode == M_CCHAN
+ * SINDEX contains CCSCBCTL flags
+ * SCBHADDR set to Host SCB address
+ * SCBPTR set to SCB src location on "push" operations
+ */
+SET_SRC_MODE M_CCHAN;
+SET_DST_MODE M_CCHAN;
+dma_scb:
+ mvi SCBHCNT, SCB_TRANSFER_SIZE;
+ mov CCSCBCTL, SINDEX ret;
+
+BEGIN_CRITICAL;
+setjmp:
+ bmov LONGJMP_ADDR, STACK, 2 ret;
+setjmp_inline:
+ bmov LONGJMP_ADDR, STACK, 2;
+longjmp:
+ bmov STACK, LONGJMP_ADDR, 2 ret;
+END_CRITICAL;
+
+/*************************** Chip Bug Work Arounds ****************************/
+/*
+ * Must disable interrupts when setting the mode pointer
+ * register as an interrupt occurring mid update will
+ * fail to store the new mode value for restoration on
+ * an iret.
+ */
+if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+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;
+}
+
+
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+set_seqint_work_around:
+ mov SEQINTCODE, SINDEX;
+ mvi SEQINTCODE, NO_SEQINT ret;
+}
+
+/************************ Packetized LongJmp Routines *************************/
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+start_selection:
+BEGIN_CRITICAL;
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * Razor #494
+ * Rev A hardware fails to update LAST/CURR/NEXTSCB
+ * correctly after a packetized selection in several
+ * situations:
+ *
+ * 1) If only one command existed in the queue, the
+ * LAST/CURR/NEXTSCB are unchanged.
+ *
+ * 2) In a non QAS, protocol allowed phase change,
+ * the queue is shifted 1 too far. LASTSCB is
+ * the last SCB that was correctly processed.
+ *
+ * 3) In the QAS case, if the full list of commands
+ * was successfully sent, NEXTSCB is NULL and neither
+ * CURRSCB nor LASTSCB can be trusted. We must
+ * manually walk the list counting MAXCMDCNT elements
+ * to find the last SCB that was sent correctly.
+ *
+ * To simplify the workaround for this bug in SELDO
+ * handling, we initialize LASTSCB prior to enabling
+ * selection so we can rely on it even for case #1 above.
+ */
+ bmov LASTSCB, WAITING_TID_HEAD, 2;
+ }
+ bmov CURRSCB, WAITING_TID_HEAD, 2;
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ shr SELOID, 4, SCB_SCSIID;
+ /*
+ * If we want to send a message to the device, ensure
+ * we are selecting with atn irregardless of our packetized
+ * agreement. Since SPI4 only allows target reset or PPR
+ * messages if this is a packetized connection, the change
+ * to our negotiation table entry for this selection will
+ * be cleared when the message is acted on.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 3;
+ mov NEGOADDR, SELOID;
+ or NEGCONOPTS, ENAUTOATNO;
+ or SCSISEQ0, ENSELO ret;
+END_CRITICAL;
+
+/*
+ * Allocate a FIFO for a non-packetized transaction.
+ * In RevA hardware, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction.
+ */
+allocate_fifo_loop:
+ /*
+ * Do whatever work is required to free a FIFO.
+ */
+ call idle_loop_service_fifos;
+ SET_MODE(M_SCSI, M_SCSI)
+allocate_fifo:
+ if ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0) {
+ and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+ cmp A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+ } else {
+ test DFFSTAT, FIFO1FREE jnz allocate_fifo1;
+ test DFFSTAT, FIFO0FREE jz allocate_fifo_loop;
+ mvi DFFSTAT, B_CURRFIFO_0;
+ SET_MODE(M_DFF0, M_DFF0)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+ }
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+allocate_fifo1:
+ mvi DFFSTAT, CURRFIFO_1;
+ SET_MODE(M_DFF1, M_DFF1)
+ bmov SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+
+/*
+ * We have been reselected as an initiator
+ * or selected as a target.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_in:
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * Test to ensure that the bus has not
+ * already gone free prior to clearing
+ * any stale busfree status. This avoids
+ * a window whereby a busfree just after
+ * a selection could be missed.
+ */
+ test SCSISIGI, BSYI jz . + 2;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ or SXFRCTL0, SPIOEN;
+ and SAVED_SCSIID, SELID_MASK, SELID;
+ and A, OID, IOWNID;
+ or SAVED_SCSIID, A;
+ mvi CLRSINT0, CLRSELDI;
+ jmp ITloop;
+
+/*
+ * We have successfully selected out.
+ *
+ * Clear SELDO.
+ * Dequeue all SCBs sent from the waiting queue
+ * Requeue all SCBs *not* sent to the tail of the waiting queue
+ * Take Razor #494 into account for above.
+ *
+ * In Packetized Mode:
+ * Return to the idle loop. Our interrupt handler will take
+ * care of any incoming L_Qs.
+ *
+ * In Non-Packetize Mode:
+ * Continue to our normal state machine.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+select_out:
+BEGIN_CRITICAL;
+ /* Clear out all SCBs that have been successfully sent. */
+ if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+ /*
+ * For packetized, the LQO manager clears ENSELO on
+ * the assertion of SELDO. If we are non-packetized,
+ * LASTSCB and CURRSCB are accurate.
+ */
+ test SCSISEQ0, ENSELO jnz use_lastscb;
+
+ /*
+ * The update is correct for LQOSTAT1 errors. All
+ * but LQOBUSFREE are handled by kernel interrupts.
+ * If we see LQOBUSFREE, return to the idle loop.
+ * Once we are out of the select_out critical section,
+ * the kernel will cleanup the LQOBUSFREE and we will
+ * eventually restart the selection if appropriate.
+ */
+ test LQOSTAT1, LQOBUSFREE jnz idle_loop;
+
+ /*
+ * On a phase change oustside of packet boundaries,
+ * LASTSCB points to the currently active SCB context
+ * on the bus.
+ */
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz use_lastscb;
+
+ /*
+ * If the hardware has traversed the whole list, NEXTSCB
+ * will be NULL, CURRSCB and LASTSCB cannot be trusted,
+ * but MAXCMDCNT is accurate. If we stop part way through
+ * the list or only had one command to issue, NEXTSCB[1] is
+ * not NULL and LASTSCB is the last command to go out.
+ */
+ cmp NEXTSCB[1], SCB_LIST_NULL jne use_lastscb;
+
+ /*
+ * Brute force walk.
+ */
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ mvi SEQINTCTL, INTVEC1DSL;
+ mvi MODE_PTR, MK_MODE(M_CFG, M_CFG);
+ mov A, MAXCMDCNT;
+ mvi MODE_PTR, MK_MODE(M_SCSI, M_SCSI);
+ clr SEQINTCTL;
+find_lastscb_loop:
+ dec A;
+ test A, 0xFF jz found_last_sent_scb;
+ bmov SCBPTR, SCB_NEXT, 2;
+ jmp find_lastscb_loop;
+use_lastscb:
+ bmov SCBPTR, LASTSCB, 2;
+found_last_sent_scb:
+ bmov CURRSCB, SCBPTR, 2;
+curscb_ww_done:
+ } else {
+ bmov SCBPTR, CURRSCB, 2;
+ }
+
+ /*
+ * Requeue any SCBs not sent, to the tail of the waiting Q.
+ */
+ cmp SCB_NEXT[1], SCB_LIST_NULL je select_out_list_done;
+
+ /*
+ * We know that neither the per-TID list nor the list of
+ * TIDs is empty. Use this knowledge to our advantage.
+ */
+ bmov REG0, SCB_NEXT, 2;
+ bmov SCBPTR, WAITING_TID_TAIL, 2;
+ bmov SCB_NEXT2, REG0, 2;
+ bmov WAITING_TID_TAIL, REG0, 2;
+ jmp select_out_inc_tid_q;
+
+select_out_list_done:
+ /*
+ * The whole list made it. Just clear our TID's tail pointer
+ * unless we were queued independently due to our need to
+ * send a message.
+ */
+ test SCB_CONTROL, MK_MESSAGE jnz select_out_inc_tid_q;
+ shr DINDEX, 3, SCB_SCSIID;
+ or DINDEX, 1; /* Want only the second byte */
+ mvi DINDEX[1], ((WAITING_SCB_TAILS) >> 8);
+ mvi DINDIR, SCB_LIST_NULL;
+select_out_inc_tid_q:
+ bmov SCBPTR, WAITING_TID_HEAD, 2;
+ bmov WAITING_TID_HEAD, SCB_NEXT2, 2;
+ cmp WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
+ mvi WAITING_TID_TAIL[1], SCB_LIST_NULL;
+ bmov SCBPTR, CURRSCB, 2;
+ mvi CLRSINT0, CLRSELDO;
+ test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase;
+ test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase;
+
+ /*
+ * If this is a packetized connection, return to our
+ * idle_loop and let our interrupt handler deal with
+ * any connection setup/teardown issues. The only
+ * exceptions are the case of MK_MESSAGE and task management
+ * SCBs.
+ */
+ if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
+ /*
+ * In the A, the LQO manager transitions to LQOSTOP0 even if
+ * we have selected out with ATN asserted and the target
+ * REQs in a non-packet phase.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
+ test SCSISIGO, ATNO jnz select_out_non_packetized;
+select_out_no_message:
+ }
+ test LQOSTAT2, LQOSTOP0 jz select_out_non_packetized;
+ test SCB_TASK_MANAGEMENT, 0xFF jz idle_loop;
+ SET_SEQINTCODE(TASKMGMT_FUNC_COMPLETE)
+ jmp idle_loop;
+
+select_out_non_packetized:
+ /* Non packetized request. */
+ and SCSISEQ0, ~ENSELO;
+ if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+ /*
+ * Test to ensure that the bus has not
+ * already gone free prior to clearing
+ * any stale busfree status. This avoids
+ * a window whereby a busfree just after
+ * a selection could be missed.
+ */
+ test SCSISIGI, BSYI jz . + 2;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE;
+ }
+ mov SAVED_SCSIID, SCB_SCSIID;
+ mov SAVED_LUN, SCB_LUN;
+ mvi SEQ_FLAGS, NO_CDB_SENT;
+END_CRITICAL;
+ or SXFRCTL0, SPIOEN;
+
+ /*
+ * As soon as we get a successful selection, the target
+ * should go into the message out phase since we have ATN
+ * asserted.
+ */
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+
+ /*
+ * Main loop for information transfer phases. Wait for the
+ * target to assert REQ before checking MSG, C/D and I/O for
+ * the bus phase.
+ */
+mesgin_phasemis:
+ITloop:
+ call phase_lock;
+
+ mov A, LASTPHASE;
+
+ test A, ~P_DATAIN_DT jz p_data;
+ cmp A,P_COMMAND je p_command;
+ cmp A,P_MESGOUT je p_mesgout;
+ cmp A,P_STATUS je p_status;
+ cmp A,P_MESGIN je p_mesgin;
+
+ SET_SEQINTCODE(BAD_PHASE)
+ jmp ITloop; /* Try reading the bus again. */
+
+/*
+ * Command phase. Set up the DMA registers and let 'er rip.
+ */
+p_command:
+ test SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+ SET_SEQINTCODE(PROTO_VIOLATION)
+p_command_okay:
+ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+ jnz p_command_allocate_fifo;
+ /*
+ * Command retry. Free our current FIFO and
+ * re-allocate a FIFO so transfer state is
+ * reset.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT;
+ SET_MODE(M_SCSI, M_SCSI)
+p_command_allocate_fifo:
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+ add NONE, -17, SCB_CDB_LEN;
+ jnc p_command_embedded;
+p_command_from_host:
+ bmov HADDR[0], SCB_HOST_CDB_PTR, 9;
+ mvi SG_CACHE_PRE, LAST_SEG;
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+ jmp p_command_xfer;
+p_command_embedded:
+ bmov SHCNT[0], SCB_CDB_LEN, 1;
+ bmov DFDAT, SCB_CDB_STORE, 16;
+ mvi DFCNTRL, SCSIEN;
+p_command_xfer:
+ and SEQ_FLAGS, ~NO_CDB_SENT;
+ if ((ahd->features & AHD_FAST_CDB_DELIVERY) != 0) {
+ /*
+ * To speed up CDB delivery in Rev B, all CDB acks
+ * are "released" to the output sync as soon as the
+ * command phase starts. There is only one problem
+ * with this approach. If the target changes phase
+ * before all data are sent, we have left over acks
+ * that can go out on the bus in a data phase. Due
+ * to other chip contraints, this only happens if
+ * the target goes to data-in, but if the acks go
+ * out before we can test SDONE, we'll think that
+ * the transfer has completed successfully. Work
+ * around this by taking advantage of the 400ns or
+ * 800ns dead time between command phase and the REQ
+ * of the new phase. If the transfer has completed
+ * successfully, SCSIEN should fall *long* before we
+ * see a phase change. We thus treat any phasemiss
+ * that occurs before SCSIEN falls as an incomplete
+ * transfer.
+ */
+ test SSTAT1, PHASEMIS jnz p_command_xfer_failed;
+ test DFCNTRL, SCSIEN jnz . - 1;
+ } else {
+ test DFCNTRL, SCSIEN jnz .;
+ }
+ /*
+ * DMA Channel automatically disabled.
+ * Don't allow a data phase if the command
+ * was not fully transferred.
+ */
+ test SSTAT2, SDONE jnz ITloop;
+p_command_xfer_failed:
+ or SEQ_FLAGS, NO_CDB_SENT;
+ jmp ITloop;
+
+
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+SET_SRC_MODE M_SCSI;
+SET_DST_MODE M_SCSI;
+p_status:
+ test SEQ_FLAGS,NOT_IDENTIFIED jnz mesgin_proto_violation;
+p_status_okay:
+ mov SCB_SCSI_STATUS, SCSIDAT;
+ or SCB_CONTROL, STATUS_RCVD;
+ jmp ITloop;
+
+/*
+ * Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target. The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte. This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own. If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+ /* Turn on ATN for the retry */
+ mvi SCSISIGO, ATNO;
+p_mesgout:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+ test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
+p_mesgout_identify:
+ or SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+ test SCB_CONTROL, DISCENB jnz . + 2;
+ and SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_NONPACKET_TAG as the tag value.
+ */
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDAT, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDAT,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCBPTR jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ jmp host_message_loop;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDAT, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
+
+p_mesgout_done:
+ mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
+ mov LAST_MSG, MSG_OUT;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
+ jmp ITloop;
+
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+ /* read the 1st message byte */
+ mvi ACCUM call inb_first;
+
+ test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
+ cmp A,MSG_DISCONNECT je mesgin_disconnect;
+ cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
+ cmp ALLZEROS,A je mesgin_complete;
+ cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
+ cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_ign_wide_residue;
+ cmp A,MSG_NOOP je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine. To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop. Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+ call phase_lock; /* Benign the first time through. */
+ SET_SEQINTCODE(HOST_MSG_LOOP)
+ cmp RETURN_1, EXIT_MSG_LOOP je ITloop;
+ cmp RETURN_1, CONT_MSG_LOOP_WRITE jne . + 3;
+ mov SCSIDAT, RETURN_2;
+ jmp host_message_loop;
+ /* Must be CONT_MSG_LOOP_READ */
+ mov NONE, SCSIDAT; /* ACK Byte */
+ jmp host_message_loop;
+
+mesgin_ign_wide_residue:
+ mov SAVED_MODE, MODE_PTR;
+ SET_MODE(M_SCSI, M_SCSI)
+ shr NEGOADDR, 4, SAVED_SCSIID;
+ mov A, NEGCONOPTS;
+ RESTORE_MODE(SAVED_MODE)
+ test A, WIDEXFER jz mesgin_reject;
+ /* Pull the residue byte */
+ mvi REG0 call inb_next;
+ cmp REG0, 0x01 jne mesgin_reject;
+ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+ test SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jnz mesgin_done;
+ SET_SEQINTCODE(IGN_WIDE_RES)
+ jmp mesgin_done;
+
+mesgin_proto_violation:
+ SET_SEQINTCODE(PROTO_VIOLATION)
+ jmp mesgin_done;
+mesgin_reject:
+ mvi MSG_MESSAGE_REJECT call mk_mesg;
+mesgin_done:
+ mov NONE,SCSIDAT; /*dummy read from latch to ACK*/
+ jmp ITloop;
+
+#define INDEX_DISC_LIST(scsiid, lun) \
+ and A, 0xC0, scsiid; \
+ or SCBPTR, A, lun; \
+ clr SCBPTR[1]; \
+ and SINDEX, 0x30, scsiid; \
+ shr SINDEX, 3; /* Multiply by 2 */ \
+ add SINDEX, (SCB_DISCONNECTED_LISTS & 0xFF); \
+ mvi SINDEX[1], ((SCB_DISCONNECTED_LISTS >> 8) & 0xFF)
+
+mesgin_identify:
+ /*
+ * Determine whether a target is using tagged or non-tagged
+ * transactions by first looking at the transaction stored in
+ * the per-device, disconnected array. If there is no untagged
+ * transaction for this target, this must be a tagged transaction.
+ */
+ and SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+ INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+ bmov DINDEX, SINDEX, 2;
+ bmov REG0, SINDIR, 2;
+ cmp REG0[1], SCB_LIST_NULL je snoop_tag;
+ /* Untagged. Clear the busy table entry and setup the SCB. */
+ bmov DINDIR, ALLONES, 2;
+ bmov SCBPTR, REG0, 2;
+ jmp setup_SCB;
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB. After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256.
+ */
+snoop_tag:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x80;
+ }
+ mov NONE, SCSIDAT; /* ACK Identify MSG */
+ call phase_lock;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x1;
+ }
+ cmp LASTPHASE, P_MESGIN jne not_found_ITloop;
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x2;
+ }
+ cmp SCSIBUS, MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+ clr SCBPTR[1];
+ mvi SCBPTR call inb_next; /* tag value */
+verify_scb:
+ test SCB_CONTROL,DISCONNECTED jz verify_other_scb;
+ mov A, SAVED_SCSIID;
+ cmp SCB_SCSIID, A jne verify_other_scb;
+ mov A, SAVED_LUN;
+ cmp SCB_LUN, A je setup_SCB_disconnected;
+verify_other_scb:
+ xor SCBPTR[1], 1;
+ test SCBPTR[1], 0xFF jnz verify_scb;
+ jmp not_found;
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+ if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+ or SEQ_FLAGS, 0x10;
+ }
+ test SCB_CONTROL,DISCONNECTED jz not_found;
+setup_SCB_disconnected:
+ and SCB_CONTROL,~DISCONNECTED;
+ clr SEQ_FLAGS; /* make note of IDENTIFY */
+ test SCB_SGPTR, SG_LIST_NULL jnz . + 3;
+ bmov ALLOCFIFO_SCBPTR, SCBPTR, 2;
+ call allocate_fifo;
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ mvi HOST_MSG call mk_mesg;
+ jmp mesgin_done;
+
+not_found:
+ SET_SEQINTCODE(NO_MATCH)
+ jmp mesgin_done;
+
+not_found_ITloop:
+ SET_SEQINTCODE(NO_MATCH)
+ jmp 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.
+ */
+mesgin_complete:
+
+ /*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again. We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+ test SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+ /*
+ * If we are identified and have successfully sent the CDB,
+ * any status will do. Optimize this fast path.
+ */
+ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+ /*
+ * If the target never sent an identify message but instead went
+ * to mesgin to give an invalid message, let the host abort us.
+ */
+ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+ /*
+ * If we recevied good status but never successfully sent the
+ * cdb, abort the command.
+ */
+ test SCB_SCSI_STATUS,0xff jnz complete_accepted;
+ test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+complete_accepted:
+
+ /*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz complete_nomsg;
+ SET_SEQINTCODE(MKMSG_FAILED)
+complete_nomsg:
+ call queue_scb_completion;
+ jmp await_busfree;
+
+freeze_queue:
+ /* Cancel any pending select-out. */
+ test SSTAT0, SELDO|SELINGO jnz . + 2;
+ and SCSISEQ0, ~ENSELO;
+ mov ACCUM_SAVE, A;
+ clr A;
+ add QFREEZE_COUNT, 1;
+ adc QFREEZE_COUNT[1], A;
+ or SEQ_FLAGS2, SELECTOUT_QFROZEN;
+ mov A, ACCUM_SAVE ret;
+
+/*
+ * Complete the current FIFO's SCB if data for this same
+ * SCB is not transferring in the other FIFO.
+ */
+SET_SRC_MODE M_DFF1;
+SET_DST_MODE M_DFF1;
+pkt_complete_scb_if_fifos_idle:
+ bmov ARG_1, SCBPTR, 2;
+ m