patch-2.0.36 linux/drivers/scsi/aic7xxx/aic7xxx.seq

Next file: linux/drivers/scsi/aic7xxx/sequencer.h
Previous file: linux/drivers/scsi/aic7xxx/aic7xxx.reg
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -14,7 +14,7 @@
  *    derived from this software without specific prior written permission.
  *
  * Where this Software is combined with software released under the terms of 
- * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * the GNU Public License (GPL) and the terms of the GPL would require the 
  * combined work to also be released under the terms of the GPL, the terms
  * and conditions of this License will apply in addition to those of the
  * GPL with the exception of any terms or conditions of this License that
@@ -32,7 +32,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	$Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
+ *	$Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
  */
 
 #include "aic7xxx.reg"
@@ -59,38 +59,55 @@
 reset:
 	clr	SCSISIGO;		/* De-assert BSY */
 	/* Always allow reselection */
-.if ( TARGET_MODE )
-	mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
-.else
-	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
-.endif
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+	} else {
+		mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		clr	CCSGCTL;
+		clr	CCSCBCTL;
+	}
+
 	call	clear_target_state;
 	and	SXFRCTL0, ~SPIOEN;
 poll_for_work:
-	mov	A, QINPOS;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		mov	A, QINPOS;
+	}
 poll_for_work_loop:
-	and	SEQCTL, ~PAUSEDIS;
-	test	SSTAT0, SELDO|SELDI	jnz selection;
-	test	SCSISEQ, ENSELO	jnz poll_for_work;
-.if ( TWIN_CHANNEL )
-	/*
-	 * Twin channel devices cannot handle things like SELTO
-	 * interrupts on the "background" channel.  So, if we
-	 * are selecting, keep polling the current channel util
-	 * either a selection or reselection occurs.
-	 */
-	xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		and	SEQCTL, ~PAUSEDIS;
+	}
 	test	SSTAT0, SELDO|SELDI	jnz selection;
 	test	SCSISEQ, ENSELO	jnz poll_for_work;
-	xor	SBLKCTL,SELBUSB;	/* Toggle back */
-.endif
+	if ((p->features & AHC_TWIN) != 0) {
+		/*
+		 * Twin channel devices cannot handle things like SELTO
+		 * interrupts on the "background" channel.  So, if we
+		 * are selecting, keep polling the current channel util
+		 * either a selection or reselection occurs.
+		 */
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+		test	SSTAT0, SELDO|SELDI	jnz selection;
+		test	SCSISEQ, ENSELO	jnz poll_for_work;
+		xor	SBLKCTL,SELBUSB;	/* Toggle back */
+	}
 	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
 test_queue:
 	/* Has the driver posted any work for us? */
-	or	SEQCTL, PAUSEDIS;
-	cmp	KERNEL_QINPOS, A je poll_for_work_loop;
-	inc	QINPOS;
-	and	SEQCTL, ~PAUSEDIS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+		mov	NONE, SNSCB_QOFF;
+		inc	QINPOS;
+	} else {
+		or	SEQCTL, PAUSEDIS;
+		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
+		inc	QINPOS;
+		and	SEQCTL, ~PAUSEDIS;
+	}
 
 /*
  * We have at least one queued SCB now and we don't have any 
@@ -98,26 +115,24 @@
  * any SCBs available for use, pull the tag from the QINFIFO
  * and get to work on it.
  */
-.if ( SCB_PAGING )
-	mov	ALLZEROS	call	get_free_or_disc_scb;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	ALLZEROS	call	get_free_or_disc_scb;
+	}
+
 dequeue_scb:
 	add	A, -1, QINPOS;
-	mvi	QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+	mvi	QINFIFO_OFFSET call fetch_byte;
 
-	call	dma_finish;
-	mov	SINDEX, DFDAT;
-.if !( SCB_PAGING )
-	/* In the non-paging case, the SCBID == hardware SCB index */
-	mov	SCBPTR, SINDEX;
-.endif
+	if ((p->flags & AHC_PAGESCBS) == 0) {
+		/* In the non-paging case, the SCBID == hardware SCB index */
+		mov	SCBPTR, RETURN_2;
+	}
 dma_queued_scb:
 /*
  * DMA the SCB from host ram into the current SCB location.
  */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	call	dma_scb;
