📄 aic7xxx.seq
字号:
/* * 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 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 everytime 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;/* * 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. */data_phase_reinit: 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; /* We have seen a data phase */ or SEQ_FLAGS, DPHASE; /* * 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 STCNT, HCNT, 3; bmov SG_COUNT, SCB_SGCOUNT, 5; } 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_CMD_CHAN) != 0) { if ((p->features & AHC_ULTRA2) != 0) { bmov HCNT, ALLONES, 3; } bmov STCNT, ALLONES, 3; } else { 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. */ cmp SG_COUNT,0x01 jne data_phase_wideodd; if ((p->features & AHC_ULTRA2) == 0) { and DMAPARAMS, ~WIDEODD; } else { mvi SG_CACHEPTR, LAST_SEG; }data_phase_wideodd: if ((p->features & AHC_ULTRA2) != 0) { mov SINDEX, ALLONES; mov DFCNTRL, DMAPARAMS; test SSTAT0, SDONE jnz .;data_phase_dma_loop: test SSTAT0, SDONE jnz data_phase_dma_done; test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */data_phase_dma_phasemis: test SSTAT0,SDONE jnz data_phase_dma_done; clr SINDEX; /* Remember the phasemiss */ } 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 if needed */sg_advance: dec SG_COUNT; /* one less segment to go */ test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? *//* * Load a struct scatter and set up the data address and length. * If the working value of the SG count is nonzero, then * we need to load a new set of values. * * This, like all DMA's, assumes little-endian host data storage. */sg_load: if ((p->features & AHC_CMD_CHAN) != 0) { /* * Do we have any prefetch left??? */ cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -