patch-1.3.4 linux/drivers/scsi/aic7xxx.seq
Next file: linux/drivers/scsi/fdomain.c
Previous file: linux/drivers/scsi/aic7xxx.h
Back to the patch index
Back to the overall index
- Lines: 1030
- Date:
Mon Jun 26 12:42:52 1995
- Orig file:
v1.3.3/linux/drivers/scsi/aic7xxx.seq
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.3/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
@@ -0,0 +1,1029 @@
+# @(#)aic7xxx.seq 1.32 94/11/29 jda
+#
+# Adaptec 274x/284x/294x 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.32
+
+SCBMASK = 0x1f
+
+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
+
+# 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 = 12 # 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
+
+# Poll QINCNT for work - the lower bits contain
+# the number of entries in the Queue In FIFO.
+#
+start:
+ test SCSISIGI,0x4 jnz reselect # BSYI
+ test QINCNT,SCBMASK 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
+ 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 negotiation 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.
+#
+ 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, the data FIFO should be flushed auto-
+# magically on STCNT=0 or a phase change, so just wait for FIFO empty
+# status.
+#
+dma3:
+ test SINDEX,0x4 jnz dma5 # DIRECTION
+dma4:
+ test DFSTATUS,0x1 jz dma4 # !FIFOEMP
+
+# 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.
+#
+# DFON is a 7870 bit enabling digital filtering of REQ and ACK signals.
+#
+ mvi SXFRCTL0,0x8a # DFON|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.
+#
+# STPWEN is 7870-specific, enabling an external termination power source.
+#
+ and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10]
+ or SXFRCTL1,0x5,A # ENSTIMER|STPWEN
+ 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 (disconnection 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.
+#
+findSCB:
+ and A,0x7,SINDEX # lun in lower three bits
+ or A,A,SELID # can I do this?
+ and A,0xf7 # only channel A implemented
+ mov DINDEX,A # save in DINDEX for later
+
+ clr SINDEX
+
+findSCB1:
+ mov A,DINDEX # reload A after 1st iteration
+ mov SCBPTR,SINDEX # switch to new SCB
+ 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 */
+# }
+#
+ mov SCBARRAY+19,DFDAT # new data address
+ mov SCBARRAY+20,DFDAT
+ mov SCBARRAY+21,DFDAT
+ mov SCBARRAY+22,DFDAT
+
+ mov NONE,DFDAT # throw away four bytes
+ mov NONE,DFDAT
+ mov NONE,DFDAT
+ mov NONE,DFDAT
+
+ mov SCBARRAY+23,DFDAT
+ mov SCBARRAY+24,DFDAT
+ clr SCBARRAY+25
+
+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
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this