+	mov	RETURN_2	 call dma_scb;
 
 start_scb:
 	/*
@@ -135,16 +150,22 @@
 	jmp	poll_for_work;
 
 start_selection:
-.if ( TWIN_CHANNEL )
-	and	SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
-	and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
-	or	SINDEX,A;
-	mov	SBLKCTL,SINDEX;		/* select channel */
-.endif
+	if ((p->features & AHC_TWIN) != 0) {
+		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+		or	SINDEX,A;
+		mov	SBLKCTL,SINDEX;		/* select channel */
+	}
 initialize_scsiid:
-	and	A, TID, SCB_TCL;	/* Get target ID */
-	and	SCSIID, OID;		/* Clear old target */
-	or	SCSIID, A;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID_ULTRA2, OID;	/* Clear old target */
+		or	SCSIID_ULTRA2, A;
+	} else {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID, OID;		/* Clear old target */
+		or	SCSIID, A;
+	}
 	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
 
 /*
@@ -155,143 +176,154 @@
 initialize_channel:
 	or	A, CLRSTCNT|CLRCHN, SINDEX;
 	or	SXFRCTL0, A;
-.if ( ULTRA )
+	if ((p->features & AHC_ULTRA) != 0) {
 ultra:
-	mvi	SINDEX, ULTRA_ENB+1;
-	test	SAVED_TCL, 0x80		jnz ultra_2;	/* Target ID > 7 */
-	dec	SINDEX;
+		mvi	SINDEX, ULTRA_ENB+1;
+		test	SAVED_TCL, 0x80		jnz ultra_2; /* Target ID > 7 */
+		dec	SINDEX;
 ultra_2:
-	mov     FUNCTION1,SAVED_TCL;
-	mov     A,FUNCTION1;
-	test	SINDIR, A	jz ndx_dtr;
-	or	SXFRCTL0, FAST20;
-.endif
- 
+		mov     FUNCTION1,SAVED_TCL;
+		mov     A,FUNCTION1;
+		test	SINDIR, A	jz ndx_dtr;
+		or	SXFRCTL0, FAST20;
+	} 
 /*
  * Initialize SCSIRATE with the appropriate value for this target.
  * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCRATCH.
+ * based at TARG_SCSIRATE.
  */
 ndx_dtr:
 	shr	A,4,SAVED_TCL;
-	test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
-	or	SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
-	or	A,0x08;			/* Channel B entries add 8 */
+	if ((p->features & AHC_TWIN) != 0) {
+		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+		or	SAVED_TCL, SELBUSB; 
+		or	A,0x08;			/* Channel B entries add 8 */
 ndx_dtr_2:
-	add	SINDEX,TARG_SCRATCH,A;
+	}
+
+	if ((p->features & AHC_ULTRA2) != 0) {
+		add	SINDEX, TARG_OFFSET, A;
+		mov	SCSIOFFSET, SINDIR;
+	}
+
+	add	SINDEX,TARG_SCSIRATE,A;
 	mov	SCSIRATE,SINDIR ret;
 
 
 selection:
 	test	SSTAT0,SELDO	jnz select_out;
 select_in:
