📄 aic79xx.seq
字号:
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -