aic7xxx.seq revision 54211
126997Sgibbs/*
226997Sgibbs * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
313177Sgibbs *
442652Sgibbs * Copyright (c) 1994-1999 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,
1254211Sgibbs *    without modification.
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 *
1654211Sgibbs * Alternatively, this software may be distributed under the terms of the
1754211Sgibbs * the GNU Public License ("GPL").
1813177Sgibbs *
1926997Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2026997Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2126997Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2226997Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2326997Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2426997Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2526997Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2626997Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2726997Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2826997Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2926997Sgibbs * SUCH DAMAGE.
3013177Sgibbs *
3150477Speter * $FreeBSD: head/sys/dev/aic7xxx/aic7xxx.seq 54211 1999-12-06 18:23:31Z gibbs $
3226997Sgibbs */
334568Sgibbs
3423925Sgibbs#include <dev/aic7xxx/aic7xxx.reg>
3539220Sgibbs#include <cam/scsi/scsi_message.h>
365647Sgibbs
3713177Sgibbs/*
3819164Sgibbs * A few words on the waiting SCB list:
3919164Sgibbs * After starting the selection hardware, we check for reconnecting targets
4013690Sgibbs * as well as for our selection to complete just in case the reselection wins
4113690Sgibbs * bus arbitration.  The problem with this is that we must keep track of the
4213690Sgibbs * SCB that we've already pulled from the QINFIFO and started the selection
4313690Sgibbs * on just in case the reselection wins so that we can retry the selection at
4413690Sgibbs * a later time.  This problem cannot be resolved by holding a single entry
4513690Sgibbs * in scratch ram since a reconnecting target can request sense and this will
4613690Sgibbs * create yet another SCB waiting for selection.  The solution used here is to 
4713690Sgibbs * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
4819164Sgibbs * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
4919164Sgibbs * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
5019164Sgibbs * this list everytime a request sense occurs or after completing a non-tagged
5119164Sgibbs * command for which a second SCB has been queued.  The sequencer will
5219164Sgibbs * automatically consume the entries.
5313177Sgibbs */
544568Sgibbs
5514449Sgibbsreset:
5623925Sgibbs	clr	SCSISIGO;		/* De-assert BSY */
5741646Sgibbs	and	SXFRCTL1, ~BITBUCKET;
5823925Sgibbs	/* Always allow reselection */
5941816Sgibbs	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
6039220Sgibbs
6139220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
6239220Sgibbs		/* Ensure that no DMA operations are in progress */
6339220Sgibbs		clr	CCSGCTL;
6439220Sgibbs		clr	CCSCBCTL;
6539220Sgibbs	}
6639220Sgibbs
6744507Sgibbspoll_for_work:
6823925Sgibbs	call	clear_target_state;
6939220Sgibbs	and	SXFRCTL0, ~SPIOEN;
7039220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
7139220Sgibbs		mov	A, QINPOS;
7239220Sgibbs	}
7339220Sgibbspoll_for_work_loop:
7439220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
7539220Sgibbs		and	SEQCTL, ~PAUSEDIS;
7639220Sgibbs	}
7739220Sgibbs	test	SSTAT0, SELDO|SELDI	jnz selection;
7823925Sgibbs	test	SCSISEQ, ENSELO	jnz poll_for_work;
7939220Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
8039220Sgibbs		/*
8139220Sgibbs		 * Twin channel devices cannot handle things like SELTO
8239220Sgibbs		 * interrupts on the "background" channel.  So, if we
8339220Sgibbs		 * are selecting, keep polling the current channel util
8439220Sgibbs		 * either a selection or reselection occurs.
8539220Sgibbs		 */
8639220Sgibbs		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
8739220Sgibbs		test	SSTAT0, SELDO|SELDI	jnz selection;
8839220Sgibbs		test	SCSISEQ, ENSELO	jnz poll_for_work;
8939220Sgibbs		xor	SBLKCTL,SELBUSB;	/* Toggle back */
9039220Sgibbs	}
9123925Sgibbs	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
9219164Sgibbstest_queue:
9319164Sgibbs	/* Has the driver posted any work for us? */
9439220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
9539220Sgibbs		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
9639220Sgibbs		mov	NONE, SNSCB_QOFF;
9739220Sgibbs		inc	QINPOS;
9839220Sgibbs	} else {
9939220Sgibbs		or	SEQCTL, PAUSEDIS;
10039220Sgibbs		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
10139220Sgibbs		inc	QINPOS;
10239220Sgibbs		and	SEQCTL, ~PAUSEDIS;
10339220Sgibbs	}
1044568Sgibbs
10513690Sgibbs/*
10613690Sgibbs * We have at least one queued SCB now and we don't have any 
10719164Sgibbs * SCBs in the list of SCBs awaiting selection.  If we have
10823925Sgibbs * any SCBs available for use, pull the tag from the QINFIFO
10919164Sgibbs * and get to work on it.
11013177Sgibbs */
11139220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
11239220Sgibbs		mov	ALLZEROS	call	get_free_or_disc_scb;
11339220Sgibbs	}
11439220Sgibbs
11519164Sgibbsdequeue_scb:
11639220Sgibbs	add	A, -1, QINPOS;
11739220Sgibbs	mvi	QINFIFO_OFFSET call fetch_byte;
11839220Sgibbs
11939220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) == 0) {
12039220Sgibbs		/* In the non-paging case, the SCBID == hardware SCB index */
12139220Sgibbs		mov	SCBPTR, RETURN_2;
12239220Sgibbs	}
12319164Sgibbsdma_queued_scb:
12419164Sgibbs/*
12519164Sgibbs * DMA the SCB from host ram into the current SCB location.
12619164Sgibbs */
12723925Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
12839220Sgibbs	mov	RETURN_2	 call dma_scb;
1294568Sgibbs
13013177Sgibbs/*
13139220Sgibbs * Preset the residual fields in case we never go through a data phase.
13239220Sgibbs * This isn't done by the host so we can avoid a DMA to clear these
13339220Sgibbs * fields for the normal case of I/O that completes without underrun
13439220Sgibbs * or overrun conditions.
13513177Sgibbs */
13639220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
13739220Sgibbs		bmov	SCB_RESID_DCNT, SCB_DATACNT, 3;
13839220Sgibbs	} else {
13939220Sgibbs		mov	SCB_RESID_DCNT[0],SCB_DATACNT[0];
14039220Sgibbs		mov	SCB_RESID_DCNT[1],SCB_DATACNT[1];
14139220Sgibbs		mov	SCB_RESID_DCNT[2],SCB_DATACNT[2];
14239220Sgibbs	}
14339220Sgibbs	mov	SCB_RESID_SGCNT, SCB_SGCOUNT;
1444568Sgibbs
1455326Sgibbsstart_scb:
14619164Sgibbs	/*
14719164Sgibbs	 * Place us on the waiting list in case our selection
14819164Sgibbs	 * doesn't win during bus arbitration.
14919164Sgibbs	 */
15023925Sgibbs	mov	SCB_NEXT,WAITING_SCBH;
15123925Sgibbs	mov	WAITING_SCBH, SCBPTR;
15223925Sgibbsstart_waiting:
15323925Sgibbs	/*
15439220Sgibbs	 * Pull the first entry off of the waiting SCB list.
15523925Sgibbs	 */
15623925Sgibbs	mov	SCBPTR, WAITING_SCBH;
15723925Sgibbs	call	start_selection;
15823925Sgibbs	jmp	poll_for_work;
1598104Sgibbs
16023925Sgibbsstart_selection:
16139220Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
16239220Sgibbs		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
16339220Sgibbs		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
16439220Sgibbs		or	SINDEX,A;
16539220Sgibbs		mov	SBLKCTL,SINDEX;		/* select channel */
16639220Sgibbs	}
16723925Sgibbsinitialize_scsiid:
16844507Sgibbs	mov	SINDEX, SCSISEQ_TEMPLATE;
16944507Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
17044507Sgibbs		test	SCB_CONTROL, TARGET_SCB jz . + 4;
17144507Sgibbs		if ((ahc->features & AHC_ULTRA2) != 0) {
17244507Sgibbs			mov	SCSIID_ULTRA2, SCB_CMDPTR[2];
17344507Sgibbs		} else {
17444507Sgibbs			mov	SCSIID, SCB_CMDPTR[2];
17544507Sgibbs		}
17644507Sgibbs		or	SINDEX, TEMODE;
17744507Sgibbs		jmp	initialize_scsiid_fini;
17844507Sgibbs	}
17939220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
18039220Sgibbs		and	A, TID, SCB_TCL;	/* Get target ID */
18139220Sgibbs		and	SCSIID_ULTRA2, OID;	/* Clear old target */
18239220Sgibbs		or	SCSIID_ULTRA2, A;
18339220Sgibbs	} else {
18439220Sgibbs		and	A, TID, SCB_TCL;	/* Get target ID */
18539220Sgibbs		and	SCSIID, OID;		/* Clear old target */
18639220Sgibbs		or	SCSIID, A;
18739220Sgibbs	}
18844507Sgibbsinitialize_scsiid_fini:
18941816Sgibbs	mov	SCSISEQ, SINDEX ret;
19039220Sgibbs
19113177Sgibbs/*
19239220Sgibbs * Initialize transfer settings and clear the SCSI channel.
19339220Sgibbs * SINDEX should contain any additional bit's the client wants
19439220Sgibbs * set in SXFRCTL0.  We also assume that the current SCB is
19539220Sgibbs * a valid SCB for the target we wish to talk to.
19639220Sgibbs */
19739220Sgibbsinitialize_channel:
19839220Sgibbs	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
19939220Sgibbsset_transfer_settings:
20039220Sgibbs	if ((ahc->features & AHC_ULTRA) != 0) {
20139220Sgibbs		test	SCB_CONTROL, ULTRAENB jz . + 2;
20239220Sgibbs		or	SXFRCTL0, FAST20;
20339220Sgibbs	} 
20439220Sgibbs/*
20539220Sgibbs * Initialize SCSIRATE with the appropriate value for this target.
20639220Sgibbs */
20739220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
20839220Sgibbs		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
20939220Sgibbs	} else {
21039220Sgibbs		mov	SCSIRATE, SCB_SCSIRATE ret;
21139220Sgibbs	}
21239220Sgibbs
21339220Sgibbsselection:
21439220Sgibbs	test	SSTAT0,SELDO	jnz select_out;
21539220Sgibbs	mvi	CLRSINT0, CLRSELDI;
21639220Sgibbsselect_in:
21739220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
21841646Sgibbs		if ((ahc->flags & AHC_INITIATORMODE) != 0) {
21941646Sgibbs			test	SSTAT0, TARGET	jz initiator_reselect;
22041646Sgibbs		}
22142652Sgibbs
22239220Sgibbs		/*
22339220Sgibbs		 * We've just been selected.  Assert BSY and
22439220Sgibbs		 * setup the phase for receiving messages
22539220Sgibbs		 * from the target.
22639220Sgibbs		 */
22739220Sgibbs		mvi	SCSISIGO, P_MESGOUT|BSYO;
22841646Sgibbs		mvi	CLRSINT1, CLRBUSFREE;
22939220Sgibbs
23039220Sgibbs		/*
23139220Sgibbs		 * Setup the DMA for sending the identify and
23241299Sgibbs		 * command information.
23339220Sgibbs		 */
23439220Sgibbs		or	SEQ_FLAGS, CMDPHASE_PENDING;
23541299Sgibbs
23641299Sgibbs		mov     A, TQINPOS;
23739220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
23839220Sgibbs			mvi	DINDEX, CCHADDR;
23939220Sgibbs			mvi	TMODE_CMDADDR call set_32byte_addr;
24039220Sgibbs			mvi	CCSCBCTL, CCSCBRESET;
24139220Sgibbs		} else {
24239220Sgibbs			mvi	DINDEX, HADDR;
24339220Sgibbs			mvi	TMODE_CMDADDR call set_32byte_addr;
24439220Sgibbs			mvi	DFCNTRL, FIFORESET;
24539220Sgibbs		}
24639220Sgibbs
24739220Sgibbs		/* Initiator that selected us */
24839220Sgibbs		and	SAVED_TCL, SELID_MASK, SELID;
24939220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
25039220Sgibbs			mov	CCSCBRAM, SAVED_TCL;
25139220Sgibbs		} else {
25239220Sgibbs			mov	DFDAT, SAVED_TCL;
25339220Sgibbs		}
25439220Sgibbs
25539220Sgibbs		/* The Target ID we were selected at */
25644507Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
25744507Sgibbs			if ((ahc->features & AHC_MULTI_TID) != 0) {
25844507Sgibbs				and	CCSCBRAM, OID, TARGIDIN;
25944507Sgibbs			} else if ((ahc->features & AHC_ULTRA2) != 0) {
26044507Sgibbs				and	CCSCBRAM, OID, SCSIID_ULTRA2;
26139220Sgibbs			} else {
26244507Sgibbs				and	CCSCBRAM, OID, SCSIID;
26339220Sgibbs			}
26439220Sgibbs		} else {
26544507Sgibbs			if ((ahc->features & AHC_MULTI_TID) != 0) {
26644507Sgibbs				and	DFDAT, OID, TARGIDIN;
26744507Sgibbs			} else if ((ahc->features & AHC_ULTRA2) != 0) {
26844507Sgibbs				and	DFDAT, OID, SCSIID_ULTRA2;
26939220Sgibbs			} else {
27039220Sgibbs				and	DFDAT, OID, SCSIID;
27139220Sgibbs			}
27239220Sgibbs		}
27339220Sgibbs
27442652Sgibbs		/* No tag yet */
27542652Sgibbs		mvi	INITIATOR_TAG, SCB_LIST_NULL;
27642652Sgibbs
27739220Sgibbs		/*
27839220Sgibbs		 * If ATN isn't asserted, the target isn't interested
27939220Sgibbs		 * in talking to us.  Go directly to bus free.
28039220Sgibbs		 */
28139220Sgibbs		test	SCSISIGI, ATNI	jz	target_busfree;
28239220Sgibbs
28339220Sgibbs		/*
28439220Sgibbs		 * Watch ATN closely now as we pull in messages from the
28539220Sgibbs		 * initiator.  We follow the guidlines from section 6.5
28639220Sgibbs		 * of the SCSI-2 spec for what messages are allowed when.
28739220Sgibbs		 */
28841646Sgibbs		call	target_inb;
28939220Sgibbs
29039220Sgibbs		/*
29139220Sgibbs		 * Our first message must be one of IDENTIFY, ABORT, or
29239220Sgibbs		 * BUS_DEVICE_RESET.
29339220Sgibbs		 */
29441299Sgibbs		/* XXX May need to be more lax here for older initiators... */
29542652Sgibbs		test	DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
29639220Sgibbs		/* Store for host */
29739220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
29839220Sgibbs			mov	CCSCBRAM, DINDEX;
29939220Sgibbs		} else {
30039220Sgibbs			mov	DFDAT, DINDEX;
30139220Sgibbs		}
30239220Sgibbs
30339220Sgibbs		/* Remember for disconnection decision */
30439220Sgibbs		test	DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
30539220Sgibbs		/* XXX Honor per target settings too */
30639220Sgibbs		or	SEQ_FLAGS, NO_DISCONNECT;
30739220Sgibbs
30839220Sgibbs		test	SCSISIGI, ATNI	jz	ident_messages_done;
30941646Sgibbs		call	target_inb;
31039220Sgibbs		/*
31139220Sgibbs		 * If this is a tagged request, the tagged message must
31239220Sgibbs		 * immediately follow the identify.  We test for a valid
31339220Sgibbs		 * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
31439220Sgibbs		 * < MSG_IGN_WIDE_RESIDUE.
31539220Sgibbs		 */
31639220Sgibbs		add	A, -MSG_SIMPLE_Q_TAG, DINDEX;
31739220Sgibbs		jnc	ident_messages_done;
31839220Sgibbs		add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
31939220Sgibbs		jc	ident_messages_done;
32039220Sgibbs		/* Store for host */
32139220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
32239220Sgibbs			mov	CCSCBRAM, DINDEX;
32339220Sgibbs		} else {
32439220Sgibbs			mov	DFDAT, DINDEX;
32539220Sgibbs		}
32639220Sgibbs		
32739220Sgibbs		/*
32839220Sgibbs		 * If the initiator doesn't feel like providing a tag number,
32939220Sgibbs		 * we've got a failed selection and must transition to bus
33039220Sgibbs		 * free.
33139220Sgibbs		 */
33239220Sgibbs		test	SCSISIGI, ATNI	jz	target_busfree;
33342652Sgibbs
33439220Sgibbs		/*
33539220Sgibbs		 * Store the tag for the host.
33639220Sgibbs		 */
33741646Sgibbs		call	target_inb;
33839220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
33939220Sgibbs			mov	CCSCBRAM, DINDEX;
34039220Sgibbs		} else {
34139220Sgibbs			mov	DFDAT, DINDEX;
34239220Sgibbs		}
34342652Sgibbs		mov	INITIATOR_TAG, DINDEX;
34439220Sgibbs		jmp	ident_messages_done;
34539220Sgibbs
34641646Sgibbs		/*
34741646Sgibbs		 * Pushed message loop to allow the kernel to
34842652Sgibbs		 * run it's own target mode message state engine.
34941646Sgibbs		 */
35041646Sgibbshost_target_message_loop:
35141646Sgibbs		mvi	INTSTAT, HOST_MSG_LOOP;
35241646Sgibbs		nop;
35341646Sgibbs		cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
35441646Sgibbs		test	SSTAT0, SPIORDY jz .;
35541646Sgibbs		jmp	host_target_message_loop;
35641646Sgibbs
35739220Sgibbsident_messages_done:
35844507Sgibbs		/* If ring buffer is full, return busy or queue full */
35944507Sgibbs		mov	A, KERNEL_TQINPOS;
36044507Sgibbs		cmp	TQINPOS, A jne tqinfifo_has_space;
36144507Sgibbs		mvi	P_STATUS|BSYO call change_phase;
36244507Sgibbs		cmp	INITIATOR_TAG, SCB_LIST_NULL je . + 3;
36344507Sgibbs		mvi	STATUS_QUEUE_FULL call target_outb;
36444507Sgibbs		jmp	target_busfree_wait;
36544507Sgibbs		mvi	STATUS_BUSY call target_outb;
36644507Sgibbs		jmp	target_busfree_wait;
36744507Sgibbstqinfifo_has_space:	
36839220Sgibbs		/* Terminate the ident list */
36939220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
37039220Sgibbs			mvi	CCSCBRAM, SCB_LIST_NULL;
37139220Sgibbs		} else {
37239220Sgibbs			mvi	DFDAT, SCB_LIST_NULL;
37339220Sgibbs		}
37442652Sgibbs		or	SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
37541646Sgibbs		test	SCSISIGI, ATNI	jnz target_mesgout_pending_msg;
37639220Sgibbs		jmp	target_ITloop;
37739220Sgibbs		
37839220Sgibbs/*
37939220Sgibbs * We carefully toggle SPIOEN to allow us to return the 
38039220Sgibbs * message byte we receive so it can be checked prior to
38139220Sgibbs * driving REQ on the bus for the next byte.
38239220Sgibbs */
38341646Sgibbstarget_inb:
38441646Sgibbs		/*
38541646Sgibbs		 * Drive REQ on the bus by enabling SCSI PIO.
38641646Sgibbs		 */
38739220Sgibbs		or	SXFRCTL0, SPIOEN;
38839220Sgibbs		/* Wait for the byte */
38939220Sgibbs		test	SSTAT0, SPIORDY jz .;
39039220Sgibbs		/* Prevent our read from triggering another REQ */
39139220Sgibbs		and	SXFRCTL0, ~SPIOEN;
39241646Sgibbs		/* Save latched contents */
39339220Sgibbs		mov	DINDEX, SCSIDATL ret;
39439220Sgibbs	}
39539220Sgibbs
39641646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
39739220Sgibbs/*
39823925Sgibbs * Reselection has been initiated by a target. Make a note that we've been
39923925Sgibbs * reselected, but haven't seen an IDENTIFY message from the target yet.
40013177Sgibbs */
40139220Sgibbsinitiator_reselect:
40223925Sgibbs	/* XXX test for and handle ONE BIT condition */
40323925Sgibbs	and	SAVED_TCL, SELID_MASK, SELID;
40439545Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
40539545Sgibbs		test	SBLKCTL, SELBUSB	jz . + 2;
40639545Sgibbs		or	SAVED_TCL, SELBUSB;
40739545Sgibbs	}
40841646Sgibbs	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
40939220Sgibbs	mvi	CLRSINT1,CLRBUSFREE;
41039220Sgibbs	or	SIMODE1, ENBUSFREE;		/*
41139220Sgibbs						 * We aren't expecting a
41239220Sgibbs						 * bus free, so interrupt
41339220Sgibbs						 * the kernel driver if it
41439220Sgibbs						 * happens.
41539220Sgibbs						 */
41639220Sgibbs	mvi	MSG_OUT, MSG_NOOP;		/* No message to send */
41739220Sgibbs	jmp	ITloop;
41841646Sgibbs}
4194568Sgibbs
42013177Sgibbs/*
42123925Sgibbs * After the selection, remove this SCB from the "waiting SCB"
42223925Sgibbs * list.  This is achieved by simply moving our "next" pointer into
42323925Sgibbs * WAITING_SCBH.  Our next pointer will be set to null the next time this
42423925Sgibbs * SCB is used, so don't bother with it now.
42523925Sgibbs */
42639220Sgibbsselect_out:
42725005Sgibbs	/* Turn off the selection hardware */
42841816Sgibbs	and	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
42925005Sgibbs	mvi	CLRSINT0, CLRSELDO;
43025005Sgibbs	mov	SCBPTR, WAITING_SCBH;
43124914Sgibbs	mov	WAITING_SCBH,SCB_NEXT;
43223925Sgibbs	mov	SAVED_TCL, SCB_TCL;
43339220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
43439220Sgibbs		test	SSTAT0, TARGET	jz initiator_select;
4358567Sdg
43639220Sgibbs		/*
43739220Sgibbs		 * We've just re-selected an initiator.
43839220Sgibbs		 * Assert BSY and setup the phase for
43939220Sgibbs		 * sending our identify messages.
44039220Sgibbs		 */
44141646Sgibbs		mvi	P_MESGIN|BSYO call change_phase;
44241646Sgibbs		mvi	CLRSINT1,CLRBUSFREE;
4434568Sgibbs
44439220Sgibbs		/*
44539220Sgibbs		 * Start out with a simple identify message.
44639220Sgibbs		 */
44739220Sgibbs		and	A, LID, SCB_TCL;
44839220Sgibbs		or	A, MSG_IDENTIFYFLAG call target_outb;
4496608Sgibbs
45039220Sgibbs		/*
45139220Sgibbs		 * If we are the result of a tagged command, send
45239220Sgibbs		 * a simple Q tag and the tag id.
45339220Sgibbs		 */
45439220Sgibbs		test	SCB_CONTROL, TAG_ENB	jz . + 3;
45539220Sgibbs		mvi	MSG_SIMPLE_Q_TAG call target_outb;
45642652Sgibbs		mov	SCB_INITIATOR_TAG call target_outb;
45742652Sgibbs		mov	INITIATOR_TAG, SCB_INITIATOR_TAG;
45839220Sgibbstarget_synccmd:
45939220Sgibbs		/*
46039220Sgibbs		 * Now determine what phases the host wants us
46139220Sgibbs		 * to go through.
46239220Sgibbs		 */
46339220Sgibbs		mov	SEQ_FLAGS, SCB_TARGET_PHASES;
46442652Sgibbs		
46539220Sgibbs
46639220Sgibbstarget_ITloop:
46739220Sgibbs		/*
46841646Sgibbs		 * Start honoring ATN signals now that
46944507Sgibbs		 * we properly identified ourselves.
47039220Sgibbs		 */
47141646Sgibbs		test	SCSISIGI, ATNI			jnz target_mesgout;
47239220Sgibbs		test	SEQ_FLAGS, CMDPHASE_PENDING	jnz target_cmdphase;
47339220Sgibbs		test	SEQ_FLAGS, DPHASE_PENDING	jnz target_dphase;
47439220Sgibbs		test	SEQ_FLAGS, SPHASE_PENDING	jnz target_sphase;
47539220Sgibbs
47639220Sgibbs		/*
47739220Sgibbs		 * No more work to do.  Either disconnect or not depending
47839220Sgibbs		 * on the state of NO_DISCONNECT.
47939220Sgibbs		 */
48039220Sgibbs		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
48139220Sgibbs		if ((ahc->flags & AHC_PAGESCBS) != 0) {
48239220Sgibbs			mov	ALLZEROS	call	get_free_or_disc_scb;
48339220Sgibbs		}
48441646Sgibbs		mov	RETURN_1, ALLZEROS;
48539220Sgibbs		call	complete_target_cmd;
48641646Sgibbs		cmp	RETURN_1, CONT_MSG_LOOP jne .;
48739220Sgibbs		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
48839220Sgibbs		mov	SCB_TAG	 call dma_scb;
48939220Sgibbs		jmp	target_synccmd;
49039220Sgibbs
49141646Sgibbstarget_mesgout:
49241646Sgibbs		mvi	SCSISIGO, P_MESGOUT|BSYO;
49341646Sgibbs		call	target_inb;
49441646Sgibbs		/* Local Processing goes here... */
49541646Sgibbstarget_mesgout_pending_msg:
49641646Sgibbs		jmp	host_target_message_loop;
49741646Sgibbs		
49839220Sgibbstarget_disconnect:
49941646Sgibbs		mvi	P_MESGIN|BSYO call change_phase;
50041816Sgibbs		test	SEQ_FLAGS, DPHASE	jz . + 2;
50141816Sgibbs		mvi	MSG_SAVEDATAPOINTER call target_outb;
50239220Sgibbs		mvi	MSG_DISCONNECT call target_outb;
50339220Sgibbs
50443880Sgibbstarget_busfree_wait:
50543880Sgibbs		/* Wait for preceeding I/O session to complete. */
50643880Sgibbs		test	SCSISIGI, ACKI jnz .;
50739220Sgibbstarget_busfree:
50839220Sgibbs		clr	SCSISIGO;
50939220Sgibbs		call	complete_target_cmd;
51039220Sgibbs		jmp	poll_for_work;
51139220Sgibbs
51239220Sgibbstarget_cmdphase:
51341646Sgibbs		mvi	P_COMMAND|BSYO call change_phase;
51441646Sgibbs		call	target_inb;
51539220Sgibbs		mov	A, DINDEX;
51639220Sgibbs		/* Store for host */
51739220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
51839220Sgibbs			mov	CCSCBRAM, A;
51939220Sgibbs		} else {
52039220Sgibbs			mov	DFDAT, A;
52139220Sgibbs		}
52239220Sgibbs
52339220Sgibbs		/*
52439220Sgibbs		 * Determine the number of bytes to read
52541299Sgibbs		 * based on the command group code via table lookup.
52641299Sgibbs		 * We reuse the first 8 bytes of the TARG_SCSIRATE
52741299Sgibbs		 * BIOS array for this table. Count is one less than
52841299Sgibbs		 * the total for the command since we've already fetched
52941299Sgibbs		 * the first byte.
53039220Sgibbs		 */
53139220Sgibbs		shr	A, CMD_GROUP_CODE_SHIFT;
53239220Sgibbs		add	SINDEX, TARG_SCSIRATE, A;
53339220Sgibbs		mov	A, SINDIR;
53439220Sgibbs
53539220Sgibbs		test	A, 0xFF jz command_phase_done;
53639220Sgibbscommand_loop:
53739220Sgibbs		or	SXFRCTL0, SPIOEN;
53839220Sgibbs		test	SSTAT0, SPIORDY jz .;
53939220Sgibbs		cmp	A, 1 jne . + 2;
54039220Sgibbs		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
54139220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
54239220Sgibbs			mov	CCSCBRAM, SCSIDATL;
54339220Sgibbs		} else {
54439220Sgibbs			mov	DFDAT, SCSIDATL;
54539220Sgibbs		}
54639220Sgibbs		dec	A;
54739220Sgibbs		test	A, 0xFF jnz command_loop;
54839220Sgibbs
54939220Sgibbscommand_phase_done:
55039220Sgibbs		and	SEQ_FLAGS, ~CMDPHASE_PENDING;
55139220Sgibbs		jmp	target_ITloop;
55239220Sgibbs
55339220Sgibbstarget_dphase:
55439220Sgibbs		/*
55539220Sgibbs		 * Data direction flags are from the
55639220Sgibbs		 * perspective of the initiator.
55739220Sgibbs		 */
55839220Sgibbs		test	SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
55939220Sgibbs		mvi	LASTPHASE, P_DATAOUT;
56041646Sgibbs		mvi	P_DATAIN|BSYO call change_phase;
56142652Sgibbs		jmp	. + 3;
56239220Sgibbs		mvi	LASTPHASE, P_DATAIN;
56341646Sgibbs		mvi	P_DATAOUT|BSYO call change_phase;
56442652Sgibbs		mov	ALLZEROS call initialize_channel;
56539220Sgibbs		jmp	p_data;
56639220Sgibbs
56739220Sgibbstarget_sphase:
56841646Sgibbs		mvi	P_STATUS|BSYO call change_phase;
56941646Sgibbs		mvi	LASTPHASE, P_STATUS;
57039220Sgibbs		mov	SCB_TARGET_STATUS call target_outb;
57141646Sgibbs		/* XXX Watch for ATN or parity errors??? */
57239220Sgibbs		mvi	SCSISIGO, P_MESGIN|BSYO;
57339220Sgibbs		/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
57439220Sgibbs		mov	ALLZEROS call target_outb;
57543880Sgibbs		jmp	target_busfree_wait;
57639220Sgibbs	
57739220Sgibbscomplete_target_cmd:
57839220Sgibbs		test	SEQ_FLAGS, TARG_CMD_PENDING	jnz . + 2;
57939220Sgibbs		mov	SCB_TAG jmp complete_post;
58039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
58141299Sgibbs			/* Set the valid byte */
58241299Sgibbs			mvi	CCSCBADDR, 24;
58341299Sgibbs			mov	CCSCBRAM, ALLONES;
58441299Sgibbs			mvi	CCHCNT, 28;
58539220Sgibbs			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
58639220Sgibbs			test	CCSCBCTL, CCSCBDONE jz .;
58739220Sgibbs			clr	CCSCBCTL;
58839220Sgibbs		} else {
58941299Sgibbs			/* Set the valid byte */
59041299Sgibbs			or	DFCNTRL, FIFORESET;
59141299Sgibbs			mvi	DFWADDR, 3; /* Third 64bit word or byte 24 */
59241299Sgibbs			mov	DFDAT, ALLONES;
59341299Sgibbs			mvi	HCNT[0], 28;
59441299Sgibbs			clr	HCNT[1];
59541299Sgibbs			clr	HCNT[2];
59639220Sgibbs			or	DFCNTRL, HDMAEN|FIFOFLUSH;
59739220Sgibbs			call	dma_finish;
59839220Sgibbs		}
59941299Sgibbs		inc	TQINPOS;
60041299Sgibbs		mvi	INTSTAT,CMDCMPLT ret;
60139220Sgibbs	}
60241646Sgibbs
60341646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
60439220Sgibbsinitiator_select:
60539220Sgibbs	mvi	SPIOEN call	initialize_channel;
60641646Sgibbs
60741646Sgibbs	/*
60841646Sgibbs	 * We aren't expecting a bus free, so interrupt
60941646Sgibbs	 * the kernel driver if it happens.
61041646Sgibbs	 */
61125005Sgibbs	mvi	CLRSINT1,CLRBUSFREE;
61239220Sgibbs	or	SIMODE1, ENBUSFREE;
61341646Sgibbs
61441646Sgibbs	/*
61541646Sgibbs	 * As soon as we get a successful selection, the target
61641646Sgibbs	 * should go into the message out phase since we have ATN
61741646Sgibbs	 * asserted.
61841646Sgibbs	 */
61939220Sgibbs	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
62039220Sgibbs	or	SEQ_FLAGS, IDENTIFY_SEEN;
62113177Sgibbs
62241646Sgibbs	/*
62341646Sgibbs	 * Main loop for information transfer phases.  Wait for the
62441646Sgibbs	 * target to assert REQ before checking MSG, C/D and I/O for
62541646Sgibbs	 * the bus phase.
62641646Sgibbs	 */
6274568SgibbsITloop:
62839220Sgibbs	call	phase_lock;
6294568Sgibbs
63039220Sgibbs	mov	A, LASTPHASE;
6314568Sgibbs
63239220Sgibbs	test	A, ~P_DATAIN	jz p_data;
63323925Sgibbs	cmp	A,P_COMMAND	je p_command;
63423925Sgibbs	cmp	A,P_MESGOUT	je p_mesgout;
63523925Sgibbs	cmp	A,P_STATUS	je p_status;
63623925Sgibbs	cmp	A,P_MESGIN	je p_mesgin;
6374568Sgibbs
63841646Sgibbs	mvi	INTSTAT,BAD_PHASE;
63923925Sgibbs	jmp	ITloop;			/* Try reading the bus again. */
6404568Sgibbs
64123925Sgibbsawait_busfree:
64223925Sgibbs	and	SIMODE1, ~ENBUSFREE;
64323925Sgibbs	mov	NONE, SCSIDATL;		/* Ack the last byte */
64439220Sgibbs	and	SXFRCTL0, ~SPIOEN;
64523925Sgibbs	test	SSTAT1,REQINIT|BUSFREE	jz .;
64623925Sgibbs	test	SSTAT1, BUSFREE jnz poll_for_work;
64723925Sgibbs	mvi	INTSTAT, BAD_PHASE;
64841646Sgibbs}
64923925Sgibbs	
65023925Sgibbsclear_target_state:
65141646Sgibbs	/*
65241646Sgibbs	 * We assume that the kernel driver may reset us
65341646Sgibbs	 * at any time, even in the middle of a DMA, so
65441646Sgibbs	 * clear DFCNTRL too.
65541646Sgibbs	 */
65641646Sgibbs	clr	DFCNTRL;
65741646Sgibbs
65841646Sgibbs	/*
65941646Sgibbs	 * We don't know the target we will connect to,
66041646Sgibbs	 * so default to narrow transfers to avoid
66141646Sgibbs	 * parity problems.
66241646Sgibbs	 */
66341646Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
66441646Sgibbs		bmov	SCSIRATE, ALLZEROS, 2;
66541646Sgibbs	} else {
66641646Sgibbs		clr	SCSIRATE;
66741646Sgibbs		and	SXFRCTL0, ~(FAST20);
66841646Sgibbs	}
66923925Sgibbs	mvi	LASTPHASE, P_BUSFREE;
67023925Sgibbs	/* clear target specific flags */
67139220Sgibbs	clr	SEQ_FLAGS ret;
67223925Sgibbs
67313177Sgibbs/*
67413177Sgibbs * If we re-enter the data phase after going through another phase, the
67513177Sgibbs * STCNT may have been cleared, so restore it from the residual field.
67613177Sgibbs */
6779928Sgibbsdata_phase_reinit:
67851471Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
67951471Sgibbs		/*
68051471Sgibbs		 * The preload circuitry requires us to
68151471Sgibbs		 * reload the address too, so pull it from
68251471Sgibbs		 * the shaddow address.
68351471Sgibbs		 */
68451471Sgibbs		bmov	HADDR, SHADDR, 4;
68551471Sgibbs		bmov	HCNT, SCB_RESID_DCNT, 3;
68651471Sgibbs	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
68739220Sgibbs		bmov	STCNT, SCB_RESID_DCNT, 3;
68839220Sgibbs	} else {
68939220Sgibbs		mvi	DINDEX, STCNT;
69039220Sgibbs		mvi	SCB_RESID_DCNT	call bcopy_3;
69139220Sgibbs	}
69242652Sgibbs	and	DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
69323925Sgibbs	jmp	data_phase_loop;
6944568Sgibbs
69539220Sgibbsp_data:
69639220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
69739220Sgibbs		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
69839220Sgibbs	} else {
69939220Sgibbs		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
70039220Sgibbs	}
70139220Sgibbs	test	LASTPHASE, IOI jnz . + 2;
70239220Sgibbs	or	DMAPARAMS, DIRECTION;
70323925Sgibbs	call	assert;			/*
70419164Sgibbs					 * Ensure entering a data
70519164Sgibbs					 * phase is okay - seen identify, etc.
70619164Sgibbs					 */
70739220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
70839220Sgibbs		mvi	CCSGADDR, CCSGADDR_MAX;
70939220Sgibbs	}
71023925Sgibbs	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
7114568Sgibbs
71239220Sgibbs	/* We have seen a data phase */
71339220Sgibbs	or	SEQ_FLAGS, DPHASE;
71439220Sgibbs
71519164Sgibbs	/*
71619164Sgibbs	 * Initialize the DMA address and counter from the SCB.
71719164Sgibbs	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
71819164Sgibbs	 * modify the values in the SCB itself until we see a
71919164Sgibbs	 * save data pointers message.
72019164Sgibbs	 */
72139220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
72239220Sgibbs		bmov	HADDR, SCB_DATAPTR, 7;
72339220Sgibbs	} else {
72439220Sgibbs		mvi	DINDEX, HADDR;
72539220Sgibbs		mvi	SCB_DATAPTR	call bcopy_7;
72639220Sgibbs	}
72742652Sgibbs	and	DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
72819164Sgibbs
72939220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
73039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
73139220Sgibbs			bmov	STCNT, HCNT, 3;
73239220Sgibbs		} else {
73339220Sgibbs			call	set_stcnt_from_hcnt;
73439220Sgibbs		}
73539220Sgibbs	}
73619164Sgibbs
73739220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
73839220Sgibbs		bmov	SG_COUNT, SCB_SGCOUNT, 5;
73939220Sgibbs	} else {
74039220Sgibbs		mvi	DINDEX, SG_COUNT;
74139220Sgibbs		mvi	SCB_SGCOUNT	call bcopy_5;
74239220Sgibbs	}
74319164Sgibbs
7449928Sgibbsdata_phase_loop:
74516260Sgibbs/* Guard against overruns */
74623925Sgibbs	test	SG_COUNT, 0xff jnz data_phase_inbounds;
74716260Sgibbs/*
74816260Sgibbs * Turn on 'Bit Bucket' mode, set the transfer count to
74916260Sgibbs * 16meg and let the target run until it changes phase.
75016260Sgibbs * When the transfer completes, notify the host that we
75116260Sgibbs * had an overrun.
75216260Sgibbs */
75323925Sgibbs	or	SXFRCTL1,BITBUCKET;
75439220Sgibbs	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
75539220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
75639220Sgibbs		bmov	HCNT, ALLONES, 3;
75739220Sgibbs	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
75839220Sgibbs		bmov	STCNT, ALLONES, 3;
75939220Sgibbs	} else {
76039220Sgibbs		mvi	STCNT[0], 0xFF;
76139220Sgibbs		mvi	STCNT[1], 0xFF;
76239220Sgibbs		mvi	STCNT[2], 0xFF;
76339220Sgibbs	}
76416260Sgibbsdata_phase_inbounds:
76539220Sgibbs/* If we are the last SG block, tell the hardware. */
76623925Sgibbs	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
76739220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
76839220Sgibbs		or	SG_CACHEPTR, LAST_SEG;
76939220Sgibbs	} else {
77039220Sgibbs		and	DMAPARAMS, ~WIDEODD;
77139220Sgibbs	}
7729928Sgibbsdata_phase_wideodd:
77339220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
77439220Sgibbs		mov	SINDEX, ALLONES;
77539220Sgibbs		mov	DFCNTRL, DMAPARAMS;
77639220Sgibbs		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
77739220Sgibbsdata_phase_dma_loop:
77839220Sgibbs		test	SSTAT0,	SDONE jnz data_phase_dma_done;
77939220Sgibbs		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
78039220Sgibbsdata_phase_dma_phasemis:
78139220Sgibbs		test	SSTAT0,SDONE	jnz . + 2;
78239220Sgibbs		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
78339220Sgibbs	} else {
78439220Sgibbs		mov	DMAPARAMS  call dma;
78539220Sgibbs	}
7864568Sgibbs
78739220Sgibbsdata_phase_dma_done:
78816260Sgibbs/* Go tell the host about any overruns */
78923925Sgibbs	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
79016260Sgibbs
79122451Sgibbs/* Exit if we had an underrun.  dma clears SINDEX in this case. */
79223925Sgibbs	test	SINDEX,0xff	jz data_phase_finish;
7937532Sgibbs
79413177Sgibbs/*
79513177Sgibbs * Advance the scatter-gather pointers if needed 
79613177Sgibbs */
7979928Sgibbssg_advance:
79823925Sgibbs	dec	SG_COUNT;	/* one less segment to go */
7994568Sgibbs
80023925Sgibbs	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
80113177Sgibbs/*
80213177Sgibbs * Load a struct scatter and set up the data address and length.
80313177Sgibbs * If the working value of the SG count is nonzero, then
80413177Sgibbs * we need to load a new set of values.
80513177Sgibbs *
80615328Sgibbs * This, like all DMA's, assumes little-endian host data storage.
80713177Sgibbs */
8089928Sgibbssg_load:
80939220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
81039220Sgibbs		/*
81139220Sgibbs		 * Do we have any prefetch left???
81239220Sgibbs		 */
81339220Sgibbs		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
8144568Sgibbs
81539220Sgibbs		/*
81639220Sgibbs		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
81739220Sgibbs		 */
81839220Sgibbs		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
81939220Sgibbs		mvi	A, CCSGADDR_MAX;
82039220Sgibbs		jc	. + 2;
82139220Sgibbs		shl	A, 3, SG_COUNT;
82239220Sgibbs		mov	CCHCNT, A;
82339220Sgibbs		bmov	CCHADDR, SG_NEXT, 4;
82439220Sgibbs		mvi	CCSGCTL, CCSGEN|CCSGRESET;
82539220Sgibbs		test	CCSGCTL, CCSGDONE jz .;
82639220Sgibbs		and	CCSGCTL, ~CCSGEN;
82739220Sgibbs		test	CCSGCTL, CCSGEN jnz .;
82839220Sgibbs		mvi	CCSGCTL, CCSGRESET;
82939220Sgibbsprefetched_segs_avail:
83039220Sgibbs		bmov 	HADDR, CCSGRAM, 8;
83139220Sgibbs	} else {
83239220Sgibbs		mvi	DINDEX, HADDR;
83339220Sgibbs		mvi	SG_NEXT	call bcopy_4;
83422568Sgibbs
83539220Sgibbs		mvi	HCNT[0],SG_SIZEOF;
83639220Sgibbs		clr	HCNT[1];
83739220Sgibbs		clr	HCNT[2];
8389928Sgibbs
83939220Sgibbs		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
8409928Sgibbs
84139220Sgibbs		call	dma_finish;
8429928Sgibbs
84339220Sgibbs		/*
84439220Sgibbs		 * Copy data from FIFO into SCB data pointer and data count.
84539220Sgibbs		 * This assumes that the SG segments are of the form:
84639220Sgibbs		 * struct ahc_dma_seg {
84739220Sgibbs		 *	u_int32_t	addr;	four bytes, little-endian order
84839220Sgibbs		 *	u_int32_t	len;	four bytes, little endian order
84939220Sgibbs		 * };
85039220Sgibbs		 */
85139220Sgibbs		mvi	HADDR	call dfdat_in_7;
85239220Sgibbs	}
85339220Sgibbs
85442652Sgibbs	/* Track odd'ness */
85542652Sgibbs	test	HCNT[0], 0x1 jz . + 2;
85642652Sgibbs	xor	DATA_COUNT_ODD, 0x1;
85742652Sgibbs
85839220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
85939220Sgibbs		/* Load STCNT as well.  It is a mirror of HCNT */
86039220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) != 0) {
86139220Sgibbs			bmov	STCNT, HCNT, 3;
86239220Sgibbs		} else {
86339220Sgibbs			call	set_stcnt_from_hcnt;
86439220Sgibbs		}
86539220Sgibbs	}
86639220Sgibbs
86739220Sgibbs/* Advance the SG pointer */
86839220Sgibbs	clr	A;			/* add sizeof(struct scatter) */
86939220Sgibbs	add	SG_NEXT[0],SG_SIZEOF;
87039220Sgibbs	adc	SG_NEXT[1],A;
87139220Sgibbs
87241646Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
87341646Sgibbs		test	SSTAT0, TARGET jnz data_phase_loop;
87441646Sgibbs	}
87541646Sgibbs	test	SSTAT1, REQINIT jz .;
87623925Sgibbs	test	SSTAT1,PHASEMIS	jz data_phase_loop;
87741646Sgibbs
87839220Sgibbs	/* Ensure the last seg is visable at the shaddow layer */
87939220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
88039220Sgibbs		or	DFCNTRL, PRELOADEN;
88139220Sgibbs	}
8824568Sgibbs
8839928Sgibbsdata_phase_finish:
88439220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
88539220Sgibbs		call	ultra2_dmafinish;
88639220Sgibbs	}
88713177Sgibbs/*
88813177Sgibbs * After a DMA finishes, save the SG and STCNT residuals back into the SCB
88913177Sgibbs * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
89013177Sgibbs * were transferred on the SCSI (as opposed to the host) bus.
89113177Sgibbs */
89239220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
89339220Sgibbs		bmov	SCB_RESID_DCNT, STCNT, 3;
89439220Sgibbs	} else {
89539220Sgibbs		mov	SCB_RESID_DCNT[0],STCNT[0];
89639220Sgibbs		mov	SCB_RESID_DCNT[1],STCNT[1];
89739220Sgibbs		mov	SCB_RESID_DCNT[2],STCNT[2];
89839220Sgibbs	}
89923925Sgibbs	mov	SCB_RESID_SGCNT, SG_COUNT;
90022568Sgibbs
90139220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
90239220Sgibbs		or	SXFRCTL0, CLRSTCNT|CLRCHN;
90339220Sgibbs	}
90422568Sgibbs
90539220Sgibbs	if ((ahc->flags & AHC_TARGETMODE) != 0) {
90641646Sgibbs		test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
90739220Sgibbs		and	SEQ_FLAGS, ~DPHASE_PENDING;
90842652Sgibbs		/*
90942652Sgibbs		 * For data-in phases, wait for any pending acks from the
91042652Sgibbs		 * initiator before changing phase.
91142652Sgibbs		 */
91242652Sgibbs		test	DFCNTRL, DIRECTION jz target_ITloop;
91342652Sgibbs		test	SSTAT1, REQINIT	jnz .;
91439220Sgibbs		jmp	target_ITloop;
91539220Sgibbs	}
91623925Sgibbs	jmp	ITloop;
9174568Sgibbs
91816260Sgibbsdata_phase_overrun:
91939220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
92039220Sgibbs		call	ultra2_dmafinish;
92139220Sgibbs		or	SXFRCTL0, CLRSTCNT|CLRCHN;
92239220Sgibbs	}
92313177Sgibbs/*
92416260Sgibbs * Turn off BITBUCKET mode and notify the host
92516260Sgibbs */
92623925Sgibbs	and	SXFRCTL1, ~BITBUCKET;
92723925Sgibbs	mvi	INTSTAT,DATA_OVERRUN;
92823925Sgibbs	jmp	ITloop;
92916260Sgibbs
93039220Sgibbsultra2_dmafinish:
93139220Sgibbs	if ((ahc->features & AHC_ULTRA2) != 0) {
93239220Sgibbs		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
93339220Sgibbs		and	DFCNTRL, ~SCSIEN;
93439220Sgibbs		test	DFCNTRL, SCSIEN jnz .;
93551471Sgibbsultra2_dmafifoflush:
93639220Sgibbs		or	DFCNTRL, FIFOFLUSH;
93751471Sgibbs		/*
93851471Sgibbs		 * The FIFOEMP status bit on the Ultra2 class
93951471Sgibbs		 * of controllers seems to be a bit flaky.
94051471Sgibbs		 * It appears that if the FIFO is full and the
94151471Sgibbs		 * transfer ends with some data in the REQ/ACK
94251471Sgibbs		 * FIFO, FIFOEMP will fall temporarily
94351471Sgibbs		 * as the data is transferred to the PCI bus.
94451471Sgibbs		 * This glitch lasts for fewer than 5 clock cycles,
94551471Sgibbs		 * so we work around the problem by ensuring the
94651471Sgibbs		 * status bit stays false through a full glitch
94751471Sgibbs		 * window.
94851471Sgibbs		 */
94951471Sgibbs		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
95051471Sgibbs		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
95151471Sgibbs		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
95251471Sgibbs		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
95351471Sgibbs		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
95451471Sgibbs
95551471Sgibbsultra2_dmafifoempty:
95651471Sgibbs		/* Don't clobber an inprogress host data transfer */
95751471Sgibbs		test	DFSTATUS, MREQPEND	jnz ultra2_dmafifoempty;
95851471Sgibbs
95939220Sgibbsultra2_dmahalt:
96039220Sgibbs		and     DFCNTRL, ~(SCSIEN|HDMAEN);
96139220Sgibbs		test	DFCNTRL, HDMAEN jnz .;
96239220Sgibbs		ret;
96339220Sgibbs	}
96439220Sgibbs
96541646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
96616260Sgibbs/*
96715328Sgibbs * Command phase.  Set up the DMA registers and let 'er rip.
96813177Sgibbs */
9694568Sgibbsp_command:
97023925Sgibbs	call	assert;
9714568Sgibbs
97239220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
97339220Sgibbs		mov	HCNT[0], SCB_CMDLEN;
97439220Sgibbs		bmov	HCNT[1], ALLZEROS, 2;
97539220Sgibbs		if ((ahc->features & AHC_ULTRA2) == 0) {
97639220Sgibbs			bmov	STCNT, HCNT, 3;
97739220Sgibbs		}
97839220Sgibbs		add	NONE, -17, SCB_CMDLEN;
97939220Sgibbs		jc	dma_cmd_data;
98039220Sgibbs		if ((ahc->features & AHC_ULTRA2) != 0) {
98139220Sgibbs			mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
98239220Sgibbs		} else {
98339220Sgibbs			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
98439220Sgibbs		}
98539220Sgibbs		bmov   DFDAT, SCB_CMDSTORE, 16; 
98639220Sgibbs		jmp	cmd_loop;
98739220Sgibbsdma_cmd_data:
98839220Sgibbs		bmov	HADDR, SCB_CMDPTR, 4;
98939220Sgibbs	} else {
99039220Sgibbs		mvi	DINDEX, HADDR;
99139220Sgibbs		mvi	SCB_CMDPTR	call bcopy_5;
99239220Sgibbs		clr	HCNT[1];
99339220Sgibbs		clr	HCNT[2];
99439220Sgibbs	}
9954568Sgibbs
99639220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
99739220Sgibbs		if ((ahc->features & AHC_CMD_CHAN) == 0) {
99839220Sgibbs			call	set_stcnt_from_hcnt;
99939220Sgibbs		}
100039220Sgibbs		mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
100139220Sgibbs	} else {
100239220Sgibbs		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
100339220Sgibbs	}
100439220Sgibbscmd_loop:
100539220Sgibbs	test	SSTAT0, SDONE jnz . + 2;
100639220Sgibbs	test    SSTAT1, PHASEMIS jz cmd_loop;
100739220Sgibbs	and	DFCNTRL, ~(SCSIEN|HDMAEN|SDMAEN);
100839220Sgibbs	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
100923925Sgibbs	jmp	ITloop;
10104568Sgibbs
101113177Sgibbs/*
101213177Sgibbs * Status phase.  Wait for the data byte to appear, then read it
101313177Sgibbs * and store it into the SCB.
101413177Sgibbs */
10154568Sgibbsp_status:
101623925Sgibbs	call	assert;
101719803Sgibbs
101823925Sgibbs	mov	SCB_TARGET_STATUS, SCSIDATL;
101923925Sgibbs	jmp	ITloop;
10204568Sgibbs
102113177Sgibbs/*
102241646Sgibbs * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
102341646Sgibbs * indentify message sequence and send it to the target.  The host may
102441646Sgibbs * override this behavior by setting the MK_MESSAGE bit in the SCB
102541646Sgibbs * control byte.  This will cause us to interrupt the host and allow
102641646Sgibbs * it to handle the message phase completely on its own.  If the bit
102741646Sgibbs * associated with this target is set, we will also interrupt the host,
102841646Sgibbs * thereby allowing it to send a message on the next selection regardless
102941646Sgibbs * of the transaction being sent.
103039220Sgibbs * 
103139220Sgibbs * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
103241646Sgibbs * This is done to allow the host to send messages outside of an identify
103339220Sgibbs * sequence while protecting the seqencer from testing the MK_MESSAGE bit
103439220Sgibbs * on an SCB that might not be for the current nexus. (For example, a
103539220Sgibbs * BDR message in responce to a bad reselection would leave us pointed to
103639220Sgibbs * an SCB that doesn't have anything to do with the current target).
103741646Sgibbs *
103839220Sgibbs * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
103939220Sgibbs * bus device reset).
104039220Sgibbs *
104139220Sgibbs * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
104239220Sgibbs * in case the target decides to put us in this phase for some strange
104339220Sgibbs * reason.
104413177Sgibbs */
104541646Sgibbsp_mesgout_retry:
104641646Sgibbs	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
10474568Sgibbsp_mesgout:
104839220Sgibbs	mov	SINDEX, MSG_OUT;
104939220Sgibbs	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
105041646Sgibbs	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
105141646Sgibbs	mov	FUNCTION1, SCB_TCL;
105241646Sgibbs	mov	A, FUNCTION1;
105347158Sgibbs	if ((ahc->features & AHC_HS_MAILBOX) != 0) {
105447158Sgibbs		/*
105547158Sgibbs		 * Work around a pausing bug in at least the aic7890.
105647158Sgibbs		 * If the host needs to update the TARGET_MSG_REQUEST
105747158Sgibbs		 * bit field, it will set the HS_MAILBOX to 1.  In
105847158Sgibbs		 * response, we pause with a specific interrupt code
105947158Sgibbs		 * asking for the mask to be updated before we continue.
106047158Sgibbs		 * Ugh.
106147158Sgibbs		 */
106247158Sgibbs		test	HS_MAILBOX, 0xF0	jz . + 2;
106347158Sgibbs		mvi	INTSTAT, UPDATE_TMSG_REQ;
106447414Sgibbs		nop;
106547158Sgibbs	}
106647414Sgibbs	mov	SINDEX, TARGET_MSG_REQUEST[0];
106741646Sgibbs	if ((ahc->features & AHC_TWIN) != 0) {
106841646Sgibbs		/* Second Channel uses high byte bits */
106941646Sgibbs		test	SCB_TCL, SELBUSB	jz . + 2;
107041646Sgibbs		mov	SINDEX, TARGET_MSG_REQUEST[1];
107141646Sgibbs	} else if ((ahc->features & AHC_WIDE) != 0) {
107241646Sgibbs		test	SCB_TCL, 0x80		jz . + 2; /* target > 7 */
107341646Sgibbs		mov	SINDEX, TARGET_MSG_REQUEST[1];
107441646Sgibbs	}
107541646Sgibbs	test	SINDEX, A	jnz host_message_loop;
107639220Sgibbsp_mesgout_identify:
107741299Sgibbs	and	SINDEX,LID,SCB_TCL;	/* lun */
107839220Sgibbs	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
107939220Sgibbs	or	SINDEX,A;		/* or in disconnect privledge */
108039220Sgibbs	or	SINDEX,MSG_IDENTIFYFLAG;
108113177Sgibbs/*
108239220Sgibbs * Send a tag message if TAG_ENB is set in the SCB control block.
108339220Sgibbs * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
108413177Sgibbs */
108539220Sgibbsp_mesgout_tag:
108639220Sgibbs	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
108739220Sgibbs	mov	SCSIDATL, SINDEX;	/* Send the identify message */
108839220Sgibbs	call	phase_lock;
108939220Sgibbs	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
109039220Sgibbs	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
109139220Sgibbs	call	phase_lock;
109239220Sgibbs	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
109339220Sgibbs	mov	SCB_TAG	jmp p_mesgout_onebyte;
109413177Sgibbs/*
109541646Sgibbs * Interrupt the driver, and allow it to handle this message
109641646Sgibbs * phase and any required retries.
109713177Sgibbs */
109839220Sgibbsp_mesgout_from_host:
109939220Sgibbs	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
110041646Sgibbs	jmp	host_message_loop;
110139220Sgibbs
110239220Sgibbsp_mesgout_onebyte:
110339220Sgibbs	mvi	CLRSINT1, CLRATNO;
110439220Sgibbs	mov	SCSIDATL, SINDEX;
110539220Sgibbs
110613177Sgibbs/*
110741646Sgibbs * If the next bus phase after ATN drops is message out, it means
110813177Sgibbs * that the target is requesting that the last message(s) be resent.
110913177Sgibbs */
111039220Sgibbs	call	phase_lock;
111141646Sgibbs	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
11124568Sgibbs
111319906Sgibbsp_mesgout_done:
111423925Sgibbs	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
111539220Sgibbs	mov	LAST_MSG, MSG_OUT;
111639220Sgibbs	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
111723925Sgibbs	jmp	ITloop;
11184568Sgibbs
111913177Sgibbs/*
112013177Sgibbs * Message in phase.  Bytes are read using Automatic PIO mode.
112113177Sgibbs */
11224568Sgibbsp_mesgin:
112323925Sgibbs	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
11244568Sgibbs
112523925Sgibbs	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
112623925Sgibbs	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
112723925Sgibbs	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
112823925Sgibbs	cmp	ALLZEROS,A		je mesgin_complete;
112923925Sgibbs	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
113023925Sgibbs	cmp	A,MSG_NOOP		je mesgin_done;
11314568Sgibbs
113213177Sgibbs/*
113341887Sgibbs * Pushed message loop to allow the kernel to
113441887Sgibbs * RUN IT's own message state engine.  To avoid an
113541887Sgibbs * extra nop instruction after signaling the kernel,
113641887Sgibbs * we perform the phase_lock before checking to see
113741887Sgibbs * if we should exit the loop and skip the phase_lock
113841887Sgibbs * in the ITloop.  Performing back to back phase_locks
113941887Sgibbs * shouldn't hurt, but why do it twice...
114013177Sgibbs */
114141887Sgibbshost_message_loop:
114241887Sgibbs	mvi	INTSTAT, HOST_MSG_LOOP;
114341887Sgibbs	call	phase_lock;
114441887Sgibbs	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
114541887Sgibbs	jmp	host_message_loop;
11469954Sgibbs
11479954Sgibbsmesgin_done:
114823925Sgibbs	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
114923925Sgibbs	jmp	ITloop;
11509954Sgibbs
11519954Sgibbs
11529954Sgibbsmesgin_complete:
115313177Sgibbs/*
115419164Sgibbs * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
115519164Sgibbs * and trigger a completion interrupt.  Before doing so, check to see if there
115639220Sgibbs * is a residual or the status byte is something other than STATUS_GOOD (0).
115739220Sgibbs * In either of these conditions, we upload the SCB back to the host so it can
115819164Sgibbs * process this information.  In the case of a non zero status byte, we 
115919164Sgibbs * additionally interrupt the kernel driver synchronously, allowing it to
116019164Sgibbs * decide if sense should be retrieved.  If the kernel driver wishes to request
116119164Sgibbs * sense, it will fill the kernel SCB with a request sense command and set
116219164Sgibbs * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
116319164Sgibbs * the SCB, and process it as the next command by adding it to the waiting list.
116419164Sgibbs * If the kernel driver does not wish to request sense, it need only clear
116519164Sgibbs * RETURN_1, and the command is allowed to complete normally.  We don't bother
116619164Sgibbs * to post to the QOUTFIFO in the error cases since it would require extra
116719164Sgibbs * work in the kernel driver to ensure that the entry was removed before the
116819164Sgibbs * command complete code tried processing it.
116913177Sgibbs */
117019164Sgibbs
117113177Sgibbs/*
117219164Sgibbs * First check for residuals
117313177Sgibbs */
117423925Sgibbs	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
117539220Sgibbs	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
117619164Sgibbsupload_scb:
117723925Sgibbs	mvi	DMAPARAMS, FIFORESET;
117823925Sgibbs	mov	SCB_TAG		call dma_scb;
11797532Sgibbscheck_status:
118039220Sgibbs	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
118123925Sgibbs	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
118239220Sgibbs	nop;
118339220Sgibbs	cmp	RETURN_1, SEND_SENSE	jne complete;
118419164Sgibbs	/* This SCB becomes the next to execute as it will retrieve sense */
118523925Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
118639220Sgibbs	mov	SCB_TAG		call dma_scb;
118719164Sgibbsadd_to_waiting_list:
118823925Sgibbs	mov	SCB_NEXT,WAITING_SCBH;
118923925Sgibbs	mov	WAITING_SCBH, SCBPTR;
119023925Sgibbs	/*
119123925Sgibbs	 * Prepare our selection hardware before the busfree so we have a
119223925Sgibbs	 * high probability of winning arbitration.
119323925Sgibbs	 */
119423925Sgibbs	call	start_selection;
119523925Sgibbs	jmp	await_busfree;
119639220Sgibbs
119739220Sgibbscomplete:
119839220Sgibbs	/* If we are untagged, clear our address up in host ram */
119939220Sgibbs	test	SCB_CONTROL, TAG_ENB jnz complete_queue;
120039220Sgibbs	mov	A, SAVED_TCL;
120139220Sgibbs	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
120239220Sgibbs	mvi	SCB_LIST_NULL call post_byte;
120339220Sgibbs
120439220Sgibbscomplete_queue:
120539220Sgibbs	mov	SCB_TAG call complete_post;
120623925Sgibbs	jmp	await_busfree;
120741646Sgibbs}
12084568Sgibbs
120939220Sgibbscomplete_post:
121039220Sgibbs	/* Post the SCBID in SINDEX and issue an interrupt */
121144507Sgibbs	call	add_scb_to_free_list;
121239220Sgibbs	mov	ARG_1, SINDEX;
121339220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
121439220Sgibbs		mov	A, SDSCB_QOFF;
121539220Sgibbs	} else {
121639220Sgibbs		mov	A, QOUTPOS;
121739220Sgibbs	}
121839220Sgibbs	mvi	QOUTFIFO_OFFSET call post_byte_setup;
121939220Sgibbs	mov	ARG_1 call post_byte;
122039220Sgibbs	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
122139220Sgibbs		inc 	QOUTPOS;
122239220Sgibbs	}
122339220Sgibbs	mvi	INTSTAT,CMDCMPLT ret;
122439220Sgibbs
122541646Sgibbsif ((ahc->flags & AHC_INITIATORMODE) != 0) {
122613177Sgibbs/*
122713177Sgibbs * Is it a disconnect message?  Set a flag in the SCB to remind us
122813177Sgibbs * and await the bus going free.
122913177Sgibbs */
12309954Sgibbsmesgin_disconnect:
123123925Sgibbs	or	SCB_CONTROL,DISCONNECTED;
123223925Sgibbs	call	add_scb_to_disc_list;
123323925Sgibbs	jmp	await_busfree;
123419164Sgibbs
123515328Sgibbs/*
123619164Sgibbs * Save data pointers message:
123719164Sgibbs * Copying RAM values back to SCB, for Save Data Pointers message, but
123819164Sgibbs * only if we've actually been into a data phase to change them.  This
123919164Sgibbs * protects against bogus data in scratch ram and the residual counts
124019164Sgibbs * since they are only initialized when we go into data_in or data_out.
124115328Sgibbs */
124219164Sgibbsmesgin_sdptrs:
124323925Sgibbs	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
12444568Sgibbs
124539220Sgibbs	/*
124639220Sgibbs	 * The SCB SGPTR becomes the next one we'll download,
124739220Sgibbs	 * and the SCB DATAPTR becomes the current SHADDR.
124839220Sgibbs	 * Use the residual number since STCNT is corrupted by
124939220Sgibbs	 * any message transfer.
125039220Sgibbs	 */
125139220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
125239220Sgibbs		bmov	SCB_SGCOUNT, SG_COUNT, 5;
125339220Sgibbs		bmov	SCB_DATAPTR, SHADDR, 4;
125439220Sgibbs		bmov	SCB_DATACNT, SCB_RESID_DCNT, 3;
125539220Sgibbs	} else {
125639220Sgibbs		mvi	DINDEX, SCB_SGCOUNT;
125739220Sgibbs		mvi	SG_COUNT call bcopy_5;
125819164Sgibbs	
125939220Sgibbs		mvi	DINDEX, SCB_DATAPTR;
126039220Sgibbs		mvi	SHADDR		call bcopy_4;
126139220Sgibbs		mvi	SCB_RESID_DCNT	call bcopy_3;
126239220Sgibbs	}
126323925Sgibbs	jmp	mesgin_done;
12644568Sgibbs
126513177Sgibbs/*
126613177Sgibbs * Restore pointers message?  Data pointers are recopied from the
126713177Sgibbs * SCB anytime we enter a data phase for the first time, so all
126813177Sgibbs * we need to do is clear the DPHASE flag and let the data phase
126913177Sgibbs * code do the rest.
127013177Sgibbs */
12719954Sgibbsmesgin_rdptrs:
127223925Sgibbs	and	SEQ_FLAGS, ~DPHASE;		/*
127323925Sgibbs						 * We'll reload them
127413177Sgibbs						 * the next time through
127523925Sgibbs						 * the dataphase.
127613177Sgibbs						 */
127723925Sgibbs	jmp	mesgin_done;
12784568Sgibbs
127913177Sgibbs/*
128013177Sgibbs * Identify message?  For a reconnecting target, this tells us the lun
128113177Sgibbs * that the reconnection is for - find the correct SCB and switch to it,
128213177Sgibbs * clearing the "disconnected" bit so we don't "find" it by accident later.
128313177Sgibbs */
12849954Sgibbsmesgin_identify:
128539220Sgibbs	
128639220Sgibbs	if ((ahc->features & AHC_WIDE) != 0) {
128739220Sgibbs		and	A,0x0f;		/* lun in lower four bits */
128839220Sgibbs	} else {
128939220Sgibbs		and	A,0x07;		/* lun in lower three bits */
129039220Sgibbs	}
129123925Sgibbs	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
129239220Sgibbs
129339220Sgibbs	mvi	ARG_2, SCB_LIST_NULL;	/* SCBID of prev SCB in disc List */
129439220Sgibbs	call	get_untagged_SCBID;
129539220Sgibbs	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
129639220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
129739220Sgibbs		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
129839220Sgibbs	}
129939220Sgibbs	/*
130039220Sgibbs	 * If the SCB was found in the disconnected list (as is
130139220Sgibbs	 * always the case in non-paging scenarios), SCBPTR is already
130239220Sgibbs	 * set to the correct SCB.  So, simply setup the SCB and get
130339220Sgibbs	 * on with things.
130439220Sgibbs	 */
130539220Sgibbs	call	rem_scb_from_disc_list;
130624608Sgibbs	jmp	setup_SCB;
130713177Sgibbs/*
130813177Sgibbs * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
130923168Sgibbs * If we get one, we use the tag returned to find the proper
131039220Sgibbs * SCB.  With SCB paging, this requires using search for both tagged
131115328Sgibbs * and non-tagged transactions since the SCB may exist in any slot.
131215328Sgibbs * If we're not using SCB paging, we can use the tag as the direct
131315328Sgibbs * index to the SCB.
131413177Sgibbs */
131524608Sgibbssnoop_tag:
131623925Sgibbs	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
131713177Sgibbssnoop_tag_loop:
131839220Sgibbs	call	phase_lock;
131923925Sgibbs	cmp	LASTPHASE, P_MESGIN	jne not_found;
132023925Sgibbs	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
13216608Sgibbsget_tag:
132223925Sgibbs	mvi	ARG_1	call inb_next;	/* tag value */
132313177Sgibbs
132439220Sgibbs	/*
132539220Sgibbs	 * Ensure that the SCB the tag points to is for
132639220Sgibbs	 * an SCB transaction to the reconnecting target.
132739220Sgibbs	 */
132839220Sgibbsuse_retrieveSCB:
132939220Sgibbs	call	retrieveSCB;
133039220Sgibbssetup_SCB:
133124608Sgibbs	mov	A, SAVED_TCL;
133239220Sgibbs	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
133339220Sgibbs	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
133423925Sgibbs	and	SCB_CONTROL,~DISCONNECTED;
133523925Sgibbs	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
133639220Sgibbs	call	set_transfer_settings;
133739220Sgibbs	/* See if the host wants to send a message upon reconnection */
133839220Sgibbs	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
133939220Sgibbs	and	SCB_CONTROL, ~MK_MESSAGE;
134039220Sgibbs	mvi	HOST_MSG	call mk_mesg;
134123925Sgibbs	jmp	mesgin_done;
134215328Sgibbs
134339220Sgibbsnot_found_cleanup_scb:
134439220Sgibbs	test	SCB_CONTROL, DISCONNECTED jz . + 3;
134539220Sgibbs	call	add_scb_to_disc_list;
134639220Sgibbs	jmp	not_found;
134739220Sgibbs	call	add_scb_to_free_list;
134819218Sgibbsnot_found:
134923925Sgibbs	mvi	INTSTAT, NO_MATCH;
135023925Sgibbs	jmp	mesgin_done;
13516608Sgibbs
135213177Sgibbs/*
135313177Sgibbs * [ ADD MORE MESSAGE HANDLING HERE ]
135413177Sgibbs */
13554568Sgibbs
135613177Sgibbs/*
135713177Sgibbs * Locking the driver out, build a one-byte message passed in SINDEX
135813177Sgibbs * if there is no active message already.  SINDEX is returned intact.
135913177Sgibbs */
13604568Sgibbsmk_mesg:
136123925Sgibbs	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
136239220Sgibbs	mov	MSG_OUT,SINDEX ret;
13634568Sgibbs
136413177Sgibbs/*
136513177Sgibbs * Functions to read data in Automatic PIO mode.
136613177Sgibbs *
136713177Sgibbs * According to Adaptec's documentation, an ACK is not sent on input from
136813177Sgibbs * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
136913177Sgibbs * latched (the usual way), then read the data byte directly off the bus
137013177Sgibbs * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
137113177Sgibbs * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
137213177Sgibbs * spec guarantees that the target will hold the data byte on the bus until
137313177Sgibbs * we send our ACK.
137413177Sgibbs *
137513177Sgibbs * The assumption here is that these are called in a particular sequence,
137613177Sgibbs * and that REQ is already set when inb_first is called.  inb_{first,next}
137713177Sgibbs * use the same calling convention as inb.
137813177Sgibbs */
137913177Sgibbs
138013177Sgibbsinb_next:
138123925Sgibbs	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
138213360Sgibbsinb_next_wait:
138321947Sgibbs	/*
138421947Sgibbs	 * If there is a parity error, wait for the kernel to
138521947Sgibbs	 * see the interrupt and prepare our message response
138621947Sgibbs	 * before continuing.
138721947Sgibbs	 */
138823925Sgibbs	test	SSTAT1, REQINIT	jz inb_next_wait;
138923925Sgibbs	test	SSTAT1, SCSIPERR jnz inb_next_wait;
139023925Sgibbs	and	LASTPHASE, PHASE_MASK, SCSISIGI;
139123925Sgibbs	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
139219623Sgibbsinb_first:
139323925Sgibbs	mov	DINDEX,SINDEX;
139423925Sgibbs	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
139513177Sgibbsinb_last:
139623925Sgibbs	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
139741646Sgibbs}
13984568Sgibbs
139939220Sgibbsif ((ahc->flags & AHC_TARGETMODE) != 0) {
140041646Sgibbs/*
140141646Sgibbs * Change to a new phase.  If we are changing the state of the I/O signal,
140241646Sgibbs * from out to in, wait an additional data release delay before continuing.
140341646Sgibbs */
140441646Sgibbschange_phase:
140543880Sgibbs	/* Wait for preceeding I/O session to complete. */
140643880Sgibbs	test	SCSISIGI, ACKI jnz .;
140743880Sgibbs
140843880Sgibbs	/* Change the phase */
140941646Sgibbs	and	DINDEX, IOI, SCSISIGI;
141041646Sgibbs	mov	SCSISIGO, SINDEX;
141141646Sgibbs	and	A, IOI, SINDEX;
141243880Sgibbs
141343880Sgibbs	/*
141443880Sgibbs	 * If the data direction has changed, from
141543880Sgibbs	 * out (initiator driving) to in (target driving),
141643880Sgibbs	 * we must waitat least a data release delay plus
141743880Sgibbs	 * the normal bus settle delay. [SCSI III SPI 10.11.0]
141843880Sgibbs	 */
141941646Sgibbs	cmp 	DINDEX, A je change_phase_wait;
142041646Sgibbs	test	SINDEX, IOI jz change_phase_wait;
142141646Sgibbs	call	change_phase_wait;
142241646Sgibbschange_phase_wait:
142341646Sgibbs	nop;
142441646Sgibbs	nop;
142541646Sgibbs	nop;
142641646Sgibbs	nop ret;
142741646Sgibbs
142841646Sgibbs/*
142941646Sgibbs * Send a byte to an initiator in Automatic PIO mode.
143041646Sgibbs */
143139220Sgibbstarget_outb:
143239220Sgibbs	or	SXFRCTL0, SPIOEN;
143339220Sgibbs	test	SSTAT0, SPIORDY	jz .;
143439220Sgibbs	mov	SCSIDATL, SINDEX;
143539220Sgibbs	test	SSTAT0, SPIORDY	jz .;
143641646Sgibbs	and	SXFRCTL0, ~SPIOEN ret;
143739220Sgibbs}
143839220Sgibbs	
143913177Sgibbsmesgin_phasemis:
144013177Sgibbs/*
144113177Sgibbs * We expected to receive another byte, but the target changed phase
144213177Sgibbs */
144323925Sgibbs	mvi	INTSTAT, MSGIN_PHASEMIS;
144423925Sgibbs	jmp	ITloop;
14454568Sgibbs
144613177Sgibbs/*
144713177Sgibbs * DMA data transfer.  HADDR and HCNT must be loaded first, and
144813177Sgibbs * SINDEX should contain the value to load DFCNTRL with - 0x3d for
144913177Sgibbs * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
145013177Sgibbs * during initialization.
145113177Sgibbs */
14524568Sgibbsdma:
145323925Sgibbs	mov	DFCNTRL,SINDEX;
145422568Sgibbsdma_loop:
145523925Sgibbs	test	SSTAT0,DMADONE	jnz dma_dmadone;
145623925Sgibbs	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
145722568Sgibbsdma_phasemis:
145823925Sgibbs	test	SSTAT0,SDONE	jnz dma_checkfifo;
145923925Sgibbs	mov	SINDEX,ALLZEROS;		/* Notify caller of phasemiss */
14604568Sgibbs
146113177Sgibbs/*
146213177Sgibbs * We will be "done" DMAing when the transfer count goes to zero, or
146313177Sgibbs * the target changes the phase (in light of this, it makes sense that
146413177Sgibbs * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
146513177Sgibbs * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
146613177Sgibbs * magically on STCNT=0 or a phase change, so just wait for FIFO empty
146713177Sgibbs * status.
146813177Sgibbs */
146922568Sgibbsdma_checkfifo:
147023925Sgibbs	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
147122568Sgibbsdma_fifoflush:
147223925Sgibbs	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
14734568Sgibbs
147422568Sgibbsdma_fifoempty:
147522568Sgibbs	/* Don't clobber an inprogress host data transfer */
147623925Sgibbs	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
147713177Sgibbs/*
147813177Sgibbs * Now shut the DMA enables off and make sure that the DMA enables are 
147913177Sgibbs * actually off first lest we get an ILLSADDR.
148013177Sgibbs */
148122568Sgibbsdma_dmadone:
148223925Sgibbs	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
148322568Sgibbsdma_halt:
148437223Sgibbs	/*
148537223Sgibbs	 * Some revisions of the aic7880 have a problem where, if the
148637223Sgibbs	 * data fifo is full, but the PCI input latch is not empty, 
148737223Sgibbs	 * HDMAEN cannot be cleared.  The fix used here is to attempt
148837223Sgibbs	 * to drain the data fifo until there is space for the input
148937223Sgibbs	 * latch to drain and HDMAEN de-asserts.
149037223Sgibbs	 */
149139220Sgibbs	if ((ahc->features & AHC_ULTRA2) == 0) {
149239220Sgibbs		mov	NONE, DFDAT;
149339220Sgibbs	}
149439220Sgibbs	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
149518762Sgibbsreturn:
149623925Sgibbs	ret;
14974568Sgibbs
149813177Sgibbs/*
149913177Sgibbs * Assert that if we've been reselected, then we've seen an IDENTIFY
150013177Sgibbs * message.
150113177Sgibbs */
15024568Sgibbsassert:
150323925Sgibbs	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
15044568Sgibbs
150523925Sgibbs	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
15064568Sgibbs
150713177Sgibbs/*
150819218Sgibbs * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
150939220Sgibbs * or by the SCBID ARG_1.  The search begins at the SCB index passed in
151039220Sgibbs * via SINDEX which is an SCB that must be on the disconnected list.  If
151139220Sgibbs * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
151239220Sgibbs * is set to the proper SCB.
151313177Sgibbs */
15144568SgibbsfindSCB:
151539220Sgibbs	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
151639220Sgibbs	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
151739220Sgibbs	mov	A, SAVED_TCL;
151839220Sgibbs	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
151939220SgibbsfindSCB_by_SCBID:
152023925Sgibbs	mov	A, ARG_1;			/* Tag passed in ARG_1 */
152139220Sgibbs	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
152239220SgibbsfindSCB_next:
152339220Sgibbs	mov	ARG_2, SCBPTR;
152439220Sgibbs	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
152539220Sgibbs	mov	SCBPTR,SCB_NEXT;
152639220Sgibbs	dec	SINDEX;		/* Last comparison moved us too far */
152723168SgibbsfindSCB_loop:
152839220Sgibbs	cmp	SINDIR, A	jne findSCB_next;
152939220Sgibbs	mov	SINDEX, SCBPTR 	ret;
153039220SgibbsnotFound:
153139220Sgibbs	mvi	SINDEX, SCB_LIST_NULL	ret;
153239220Sgibbs
153319164Sgibbs/*
153439220Sgibbs * Retrieve an SCB by SCBID first searching the disconnected list falling
153539220Sgibbs * back to DMA'ing the SCB down from the host.  This routine assumes that
153639220Sgibbs * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
153739220Sgibbs * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
153839220Sgibbs * we go directly to the host for the SCB.
153919164Sgibbs */
154039220SgibbsretrieveSCB:
154139220Sgibbs	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
154239220Sgibbs	mov	SCBPTR	call findSCB;	/* Continue the search */
154339220Sgibbs	cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;
154439220Sgibbs
154539220Sgibbs/*
154639220Sgibbs * This routine expects SINDEX to contain the index of the SCB to be
154739220Sgibbs * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
154839220Sgibbs * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
154939220Sgibbs * if it is at the head.
155039220Sgibbs */
155119164Sgibbsrem_scb_from_disc_list:
155215328Sgibbs/* Remove this SCB from the disconnection list */
155339220Sgibbs	cmp	ARG_2, SCB_LIST_NULL	je rHead;
155439220Sgibbs	mov	DINDEX, SCB_NEXT;
155539220Sgibbs	mov	SCBPTR, ARG_2;
155639220Sgibbs	mov	SCB_NEXT, DINDEX;
155723925Sgibbs	mov	SCBPTR, SINDEX ret;
155815328SgibbsrHead:
155923925Sgibbs	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
15604568Sgibbs
156139220Sgibbsretrieve_from_host:
156239220Sgibbs/*
156339220Sgibbs * We didn't find it.  Pull an SCB and DMA down the one we want.
156439220Sgibbs * We should never get here in the non-paging case.
156539220Sgibbs */
156639220Sgibbs	mov	ALLZEROS	call	get_free_or_disc_scb;
156739220Sgibbs	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
156839220Sgibbs	/* Jump instead of call as we want to return anyway */
156939220Sgibbs	mov	ARG_1	jmp dma_scb;
157039220Sgibbs
157139220Sgibbs/*
157239220Sgibbs * Determine whether a target is using tagged or non-tagged transactions
157339220Sgibbs * by first looking for a matching transaction based on the TCL and if
157439220Sgibbs * that fails, looking up this device in the host's untagged SCB array.
157539220Sgibbs * The TCL to search for is assumed to be in SAVED_TCL.  The value is
157639220Sgibbs * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
157739220Sgibbs * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
157839220Sgibbs * in an SCB instead of having to go to the host.
157939220Sgibbs */
158039220Sgibbsget_untagged_SCBID:
158139220Sgibbs	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
158239220Sgibbs	mvi	ARG_1, SCB_LIST_NULL;
158339220Sgibbs	mov	DISCONNECTED_SCBH call findSCB;
158439220Sgibbs	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
158539220Sgibbs	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
158639220Sgibbs	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
158739220Sgibbs	mov	ARG_1, SCB_TAG	ret;
158839220Sgibbs	mvi	ARG_1, SCB_LIST_NULL ret;
158939220Sgibbs
159039220Sgibbs/*
159139220Sgibbs * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
159239220Sgibbs * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
159339220Sgibbs */
159439220Sgibbsfetch_byte:
159539220Sgibbs	mov	ARG_2, SINDEX;
159639220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
159739220Sgibbs		mvi	DINDEX, CCHADDR;
159839220Sgibbs		mvi	SCBID_ADDR call set_1byte_addr;
159939220Sgibbs		mvi	CCHCNT, 1;
160039220Sgibbs		mvi	CCSGCTL, CCSGEN|CCSGRESET;
160139220Sgibbs		test	CCSGCTL, CCSGDONE jz .;
160239220Sgibbs		mvi	CCSGCTL, CCSGRESET;
160339220Sgibbs		bmov	RETURN_2, CCSGRAM, 1 ret;
160439220Sgibbs	} else {
160539220Sgibbs		mvi	DINDEX, HADDR;
160639220Sgibbs		mvi	SCBID_ADDR call set_1byte_addr;
160739220Sgibbs		mvi	HCNT[0], 1;
160839220Sgibbs		clr	HCNT[1];
160939220Sgibbs		clr	HCNT[2];
161039220Sgibbs		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
161139220Sgibbs		call	dma_finish;
161239220Sgibbs		mov	RETURN_2, DFDAT ret;
161339220Sgibbs	}
161439220Sgibbs
161539220Sgibbs/*
161639220Sgibbs * Prepare the hardware to post a byte to host memory given an
161739220Sgibbs * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
161839220Sgibbs */
161939220Sgibbspost_byte_setup:
162039220Sgibbs	mov	ARG_2, SINDEX;
162139220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
162239220Sgibbs		mvi	DINDEX, CCHADDR;
162339220Sgibbs		mvi	SCBID_ADDR call	set_1byte_addr;
162439220Sgibbs		mvi	CCHCNT, 1;
162539220Sgibbs		mvi	CCSCBCTL, CCSCBRESET ret;
162639220Sgibbs	} else {
162739220Sgibbs		mvi	DINDEX, HADDR;
162839220Sgibbs		mvi	SCBID_ADDR call	set_1byte_addr;
162939220Sgibbs		mvi	HCNT[0], 1;
163039220Sgibbs		clr	HCNT[1];
163139220Sgibbs		clr	HCNT[2];
163239220Sgibbs		mvi	DFCNTRL, FIFORESET ret;
163339220Sgibbs	}
163439220Sgibbs
163539220Sgibbspost_byte:
163639220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
163739220Sgibbs		bmov	CCSCBRAM, SINDEX, 1;
163839220Sgibbs		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
163939220Sgibbs		test	CCSCBCTL, CCSCBDONE jz .;
164039220Sgibbs		clr	CCSCBCTL ret;
164139220Sgibbs	} else {
164239220Sgibbs		mov	DFDAT, SINDEX;
164339220Sgibbs		or	DFCNTRL, HDMAEN|FIFOFLUSH;
164439220Sgibbs		jmp	dma_finish;
164539220Sgibbs	}
164639220Sgibbs
164739220Sgibbsget_SCBID_from_host:
164839220Sgibbs	mov	A, SAVED_TCL;
164939220Sgibbs	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
165039220Sgibbs	mov	RETURN_1,  RETURN_2 ret;
165139220Sgibbs
165239220Sgibbsphase_lock:     
165339220Sgibbs	test	SSTAT1, REQINIT jz phase_lock;
165439220Sgibbs	test	SSTAT1, SCSIPERR jnz phase_lock;
165541646Sgibbs	and	SCSISIGO, PHASE_MASK, SCSISIGI;
165641646Sgibbs	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
165739220Sgibbs
165839220Sgibbsif ((ahc->features & AHC_CMD_CHAN) == 0) {
165919164Sgibbsset_stcnt_from_hcnt:
166023925Sgibbs	mov	STCNT[0], HCNT[0];
166123925Sgibbs	mov	STCNT[1], HCNT[1];
166223925Sgibbs	mov	STCNT[2], HCNT[2] ret;
16634568Sgibbs
166419164Sgibbsbcopy_7:
166523925Sgibbs	mov	DINDIR, SINDIR;
166623925Sgibbs	mov	DINDIR, SINDIR;
166719164Sgibbsbcopy_5:
166823925Sgibbs	mov	DINDIR, SINDIR;
166919164Sgibbsbcopy_4:
167023925Sgibbs	mov	DINDIR, SINDIR;
167119164Sgibbsbcopy_3:
167223925Sgibbs	mov	DINDIR, SINDIR;
167323925Sgibbs	mov	DINDIR, SINDIR;
167423925Sgibbs	mov	DINDIR, SINDIR ret;
167539220Sgibbs}
16764568Sgibbs
167739220Sgibbsif ((ahc->flags & AHC_TARGETMODE) != 0) {
167839220Sgibbs/*
167939220Sgibbs * Setup addr assuming that A is an index into
168039220Sgibbs * an array of 32byte objects, SINDEX contains
168139220Sgibbs * the base address of that array, and DINDEX
168239220Sgibbs * contains the base address of the location
168339220Sgibbs * to store the indexed address.
168439220Sgibbs */
168539220Sgibbsset_32byte_addr:
168639220Sgibbs	shr	ARG_2, 3, A;
168739220Sgibbs	shl	A, 5;
168839220Sgibbs	jmp	set_1byte_addr;
168939220Sgibbs}
169039220Sgibbs
169139220Sgibbs/*
169239220Sgibbs * Setup addr assuming that A is an index into
169339220Sgibbs * an array of 64byte objects, SINDEX contains
169439220Sgibbs * the base address of that array, and DINDEX
169539220Sgibbs * contains the base address of the location
169639220Sgibbs * to store the indexed address.
169739220Sgibbs */
169839220Sgibbsset_64byte_addr:
169939220Sgibbs	shr	ARG_2, 2, A;
170039220Sgibbs	shl	A, 6;
170139220Sgibbs
170239220Sgibbs/*
170339220Sgibbs * Setup addr assuming that A + (ARG_1 * 256) is an
170439220Sgibbs * index into an array of 1byte objects, SINDEX contains
170539220Sgibbs * the base address of that array, and DINDEX contains
170639220Sgibbs * the base address of the location to store the computed
170739220Sgibbs * address.
170839220Sgibbs */
170939220Sgibbsset_1byte_addr:
171039220Sgibbs	add     DINDIR, A, SINDIR;
171139220Sgibbs	mov     A, ARG_2;
171239220Sgibbs	adc	DINDIR, A, SINDIR;
171339220Sgibbs	clr	A;
171439220Sgibbs	adc	DINDIR, A, SINDIR;
171539220Sgibbs	adc	DINDIR, A, SINDIR ret;
171639220Sgibbs
171739220Sgibbs/*
171839220Sgibbs * Either post or fetch and SCB from host memory based on the
171939220Sgibbs * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
172039220Sgibbs */
172119164Sgibbsdma_scb:
172239220Sgibbs	mov	A, SINDEX;
172339220Sgibbs	if ((ahc->features & AHC_CMD_CHAN) != 0) {
172439220Sgibbs		mvi	DINDEX, CCHADDR;
172539220Sgibbs		mvi	HSCB_ADDR call set_64byte_addr;
172639220Sgibbs		mov	CCSCBPTR, SCBPTR;
172739220Sgibbs		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
172839220Sgibbs		mvi	CCHCNT, SCB_64BYTE_SIZE;
172939220Sgibbs		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
173039220Sgibbs		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
173139220Sgibbs		jmp	dma_scb_finish;
173239220Sgibbsdma_scb_tohost:
173339220Sgibbs		mvi	CCHCNT, SCB_32BYTE_SIZE;
173439220Sgibbs		if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
173539220Sgibbs			mvi	CCSCBCTL, CCSCBRESET;
173639220Sgibbs			bmov	CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
173739220Sgibbs			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
173839220Sgibbs			test	CCSCBCTL, CCSCBDONE jz .;
173939220Sgibbs		} else {
174039220Sgibbs			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
174139220Sgibbs			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
174239220Sgibbs		}
174339220Sgibbsdma_scb_finish:
174439220Sgibbs		clr	CCSCBCTL;
174539220Sgibbs		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
174639220Sgibbs		ret;
174739220Sgibbs	} else {
174839220Sgibbs		mvi	DINDEX, HADDR;
174939220Sgibbs		mvi	HSCB_ADDR call set_64byte_addr;
175039220Sgibbs		mvi	HCNT[0], SCB_32BYTE_SIZE;
175139220Sgibbs		clr	HCNT[1];
175239220Sgibbs		clr	HCNT[2];
175339220Sgibbs		mov	DFCNTRL, DMAPARAMS;
175439220Sgibbs		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
175539220Sgibbs		/* Fill it with the SCB data */
175624175Sgibbscopy_scb_tofifo:
175739220Sgibbs		mvi	SINDEX, SCB_CONTROL;
175839220Sgibbs		add	A, SCB_32BYTE_SIZE, SINDEX;
175924175Sgibbscopy_scb_tofifo_loop:
176039220Sgibbs		mov	DFDAT,SINDIR;
176139220Sgibbs		mov	DFDAT,SINDIR;
176239220Sgibbs		mov	DFDAT,SINDIR;
176339220Sgibbs		mov	DFDAT,SINDIR;
176439220Sgibbs		mov	DFDAT,SINDIR;
176539220Sgibbs		mov	DFDAT,SINDIR;
176639220Sgibbs		mov	DFDAT,SINDIR;
176739220Sgibbs		cmp	SINDEX, A jne copy_scb_tofifo_loop;
176839220Sgibbs		or	DFCNTRL, HDMAEN|FIFOFLUSH;
176919164Sgibbsdma_scb_fromhost:
177039220Sgibbs		call	dma_finish;
177139220Sgibbs		/* If we were putting the SCB, we are done */
177239220Sgibbs		test	DMAPARAMS, DIRECTION	jz	return;
177339220Sgibbs		mvi	SCB_CONTROL  call dfdat_in_7;
177439220Sgibbs		call	dfdat_in_7_continued;
177539220Sgibbs		call	dfdat_in_7_continued;
177639220Sgibbs		jmp	dfdat_in_7_continued;
177719164Sgibbsdfdat_in_7:
177839220Sgibbs		mov     DINDEX,SINDEX;
177919164Sgibbsdfdat_in_7_continued:
178039220Sgibbs		mov	DINDIR,DFDAT;
178139220Sgibbs		mov	DINDIR,DFDAT;
178239220Sgibbs		mov	DINDIR,DFDAT;
178339220Sgibbs		mov	DINDIR,DFDAT;
178439220Sgibbs		mov	DINDIR,DFDAT;
178539220Sgibbs		mov	DINDIR,DFDAT;
178639220Sgibbs		mov	DINDIR,DFDAT ret;
178739220Sgibbs	}
178819164Sgibbs
178939220Sgibbs
179013177Sgibbs/*
179119164Sgibbs * Wait for DMA from host memory to data FIFO to complete, then disable
179219164Sgibbs * DMA and wait for it to acknowledge that it's off.
179313177Sgibbs */
179419164Sgibbsdma_finish:
179523925Sgibbs	test	DFSTATUS,HDONE	jz dma_finish;
179622234Sgibbs	/* Turn off DMA */
179723925Sgibbs	and	DFCNTRL, ~HDMAEN;
179823925Sgibbs	test	DFCNTRL, HDMAEN jnz .;
179923925Sgibbs	ret;
18009928Sgibbs
180123925Sgibbsadd_scb_to_free_list:
180239220Sgibbs	if ((ahc->flags & AHC_PAGESCBS) != 0) {
180339220Sgibbs		mov	SCB_NEXT, FREE_SCBH;
180439220Sgibbs		mov	FREE_SCBH, SCBPTR;
180539220Sgibbs	}
180639220Sgibbs	mvi	SCB_TAG, SCB_LIST_NULL ret;
18074568Sgibbs
180839220Sgibbsif ((ahc->flags & AHC_PAGESCBS) != 0) {
180919164Sgibbsget_free_or_disc_scb:
181023925Sgibbs	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
181123925Sgibbs	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
181219623Sgibbsreturn_error:
181323925Sgibbs	mvi	SINDEX, SCB_LIST_NULL	ret;
181419623Sgibbsdequeue_disc_scb:
181523925Sgibbs	mov	SCBPTR, DISCONNECTED_SCBH;
181623925Sgibbsdma_up_scb:
181723925Sgibbs	mvi	DMAPARAMS, FIFORESET;
181823925Sgibbs	mov	SCB_TAG		call dma_scb;
181919164Sgibbsunlink_disc_scb:
182039220Sgibbs	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
182119164Sgibbsdequeue_free_scb:
182223925Sgibbs	mov	SCBPTR, FREE_SCBH;
182323925Sgibbs	mov	FREE_SCBH, SCB_NEXT ret;
182439220Sgibbs}
18254568Sgibbs
182619164Sgibbsadd_scb_to_disc_list:
182713177Sgibbs/*
182819164Sgibbs * Link this SCB into the DISCONNECTED list.  This list holds the
182919164Sgibbs * candidates for paging out an SCB if one is needed for a new command.
183019164Sgibbs * Modifying the disconnected list is a critical(pause dissabled) section.
183113177Sgibbs */
183223925Sgibbs	mov	SCB_NEXT, DISCONNECTED_SCBH;
183339220Sgibbs	mov	DISCONNECTED_SCBH, SCBPTR ret;
1834