-.if ( TARGET_MODE )
-	test	SSTAT0, TARGET	jz initiator_reselect;
-	/*
-	 * We've just been selected.  Assert BSY and
-	 * setup the phase for receiving the messages
-	 * from the target.
-	 */
-	mvi	SCSISIGO, P_MESGOUT|BSYO;
-	mvi	CLRSINT0, CLRSELDO;
-
-	/*
-	 * If ATN isn't asserted, go directly to bus free.
-	 */
-	test	SCSISIGI, ATNI	jz	target_busfree;
-
-	/*
-	 * Setup the DMA for sending the identify and
-	 * command information.
-	 */
-	mov	A, TMODE_CMDADDR_NEXT;
-	mvi	TMODE_CMDADDR call set_32byte_haddr_and_clrcnt;
-	mvi	DFCNTRL, FIFORESET;
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		test	SSTAT0, TARGET	jz initiator_reselect;
+		/*
+		 * We've just been selected.  Assert BSY and
+		 * setup the phase for receiving the messages
+		 * from the target.
+		 */
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+		mvi	CLRSINT0, CLRSELDO;
+
+		/*
+		 * If ATN isn't asserted, go directly to bus free.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Setup the DMA for sending the identify and
+		 * command information.
+		 */
+		mov	A, TMODE_CMDADDR_NEXT;
+		mvi	DINDEX, HADDR;
+		mvi	TMODE_CMDADDR call set_32byte_addr;
+		mvi	DFCNTRL, FIFORESET;
 
-	clr	SINDEX;
-	/* Watch ATN closely now */
+		clr	SINDEX;
+		/* Watch ATN closely now */
 message_loop:
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	and	SXFRCTL0, ~SPIOEN;
-	mov	DINDEX, SCSIDATL;
-	mov	DFDAT, DINDEX;
-	inc	SINDEX;
-
-	/* Message Testing... */
-	test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
-	mov	ARG_1, DINDEX;
-
-	test	SCSISIGI, ATNI	jnz	message_loop;
-	add	A, -4, SINDEX;
-	jc	target_cmdphase;
-	mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		and	SXFRCTL0, ~SPIOEN;
+		mov	DINDEX, SCSIDATL;
+		mov	DFDAT, DINDEX;
+		inc	SINDEX;
+
+		/* Message Testing... */
+		test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
+		mov	ARG_1, DINDEX;
+
+		test	SCSISIGI, ATNI	jnz	message_loop;
+		add	A, -4, SINDEX;
+		jc	target_cmdphase;
+		mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
 
 target_cmdphase:
-	add	HCNT[0], 1, A;
-	mvi	SCSISIGO, P_COMMAND|BSYO;
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	mov	A, SCSIDATL;
-	mov	DFDAT, A;	/* Store for host */
-
-	/*
-	 * Determine the number of bytes to read
-	 * based on the command group code.  Count is
-	 * one less than the total since we've already
-	 * fetched the first byte.
-	 */
-	clr	SINDEX;
-	shr	A, CMD_GROUP_CODE_SHIFT;
-	add	SEQADDR0, A;
-
-	add	SINDEX, CMD_GROUP0_BYTE_DELTA;
-	nop;	/* Group 1 and 2 are the same */
-	add	SINDEX, CMD_GROUP2_BYTE_DELTA;
-	nop;	/* Group 3 is reserved */
-	add	SINDEX, CMD_GROUP4_BYTE_DELTA;
-	add	SINDEX, CMD_GROUP5_BYTE_DELTA;
+		add	HCNT[0], 1, A;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	SCSISIGO, P_COMMAND|BSYO;
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		mov	A, SCSIDATL;
+		mov	DFDAT, A;	/* Store for host */
+
+		/*
+		 * Determine the number of bytes to read
+		 * based on the command group code.  Count is
+		 * one less than the total since we've already
+		 * fetched the first byte.
+		 */
+		clr	SINDEX;
+		shr	A, CMD_GROUP_CODE_SHIFT;
+		add	SEQADDR0, A;
+
+		add	SINDEX, CMD_GROUP0_BYTE_DELTA;
+		nop;	/* Group 1 and 2 are the same */
+		add	SINDEX, CMD_GROUP2_BYTE_DELTA;
+		nop;	/* Group 3 is reserved */
+		add	SINDEX, CMD_GROUP4_BYTE_DELTA;
+		add	SINDEX, CMD_GROUP5_BYTE_DELTA;
 		/* Group 6 and 7 are not handled yet */
 
-	mov	A, SINDEX;
-	add	HCNT[0], A;
+		mov	A, SINDEX;
+		add	HCNT[0], A;
 
 command_loop:
-	test	SSTAT0, SPIORDY jz .;
-	cmp	SINDEX, 1 jne . + 2;
-	and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
-	mov	DFDAT, SCSIDATL;
-	dec	SINDEX;
-	test	SINDEX, 0xFF jnz command_loop;
+		test	SSTAT0, SPIORDY jz .;
+		cmp	SINDEX, 1 jne . + 2;
+		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
+		mov	DFDAT, SCSIDATL;
+		dec	SINDEX;
+		test	SINDEX, 0xFF jnz command_loop;
 
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 	
-	call	dma_finish;
+		call	dma_finish;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
 
-	mvi	SCSISIGO, P_MESGIN|BSYO;
+		mvi	SCSISIGO, P_MESGIN|BSYO;
 
-	or	SXFRCTL0, SPIOEN;
+		or	SXFRCTL0, SPIOEN;
 
-	mvi	MSG_DISCONNECT call target_outb;
+		mvi	MSG_DISCONNECT call target_outb;
 	
 selectin_post:
-	inc	TMODE_CMDADDR_NEXT;
-	cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
-	clr	TMODE_CMDADDR_NEXT;
-	mvi	QOUTFIFO, SCB_LIST_NULL;
-	mvi	INTSTAT,CMDCMPLT;
+		inc	TMODE_CMDADDR_NEXT;
+		cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
+		clr	TMODE_CMDADDR_NEXT;
+		mvi	QOUTFIFO, SCB_LIST_NULL;
+		mvi	INTSTAT,CMDCMPLT;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
 
-	/* Busy loop on something then go to data or status phase */
+		/* Busy loop on something then go to data or status phase */
 
 target_busfree:
-	clr	SCSISIGO;
-	jmp	poll_for_work;
+		clr	SCSISIGO;
+		jmp	poll_for_work;
+
+	}
 
-.endif /* TARGET_MODE */
 /*
  * Reselection has been initiated by a target. Make a note that we've been
  * reselected, but haven't seen an IDENTIFY message from the target yet.
@@ -396,26 +428,41 @@
 	jmp	data_phase_loop;
 
 p_data:
-	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+	} else {
+		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	}
 	test	LASTPHASE, IOI jnz . + 2;
 	or	DMAPARAMS, DIRECTION;
 	call	assert;			/*
 					 * Ensure entering a data
 					 * phase is okay - seen identify, etc.
 					 */
-
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	CCSGADDR, CCSGADDR_MAX;
+	}
 	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
 
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
 	/*
 	 * Initialize the DMA address and counter from the SCB.
 	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
 	 * modify the values in the SCB itself until we see a
 	 * save data pointers message.
 	 */
-	mvi	DINDEX, HADDR;
-	mvi	SCB_DATAPTR	call bcopy_7;
-
-	call	set_stcnt_from_hcnt;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_DATAPTR, 7;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_DATAPTR	call bcopy_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+	}
 
 	mov	SG_COUNT,SCB_SGCOUNT;
 
@@ -432,19 +479,38 @@
  * had an overrun.
  */
 	or	SXFRCTL1,BITBUCKET;
-	mvi	HCNT[0], 0xff;
-	mvi	HCNT[1], 0xff;
-	mvi	HCNT[2], 0xff;
-	call	set_stcnt_from_hcnt;
 	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
-
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov	HCNT, ALLONES, 3;
+	} else {
+		mvi	STCNT[0], 0xFF;
+		mvi	STCNT[1], 0xFF;
+		mvi	STCNT[2], 0xFF;
+	}
 data_phase_inbounds:
-/* If we are the last SG block, ensure wideodd is off. */
+/* If we are the last SG block, tell the hardware. */
 	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
-	and	DMAPARAMS, ~WIDEODD;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	} else {
+		and	DMAPARAMS, ~WIDEODD;
+	}
 data_phase_wideodd:
