Date: Tue, 13 Jun 2000 18:00:03 -0700 (PDT) From: Geir Inge Jensen <gij@jk.priv.no> To: freebsd-bugs@FreeBSD.org Subject: Re: i386/19226: SCSI timeouts during heavy load Message-ID: <200006140100.SAA13387@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR i386/19226; it has been noted by GNATS.
From: Geir Inge Jensen <gij@jk.priv.no>
To: ken@kdm.org (Kenneth D. Merry)
Cc: gij@jk.priv.no (Geir Inge Jensen),
FreeBSD-gnats-submit@FreeBSD.ORG, gibbs@FreeBSD.ORG
Subject: Re: i386/19226: SCSI timeouts during heavy load
Date: Wed, 14 Jun 2000 02:59:11 +0200 (CEST)
"Kenneth D. Merry" wrote:
> I'm really not sure what your problem is, so I've handed it off to Justin
> Gibbs <gibbs@FreeBSD.ORG>, who is the author of the Adaptec driver.
We have done some more debugging since yesterday.
The timeouts could have been caused by a blocked interrupt line. However,
we checked both the IOAPIC and cpl after we lost contact with the drives,
and that is not the case. This is the output of our kld module:
SMP: blocked=0x00fba200, apending=0x00000000, imen=0x00fba200
SMP: pending=0x00000000, active=0x00000000, cpl=0x00000000
- blocked shows which interrupts has the IOART_INTMSET bit set in the
corresponding IOAPIC redirection table entry
- apending shows which interrupts has the IOART_REM_IRR bit set in the
corresponding IOAPIC redirection table entry.
- imem is a copy of apic_imem.
- pending is a copy of ipending.
- active is a copy of iactive.
- cpl is cpl.
ahc0 has irq 11, and ahc1 has irq 18. As you can see, neither is blocked.
We also tried to get in contact with the disk drives by polling ahc_poll
for each controller 100 times pr. second. It didn't work. This action did
help on another test system we had (by first disabling the interrupt
lines).
So we began looking at the sequencer code. This is a typical timeout
message:
(da6:ahc1:0:8:0): SCB 0x33 - timed out while idle, SEQADDR == 0x157
The SCB's differ, but is is always with SEQADDR == 0x157.
Below is the sequencer code for the AHA29160 controller. It looks
like the cmd channel is 'stuck', and the sequencer code is hanging in
fetch_byte. Or it might be that CCSGDONE never is set in CCSGCTL. So
we end up with an endless loop.
- Geir Inge
----- SEQUENCER CODE FOR AHA 29160 -----
/*
* Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
*
* Copyright (c) 1994-2000 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* the GNU Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.95 2000/03/18 22:28:20 gibbs Exp $
*/
#include <dev/aic7xxx/aic7xxx.reg>
#include <cam/scsi/scsi_message.h>
/*
* A few words on the waiting SCB list:
* After starting the selection hardware, we check for reconnecting targets
* as well as for our selection to complete just in case the reselection wins
* bus arbitration. The problem with this is that we must keep track of the
* SCB that we've already pulled from the QINFIFO and started the selection
* on just in case the reselection wins so that we can retry the selection at
* a later time. This problem cannot be resolved by holding a single entry
* in scratch ram since a reconnecting target can request sense and this will
* create yet another SCB waiting for selection. The solution used here is to
* use byte 27 of the SCB as a psuedo-next pointer and to thread a list
* of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes,
* SCB_LIST_NULL is 0xff which is out of range. An entry is also added to
* this list everytime a request sense occurs or after completing a non-tagged
* command for which a second SCB has been queued. The sequencer will
* automatically consume the entries.
*/
reset:
000 ff6a0608 clr SCSISIGO; /* De-assert BSY */
001 086a6800 mvi MSG_OUT, MSG_NOOP; /* No message to send */
002 7f020408 and SXFRCTL1, ~BITBUCKET;
/* Always allow reselection */
003 32580008 and SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ_TEMPLATE;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
/* Ensure that no DMA operations are in progress */
004 ff6ad609 clr CCSGCTL;
005 ff6adc09 clr CCSCBCTL;
}
poll_for_work:
006 0065ec59 call clear_target_state;
007 f7010208 and SXFRCTL0, ~SPIOEN;
if ((ahc->features & AHC_QUEUE_REGS) == 0) {
mov A, QINPOS;
}
poll_for_work_loop:
if ((ahc->features & AHC_QUEUE_REGS) == 0) {
and SEQCTL, ~PAUSEDIS;
}
008 600b7c68 test SSTAT0, SELDO|SELDI jnz selection;
009 40001268 test SCSISEQ, ENSELO jnz poll_for_work_loop;
if ((ahc->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 */
}
00a ff3e4a60 cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
test_queue:
/* Has the driver posted any work for us? */
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
00b 40fa1278 test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
00c fff6d408 mov NONE, SNSCB_QOFF;
00d 014e9c18 inc QINPOS;
} else {
or SEQCTL, PAUSEDIS;
cmp KERNEL_QINPOS, A je poll_for_work_loop;
inc QINPOS;
and SEQCTL, ~PAUSEDIS;
}
/*
* We have at least one queued SCB now and we don't have any
* SCBs in the list of SCBs awaiting selection. If we have
* any SCBs available for use, pull the tag from the QINFIFO
* and get to work on it.
*/
if ((ahc->flags & AHC_PAGESCBS) != 0) {
00e 006a945d mov ALLZEROS call get_free_or_disc_scb;
}
dequeue_scb:
00f ff4ec818 add A, -1, QINPOS;
010 016aa05c mvi QINFIFO_OFFSET call fetch_byte;
if ((ahc->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.
*/
011 0d6a6a00 mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
012 00531e5d mov RETURN_2 call dma_scb;
/*
* Preset the residual fields in case we never go through a data phase.
* This isn't done by the host so we can avoid a DMA to clear these
* fields for the normal case of I/O that completes without underrun
* or overrun conditions.
*/
if ((ahc->features & AHC_CMD_CHAN) != 0) {
013 03b05231 bmov SCB_RESID_DCNT, SCB_DATACNT, 3;
} else {
mov SCB_RESID_DCNT[0],SCB_DATACNT[0];
mov SCB_RESID_DCNT[1],SCB_DATACNT[1];
mov SCB_RESID_DCNT[2],SCB_DATACNT[2];
}
014 ffa35009 mov SCB_RESID_SGCNT, SCB_SGCOUNT;
start_scb:
/*
* Place us on the waiting list in case our selection
* doesn't win during bus arbitration.
*/
015 ff3e7409 mov SCB_NEXT,WAITING_SCBH;
016 ff907c08 mov WAITING_SCBH, SCBPTR;
start_waiting:
/*
* Pull the first entry off of the waiting SCB list.
*/
017 ff3e2009 mov SCBPTR, WAITING_SCBH;
018 00655058 call start_selection;
019 00650c40 jmp poll_for_work;
start_selection:
if ((ahc->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:
01a ff58ca08 mov SINDEX, SCSISEQ_TEMPLATE;
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SCB_CONTROL, TARGET_SCB jz . + 4;
if ((ahc->features & AHC_ULTRA2) != 0) {
mov SCSIID_ULTRA2, SCB_CMDPTR[2];
} else {
mov SCSIID, SCB_CMDPTR[2];
}
or SINDEX, TEMODE;
jmp initialize_scsiid_fini;
}
if ((ahc->features & AHC_ULTRA2) != 0) {
01b f0a1c808 and A, TID, SCB_TCL; /* Get target ID */
01c 0f0f1e08 and SCSIID_ULTRA2, OID; /* Clear old target */
01d 000f1e00 or SCSIID_ULTRA2, A;
} else {
and A, TID, SCB_TCL; /* Get target ID */
and SCSIID, OID; /* Clear old target */
or SCSIID, A;
}
initialize_scsiid_fini:
01e ff65000c mov SCSISEQ, SINDEX ret;
/*
* Initialize transfer settings and clear the SCSI channel.
* SINDEX should contain any additional bit's the client wants
* set in SXFRCTL0. We also assume that the current SCB is
* a valid SCB for the target we wish to talk to.
*/
initialize_channel:
01f 12650200 or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
set_transfer_settings:
if ((ahc->features & AHC_ULTRA) != 0) {
test SCB_CONTROL, ULTRAENB jz . + 2;
or SXFRCTL0, FAST20;
}
/*
* Initialize SCSIRATE with the appropriate value for this target.
*/
if ((ahc->features & AHC_ULTRA2) != 0) {
020 02bb0834 bmov SCSIRATE, SCB_SCSIRATE, 2 ret;
} else {
mov SCSIRATE, SCB_SCSIRATE ret;
}
selection:
021 400b1069 test SSTAT0,SELDO jnz select_out;
022 206a1600 mvi CLRSINT0, CLRSELDI;
select_in:
if ((ahc->flags & AHC_TARGETMODE) != 0) {
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
test SSTAT0, TARGET jz initiator_reselect;
}
/*
* We've just been selected. Assert BSY and
* setup the phase for receiving messages
* from the target.
*/
mvi SCSISIGO, P_MESGOUT|BSYO;
mvi CLRSINT1, CLRBUSFREE;
/*
* Setup the DMA for sending the identify and
* command information.
*/
or SEQ_FLAGS, CMDPHASE_PENDING;
mov A, TQINPOS;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mvi DINDEX, CCHADDR;
mvi TMODE_CMDADDR call set_32byte_addr;
mvi CCSCBCTL, CCSCBRESET;
} else {
mvi DINDEX, HADDR;
mvi TMODE_CMDADDR call set_32byte_addr;
mvi DFCNTRL, FIFORESET;
}
/* Initiator that selected us */
and SAVED_TCL, SELID_MASK, SELID;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, SAVED_TCL;
} else {
mov DFDAT, SAVED_TCL;
}
/* The Target ID we were selected at */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
if ((ahc->features & AHC_MULTI_TID) != 0) {
and CCSCBRAM, OID, TARGIDIN;
} else if ((ahc->features & AHC_ULTRA2) != 0) {
and CCSCBRAM, OID, SCSIID_ULTRA2;
} else {
and CCSCBRAM, OID, SCSIID;
}
} else {
if ((ahc->features & AHC_MULTI_TID) != 0) {
and DFDAT, OID, TARGIDIN;
} else if ((ahc->features & AHC_ULTRA2) != 0) {
and DFDAT, OID, SCSIID_ULTRA2;
} else {
and DFDAT, OID, SCSIID;
}
}
/* No tag yet */
mvi INITIATOR_TAG, SCB_LIST_NULL;
/*
* If ATN isn't asserted, the target isn't interested
* in talking to us. Go directly to bus free.
*/
test SCSISIGI, ATNI jz target_busfree;
/*
* Watch ATN closely now as we pull in messages from the
* initiator. We follow the guidlines from section 6.5
* of the SCSI-2 spec for what messages are allowed when.
*/
call target_inb;
/*
* Our first message must be one of IDENTIFY, ABORT, or
* BUS_DEVICE_RESET.
*/
/* XXX May need to be more lax here for older initiators... */
test DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
/* Store for host */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, DINDEX;
} else {
mov DFDAT, DINDEX;
}
/* Remember for disconnection decision */
test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
/* XXX Honor per target settings too */
or SEQ_FLAGS, NO_DISCONNECT;
test SCSISIGI, ATNI jz ident_messages_done;
call target_inb;
/*
* If this is a tagged request, the tagged message must
* immediately follow the identify. We test for a valid
* tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
* < MSG_IGN_WIDE_RESIDUE.
*/
add A, -MSG_SIMPLE_Q_TAG, DINDEX;
jnc ident_messages_done;
add A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
jc ident_messages_done;
/* Store for host */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, DINDEX;
} else {
mov DFDAT, DINDEX;
}
/*
* If the initiator doesn't feel like providing a tag number,
* we've got a failed selection and must transition to bus
* free.
*/
test SCSISIGI, ATNI jz target_busfree;
/*
* Store the tag for the host.
*/
call target_inb;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, DINDEX;
} else {
mov DFDAT, DINDEX;
}
mov INITIATOR_TAG, DINDEX;
jmp ident_messages_done;
/*
* Pushed message loop to allow the kernel to
* run it's own target mode message state engine.
*/
host_target_message_loop:
mvi INTSTAT, HOST_MSG_LOOP;
nop;
cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop;
test SSTAT0, SPIORDY jz .;
jmp host_target_message_loop;
ident_messages_done:
/* If ring buffer is full, return busy or queue full */
if ((ahc->features & AHC_HS_MAILBOX) != 0) {
and A, HOST_TQINPOS, HS_MAILBOX;
} else {
mov A, KERNEL_TQINPOS;
}
cmp TQINPOS, A jne tqinfifo_has_space;
mvi P_STATUS|BSYO call change_phase;
cmp INITIATOR_TAG, SCB_LIST_NULL je . + 3;
mvi STATUS_QUEUE_FULL call target_outb;
jmp target_busfree_wait;
mvi STATUS_BUSY call target_outb;
jmp target_busfree_wait;
tqinfifo_has_space:
/* Terminate the ident list */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mvi CCSCBRAM, SCB_LIST_NULL;
} else {
mvi DFDAT, SCB_LIST_NULL;
}
or SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
test SCSISIGI, ATNI jnz target_mesgout_pending_msg;
jmp target_ITloop;
/*
* We carefully toggle SPIOEN to allow us to return the
* message byte we receive so it can be checked prior to
* driving REQ on the bus for the next byte.
*/
target_inb:
/*
* Drive REQ on the bus by enabling SCSI PIO.
*/
or SXFRCTL0, SPIOEN;
/* Wait for the byte */
test SSTAT0, SPIORDY jz .;
/* Prevent our read from triggering another REQ */
and SXFRCTL0, ~SPIOEN;
/* Save latched contents */
mov DINDEX, SCSIDATL ret;
}
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
/*
* Reselection has been initiated by a target. Make a note that we've been
* reselected, but haven't seen an IDENTIFY message from the target yet.
*/
initiator_reselect:
/* XXX test for and handle ONE BIT condition */
023 f0196e08 and SAVED_TCL, SELID_MASK, SELID;
if ((ahc->features & AHC_TWIN) != 0) {
test SBLKCTL, SELBUSB jz . + 2;
or SAVED_TCL, SELBUSB;
}
024 1a010200 or SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
025 086a1800 mvi CLRSINT1,CLRBUSFREE;
026 08112200 or SIMODE1, ENBUSFREE; /*
* We aren't expecting a
* bus free, so interrupt
* the kernel driver if it
* happens.
*/
027 0065ce41 jmp ITloop;
}
/*
* After the selection, remove this SCB from the "waiting SCB"
* list. This is achieved by simply moving our "next" pointer into
* WAITING_SCBH. Our next pointer will be set to null the next time this
* SCB is used, so don't bother with it now.
*/
select_out:
/* Turn off the selection hardware */
028 b2000008 and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
029 406a1600 mvi CLRSINT0, CLRSELDO;
02a ff3e2009 mov SCBPTR, WAITING_SCBH;
02b ffba7c08 mov WAITING_SCBH,SCB_NEXT;
02c ffa16e08 mov SAVED_TCL, SCB_TCL;
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SSTAT0, TARGET jz initiator_select;
/*
* We've just re-selected an initiator.
* Assert BSY and setup the phase for
* sending our identify messages.
*/
mvi P_MESGIN|BSYO call change_phase;
mvi CLRSINT1,CLRBUSFREE;
/*
* Start out with a simple identify message.
*/
and A, LID, SCB_TCL;
or A, MSG_IDENTIFYFLAG call target_outb;
/*
* If we are the result of a tagged command, send
* a simple Q tag and the tag id.
*/
test SCB_CONTROL, TAG_ENB jz . + 3;
mvi MSG_SIMPLE_Q_TAG call target_outb;
mov SCB_INITIATOR_TAG call target_outb;
mov INITIATOR_TAG, SCB_INITIATOR_TAG;
target_synccmd:
/*
* Now determine what phases the host wants us
* to go through.
*/
mov SEQ_FLAGS, SCB_TARGET_PHASES;
target_ITloop:
/*
* Start honoring ATN signals now that
* we properly identified ourselves.
*/
test SCSISIGI, ATNI jnz target_mesgout;
test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase;
test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase;
test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase;
/*
* No more work to do. Either disconnect or not depending
* on the state of NO_DISCONNECT.
*/
test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
if ((ahc->flags & AHC_PAGESCBS) != 0) {
mov ALLZEROS call get_free_or_disc_scb;
}
mov RETURN_1, ALLZEROS;
call complete_target_cmd;
cmp RETURN_1, CONT_MSG_LOOP jne .;
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
mov SCB_TAG call dma_scb;
jmp target_synccmd;
target_mesgout:
mvi SCSISIGO, P_MESGOUT|BSYO;
call target_inb;
/* Local Processing goes here... */
target_mesgout_pending_msg:
jmp host_target_message_loop;
target_disconnect:
mvi P_MESGIN|BSYO call change_phase;
test SEQ_FLAGS, DPHASE jz . + 2;
mvi MSG_SAVEDATAPOINTER call target_outb;
mvi MSG_DISCONNECT call target_outb;
target_busfree_wait:
/* Wait for preceeding I/O session to complete. */
test SCSISIGI, ACKI jnz .;
target_busfree:
clr SCSISIGO;
mvi LASTPHASE, P_BUSFREE;
call complete_target_cmd;
jmp poll_for_work;
target_cmdphase:
mvi P_COMMAND|BSYO call change_phase;
call target_inb;
mov A, DINDEX;
/* Store for host */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, A;
} else {
mov DFDAT, A;
}
/*
* Determine the number of bytes to read
* based on the command group code via table lookup.
* We reuse the first 8 bytes of the TARG_SCSIRATE
* BIOS array for this table. Count is one less than
* the total for the command since we've already fetched
* the first byte.
*/
shr A, CMD_GROUP_CODE_SHIFT;
add SINDEX, TARG_SCSIRATE, A;
mov A, SINDIR;
test A, 0xFF jz command_phase_done;
command_loop:
or SXFRCTL0, SPIOEN;
test SSTAT0, SPIORDY jz .;
cmp A, 1 jne . + 2;
and SXFRCTL0, ~SPIOEN; /* Last Byte */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
mov CCSCBRAM, SCSIDATL;
} else {
mov DFDAT, SCSIDATL;
}
dec A;
test A, 0xFF jnz command_loop;
command_phase_done:
and SEQ_FLAGS, ~CMDPHASE_PENDING;
jmp target_ITloop;
target_dphase:
/*
* Data direction flags are from the
* perspective of the initiator.
*/
test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
mvi LASTPHASE, P_DATAOUT;
mvi P_DATAIN|BSYO call change_phase;
jmp . + 3;
mvi LASTPHASE, P_DATAIN;
mvi P_DATAOUT|BSYO call change_phase;
mov ALLZEROS call initialize_channel;
jmp p_data;
target_sphase:
mvi P_STATUS|BSYO call change_phase;
mvi LASTPHASE, P_STATUS;
mov SCB_TARGET_STATUS call target_outb;
/* XXX Watch for ATN or parity errors??? */
mvi SCSISIGO, P_MESGIN|BSYO;
/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
mov ALLZEROS call target_outb;
jmp target_busfree_wait;
complete_target_cmd:
test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2;
mov SCB_TAG jmp complete_post;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
/* Set the valid byte */
mvi CCSCBADDR, 24;
mov CCSCBRAM, ALLONES;
mvi CCHCNT, 28;
or CCSCBCTL, CCSCBEN|CCSCBRESET;
test CCSCBCTL, CCSCBDONE jz .;
clr CCSCBCTL;
} else {
/* Set the valid byte */
or DFCNTRL, FIFORESET;
mvi DFWADDR, 3; /* Third 64bit word or byte 24 */
mov DFDAT, ALLONES;
mvi HCNT[0], 28;
clr HCNT[1];
clr HCNT[2];
or DFCNTRL, HDMAEN|FIFOFLUSH;
call dma_finish;
}
inc TQINPOS;
mvi INTSTAT,CMDCMPLT ret;
}
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
initiator_select:
02d 086a7258 mvi SPIOEN call initialize_channel;
/*
* We aren't expecting a bus free, so interrupt
* the kernel driver if it happens.
*/
02e 086a1800 mvi CLRSINT1,CLRBUSFREE;
02f 08112200 or SIMODE1, ENBUSFREE;
/*
* As soon as we get a successful selection, the target
* should go into the message out phase since we have ATN
* asserted.
*/
030 806a6800 mvi MSG_OUT, MSG_IDENTIFYFLAG;
031 80366c00 or SEQ_FLAGS, IDENTIFY_SEEN;
/*
* Main loop for information transfer phases. Wait for the
* target to assert REQ before checking MSG, C/D and I/O for
* the bus phase.
*/
ITloop:
032 0065ec5c call phase_lock;
033 ff3dc808 mov A, LASTPHASE;
034 bf64067a test A, ~P_DATAIN jz p_data;
035 8064da72 cmp A,P_COMMAND je p_command;
036 a0641473 cmp A,P_MESGOUT je p_mesgout;
037 c0640c73 cmp A,P_STATUS je p_status;
038 e0645c73 cmp A,P_MESGIN je p_mesgin;
039 016a2201 mvi INTSTAT,BAD_PHASE;
03a 0065ce41 jmp ITloop; /* Try reading the bus again. */
await_busfree:
03b f7112208 and SIMODE1, ~ENBUSFREE;
03c ff06d408 mov NONE, SCSIDATL; /* Ack the last byte */
03d f7010208 and SXFRCTL0, ~SPIOEN;
03e 090ce679 test SSTAT1,REQINIT|BUSFREE jz .;
03f 080c0c68 test SSTAT1, BUSFREE jnz poll_for_work;
040 016a2201 mvi INTSTAT, BAD_PHASE;
}
clear_target_state:
/*
* We assume that the kernel driver may reset us
* at any time, even in the middle of a DMA, so
* clear DFCNTRL too.
*/
041 ff6a2609 clr DFCNTRL;
/*
* We don't know the target we will connect to,
* so default to narrow transfers to avoid
* parity problems.
*/
if ((ahc->features & AHC_ULTRA2) != 0) {
042 026a0830 bmov SCSIRATE, ALLZEROS, 2;
} else {
clr SCSIRATE;
and SXFRCTL0, ~(FAST20);
}
043 016a7a00 mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
044 ff6a6c0c clr SEQ_FLAGS ret;
/*
* If we re-enter the data phase after going through another phase, the
* STCNT may have been cleared, so restore it from the residual field.
*/
data_phase_reinit:
if ((ahc->features & AHC_ULTRA2) != 0) {
/*
* The preload circuitry requires us to
* reload the address too, so pull it from
* the shaddow address.
*/
045 04141031 bmov HADDR, SHADDR, 4;
046 03a91831 bmov HCNT, SCB_RESID_DCNT, 3;
} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
bmov STCNT, SCB_RESID_DCNT, 3;
} else {
mvi DINDEX, STCNT;
mvi SCB_RESID_DCNT call bcopy_3;
}
047 01a9b208 and DATA_COUNT_ODD, 0x1, SCB_RESID_DCNT[0];
048 00652842 jmp data_phase_loop;
p_data:
if ((ahc->features & AHC_ULTRA2) != 0) {
049 a86a6a00 mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
} else {
mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
}
04a 403d0e6a test LASTPHASE, IOI jnz . + 2;
04b 04356a00 or DMAPARAMS, DIRECTION;
04c 00655a5c call assert; /*
* Ensure entering a data
* phase is okay - seen identify, etc.
*/
if ((ahc->features & AHC_CMD_CHAN) != 0) {
04d 806ad401 mvi CCSGADDR, CCSGADDR_MAX;
}
04e 2036f869 test SEQ_FLAGS, DPHASE jnz data_phase_reinit;
/* We have seen a data phase */
04f 20366c00 or SEQ_FLAGS, DPHASE;
/*
* Initialize the DMA address and counter from the SCB.
* Also set SG_COUNT and SG_NEXT in memory since we cannot
* modify the values in the SCB itself until we see a
* save data pointers message.
*/
if ((ahc->features & AHC_CMD_CHAN) != 0) {
050 07ac1031 bmov HADDR, SCB_DATAPTR, 7;
} else {
mvi DINDEX, HADDR;
mvi SCB_DATAPTR call bcopy_7;
}
051 01b0b208 and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
if ((ahc->features & AHC_ULTRA2) == 0) {
if ((ahc->features & AHC_CMD_CHAN) != 0) {
bmov STCNT, HCNT, 3;
} else {
call set_stcnt_from_hcnt;
}
}
if ((ahc->features & AHC_CMD_CHAN) != 0) {
052 05a37030 bmov SG_COUNT, SCB_SGCOUNT, 5;
} else {
mvi DINDEX, SG_COUNT;
mvi SCB_SGCOUNT call bcopy_5;
}
data_phase_loop:
/* Guard against overruns */
053 ff38386a test SG_COUNT, 0xff jnz data_phase_inbounds;
/*
* Turn on 'Bit Bucket' mode, set the transfer count to
* 16meg and let the target run until it changes phase.
* When the transfer completes, notify the host that we
* had an overrun.
*/
054 80020400 or SXFRCTL1,BITBUCKET;
055 e7356a08 and DMAPARAMS, ~(HDMAEN|SDMAEN);
if ((ahc->features & AHC_ULTRA2) != 0) {
056 03691831 bmov HCNT, ALLONES, 3;
} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
bmov STCNT, ALLONES, 3;
} else {
mvi STCNT[0], 0xFF;
mvi STCNT[1], 0xFF;
mvi STCNT[2], 0xFF;
}
data_phase_inbounds:
/* If we are the last SG block, tell the hardware. */
057 01384262 cmp SG_COUNT,0x01 jne data_phase_wideodd;
if ((ahc->features & AHC_ULTRA2) != 0) {
058 02fcf801 or SG_CACHEPTR, LAST_SEG;
} else {
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SSTAT0, TARGET jz . + 2;
test DMAPARAMS, DIRECTION jz data_phase_wideodd;
}
and DMAPARAMS, ~WIDEODD;
}
data_phase_wideodd:
if ((ahc->features & AHC_ULTRA2) != 0) {
059 ff69ca08 mov SINDEX, ALLONES;
05a ff352609 mov DFCNTRL, DMAPARAMS;
05b 040b466a test SSTAT0, SDONE jnz .;/* Wait for preload to complete */
data_phase_dma_loop:
05c 040b4e6a test SSTAT0, SDONE jnz data_phase_dma_done;
05d 100c487a test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */
} else {
mov DMAPARAMS call dma;
}
data_phase_dma_done:
/* Go tell the host about any overruns */
05e 8002b66a test SXFRCTL1,BITBUCKET jnz data_phase_overrun;
/* See if we completed this segment */
05f ff089c6a test STCNT[0], 0xff jnz data_phase_finish;
060 ff099c6a test STCNT[1], 0xff jnz data_phase_finish;
061 ff0a9c6a test STCNT[2], 0xff jnz data_phase_finish;
/*
* Advance the scatter-gather pointers if needed
*/
sg_advance:
062 ff387018 dec SG_COUNT; /* one less segment to go */
063 ff389c7a test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */
/*
* Load a struct scatter and set up the data address and length.
* If the working value of the SG count is nonzero, then
* we need to load a new set of values.
*
* This, like all DMA's, assumes little-endian host data storage.
*/
sg_load:
if ((ahc->features & AHC_CMD_CHAN) != 0) {
/*
* Do we have any prefetch left???
*/
064 80ea7262 cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
/*
* Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
*/
065 ef38c818 add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
066 806ac800 mvi A, CCSGADDR_MAX;
067 0065644a jc . + 2;
068 3338c828 shl A, 3, SG_COUNT;
069 ff64d009 mov CCHCNT, A;
06a 0439c031 bmov CCHADDR, SG_NEXT, 4;
06b 096ad601 mvi CCSGCTL, CCSGEN|CCSGRESET;
06c 80eb6a7a test CCSGCTL, CCSGDONE jz .;
06d f7ebd609 and CCSGCTL, ~CCSGEN;
06e 08eb6e6a test CCSGCTL, CCSGEN jnz .;
06f 016ad601 mvi CCSGCTL, CCSGRESET;
prefetched_segs_avail:
070 08e91031 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;
}
/* Track odd'ness */
071 018c887a test HCNT[0], 0x1 jz . + 2;
072 0159b210 xor DATA_COUNT_ODD, 0x1;
if ((ahc->features & AHC_ULTRA2) == 0) {
/* Load STCNT as well. It is a mirror of HCNT */
if ((ahc->features & AHC_CMD_CHAN) != 0) {
bmov STCNT, HCNT, 3;
} else {
call set_stcnt_from_hcnt;
}
}
/* Advance the SG pointer */
073 ff6ac808 clr A; /* add sizeof(struct scatter) */
074 08397218 add SG_NEXT[0],SG_SIZEOF;
075 003a7420 adc SG_NEXT[1],A;
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SSTAT0, TARGET jnz data_phase_loop;
}
076 010c947a test SSTAT1, REQINIT jz .;
077 100c287a test SSTAT1,PHASEMIS jz data_phase_loop;
/* Ensure the last seg is visable at the shaddow layer */
if ((ahc->features & AHC_ULTRA2) != 0) {
078 ff352609 mov DFCNTRL, DMAPARAMS;
079 040b9a6a test SSTAT0, SDONE jnz .;/* Wait for preload to complete */
}
data_phase_finish:
if ((ahc->features & AHC_ULTRA2) != 0) {
07a 0065c05a 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
* were transferred on the SCSI (as opposed to the host) bus.
*/
if ((ahc->features & AHC_CMD_CHAN) != 0) {
07b 03085231 bmov SCB_RESID_DCNT, STCNT, 3;
} else {
mov SCB_RESID_DCNT[0],STCNT[0];
mov SCB_RESID_DCNT[1],STCNT[1];
mov SCB_RESID_DCNT[2],STCNT[2];
}
07c ff385009 mov SCB_RESID_SGCNT, SG_COUNT;
if ((ahc->features & AHC_ULTRA2) != 0) {
07d 12010200 or SXFRCTL0, CLRSTCNT|CLRCHN;
}
if ((ahc->flags & AHC_TARGETMODE) != 0) {
test SEQ_FLAGS, DPHASE_PENDING jz ITloop;
and SEQ_FLAGS, ~DPHASE_PENDING;
/*
* For data-in phases, wait for any pending acks from the
* initiator before changing phase.
*/
test DFCNTRL, DIRECTION jz target_ITloop;
test SSTAT1, REQINIT jnz .;
jmp target_ITloop;
}
07e 0065ce41 jmp ITloop;
data_phase_overrun:
if ((ahc->features & AHC_ULTRA2) != 0) {
07f 0065c05a call ultra2_dmafinish;
080 12010200 or SXFRCTL0, CLRSTCNT|CLRCHN;
}
/*
* Turn off BITBUCKET mode and notify the host
*/
081 7f020408 and SXFRCTL1, ~BITBUCKET;
082 f16a2201 mvi INTSTAT,DATA_OVERRUN;
083 0065ce41 jmp ITloop;
ultra2_dmafinish:
if ((ahc->features & AHC_ULTRA2) != 0) {
084 0493d26a test DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
085 df932609 and DFCNTRL, ~SCSIEN;
086 2093c46a test DFCNTRL, SCSIEN jnz .;
ultra2_dmafifoflush:
087 02932601 or DFCNTRL, FIFOFLUSH;
/*
* The FIFOEMP status bit on the Ultra2 class
* of controllers seems to be a bit flaky.
* It appears that if the FIFO is full and the
* transfer ends with some data in the REQ/ACK
* FIFO, FIFOEMP will fall temporarily
* as the data is transferred to the PCI bus.
* This glitch lasts for fewer than 5 clock cycles,
* so we work around the problem by ensuring the
* status bit stays false through a full glitch
* window.
*/
088 0194c67a test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
089 0194c67a test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
08a 0194c67a test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
08b 0194c67a test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
08c 0194c67a test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
ultra2_dmafifoempty:
/* Don't clobber an inprogress host data transfer */
08d 1094d26a test DFSTATUS, MREQPEND jnz ultra2_dmafifoempty;
ultra2_dmahalt:
08e d7932609 and DFCNTRL, ~(SCSIEN|HDMAEN);
08f 0893d66a test DFCNTRL, HDMAEN jnz .;
090 ff6ad40c ret;
}
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
/*
* Command phase. Set up the DMA registers and let 'er rip.
*/
p_command:
091 00655a5c call assert;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
092 ffb81809 mov HCNT[0], SCB_CMDLEN;
093 026a1a31 bmov HCNT[1], ALLZEROS, 2;
if ((ahc->features & AHC_ULTRA2) == 0) {
bmov STCNT, HCNT, 3;
}
094 efb8d418 add NONE, -17, SCB_CMDLEN;
095 0065f04a jc dma_cmd_data;
/*
* The data fifo seems to require 4 byte alligned
* transfers from the sequencer. Force this to
* be the case by clearing HADDR[0] even though
* we aren't going to touch host memeory.
*/
096 016a1031 bmov HADDR[0], ALLZEROS, 1;
if ((ahc->features & AHC_ULTRA2) != 0) {
097 a46a2601 mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
} else {
mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
}
098 10c03231 bmov DFDAT, SCB_CMDSTORE, 16;
099 00650043 jmp cmd_loop;
dma_cmd_data:
09a 04b41031 bmov HADDR, SCB_CMDPTR, 4;
} else {
mvi DINDEX, HADDR;
mvi SCB_CMDPTR call bcopy_5;
clr HCNT[1];
clr HCNT[2];
}
if ((ahc->features & AHC_ULTRA2) == 0) {
if ((ahc->features & AHC_CMD_CHAN) == 0) {
call set_stcnt_from_hcnt;
}
mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
} else {
09b ac6a2601 mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
}
cmd_loop:
09c 040b046b test SSTAT0, SDONE jnz . + 2;
09d 100c007b test SSTAT1, PHASEMIS jz cmd_loop;
/*
* Wait for our ACK to go-away on it's own
* instead of being killed by SCSIEN getting cleared.
*/
09e 0103046b test SCSISIGI, ACKI jnz .;
09f c7932609 and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
0a0 3893086b test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
0a1 0065ce41 jmp ITloop;
/*
* Status phase. Wait for the data byte to appear, then read it
* and store it into the SCB.
*/
p_status:
0a2 00655a5c call assert;
0a3 ff064409 mov SCB_TARGET_STATUS, SCSIDATL;
0a4 0065ce41 jmp ITloop;
/*
* Message out phase. If MSG_OUT is MSG_IDENTIFYFLAG, build a full
* indentify message sequence and send it to the target. The host may
* override this behavior by setting the MK_MESSAGE bit in the SCB
* control byte. This will cause us to interrupt the host and allow
* it to handle the message phase completely on its own. If the bit
* associated with this target is set, we will also interrupt the host,
* thereby allowing it to send a message on the next selection regardless
* of the transaction being sent.
*
* If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
* This is done to allow the host to send messages outside of an identify
* sequence while protecting the seqencer from testing the MK_MESSAGE bit
* on an SCB that might not be for the current nexus. (For example, a
* BDR message in 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).
*
* When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
* in case the target decides to put us in this phase for some strange
* reason.
*/
p_mesgout_retry:
0a5 103d0600 or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
p_mesgout:
0a6 ff34ca08 mov SINDEX, MSG_OUT;
0a7 80654863 cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
0a8 10a06a6b test SCB_CONTROL,MK_MESSAGE jnz host_message_loop;
0a9 ffa1dc08 mov FUNCTION1, SCB_TCL;
0aa ff6ec808 mov A, FUNCTION1;
if ((ahc->features & AHC_HS_MAILBOX) != 0) {
/*
* Work around a pausing bug in at least the aic7890.
* If the host needs to update the TARGET_MSG_REQUEST
* bit field, it will set the HS_MAILBOX to 1. In
* response, we pause with a specific interrupt code
* asking for the mask to be updated before we continue.
* Ugh.
*/
0ab f086227b test HS_MAILBOX, 0xF0 jz . + 2;
0ac 616a2201 mvi INTSTAT, UPDATE_TMSG_REQ;
0ad ff6ad408 nop;
}
0ae ff56ca08 mov SINDEX, TARGET_MSG_REQUEST[0];
if ((ahc->features & AHC_TWIN) != 0) {
/* Second Channel uses high byte bits */
test SCB_TCL, SELBUSB jz . + 2;
mov SINDEX, TARGET_MSG_REQUEST[1];
} else if ((ahc->features & AHC_WIDE) != 0) {
0af 80a12e7b test SCB_TCL, 0x80 jz . + 2; /* target > 7 */
0b0 ff57ca08 mov SINDEX, TARGET_MSG_REQUEST[1];
}
0b1 00656a6b test SINDEX, A jnz host_message_loop;
p_mesgout_identify:
0b2 07a1ca08 and SINDEX,LID,SCB_TCL; /* lun */
0b3 40a0c808 and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
0b4 0065ca00 or SINDEX,A; /* or in disconnect privledge */
0b5 8065ca00 or SINDEX,MSG_IDENTIFYFLAG;
/*
* Send a tag message if TAG_ENB is set in the SCB control block.
* Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
*/
p_mesgout_tag:
0b6 20a04c7b test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
0b7 ff650c08 mov SCSIDATL, SINDEX; /* Send the identify message */
0b8 0065ec5c call phase_lock;
0b9 a03d5463 cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
0ba 23a00c08 and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
0bb 0065ec5c call phase_lock;
0bc a03d5463 cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
0bd 00b94c43 mov SCB_TAG jmp p_mesgout_onebyte;
/*
* Interrupt the driver, and allow it to handle this message
* phase and any required retries.
*/
p_mesgout_from_host:
0be ff654c63 cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
0bf 00656a43 jmp host_message_loop;
p_mesgout_onebyte:
0c0 406a1800 mvi CLRSINT1, CLRATNO;
0c1 ff650c08 mov SCSIDATL, SINDEX;
/*
* If the next bus phase after ATN drops is message out, it means
* that the target is requesting that the last message(s) be resent.
*/
0c2 0065ec5c call phase_lock;
0c3 a03d1273 cmp LASTPHASE, P_MESGOUT je p_mesgout_retry;
p_mesgout_done:
0c4 406a1800 mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
0c5 ff34a808 mov LAST_MSG, MSG_OUT;
0c6 086a6800 mvi MSG_OUT, MSG_NOOP; /* No message left */
0c7 0065ce41 jmp ITloop;
/*
* Message in phase. Bytes are read using Automatic PIO mode.
*/
p_mesgin:
0c8 646a1c5c mvi ACCUM call inb_first; /* read the 1st message byte */
0c9 8064cc6b test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
0ca 0464ae73 cmp A,MSG_DISCONNECT je mesgin_disconnect;
0cb 0264b473 cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs;
0cc 006a7673 cmp ALLZEROS,A je mesgin_complete;
0cd 0364c873 cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs;
0ce 08647273 cmp A,MSG_NOOP je mesgin_done;
/*
* Pushed message loop to allow the kernel to
* run it's own message state engine. To avoid an
* extra nop instruction after signaling the kernel,
* we perform the phase_lock before checking to see
* if we should exit the loop and skip the phase_lock
* in the ITloop. Performing back to back phase_locks
* shouldn't hurt, but why do it twice...
*/
host_message_loop:
0cf a16a2201 mvi INTSTAT, HOST_MSG_LOOP;
0d0 0065ec5c call phase_lock;
0d1 0852d071 cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1;
0d2 00656a43 jmp host_message_loop;
mesgin_done:
0d3 ff06d408 mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
0d4 0065ce41 jmp ITloop;
mesgin_complete:
/*
* We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
* and trigger a completion interrupt. Before doing so, check to see if there
* is a residual or the status byte is something other than STATUS_GOOD (0).
* In either of these conditions, we upload the SCB back to the host so it can
* process this information. In the case of a non zero status byte, we
* additionally interrupt the kernel driver synchronously, allowing it to
* decide if sense should be retrieved. If the kernel driver wishes to request
* sense, it will fill the kernel SCB with a request sense command and set
* RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload
* the SCB, and process it as the next command by adding it to the waiting list.
* If the kernel driver does not wish to request sense, it need only clear
* RETURN_1, and the command is allowed to complete normally. We don't bother
* to post to the QOUTFIFO in the error cases since it would require extra
* work in the kernel driver to ensure that the entry was removed before the
* command complete code tried processing it.
*/
/*
* First check for residuals
*/
0d5 ffa87a6b test SCB_RESID_SGCNT,0xff jnz upload_scb;
0d6 ffa2927b test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */
upload_scb:
0d7 016a6a00 mvi DMAPARAMS, FIFORESET;
0d8 00b91e5d mov SCB_TAG call dma_scb;
check_status:
0d9 ffa2927b test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */
0da 716a2201 mvi INTSTAT,BAD_STATUS; /* let driver know */
0db ff6ad408 nop;
0dc 40529263 cmp RETURN_1, SEND_SENSE jne complete;
/* This SCB becomes the next to execute as it will retrieve sense */
0dd 0d6a6a00 mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
0de 00b91e5d mov SCB_TAG call dma_scb;
add_to_waiting_list:
0df ff3e7409 mov SCB_NEXT,WAITING_SCBH;
0e0 ff907c08 mov WAITING_SCBH, SCBPTR;
/*
* Prepare our selection hardware before the busfree so we have a
* high probability of winning arbitration.
*/
0e1 00655058 call start_selection;
0e2 0065e041 jmp await_busfree;
complete:
/* If we are untagged, clear our address up in host ram */
0e3 20a09a6b test SCB_CONTROL, TAG_ENB jnz complete_queue;
0e4 ff37c808 mov A, SAVED_TCL;
0e5 026ac05c mvi UNTAGGEDSCB_OFFSET call post_byte_setup;
0e6 ff6ad65c mvi SCB_LIST_NULL call post_byte;
complete_queue:
0e7 00b99e5b mov SCB_TAG call complete_post;
0e8 0065e041 jmp await_busfree;
}
complete_post:
/* Post the SCBID in SINDEX and issue an interrupt */
0e9 00658c5d call add_scb_to_free_list;
0ea ff65a408 mov ARG_1, SINDEX;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
0eb fff8c808 mov A, SDSCB_QOFF;
} else {
mov A, QOUTPOS;
}
0ec 006ac05c mvi QOUTFIFO_OFFSET call post_byte_setup;
0ed 0052d65c mov ARG_1 call post_byte;
if ((ahc->features & AHC_QUEUE_REGS) == 0) {
inc QOUTPOS;
}
0ee 026a2205 mvi INTSTAT,CMDCMPLT ret;
if ((ahc->flags & AHC_INITIATORMODE) != 0) {
/*
* Is it a disconnect message? Set a flag in the SCB to remind us
* and await the bus going free.
*/
mesgin_disconnect:
0ef 04a04001 or SCB_CONTROL,DISCONNECTED;
0f0 0065a65d call add_scb_to_disc_list;
0f1 0065e041 jmp await_busfree;
/*
* Save data pointers message:
* Copying RAM values back to SCB, for Save Data Pointers message, but
* only if we've actually been into a data phase to change them. This
* protects against bogus data in scratch ram and the residual counts
* since they are only initialized when we go into data_in or data_out.
*/
mesgin_sdptrs:
0f2 2036727b test SEQ_FLAGS, DPHASE jz mesgin_done;
/*
* The SCB SGPTR becomes the next one we'll download,
* and the SCB DATAPTR becomes the current SHADDR.
* Use the residual number since STCNT is corrupted by
* any message transfer.
*/
if ((ahc->features & AHC_CMD_CHAN) != 0) {
0f3 05384631 bmov SCB_SGCOUNT, SG_COUNT, 5;
0f4 04145831 bmov SCB_DATAPTR, SHADDR, 4;
0f5 03a96031 bmov SCB_DATACNT, SCB_RESID_DCNT, 3;
} else {
mvi DINDEX, SCB_SGCOUNT;
mvi SG_COUNT call bcopy_5;
mvi DINDEX, SCB_DATAPTR;
mvi SHADDR call bcopy_4;
mvi SCB_RESID_DCNT call bcopy_3;
}
0f6 00657243 jmp mesgin_done;
/*
* Restore pointers message? Data pointers are recopied from the
* SCB anytime we enter a data phase for the first time, so all
* we need to do is clear the DPHASE flag and let the data phase
* code do the rest.
*/
mesgin_rdptrs:
0f7 df366c08 and SEQ_FLAGS, ~DPHASE; /*
* We'll reload them
* the next time through
* the dataphase.
*/
0f8 00657243 jmp mesgin_done;
/*
* Identify message? For a reconnecting target, this tells us the lun
* that the reconnection is for - find the correct SCB and switch to it,
* clearing the "disconnected" bit so we don't "find" it by accident later.
*/
mesgin_identify:
if ((ahc->features & AHC_WIDE) != 0) {
0f9 0f64c808 and A,0x0f; /* lun in lower four bits */
} else {
and A,0x07; /* lun in lower three bits */
}
0fa 00376e00 or SAVED_TCL,A; /* SAVED_TCL should be complete now */
0fb ff6aa600 mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */
0fc 0065905c call get_untagged_SCBID;
0fd ff52de73 cmp ARG_1, SCB_LIST_NULL je snoop_tag;
if ((ahc->flags & AHC_PAGESCBS) != 0) {
0fe 4036e87b test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB;
}
/*
* If the SCB was found in the disconnected list (as is
* always the case in non-paging scenarios), SCBPTR is already
* set to the correct SCB. So, simply setup the SCB and get
* on with things.
*/
0ff 00657e5c call rem_scb_from_disc_list;
100 0065ea43 jmp setup_SCB;
/*
* Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
* If we get one, we use the tag returned to find the proper
* SCB. With SCB paging, this requires using search for both tagged
* and non-tagged transactions since the SCB may exist in any slot.
* If we're not using SCB paging, we can use the tag as the direct
* index to the SCB.
*/
snoop_tag:
101 ff06d408 mov NONE,SCSIDATL; /* ACK Identify MSG */
snoop_tag_loop:
102 0065ec5c call phase_lock;
103 e03d0664 cmp LASTPHASE, P_MESGIN jne not_found;
104 20120664 cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
get_tag:
105 526a125c mvi ARG_1 call inb_next; /* tag value */
/*
* Ensure that the SCB the tag points to is for
* an SCB transaction to the reconnecting target.
*/
use_retrieveSCB:
106 0065785c call retrieveSCB;
setup_SCB:
107 ff37c808 mov A, SAVED_TCL;
108 00a1fe63 cmp SCB_TCL, A jne not_found_cleanup_scb;
109 04a0fe7b test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
10a fba04009 and SCB_CONTROL,~DISCONNECTED;
10b 80366c00 or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
10c 00657458 call set_transfer_settings;
/* See if the host wants to send a message upon reconnection */
10d 10a0727b test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
10e efa04009 and SCB_CONTROL, ~MK_MESSAGE;
10f ff6a0a5c mvi HOST_MSG call mk_mesg;
110 00657243 jmp mesgin_done;
not_found_cleanup_scb:
111 04a0047c test SCB_CONTROL, DISCONNECTED jz . + 3;
112 0065a65d call add_scb_to_disc_list;
113 00650644 jmp not_found;
114 00658c5d call add_scb_to_free_list;
not_found:
115 316a2201 mvi INTSTAT, NO_MATCH;
116 00657243 jmp mesgin_done;
/*
* [ ADD MORE MESSAGE HANDLING HERE ]
*/
/*
* Locking the driver out, build a one-byte message passed in SINDEX
* if there is no active message already. SINDEX is returned intact.
*/
mk_mesg:
117 103d0600 or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
118 ff65680c mov MSG_OUT,SINDEX ret;
/*
* Functions to read data in Automatic PIO mode.
*
* According to Adaptec's documentation, an ACK is not sent on input from
* the target until SCSIDATL is read from. So we wait until SCSIDATL is
* latched (the usual way), then read the data byte directly off the bus
* using SCSIBUSL. When we have pulled the ATN line, or we just want to
* acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
* spec guarantees that the target will hold the data byte on the bus until
* we send our ACK.
*
* The assumption here is that these are called in a particular sequence,
* and that REQ is already set when inb_first is called. inb_{first,next}
* use the same calling convention as inb.
*/
inb_next_wait_perr:
119 b16a2201 mvi INTSTAT, PERR_DETECTED;
11a 00651444 jmp inb_next_wait;
inb_next:
11b ff06d408 mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
inb_next_wait:
/*
* If there is a parity error, wait for the kernel to
* see the interrupt and prepare our message response
* before continuing.
*/
11c 010c147c test SSTAT1, REQINIT jz inb_next_wait;
11d 040c0e6c test SSTAT1, SCSIPERR jnz inb_next_wait_perr;
inb_next_check_phase:
11e e0037a08 and LASTPHASE, PHASE_MASK, SCSISIGI;
11f e03d4264 cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;
inb_first:
120 ff65cc08 mov DINDEX,SINDEX;
121 ff12da0c mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/
inb_last:
122 ff06d40c mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
}
if ((ahc->flags & AHC_TARGETMODE) != 0) {
/*
* Change to a new phase. If we are changing the state of the I/O signal,
* from out to in, wait an additional data release delay before continuing.
*/
change_phase:
/* Wait for preceeding I/O session to complete. */
test SCSISIGI, ACKI jnz .;
/* Change the phase */
and DINDEX, IOI, SCSISIGI;
mov SCSISIGO, SINDEX;
and A, IOI, SINDEX;
/*
* If the data direction has changed, from
* out (initiator driving) to in (target driving),
* we must waitat least a data release delay plus
* the normal bus settle delay. [SCSI III SPI 10.11.0]
*/
cmp DINDEX, A je change_phase_wait;
test SINDEX, IOI jz change_phase_wait;
call change_phase_wait;
change_phase_wait:
nop;
nop;
nop;
nop ret;
/*
* Send a byte to an initiator in Automatic PIO mode.
*/
target_outb:
or SXFRCTL0, SPIOEN;
test SSTAT0, SPIORDY jz .;
mov SCSIDATL, SINDEX;
test SSTAT0, SPIORDY jz .;
and SXFRCTL0, ~SPIOEN ret;
}
mesgin_phasemis:
/*
* We expected to receive another byte, but the target changed phase
*/
123 e16a2201 mvi INTSTAT, MSGIN_PHASEMIS;
124 0065ce41 jmp ITloop;
/*
* DMA data transfer. HADDR and HCNT must be loaded first, and
* SINDEX should contain the value to load DFCNTRL with - 0x3d for
* host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
* during initialization.
*/
dma:
125 ff652609 mov DFCNTRL,SINDEX;
dma_loop:
126 010b526c test SSTAT0,DMADONE jnz dma_dmadone;
127 100c487c test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */
dma_phasemis:
/*
* We will be "done" DMAing when the transfer count goes to zero, or
* the target changes the phase (in light of this, it makes sense that
* the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
* doing a SCSI->Host transfer, the data FIFO should be flushed auto-
* magically on STCNT=0 or a phase change, so just wait for FIFO empty
* status.
*/
dma_checkfifo:
128 0493506c test DFCNTRL,DIRECTION jnz dma_fifoempty;
dma_fifoflush:
129 01944e7c test DFSTATUS,FIFOEMP jz dma_fifoflush;
dma_fifoempty:
/* Don't clobber an inprogress host data transfer */
12a 1094506c test DFSTATUS, MREQPEND jnz dma_fifoempty;
/*
* Now shut the DMA enables off and make sure that the DMA enables are
* actually off first lest we get an ILLSADDR.
*/
dma_dmadone:
12b c7932609 and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
dma_halt:
/*
* Some revisions of the aic7880 have a problem where, if the
* data fifo is full, but the PCI input latch is not empty,
* HDMAEN cannot be cleared. The fix used here is to attempt
* to drain the data fifo until there is space for the input
* latch to drain and HDMAEN de-asserts.
*/
if ((ahc->features & AHC_ULTRA2) == 0) {
mov NONE, DFDAT;
}
12c 3893546c test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
return:
12d ff6ad40c ret;
/*
* Assert that if we've been reselected, then we've seen an IDENTIFY
* message.
*/
assert:
12e 8036586c test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
12f 216a2205 mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
/*
* Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
* or by the SCBID ARG_1. The search begins at the SCB index passed in
* via SINDEX which is an SCB that must be on the disconnected list. If
* the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
* is set to the proper SCB.
*/
findSCB:
130 ff652009 mov SCBPTR,SINDEX; /* Initialize SCBPTR */
131 ff526664 cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID;
132 ff37c808 mov A, SAVED_TCL;
133 a16a7244 mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */
findSCB_by_SCBID:
134 ff52c808 mov A, ARG_1; /* Tag passed in ARG_1 */
135 b96a7244 mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */
findSCB_next:
136 ff90a608 mov ARG_2, SCBPTR;
137 ffba7674 cmp SCB_NEXT, SCB_LIST_NULL je notFound;
138 ffba2009 mov SCBPTR,SCB_NEXT;
139 ff65ca18 dec SINDEX; /* Last comparison moved us too far */
findSCB_loop:
13a 006c6a64 cmp SINDIR, A jne findSCB_next;
13b ff90ca0c mov SINDEX, SCBPTR ret;
notFound:
13c ff6aca04 mvi SINDEX, SCB_LIST_NULL ret;
/*
* Retrieve an SCB by SCBID first searching the disconnected list falling
* back to DMA'ing the SCB down from the host. This routine assumes that
* ARG_1 is the SCBID of interrest and that SINDEX is the position in the
* disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
* we go directly to the host for the SCB.
*/
retrieveSCB:
13d 40368a7c test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host;
13e 00905e5c mov SCBPTR call findSCB; /* Continue the search */
13f ff658a74 cmp SINDEX, SCB_LIST_NULL je retrieve_from_host;
/*
* This routine expects SINDEX to contain the index of the SCB to be
* removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
* SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
* if it is at the head.
*/
rem_scb_from_disc_list:
/* Remove this SCB from the disconnection list */
140 ff538874 cmp ARG_2, SCB_LIST_NULL je rHead;
141 ffbacc08 mov DINDEX, SCB_NEXT;
142 ff532009 mov SCBPTR, ARG_2;
143 ff667409 mov SCB_NEXT, DINDEX;
144 ff65200d mov SCBPTR, SINDEX ret;
rHead:
145 ffba7e0c mov DISCONNECTED_SCBH,SCB_NEXT ret;
retrieve_from_host:
/*
* We didn't find it. Pull an SCB and DMA down the one we want.
* We should never get here in the non-paging case.
*/
146 006a945d mov ALLZEROS call get_free_or_disc_scb;
147 0d6a6a00 mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
/* Jump instead of call as we want to return anyway */
148 00521e45 mov ARG_1 jmp dma_scb;
/*
* Determine whether a target is using tagged or non-tagged transactions
* by first looking for a matching transaction based on the TCL and if
* that fails, looking up this device in the host's untagged SCB array.
* The TCL to search for is assumed to be in SAVED_TCL. The value is
* returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
* The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
* in an SCB instead of having to go to the host.
*/
get_untagged_SCBID:
149 ff3fe474 cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
14a ff6aa400 mvi ARG_1, SCB_LIST_NULL;
14b 003f5e5c mov DISCONNECTED_SCBH call findSCB;
14c ff65e474 cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host;
14d 40366c00 or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
14e 20a09e6c test SCB_CONTROL, TAG_ENB jnz . + 2;
14f ffb9a40c mov ARG_1, SCB_TAG ret;
150 ff6aa404 mvi ARG_1, SCB_LIST_NULL ret;
/*
* Fetch a byte from host memory given an index of (A + (256 * SINDEX))
* and a base address of SCBID_ADDR. The byte is returned in RETURN_2.
*/
fetch_byte:
151 ff65a608 mov ARG_2, SINDEX;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
152 e06acc00 mvi DINDEX, CCHADDR;
153 456a125d mvi SCBID_ADDR call set_1byte_addr;
154 016ad001 mvi CCHCNT, 1;
155 096ad601 mvi CCSGCTL, CCSGEN|CCSGRESET;
156 80ebaa7c test CCSGCTL, CCSGDONE jz .;
157 016ad601 mvi CCSGCTL, CCSGRESET;
158 01e9a634 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:
159 ff65a608 mov ARG_2, SINDEX;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
15a e06acc00 mvi DINDEX, CCHADDR;
15b 456a125d mvi SCBID_ADDR call set_1byte_addr;
15c 016ad001 mvi CCHCNT, 1;
15d 016adc05 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 ((ahc->features & AHC_CMD_CHAN) != 0) {
15e 0165d831 bmov CCSCBRAM, SINDEX, 1;
15f 09eedc01 or CCSCBCTL, CCSCBEN|CCSCBRESET;
160 80eeda7c test CCSCBCTL, CCSCBDONE jz .;
161 ff6adc0d clr CCSCBCTL ret;
} else {
mov DFDAT, SINDEX;
or DFCNTRL, HDMAEN|FIFOFLUSH;
jmp dma_finish;
}
get_SCBID_from_host:
162 ff37c808 mov A, SAVED_TCL;
163 026aa05c mvi UNTAGGEDSCB_OFFSET call fetch_byte;
164 ff53a40c mov RETURN_1, RETURN_2 ret;
phase_lock_perr:
165 b16a2201 mvi INTSTAT, PERR_DETECTED;
phase_lock:
/*
* If there is a parity error, wait for the kernel to
* see the interrupt and prepare our message response
* before continuing.
*/
166 010cec7c test SSTAT1, REQINIT jz phase_lock;
167 040cea6c test SSTAT1, SCSIPERR jnz phase_lock_perr;
phase_lock_latch_phase:
168 e0030608 and SCSISIGO, PHASE_MASK, SCSISIGI;
169 e0037a0c and LASTPHASE, PHASE_MASK, SCSISIGI ret;
if ((ahc->features & AHC_CMD_CHAN) == 0) {
set_stcnt_from_hcnt:
mov STCNT[0], HCNT[0];
mov STCNT[1], HCNT[1];
mov STCNT[2], HCNT[2] ret;
bcopy_7:
mov DINDIR, SINDIR;
mov DINDIR, SINDIR;
bcopy_5:
mov DINDIR, SINDIR;
bcopy_4:
mov DINDIR, SINDIR;
bcopy_3:
mov DINDIR, SINDIR;
mov DINDIR, SINDIR;
mov DINDIR, SINDIR ret;
}
if ((ahc->flags & AHC_TARGETMODE) != 0) {
/*
* Setup addr assuming that A is an index into
* an array of 32byte objects, SINDEX contains
* the base address of that array, and DINDEX
* contains the base address of the location
* to store the indexed address.
*/
set_32byte_addr:
shr ARG_2, 3, A;
shl A, 5;
jmp set_1byte_addr;
}
/*
* Setup addr assuming that A is an index into
* an array of 64byte objects, SINDEX contains
* the base address of that array, and DINDEX
* contains the base address of the location
* to store the indexed address.
*/
set_64byte_addr:
16a 2e64a628 shr ARG_2, 2, A;
16b 6664c828 shl A, 6;
/*
* 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:
16c 006cda18 add DINDIR, A, SINDIR;
16d ff53c808 mov A, ARG_2;
16e 006cda20 adc DINDIR, A, SINDIR;
16f ff6ac808 clr A;
170 006cda20 adc DINDIR, A, SINDIR;
171 006cda24 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:
172 ff65c808 mov A, SINDEX;
if ((ahc->features & AHC_CMD_CHAN) != 0) {
173 e06acc00 mvi DINDEX, CCHADDR;
174 416a0e5d mvi HSCB_ADDR call set_64byte_addr;
175 ff90e209 mov CCSCBPTR, SCBPTR;
176 0435307d test DMAPARAMS, DIRECTION jz dma_scb_tohost;
177 306ad001 mvi CCHCNT, SCB_64BYTE_SIZE;
178 1d6adc01 mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
179 dcee2c65 cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
17a 00653e45 jmp dma_scb_finish;
dma_scb_tohost:
17b 1c6ad001 mvi CCHCNT, SCB_32BYTE_SIZE;
if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
mvi CCSCBCTL, CCSCBRESET;
bmov CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
or CCSCBCTL, CCSCBEN|CCSCBRESET;
test CCSCBCTL, CCSCBDONE jz .;
} else {
17c 196adc01 mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
17d d8ee3c65 cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
}
dma_scb_finish:
17e ff6adc09 clr CCSCBCTL;
17f 18ee406d test CCSCBCTL, CCARREN|CCSCBEN jnz .;
180 ff6ad40c ret;
} else {
mvi DINDEX, HADDR;
mvi HSCB_ADDR call set_64byte_addr;
mvi HCNT[0], SCB_32BYTE_SIZE;
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, SCB_32BYTE_SIZE, 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;
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;
dfdat_in_7:
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;
}
/*
* Wait for DMA from host memory to data FIFO to complete, then disable
* DMA and wait for it to acknowledge that it's off.
*/
dma_finish:
181 0894847d test DFSTATUS,HDONE jz dma_finish;
/* Turn off DMA */
182 f7932609 and DFCNTRL, ~HDMAEN;
183 0893886d test DFCNTRL, HDMAEN jnz .;
184 ff6ad40c ret;
add_scb_to_free_list:
if ((ahc->flags & AHC_PAGESCBS) != 0) {
185 ff407409 mov SCB_NEXT, FREE_SCBH;
186 ff6a7201 mvi SCB_TAG, SCB_LIST_NULL;
187 ff90800c mov FREE_SCBH, SCBPTR ret;
} else {
mvi SCB_TAG, SCB_LIST_NULL ret;
}
if ((ahc->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
188 ff40a265 cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
189 ff3f9a65 cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
18a ff6aca04 mvi SINDEX, SCB_LIST_NULL ret;
dequeue_disc_scb:
18b ff3f2009 mov SCBPTR, DISCONNECTED_SCBH;
dma_up_scb:
18c 016a6a00 mvi DMAPARAMS, FIFORESET;
18d 00b91e5d mov SCB_TAG call dma_scb;
unlink_disc_scb:
18e ffba7e0c mov DISCONNECTED_SCBH, SCB_NEXT ret;
dequeue_free_scb:
18f ff402009 mov SCBPTR, FREE_SCBH;
190 ffba800c mov FREE_SCBH, SCB_NEXT ret;
}
add_scb_to_disc_list:
/*
* Link this SCB into the DISCONNECTED list. This list holds the
* candidates for paging out an SCB if one is needed for a new command.
* Modifying the disconnected list is a critical(pause dissabled) section.
*/
191 ff3f7409 mov SCB_NEXT, DISCONNECTED_SCBH;
192 ff907e0c mov DISCONNECTED_SCBH, SCBPTR ret;
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200006140100.SAA13387>
