aic7xxx.seq revision 41816
126997Sgibbs/*
226997Sgibbs * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
313177Sgibbs *
439220Sgibbs * Copyright (c) 1994-1998 Justin Gibbs.
526997Sgibbs * All rights reserved.
613177Sgibbs *
726997Sgibbs * Redistribution and use in source and binary forms, with or without
826997Sgibbs * modification, are permitted provided that the following conditions
926997Sgibbs * are met:
1026997Sgibbs * 1. Redistributions of source code must retain the above copyright
1126997Sgibbs *    notice, this list of conditions, and the following disclaimer,
1226997Sgibbs *    without modification, immediately at the beginning of the file.
1339220Sgibbs * 2. The name of the author may not be used to endorse or promote products
1426997Sgibbs *    derived from this software without specific prior written permission.
1513177Sgibbs *
1626997Sgibbs * Where this Software is combined with software released under the terms of 
1739220Sgibbs * the GNU Public License (GPL) and the terms of the GPL would require the 
1826997Sgibbs * combined work to also be released under the terms of the GPL, the terms
1926997Sgibbs * and conditions of this License will apply in addition to those of the
2026997Sgibbs * GPL with the exception of any terms or conditions of this License that
2126997Sgibbs * conflict with, or are expressly prohibited by, the GPL.
2213177Sgibbs *
2326997Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2426997Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2526997Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2626997Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2726997Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2826997Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2926997Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3026997Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3126997Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3226997Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3326997Sgibbs * SUCH DAMAGE.
3413177Sgibbs *
3541816Sgibbs *	$Id: aic7xxx.seq,v 1.81 1998/12/10 04:14:50 gibbs Exp $
3626997Sgibbs */
374568Sgibbs
3823925Sgibbs#include <dev/aic7xxx/aic7xxx.reg>
3939220Sgibbs#include <cam/scsi/scsi_message.h>
405647Sgibbs
4113177Sgibbs/*
4219164Sgibbs * A few words on the waiting SCB list:
4319164Sgibbs * After starting the selection hardware, we check for reconnecting targets
4413690Sgibbs * as well as for our selection to complete just in case the reselection wins
4513690Sgibbs * bus arbitration.  The problem with this is that we must keep track of the
4613690Sgibbs * SCB that we've already pulled from the QINFIFO and started the selection
4713690Sgibbs * on just in case the reselection wins so that we can retry the selection at
4813690Sgibbs * a later time.  This problem cannot be resolved by holding a single entry
4913690Sgibbs * in scratch ram since a reconnecting target can request sense and this will
5013690Sgibbs * create yet another SCB waiting for selection.  The solution used here is to 
5113690Sgibbs * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
5219164Sgibbs * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
5319164Sgibbs * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
5419164Sgibbs * this list everytime a request sense occurs or after completing a non-tagged
5519164Sgibbs * command for which a second SCB has been queued.  The sequencer will
5619164Sgibbs * automatically consume the entries.
5713177Sgibbs */
584568Sgibbs
5914449Sgibbsreset:
6023925Sgibbs	clr	SCSISIGO;		/* De-assert BSY */
6141646Sgibbs	and	SXFRCTL1, ~BITBUCKET;
6223925Sgibbs	/* Always allow reselection */
6341816Sgibbs	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
6439220Sgibbs
6539220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
6639220Sgibbs		/* Ensure that no DMA operations are in progress */
6739220Sgibbs		clr	CCSGCTL;
6839220Sgibbs		clr	CCSCBCTL;
6939220Sgibbs	}
7039220Sgibbs
7123925Sgibbs	call	clear_target_state;
728104Sgibbspoll_for_work:
7339220Sgibbs	and	SXFRCTL0, ~SPIOEN;
7439220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
7539220Sgibbs		mov	A, QINPOS;
7639220Sgibbs	}
7739220Sgibbspoll_for_work_loop:
7839220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
7939220Sgibbs		and	SEQCTL, ~PAUSEDIS;
8039220Sgibbs	}
8139220Sgibbs	test	SSTAT0, SELDO|SELDI	jnz selection;
8223925Sgibbs	test	SCSISEQ, ENSELO	jnz poll_for_work;
8339220Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
8439220Sgibbs		/*
8539220Sgibbs		 * Twin channel devices cannot handle things like SELTO
8639220Sgibbs		 * interrupts on the "background" channel.  So, if we
8739220Sgibbs		 * are selecting, keep polling the current channel util
8839220Sgibbs		 * either a selection or reselection occurs.
8939220Sgibbs		 */
9039220Sgibbs		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
9139220Sgibbs		test	SSTAT0, SELDO|SELDI	jnz selection;
9239220Sgibbs		test	SCSISEQ, ENSELO	jnz poll_for_work;
9339220Sgibbs		xor	SBLKCTL,SELBUSB;	/* Toggle back */
9439220Sgibbs	}
9523925Sgibbs	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
9619164Sgibbstest_queue:
9719164Sgibbs	/* Has the driver posted any work for us? */
9839220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
9939220Sgibbs		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
10039220Sgibbs		mov	NONE, SNSCB_QOFF;
10139220Sgibbs		inc	QINPOS;
10239220Sgibbs	} else {
10339220Sgibbs		or	SEQCTL, PAUSEDIS;
10439220Sgibbs		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
10539220Sgibbs		inc	QINPOS;
10639220Sgibbs		and	SEQCTL, ~PAUSEDIS;
10739220Sgibbs	}
1084568Sgibbs
10913690Sgibbs/*
11013690Sgibbs * We have at least one queued SCB now and we don't have any 
11119164Sgibbs * SCBs in the list of SCBs awaiting selection.  If we have
11223925Sgibbs * any SCBs available for use, pull the tag from the QINFIFO
11319164Sgibbs * and get to work on it.
11413177Sgibbs */
11539220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
11639220Sgibbs		mov	ALLZEROS	call	get_free_or_disc_scb;
11739220Sgibbs	}
11839220Sgibbs
11919164Sgibbsdequeue_scb:
12039220Sgibbs	add	A, -1, QINPOS;
12139220Sgibbs	mvi	QINFIFO_OFFSET call fetch_byte;
12239220Sgibbs
12339220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) == 0) {
12439220Sgibbs		/* In the non-paging case, the SCBID == hardware SCB index */
12539220Sgibbs		mov	SCBPTR, RETURN_2;
12639220Sgibbs	}
12719164Sgibbsdma_queued_scb:
12819164Sgibbs/*
12919164Sgibbs * DMA the SCB from host ram into the current SCB location.
13019164Sgibbs */
13123925Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
13239220Sgibbs	mov	RETURN_2	 call dma_scb;
1334568Sgibbs
13413177Sgibbs/*
13539220Sgibbs * Preset the residual fields in case we never go through a data phase.
13639220Sgibbs * This isn't done by the host so we can avoid a DMA to clear these
13739220Sgibbs * fields for the normal case of I/O that completes without underrun
13839220Sgibbs * or overrun conditions.
13913177Sgibbs */
14039220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
14139220Sgibbs		bmov	SCB_RESID_DCNT, SCB_DATACNT, 3;
14239220Sgibbs	} else {
14339220Sgibbs		mov	SCB_RESID_DCNT[0],SCB_DATACNT[0];
14439220Sgibbs		mov	SCB_RESID_DCNT[1],SCB_DATACNT[1];
14539220Sgibbs		mov	SCB_RESID_DCNT[2],SCB_DATACNT[2];
14639220Sgibbs	}
14739220Sgibbs	mov	SCB_RESID_SGCNT, SCB_SGCOUNT;
1484568Sgibbs
1495326Sgibbsstart_scb:
15019164Sgibbs	/*
15119164Sgibbs	 * Place us on the waiting list in case our selection
15219164Sgibbs	 * doesn't win during bus arbitration.
15319164Sgibbs	 */
15423925Sgibbs	mov	SCB_NEXT,WAITING_SCBH;
15523925Sgibbs	mov	WAITING_SCBH, SCBPTR;
15623925Sgibbsstart_waiting:
15723925Sgibbs	/*
15839220Sgibbs	 * Pull the first entry off of the waiting SCB list.
15923925Sgibbs	 */
16023925Sgibbs	mov	SCBPTR, WAITING_SCBH;
16123925Sgibbs	call	start_selection;
16223925Sgibbs	jmp	poll_for_work;
1638104Sgibbs
16423925Sgibbsstart_selection:
16539220Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
16639220Sgibbs		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
16739220Sgibbs		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
16839220Sgibbs		or	SINDEX,A;
16939220Sgibbs		mov	SBLKCTL,SINDEX;		/* select channel */
17039220Sgibbs	}
17123925Sgibbsinitialize_scsiid:
17239220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
17339220Sgibbs		and	A, TID, SCB_TCL;	/* Get target ID */
17439220Sgibbs		and	SCSIID_ULTRA2, OID;	/* Clear old target */
17539220Sgibbs		or	SCSIID_ULTRA2, A;
17639220Sgibbs	} else {
17739220Sgibbs		and	A, TID, SCB_TCL;	/* Get target ID */
17839220Sgibbs		and	SCSIID, OID;		/* Clear old target */
17939220Sgibbs		or	SCSIID, A;
18039220Sgibbs	}
18141816Sgibbs	mov	SINDEX, SCSISEQ_TEMPLATE;
18239220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
18341816Sgibbs		test	SCB_CONTROL, TARGET_SCB jz . + 2;
18441816Sgibbs		or	SINDEX, TEMODE;
18539220Sgibbs	}
18641816Sgibbs	mov	SCSISEQ, SINDEX ret;
18739220Sgibbs
18813177Sgibbs/*
18939220Sgibbs * Initialize transfer settings and clear the SCSI channel.
19039220Sgibbs * SINDEX should contain any additional bit's the client wants
19139220Sgibbs * set in SXFRCTL0.  We also assume that the current SCB is
19239220Sgibbs * a valid SCB for the target we wish to talk to.
19339220Sgibbs */
19439220Sgibbsinitialize_channel:
19539220Sgibbs	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
19639220Sgibbsset_transfer_settings:
19739220Sgibbs	if ((ahc->features & AHC_ULTRA) != 0) {
19839220Sgibbs		test	SCB_CONTROL, ULTRAENB jz . + 2;
19939220Sgibbs		or	SXFRCTL0, FAST20;
20039220Sgibbs	} 
20139220Sgibbs/*
20239220Sgibbs * Initialize SCSIRATE with the appropriate value for this target.
20339220Sgibbs */
20439220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
20539220Sgibbs		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
20639220Sgibbs	} else {
20739220Sgibbs		mov	SCSIRATE, SCB_SCSIRATE ret;
20839220Sgibbs	}
20939220Sgibbs
21039220Sgibbsselection:
21139220Sgibbs	test	SSTAT0,SELDO	jnz select_out;
21239220Sgibbs	mvi	CLRSINT0, CLRSELDI;
21339220Sgibbsselect_in:
21439220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
21541646Sgibbs		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
21641646Sgibbs			test	SSTAT0, TARGET	jz initiator_reselect;
21741646Sgibbs		}
21839220Sgibbs		/*
21939220Sgibbs		 * We've just been selected.  Assert BSY and
22039220Sgibbs		 * setup the phase for receiving messages
22139220Sgibbs		 * from the target.
22239220Sgibbs		 */
22341646Sgibbs		
22439220Sgibbs		mvi	SCSISIGO, P_MESGOUT|BSYO;
22541646Sgibbs		mvi	CLRSINT1, CLRBUSFREE;
22639220Sgibbs
22739220Sgibbs		/*
22839220Sgibbs		 * LAST_MSG gives an indication to the host of what
22939220Sgibbs		 * went wrong should we need to terminate this selection
23039220Sgibbs		 * before doing real work.  Initialize it to SCB_LIST_NULL to
23139220Sgibbs		 * indicate an improper initiator selection.
23239220Sgibbs		 */
23339220Sgibbs		mvi	LAST_MSG, SCB_LIST_NULL;
23439220Sgibbs
23539220Sgibbs		/*
23639220Sgibbs		 * Setup the DMA for sending the identify and
23741299Sgibbs		 * command information.
23839220Sgibbs		 */
23939220Sgibbs		or	SEQ_FLAGS, CMDPHASE_PENDING;
24041299Sgibbs
24141299Sgibbs		/* XXX If ring buffer is full, return busy or queue full */
24241299Sgibbs		mov     A, TQINPOS;
24339220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
24439220Sgibbs			mvi	DINDEX, CCHADDR;
24539220Sgibbs			mvi	TMODE_CMDADDR call set_32byte_addr;
24639220Sgibbs			mvi	CCSCBCTL, CCSCBRESET;
24739220Sgibbs		} else {
24839220Sgibbs			mvi	DINDEX, HADDR;
24939220Sgibbs			mvi	TMODE_CMDADDR call set_32byte_addr;
25039220Sgibbs			mvi	DFCNTRL, FIFORESET;
25139220Sgibbs		}
25239220Sgibbs
25339220Sgibbs		/* Initiator that selected us */
25439220Sgibbs		and	SAVED_TCL, SELID_MASK, SELID;
25539220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
25639220Sgibbs			mov	CCSCBRAM, SAVED_TCL;
25739220Sgibbs		} else {
25839220Sgibbs			mov	DFDAT, SAVED_TCL;
25939220Sgibbs		}
26039220Sgibbs
26139220Sgibbs		/* The Target ID we were selected at */
26239220Sgibbs		if ((ahc->features & AHC_MULTI_TID) != 0) {
26339220Sgibbs			if ((ahc->features & AHC_CMD_CHAN) != 0) {
26439220Sgibbs				and	CCSCBRAM, 0x0f, TARGIDIN;
26539220Sgibbs			} else {
26639220Sgibbs				and	DFDAT, 0x0f, TARGIDIN;
26739220Sgibbs			}
26839220Sgibbs		} else {
26939220Sgibbs			if ((ahc->features & AHC_CMD_CHAN) != 0) {
27039220Sgibbs				and	CCSCBRAM, OID, SCSIID;
27139220Sgibbs			} else {
27239220Sgibbs				and	DFDAT, OID, SCSIID;
27339220Sgibbs			}
27439220Sgibbs		}
27539220Sgibbs
27639220Sgibbs		/*
27739220Sgibbs		 * If ATN isn't asserted, the target isn't interested
27839220Sgibbs		 * in talking to us.  Go directly to bus free.
27939220Sgibbs		 */
28039220Sgibbs		test	SCSISIGI, ATNI	jz	target_busfree;
28139220Sgibbs
28239220Sgibbs		/*
28339220Sgibbs		 * Watch ATN closely now as we pull in messages from the
28439220Sgibbs		 * initiator.  We follow the guidlines from section 6.5
28539220Sgibbs		 * of the SCSI-2 spec for what messages are allowed when.
28639220Sgibbs		 */
28741646Sgibbs		call	target_inb;
28839220Sgibbs
28939220Sgibbs		/*
29039220Sgibbs		 * Our first message must be one of IDENTIFY, ABORT, or
29139220Sgibbs		 * BUS_DEVICE_RESET.
29239220Sgibbs		 */
29341299Sgibbs		/* XXX May need to be more lax here for older initiators... */
29439220Sgibbs		test	DINDEX, MSG_IDENTIFYFLAG jz more_first_messages;
29539220Sgibbs		/* Store for host */
29639220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
29739220Sgibbs			mov	CCSCBRAM, DINDEX;
29839220Sgibbs		} else {
29939220Sgibbs			mov	DFDAT, DINDEX;
30039220Sgibbs		}
30139220Sgibbs
30239220Sgibbs		/* Remember for disconnection decision */
30339220Sgibbs		test	DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
30439220Sgibbs		/* XXX Honor per target settings too */
30539220Sgibbs		or	SEQ_FLAGS, NO_DISCONNECT;
30639220Sgibbs
30739220Sgibbs		test	SCSISIGI, ATNI	jz	ident_messages_done;
30841646Sgibbs		call	target_inb;
30939220Sgibbs		/*
31039220Sgibbs		 * If this is a tagged request, the tagged message must
31139220Sgibbs		 * immediately follow the identify.  We test for a valid
31239220Sgibbs		 * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
31339220Sgibbs		 * < MSG_IGN_WIDE_RESIDUE.
31439220Sgibbs		 */
31539220Sgibbs		add	A, -MSG_SIMPLE_Q_TAG, DINDEX;
31639220Sgibbs		jnc	ident_messages_done;
31739220Sgibbs		add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
31839220Sgibbs		jc	ident_messages_done;
31939220Sgibbs		/* Store for host */
32039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
32139220Sgibbs			mov	CCSCBRAM, DINDEX;
32239220Sgibbs		} else {
32339220Sgibbs			mov	DFDAT, DINDEX;
32439220Sgibbs		}
32539220Sgibbs		
32639220Sgibbs		/*
32739220Sgibbs		 * If the initiator doesn't feel like providing a tag number,
32839220Sgibbs		 * we've got a failed selection and must transition to bus
32939220Sgibbs		 * free.
33039220Sgibbs		 */
33139220Sgibbs		test	SCSISIGI, ATNI	jz	target_busfree;
33239220Sgibbs		/*
33339220Sgibbs		 * Store the tag for the host.
33439220Sgibbs		 */
33541646Sgibbs		call	target_inb;
33639220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
33739220Sgibbs			mov	CCSCBRAM, DINDEX;
33839220Sgibbs		} else {
33939220Sgibbs			mov	DFDAT, DINDEX;
34039220Sgibbs		}
34139220Sgibbs		jmp	ident_messages_done;
34239220Sgibbs
34341646Sgibbs		/*
34441646Sgibbs		 * Pushed message loop to allow the kernel to
34541646Sgibbs		 * run it's own message state engine.  To avoid an
34641646Sgibbs		 * extra nop instruction after signaling the kernel,
34741646Sgibbs		 * we perform the phase_lock before checking to see
34841646Sgibbs		 * if we should exit the loop and skip the phase_lock
34941646Sgibbs		 * in the ITloop.  Performing back to back phase_locks
35041646Sgibbs		 * shouldn't hurt, but why do it twice...
35141646Sgibbs		 */
35241646Sgibbshost_target_message_loop:
35341646Sgibbs		mvi	INTSTAT, HOST_MSG_LOOP;
35441646Sgibbs		nop;
35541646Sgibbs		cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
35641646Sgibbs		test	SSTAT0, SPIORDY jz .;
35741646Sgibbs		jmp	host_target_message_loop;
35841646Sgibbs
35939220Sgibbsmore_first_messages:
36039220Sgibbs		/*
36139220Sgibbs		 * Hmm.  Now we're down to only accepting
36239220Sgibbs		 * either an ABORT or BDR. 
36339220Sgibbs		 */
36439220Sgibbs		cmp	DINDEX, MSG_ABORT		je . + 2;
36539220Sgibbs		cmp	DINDEX, MSG_BUS_DEV_RESET	jne target_busfree;
36639220Sgibbs
36739220Sgibbs		/* Record the event and notify the host */
36839220Sgibbs		mov	LAST_MSG,  DINDEX;
36939220Sgibbs		jmp	target_busfree;
37039220Sgibbs
37139220Sgibbsident_messages_done:
37239220Sgibbs		mvi	LAST_MSG, MSG_NOOP;	/* We are so far successful */
37339220Sgibbs		/* Terminate the ident list */
37439220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
37539220Sgibbs			mvi	CCSCBRAM, SCB_LIST_NULL;
37639220Sgibbs		} else {
37739220Sgibbs			mvi	DFDAT, SCB_LIST_NULL;
37839220Sgibbs		}
37939220Sgibbs		or	SEQ_FLAGS, TARG_CMD_PENDING;
38041646Sgibbs		test	SCSISIGI, ATNI	jnz target_mesgout_pending_msg;
38139220Sgibbs		jmp	target_ITloop;
38239220Sgibbs		
38339220Sgibbs/*
38439220Sgibbs * We carefully toggle SPIOEN to allow us to return the 
38539220Sgibbs * message byte we receive so it can be checked prior to
38639220Sgibbs * driving REQ on the bus for the next byte.
38739220Sgibbs */
38841646Sgibbstarget_inb:
38941646Sgibbs		/*
39041646Sgibbs		 * Drive REQ on the bus by enabling SCSI PIO.
39141646Sgibbs		 */
39239220Sgibbs		or	SXFRCTL0, SPIOEN;
39339220Sgibbs		/* Wait for the byte */
39439220Sgibbs		test	SSTAT0, SPIORDY jz .;
39539220Sgibbs		/* Prevent our read from triggering another REQ */
39639220Sgibbs		and	SXFRCTL0, ~SPIOEN;
39741646Sgibbs		/* Save latched contents */
39839220Sgibbs		mov	DINDEX, SCSIDATL ret;
39939220Sgibbs	}
40039220Sgibbs
40141646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
40239220Sgibbs/*
40323925Sgibbs * Reselection has been initiated by a target. Make a note that we've been
40423925Sgibbs * reselected, but haven't seen an IDENTIFY message from the target yet.
40513177Sgibbs */
40639220Sgibbsinitiator_reselect:
40723925Sgibbs	/* XXX test for and handle ONE BIT condition */
40823925Sgibbs	and	SAVED_TCL, SELID_MASK, SELID;
40939545Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
41039545Sgibbs		test	SBLKCTL, SELBUSB	jz . + 2;
41139545Sgibbs		or	SAVED_TCL, SELBUSB;
41239545Sgibbs	}
41341646Sgibbs	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
41439220Sgibbs	mvi	CLRSINT1,CLRBUSFREE;
41539220Sgibbs	or	SIMODE1, ENBUSFREE;		/*
41639220Sgibbs						 * We aren't expecting a
41739220Sgibbs						 * bus free, so interrupt
41839220Sgibbs						 * the kernel driver if it
41939220Sgibbs						 * happens.
42039220Sgibbs						 */
42139220Sgibbs	mvi	MSG_OUT, MSG_NOOP;		/* No message to send */
42239220Sgibbs	jmp	ITloop;
42341646Sgibbs}
4244568Sgibbs
42513177Sgibbs/*
42623925Sgibbs * After the selection, remove this SCB from the "waiting SCB"
42723925Sgibbs * list.  This is achieved by simply moving our "next" pointer into
42823925Sgibbs * WAITING_SCBH.  Our next pointer will be set to null the next time this
42923925Sgibbs * SCB is used, so don't bother with it now.
43023925Sgibbs */
43139220Sgibbsselect_out:
43225005Sgibbs	/* Turn off the selection hardware */
43341816Sgibbs	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
43425005Sgibbs	mvi	CLRSINT0, CLRSELDO;
43525005Sgibbs	mov	SCBPTR, WAITING_SCBH;
43624914Sgibbs	mov	WAITING_SCBH,SCB_NEXT;
43723925Sgibbs	mov	SAVED_TCL, SCB_TCL;
43839220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
43939220Sgibbs		test	SSTAT0, TARGET	jz initiator_select;
4408567Sdg
44139220Sgibbs		/*
44239220Sgibbs		 * We've just re-selected an initiator.
44339220Sgibbs		 * Assert BSY and setup the phase for
44439220Sgibbs		 * sending our identify messages.
44539220Sgibbs		 */
44641646Sgibbs		mvi	P_MESGIN|BSYO call change_phase;
44741646Sgibbs		mvi	CLRSINT1,CLRBUSFREE;
4484568Sgibbs
44939220Sgibbs		/*
45039220Sgibbs		 * Start out with a simple identify message.
45139220Sgibbs		 */
45239220Sgibbs		and	A, LID, SCB_TCL;
45339220Sgibbs		or	A, MSG_IDENTIFYFLAG call target_outb;
4546608Sgibbs
45539220Sgibbs		/*
45639220Sgibbs		 * If we are the result of a tagged command, send
45739220Sgibbs		 * a simple Q tag and the tag id.
45839220Sgibbs		 */
45939220Sgibbs		test	SCB_CONTROL, TAG_ENB	jz . + 3;
46039220Sgibbs		mvi	MSG_SIMPLE_Q_TAG call target_outb;
46139220Sgibbs		mov	SCB_TAG call target_outb;
46239220Sgibbstarget_synccmd:
46339220Sgibbs		/*
46439220Sgibbs		 * Now determine what phases the host wants us
46539220Sgibbs		 * to go through.
46639220Sgibbs		 */
46739220Sgibbs		mov	SEQ_FLAGS, SCB_TARGET_PHASES;
46839220Sgibbs
46939220Sgibbstarget_ITloop:
47039220Sgibbs		/*
47141646Sgibbs		 * Start honoring ATN signals now that
47241646Sgibbs		 * we properly identified ourself.
47339220Sgibbs		 */
47441646Sgibbs		test	SCSISIGI, ATNI			jnz target_mesgout;
47539220Sgibbs		test	SEQ_FLAGS, CMDPHASE_PENDING	jnz target_cmdphase;
47639220Sgibbs		test	SEQ_FLAGS, DPHASE_PENDING	jnz target_dphase;
47739220Sgibbs		test	SEQ_FLAGS, SPHASE_PENDING	jnz target_sphase;
47839220Sgibbs
47939220Sgibbs		/*
48039220Sgibbs		 * No more work to do.  Either disconnect or not depending
48139220Sgibbs		 * on the state of NO_DISCONNECT.
48239220Sgibbs		 */
48339220Sgibbs		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
48439220Sgibbs		if ((ahc->flags & AHC_PAGESCBS) != 0) {
48539220Sgibbs			mov	ALLZEROS	call	get_free_or_disc_scb;
48639220Sgibbs		}
48741646Sgibbs		mov	RETURN_1, ALLZEROS;
48839220Sgibbs		call	complete_target_cmd;
48941646Sgibbs		cmp	RETURN_1, CONT_MSG_LOOP jne .;
49039220Sgibbs		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
49139220Sgibbs		mov	SCB_TAG	 call dma_scb;
49239220Sgibbs		jmp	target_synccmd;
49339220Sgibbs
49441646Sgibbstarget_mesgout:
49541646Sgibbs		mvi	SCSISIGO, P_MESGOUT|BSYO;
49641646Sgibbs		call	target_inb;
49741646Sgibbs		/* Local Processing goes here... */
49841646Sgibbstarget_mesgout_pending_msg:
49941646Sgibbs		jmp	host_target_message_loop;
50041646Sgibbs		
50139220Sgibbstarget_disconnect:
50241646Sgibbs		mvi	P_MESGIN|BSYO call change_phase;
50341816Sgibbs		test	SEQ_FLAGS, DPHASE	jz . + 2;
50441816Sgibbs		mvi	MSG_SAVEDATAPOINTER call target_outb;
50539220Sgibbs		mvi	MSG_DISCONNECT call target_outb;
50639220Sgibbs
50739220Sgibbstarget_busfree:
50839220Sgibbs		clr	SCSISIGO;
50939220Sgibbs		call	complete_target_cmd;
51039220Sgibbs		cmp	LAST_MSG, MSG_NOOP	je . + 2;
51139220Sgibbs		mvi	INTSTAT, TARGET_MSG_HELP;
51239220Sgibbs		call	clear_target_state;
51339220Sgibbs		jmp	poll_for_work;
51439220Sgibbs
51539220Sgibbstarget_cmdphase:
51641646Sgibbs		mvi	P_COMMAND|BSYO call change_phase;
51741646Sgibbs		call	target_inb;
51839220Sgibbs		mov	A, DINDEX;
51939220Sgibbs		/* Store for host */
52039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
52139220Sgibbs			mov	CCSCBRAM, A;
52239220Sgibbs		} else {
52339220Sgibbs			mov	DFDAT, A;
52439220Sgibbs		}
52539220Sgibbs
52639220Sgibbs		/*
52739220Sgibbs		 * Determine the number of bytes to read
52841299Sgibbs		 * based on the command group code via table lookup.
52941299Sgibbs		 * We reuse the first 8 bytes of the TARG_SCSIRATE
53041299Sgibbs		 * BIOS array for this table. Count is one less than
53141299Sgibbs		 * the total for the command since we've already fetched
53241299Sgibbs		 * the first byte.
53339220Sgibbs		 */
53439220Sgibbs		shr	A, CMD_GROUP_CODE_SHIFT;
53539220Sgibbs		add	SINDEX, TARG_SCSIRATE, A;
53639220Sgibbs		mov	A, SINDIR;
53739220Sgibbs
53839220Sgibbs		test	A, 0xFF jz command_phase_done;
53939220Sgibbscommand_loop:
54039220Sgibbs		or	SXFRCTL0, SPIOEN;
54139220Sgibbs		test	SSTAT0, SPIORDY jz .;
54239220Sgibbs		cmp	A, 1 jne . + 2;
54339220Sgibbs		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
54439220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
54539220Sgibbs			mov	CCSCBRAM, SCSIDATL;
54639220Sgibbs		} else {
54739220Sgibbs			mov	DFDAT, SCSIDATL;
54839220Sgibbs		}
54939220Sgibbs		dec	A;
55039220Sgibbs		test	A, 0xFF jnz command_loop;
55139220Sgibbs
55239220Sgibbscommand_phase_done:
55339220Sgibbs		and	SEQ_FLAGS, ~CMDPHASE_PENDING;
55439220Sgibbs		jmp	target_ITloop;
55539220Sgibbs
55639220Sgibbstarget_dphase:
55739220Sgibbs		/*
55839220Sgibbs		 * Data direction flags are from the
55939220Sgibbs		 * perspective of the initiator.
56039220Sgibbs		 */
56141646Sgibbs		mov	ALLZEROS call initialize_channel;
56239220Sgibbs		test	SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
56339220Sgibbs		mvi	LASTPHASE, P_DATAOUT;
56441646Sgibbs		mvi	P_DATAIN|BSYO call change_phase;
56539220Sgibbs		jmp	p_data;
56639220Sgibbs		mvi	LASTPHASE, P_DATAIN;
56741646Sgibbs		mvi	P_DATAOUT|BSYO call change_phase;
56839220Sgibbs		jmp	p_data;
56939220Sgibbs
57039220Sgibbstarget_sphase:
57141646Sgibbs		mvi	P_STATUS|BSYO call change_phase;
57241646Sgibbs		mvi	LASTPHASE, P_STATUS;
57339220Sgibbs		mov	SCB_TARGET_STATUS call target_outb;
57441646Sgibbs		/* XXX Watch for ATN or parity errors??? */
57539220Sgibbs		mvi	SCSISIGO, P_MESGIN|BSYO;
57639220Sgibbs		/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
57739220Sgibbs		mov	ALLZEROS call target_outb;
57839220Sgibbs		jmp	target_busfree;
57939220Sgibbs	
58039220Sgibbscomplete_target_cmd:
58139220Sgibbs		test	SEQ_FLAGS, TARG_CMD_PENDING	jnz . + 2;
58239220Sgibbs		mov	SCB_TAG jmp complete_post;
58339220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
58441299Sgibbs			/* Set the valid byte */
58541299Sgibbs			mvi	CCSCBADDR, 24;
58641299Sgibbs			mov	CCSCBRAM, ALLONES;
58741299Sgibbs			mvi	CCHCNT, 28;
58839220Sgibbs			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
58939220Sgibbs			test	CCSCBCTL, CCSCBDONE jz .;
59039220Sgibbs			clr	CCSCBCTL;
59139220Sgibbs		} else {
59241299Sgibbs			/* Set the valid byte */
59341299Sgibbs			or	DFCNTRL, FIFORESET;
59441299Sgibbs			mvi	DFWADDR, 3; /* Third 64bit word or byte 24 */
59541299Sgibbs			mov	DFDAT, ALLONES;
59641299Sgibbs			mvi	HCNT[0], 28;
59741299Sgibbs			clr	HCNT[1];
59841299Sgibbs			clr	HCNT[2];
59939220Sgibbs			or	DFCNTRL, HDMAEN|FIFOFLUSH;
60039220Sgibbs			call	dma_finish;
60139220Sgibbs		}
60241299Sgibbs		inc	TQINPOS;
60341299Sgibbs		mvi	INTSTAT,CMDCMPLT ret;
60439220Sgibbs	}
60541646Sgibbs
60641646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
60739220Sgibbsinitiator_select:
60839220Sgibbs	mvi	SPIOEN call	initialize_channel;
60941646Sgibbs
61041646Sgibbs	/*
61141646Sgibbs	 * We aren't expecting a bus free, so interrupt
61241646Sgibbs	 * the kernel driver if it happens.
61341646Sgibbs	 */
61425005Sgibbs	mvi	CLRSINT1,CLRBUSFREE;
61539220Sgibbs	or	SIMODE1, ENBUSFREE;
61641646Sgibbs
61741646Sgibbs	/*
61841646Sgibbs	 * As soon as we get a successful selection, the target
61941646Sgibbs	 * should go into the message out phase since we have ATN
62041646Sgibbs	 * asserted.
62141646Sgibbs	 */
62239220Sgibbs	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
62339220Sgibbs	or	SEQ_FLAGS, IDENTIFY_SEEN;
62413177Sgibbs
62541646Sgibbs	/*
62641646Sgibbs	 * Main loop for information transfer phases.  Wait for the
62741646Sgibbs	 * target to assert REQ before checking MSG, C/D and I/O for
62841646Sgibbs	 * the bus phase.
62941646Sgibbs	 */
6304568SgibbsITloop:
63139220Sgibbs	call	phase_lock;
6324568Sgibbs
63339220Sgibbs	mov	A, LASTPHASE;
6344568Sgibbs
63539220Sgibbs	test	A, ~P_DATAIN	jz p_data;
63623925Sgibbs	cmp	A,P_COMMAND	je p_command;
63723925Sgibbs	cmp	A,P_MESGOUT	je p_mesgout;
63823925Sgibbs	cmp	A,P_STATUS	je p_status;
63923925Sgibbs	cmp	A,P_MESGIN	je p_mesgin;
6404568Sgibbs
64141646Sgibbs	mvi	INTSTAT,BAD_PHASE;
64223925Sgibbs	jmp	ITloop;			/* Try reading the bus again. */
6434568Sgibbs
64423925Sgibbsawait_busfree:
64523925Sgibbs	and	SIMODE1, ~ENBUSFREE;
64624794Sgibbs	call	clear_target_state;
64723925Sgibbs	mov	NONE, SCSIDATL;		/* Ack the last byte */
64839220Sgibbs	and	SXFRCTL0, ~SPIOEN;
64923925Sgibbs	test	SSTAT1,REQINIT|BUSFREE	jz .;
65023925Sgibbs	test	SSTAT1, BUSFREE jnz poll_for_work;
65123925Sgibbs	mvi	INTSTAT, BAD_PHASE;
65241646Sgibbs}
65323925Sgibbs	
65423925Sgibbsclear_target_state:
65541646Sgibbs	/*
65641646Sgibbs	 * We assume that the kernel driver may reset us
65741646Sgibbs	 * at any time, even in the middle of a DMA, so
65841646Sgibbs	 * clear DFCNTRL too.
65941646Sgibbs	 */
66041646Sgibbs	clr	DFCNTRL;
66141646Sgibbs
66241646Sgibbs	/*
66341646Sgibbs	 * We don't know the target we will connect to,
66441646Sgibbs	 * so default to narrow transfers to avoid
66541646Sgibbs	 * parity problems.
66641646Sgibbs	 */
66741646Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
66841646Sgibbs		bmov	SCSIRATE, ALLZEROS, 2;
66941646Sgibbs	} else {
67041646Sgibbs		clr	SCSIRATE;
67141646Sgibbs		and	SXFRCTL0, ~(FAST20);
67241646Sgibbs	}
67323925Sgibbs	mvi	LASTPHASE, P_BUSFREE;
67423925Sgibbs	/* clear target specific flags */
67539220Sgibbs	clr	SEQ_FLAGS ret;
67623925Sgibbs
67713177Sgibbs/*
67813177Sgibbs * If we re-enter the data phase after going through another phase, the
67913177Sgibbs * STCNT may have been cleared, so restore it from the residual field.
68013177Sgibbs */
6819928Sgibbsdata_phase_reinit:
68239220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
68339220Sgibbs		bmov	STCNT, SCB_RESID_DCNT, 3;
68439220Sgibbs	} else {
68539220Sgibbs		mvi	DINDEX, STCNT;
68639220Sgibbs		mvi	SCB_RESID_DCNT	call bcopy_3;
68739220Sgibbs	}
68823925Sgibbs	jmp	data_phase_loop;
6894568Sgibbs
69039220Sgibbsp_data:
69139220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
69239220Sgibbs		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
69339220Sgibbs	} else {
69439220Sgibbs		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
69539220Sgibbs	}
69639220Sgibbs	test	LASTPHASE, IOI jnz . + 2;
69739220Sgibbs	or	DMAPARAMS, DIRECTION;
69823925Sgibbs	call	assert;			/*
69919164Sgibbs					 * Ensure entering a data
70019164Sgibbs					 * phase is okay - seen identify, etc.
70119164Sgibbs					 */
70239220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
70339220Sgibbs		mvi	CCSGADDR, CCSGADDR_MAX;
70439220Sgibbs	}
70523925Sgibbs	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
7064568Sgibbs
70739220Sgibbs	/* We have seen a data phase */
70839220Sgibbs	or	SEQ_FLAGS, DPHASE;
70939220Sgibbs
71019164Sgibbs	/*
71119164Sgibbs	 * Initialize the DMA address and counter from the SCB.
71219164Sgibbs	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
71319164Sgibbs	 * modify the values in the SCB itself until we see a
71419164Sgibbs	 * save data pointers message.
71519164Sgibbs	 */
71639220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
71739220Sgibbs		bmov	HADDR, SCB_DATAPTR, 7;
71839220Sgibbs	} else {
71939220Sgibbs		mvi	DINDEX, HADDR;
72039220Sgibbs		mvi	SCB_DATAPTR	call bcopy_7;
72139220Sgibbs	}
72219164Sgibbs
72339220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
72439220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
72539220Sgibbs			bmov	STCNT, HCNT, 3;
72639220Sgibbs		} else {
72739220Sgibbs			call	set_stcnt_from_hcnt;
72839220Sgibbs		}
72939220Sgibbs	}
73019164Sgibbs
73139220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
73239220Sgibbs		bmov	SG_COUNT, SCB_SGCOUNT, 5;
73339220Sgibbs	} else {
73439220Sgibbs		mvi	DINDEX, SG_COUNT;
73539220Sgibbs		mvi	SCB_SGCOUNT	call bcopy_5;
73639220Sgibbs	}
73719164Sgibbs
7389928Sgibbsdata_phase_loop:
73916260Sgibbs/* Guard against overruns */
74023925Sgibbs	test	SG_COUNT, 0xff jnz data_phase_inbounds;
74116260Sgibbs/*
74216260Sgibbs * Turn on 'Bit Bucket' mode, set the transfer count to
74316260Sgibbs * 16meg and let the target run until it changes phase.
74416260Sgibbs * When the transfer completes, notify the host that we
74516260Sgibbs * had an overrun.
74616260Sgibbs */
74723925Sgibbs	or	SXFRCTL1,BITBUCKET;
74839220Sgibbs	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
74939220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
75039220Sgibbs		bmov	HCNT, ALLONES, 3;
75139220Sgibbs	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
75239220Sgibbs		bmov	STCNT, ALLONES, 3;
75339220Sgibbs	} else {
75439220Sgibbs		mvi	STCNT[0], 0xFF;
75539220Sgibbs		mvi	STCNT[1], 0xFF;
75639220Sgibbs		mvi	STCNT[2], 0xFF;
75739220Sgibbs	}
75816260Sgibbsdata_phase_inbounds:
75939220Sgibbs/* If we are the last SG block, tell the hardware. */
76023925Sgibbs	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
76139220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
76239220Sgibbs		or	SG_CACHEPTR, LAST_SEG;
76339220Sgibbs	} else {
76439220Sgibbs		and	DMAPARAMS, ~WIDEODD;
76539220Sgibbs	}
7669928Sgibbsdata_phase_wideodd:
76739220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
76839220Sgibbs		mov	SINDEX, ALLONES;
76939220Sgibbs		mov	DFCNTRL, DMAPARAMS;
77039220Sgibbs		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
77139220Sgibbsdata_phase_dma_loop:
77239220Sgibbs		test	SSTAT0,	SDONE jnz data_phase_dma_done;
77339220Sgibbs		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
77439220Sgibbsdata_phase_dma_phasemis:
77539220Sgibbs		test	SSTAT0,SDONE	jnz . + 2;
77639220Sgibbs		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
77739220Sgibbs	} else {
77839220Sgibbs		mov	DMAPARAMS  call dma;
77939220Sgibbs	}
7804568Sgibbs
78139220Sgibbsdata_phase_dma_done:
78216260Sgibbs/* Go tell the host about any overruns */
78323925Sgibbs	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
78416260Sgibbs
78522451Sgibbs/* Exit if we had an underrun.  dma clears SINDEX in this case. */
78623925Sgibbs	test	SINDEX,0xff	jz data_phase_finish;
7877532Sgibbs
78813177Sgibbs/*
78913177Sgibbs * Advance the scatter-gather pointers if needed 
79013177Sgibbs */
7919928Sgibbssg_advance:
79223925Sgibbs	dec	SG_COUNT;	/* one less segment to go */
7934568Sgibbs
79423925Sgibbs	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
79513177Sgibbs/*
79613177Sgibbs * Load a struct scatter and set up the data address and length.
79713177Sgibbs * If the working value of the SG count is nonzero, then
79813177Sgibbs * we need to load a new set of values.
79913177Sgibbs *
80015328Sgibbs * This, like all DMA's, assumes little-endian host data storage.
80113177Sgibbs */
8029928Sgibbssg_load:
80339220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
80439220Sgibbs		/*
80539220Sgibbs		 * Do we have any prefetch left???
80639220Sgibbs		 */
80739220Sgibbs		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
8084568Sgibbs
80939220Sgibbs		/*
81039220Sgibbs		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
81139220Sgibbs		 */
81239220Sgibbs		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
81339220Sgibbs		mvi	A, CCSGADDR_MAX;
81439220Sgibbs		jc	. + 2;
81539220Sgibbs		shl	A, 3, SG_COUNT;
81639220Sgibbs		mov	CCHCNT, A;
81739220Sgibbs		bmov	CCHADDR, SG_NEXT, 4;
81839220Sgibbs		mvi	CCSGCTL, CCSGEN|CCSGRESET;
81939220Sgibbs		test	CCSGCTL, CCSGDONE jz .;
82039220Sgibbs		and	CCSGCTL, ~CCSGEN;
82139220Sgibbs		test	CCSGCTL, CCSGEN jnz .;
82239220Sgibbs		mvi	CCSGCTL, CCSGRESET;
82339220Sgibbsprefetched_segs_avail:
82439220Sgibbs		bmov 	HADDR, CCSGRAM, 8;
82539220Sgibbs	} else {
82639220Sgibbs		mvi	DINDEX, HADDR;
82739220Sgibbs		mvi	SG_NEXT	call bcopy_4;
82822568Sgibbs
82939220Sgibbs		mvi	HCNT[0],SG_SIZEOF;
83039220Sgibbs		clr	HCNT[1];
83139220Sgibbs		clr	HCNT[2];
8329928Sgibbs
83339220Sgibbs		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
8349928Sgibbs
83539220Sgibbs		call	dma_finish;
8369928Sgibbs
83739220Sgibbs		/*
83839220Sgibbs		 * Copy data from FIFO into SCB data pointer and data count.
83939220Sgibbs		 * This assumes that the SG segments are of the form:
84039220Sgibbs		 * struct ahc_dma_seg {
84139220Sgibbs		 *	u_int32_t	addr;	four bytes, little-endian order
84239220Sgibbs		 *	u_int32_t	len;	four bytes, little endian order
84339220Sgibbs		 * };
84439220Sgibbs		 */
84539220Sgibbs		mvi	HADDR	call dfdat_in_7;
84639220Sgibbs	}
84739220Sgibbs
84839220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
84939220Sgibbs		/* Load STCNT as well.  It is a mirror of HCNT */
85039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
85139220Sgibbs			bmov	STCNT, HCNT, 3;
85239220Sgibbs		} else {
85339220Sgibbs			call	set_stcnt_from_hcnt;
85439220Sgibbs		}
85539220Sgibbs	}
85639220Sgibbs
85739220Sgibbs/* Advance the SG pointer */
85839220Sgibbs	clr	A;			/* add sizeof(struct scatter) */
85939220Sgibbs	add	SG_NEXT[0],SG_SIZEOF;
86039220Sgibbs	adc	SG_NEXT[1],A;
86139220Sgibbs
86241646Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
86341646Sgibbs		test	SSTAT0, TARGET jnz data_phase_loop;
86441646Sgibbs	}
86541646Sgibbs	test	SSTAT1, REQINIT jz .;
86623925Sgibbs	test	SSTAT1,PHASEMIS	jz data_phase_loop;
86741646Sgibbs
86839220Sgibbs	/* Ensure the last seg is visable at the shaddow layer */
86939220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
87039220Sgibbs		or	DFCNTRL, PRELOADEN;
87139220Sgibbs	}
8724568Sgibbs
8739928Sgibbsdata_phase_finish:
87439220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
87539220Sgibbs		call	ultra2_dmafinish;
87639220Sgibbs	}
87713177Sgibbs/*
87813177Sgibbs * After a DMA finishes, save the SG and STCNT residuals back into the SCB
87913177Sgibbs * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
88013177Sgibbs * were transferred on the SCSI (as opposed to the host) bus.
88113177Sgibbs */
88239220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
88339220Sgibbs		bmov	SCB_RESID_DCNT, STCNT, 3;
88439220Sgibbs	} else {
88539220Sgibbs		mov	SCB_RESID_DCNT[0],STCNT[0];
88639220Sgibbs		mov	SCB_RESID_DCNT[1],STCNT[1];
88739220Sgibbs		mov	SCB_RESID_DCNT[2],STCNT[2];
88839220Sgibbs	}
88923925Sgibbs	mov	SCB_RESID_SGCNT, SG_COUNT;
89022568Sgibbs
89139220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
89239220Sgibbs		or	SXFRCTL0, CLRSTCNT|CLRCHN;
89339220Sgibbs	}
89422568Sgibbs
89539220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
89641646Sgibbs		test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
89739220Sgibbs		and	SEQ_FLAGS, ~DPHASE_PENDING;
89839220Sgibbs		jmp	target_ITloop;
89939220Sgibbs	}
90023925Sgibbs	jmp	ITloop;
9014568Sgibbs
90216260Sgibbsdata_phase_overrun:
90339220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
90439220Sgibbs		call	ultra2_dmafinish;
90539220Sgibbs		or	SXFRCTL0, CLRSTCNT|CLRCHN;
90639220Sgibbs	}
90713177Sgibbs/*
90816260Sgibbs * Turn off BITBUCKET mode and notify the host
90916260Sgibbs */
91023925Sgibbs	and	SXFRCTL1, ~BITBUCKET;
91123925Sgibbs	mvi	INTSTAT,DATA_OVERRUN;
91223925Sgibbs	jmp	ITloop;
91316260Sgibbs
91439220Sgibbsultra2_dmafinish:
91539220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
91639220Sgibbs		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
91739220Sgibbs		and	DFCNTRL, ~SCSIEN;
91839220Sgibbs		test	DFCNTRL, SCSIEN jnz .;
91939220Sgibbs		or	DFCNTRL, FIFOFLUSH;
92039220Sgibbs		test	DFSTATUS, FIFOEMP jz . - 1;
92139220Sgibbsultra2_dmahalt:
92239220Sgibbs		and     DFCNTRL, ~(SCSIEN|HDMAEN);
92339220Sgibbs		test	DFCNTRL, HDMAEN jnz .;
92439220Sgibbs		ret;
92539220Sgibbs	}
92639220Sgibbs
92741646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
92816260Sgibbs/*
92915328Sgibbs * Command phase.  Set up the DMA registers and let 'er rip.
93013177Sgibbs */
9314568Sgibbsp_command:
93223925Sgibbs	call	assert;
9334568Sgibbs
93439220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
93539220Sgibbs		mov	HCNT[0], SCB_CMDLEN;
93639220Sgibbs		bmov	HCNT[1], ALLZEROS, 2;
93739220Sgibbs		if ((ahc->features & AHC_ULTRA2) == 0) {
93839220Sgibbs			bmov	STCNT, HCNT, 3;
93939220Sgibbs		}
94039220Sgibbs		add	NONE, -17, SCB_CMDLEN;
94139220Sgibbs		jc	dma_cmd_data;
94239220Sgibbs		if ((ahc->features & AHC_ULTRA2) != 0) {
94339220Sgibbs			mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
94439220Sgibbs		} else {
94539220Sgibbs			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
94639220Sgibbs		}
94739220Sgibbs		bmov   DFDAT, SCB_CMDSTORE, 16; 
94839220Sgibbs		jmp	cmd_loop;
94939220Sgibbsdma_cmd_data:
95039220Sgibbs		bmov	HADDR, SCB_CMDPTR, 4;
95139220Sgibbs	} else {
95239220Sgibbs		mvi	DINDEX, HADDR;
95339220Sgibbs		mvi	SCB_CMDPTR	call bcopy_5;
95439220Sgibbs		clr	HCNT[1];
95539220Sgibbs		clr	HCNT[2];
95639220Sgibbs	}
9574568Sgibbs
95839220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
95939220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) == 0) {
96039220Sgibbs			call	set_stcnt_from_hcnt;
96139220Sgibbs		}
96239220Sgibbs		mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
96339220Sgibbs	} else {
96439220Sgibbs		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
96539220Sgibbs	}
96639220Sgibbscmd_loop:
96739220Sgibbs	test	SSTAT0, SDONE jnz . + 2;
96839220Sgibbs	test    SSTAT1, PHASEMIS jz cmd_loop;
96939220Sgibbs	and	DFCNTRL, ~(SCSIEN|HDMAEN|SDMAEN);
97039220Sgibbs	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
97123925Sgibbs	jmp	ITloop;
9724568Sgibbs
97313177Sgibbs/*
97413177Sgibbs * Status phase.  Wait for the data byte to appear, then read it
97513177Sgibbs * and store it into the SCB.
97613177Sgibbs */
9774568Sgibbsp_status:
97823925Sgibbs	call	assert;
97919803Sgibbs
98023925Sgibbs	mov	SCB_TARGET_STATUS, SCSIDATL;
98123925Sgibbs	jmp	ITloop;
9824568Sgibbs
98313177Sgibbs/*
98441646Sgibbs * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
98541646Sgibbs * indentify message sequence and send it to the target.  The host may
98641646Sgibbs * override this behavior by setting the MK_MESSAGE bit in the SCB
98741646Sgibbs * control byte.  This will cause us to interrupt the host and allow
98841646Sgibbs * it to handle the message phase completely on its own.  If the bit
98941646Sgibbs * associated with this target is set, we will also interrupt the host,
99041646Sgibbs * thereby allowing it to send a message on the next selection regardless
99141646Sgibbs * of the transaction being sent.
99239220Sgibbs * 
99339220Sgibbs * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
99441646Sgibbs * This is done to allow the host to send messages outside of an identify
99539220Sgibbs * sequence while protecting the seqencer from testing the MK_MESSAGE bit
99639220Sgibbs * on an SCB that might not be for the current nexus. (For example, a
99739220Sgibbs * BDR message in responce to a bad reselection would leave us pointed to
99839220Sgibbs * an SCB that doesn't have anything to do with the current target).
99941646Sgibbs *
100039220Sgibbs * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
100139220Sgibbs * bus device reset).
100239220Sgibbs *
100339220Sgibbs * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
100439220Sgibbs * in case the target decides to put us in this phase for some strange
100539220Sgibbs * reason.
100613177Sgibbs */
100741646Sgibbsp_mesgout_retry:
100841646Sgibbs	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
10094568Sgibbsp_mesgout:
101039220Sgibbs	mov	SINDEX, MSG_OUT;
101139220Sgibbs	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
101241646Sgibbs	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
101341646Sgibbs	mov	FUNCTION1, SCB_TCL;
101441646Sgibbs	mov	A, FUNCTION1;
101541646Sgibbs	mov	SINDEX, TARGET_MSG_REQUEST[0];
101641646Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
101741646Sgibbs		/* Second Channel uses high byte bits */
101841646Sgibbs		test	SCB_TCL, SELBUSB	jz . + 2;
101941646Sgibbs		mov	SINDEX, TARGET_MSG_REQUEST[1];
102041646Sgibbs	} else if ((ahc->features & AHC_WIDE) != 0) {
102141646Sgibbs		test	SCB_TCL, 0x80		jz . + 2; /* target > 7 */
102241646Sgibbs		mov	SINDEX, TARGET_MSG_REQUEST[1];
102341646Sgibbs	}
102441646Sgibbs	test	SINDEX, A	jnz host_message_loop;
102539220Sgibbsp_mesgout_identify:
102641299Sgibbs	and	SINDEX,LID,SCB_TCL;	/* lun */
102739220Sgibbs	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
102839220Sgibbs	or	SINDEX,A;		/* or in disconnect privledge */
102939220Sgibbs	or	SINDEX,MSG_IDENTIFYFLAG;
103013177Sgibbs/*
103139220Sgibbs * Send a tag message if TAG_ENB is set in the SCB control block.
103239220Sgibbs * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
103313177Sgibbs */
103439220Sgibbsp_mesgout_tag:
103539220Sgibbs	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
103639220Sgibbs	mov	SCSIDATL, SINDEX;	/* Send the identify message */
103739220Sgibbs	call	phase_lock;
103839220Sgibbs	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
103939220Sgibbs	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
104039220Sgibbs	call	phase_lock;
104139220Sgibbs	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
104239220Sgibbs	mov	SCB_TAG	jmp p_mesgout_onebyte;
104313177Sgibbs/*
104441646Sgibbs * Interrupt the driver, and allow it to handle this message
104541646Sgibbs * phase and any required retries.
104613177Sgibbs */
104739220Sgibbsp_mesgout_from_host:
104839220Sgibbs	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
104941646Sgibbs	jmp	host_message_loop;
105039220Sgibbs
105139220Sgibbsp_mesgout_onebyte:
105239220Sgibbs	mvi	CLRSINT1, CLRATNO;
105339220Sgibbs	mov	SCSIDATL, SINDEX;
105439220Sgibbs
105513177Sgibbs/*
105641646Sgibbs * If the next bus phase after ATN drops is message out, it means
105713177Sgibbs * that the target is requesting that the last message(s) be resent.
105813177Sgibbs */
105939220Sgibbs	call	phase_lock;
106041646Sgibbs	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
10614568Sgibbs
106219906Sgibbsp_mesgout_done:
106323925Sgibbs	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
106439220Sgibbs	mov	LAST_MSG, MSG_OUT;
106539220Sgibbs	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
106623925Sgibbs	jmp	ITloop;
10674568Sgibbs
106813177Sgibbs/*
106941646Sgibbs * Pushed message loop to allow the kernel to
107041646Sgibbs * RUN IT's own message state engine.  To avoid an
107141646Sgibbs * extra nop instruction after signaling the kernel,
107241646Sgibbs * we perform the phase_lock before checking to see
107341646Sgibbs * if we should exit the loop and skip the phase_lock
107441646Sgibbs * in the ITloop.  Performing back to back phase_locks
107541646Sgibbs * shouldn't hurt, but why do it twice...
107641646Sgibbs */
107741646Sgibbshost_message_loop:
107841646Sgibbs	mvi	INTSTAT, HOST_MSG_LOOP;
107941646Sgibbs	call	phase_lock;
108041646Sgibbs	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
108141646Sgibbs	jmp	host_message_loop;
108241646Sgibbs
108341646Sgibbs/*
108413177Sgibbs * Message in phase.  Bytes are read using Automatic PIO mode.
108513177Sgibbs */
10864568Sgibbsp_mesgin:
108723925Sgibbs	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
10884568Sgibbs
108923925Sgibbs	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
109023925Sgibbs	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
109123925Sgibbs	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
109223925Sgibbs	cmp	ALLZEROS,A		je mesgin_complete;
109323925Sgibbs	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
109441646Sgibbs	cmp	A,MSG_EXTENDED		je host_message_loop;
109523925Sgibbs	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject;
109623925Sgibbs	cmp	A,MSG_NOOP		je mesgin_done;
10974568Sgibbs
10989954Sgibbsrej_mesgin:
109913177Sgibbs/*
110019164Sgibbs * We have no idea what this message in is, so we issue a message reject
110119164Sgibbs * and hope for the best.  In any case, rejection should be a rare
110219164Sgibbs * occurrence - signal the driver when it happens.
110313177Sgibbs */
110423925Sgibbs	mvi	INTSTAT,SEND_REJECT;		/* let driver know */
11059954Sgibbs
110623925Sgibbs	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
11079954Sgibbs
11089954Sgibbsmesgin_done:
110923925Sgibbs	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
111023925Sgibbs	jmp	ITloop;
11119954Sgibbs
11129954Sgibbs
11139954Sgibbsmesgin_complete:
111413177Sgibbs/*
111519164Sgibbs * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
111619164Sgibbs * and trigger a completion interrupt.  Before doing so, check to see if there
111739220Sgibbs * is a residual or the status byte is something other than STATUS_GOOD (0).
111839220Sgibbs * In either of these conditions, we upload the SCB back to the host so it can
111919164Sgibbs * process this information.  In the case of a non zero status byte, we 
112019164Sgibbs * additionally interrupt the kernel driver synchronously, allowing it to
112119164Sgibbs * decide if sense should be retrieved.  If the kernel driver wishes to request
112219164Sgibbs * sense, it will fill the kernel SCB with a request sense command and set
112319164Sgibbs * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
112419164Sgibbs * the SCB, and process it as the next command by adding it to the waiting list.
112519164Sgibbs * If the kernel driver does not wish to request sense, it need only clear
112619164Sgibbs * RETURN_1, and the command is allowed to complete normally.  We don't bother
112719164Sgibbs * to post to the QOUTFIFO in the error cases since it would require extra
112819164Sgibbs * work in the kernel driver to ensure that the entry was removed before the
112919164Sgibbs * command complete code tried processing it.
113013177Sgibbs */
113119164Sgibbs
113213177Sgibbs/*
113319164Sgibbs * First check for residuals
113413177Sgibbs */
113523925Sgibbs	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
113639220Sgibbs	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
113719164Sgibbsupload_scb:
113823925Sgibbs	mvi	DMAPARAMS, FIFORESET;
113923925Sgibbs	mov	SCB_TAG		call dma_scb;
11407532Sgibbscheck_status:
114139220Sgibbs	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
114223925Sgibbs	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
114339220Sgibbs	nop;
114439220Sgibbs	cmp	RETURN_1, SEND_SENSE	jne complete;
114519164Sgibbs	/* This SCB becomes the next to execute as it will retrieve sense */
114623925Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
114739220Sgibbs	mov	SCB_TAG		call dma_scb;
114819164Sgibbsadd_to_waiting_list:
114923925Sgibbs	mov	SCB_NEXT,WAITING_SCBH;
115023925Sgibbs	mov	WAITING_SCBH, SCBPTR;
115123925Sgibbs	/*
115223925Sgibbs	 * Prepare our selection hardware before the busfree so we have a
115323925Sgibbs	 * high probability of winning arbitration.
115423925Sgibbs	 */
115523925Sgibbs	call	start_selection;
115623925Sgibbs	jmp	await_busfree;
115739220Sgibbs
115839220Sgibbscomplete:
115939220Sgibbs	/* If we are untagged, clear our address up in host ram */
116039220Sgibbs	test	SCB_CONTROL, TAG_ENB jnz complete_queue;
116139220Sgibbs	mov	A, SAVED_TCL;
116239220Sgibbs	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
116339220Sgibbs	mvi	SCB_LIST_NULL call post_byte;
116439220Sgibbs
116539220Sgibbscomplete_queue:
116639220Sgibbs	mov	SCB_TAG call complete_post;
116739220Sgibbs
116819921Sgibbsadd_to_free_list:
116923925Sgibbs	call	add_scb_to_free_list;
117023925Sgibbs	jmp	await_busfree;
117141646Sgibbs}
11724568Sgibbs
117339220Sgibbscomplete_post:
117439220Sgibbs	/* Post the SCBID in SINDEX and issue an interrupt */
117539220Sgibbs	mov	ARG_1, SINDEX;
117639220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
117739220Sgibbs		mov	A, SDSCB_QOFF;
117839220Sgibbs	} else {
117939220Sgibbs		mov	A, QOUTPOS;
118039220Sgibbs	}
118139220Sgibbs	mvi	QOUTFIFO_OFFSET call post_byte_setup;
118239220Sgibbs	mov	ARG_1 call post_byte;
118339220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
118439220Sgibbs		inc 	QOUTPOS;
118539220Sgibbs	}
118639220Sgibbs	mvi	INTSTAT,CMDCMPLT ret;
118739220Sgibbs
118841646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
118913177Sgibbs/*
119013177Sgibbs * Is it a disconnect message?  Set a flag in the SCB to remind us
119113177Sgibbs * and await the bus going free.
119213177Sgibbs */
11939954Sgibbsmesgin_disconnect:
119423925Sgibbs	or	SCB_CONTROL,DISCONNECTED;
119523925Sgibbs	call	add_scb_to_disc_list;
119623925Sgibbs	jmp	await_busfree;
119719164Sgibbs
119815328Sgibbs/*
119919164Sgibbs * Save data pointers message:
120019164Sgibbs * Copying RAM values back to SCB, for Save Data Pointers message, but
120119164Sgibbs * only if we've actually been into a data phase to change them.  This
120219164Sgibbs * protects against bogus data in scratch ram and the residual counts
120319164Sgibbs * since they are only initialized when we go into data_in or data_out.
120415328Sgibbs */
120519164Sgibbsmesgin_sdptrs:
120623925Sgibbs	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
12074568Sgibbs
120839220Sgibbs	/*
120939220Sgibbs	 * The SCB SGPTR becomes the next one we'll download,
121039220Sgibbs	 * and the SCB DATAPTR becomes the current SHADDR.
121139220Sgibbs	 * Use the residual number since STCNT is corrupted by
121239220Sgibbs	 * any message transfer.
121339220Sgibbs	 */
121439220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
121539220Sgibbs		bmov	SCB_SGCOUNT, SG_COUNT, 5;
121639220Sgibbs		bmov	SCB_DATAPTR, SHADDR, 4;
121739220Sgibbs		bmov	SCB_DATACNT, SCB_RESID_DCNT, 3;
121839220Sgibbs	} else {
121939220Sgibbs		mvi	DINDEX, SCB_SGCOUNT;
122039220Sgibbs		mvi	SG_COUNT call bcopy_5;
122119164Sgibbs	
122239220Sgibbs		mvi	DINDEX, SCB_DATAPTR;
122339220Sgibbs		mvi	SHADDR		call bcopy_4;
122439220Sgibbs		mvi	SCB_RESID_DCNT	call bcopy_3;
122539220Sgibbs	}
122623925Sgibbs	jmp	mesgin_done;
12274568Sgibbs
122813177Sgibbs/*
122913177Sgibbs * Restore pointers message?  Data pointers are recopied from the
123013177Sgibbs * SCB anytime we enter a data phase for the first time, so all
123113177Sgibbs * we need to do is clear the DPHASE flag and let the data phase
123213177Sgibbs * code do the rest.
123313177Sgibbs */
12349954Sgibbsmesgin_rdptrs:
123523925Sgibbs	and	SEQ_FLAGS, ~DPHASE;		/*
123623925Sgibbs						 * We'll reload them
123713177Sgibbs						 * the next time through
123823925Sgibbs						 * the dataphase.
123913177Sgibbs						 */
124023925Sgibbs	jmp	mesgin_done;
12414568Sgibbs
124213177Sgibbs/*
124313177Sgibbs * Identify message?  For a reconnecting target, this tells us the lun
124413177Sgibbs * that the reconnection is for - find the correct SCB and switch to it,
124513177Sgibbs * clearing the "disconnected" bit so we don't "find" it by accident later.
124613177Sgibbs */
12479954Sgibbsmesgin_identify:
124839220Sgibbs	
124939220Sgibbs	if ((ahc->features & AHC_WIDE) != 0) {
125039220Sgibbs		and	A,0x0f;		/* lun in lower four bits */
125139220Sgibbs	} else {
125239220Sgibbs		and	A,0x07;		/* lun in lower three bits */
125339220Sgibbs	}
125423925Sgibbs	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
125539220Sgibbs
125639220Sgibbs	mvi	ARG_2, SCB_LIST_NULL;	/* SCBID of prev SCB in disc List */
125739220Sgibbs	call	get_untagged_SCBID;
125839220Sgibbs	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
125939220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
126039220Sgibbs		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
126139220Sgibbs	}
126239220Sgibbs	/*
126339220Sgibbs	 * If the SCB was found in the disconnected list (as is
126439220Sgibbs	 * always the case in non-paging scenarios), SCBPTR is already
126539220Sgibbs	 * set to the correct SCB.  So, simply setup the SCB and get
126639220Sgibbs	 * on with things.
126739220Sgibbs	 */
126839220Sgibbs	call	rem_scb_from_disc_list;
126924608Sgibbs	jmp	setup_SCB;
127013177Sgibbs/*
127113177Sgibbs * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
127223168Sgibbs * If we get one, we use the tag returned to find the proper
127339220Sgibbs * SCB.  With SCB paging, this requires using search for both tagged
127415328Sgibbs * and non-tagged transactions since the SCB may exist in any slot.
127515328Sgibbs * If we're not using SCB paging, we can use the tag as the direct
127615328Sgibbs * index to the SCB.
127713177Sgibbs */
127824608Sgibbssnoop_tag:
127923925Sgibbs	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
128013177Sgibbssnoop_tag_loop:
128139220Sgibbs	call	phase_lock;
128223925Sgibbs	cmp	LASTPHASE, P_MESGIN	jne not_found;
128323925Sgibbs	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
12846608Sgibbsget_tag:
128523925Sgibbs	mvi	ARG_1	call inb_next;	/* tag value */
128613177Sgibbs
128739220Sgibbs	/*
128839220Sgibbs	 * Ensure that the SCB the tag points to is for
128939220Sgibbs	 * an SCB transaction to the reconnecting target.
129039220Sgibbs	 */
129139220Sgibbsuse_retrieveSCB:
129239220Sgibbs	call	retrieveSCB;
129339220Sgibbssetup_SCB:
129424608Sgibbs	mov	A, SAVED_TCL;
129539220Sgibbs	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
129639220Sgibbs	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
129723925Sgibbs	and	SCB_CONTROL,~DISCONNECTED;
129823925Sgibbs	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
129939220Sgibbs	call	set_transfer_settings;
130039220Sgibbs	/* See if the host wants to send a message upon reconnection */
130139220Sgibbs	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
130239220Sgibbs	and	SCB_CONTROL, ~MK_MESSAGE;
130339220Sgibbs	mvi	HOST_MSG	call mk_mesg;
130423925Sgibbs	jmp	mesgin_done;
130515328Sgibbs
130639220Sgibbsnot_found_cleanup_scb:
130739220Sgibbs	test	SCB_CONTROL, DISCONNECTED jz . + 3;
130839220Sgibbs	call	add_scb_to_disc_list;
130939220Sgibbs	jmp	not_found;
131039220Sgibbs	call	add_scb_to_free_list;
131119218Sgibbsnot_found:
131223925Sgibbs	mvi	INTSTAT, NO_MATCH;
131325005Sgibbs	mvi	MSG_BUS_DEV_RESET	call mk_mesg;
131423925Sgibbs	jmp	mesgin_done;
13156608Sgibbs
131613177Sgibbs/*
131713177Sgibbs * Message reject?  Let the kernel driver handle this.  If we have an 
131813177Sgibbs * outstanding WDTR or SDTR negotiation, assume that it's a response from 
131913177Sgibbs * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
132013177Sgibbs * it since we have no clue what it pertains to.
132113177Sgibbs */
13229954Sgibbsmesgin_reject:
132323925Sgibbs	mvi	INTSTAT, REJECT_MSG;
132423925Sgibbs	jmp	mesgin_done;
13255562Sgibbs
132613177Sgibbs/*
132713177Sgibbs * [ ADD MORE MESSAGE HANDLING HERE ]
132813177Sgibbs */
13294568Sgibbs
133013177Sgibbs/*
133113177Sgibbs * Locking the driver out, build a one-byte message passed in SINDEX
133213177Sgibbs * if there is no active message already.  SINDEX is returned intact.
133313177Sgibbs */
13344568Sgibbsmk_mesg:
133523925Sgibbs	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
133639220Sgibbs	mov	MSG_OUT,SINDEX ret;
13374568Sgibbs
133813177Sgibbs/*
133913177Sgibbs * Functions to read data in Automatic PIO mode.
134013177Sgibbs *
134113177Sgibbs * According to Adaptec's documentation, an ACK is not sent on input from
134213177Sgibbs * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
134313177Sgibbs * latched (the usual way), then read the data byte directly off the bus
134413177Sgibbs * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
134513177Sgibbs * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
134613177Sgibbs * spec guarantees that the target will hold the data byte on the bus until
134713177Sgibbs * we send our ACK.
134813177Sgibbs *
134913177Sgibbs * The assumption here is that these are called in a particular sequence,
135013177Sgibbs * and that REQ is already set when inb_first is called.  inb_{first,next}
135113177Sgibbs * use the same calling convention as inb.
135213177Sgibbs */
135313177Sgibbs
135413177Sgibbsinb_next:
135523925Sgibbs	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
135613360Sgibbsinb_next_wait:
135721947Sgibbs	/*
135821947Sgibbs	 * If there is a parity error, wait for the kernel to
135921947Sgibbs	 * see the interrupt and prepare our message response
136021947Sgibbs	 * before continuing.
136121947Sgibbs	 */
136223925Sgibbs	test	SSTAT1, REQINIT	jz inb_next_wait;
136323925Sgibbs	test	SSTAT1, SCSIPERR jnz inb_next_wait;
136423925Sgibbs	and	LASTPHASE, PHASE_MASK, SCSISIGI;
136523925Sgibbs	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
136619623Sgibbsinb_first:
136723925Sgibbs	mov	DINDEX,SINDEX;
136823925Sgibbs	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
136913177Sgibbsinb_last:
137023925Sgibbs	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
137141646Sgibbs}
13724568Sgibbs
137339220Sgibbsif ((ahc->flags & AHC_TARGETMODE) != 0) {
137441646Sgibbs/*
137541646Sgibbs * Change to a new phase.  If we are changing the state of the I/O signal,
137641646Sgibbs * from out to in, wait an additional data release delay before continuing.
137741646Sgibbs */
137841646Sgibbschange_phase:
137941646Sgibbs	and	DINDEX, IOI, SCSISIGI;
138041646Sgibbs	mov	SCSISIGO, SINDEX;
138141646Sgibbs	and	A, IOI, SINDEX;
138241646Sgibbs	cmp 	DINDEX, A je change_phase_wait;
138341646Sgibbs	test	SINDEX, IOI jz change_phase_wait;
138441646Sgibbs	call	change_phase_wait;
138541646Sgibbschange_phase_wait:
138641646Sgibbs	nop;
138741646Sgibbs	nop;
138841646Sgibbs	nop;
138941646Sgibbs	nop ret;
139041646Sgibbs
139141646Sgibbs/*
139241646Sgibbs * Send a byte to an initiator in Automatic PIO mode.
139341646Sgibbs */
139439220Sgibbstarget_outb:
139539220Sgibbs	or	SXFRCTL0, SPIOEN;
139639220Sgibbs	test	SSTAT0, SPIORDY	jz .;
139739220Sgibbs	mov	SCSIDATL, SINDEX;
139839220Sgibbs	test	SSTAT0, SPIORDY	jz .;
139941646Sgibbs	and	SXFRCTL0, ~SPIOEN ret;
140039220Sgibbs}
140139220Sgibbs	
140213177Sgibbsmesgin_phasemis:
140313177Sgibbs/*
140413177Sgibbs * We expected to receive another byte, but the target changed phase
140513177Sgibbs */
140623925Sgibbs	mvi	INTSTAT, MSGIN_PHASEMIS;
140723925Sgibbs	jmp	ITloop;
14084568Sgibbs
140913177Sgibbs/*
141013177Sgibbs * DMA data transfer.  HADDR and HCNT must be loaded first, and
141113177Sgibbs * SINDEX should contain the value to load DFCNTRL with - 0x3d for
141213177Sgibbs * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
141313177Sgibbs * during initialization.
141413177Sgibbs */
14154568Sgibbsdma:
141623925Sgibbs	mov	DFCNTRL,SINDEX;
141722568Sgibbsdma_loop:
141823925Sgibbs	test	SSTAT0,DMADONE	jnz dma_dmadone;
141923925Sgibbs	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
142022568Sgibbsdma_phasemis:
142123925Sgibbs	test	SSTAT0,SDONE	jnz dma_checkfifo;
142223925Sgibbs	mov	SINDEX,ALLZEROS;		/* Notify caller of phasemiss */
14234568Sgibbs
142413177Sgibbs/*
142513177Sgibbs * We will be "done" DMAing when the transfer count goes to zero, or
142613177Sgibbs * the target changes the phase (in light of this, it makes sense that
142713177Sgibbs * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
142813177Sgibbs * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
142913177Sgibbs * magically on STCNT=0 or a phase change, so just wait for FIFO empty
143013177Sgibbs * status.
143113177Sgibbs */
143222568Sgibbsdma_checkfifo:
143323925Sgibbs	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
143422568Sgibbsdma_fifoflush:
143523925Sgibbs	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
14364568Sgibbs
143722568Sgibbsdma_fifoempty:
143822568Sgibbs	/* Don't clobber an inprogress host data transfer */
143923925Sgibbs	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
144013177Sgibbs/*
144113177Sgibbs * Now shut the DMA enables off and make sure that the DMA enables are 
144213177Sgibbs * actually off first lest we get an ILLSADDR.
144313177Sgibbs */
144422568Sgibbsdma_dmadone:
144523925Sgibbs	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
144622568Sgibbsdma_halt:
144737223Sgibbs	/*
144837223Sgibbs	 * Some revisions of the aic7880 have a problem where, if the
144937223Sgibbs	 * data fifo is full, but the PCI input latch is not empty, 
145037223Sgibbs	 * HDMAEN cannot be cleared.  The fix used here is to attempt
145137223Sgibbs	 * to drain the data fifo until there is space for the input
145237223Sgibbs	 * latch to drain and HDMAEN de-asserts.
145337223Sgibbs	 */
145439220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
145539220Sgibbs		mov	NONE, DFDAT;
145639220Sgibbs	}
145739220Sgibbs	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
145818762Sgibbsreturn:
145923925Sgibbs	ret;
14604568Sgibbs
146113177Sgibbs/*
146213177Sgibbs * Assert that if we've been reselected, then we've seen an IDENTIFY
146313177Sgibbs * message.
146413177Sgibbs */
14654568Sgibbsassert:
146623925Sgibbs	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
14674568Sgibbs
146823925Sgibbs	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
14694568Sgibbs
147013177Sgibbs/*
147119218Sgibbs * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
147239220Sgibbs * or by the SCBID ARG_1.  The search begins at the SCB index passed in
147339220Sgibbs * via SINDEX which is an SCB that must be on the disconnected list.  If
147439220Sgibbs * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
147539220Sgibbs * is set to the proper SCB.
147613177Sgibbs */
14774568SgibbsfindSCB:
147839220Sgibbs	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
147939220Sgibbs	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
148039220Sgibbs	mov	A, SAVED_TCL;
148139220Sgibbs	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
148239220SgibbsfindSCB_by_SCBID:
148323925Sgibbs	mov	A, ARG_1;			/* Tag passed in ARG_1 */
148439220Sgibbs	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
148539220SgibbsfindSCB_next:
148639220Sgibbs	mov	ARG_2, SCBPTR;
148739220Sgibbs	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
148839220Sgibbs	mov	SCBPTR,SCB_NEXT;
148939220Sgibbs	dec	SINDEX;		/* Last comparison moved us too far */
149023168SgibbsfindSCB_loop:
149139220Sgibbs	cmp	SINDIR, A	jne findSCB_next;
149239220Sgibbs	mov	SINDEX, SCBPTR 	ret;
149339220SgibbsnotFound:
149439220Sgibbs	mvi	SINDEX, SCB_LIST_NULL	ret;
149539220Sgibbs
149619164Sgibbs/*
149739220Sgibbs * Retrieve an SCB by SCBID first searching the disconnected list falling
149839220Sgibbs * back to DMA'ing the SCB down from the host.  This routine assumes that
149939220Sgibbs * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
150039220Sgibbs * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
150139220Sgibbs * we go directly to the host for the SCB.
150219164Sgibbs */
150339220SgibbsretrieveSCB:
150439220Sgibbs	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
150539220Sgibbs	mov	SCBPTR	call findSCB;	/* Continue the search */
150639220Sgibbs	cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;
150739220Sgibbs
150839220Sgibbs/*
150939220Sgibbs * This routine expects SINDEX to contain the index of the SCB to be
151039220Sgibbs * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
151139220Sgibbs * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
151239220Sgibbs * if it is at the head.
151339220Sgibbs */
151419164Sgibbsrem_scb_from_disc_list:
151515328Sgibbs/* Remove this SCB from the disconnection list */
151639220Sgibbs	cmp	ARG_2, SCB_LIST_NULL	je rHead;
151739220Sgibbs	mov	DINDEX, SCB_NEXT;
151839220Sgibbs	mov	SCBPTR, ARG_2;
151939220Sgibbs	mov	SCB_NEXT, DINDEX;
152023925Sgibbs	mov	SCBPTR, SINDEX ret;
152115328SgibbsrHead:
152223925Sgibbs	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
15234568Sgibbs
152439220Sgibbsretrieve_from_host:
152539220Sgibbs/*
152639220Sgibbs * We didn't find it.  Pull an SCB and DMA down the one we want.
152739220Sgibbs * We should never get here in the non-paging case.
152839220Sgibbs */
152939220Sgibbs	mov	ALLZEROS	call	get_free_or_disc_scb;
153039220Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
153139220Sgibbs	/* Jump instead of call as we want to return anyway */
153239220Sgibbs	mov	ARG_1	jmp dma_scb;
153339220Sgibbs
153439220Sgibbs/*
153539220Sgibbs * Determine whether a target is using tagged or non-tagged transactions
153639220Sgibbs * by first looking for a matching transaction based on the TCL and if
153739220Sgibbs * that fails, looking up this device in the host's untagged SCB array.
153839220Sgibbs * The TCL to search for is assumed to be in SAVED_TCL.  The value is
153939220Sgibbs * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
154039220Sgibbs * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
154139220Sgibbs * in an SCB instead of having to go to the host.
154239220Sgibbs */
154339220Sgibbsget_untagged_SCBID:
154439220Sgibbs	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
154539220Sgibbs	mvi	ARG_1, SCB_LIST_NULL;
154639220Sgibbs	mov	DISCONNECTED_SCBH call findSCB;
154739220Sgibbs	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
154839220Sgibbs	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
154939220Sgibbs	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
155039220Sgibbs	mov	ARG_1, SCB_TAG	ret;
155139220Sgibbs	mvi	ARG_1, SCB_LIST_NULL ret;
155239220Sgibbs
155339220Sgibbs/*
155439220Sgibbs * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
155539220Sgibbs * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
155639220Sgibbs */
155739220Sgibbsfetch_byte:
155839220Sgibbs	mov	ARG_2, SINDEX;
155939220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
156039220Sgibbs		mvi	DINDEX, CCHADDR;
156139220Sgibbs		mvi	SCBID_ADDR call set_1byte_addr;
156239220Sgibbs		mvi	CCHCNT, 1;
156339220Sgibbs		mvi	CCSGCTL, CCSGEN|CCSGRESET;
156439220Sgibbs		test	CCSGCTL, CCSGDONE jz .;
156539220Sgibbs		mvi	CCSGCTL, CCSGRESET;
156639220Sgibbs		bmov	RETURN_2, CCSGRAM, 1 ret;
156739220Sgibbs	} else {
156839220Sgibbs		mvi	DINDEX, HADDR;
156939220Sgibbs		mvi	SCBID_ADDR call set_1byte_addr;
157039220Sgibbs		mvi	HCNT[0], 1;
157139220Sgibbs		clr	HCNT[1];
157239220Sgibbs		clr	HCNT[2];
157339220Sgibbs		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
157439220Sgibbs		call	dma_finish;
157539220Sgibbs		mov	RETURN_2, DFDAT ret;
157639220Sgibbs	}
157739220Sgibbs
157839220Sgibbs/*
157939220Sgibbs * Prepare the hardware to post a byte to host memory given an
158039220Sgibbs * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
158139220Sgibbs */
158239220Sgibbspost_byte_setup:
158339220Sgibbs	mov	ARG_2, SINDEX;
158439220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
158539220Sgibbs		mvi	DINDEX, CCHADDR;
158639220Sgibbs		mvi	SCBID_ADDR call	set_1byte_addr;
158739220Sgibbs		mvi	CCHCNT, 1;
158839220Sgibbs		mvi	CCSCBCTL, CCSCBRESET ret;
158939220Sgibbs	} else {
159039220Sgibbs		mvi	DINDEX, HADDR;
159139220Sgibbs		mvi	SCBID_ADDR call	set_1byte_addr;
159239220Sgibbs		mvi	HCNT[0], 1;
159339220Sgibbs		clr	HCNT[1];
159439220Sgibbs		clr	HCNT[2];
159539220Sgibbs		mvi	DFCNTRL, FIFORESET ret;
159639220Sgibbs	}
159739220Sgibbs
159839220Sgibbspost_byte:
159939220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
160039220Sgibbs		bmov	CCSCBRAM, SINDEX, 1;
160139220Sgibbs		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
160239220Sgibbs		test	CCSCBCTL, CCSCBDONE jz .;
160339220Sgibbs		clr	CCSCBCTL ret;
160439220Sgibbs	} else {
160539220Sgibbs		mov	DFDAT, SINDEX;
160639220Sgibbs		or	DFCNTRL, HDMAEN|FIFOFLUSH;
160739220Sgibbs		jmp	dma_finish;
160839220Sgibbs	}
160939220Sgibbs
161039220Sgibbsget_SCBID_from_host:
161139220Sgibbs	mov	A, SAVED_TCL;
161239220Sgibbs	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
161339220Sgibbs	mov	RETURN_1,  RETURN_2 ret;
161439220Sgibbs
161539220Sgibbsphase_lock:     
161639220Sgibbs	test	SSTAT1, REQINIT jz phase_lock;
161739220Sgibbs	test	SSTAT1, SCSIPERR jnz phase_lock;
161841646Sgibbs	and	SCSISIGO, PHASE_MASK, SCSISIGI;
161941646Sgibbs	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
162039220Sgibbs
162139220Sgibbsif ((ahc->features & AHC_CMD_CHAN) == 0) {
162219164Sgibbsset_stcnt_from_hcnt:
162323925Sgibbs	mov	STCNT[0], HCNT[0];
162423925Sgibbs	mov	STCNT[1], HCNT[1];
162523925Sgibbs	mov	STCNT[2], HCNT[2] ret;
16264568Sgibbs
162719164Sgibbsbcopy_7:
162823925Sgibbs	mov	DINDIR, SINDIR;
162923925Sgibbs	mov	DINDIR, SINDIR;
163019164Sgibbsbcopy_5:
163123925Sgibbs	mov	DINDIR, SINDIR;
163219164Sgibbsbcopy_4:
163323925Sgibbs	mov	DINDIR, SINDIR;
163419164Sgibbsbcopy_3:
163523925Sgibbs	mov	DINDIR, SINDIR;
163623925Sgibbs	mov	DINDIR, SINDIR;
163723925Sgibbs	mov	DINDIR, SINDIR ret;
163839220Sgibbs}
16394568Sgibbs
164039220Sgibbsif ((ahc->flags & AHC_TARGETMODE) != 0) {
164139220Sgibbs/*
164239220Sgibbs * Setup addr assuming that A is an index into
164339220Sgibbs * an array of 32byte objects, SINDEX contains
164439220Sgibbs * the base address of that array, and DINDEX
164539220Sgibbs * contains the base address of the location
164639220Sgibbs * to store the indexed address.
164739220Sgibbs */
164839220Sgibbsset_32byte_addr:
164939220Sgibbs	shr	ARG_2, 3, A;
165039220Sgibbs	shl	A, 5;
165139220Sgibbs	jmp	set_1byte_addr;
165239220Sgibbs}
165339220Sgibbs
165439220Sgibbs/*
165539220Sgibbs * Setup addr assuming that A is an index into
165639220Sgibbs * an array of 64byte objects, SINDEX contains
165739220Sgibbs * the base address of that array, and DINDEX
165839220Sgibbs * contains the base address of the location
165939220Sgibbs * to store the indexed address.
166039220Sgibbs */
166139220Sgibbsset_64byte_addr:
166239220Sgibbs	shr	ARG_2, 2, A;
166339220Sgibbs	shl	A, 6;
166439220Sgibbs
166539220Sgibbs/*
166639220Sgibbs * Setup addr assuming that A + (ARG_1 * 256) is an
166739220Sgibbs * index into an array of 1byte objects, SINDEX contains
166839220Sgibbs * the base address of that array, and DINDEX contains
166939220Sgibbs * the base address of the location to store the computed
167039220Sgibbs * address.
167139220Sgibbs */
167239220Sgibbsset_1byte_addr:
167339220Sgibbs	add     DINDIR, A, SINDIR;
167439220Sgibbs	mov     A, ARG_2;
167539220Sgibbs	adc	DINDIR, A, SINDIR;
167639220Sgibbs	clr	A;
167739220Sgibbs	adc	DINDIR, A, SINDIR;
167839220Sgibbs	adc	DINDIR, A, SINDIR ret;
167939220Sgibbs
168039220Sgibbs/*
168139220Sgibbs * Either post or fetch and SCB from host memory based on the
168239220Sgibbs * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
168339220Sgibbs */
168419164Sgibbsdma_scb:
168539220Sgibbs	mov	A, SINDEX;
168639220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
168739220Sgibbs		mvi	DINDEX, CCHADDR;
168839220Sgibbs		mvi	HSCB_ADDR call set_64byte_addr;
168939220Sgibbs		mov	CCSCBPTR, SCBPTR;
169039220Sgibbs		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
169139220Sgibbs		mvi	CCHCNT, SCB_64BYTE_SIZE;
169239220Sgibbs		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
169339220Sgibbs		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
169439220Sgibbs		jmp	dma_scb_finish;
169539220Sgibbsdma_scb_tohost:
169639220Sgibbs		mvi	CCHCNT, SCB_32BYTE_SIZE;
169739220Sgibbs		if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
169839220Sgibbs			mvi	CCSCBCTL, CCSCBRESET;
169939220Sgibbs			bmov	CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
170039220Sgibbs			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
170139220Sgibbs			test	CCSCBCTL, CCSCBDONE jz .;
170239220Sgibbs		} else {
170339220Sgibbs			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
170439220Sgibbs			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
170539220Sgibbs		}
170639220Sgibbsdma_scb_finish:
170739220Sgibbs		clr	CCSCBCTL;
170839220Sgibbs		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
170939220Sgibbs		ret;
171039220Sgibbs	} else {
171139220Sgibbs		mvi	DINDEX, HADDR;
171239220Sgibbs		mvi	HSCB_ADDR call set_64byte_addr;
171339220Sgibbs		mvi	HCNT[0], SCB_32BYTE_SIZE;
171439220Sgibbs		clr	HCNT[1];
171539220Sgibbs		clr	HCNT[2];
171639220Sgibbs		mov	DFCNTRL, DMAPARAMS;
171739220Sgibbs		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
171839220Sgibbs		/* Fill it with the SCB data */
171924175Sgibbscopy_scb_tofifo:
172039220Sgibbs		mvi	SINDEX, SCB_CONTROL;
172139220Sgibbs		add	A, SCB_32BYTE_SIZE, SINDEX;
172224175Sgibbscopy_scb_tofifo_loop:
172339220Sgibbs		mov	DFDAT,SINDIR;
172439220Sgibbs		mov	DFDAT,SINDIR;
172539220Sgibbs		mov	DFDAT,SINDIR;
172639220Sgibbs		mov	DFDAT,SINDIR;
172739220Sgibbs		mov	DFDAT,SINDIR;
172839220Sgibbs		mov	DFDAT,SINDIR;
172939220Sgibbs		mov	DFDAT,SINDIR;
173039220Sgibbs		cmp	SINDEX, A jne copy_scb_tofifo_loop;
173139220Sgibbs		or	DFCNTRL, HDMAEN|FIFOFLUSH;
173219164Sgibbsdma_scb_fromhost:
173339220Sgibbs		call	dma_finish;
173439220Sgibbs		/* If we were putting the SCB, we are done */
173539220Sgibbs		test	DMAPARAMS, DIRECTION	jz	return;
173639220Sgibbs		mvi	SCB_CONTROL  call dfdat_in_7;
173739220Sgibbs		call	dfdat_in_7_continued;
173839220Sgibbs		call	dfdat_in_7_continued;
173939220Sgibbs		jmp	dfdat_in_7_continued;
174019164Sgibbsdfdat_in_7:
174139220Sgibbs		mov     DINDEX,SINDEX;
174219164Sgibbsdfdat_in_7_continued:
174339220Sgibbs		mov	DINDIR,DFDAT;
174439220Sgibbs		mov	DINDIR,DFDAT;
174539220Sgibbs		mov	DINDIR,DFDAT;
174639220Sgibbs		mov	DINDIR,DFDAT;
174739220Sgibbs		mov	DINDIR,DFDAT;
174839220Sgibbs		mov	DINDIR,DFDAT;
174939220Sgibbs		mov	DINDIR,DFDAT ret;
175039220Sgibbs	}
175119164Sgibbs
175239220Sgibbs
175313177Sgibbs/*
175419164Sgibbs * Wait for DMA from host memory to data FIFO to complete, then disable
175519164Sgibbs * DMA and wait for it to acknowledge that it's off.
175613177Sgibbs */
175719164Sgibbsdma_finish:
175823925Sgibbs	test	DFSTATUS,HDONE	jz dma_finish;
175922234Sgibbs	/* Turn off DMA */
176023925Sgibbs	and	DFCNTRL, ~HDMAEN;
176123925Sgibbs	test	DFCNTRL, HDMAEN jnz .;
176223925Sgibbs	ret;
17639928Sgibbs
176423925Sgibbsadd_scb_to_free_list:
176539220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
176639220Sgibbs		mov	SCB_NEXT, FREE_SCBH;
176739220Sgibbs		mov	FREE_SCBH, SCBPTR;
176839220Sgibbs	}
176939220Sgibbs	mvi	SCB_TAG, SCB_LIST_NULL ret;
17704568Sgibbs
177139220Sgibbsif ((ahc->flags & AHC_PAGESCBS) != 0) {
177219164Sgibbsget_free_or_disc_scb:
177323925Sgibbs	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
177423925Sgibbs	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
177519623Sgibbsreturn_error:
177623925Sgibbs	mvi	SINDEX, SCB_LIST_NULL	ret;
177719623Sgibbsdequeue_disc_scb:
177823925Sgibbs	mov	SCBPTR, DISCONNECTED_SCBH;
177923925Sgibbsdma_up_scb:
178023925Sgibbs	mvi	DMAPARAMS, FIFORESET;
178123925Sgibbs	mov	SCB_TAG		call dma_scb;
178219164Sgibbsunlink_disc_scb:
178339220Sgibbs	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
178419164Sgibbsdequeue_free_scb:
178523925Sgibbs	mov	SCBPTR, FREE_SCBH;
178623925Sgibbs	mov	FREE_SCBH, SCB_NEXT ret;
178739220Sgibbs}
17884568Sgibbs
178919164Sgibbsadd_scb_to_disc_list:
179013177Sgibbs/*
179119164Sgibbs * Link this SCB into the DISCONNECTED list.  This list holds the
179219164Sgibbs * candidates for paging out an SCB if one is needed for a new command.
179319164Sgibbs * Modifying the disconnected list is a critical(pause dissabled) section.
179413177Sgibbs */
179523925Sgibbs	mov	SCB_NEXT, DISCONNECTED_SCBH;
179639220Sgibbs	mov	DISCONNECTED_SCBH, SCBPTR ret;
1797