-	mov	DMAPARAMS  call dma;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, ALLONES;
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+data_phase_dma_loop:
+		test	SSTAT0,	SDONE jnz data_phase_dma_done;
+		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
+data_phase_dma_phasemis:
+		test	SSTAT0,SDONE	jnz . + 2;
+		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
+	} else {
+		mov	DMAPARAMS  call dma;
+	}
 
+data_phase_dma_done:
 /* Go tell the host about any overruns */
 	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
 
@@ -458,11 +524,6 @@
 	dec	SG_COUNT;	/* one less segment to go */
 
 	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
-
-	clr	A;			/* add sizeof(struct scatter) */
-	add	SG_NEXT[0],SG_SIZEOF;
-	adc	SG_NEXT[1],A;
-
 /*
  * Load a struct scatter and set up the data address and length.
  * If the working value of the SG count is nonzero, then
@@ -471,33 +532,71 @@
  * This, like all DMA's, assumes little-endian host data storage.
  */
 sg_load:
-	mvi	DINDEX, HADDR;
-	mvi	SG_NEXT	call bcopy_4;
-
-	mvi	HCNT[0],SG_SIZEOF;
-	clr	HCNT[1];
-	clr	HCNT[2];
-
-	or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
-	call	dma_finish;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/*
+		 * Do we have any prefetch left???
+		 */
+		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+
+		/*
+		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+		 */
+		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+		mvi	A, CCSGADDR_MAX;
+		jc	. + 2;
+		shl	A, 3, SG_COUNT;
+		mov	CCHCNT, A;
+		bmov	CCHADDR, SG_NEXT, 4;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		and	CCSGCTL, ~CCSGEN;
+		test	CCSGCTL, CCSGEN jnz .;
+		mvi	CCSGCTL, CCSGRESET;
+prefetched_segs_avail:
+		bmov 	HADDR, CCSGRAM, 8;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SG_NEXT	call bcopy_4;
+
+		mvi	HCNT[0],SG_SIZEOF;
+		clr	HCNT[1];
+		clr	HCNT[2];
+
+		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+		call	dma_finish;
+
+		/*
+		 * Copy data from FIFO into SCB data pointer and data count.
+		 * This assumes that the SG segments are of the form:
+		 * struct ahc_dma_seg {
+		 *	u_int32_t	addr;	four bytes, little-endian order
+		 *	u_int32_t	len;	four bytes, little endian order
+		 * };
+		 */
+		mvi	HADDR	call dfdat_in_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		/* Load STCNT as well.  It is a mirror of HCNT */
+		call	set_stcnt_from_hcnt;
+	}
 
-/*
- * Copy data from FIFO into SCB data pointer and data count.  This assumes
- * that the SG segments are of the form:
- *
- * struct ahc_dma_seg {
- *	u_int32_t	addr;		four bytes, little-endian order
- *	u_int32_t	len;		four bytes, little endian order
- * };
- */
-	mvi	HADDR	call dfdat_in_7;
+/* Advance the SG pointer */
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
 
-/* Load STCNT as well.  It is a mirror of HCNT */
-	call	set_stcnt_from_hcnt;
 	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+	/* Ensure the last seg is visable at the shaddow layer */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	DFCNTRL, PRELOADEN;
+	}
 
 data_phase_finish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+	}
 /*
  * After a DMA finishes, save the SG and STCNT residuals back into the SCB
  * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
@@ -508,12 +607,17 @@
 	mov	SCB_RESID_DCNT[2],STCNT[2];
 	mov	SCB_RESID_SGCNT, SG_COUNT;
 
-	/* We have seen a data phase */
-	or	SEQ_FLAGS, DPHASE;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 
 	jmp	ITloop;
 
 data_phase_overrun:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 /*
  * Turn off BITBUCKET mode and notify the host
  */
@@ -521,6 +625,19 @@
 	mvi	INTSTAT,DATA_OVERRUN;
 	jmp	ITloop;
 
