diff options
Diffstat (limited to 'drivers/scsi/aic7xxx_old/aic7xxx.seq')
-rw-r--r-- | drivers/scsi/aic7xxx_old/aic7xxx.seq | 1539 |
1 files changed, 0 insertions, 1539 deletions
diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.seq b/drivers/scsi/aic7xxx_old/aic7xxx.seq deleted file mode 100644 index dc3bb81cff0c..000000000000 --- a/drivers/scsi/aic7xxx_old/aic7xxx.seq +++ /dev/null @@ -1,1539 +0,0 @@ -/* - * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. - * - * Copyright (c) 1994-1999 Justin Gibbs. - * 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, immediately at the beginning of the file. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * Where this Software is combined with software released under the terms of - * the GNU General Public License (GPL) and the terms of the GPL would require the - * combined work to also be released under the terms of the GPL, the terms - * and conditions of this License will apply in addition to those of the - * GPL with the exception of any terms or conditions of this License that - * conflict with, or are expressly prohibited by, the GPL. - * - * 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.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $ - */ - -#include "aic7xxx.reg" -#include "scsi_message.h" - -/* - * A few words on the waiting SCB list: - * After starting the selection hardware, we check for reconnecting targets - * as well as for our selection to complete just in case the reselection wins - * bus arbitration. The problem with this is that we must keep track of the - * SCB that we've already pulled from the QINFIFO and started the selection - * on just in case the reselection wins so that we can retry the selection at - * a later time. This problem cannot be resolved by holding a single entry - * in scratch ram since a reconnecting target can request sense and this will - * create yet another SCB waiting for selection. The solution used here is to - * use byte 27 of the SCB as a pseudo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, - * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to - * this list every time a request sense occurs or after completing a non-tagged - * command for which a second SCB has been queued. The sequencer will - * automatically consume the entries. - */ - -reset: - clr SCSISIGO; /* De-assert BSY */ - and SXFRCTL1, ~BITBUCKET; - /* Always allow reselection */ - mvi SCSISEQ, ENRSELI|ENAUTOATNP; - - if ((p->features & AHC_CMD_CHAN) != 0) { - /* Ensure that no DMA operations are in progress */ - clr CCSGCTL; - clr CCSCBCTL; - } - - call clear_target_state; -poll_for_work: - and SXFRCTL0, ~SPIOEN; - if ((p->features & AHC_QUEUE_REGS) == 0) { - mov A, QINPOS; - } -poll_for_work_loop: - if ((p->features & AHC_QUEUE_REGS) == 0) { - and SEQCTL, ~PAUSEDIS; - } - test SSTAT0, SELDO|SELDI jnz selection; - test SCSISEQ, ENSELO jnz poll_for_work; - if ((p->features & AHC_TWIN) != 0) { - /* - * Twin channel devices cannot handle things like SELTO - * interrupts on the "background" channel. So, if we - * are selecting, keep polling the current channel util - * either a selection or reselection occurs. - */ - xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ - test SSTAT0, SELDO|SELDI jnz selection; - test SCSISEQ, ENSELO jnz poll_for_work; - xor SBLKCTL,SELBUSB; /* Toggle back */ - } - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; -test_queue: - /* Has the driver posted any work for us? */ - if ((p->features & AHC_QUEUE_REGS) != 0) { - test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; - mov NONE, SNSCB_QOFF; - inc QINPOS; - } else { - or SEQCTL, PAUSEDIS; - cmp KERNEL_QINPOS, A je poll_for_work_loop; - inc QINPOS; - and SEQCTL, ~PAUSEDIS; - } - -/* - * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. If we have - * any SCBs available for use, pull the tag from the QINFIFO - * and get to work on it. - */ - if ((p->flags & AHC_PAGESCBS) != 0) { - mov ALLZEROS call get_free_or_disc_scb; - } - -dequeue_scb: - add A, -1, QINPOS; - mvi QINFIFO_OFFSET call fetch_byte; - - if ((p->flags & AHC_PAGESCBS) == 0) { - /* In the non-paging case, the SCBID == hardware SCB index */ - mov SCBPTR, RETURN_2; - } -dma_queued_scb: -/* - * DMA the SCB from host ram into the current SCB location. - */ - mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - mov RETURN_2 call dma_scb; - -/* - * Preset the residual fields in case we never go through a data phase. - * This isn't done by the host so we can avoid a DMA to clear these - * fields for the normal case of I/O that completes without underrun - * or overrun conditions. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov SCB_RESID_DCNT, SCB_DATACNT, 3; - } else { - mov SCB_RESID_DCNT[0],SCB_DATACNT[0]; - mov SCB_RESID_DCNT[1],SCB_DATACNT[1]; - mov SCB_RESID_DCNT[2],SCB_DATACNT[2]; - } - mov SCB_RESID_SGCNT, SCB_SGCOUNT; - -start_scb: - /* - * Place us on the waiting list in case our selection - * doesn't win during bus arbitration. - */ - mov SCB_NEXT,WAITING_SCBH; - mov WAITING_SCBH, SCBPTR; -start_waiting: - /* - * Pull the first entry off of the waiting SCB list. - */ - mov SCBPTR, WAITING_SCBH; - call start_selection; - jmp poll_for_work; - -start_selection: - if ((p->features & AHC_TWIN) != 0) { - and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ - and A,SELBUSB,SCB_TCL; /* Get new channel bit */ - or SINDEX,A; - mov SBLKCTL,SINDEX; /* select channel */ - } -initialize_scsiid: - if ((p->features & AHC_ULTRA2) != 0) { - and A, TID, SCB_TCL; /* Get target ID */ - and SCSIID_ULTRA2, OID; /* Clear old target */ - or SCSIID_ULTRA2, A; - } else { - and A, TID, SCB_TCL; /* Get target ID */ - and SCSIID, OID; /* Clear old target */ - or SCSIID, A; - } - mov SCSIDATL, ALLZEROS; /* clear out the latched */ - /* data register, this */ - /* fixes a bug on some */ - /* controllers where the */ - /* last byte written to */ - /* this register can leak */ - /* onto the data bus at */ - /* bad times, such as during */ - /* selection timeouts */ - mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; - -/* - * Initialize Ultra mode setting and clear the SCSI channel. - * SINDEX should contain any additional bit's the client wants - * set in SXFRCTL0. - */ -initialize_channel: - or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX; - if ((p->features & AHC_ULTRA) != 0) { -ultra: - mvi SINDEX, ULTRA_ENB+1; - test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ - dec SINDEX; -ultra_2: - mov FUNCTION1,SAVED_TCL; - mov A,FUNCTION1; - test SINDIR, A jz ndx_dtr; - or SXFRCTL0, FAST20; - } -/* - * Initialize SCSIRATE with the appropriate value for this target. - * The SCSIRATE settings for each target are stored in an array - * based at TARG_SCSIRATE. - */ -ndx_dtr: - shr A,4,SAVED_TCL; - if ((p->features & AHC_TWIN) != 0) { - test SBLKCTL,SELBUSB jz ndx_dtr_2; - or SAVED_TCL, SELBUSB; - or A,0x08; /* Channel B entries add 8 */ -ndx_dtr_2: - } - - if ((p->features & AHC_ULTRA2) != 0) { - add SINDEX, TARG_OFFSET, A; - mov SCSIOFFSET, SINDIR; - } - - add SINDEX,TARG_SCSIRATE,A; - mov SCSIRATE,SINDIR ret; - - -selection: - test SSTAT0,SELDO jnz select_out; -/* - * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target yet. - */ -initiator_reselect: - mvi CLRSINT0, CLRSELDI; - /* XXX test for and handle ONE BIT condition */ - and SAVED_TCL, SELID_MASK, SELID; - mvi CLRSINT1,CLRBUSFREE; - or SIMODE1, ENBUSFREE; /* - * We aren't expecting a - * bus free, so interrupt - * the kernel driver if it - * happens. - */ - mvi SPIOEN call initialize_channel; - mvi MSG_OUT, MSG_NOOP; /* No message to send */ - jmp ITloop; - -/* - * After the selection, remove this SCB from the "waiting SCB" - * list. This is achieved by simply moving our "next" pointer into - * WAITING_SCBH. Our next pointer will be set to null the next time this - * SCB is used, so don't bother with it now. - */ -select_out: - /* Turn off the selection hardware */ - mvi SCSISEQ, ENRSELI|ENAUTOATNP; /* - * ATN on parity errors - * for "in" phases - */ - mvi CLRSINT0, CLRSELDO; - mov SCBPTR, WAITING_SCBH; - mov WAITING_SCBH,SCB_NEXT; - mov SAVED_TCL, SCB_TCL; - mvi CLRSINT1,CLRBUSFREE; - or SIMODE1, ENBUSFREE; /* - * We aren't expecting a - * bus free, so interrupt - * the kernel driver if it - * happens. - */ - mvi SPIOEN call initialize_channel; -/* - * 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; - or SEQ_FLAGS, IDENTIFY_SEEN; - -/* - * 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. - */ -ITloop: - call phase_lock; - - mov A, LASTPHASE; - - test A, ~P_DATAIN 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; - - mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */ - jmp ITloop; /* Try reading the bus again. */ - -await_busfree: - and SIMODE1, ~ENBUSFREE; - call clear_target_state; - mov NONE, SCSIDATL; /* Ack the last byte */ - and SXFRCTL0, ~SPIOEN; - test SSTAT1,REQINIT|BUSFREE jz .; - test SSTAT1, BUSFREE jnz poll_for_work; - mvi INTSTAT, BAD_PHASE; - -clear_target_state: - /* - * We assume that the kernel driver may reset us - * at any time, even in the middle of a DMA, so - * clear DFCNTRL too. - */ - clr DFCNTRL; - - /* - * We don't know the target we will connect to, - * so default to narrow transfers to avoid - * parity problems. - */ - if ((p->features & AHC_ULTRA2) != 0) { - bmov SCSIRATE, ALLZEROS, 2; - } else { - clr SCSIRATE; - and SXFRCTL0, ~(FAST20); - } - mvi LASTPHASE, P_BUSFREE; - /* clear target specific flags */ - clr SEQ_FLAGS ret; - - -data_phase_reinit: -/* - * If we re-enter the data phase after going through another phase, the - * STCNT may have been cleared, so restore it from the residual field. - * On Ultra2, we have to put it into the HCNT field because we have to - * drop the data down into the shadow layer via the preload ability. - */ - if ((p->features & AHC_ULTRA2) != 0) { - bmov HADDR, SHADDR, 4; - bmov HCNT, SCB_RESID_DCNT, 3; - } - if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { - bmov STCNT, SCB_RESID_DCNT, 3; - } - if ((p->features & AHC_CMD_CHAN) == 0) { - mvi DINDEX, STCNT; - mvi SCB_RESID_DCNT call bcopy_3; - } - jmp data_phase_loop; -p_data: - if ((p->features & AHC_ULTRA2) != 0) { - mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; - } else { - mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; - } - test LASTPHASE, IOI jnz . + 2; - or DMAPARAMS, DIRECTION; - call assert; /* - * Ensure entering a data - * phase is okay - seen identify, etc. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - mvi CCSGADDR, CCSGADDR_MAX; - } - - test SEQ_FLAGS, DPHASE jnz data_phase_reinit; - or SEQ_FLAGS, DPHASE; /* we've seen a data phase */ - /* - * Initialize the DMA address and counter from the SCB. - * Also set SG_COUNT and SG_NEXT in memory since we cannot - * modify the values in the SCB itself until we see a - * save data pointers message. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov HADDR, SCB_DATAPTR, 7; - bmov SG_COUNT, SCB_SGCOUNT, 5; - if ((p->features & AHC_ULTRA2) == 0) { - bmov STCNT, HCNT, 3; - } - } else { - mvi DINDEX, HADDR; - mvi SCB_DATAPTR call bcopy_7; - call set_stcnt_from_hcnt; - mvi DINDEX, SG_COUNT; - mvi SCB_SGCOUNT call bcopy_5; - } -data_phase_loop: - /* Guard against overruns */ - test SG_COUNT, 0xff jnz data_phase_inbounds; -/* - * Turn on 'Bit Bucket' mode, set the transfer count to - * 16meg and let the target run until it changes phase. - * When the transfer completes, notify the host that we - * had an overrun. - */ - or SXFRCTL1,BITBUCKET; - and DMAPARAMS, ~(HDMAEN|SDMAEN); - if ((p->features & AHC_ULTRA2) != 0) { - bmov HCNT, ALLONES, 3; - } - if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { - bmov STCNT, ALLONES, 3; - } - if ((p->features & AHC_CMD_CHAN) == 0) { - mvi STCNT[0], 0xFF; - mvi STCNT[1], 0xFF; - mvi STCNT[2], 0xFF; - } - -data_phase_inbounds: -/* If we are the last SG block, tell the hardware. */ - if ((p->features & AHC_ULTRA2) != 0) { - shl A, 2, SG_COUNT; - cmp SG_COUNT,0x01 jne data_phase_wideodd; - or A, LAST_SEG; - } else { - cmp SG_COUNT,0x01 jne data_phase_wideodd; - and DMAPARAMS, ~WIDEODD; - } -data_phase_wideodd: - if ((p->features & AHC_ULTRA2) != 0) { - mov SG_CACHEPTR, A; - mov DFCNTRL, DMAPARAMS; /* start the operation */ - test SXFRCTL1, BITBUCKET jnz data_phase_overrun; -u2_preload_wait: - test SSTAT1, PHASEMIS jnz u2_phasemis; - test DFSTATUS, PRELOAD_AVAIL jz u2_preload_wait; - } else { - mov DMAPARAMS call dma; -data_phase_dma_done: -/* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun; - -/* Exit if we had an underrun. dma clears SINDEX in this case. */ - test SINDEX,0xff jz data_phase_finish; - } -/* - * Advance the scatter-gather pointers - */ -sg_advance: - if ((p->features & AHC_ULTRA2) != 0) { - cmp SG_COUNT, 0x01 je u2_data_phase_finish; - } else { - dec SG_COUNT; - test SG_COUNT, 0xff jz data_phase_finish; - } - - if ((p->features & AHC_CMD_CHAN) != 0) { - - /* - * Do we have any prefetch left??? - */ - cmp CCSGADDR, CCSGADDR_MAX jne prefetch_avail; - - /* - * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. - */ - add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; - mvi A, CCSGADDR_MAX; - jc . + 2; - shl A, 3, SG_COUNT; - mov CCHCNT, A; - bmov CCHADDR, SG_NEXT, 4; - mvi CCSGCTL, CCSGEN|CCSGRESET; - test CCSGCTL, CCSGDONE jz .; - and CCSGCTL, ~CCSGEN; - test CCSGCTL, CCSGEN jnz .; - mvi CCSGCTL, CCSGRESET; -prefetch_avail: - bmov HADDR, CCSGRAM, 8; - if ((p->features & AHC_ULTRA2) == 0) { - bmov STCNT, HCNT, 3; - } else { - dec SG_COUNT; - } - } else { - mvi DINDEX, HADDR; - mvi SG_NEXT call bcopy_4; - - mvi HCNT[0],SG_SIZEOF; - clr HCNT[1]; - clr HCNT[2]; - - or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - - call dma_finish; - -/* - * Copy data from FIFO into SCB data pointer and data count. - * This assumes that the SG segments are of the form: - * struct ahc_dma_seg { - * u_int32_t addr; four bytes, little-endian order - * u_int32_t len; four bytes, little endian order - * }; - */ - mvi DINDEX, HADDR; - call dfdat_in_7; - call set_stcnt_from_hcnt; - } -/* Advance the SG pointer */ - clr A; /* add sizeof(struct scatter) */ - add SG_NEXT[0],SG_SIZEOF; - adc SG_NEXT[1],A; - - if ((p->features & AHC_ULTRA2) != 0) { - jmp data_phase_loop; - } else { - test SSTAT1, REQINIT jz .; - test SSTAT1,PHASEMIS jz data_phase_loop; - } - - -/* - * We've loaded all of our segments into the preload layer. Now, we simply - * have to wait for it to finish or for us to get a phasemis. And, since - * we'll get a phasemis if we do finish, all we really need to do is wait - * for a phasemis then check if we did actually complete all the segments. - */ - if ((p->features & AHC_ULTRA2) != 0) { -u2_data_phase_finish: - test SSTAT1, PHASEMIS jnz u2_phasemis; - test SG_CACHEPTR, LAST_SEG_DONE jz u2_data_phase_finish; - clr SG_COUNT; - test SSTAT1, REQINIT jz .; - test SSTAT1, PHASEMIS jz data_phase_loop; -u2_phasemis: - call ultra2_dmafinish; - test SG_CACHEPTR, LAST_SEG_DONE jnz data_phase_finish; - test SSTAT2, SHVALID jnz u2_fixup_residual; - mvi INTSTAT, SEQ_SG_FIXUP; - jmp data_phase_finish; -u2_fixup_residual: - shr ARG_1, 2, SG_CACHEPTR; -u2_phasemis_loop: - and A, 0x3f, SG_COUNT; - cmp ARG_1, A je data_phase_finish; -/* - * Subtract SG_SIZEOF from the SG_NEXT pointer and add 1 to the SG_COUNT - */ - clr A; - add SG_NEXT[0], -SG_SIZEOF; - adc SG_NEXT[1], 0xff; - inc SG_COUNT; - jmp u2_phasemis_loop; - } - -data_phase_finish: -/* - * After a DMA finishes, save the SG and STCNT residuals back into the SCB - * We use STCNT instead of HCNT, since it's a reflection of how many bytes - * were transferred on the SCSI (as opposed to the host) bus. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov SCB_RESID_DCNT, STCNT, 3; - mov SCB_RESID_SGCNT, SG_COUNT; - if ((p->features & AHC_ULTRA2) != 0) { - or SXFRCTL0, CLRSTCNT|CLRCHN; - } - } else { - mov SCB_RESID_DCNT[0],STCNT[0]; - mov SCB_RESID_DCNT[1],STCNT[1]; - mov SCB_RESID_DCNT[2],STCNT[2]; - mov SCB_RESID_SGCNT, SG_COUNT; - } - - jmp ITloop; - -data_phase_overrun: -/* - * Turn off BITBUCKET mode and notify the host - */ - if ((p->features & AHC_ULTRA2) != 0) { -/* - * Wait for the target to quit transferring data on the SCSI bus - */ - test SSTAT1, PHASEMIS jz .; - call ultra2_dmafinish; - } - and SXFRCTL1, ~BITBUCKET; - mvi INTSTAT,DATA_OVERRUN; - jmp ITloop; - - - - -/* - * Actually turn off the DMA hardware, save our current position into the - * proper residual variables, wait for the next REQ signal, then jump to - * the ITloop. Jumping to the ITloop ensures that if we happen to get - * brought into the data phase again (or are still in it after our last - * segment) that we will properly signal an overrun to the kernel. - */ - if ((p->features & AHC_ULTRA2) != 0) { -ultra2_dmafinish: - test DFCNTRL, DIRECTION jnz ultra2_dmahalt; - and DFCNTRL, ~SCSIEN; - test DFCNTRL, SCSIEN jnz .; - if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) { - or DFCNTRL, FIFOFLUSH; - } -ultra2_dmafifoflush: - if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) { - /* - * hardware bug alert! This needless set of jumps - * works around a glitch in the silicon. When the - * PCI DMA fifo goes empty, but there is still SCSI - * data to be flushed into the PCI DMA fifo (and from - * there on into main memory), the FIFOEMP bit will - * come on between the time when the PCI DMA buffer - * went empty and the next bit of data is copied from - * the SCSI fifo into the PCI fifo. It should only - * come on when both FIFOs (meaning the entire FIFO - * chain) are empty. Since it can take up to 4 cycles - * for new data to be copied from the SCSI fifo into - * the PCI fifo, testing for FIFOEMP status for 4 - * extra times gives the needed time for any - * remaining SCSI fifo data to be put in the PCI fifo - * before we declare it *truly* empty. - */ - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - } - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, MREQPEND jnz .; -ultra2_dmahalt: - and DFCNTRL, ~(HDMAEN|SCSIEN); - test DFCNTRL, (HDMAEN|SCSIEN) jnz .; - ret; - } - -/* - * Command phase. Set up the DMA registers and let 'er rip. - */ -p_command: - call assert; - -/* - * Load HADDR and HCNT. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov HADDR, SCB_CMDPTR, 5; - bmov HCNT[1], ALLZEROS, 2; - if ((p->features & AHC_ULTRA2) == 0) { - bmov STCNT, HCNT, 3; - } - } else { - mvi DINDEX, HADDR; - mvi SCB_CMDPTR call bcopy_5; - clr HCNT[1]; - clr HCNT[2]; - call set_stcnt_from_hcnt; - } - - if ((p->features & AHC_ULTRA2) == 0) { - mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; - } else { - mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); - test SSTAT0, SDONE jnz .; -p_command_dma_loop: - test SSTAT0, SDONE jnz p_command_ultra2_dma_done; - test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */ -p_command_ultra2_dma_done: - test SCSISIGI, REQI jz p_command_ultra2_shutdown; - test SSTAT1, (PHASEMIS|REQINIT) jz p_command_ultra2_dma_done; -p_command_ultra2_shutdown: - and DFCNTRL, ~(HDMAEN|SCSIEN); - test DFCNTRL, (HDMAEN|SCSIEN) jnz .; - or SXFRCTL0, CLRSTCNT|CLRCHN; - } - jmp ITloop; - -/* - * Status phase. Wait for the data byte to appear, then read it - * and store it into the SCB. - */ -p_status: - call assert; - - mov SCB_TARGET_STATUS, SCSIDATL; - jmp ITloop; - -/* - * Message out phase. If MSG_OUT is 0x80, build I full indentify message - * sequence and send it to the target. In addition, if the MK_MESSAGE bit - * is set in the SCB_CONTROL byte, interrupt the host and allow it to send - * it's own message. - * - * 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 response 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: - or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ -p_mesgout: - mov SINDEX, MSG_OUT; - cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; -p_mesgout_identify: - if ((p->features & AHC_WIDE) != 0) { - and SINDEX,0xf,SCB_TCL; /* lun */ - } else { - and SINDEX,0x7,SCB_TCL; /* lun */ - } - and A,DISCENB,SCB_CONTROL; /* mask off disconnect privilege */ - or SINDEX,A; /* or in disconnect privilege */ - or SINDEX,MSG_IDENTIFYFLAG; -p_mesgout_mk_message: - test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag; - mov SCSIDATL, SINDEX; /* Send the last byte */ - jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */ -/* - * Send a tag message if TAG_ENB is set in the SCB control block. - * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. - */ -p_mesgout_tag: - test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte; - mov SCSIDATL, SINDEX; /* Send the identify message */ - call phase_lock; - cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; - and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; - call phase_lock; - cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; - mov SCB_TAG jmp p_mesgout_onebyte; -/* - * Interrupt the driver, and allow it to send a message - * if it asks. - */ -p_mesgout_from_host: - cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; - mvi INTSTAT,AWAITING_MSG; - nop; - /* - * Did the host detect a phase change? - */ - cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done; - -p_mesgout_onebyte: - mvi CLRSINT1, CLRATNO; - mov SCSIDATL, SINDEX; - -/* - * If the next bus phase after ATN drops is a 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; - cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2; - and SCB_CONTROL, ~MK_MESSAGE; - mvi MSG_OUT, MSG_NOOP; /* No message left */ - jmp ITloop; - -/* - * Message in phase. Bytes are read using Automatic PIO mode. - */ -p_mesgin: - mvi ACCUM call inb_first; /* read the 1st message byte */ - - 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_EXTENDED je mesgin_extended; - cmp A,MSG_MESSAGE_REJECT je mesgin_reject; - cmp A,MSG_NOOP je mesgin_done; - cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_wide_residue; - -rej_mesgin: -/* - * We have no idea what this message in is, so we issue a message reject - * and hope for the best. In any case, rejection should be a rare - * occurrence - signal the driver when it happens. - */ - mvi INTSTAT,SEND_REJECT; /* let driver know */ - - mvi MSG_MESSAGE_REJECT call mk_mesg; - -mesgin_done: - mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ - jmp ITloop; - - -mesgin_complete: -/* - * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, - * and trigger a completion interrupt. 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 and set - * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload - * the SCB, and process it as the next command by adding it to the waiting list. - * If the kernel driver does not wish to request sense, it need only clear - * RETURN_1, and the command is allowed to complete normally. We don't bother - * to post to the QOUTFIFO in the error cases since it would require extra - * work in the kernel driver to ensure that the entry was removed before the - * command complete code tried processing it. - */ - -/* - * First check for residuals - */ - test SCB_RESID_SGCNT,0xff jnz upload_scb; - test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */ -upload_scb: - mvi DMAPARAMS, FIFORESET; - mov SCB_TAG call dma_scb; -check_status: - test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ - mvi INTSTAT,BAD_STATUS; /* let driver know */ - nop; - cmp RETURN_1, SEND_SENSE jne complete; - /* This SCB becomes the next to execute as it will retrieve sense */ - mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - mov SCB_TAG call dma_scb; -add_to_waiting_list: - mov SCB_NEXT,WAITING_SCBH; - mov WAITING_SCBH, SCBPTR; - /* - * Prepare our selection hardware before the busfree so we have a - * high probability of winning arbitration. - */ - call start_selection; - jmp await_busfree; - -complete: - /* If we are untagged, clear our address up in host ram */ - test SCB_CONTROL, TAG_ENB jnz complete_post; - mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call post_byte_setup; - mvi SCB_LIST_NULL call post_byte; - -complete_post: - /* Post the SCB and issue an interrupt */ - if ((p->features & AHC_QUEUE_REGS) != 0) { - mov A, SDSCB_QOFF; - } else { - mov A, QOUTPOS; - } - mvi QOUTFIFO_OFFSET call post_byte_setup; - mov SCB_TAG call post_byte; - if ((p->features & AHC_QUEUE_REGS) == 0) { - inc QOUTPOS; - } - mvi INTSTAT,CMDCMPLT; - -add_to_free_list: - call add_scb_to_free_list; - jmp await_busfree; - -/* - * Is it an extended message? Copy the message to our message buffer and - * notify the host. The host will tell us whether to reject this message, - * respond to it with the message that the host placed in our message buffer, - * or simply to do nothing. - */ -mesgin_extended: - mvi INTSTAT,EXTENDED_MSG; /* let driver know */ - jmp ITloop; - -/* - * Is it a disconnect message? Set a flag in the SCB to remind us - * and await the bus going free. - */ -mesgin_disconnect: - or SCB_CONTROL,DISCONNECTED; - call add_scb_to_disc_list; - jmp await_busfree; - -/* - * Save data pointers message: - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. - */ -mesgin_sdptrs: - test SEQ_FLAGS, DPHASE jz mesgin_done; - /* - * The SCB SGPTR becomes the next one we'll download, - * and the SCB DATAPTR becomes the current SHADDR. - * Use the residual number since STCNT is corrupted by - * any message transfer. - */ - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov SCB_SGCOUNT, SG_COUNT, 5; - bmov SCB_DATAPTR, SHADDR, 4; - bmov SCB_DATACNT, SCB_RESID_DCNT, 3; - } else { - mvi DINDEX, SCB_SGCOUNT; - mvi SG_COUNT call bcopy_5; - mvi DINDEX, SCB_DATAPTR; - mvi SHADDR call bcopy_4; - mvi SCB_RESID_DCNT call bcopy_3; - } - jmp mesgin_done; - -/* - * Restore pointers message? Data pointers are recopied from the - * SCB anytime we enter a data phase for the first time, so all - * we need to do is clear the DPHASE flag and let the data phase - * code do the rest. - */ -mesgin_rdptrs: - and SEQ_FLAGS, ~DPHASE; /* - * We'll reload them - * the next time through - * the dataphase. - */ - jmp mesgin_done; - -/* - * Identify message? For a reconnecting target, this tells us the lun - * that the reconnection is for - find the correct SCB and switch to it, - * clearing the "disconnected" bit so we don't "find" it by accident later. - */ -mesgin_identify: - - if ((p->features & AHC_WIDE) != 0) { - and A,0x0f; /* lun in lower four bits */ - } else { - and A,0x07; /* lun in lower three bits */ - } - or SAVED_TCL,A; /* SAVED_TCL should be complete now */ - - mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */ - call get_untagged_SCBID; - cmp ARG_1, SCB_LIST_NULL je snoop_tag; - if ((p->flags & AHC_PAGESCBS) != 0) { - test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; - } - /* - * If the SCB was found in the disconnected list (as is - * always the case in non-paging scenarios), SCBPTR is already - * set to the correct SCB. So, simply setup the SCB and get - * on with things. - */ - mov SCBPTR call rem_scb_from_disc_list; - 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. With SCB paging, this requires using search for both tagged - * and non-tagged transactions since the SCB may exist in any slot. - * If we're not using SCB paging, we can use the tag as the direct - * index to the SCB. - */ -snoop_tag: - mov NONE,SCSIDATL; /* ACK Identify MSG */ -snoop_tag_loop: - call phase_lock; - cmp LASTPHASE, P_MESGIN jne not_found; - cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; -get_tag: - mvi ARG_1 call inb_next; /* tag value */ - -use_retrieveSCB: - call retrieveSCB; -setup_SCB: - mov A, SAVED_TCL; - cmp SCB_TCL, A jne not_found_cleanup_scb; - test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb; - and SCB_CONTROL,~DISCONNECTED; - or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ - /* See if the host wants to send a message upon reconnection */ - test SCB_CONTROL, MK_MESSAGE jz mesgin_done; - and SCB_CONTROL, ~MK_MESSAGE; - mvi HOST_MSG call mk_mesg; - jmp mesgin_done; - -not_found_cleanup_scb: - test SCB_CONTROL, DISCONNECTED jz . + 3; - call add_scb_to_disc_list; - jmp not_found; - call add_scb_to_free_list; -not_found: - mvi INTSTAT, NO_MATCH; - mvi MSG_BUS_DEV_RESET call mk_mesg; - jmp mesgin_done; - -/* - * Message reject? Let the kernel driver handle this. If we have an - * outstanding WDTR or SDTR negotiation, assume that it's a response from - * the target selecting 8bit or asynchronous transfer, otherwise just ignore - * it since we have no clue what it pertains to. - */ -mesgin_reject: - mvi INTSTAT, REJECT_MSG; - jmp mesgin_done; - -/* - * Wide Residue. We handle the simple cases, but pass of the one hard case - * to the kernel (when the residue byte happened to cause us to advance our - * sg element array, so we know have to back that advance out). - */ -mesgin_wide_residue: - mvi ARG_1 call inb_next; /* ACK the wide_residue and get */ - /* the size byte */ -/* - * In order for this to be reliable, we have to do all sorts of horrible - * magic in terms of resetting the datafifo and reloading the shadow layer - * with the correct new values (so that a subsequent save data pointers - * message will do the right thing). We let the kernel do that work. - */ - mvi INTSTAT, WIDE_RESIDUE; - jmp mesgin_done; - -/* - * [ ADD MORE MESSAGE HANDLING HERE ] - */ - -/* - * Locking the driver out, build a one-byte message passed in SINDEX - * if there is no active message already. SINDEX is returned intact. - */ -mk_mesg: - or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ - mov MSG_OUT,SINDEX ret; - -/* - * Functions to read data in Automatic PIO mode. - * - * According to Adaptec's documentation, an ACK is not sent on input from - * the target until SCSIDATL is read from. So we wait until SCSIDATL is - * latched (the usual way), then read the data byte directly off the bus - * using SCSIBUSL. When we have pulled the ATN line, or we just want to - * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI - * spec guarantees that the target will hold the data byte on the bus until - * we send our ACK. - * - * The assumption here is that these are called in a particular sequence, - * and that REQ is already set when inb_first is called. inb_{first,next} - * use the same calling convention as inb. - */ - -inb_next: - mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ -inb_next_wait: - /* - * If there is a parity error, wait for the kernel to - * see the interrupt and prepare our message response - * before continuing. - */ - test SSTAT1, REQINIT jz inb_next_wait; - test SSTAT1, SCSIPERR jnz .; - and LASTPHASE, PHASE_MASK, SCSISIGI; - cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; -inb_first: - mov DINDEX,SINDEX; - mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ -inb_last: - mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ - - -mesgin_phasemis: -/* - * We expected to receive another byte, but the target changed phase - */ - mvi INTSTAT, MSGIN_PHASEMIS; - jmp ITloop; - -/* - * DMA data transfer. HADDR and HCNT must be loaded first, and - * SINDEX should contain the value to load DFCNTRL with - 0x3d for - * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared - * during initialization. - */ -if ((p->features & AHC_ULTRA2) == 0) { -dma: - mov DFCNTRL,SINDEX; -dma_loop: - test SSTAT0,DMADONE jnz dma_dmadone; - test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ -dma_phasemis: - test SSTAT0,SDONE jnz dma_checkfifo; - mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */ - -/* - * We will be "done" DMAing when the transfer count goes to zero, or - * the target changes the phase (in light of this, it makes sense that - * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are - * doing a SCSI->Host transfer, the data FIFO should be flushed auto- - * magically on STCNT=0 or a phase change, so just wait for FIFO empty - * status. - */ -dma_checkfifo: - test DFCNTRL,DIRECTION jnz dma_fifoempty; -dma_fifoflush: - test DFSTATUS,FIFOEMP jz dma_fifoflush; - -dma_fifoempty: - /* Don't clobber an inprogress host data transfer */ - test DFSTATUS, MREQPEND jnz dma_fifoempty; -/* - * Now shut the DMA enables off and make sure that the DMA enables are - * actually off first lest we get an ILLSADDR. - */ -dma_dmadone: - cmp LASTPHASE, P_COMMAND je dma_await_nreq; - test SCSIRATE, 0x0f jnz dma_shutdown; -dma_await_nreq: - test SCSISIGI, REQI jz dma_shutdown; - test SSTAT1, (PHASEMIS|REQINIT) jz dma_await_nreq; -dma_shutdown: - and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); -dma_halt: - /* - * Some revisions of the aic7880 have a problem where, if the - * data fifo is full, but the PCI input latch is not empty, - * HDMAEN cannot be cleared. The fix used here is to attempt - * to drain the data fifo until there is space for the input - * latch to drain and HDMAEN de-asserts. - */ - if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) { - mov NONE, DFDAT; - } - test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; -} -return: - ret; - -/* - * Assert that if we've been reselected, then we've seen an IDENTIFY - * message. - */ -assert: - test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ - - mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ - -/* - * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) - * or by the SCBID ARG_1. The search begins at the SCB index passed in - * via SINDEX which is an SCB that must be on the disconnected list. If - * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR - * is set to the proper SCB. - */ -findSCB: - mov SCBPTR,SINDEX; /* Initialize SCBPTR */ - cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID; - mov A, SAVED_TCL; - mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */ -findSCB_by_SCBID: - mov A, ARG_1; /* Tag passed in ARG_1 */ - mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */ -findSCB_next: - mov ARG_2, SCBPTR; - cmp SCB_NEXT, SCB_LIST_NULL je notFound; - mov SCBPTR,SCB_NEXT; - dec SINDEX; /* Last comparison moved us too far */ -findSCB_loop: - cmp SINDIR, A jne findSCB_next; - mov SINDEX, SCBPTR ret; -notFound: - mvi SINDEX, SCB_LIST_NULL ret; - -/* - * Retrieve an SCB by SCBID first searching the disconnected list falling - * back to DMA'ing the SCB down from the host. This routine assumes that - * ARG_1 is the SCBID of interest and that SINDEX is the position in the - * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL, - * we go directly to the host for the SCB. - */ -retrieveSCB: - test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host; - mov SCBPTR call findSCB; /* Continue the search */ - cmp SINDEX, SCB_LIST_NULL je retrieve_from_host; - -/* - * This routine expects SINDEX to contain the index of the SCB to be - * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the - * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL - * if it is at the head. - */ -rem_scb_from_disc_list: -/* Remove this SCB from the disconnection list */ - cmp ARG_2, SCB_LIST_NULL je rHead; - mov DINDEX, SCB_NEXT; - mov SCBPTR, ARG_2; - mov SCB_NEXT, DINDEX; - mov SCBPTR, SINDEX ret; -rHead: - mov DISCONNECTED_SCBH,SCB_NEXT ret; - -retrieve_from_host: -/* - * We didn't find it. Pull an SCB and DMA down the one we want. - * We should never get here in the non-paging case. - */ - mov ALLZEROS call get_free_or_disc_scb; - mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - /* Jump instead of call as we want to return anyway */ - mov ARG_1 jmp dma_scb; - -/* - * Determine whether a target is using tagged or non-tagged transactions - * by first looking for a matching transaction based on the TCL and if - * that fails, looking up this device in the host's untagged SCB array. - * The TCL to search for is assumed to be in SAVED_TCL. The value is - * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged). - * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information - * in an SCB instead of having to go to the host. - */ -get_untagged_SCBID: - cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host; - mvi ARG_1, SCB_LIST_NULL; - mov DISCONNECTED_SCBH call findSCB; - cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host; - or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */ - test SCB_CONTROL, TAG_ENB jnz . + 2; - mov ARG_1, SCB_TAG ret; - mvi ARG_1, SCB_LIST_NULL ret; - -/* - * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) - * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. - */ -fetch_byte: - mov ARG_2, SINDEX; - if ((p->features & AHC_CMD_CHAN) != 0) { - mvi DINDEX, CCHADDR; - mvi SCBID_ADDR call set_1byte_addr; - mvi CCHCNT, 1; - mvi CCSGCTL, CCSGEN|CCSGRESET; - test CCSGCTL, CCSGDONE jz .; - mvi CCSGCTL, CCSGRESET; - bmov RETURN_2, CCSGRAM, 1 ret; - } else { - mvi DINDEX, HADDR; - mvi SCBID_ADDR call set_1byte_addr; - mvi HCNT[0], 1; - clr HCNT[1]; - clr HCNT[2]; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - call dma_finish; - mov RETURN_2, DFDAT ret; - } - -/* - * Prepare the hardware to post a byte to host memory given an - * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. - */ -post_byte_setup: - mov ARG_2, SINDEX; - if ((p->features & AHC_CMD_CHAN) != 0) { - mvi DINDEX, CCHADDR; - mvi SCBID_ADDR call set_1byte_addr; - mvi CCHCNT, 1; - mvi CCSCBCTL, CCSCBRESET ret; - } else { - mvi DINDEX, HADDR; - mvi SCBID_ADDR call set_1byte_addr; - mvi HCNT[0], 1; - clr HCNT[1]; - clr HCNT[2]; - mvi DFCNTRL, FIFORESET ret; - } - -post_byte: - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov CCSCBRAM, SINDEX, 1; - or CCSCBCTL, CCSCBEN|CCSCBRESET; - test CCSCBCTL, CCSCBDONE jz .; - clr CCSCBCTL ret; - } else { - mov DFDAT, SINDEX; - or DFCNTRL, HDMAEN|FIFOFLUSH; - jmp dma_finish; - } - -get_SCBID_from_host: - mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call fetch_byte; - mov RETURN_1, RETURN_2 ret; - -phase_lock: - test SSTAT1, REQINIT jz phase_lock; - test SSTAT1, SCSIPERR jnz phase_lock; - and SCSISIGO, PHASE_MASK, SCSISIGI; - and LASTPHASE, PHASE_MASK, SCSISIGI ret; - -if ((p->features & AHC_CMD_CHAN) == 0) { -set_stcnt_from_hcnt: - mov STCNT[0], HCNT[0]; - mov STCNT[1], HCNT[1]; - mov STCNT[2], HCNT[2] ret; - -bcopy_7: - mov DINDIR, SINDIR; - mov DINDIR, SINDIR; -bcopy_5: - mov DINDIR, SINDIR; -bcopy_4: - mov DINDIR, SINDIR; -bcopy_3: - mov DINDIR, SINDIR; - mov DINDIR, SINDIR; - mov DINDIR, SINDIR ret; -} - -/* - * Setup addr assuming that A is an index into - * an array of 32byte objects, SINDEX contains - * the base address of that array, and DINDEX - * contains the base address of the location - * to store the indexed address. - */ -set_32byte_addr: - shr ARG_2, 3, A; - shl A, 5; -/* - * Setup addr assuming that A + (ARG_1 * 256) is an - * index into an array of 1byte objects, SINDEX contains - * the base address of that array, and DINDEX contains - * the base address of the location to store the computed - * address. - */ -set_1byte_addr: - add DINDIR, A, SINDIR; - mov A, ARG_2; - adc DINDIR, A, SINDIR; - clr A; - adc DINDIR, A, SINDIR; - adc DINDIR, A, SINDIR ret; - -/* - * Either post or fetch and SCB from host memory based on the - * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. - */ -dma_scb: - mov A, SINDEX; - if ((p->features & AHC_CMD_CHAN) != 0) { - mvi DINDEX, CCHADDR; - mvi HSCB_ADDR call set_32byte_addr; - mov CCSCBPTR, SCBPTR; - mvi CCHCNT, 32; - test DMAPARAMS, DIRECTION jz dma_scb_tohost; - mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; - cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; - jmp dma_scb_finish; -dma_scb_tohost: - if ((p->features & AHC_ULTRA2) == 0) { - mvi CCSCBCTL, CCSCBRESET; - bmov CCSCBRAM, SCB_CONTROL, 32; - or CCSCBCTL, CCSCBEN|CCSCBRESET; - test CCSCBCTL, CCSCBDONE jz .; - } - if ((p->features & AHC_ULTRA2) != 0) { - if ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0) { - mvi CCSCBCTL, CCARREN|CCSCBRESET; - cmp CCSCBCTL, ARRDONE|CCARREN jne .; - mvi CCHCNT, 32; - mvi CCSCBCTL, CCSCBEN|CCSCBRESET; - cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .; - } else { - mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; - cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; - } - } -dma_scb_finish: - clr CCSCBCTL; - test CCSCBCTL, CCARREN|CCSCBEN jnz .; - ret; - } - if ((p->features & AHC_CMD_CHAN) == 0) { - mvi DINDEX, HADDR; - mvi HSCB_ADDR call set_32byte_addr; - mvi HCNT[0], 32; - clr HCNT[1]; - clr HCNT[2]; - mov DFCNTRL, DMAPARAMS; - test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; - /* Fill it with the SCB data */ -copy_scb_tofifo: - mvi SINDEX, SCB_CONTROL; - add A, 32, SINDEX; -copy_scb_tofifo_loop: - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - cmp SINDEX, A jne copy_scb_tofifo_loop; - or DFCNTRL, HDMAEN|FIFOFLUSH; - jmp dma_finish; -dma_scb_fromhost: - mvi DINDEX, SCB_CONTROL; - if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) { - /* - * Set the A to -24. It it hits 0, then we let - * our code fall through to dfdat_in_8 to complete - * the last of the copy. - * - * Also, things happen 8 bytes at a time in this - * case, so we may need to drain the fifo at most - * 3 times to keep things flowing - */ - mvi A, -24; -dma_scb_hang_fifo: - /* Wait for the first bit of data to hit the fifo */ - test DFSTATUS, FIFOEMP jnz .; -dma_scb_hang_wait: - /* OK, now they've started to transfer into the fifo, - * so wait for them to stop trying to transfer any - * more data. - */ - test DFSTATUS, MREQPEND jnz .; - /* - * OK, they started, then they stopped, now see if they - * managed to complete the job before stopping. Try - * it multiple times to give the chip a few cycles to - * set the flag if it did complete. - */ - test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; - test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; - test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; - /* - * Too bad, the chip didn't complete the DMA, but there - * aren't any more memory requests pending, so that - * means it stopped part way through and hung. That's - * our bug, so now we drain what data there is in the - * fifo in order to get things going again. - */ -dma_scb_hang_empty_fifo: - call dfdat_in_8; - add A, 8; - add SINDEX, A, HCNT; - /* - * If there are another 8 bytes of data waiting in the - * fifo, then the carry bit will be set as a result - * of the above add command (unless A is non-negative, - * in which case the carry bit won't be set). - */ - jc dma_scb_hang_empty_fifo; - /* - * We've emptied the fifo now, but we wouldn't have got - * here if the memory transfer hadn't stopped part way - * through, so go back up to the beginning of the - * loop and start over. When it succeeds in getting - * all the data down, HDONE will be set and we'll - * jump to the code just below here. - */ - jmp dma_scb_hang_fifo; -dma_scb_hang_dma_done: - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - call dfdat_in_8; - add A, 8; - cmp A, 8 jne . - 2; - ret; - } else { - call dma_finish; - call dfdat_in_8; - call dfdat_in_8; - call dfdat_in_8; - } -dfdat_in_8: - mov DINDIR,DFDAT; -dfdat_in_7: - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT ret; - } - - -/* - * Wait for DMA from host memory to data FIFO to complete, then disable - * DMA and wait for it to acknowledge that it's off. - */ -if ((p->features & AHC_CMD_CHAN) == 0) { -dma_finish: - test DFSTATUS,HDONE jz dma_finish; - /* Turn off DMA */ - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - ret; -} - -add_scb_to_free_list: - if ((p->flags & AHC_PAGESCBS) != 0) { - mov SCB_NEXT, FREE_SCBH; - mov FREE_SCBH, SCBPTR; - } - mvi SCB_TAG, SCB_LIST_NULL ret; - -if ((p->flags & AHC_PAGESCBS) != 0) { -get_free_or_disc_scb: - cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; - cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; -return_error: - mvi SINDEX, SCB_LIST_NULL ret; -dequeue_disc_scb: - mov SCBPTR, DISCONNECTED_SCBH; -dma_up_scb: - mvi DMAPARAMS, FIFORESET; - mov SCB_TAG call dma_scb; -unlink_disc_scb: - mov DISCONNECTED_SCBH, SCB_NEXT ret; -dequeue_free_scb: - mov SCBPTR, FREE_SCBH; - mov FREE_SCBH, SCB_NEXT ret; -} - -add_scb_to_disc_list: -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mov SCB_NEXT, DISCONNECTED_SCBH; - mov DISCONNECTED_SCBH, SCBPTR ret; |