diff options
Diffstat (limited to 'sys/gnu')
-rw-r--r-- | sys/gnu/misc/aha274x.seq | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/sys/gnu/misc/aha274x.seq b/sys/gnu/misc/aha274x.seq new file mode 100644 index 0000000..9b83b84 --- /dev/null +++ b/sys/gnu/misc/aha274x.seq @@ -0,0 +1,1064 @@ +# @(#)aic7xxx.seq 1.30 94/11/09 jda +# +# Adaptec 274x device driver for Linux. +# Copyright (c) 1994 The University of Calgary Department of Computer Science. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +VERSION AIC7XXX_SEQ_VERSION 1.30 + +MAXSCB-1 = 0xf + +SCSISEQ = 0x00 +SXFRCTL0 = 0x01 +SXFRCTL1 = 0x02 +SCSISIGI = 0x03 +SCSISIGO = 0x03 +SCSIRATE = 0x04 +SCSIID = 0x05 +SCSIDATL = 0x06 +STCNT = 0x08 +STCNT+0 = 0x08 +STCNT+1 = 0x09 +STCNT+2 = 0x0a +SSTAT0 = 0x0b +CLRSINT1 = 0x0c +SSTAT1 = 0x0c +SIMODE1 = 0x11 +SCSIBUSL = 0x12 +SHADDR = 0x14 +SELID = 0x19 +SBLKCTL = 0x1f +SEQCTL = 0x60 +A = 0x64 # == ACCUM +SINDEX = 0x65 +DINDEX = 0x66 +ALLZEROS = 0x6a +NONE = 0x6a +SINDIR = 0x6c +DINDIR = 0x6d +FUNCTION1 = 0x6e +HADDR = 0x88 +HCNT = 0x8c +HCNT+0 = 0x8c +HCNT+1 = 0x8d +HCNT+2 = 0x8e +SCBPTR = 0x90 +INTSTAT = 0x91 +DFCNTRL = 0x93 +DFSTATUS = 0x94 +DFDAT = 0x99 +QINFIFO = 0x9b +QINCNT = 0x9c +QOUTFIFO = 0x9d + +SCSICONF = 0x5a + +# The two reserved bytes at SCBARRAY+1[23] are expected to be set to +# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag +# to indicate whether or not to reload scatter-gather parameters after +# a disconnect. +# +SCBARRAY+0 = 0xa0 +SCBARRAY+1 = 0xa1 +SCBARRAY+2 = 0xa2 +SCBARRAY+3 = 0xa3 +SCBARRAY+7 = 0xa7 +SCBARRAY+11 = 0xab +SCBARRAY+14 = 0xae +SCBARRAY+15 = 0xaf +SCBARRAY+16 = 0xb0 +SCBARRAY+17 = 0xb1 +SCBARRAY+18 = 0xb2 +SCBARRAY+19 = 0xb3 +SCBARRAY+20 = 0xb4 +SCBARRAY+21 = 0xb5 +SCBARRAY+22 = 0xb6 +SCBARRAY+23 = 0xb7 +SCBARRAY+24 = 0xb8 +SCBARRAY+25 = 0xb9 + +SIGNAL_0 = 0x01 # unknown scsi bus phase +SIGNAL_1 = 0x11 # message reject +SIGNAL_2 = 0x21 # no IDENTIFY after reconnect +SIGNAL_3 = 0x31 # no cmd match for reconnect +SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion +STATUS_ERROR = 0x51 + +# The host adapter card (at least the BIOS) uses 20-2f for SCSI +# device information, 32-33 and 5a-5f as well. Since we don't support +# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the +# BIOS trashes 20-27 anyway, writing the synchronous negotiation results +# on top of the BIOS values, so we re-use those for our per-target +# scratchspace (actually a value that can be copied directly into +# SCSIRATE). This implies, since we can't get the BIOS config values, +# that all targets will be negotiated with for synchronous transfer. +# NEEDSDTR has one bit per target indicating if an SDTR message is +# needed for that device - this will be set initially, as well as +# after a bus reset condition. +# +# The high bit of DROPATN is set if ATN should be dropped before the ACK +# when outb is called. REJBYTE contains the first byte of a MESSAGE IN +# message, so the driver can report an intelligible error if a message is +# rejected. +# +# RESELECT's high bit is true if we are currently handling a reselect; +# its next-highest bit is true ONLY IF we've seen an IDENTIFY message +# from the reselecting target. If we haven't had IDENTIFY, then we have +# no idea what the lun is, and we can't select the right SCB register +# bank, so force a kernel panic if the target attempts a data in/out or +# command phase instead of corrupting something. +# +# Note that SG_NEXT occupies four bytes. +# +SYNCNEG = 0x20 +DISC_DSB_A = 0x32 + +DROPATN = 0x30 +REJBYTE = 0x31 +RESELECT = 0x34 + +MSG_FLAGS = 0x35 +MSG_LEN = 0x36 +MSG_START+0 = 0x37 +MSG_START+1 = 0x38 +MSG_START+2 = 0x39 +MSG_START+3 = 0x3a +MSG_START+4 = 0x3b +MSG_START+5 = 0x3c +-MSG_START+0 = 0xc9 # 2's complement of MSG_START+0 + +ARG_1 = 0x4c # sdtr conversion args & return +ARG_2 = 0x4d +RETURN_1 = 0x4c + +SIGSTATE = 0x4e # value written to SCSISIGO +NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt + +SG_SIZEOF = 0x8 # sizeof(struct scatterlist) +SG_NOLOAD = 0x50 # load SG pointer/length? +SG_COUNT = 0x51 # working value of SG count +SG_NEXT = 0x52 # working value of SG pointer +SG_NEXT+0 = 0x52 +SG_NEXT+1 = 0x53 +SG_NEXT+2 = 0x54 +SG_NEXT+3 = 0x55 + +SCBCOUNT = 0x56 # the actual number of SCBs +ACTIVE_A = 0x57 + +# Poll QINCNT for work - the lower three bits contain +# the number of entries in the Queue In FIFO. +# +start: + test SCSISIGI,0x4 jnz reselect # BSYI + test QINCNT,MAXSCB-1 jz start + +# We have at least one queued SCB now. Set the SCB pointer +# from the FIFO so we see the right bank of SCB registers, +# then set SCSI options and set the initiator and target +# SCSI IDs. +# + mov SCBPTR,QINFIFO + +# See if there is not already an active SCB for this target. This code +# will have to be modified when we add support for dual and wide busses. + + and FUNCTION1,0x70,SCBARRAY+1 + mov A,FUNCTION1 + test ACTIVE_A,A jz active +# Place the currently active back on the queue for later processing + mov QINFIFO, SCBPTR + jmp start + +# Mark the current target as busy and get working on the SCB +active: + or ACTIVE_A,A + mov SCBARRAY+1 call initialize + clr SG_NOLOAD + clr RESELECT + +# As soon as we get a successful selection, the target should go +# into the message out phase since we have ATN asserted. Prepare +# the message to send, locking out the device driver. If the device +# driver hasn't beaten us with an ABORT or RESET message, then tack +# on a SDTR negotation if required. +# +# Messages are stored in scratch RAM starting with a flag byte (high bit +# set means active message), one length byte, and then the message itself. +# + mov SCBARRAY+1 call disconnect # disconnect ok? + + and SINDEX,0x7,SCBARRAY+1 # lun + or SINDEX,A # return value from disconnect + or SINDEX,0x80 call mk_mesg # IDENTIFY message + + mov A,SINDEX + cmp MSG_START+0,A jne !message # did driver beat us? + mvi MSG_START+1 call mk_sdtr # build SDTR message if needed + +!message: + +# Enable selection phase as an initiator, and do automatic ATN +# after the selection. +# + mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO + +# Wait for successful arbitration. The AIC-7770 documentation says +# that SELINGO indicates successful arbitration, and that it should +# be used to look for SELDO. However, if the sequencer is paused at +# just the right time - a parallel fsck(8) on two drives did it for +# me - then SELINGO can flip back to false before we've seen it. This +# makes the sequencer sit in the arbitration loop forever. This is +# Not Good. +# +# Therefore, I've added a check in the arbitration loop for SELDO +# too. This could arguably be made a critical section by disabling +# pauses, but I don't want to make a potentially infinite loop a CS. +# I suppose you could fold it into the select loop, too, but since +# I've been hunting this bug for four days it's kinda like a trophy. +# +arbitrate: + test SSTAT0,0x40 jnz *select # SELDO + test SSTAT0,0x10 jz arbitrate # SELINGO + +# Wait for a successful selection. If the hardware selection +# timer goes off, then the driver gets the interrupt, so we don't +# need to worry about it. +# +select: + test SSTAT0,0x40 jz select # SELDO + jmp *select + +# Reselection is being initiated by a target - we've seen the BSY +# line driven active, and we didn't do it! Enable the reselection +# hardware, and wait for it to finish. Make a note that we've been +# reselected, but haven't seen an IDENTIFY message from the target +# yet. +# +reselect: + mvi SCSISEQ,0x10 # ENRSELI + +reselect1: + test SSTAT0,0x20 jz reselect1 # SELDI + mov SELID call initialize + + mvi RESELECT,0x80 # reselected, no IDENTIFY + +# After the [re]selection, make sure that the [re]selection enable +# bit is off. This chip is flaky enough without extra things +# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be +# using it shortly. +# +*select: + clr SCSISEQ + mvi CLRSINT1,0x8 # CLRBUSFREE + +# Main loop for information transfer phases. If BSY is false, then +# we have a bus free condition, expected or not. Otherwise, wait +# for the target to assert REQ before checking MSG, C/D and I/O +# for the bus phase. +# +# We can't simply look at the values of SCSISIGI here (if we want +# to do synchronous data transfer), because the target won't assert +# REQ if it's already sent us some data that we haven't acknowledged +# yet. +# +ITloop: + test SSTAT1,0x8 jnz p_busfree # BUSFREE + test SSTAT1,0x1 jz ITloop # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + + cmp ALLZEROS,A je p_dataout + cmp A,0x40 je p_datain + cmp A,0x80 je p_command + cmp A,0xc0 je p_status + cmp A,0xa0 je p_mesgout + cmp A,0xe0 je p_mesgin + + mvi INTSTAT,SIGNAL_0 # unknown - signal driver + +p_dataout: + mvi 0 call scsisig # !CDO|!IOO|!MSGO + call assert + call sg_load + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+23 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + +# After a DMA finishes, save the final transfer pointer and count +# back into the SCB, in case a device disconnects in the middle of +# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since +# it's a reflection of how many bytes were transferred on the SCSI +# (as opposed to the host) bus. +# + mvi A,3 + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy + + mvi A,4 + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +p_datain: + mvi 0x40 call scsisig # !CDO|IOO|!MSGO + call assert + call sg_load + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+23 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy + + mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| + # !DIRECTION|FIFORESET + mvi A,3 + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy + + mvi A,4 + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +# Command phase. Set up the DMA registers and let 'er rip - the +# two bytes after the SCB SCSI_cmd_length are zeroed by the driver, +# so we can copy those three bytes directly into HCNT. +# +p_command: + mvi 0x80 call scsisig # CDO|!IOO|!MSGO + call assert + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+11 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+11 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+7 call bcopy + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + jmp ITloop + +# Status phase. Wait for the data byte to appear, then read it +# and store it into the SCB. +# +p_status: + mvi 0xc0 call scsisig # CDO|IOO|!MSGO + + mvi SCBARRAY+14 call inb + jmp ITloop + +# Message out phase. If there is no active message, but the target +# took us into this phase anyway, build a no-op message and send it. +# +p_mesgout: + mvi 0xa0 call scsisig # CDO|!IOO|MSGO + mvi 0x8 call mk_mesg # build NOP message + +# Set up automatic PIO transfer from MSG_START. Bit 3 in +# SXFRCTL0 (SPIOEN) is already on. +# + mvi SINDEX,MSG_START+0 + mov DINDEX,MSG_LEN + clr A + +# When target asks for a byte, drop ATN if it's the last one in +# the message. Otherwise, keep going until the message is exhausted. +# (We can't use outb for this since it wants the input in SINDEX.) +# +# Keep an eye out for a phase change, in case the target issues +# a MESSAGE REJECT. +# +p_mesgout2: + test SSTAT0,0x2 jz p_mesgout2 # SPIORDY + test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS + + cmp DINDEX,1 jne p_mesgout3 # last byte? + mvi CLRSINT1,0x40 # CLRATNO - drop ATN + +# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically +# send ACKs in automatic PIO or DMA mode unless you make sure that the +# "expected" bus phase in SCSISIGO matches the actual bus phase. This +# behaviour is completely undocumented and caused me several days of +# grief. +# +# After plugging in different drives to test with and using a longer +# SCSI cable, I found that I/O in Automatic PIO mode ceased to function, +# especially when transferring >1 byte. It seems to be much more stable +# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is +# polled for transfer completion - for both output _and_ input. The +# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL +# is accessed (like the documentation says it does), and that on a longer +# cable run, the sequencer code was fast enough to loop back and see +# an SPIORDY that hadn't dropped yet. +# +p_mesgout3: + call one_stcnt + mov SCSIDATL,SINDIR + +p_mesgout4: + test SSTAT0,0x4 jz p_mesgout4 # SDONE + dec DINDEX + inc A + cmp MSG_LEN,A jne p_mesgout2 + +# If the next bus phase after ATN drops is a message out, it means +# that the target is requesting that the last message(s) be resent. +# +p_mesgout5: + test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE + test SSTAT1,0x1 jz p_mesgout5 # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + cmp A,0xa0 jne p_mesgout6 + mvi 0x10 call scsisig # ATNO - re-assert ATN + + jmp ITloop + +p_mesgout6: + mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS + clr MSG_FLAGS # no active msg + jmp ITloop + +# Message in phase. Bytes are read using Automatic PIO mode, but not +# using inb. This alleviates a race condition, namely that if ATN had +# to be asserted under Automatic PIO mode, it had to beat the SCSI +# circuitry sending an ACK to the target. This showed up under heavy +# loads and really confused things, since ABORT commands wouldn't be +# seen by the drive after an IDENTIFY message in until it had changed +# to a data I/O phase. +# +p_mesgin: + mvi 0xe0 call scsisig # CDO|IOO|MSGO + mvi A call inb_first # read the 1st message byte + mvi REJBYTE,A # save it for the driver + + cmp ALLZEROS,A jne p_mesgin1 + +# We got a "command complete" message, so put the SCB pointer +# into the Queue Out, and trigger a completion interrupt. +# Check status for non zero return and interrupt driver if needed +# This allows the driver to do a sense command to find out the +# source of error. We don't bother to post to the QOUTFIFO in +# the error case 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, mark this target as free. + and FUNCTION1,0x70,SCBARRAY+1 + mov A,FUNCTION1 + xor ACTIVE_A,A + + test SCBARRAY+14,0xff jz status_ok # 0 Status? + call inb_last # ack & turn auto PIO back on + mvi INTSTAT,STATUS_ERROR # let driver know + jmp ITloop +status_ok: + mov QOUTFIFO,SCBPTR + mvi INTSTAT,0x2 # CMDCMPLT + jmp p_mesgin_done + +# Is it an extended message? We only support the synchronous data +# transfer request message, which will probably be in response to +# an SDTR message out from us. If it's not an SDTR, reject it - +# apparently this can be done after any message in byte, according +# to the SCSI-2 spec. +# +# XXX - we should really reject this if we didn't initiate the SDTR +# negotiation; this may cause problems with unusual devices. +# +p_mesgin1: + cmp A,1 jne p_mesgin2 # extended message code? + + mvi A call inb_next + cmp A,3 jne p_mesginN # extended mesg length = 3 + mvi A call inb_next + cmp A,1 jne p_mesginN # SDTR code + + mvi ARG_1 call inb_next # xfer period + mvi ARG_2 call inb_next # REQ/ACK offset + mvi INTSTAT,SIGNAL_4 # call driver to convert + + call ndx_sdtr # index sync config for target + mov DINDEX,SINDEX + mov DINDIR,RETURN_1 # save returned value + + not A # turn off "need sdtr" flag + and NEEDSDTR,A + +# Even though the SCSI-2 specification says that a device responding +# to our SDTR message should honor our parameters for transmitting +# to us, it doesn't seem to work too well in real life. In particular, +# a lot of CD-ROM and tape units don't function: try using the SDTR +# parameters the device sent us for both transmitting and receiving. +# + mov SCSIRATE,RETURN_1 + jmp p_mesgin_done + +# Is it a disconnect message? Set a flag in the SCB to remind us +# and await the bus going free. +# +p_mesgin2: + cmp A,4 jne p_mesgin3 # disconnect code? + + or SCBARRAY+0,0x4 # set "disconnected" bit + jmp p_mesgin_done + +# Save data pointers message? Copy working values into the SCB, +# usually in preparation for a disconnect. +# +p_mesgin3: + cmp A,2 jne p_mesgin4 # save data pointers code? + + call sg_ram2scb + jmp p_mesgin_done + +# Restore pointers message? Data pointers are recopied from the +# SCB anyway at the start of any DMA operation, so the only thing +# to copy is the scatter-gather values. +# +p_mesgin4: + cmp A,3 jne p_mesgin5 # restore pointers code? + + call sg_scb2ram + jmp p_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. +# +p_mesgin5: + test A,0x80 jz p_mesgin6 # identify message? + + test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved + + mov A call findSCB # switch to correct SCB + +# If a active message is present after calling findSCB, then either it +# or the driver is trying to abort the command. Either way, something +# untoward has happened and we should just leave it alone. +# + test MSG_FLAGS,0x80 jnz p_mesgin_done + + xor SCBARRAY+0,0x4 # clear disconnect bit in SCB + mvi RESELECT,0xc0 # make note of IDENTIFY + + call sg_scb2ram # implied restore pointers + # required on reselect + jmp p_mesgin_done + +# Message reject? If we have an outstanding SDTR negotiation, assume +# that it's a response from the target selecting asynchronous transfer, +# otherwise just ignore it since we have no clue what it pertains to. +# +# XXX - I don't have a device that responds this way. Does this code +# actually work? +# +p_mesgin6: + cmp A,7 jne p_mesgin7 # message reject code? + + and FUNCTION1,0x70,SCSIID # outstanding SDTR message? + mov A,FUNCTION1 + test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection + + call ndx_sdtr # note use of asynch xfer + mov DINDEX,SINDEX + clr DINDIR + + not A # turn off "active sdtr" flag + and NEEDSDTR,A + + clr SCSIRATE # select asynch xfer + jmp p_mesgin_done + +# [ ADD MORE MESSAGE HANDLING HERE ] +# +p_mesgin7: + +# We have no idea what this message in is, and there's no way +# to pass it up to the kernel, so we issue a message reject and +# hope for the best. Since we're now using manual PIO mode to +# read in the message, there should no longer be a race condition +# present when we assert ATN. In any case, rejection should be a +# rare occurrence - signal the driver when it happens. +# +p_mesginN: + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + mvi INTSTAT,SIGNAL_1 # let driver know + + mvi 0x7 call mk_mesg # MESSAGE REJECT message + +p_mesgin_done: + call inb_last # ack & turn auto PIO back on + jmp ITloop + +# Bus free phase. It might be useful to interrupt the device +# driver if we aren't expecting this. For now, make sure that +# ATN isn't being asserted and look for a new command. +# +p_busfree: + mvi CLRSINT1,0x40 # CLRATNO + clr SIGSTATE + jmp start + +# Bcopy: number of bytes to transfer should be in A, DINDEX should +# contain the destination address, and SINDEX should contain the +# source address. All input parameters are trashed on return. +# +bcopy: + mov DINDIR,SINDIR + dec A + cmp ALLZEROS,A jne bcopy + ret + +# 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: + mvi SEQCTL,0x40 # PAUSEDIS + test MSG_FLAGS,0x80 jnz mk_mesg1 # active message? + + mvi MSG_FLAGS,0x80 # if not, there is now + mvi MSG_LEN,1 # length = 1 + mov MSG_START+0,SINDEX # 1-byte message + +mk_mesg1: + clr SEQCTL # !PAUSEDIS + ret + +# Input byte in Automatic PIO mode. The address to store the byte +# in should be in SINDEX. DINDEX will be used by this routine. +# +inb: + test SSTAT0,0x2 jz inb # SPIORDY + mov DINDEX,SINDEX + call one_stcnt # xfer one byte + mov DINDIR,SCSIDATL +inb1: + test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish" + ret + +# Carefully read data in Automatic PIO mode. I first tried this using +# Manual PIO mode, but it gave me continual underrun errors, probably +# indicating that I did something wrong, but I feel more secure leaving +# Automatic PIO on all the time. +# +# 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_first: + mov DINDEX,SINDEX + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_next: + mov DINDEX,SINDEX # save SINDEX + + call one_stcnt # xfer one byte + mov NONE,SCSIDATL # dummy read from latch to ACK +inb_next1: + test SSTAT0,0x4 jz inb_next1 # SDONE +inb_next2: + test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_last: + call one_stcnt # ACK with dummy read + mov NONE,SCSIDATL +inb_last1: + test SSTAT0,0x4 jz inb_last1 # wait for completion + ret + +# Output byte in Automatic PIO mode. The byte to output should be +# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped +# before the byte is output. +# +outb: + test SSTAT0,0x2 jz outb # SPIORDY + call one_stcnt # xfer one byte + + test DROPATN,0x80 jz outb1 + mvi CLRSINT1,0x40 # CLRATNO + clr DROPATN +outb1: + mov SCSIDATL,SINDEX +outb2: + test SSTAT0,0x4 jz outb2 # SDONE + ret + +# Write the value "1" into the STCNT registers, for Automatic PIO +# transfers. +# +one_stcnt: + clr STCNT+2 + clr STCNT+1 + mvi STCNT+0,1 ret + +# 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: + mov DFCNTRL,SINDEX +dma1: +dma2: + test SSTAT0,0x1 jnz dma3 # DMADONE + test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun + +# 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, flush the data FIFO. +# +dma3: + test SINDEX,0x4 jnz dma5 # DIRECTION + and SINDEX,0xfe # mask out FIFORESET + or DFCNTRL,0x2,SINDEX # FIFOFLUSH +dma4: + test DFCNTRL,0x2 jnz dma4 # FIFOFLUSHACK + +# Now shut the DMA enables off, and copy STCNT (ie. the underrun +# amount, if any) to the SCB registers; SG_COUNT will get copied to +# the SCB's residual S/G count field after sg_advance is called. Make +# sure that the DMA enables are actually off first lest we get an ILLSADDR. +# +dma5: + clr DFCNTRL # disable DMA +dma6: + test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK + + mvi A,3 + mvi DINDEX,SCBARRAY+15 + mvi STCNT call bcopy + + ret + +# Common SCSI initialization for selection and reselection. Expects +# the target SCSI ID to be in the upper four bits of SINDEX, and A's +# contents are stomped on return. +# +initialize: + clr SBLKCTL # channel A, !wide + and SCSIID,0xf0,SINDEX # target ID + and A,0x7,SCSICONF # SCSI_ID_A[210] + or SCSIID,A + +# Esundry initialization. +# + clr DROPATN + clr SIGSTATE + +# Turn on Automatic PIO mode now, before we expect to see an REQ +# from the target. It shouldn't hurt anything to leave it on. Set +# CLRCHN here before the target has entered a data transfer mode - +# with synchronous SCSI, if you do it later, you blow away some +# data in the SCSI FIFO that the target has already sent to you. +# + mvi SXFRCTL0,0xa # SPIOEN|CLRCHN + +# Set SCSI bus parity checking and the selection timeout value, +# and enable the hardware selection timer. Set the SELTO interrupt +# to signal the driver. +# + and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10] + or SXFRCTL1,0x4,A # ENSTIMER + mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR + +# Initialize scatter-gather pointers by setting up the working copy +# in scratch RAM. +# + call sg_scb2ram + +# Initialize SCSIRATE with the appropriate value for this target. +# + call ndx_sdtr + mov SCSIRATE,SINDIR + ret + +# Assert that if we've been reselected, then we've seen an IDENTIFY +# message. +# +assert: + test RESELECT,0x80 jz assert1 # reselected? + test RESELECT,0x40 jnz assert1 # seen IDENTIFY? + + mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic + +assert1: + ret + +# Find out if disconnection is ok from the information the BIOS has left +# us. The target ID should be in the upper four bits of SINDEX; A will +# contain either 0x40 (disconnection ok) or 0x00 (diconnection not ok) +# on exit. +# +# This is the only place the target ID is limited to three bits, so we +# can use the FUNCTION1 register. +# +disconnect: + and FUNCTION1,0x70,SINDEX # strip off extra just in case + mov A,FUNCTION1 + test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled + + clr A ret +disconnect1: + mvi A,0x40 ret + +# Locate the SCB matching the target ID in SELID and the lun in the lower +# three bits of SINDEX, and switch the SCB to it. Have the kernel print +# a warning message if it can't be found, and generate an ABORT message +# to the target. We keep the value of the t/c/l that we are trying to +# in DINDEX so it is not overwritten during our check to see if we are +# at the last SCB. +# +findSCB: + and A,0x7,SINDEX # lun in lower three bits + or DINDEX,A,SELID # can I do this? + and DINDEX,0xf7 # only channel A implemented + + clr SINDEX + +findSCB1: + mov SCBPTR,SINDEX # switch to new SCB + mov A,DINDEX + cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match? + test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected + + ret + +findSCB2: + inc SINDEX + mov A,SCBCOUNT + cmp SINDEX,A jne findSCB1 + + mvi INTSTAT,SIGNAL_3 # not found - signal kernel + mvi 0x6 call mk_mesg # ABORT message + + or SINDEX,0x10,SIGSTATE # assert ATNO + call scsisig + ret + +# Make a working copy of the scatter-gather parameters in the SCB. +# +sg_scb2ram: + mov SG_COUNT,SCBARRAY+2 + + mvi A,4 + mvi DINDEX,SG_NEXT + mvi SCBARRAY+3 call bcopy + + mvi SG_NOLOAD,0x80 + test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g? + clr SG_NOLOAD + +sg_scb2ram1: + ret + +# Copying RAM values back to SCB, for Save Data Pointers message. +# +sg_ram2scb: + mov SCBARRAY+2,SG_COUNT + + mvi A,4 + mvi DINDEX,SCBARRAY+3 + mvi SG_NEXT call bcopy + + and SCBARRAY+0,0xef,SCBARRAY+0 + test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g? + or SCBARRAY+0,0x10 + +sg_ram2scb1: + ret + +# Load a struct scatter if needed 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 the above DMA, assumes a little-endian host data storage. +# +sg_load: + test SG_COUNT,0xff jz sg_load3 # SG being used? + test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g? + + clr HCNT+2 + clr HCNT+1 + mvi HCNT+0,SG_SIZEOF + + mvi A,4 + mvi DINDEX,HADDR + mvi SG_NEXT call bcopy + + mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET + +# Wait for DMA from host memory to data FIFO to complete, then disable +# DMA and wait for it to acknowledge that it's off. +# +sg_load1: + test DFSTATUS,0x8 jz sg_load1 # HDONE + + clr DFCNTRL # disable DMA +sg_load2: + test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK + +# Copy data from FIFO into SCB data pointer and data count. This assumes +# that the struct scatterlist has this structure (this and sizeof(struct +# scatterlist) == 12 are asserted in aic7xxx.c): +# +# struct scatterlist { +# char *address; /* four bytes, little-endian order */ +# ... /* four bytes, ignored */ +# unsigned short length; /* two bytes, little-endian order */ +# } +# + +# Not in FreeBSD. the scatter list is only 8 bytes. +# +# struct ahc_dma_seg { +# physaddr addr; /* four bytes, little-endian order */ +# long len; /* four bytes, little endian order */ +# }; +# + + mov SCBARRAY+19,DFDAT # new data address + mov SCBARRAY+20,DFDAT + mov SCBARRAY+21,DFDAT + mov SCBARRAY+22,DFDAT + + mov SCBARRAY+23,DFDAT + mov SCBARRAY+24,DFDAT + mov SCBARRAY+25,DFDAT + mov NONE,DFDAT #Only support 24 bit length. + +sg_load3: + ret + +# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, +# and the SCSI transfer count is zero (note that this should be called +# right after a DMA finishes), then move the working copies of the SG +# pointer/length along. If the SCSI transfer count is not zero, then +# presumably the target is disconnecting - do not reload the SG values +# next time. +# +sg_advance: + test SG_COUNT,0xff jz sg_advance2 # s/g enabled? + + test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? + test STCNT+1,0xff jnz sg_advance1 + test STCNT+2,0xff jnz sg_advance1 + + clr SG_NOLOAD # reload s/g next time + dec SG_COUNT # one less segment to go + + clr A # add sizeof(struct scatter) + add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 + adc SG_NEXT+1,A,SG_NEXT+1 + adc SG_NEXT+2,A,SG_NEXT+2 + adc SG_NEXT+3,A,SG_NEXT+3 + + ret + +sg_advance1: + mvi SG_NOLOAD,0x80 # don't reload s/g next time +sg_advance2: + ret + +# Add the array base SYNCNEG to the target offset (the target address +# is in SCSIID), and return the result in SINDEX. The accumulator +# contains the 3->8 decoding of the target ID on return. +# +ndx_sdtr: + shr A,SCSIID,4 + and A,0x7 + add SINDEX,SYNCNEG,A + + and FUNCTION1,0x70,SCSIID # 3-bit target address decode + mov A,FUNCTION1 ret + +# If we need to negotiate transfer parameters, build the SDTR message +# starting at the address passed in SINDEX. DINDEX is modified on return. +# +mk_sdtr: + mov DINDEX,SINDEX # save SINDEX + + call ndx_sdtr + test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation? + ret + +mk_sdtr1: + mvi DINDIR,1 # extended message + mvi DINDIR,3 # extended message length = 3 + mvi DINDIR,1 # SDTR code + mvi DINDIR,25 # REQ/ACK transfer period + mvi DINDIR,15 # REQ/ACK offset + + add MSG_LEN,-MSG_START+0,DINDEX # update message length + ret + +# Set SCSI bus control signal state. This also saves the last-written +# value into a location where the higher-level driver can read it - if +# it has to send an ABORT or RESET message, then it needs to know this +# so it can assert ATN without upsetting SCSISIGO. The new value is +# expected in SINDEX. Change the actual state last to avoid contention +# from the driver. +# +scsisig: + mov SIGSTATE,SINDEX + mov SCSISIGO,SINDEX ret + |