+ultra2_dmafinish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+		and	DFCNTRL, ~SCSIEN;
+		test	DFCNTRL, SCSIEN jnz .;
+		or	DFCNTRL, FIFOFLUSH;
+		test	DFSTATUS, FIFOEMP jz . - 1;
+ultra2_dmahalt:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, HDMAEN jnz .;
+		ret;
+	}
+
 /*
  * Command phase.  Set up the DMA registers and let 'er rip.
  */
@@ -530,14 +647,26 @@
 /*
  * Load HADDR and HCNT.
  */
-	mvi	DINDEX, HADDR;
-	mvi	SCB_CMDPTR	call bcopy_5;
-	clr	HCNT[1];
-	clr	HCNT[2];
-
-	call	set_stcnt_from_hcnt;
-
-	mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_CMDPTR, 5;
+		bmov	HCNT[1], ALLZEROS, 2;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_CMDPTR	call bcopy_5;
+		clr	HCNT[1];
+		clr	HCNT[2];
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	} else {
+		mvi	(PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma;
+	}
 	jmp	ITloop;
 
 /*
@@ -562,7 +691,6 @@
  * on an SCB that might not be for the current nexus. (For example, a
  * BDR message in responce to a bad reselection would leave us pointed to
  * an SCB that doesn't have anything to do with the current target).
-
  * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
  * bus device reset).
  *
@@ -574,11 +702,11 @@
 	mov	SINDEX, MSG_OUT;
 	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
 p_mesgout_identify:
-.if ( WIDE )
-	and	SINDEX,0xf,SCB_TCL;	/* lun */
-.else
-	and	SINDEX,0x7,SCB_TCL;	/* lun */
-.endif
+	if ((p->features & AHC_WIDE) != 0) {
+		and	SINDEX,0xf,SCB_TCL;	/* lun */
+	} else {
+		and	SINDEX,0x7,SCB_TCL;	/* lun */
+	}
 	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
 	or	SINDEX,A;		/* or in disconnect privledge */
 	or	SINDEX,MSG_IDENTIFYFLAG;
@@ -606,6 +734,7 @@
 p_mesgout_from_host:
 	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
 	mvi     INTSTAT,AWAITING_MSG;
+	nop;
 	/*
 	 * Did the host detect a phase change?
 	 */
@@ -692,6 +821,7 @@
 check_status:
 	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
 	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	nop;
 	cmp	RETURN_1, SEND_SENSE	jne complete;
 	/* This SCB becomes the next to execute as it will retrieve sense */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
@@ -710,21 +840,21 @@
 	/* If we are untagged, clear our address up in host ram */
 	test	SCB_CONTROL, TAG_ENB jnz complete_post;
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mvi	DFDAT, SCB_LIST_NULL;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
+	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
+	mvi	SCB_LIST_NULL call post_byte;
 
 complete_post:
 	/* Post the SCB and issue an interrupt */
-	mov	A, QOUTPOS;
-	mvi	QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mov	DFDAT, SCB_TAG;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
-	inc 	QOUTPOS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		mov	A, SDSCB_QOFF;
+	} else {
+		mov	A, QOUTPOS;
+	}
+	mvi	QOUTFIFO_OFFSET call post_byte_setup;
+	mov	SCB_TAG call post_byte;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		inc 	QOUTPOS;
+	}
 	mvi	INTSTAT,CMDCMPLT;
 
 add_to_free_list:
@@ -796,18 +926,19 @@
  * clearing the "disconnected" bit so we don't "find" it by accident later.
  */
 mesgin_identify:
-.if ( WIDE )
-	and	A,0x0f;			/* lun in lower four bits */
-.else
-	and	A,0x07;			/* lun in lower three bits */
-.endif
+	
+	if ((p->features & AHC_WIDE) != 0) {
+		and	A,0x0f;		/* lun in lower four bits */
+	} else {
+		and	A,0x07;		/* lun in lower three bits */
+	}
 	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
 
 	call	get_untagged_SCBID;
 	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
-.if ( SCB_PAGING )
-	test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
+	}
 	/*
 	 * If the SCB was found in the disconnected list (as is
 	 * always the case in non-paging scenarios), SCBPTR is already
@@ -833,19 +964,19 @@
 get_tag:
 	mvi	ARG_1	call inb_next;	/* tag value */
 
-.if ! ( SCB_PAGING )
+	if ((p->flags & AHC_PAGESCBS) == 0) {
 index_by_tag:
-	mov	SCBPTR,ARG_1;
-	test	SCB_CONTROL,TAG_ENB	jz  not_found;
-	mov	SCBPTR	call rem_scb_from_disc_list;
-.else
-/*
- * Ensure that the SCB the tag points to is for an SCB transaction
- * to the reconnecting target.
- */
+		mov	SCBPTR,ARG_1;
+		test	SCB_CONTROL,TAG_ENB	jz  not_found;
+		mov	SCBPTR	call rem_scb_from_disc_list;
+	} else {
+		/*
+		 * Ensure that the SCB the tag points to is for
+		 * an SCB transaction to the reconnecting target.
+		 */
 use_retrieveSCB:
-	call	retrieveSCB;
-.endif
+		call	retrieveSCB;
+	}
 setup_SCB:
 	mov	A, SAVED_TCL;
 	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
@@ -924,16 +1055,16 @@
 inb_last:
 	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
 
-.if ( TARGET_MODE )
-/*
- * Send a byte to an initiator in Automatic PIO mode.
- * SPIOEN must be on prior to calling this routine.
- */
+if ((p->flags & AHC_TARGETMODE) != 0) {
+	/*
+	 * Send a byte to an initiator in Automatic PIO mode.
+	 * SPIOEN must be on prior to calling this routine.
+	 */
 target_outb:
 	mov	SCSIDATL, SINDEX;
 	test	SSTAT0, SPIORDY	jz .;
 	ret;
-.endif
+}
 	
 mesgin_phasemis:
 /*
@@ -980,7 +1111,17 @@
 dma_dmadone:
 	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
 dma_halt:
-	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN)	jnz dma_halt; 
+	/*
+	 * Some revisions of the aic7880 have a problem where, if the
+	 * data fifo is full, but the PCI input latch is not empty, 
+	 * HDMAEN cannot be cleared.  The fix used here is to attempt
+	 * to drain the data fifo until there is space for the input
+	 * latch to drain and HDMAEN de-asserts.
+	 */
+	if ((p->features & AHC_ULTRA2) == 0) {
+		mov	NONE, DFDAT;
+	}
+	test	DFCNTRL, HDMAEN jnz dma_halt;
 return:
 	ret;
 
@@ -1079,18 +1220,67 @@
 	mov	ARG_1, SCB_TAG	ret;
 	mvi	ARG_1, SCB_LIST_NULL ret;
 
-set_SCBID_host_addr_and_cnt:
-	mov	DINDEX, SINDEX;
-	mvi	SCBID_ADDR call	set_1byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 1 ret;
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
+ */
+fetch_byte:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		mvi	CCSGCTL, CCSGRESET;
+		bmov	RETURN_2, CCSGRAM, 1 ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+		call	dma_finish;
+		mov	RETURN_2, DFDAT ret;
+	}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSCBCTL, CCSCBRESET ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, FIFORESET ret;
+	}
+
+post_byte:
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	CCSCBRAM, SINDEX, 1;
+		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+		test	CCSCBCTL, CCSCBDONE jz .;
+		clr	CCSCBCTL ret;
+	} else {
+		mov	DFDAT, SINDEX;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+	}
 
 get_SCBID_from_host:
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
-	call	dma_finish;
-	mov	ARG_1, DFDAT ret;
+	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
+	mov	RETURN_1,  RETURN_2 ret;
 
 phase_lock:     
 	test	SSTAT1, REQINIT jz phase_lock;
@@ -1116,65 +1306,102 @@
 	mov	DINDIR, SINDIR ret;
 
 /*
- * Setup haddr and count assuming that A is an
- * index into an array of 32byte objects.
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
  */
-set_32byte_haddr_and_clrcnt:
-	shr	DINDEX, 3, A;
+set_32byte_addr:
+	shr	ARG_2, 3, A;
 	shl	A, 5;
-set_1byte_haddr_and_clrcnt:	/* DINDEX must be 0 upon call */
-	add	HADDR[0], A, SINDIR;
-	mov	A, DINDEX;
-	adc	HADDR[1], A, SINDIR;
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+	add	DINDIR, A, SINDIR;
+	mov	A, ARG_2;
+	adc	DINDIR, A, SINDIR;
 	clr	A;
-	adc	HADDR[2], A, SINDIR;
-	adc	HADDR[3], A, SINDIR;
-	/* Clear Count */
-	clr	HCNT[1];
-	clr	HCNT[2] ret;
+	adc	DINDIR, A, SINDIR;
+	adc	DINDIR, A, SINDIR ret;
 
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
 dma_scb:
-	/*
-	 * SCB index is in SINDEX.  Determine the physical address in
-	 * the host where this SCB is located and load HADDR with it.
-	 */
 	mov	A, SINDEX;
-	mvi	HSCB_ADDR call set_32byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 28;
-	mov	DFCNTRL, DMAPARAMS;
-	test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
-	/* Fill it with the SCB data */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mov	CCSCBPTR, SCBPTR;
+		mvi	CCHCNT, 32;
+		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+		jmp	dma_scb_finish;
+dma_scb_tohost:
+		if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+			mvi	CCSCBCTL, CCSCBRESET;
+			bmov	CCSCBRAM, SCB_CONTROL, 32;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+		} else {
+			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+		}
+dma_scb_finish:
+		clr	CCSCBCTL;
+		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
+		ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mvi	HCNT[0], 32;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mov	DFCNTRL, DMAPARAMS;
+		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+		/* Fill it with the SCB data */
 copy_scb_tofifo:
-	mvi	SINDEX, SCB_CONTROL;
-	add	A, 28, SINDEX;
+		mvi	SINDEX, SCB_CONTROL;
+		add	A, 32, SINDEX;
 copy_scb_tofifo_loop:
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	cmp	SINDEX, A jne copy_scb_tofifo_loop;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		cmp	SINDEX, A jne copy_scb_tofifo_loop;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 dma_scb_fromhost:
-	call	dma_finish;
-	/* If we were putting the SCB, we are done */
-	test	DMAPARAMS, DIRECTION	jz	return;
-	mvi	SCB_CONTROL  call dfdat_in_7;
-	call	dfdat_in_7_continued;
-	call	dfdat_in_7_continued;
-	jmp	dfdat_in_7_continued;
+		call	dma_finish;
+		/* If we were putting the SCB, we are done */
+		test	DMAPARAMS, DIRECTION	jz	return;
+		mvi	SCB_CONTROL  call dfdat_in_7;
+		call	dfdat_in_7_continued;
+		call	dfdat_in_7_continued;
+		jmp	dfdat_in_7_continued;
 dfdat_in_7:
-	mov     DINDEX,SINDEX;
+		mov     DINDEX,SINDEX;
 dfdat_in_7_continued:
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT ret;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT ret;
+	}
+
 
 /*
  * Wait for DMA from host memory to data FIFO to complete, then disable
@@ -1188,13 +1415,13 @@
 	ret;
 
 add_scb_to_free_list:
-.if ( SCB_PAGING )
-	mov	SCB_NEXT, FREE_SCBH;
-	mov	FREE_SCBH, SCBPTR;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	SCB_NEXT, FREE_SCBH;
+		mov	FREE_SCBH, SCBPTR;
+	}
 	mvi	SCB_TAG, SCB_LIST_NULL ret;
 
-.if ( SCB_PAGING )
+if ((p->flags & AHC_PAGESCBS) != 0) {
 get_free_or_disc_scb:
 	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
 	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
@@ -1211,7 +1438,7 @@
 dequeue_free_scb:
 	mov	SCBPTR, FREE_SCBH;
 	mov	FREE_SCBH, SCB_NEXT ret;
-.endif
+}
 
 add_scb_to_disc_list:
 /*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov