patch-1.3.5 linux/drivers/scsi/aic7xxx.seq
Next file: linux/drivers/scsi/aic7xxx_asm.c
Previous file: linux/drivers/scsi/aic7xxx.h
Back to the patch index
Back to the overall index
- Lines: 1487
- Date:
Thu Jun 29 10:37:10 1995
- Orig file:
v1.3.4/linux/drivers/scsi/aic7xxx.seq
- Orig date:
Tue Jun 27 14:11:38 1995
diff -u --recursive --new-file v1.3.4/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
@@ -1,27 +1,52 @@
-# @(#)aic7xxx.seq 1.32 94/11/29 jda
+##+M#########################################################################
+# Adaptec 274x/284x/294x device driver for Linux and FreeBSD.
#
-# Adaptec 274x/284x/294x device driver for Linux.
-# Copyright (c) 1994 The University of Calgary Department of Computer Science.
+# Copyright (c) 1994 John Aycock
+# The University of Calgary Department of Computer Science.
+# All rights reserved.
+#
+# Modifications/enhancements:
+# Copyright (c) 1994, 1995 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.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of Calgary
+# Department of Computer Science and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# 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.
#
-# 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.
+# FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other
+# optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
+#
+##-M#########################################################################
-VERSION AIC7XXX_SEQ_VERSION 1.32
+VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.19 1995/06/05 06:11:41 deang Exp $"
SCBMASK = 0x1f
SCSISEQ = 0x00
+ENRSELI = 0x10
SXFRCTL0 = 0x01
SXFRCTL1 = 0x02
SCSISIGI = 0x03
@@ -33,7 +58,10 @@
STCNT+0 = 0x08
STCNT+1 = 0x09
STCNT+2 = 0x0a
+CLRSINT0 = 0x0b
SSTAT0 = 0x0b
+SELDO = 0x40
+SELDI = 0x20
CLRSINT1 = 0x0c
SSTAT1 = 0x0c
SIMODE1 = 0x11
@@ -51,6 +79,9 @@
DINDIR = 0x6d
FUNCTION1 = 0x6e
HADDR = 0x88
+HADDR+1 = 0x89
+HADDR+2 = 0x8a
+HADDR+3 = 0x8b
HCNT = 0x8c
HCNT+0 = 0x8c
HCNT+1 = 0x8d
@@ -64,19 +95,37 @@
QINCNT = 0x9c
QOUTFIFO = 0x9d
-SCSICONF = 0x5a
+SCSICONF_A = 0x5a
+SCSICONF_B = 0x5b
# 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.
+# a disconnect. We also use bits 6 & 7 to indicate whether or not to
+# initiate SDTR or WDTR repectively when starting this command.
#
SCBARRAY+0 = 0xa0
+
+DISCONNECTED = 0x04
+NEEDDMA = 0x08
+SG_LOAD = 0x10
+TAG_ENB = 0x20
+NEEDSDTR = 0x40
+NEEDWDTR = 0x80
+
SCBARRAY+1 = 0xa1
SCBARRAY+2 = 0xa2
SCBARRAY+3 = 0xa3
+SCBARRAY+4 = 0xa4
+SCBARRAY+5 = 0xa5
+SCBARRAY+6 = 0xa6
SCBARRAY+7 = 0xa7
+SCBARRAY+8 = 0xa8
+SCBARRAY+9 = 0xa9
+SCBARRAY+10 = 0xaa
SCBARRAY+11 = 0xab
+SCBARRAY+12 = 0xac
+SCBARRAY+13 = 0xad
SCBARRAY+14 = 0xae
SCBARRAY+15 = 0xaf
SCBARRAY+16 = 0xb0
@@ -89,100 +138,265 @@
SCBARRAY+23 = 0xb7
SCBARRAY+24 = 0xb8
SCBARRAY+25 = 0xb9
+SCBARRAY+26 = 0xba
+SCBARRAY+27 = 0xbb
+SCBARRAY+28 = 0xbc
+SCBARRAY+29 = 0xbd
+SCBARRAY+30 = 0xbe
+
+BAD_PHASE = 0x01 # unknown scsi bus phase
+CMDCMPLT = 0x02 # Command Complete
+SEND_REJECT = 0x11 # sending a message reject
+NO_IDENT = 0x21 # no IDENTIFY after reconnect
+NO_MATCH = 0x31 # no cmd match for reconnect
+MSG_SDTR = 0x41 # SDTR message recieved
+MSG_WDTR = 0x51 # WDTR message recieved
+MSG_REJECT = 0x61 # Reject message recieved
+BAD_STATUS = 0x71 # Bad status from target
+RESIDUAL = 0x81 # Residual byte count != 0
+ABORT_TAG = 0x91 # Sent an ABORT_TAG message
+AWAITING_MSG = 0xa1 # Kernel requested to specify
+ # a message to this target
+ # (command was null), so tell
+ # it that it can fill the
+ # message buffer.
-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
+# device information, 32-33 and 5a-5f as well. As it turns out, the
+# BIOS trashes 20-2f, 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.
+# SCSIRATE). The kernel driver will enable synchronous negotiation
+# for all targets that have a value other than 0 in the lower four
+# bits of the target scratch space. This should work irregardless of
+# whether the bios has been installed. NEEDWDTR and NEEDSDTR are the top
+# two bits of the SCB control byte. The kernel driver will set these
+# when a WDTR or SDTR message should be sent to the target the SCB's
+# command references.
+#
+# 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;
+# FLAGS'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.
+# command phase instead of corrupting something. FLAGS also contains
+# configuration bits so that we can optimize for TWIN and WIDE controllers
+# as well as the MAX_OFFSET bit which we set when we want to negotiate for
+# maximum sync offset irregardless of what the per target scratch space says.
#
# Note that SG_NEXT occupies four bytes.
#
SYNCNEG = 0x20
-DISC_DSB_A = 0x32
-DROPATN = 0x30
REJBYTE = 0x31
-RESELECT = 0x34
+DISC_DSB_A = 0x32
+DISC_DSB_B = 0x33
-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
+MSG_LEN = 0x34
+MSG_START+0 = 0x35
+MSG_START+1 = 0x36
+MSG_START+2 = 0x37
+MSG_START+3 = 0x38
+MSG_START+4 = 0x39
+MSG_START+5 = 0x3a
+-MSG_START+0 = 0xcb # 2's complement of MSG_START+0
+
+ARG_1 = 0x4a # sdtr conversion args & return
+BUS_16_BIT = 0x01
+RETURN_1 = 0x4a
+
+SIGSTATE = 0x4b # value written to SCSISIGO
+
+# Linux users should use 0xc (12) for SG_SIZEOF
+#SG_SIZEOF = 0x8 # sizeof(struct ahc_dma)
+SG_SIZEOF = 0xc # sizeof(struct scatterlist)
+# if AIC7XXX_USE_SG
+SCB_SIZEOF = 0x13 # sizeof SCB to DMA (19 bytes)
+# else
+#SCB_SIZEOF = 0x1a # sizeof SCB without SG
+# endif
+
+SG_NOLOAD = 0x4c # load SG pointer/length?
+SG_COUNT = 0x4d # working value of SG count
+SG_NEXT = 0x4e # working value of SG pointer
+SG_NEXT+0 = 0x4e
+SG_NEXT+1 = 0x4f
+SG_NEXT+2 = 0x50
+SG_NEXT+3 = 0x51
+
+SCBCOUNT = 0x52 # the actual number of SCBs
+FLAGS = 0x53 # Device configuration flags
+TWIN_BUS = 0x01
+WIDE_BUS = 0x02
+MAX_OFFSET = 0x08
+ACTIVE_MSG = 0x20
+IDENTIFY_SEEN = 0x40
+RESELECTED = 0x80
+
+MAX_OFFSET_8BIT = 0x0f
+MAX_OFFSET_WIDE = 0x08
+
+ACTIVE_A = 0x54
+ACTIVE_B = 0x55
+SAVED_TCL = 0x56 # Temporary storage for the
+ # target/channel/lun of a
+ # reconnecting target
+
+# After starting the selection hardware, we return to the "poll_for_work"
+# loop so that we can 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 31 of the SCB as a psuedo-next pointer and to thread a list
+# of SCBs that are awaiting selection. Since 0 is a valid SCB offset,
+# SCB_LIST_NULL is 0x10 which is out of range. The kernel driver must
+# add an entry to this list everytime a request sense occurs. The sequencer
+# will automatically consume the entries.
+
+WAITING_SCBH = 0x57 # head of list of SCBs awaiting
+ # selection
+WAITING_SCBT = 0x58 # tail of list of SCBs awaiting
+ # selection
+SCB_LIST_NULL = 0x10
-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.
+ test WAITING_SCBH,SCB_LIST_NULL jz start_waiting
+poll_for_work:
+ test FLAGS,TWIN_BUS jz start2 # Are we a twin channel device?
+# For fairness, we check the other bus first, since we just finished a
+# transaction on the current channel.
+ xor SBLKCTL,0x08 # Toggle to the other bus
+ test SSTAT0,SELDI jnz reselect
+ test SSTAT0,SELDO jnz select
+ xor SBLKCTL,0x08 # Toggle to the original bus
+start2:
+ test SSTAT0,SELDI jnz reselect
+ test SSTAT0,SELDO jnz select
+ test WAITING_SCBH,SCB_LIST_NULL jz start_waiting
+ test QINCNT,SCBMASK jz poll_for_work
+
+# We have at least one queued SCB now and we don't have any
+# SCBs in the list of SCBs awaiting selection. 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
+
+# If the control byte of this SCB has the NEEDDMA flag set, we have
+# yet to DMA it from host memory
+
+test SCBARRAY+0,NEEDDMA jz test_busy
+ clr HCNT+2
+ clr HCNT+1
+ mvi HCNT+0,SCB_SIZEOF
+
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+26 call bcopy_4
+
+ 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.
+#
+ call dma_finish
+
+# Copy the SCB from the FIFO to the SCBARRAY
+
+ mvi DINDEX, SCBARRAY+0
+ call bcopy_3_dfdat
+ call bcopy_4_dfdat
+ call bcopy_4_dfdat
+ call bcopy_4_dfdat
+ call bcopy_4_dfdat
+# ifndef AIC7XXX_USE_SG
+# call bcopy_3_dfdat
+# call bcopy_4_dfdat
+# endif
+
+# See if there is not already an active SCB for this target. This code
+# locks out on a per target basis instead of target/lun. Although this
+# is not ideal for devices that have multiple luns active at the same
+# time, it is faster than looping through all SCB's looking for active
+# commands. It may be benificial to make findscb a more general procedure
+# to see if the added cost of the search is negligible. This code also
+# assumes that the kernel driver will clear the active flags on board
+# initialization, board reset, and a target's SELTO.
+
+test_busy:
+ test SCBARRAY+0,0x20 jnz start_scb
+ and FUNCTION1,0x70,SCBARRAY+1
+ mov A,FUNCTION1
+ test SCBARRAY+1,0x88 jz test_a # Id < 8 && A channel
+
+ test ACTIVE_B,A jnz requeue
+ or ACTIVE_B,A # Mark the current target as busy
+ jmp start_scb
+
+# Place the currently active back on the queue for later processing
+requeue:
+ mov QINFIFO, SCBPTR
+ jmp poll_for_work
+
+# Pull the first entry off of the waiting for selection list
+start_waiting:
+ mov SCBPTR,WAITING_SCBH
+ jmp start_scb
+
+test_a:
+ test ACTIVE_A,A jnz requeue
+ or ACTIVE_A,A # Mark the current target as busy
+
+start_scb:
+ and SINDEX,0xf7,SBLKCTL #Clear the channel select bit
+ and A,0x08,SCBARRAY+1 #Get new channel bit
+ or SINDEX,A
+ mov SBLKCTL,SINDEX # select channel
+ mov SCBARRAY+1 call initialize_scsiid
+
+# Enable selection phase as an initiator, and do automatic ATN
+# after the selection. We do this now so that we can overlap the
+# rest of our work to set up this target with the arbitration and
+# selection bus phases.
+#
+start_selection:
+ or SCSISEQ,0x48 # ENSELO|ENAUTOATNO
+ mov WAITING_SCBH, SCBPTR
clr SG_NOLOAD
- clr RESELECT
+ and FLAGS,0x3f # !RESELECTING
# 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.
+# on an 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.
#
+
+ test SCBARRAY+11,0xff jnz identify # 0 Length Command?
+
+# The kernel has sent us an SCB with no command attached. This implies
+# that the kernel wants to send a message of some sort to this target,
+# so we interrupt the driver, allow it to fill the message buffer, and
+# then go back into the arbitration loop
+ mvi INTSTAT,AWAITING_MSG
+ jmp poll_for_work
+
+identify:
mov SCBARRAY+1 call disconnect # disconnect ok?
and SINDEX,0x7,SCBARRAY+1 # lun
@@ -190,64 +404,50 @@
or SINDEX,0x80 call mk_mesg # IDENTIFY message
mov A,SINDEX
+ test SCBARRAY+0,0xe0 jz !message # WDTR, SDTR or TAG??
cmp MSG_START+0,A jne !message # did driver beat us?
- mvi MSG_START+1 call mk_sdtr # build SDTR message if needed
-!message:
+# Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag
+# value
-# Enable selection phase as an initiator, and do automatic ATN
-# after the selection.
-#
- mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO
+mk_tag:
+ mvi DINDEX, MSG_START+1
+ test SCBARRAY+0,TAG_ENB jz mk_tag_done
+ and A,0x23,SCBARRAY+0
+ mov DINDIR,A
+ mov DINDIR,SCBPTR
-# 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
+ add MSG_LEN,-MSG_START+0,DINDEX # update message length
+
+mk_tag_done:
-# 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
+ mov DINDEX call mk_dtr # build DTR message if needed
+
+!message:
+ jmp poll_for_work
+
+# 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.
#
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.
+ mov SELID call initialize_scsiid
+ and FLAGS,0x3f # reselected, no IDENTIFY
+ or FLAGS,RESELECTED jmp select2
+
+# After the selection, remove this SCB from the "waiting for selection"
+# list. This is achieved by simply moving our "next" pointer into
+# WAITING_SCBH and setting our next pointer to null so that the next
+# time this SCB is used, we don't get confused.
#
-*select:
- clr SCSISEQ
+select:
+ or SCBARRAY+0,NEEDDMA
+ mov WAITING_SCBH,SCBARRAY+30
+ mvi SCBARRAY+30,SCB_LIST_NULL
+select2:
+ call initialize_for_target
+ mvi SCSISEQ,ENRSELI
+ mvi CLRSINT0,0x60 # CLRSELDI|CLRSELDO
mvi CLRSINT1,0x8 # CLRBUSFREE
# Main loop for information transfer phases. If BSY is false, then
@@ -273,41 +473,44 @@
cmp A,0xa0 je p_mesgout
cmp A,0xe0 je p_mesgin
- mvi INTSTAT,SIGNAL_0 # unknown - signal driver
+ mvi INTSTAT,BAD_PHASE # 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 DINDEX,HADDR
+ mvi SCBARRAY+19 call bcopy_4
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+23 call bcopy
+# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR
+ mvi SCBARRAY+23 call bcopy_3
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+19 call bcopy
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+23 call bcopy_3
+# If we are the last SG block, don't set wideodd.
+ test SCBARRAY+18,0xff jnz p_dataout_wideodd
mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
# DIRECTION|FIFORESET
+ jmp p_dataout_rest
+p_dataout_wideodd:
+ mvi 0xbd call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+ # DIRECTION|FIFORESET
+
+p_dataout_rest:
# 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 STCNT call bcopy_3
- mvi A,4
mvi DINDEX,SCBARRAY+19
- mvi SHADDR call bcopy
+ mvi SHADDR call bcopy_4
call sg_advance
mov SCBARRAY+18,SG_COUNT # residual S/G count
@@ -319,27 +522,29 @@
call assert
call sg_load
- mvi A,3
- mvi DINDEX,HCNT
- mvi SCBARRAY+23 call bcopy
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+19 call bcopy_4
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+23 call bcopy
+# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR
+ mvi SCBARRAY+23 call bcopy_3
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+19 call bcopy
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+23 call bcopy_3
+# If we are the last SG block, don't set wideodd.
+ test SCBARRAY+18,0xff jnz p_datain_wideodd
mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN|
# !DIRECTION|FIFORESET
- mvi A,3
+ jmp p_datain_rest
+p_datain_wideodd:
+ mvi 0xb9 call dma # WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+ # !DIRECTION|FIFORESET
+p_datain_rest:
mvi DINDEX,SCBARRAY+23
- mvi STCNT call bcopy
+ mvi STCNT call bcopy_3
- mvi A,4
mvi DINDEX,SCBARRAY+19
- mvi SHADDR call bcopy
+ mvi SHADDR call bcopy_4
call sg_advance
mov SCBARRAY+18,SG_COUNT # residual S/G count
@@ -354,17 +559,14 @@
mvi 0x80 call scsisig # CDO|!IOO|!MSGO
call assert
- mvi A,3
- mvi DINDEX,HCNT
- mvi SCBARRAY+11 call bcopy
+ mvi DINDEX,HADDR
+ mvi SCBARRAY+7 call bcopy_4
- mvi A,3
- mvi DINDEX,STCNT
- mvi SCBARRAY+11 call bcopy
+# mvi DINDEX,HCNT # implicit since HCNT is next to HADDR
+ mvi SCBARRAY+11 call bcopy_3
- mvi A,4
- mvi DINDEX,HADDR
- mvi SCBARRAY+7 call bcopy
+ mvi DINDEX,STCNT
+ mvi SCBARRAY+11 call bcopy_3
mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
# DIRECTION|FIFORESET
@@ -376,8 +578,8 @@
p_status:
mvi 0xc0 call scsisig # CDO|IOO|!MSGO
- mvi SCBARRAY+14 call inb
- jmp ITloop
+ mvi SCBARRAY+14 call inb_first
+ jmp p_mesgin_done
# 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.
@@ -386,12 +588,14 @@
mvi 0xa0 call scsisig # CDO|!IOO|MSGO
mvi 0x8 call mk_mesg # build NOP message
+ clr STCNT+2
+ clr STCNT+1
+
# 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.
@@ -424,14 +628,13 @@
# an SPIORDY that hadn't dropped yet.
#
p_mesgout3:
- call one_stcnt
+ mvi STCNT+0, 0x01
mov SCSIDATL,SINDIR
p_mesgout4:
test SSTAT0,0x4 jz p_mesgout4 # SDONE
dec DINDEX
- inc A
- cmp MSG_LEN,A jne p_mesgout2
+ test DINDEX,0xff jnz 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.
@@ -448,7 +651,7 @@
p_mesgout6:
mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS
- clr MSG_FLAGS # no active msg
+ and FLAGS,0xdf # no active msg
jmp ITloop
# Message in phase. Bytes are read using Automatic PIO mode, but not
@@ -468,46 +671,103 @@
# 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 interpret errors only when they occur
+# instead of always uploading the scb. If the status is SCSI_CHECK,
+# the driver will download a new scb requesting sense to replace
+# the old one, modify the "waiting for selection" SCB list and set
+# RETURN_1 to 0x80. If RETURN_1 is set to 0x80 the sequencer imediately
+# jumps to main loop where it will run down the waiting SCB list.
+# If the kernel driver does not wish to request sense, it need
+# only clear RETURN_1, and the command is allowed to complete. 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 check for residuals
+ test SCBARRAY+15,0xff jnz resid
+ test SCBARRAY+16,0xff jnz resid
+ test SCBARRAY+17,0xff jnz resid
+
+check_status:
+ test SCBARRAY+14,0xff jz status_ok # 0 Status?
+ mvi INTSTAT,BAD_STATUS # let driver know
+ test RETURN_1, 0x80 jz status_ok
+ jmp p_mesgin_done
+
+status_ok:
+# First, mark this target as free.
+ test SCBARRAY+0,0x20 jnz complete # Tagged command
+ and FUNCTION1,0x70,SCBARRAY+1
+ mov A,FUNCTION1
+ test SCBARRAY+1,0x88 jz clear_a
+ xor ACTIVE_B,A
+ jmp complete
+
+clear_a:
+ xor ACTIVE_A,A
+
+complete:
mov QOUTFIFO,SCBPTR
- mvi INTSTAT,0x2 # CMDCMPLT
+ mvi INTSTAT,CMDCMPLT
+ test SCBARRAY+11,0xff jz start # Immediate message complete
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 -
+# If we have a residual count, interrupt and tell the host. Other
+# alternatives are to pause the sequencer on all command completes (yuck),
+# dma the resid directly to the host (slick, but a ton of instructions), or
+# have the sequencer pause itself when it encounters a non-zero resid
+# (unecessary pause just to flag the command -- yuck, but takes few instructions
+# and since it shouldn't happen that often is good enough for our purposes).
+
+resid:
+ mvi INTSTAT,RESIDUAL
+ jmp check_status
+
+# Is it an extended message? We only support the synchronous and wide data
+# transfer request messages, which will probably be in response to
+# WDTR or SDTR message outs from us. If it's not SDTR or WDTR, 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
+ mvi ARG_1 call inb_next # extended message length
+ mvi A call inb_next # extended message code
- call ndx_sdtr # index sync config for target
- mov DINDEX,SINDEX
- mov DINDIR,RETURN_1 # save returned value
+ cmp A,1 je p_mesginSDTR # Syncronous negotiation message
+ cmp A,3 je p_mesginWDTR # Wide negotiation message
+ jmp p_mesginN
+
+p_mesginWDTR:
+ cmp ARG_1,2 jne p_mesginN # extended mesg length = 2
+ mvi A call inb_next # Width of bus
+ mvi INTSTAT,MSG_WDTR # let driver know
+ test RETURN_1,0x80 jz p_mesgin_done# Do we need to send WDTR?
+
+# We didn't initiate the wide negotiation, so we must respond to the request
+ and RETURN_1,0x7f # Clear the SEND_WDTR Flag
+ or FLAGS,ACTIVE_MSG
+ mvi DINDEX,MSG_START+0
+ mvi MSG_START+0 call mk_wdtr # build WDTR message
+ or SINDEX,0x10,SIGSTATE # turn on ATNO
+ call scsisig
+ jmp p_mesgin_done
- not A # turn off "need sdtr" flag
- and NEEDSDTR,A
+p_mesginSDTR:
+ cmp ARG_1,3 jne p_mesginN # extended mesg length = 3
+ mvi ARG_1 call inb_next # xfer period
+ mvi A call inb_next # REQ/ACK offset
+ mvi INTSTAT,MSG_SDTR # call driver to convert
-# 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
+ test RETURN_1,0xc0 jz p_mesgin_done# Do we need to mk_sdtr or rej?
+ test RETURN_1,0x40 jnz p_mesginN # Requested SDTR too small - rej
+ or FLAGS,ACTIVE_MSG
+ mvi DINDEX, MSG_START+0
+ mvi MSG_START+0 call mk_sdtr
+ or SINDEX,0x10,SIGSTATE # turn on ATNO
+ call scsisig
jmp p_mesgin_done
# Is it a disconnect message? Set a flag in the SCB to remind us
@@ -547,43 +807,47 @@
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
+ and A,0x07 # lun in lower three bits
+ or SAVED_TCL,A,SELID
+ and SAVED_TCL,0xf7
+ and A,0x08,SBLKCTL # B Channel??
+ or SAVED_TCL,A
+ call inb_last # ACK
+ mov ALLZEROS call findSCB
+setup_SCB:
+ and SCBARRAY+0,0xfb # clear disconnect bit in SCB
+ or FLAGS,IDENTIFY_SEEN # make note of IDENTIFY
call sg_scb2ram # implied restore pointers
# required on reselect
- jmp p_mesgin_done
+ jmp ITloop
+get_tag:
+ mvi A call inb_first
+ cmp A,0x20 jne return # Simple Tag message?
+ mvi A call inb_next
+ call inb_last
+ test A,0xf0 jnz abort_tag # Tag in range?
+ mov SCBPTR,A
+ mov A,SAVED_TCL
+ cmp SCBARRAY+1,A jne abort_tag
+ test SCBARRAY+0,TAG_ENB jz abort_tag
+ ret
+abort_tag:
+ or SINDEX,0x10,SIGSTATE # turn on ATNO
+ call scsisig
+ mvi INTSTAT,ABORT_TAG # let driver know
+ mvi 0xd call mk_mesg # ABORT TAG message
+ ret
-# 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?
+# Message reject? Let the kernel driver handle this. If we have an
+# outstanding WDTR or SDTR negotiation, assume that it's a response from
+# the target selecting 8bit or asynchronous transfer, otherwise just ignore
+# it since we have no clue what it pertains to.
#
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
+ mvi INTSTAT, MSG_REJECT
jmp p_mesgin_done
# [ ADD MORE MESSAGE HANDLING HERE ]
@@ -600,7 +864,7 @@
p_mesginN:
or SINDEX,0x10,SIGSTATE # turn on ATNO
call scsisig
- mvi INTSTAT,SIGNAL_1 # let driver know
+ mvi INTSTAT,SEND_REJECT # let driver know
mvi 0x7 call mk_mesg # MESSAGE REJECT message
@@ -608,6 +872,7 @@
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.
@@ -615,44 +880,54 @@
p_busfree:
mvi CLRSINT1,0x40 # CLRATNO
clr SIGSTATE
+
+# if this is an immediate command, perform a psuedo command complete to
+# notify the driver.
+ test SCBARRAY+11,0xff jz status_ok
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:
+# Instead of a generic bcopy routine that requires an argument, we unroll
+# the two cases that are actually used, and call them explicitly. This
+# not only reduces the overhead of doing a bcopy by 2/3rds, but ends up
+# saving space in the program since you don't have to put the argument
+# into the accumulator before the call. Both functions expect DINDEX to
+# contain the destination address and SINDEX to contain the source
+# address.
+bcopy_3:
mov DINDIR,SINDIR
- dec A
- cmp ALLZEROS,A jne bcopy
- ret
+ mov DINDIR,SINDIR
+ mov DINDIR,SINDIR ret
+
+bcopy_4:
+ mov DINDIR,SINDIR
+ mov DINDIR,SINDIR
+ mov DINDIR,SINDIR
+ mov DINDIR,SINDIR ret
+
+bcopy_3_dfdat:
+ mov DINDIR,DFDAT
+ mov DINDIR,DFDAT
+ mov DINDIR,DFDAT ret
+
+bcopy_4_dfdat:
+ mov DINDIR,DFDAT
+ mov DINDIR,DFDAT
+ mov DINDIR,DFDAT
+ mov DINDIR,DFDAT 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 SEQCTL,0x50 # PAUSEDIS|FASTMODE
+ test FLAGS,ACTIVE_MSG jnz mk_mesg1 # active message?
- mvi MSG_FLAGS,0x80 # if not, there is now
+ or FLAGS,ACTIVE_MSG # 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
+ mvi SEQCTL,0x10 ret # !PAUSEDIS|FASTMODE
# Carefully read data in Automatic PIO mode. I first tried this using
# Manual PIO mode, but it gave me continual underrun errors, probably
@@ -672,13 +947,15 @@
# use the same calling convention as inb.
#
inb_first:
+ clr STCNT+2
+ clr STCNT+1
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
+ mvi STCNT+0,1 # xfer one byte
mov NONE,SCSIDATL # dummy read from latch to ACK
inb_next1:
test SSTAT0,0x4 jz inb_next1 # SDONE
@@ -687,37 +964,12 @@
mov DINDIR,SCSIBUSL ret # read byte directly from bus
inb_last:
- call one_stcnt # ACK with dummy read
+ mvi STCNT+0,1 # 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
@@ -752,47 +1004,40 @@
dma6:
test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK
- mvi A,3
mvi DINDEX,SCBARRAY+15
- mvi STCNT call bcopy
+ mvi STCNT call bcopy_3
ret
+dma_finish:
+ test DFSTATUS,0x8 jz dma_finish # HDONE
+
+ clr DFCNTRL # disable DMA
+dma_finish2:
+ test DFCNTRL,0x8 jnz dma_finish2 # HDMAENACK
+ 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
+initialize_scsiid:
+ and SINDEX,0xf0 # Get target ID
+ and A,0x0f,SCSIID
+ or SINDEX,A
+ mov SCSIID,SINDEX ret
-# Turn on Automatic PIO mode now, before we expect to see an REQ
+initialize_for_target:
+# Turn on Automatic PIO mode now, before we expect to see a 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.
-#
+ clr SIGSTATE
+
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.
#
@@ -800,66 +1045,60 @@
# Initialize SCSIRATE with the appropriate value for this target.
#
- call ndx_sdtr
- mov SCSIRATE,SINDIR
- ret
+ call ndx_dtr
+ 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?
+ test FLAGS,RESELECTED jz return # reselected?
+ test FLAGS,IDENTIFY_SEEN jnz return # seen IDENTIFY?
- mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic
-
-assert1:
- ret
+ mvi INTSTAT,NO_IDENT ret # no - cause a kernel panic
# 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
+# us. The tcl from SCBARRAY+1 should be in 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.
+# To allow for wide or twin busses, we check the upper bit of the target ID
+# and the channel ID and look at the appropriate disconnect 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
+ test SINDEX, 0x88 jz disconnect_a
+
+ test DISC_DSB_B,A jz disconnect1 # bit nonzero if DISabled
+ clr A ret
+disconnect_a:
+ 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.
+# Locate the SCB matching the target ID/channel/lun in SAVED_TCL 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. SINDEX should be
+# cleared on call.
#
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 A,SAVED_TCL
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
-
+ cmp SCBARRAY+1,A jne findSCB1 # target ID/channel/lun match?
+ test SCBARRAY+0,0x4 jz findSCB1 # should be disconnected
+ test SCBARRAY+0,TAG_ENB jnz get_tag
ret
-findSCB2:
+findSCB1:
inc SINDEX
mov A,SCBCOUNT
- cmp SINDEX,A jne findSCB1
+ cmp SINDEX,A jne findSCB
- mvi INTSTAT,SIGNAL_3 # not found - signal kernel
+ mvi INTSTAT,NO_MATCH # not found - signal kernel
mvi 0x6 call mk_mesg # ABORT message
or SINDEX,0x10,SIGSTATE # assert ATNO
@@ -871,32 +1110,24 @@
sg_scb2ram:
mov SG_COUNT,SCBARRAY+2
- mvi A,4
mvi DINDEX,SG_NEXT
- mvi SCBARRAY+3 call bcopy
+ mvi SCBARRAY+3 call bcopy_4
mvi SG_NOLOAD,0x80
- test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g?
- clr SG_NOLOAD
-
-sg_scb2ram1:
- ret
+ test SCBARRAY+0,0x10 jnz return # don't reload s/g?
+ clr SG_NOLOAD 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
+ mvi SG_NEXT call bcopy_4
and SCBARRAY+0,0xef,SCBARRAY+0
- test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g?
- or SCBARRAY+0,0x10
-
-sg_ram2scb1:
- ret
+ test SG_NOLOAD,0x80 jz return # reload s/g?
+ or SCBARRAY+0,SG_LOAD 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
@@ -905,28 +1136,23 @@
# 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?
+ test SG_COUNT,0xff jz return # SG being used?
+ test SG_NOLOAD,0x80 jnz return # 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 SG_NEXT call bcopy_4
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
+ call dma_finish
# Copy data from FIFO into SCB data pointer and data count. This assumes
# that the struct scatterlist has this structure (this and sizeof(struct
@@ -938,21 +1164,26 @@
# 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
+# Not in FreeBSD. the scatter list entry is only 8 bytes.
+#
+# struct ahc_dma_seg {
+# physaddr addr; /* four bytes, little-endian order */
+# long len; /* four bytes, little endian order */
+# };
+#
+
+ mvi DINDEX, SCBARRAY+19
+ call bcopy_4_dfdat
+
+# For Linux, we must throw away four bytes since there is a 32bit gap
+# in the middle of a struct scatterlist
+ mov NONE,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
- mov SCBARRAY+23,DFDAT
- mov SCBARRAY+24,DFDAT
- clr SCBARRAY+25
-
-sg_load3:
+ call bcopy_3_dfdat #Only support 24 bit length.
ret
# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled,
@@ -963,7 +1194,7 @@
# next time.
#
sg_advance:
- test SG_COUNT,0xff jz sg_advance2 # s/g enabled?
+ test SG_COUNT,0xff jz return # s/g enabled?
test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero?
test STCNT+1,0xff jnz sg_advance1
@@ -976,47 +1207,69 @@
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
+ 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
+ mvi SG_NOLOAD,0x80 ret # don't reload s/g next time
# 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:
+ndx_dtr:
shr A,SCSIID,4
- and A,0x7
+ test SBLKCTL,0x08 jz ndx_dtr_2
+ or A,0x08 # Channel B entries add 8
+ndx_dtr_2:
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
+# If we need to negotiate transfer parameters, build the WDTR or 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
+# The SCSI-II spec requires that Wide negotiation occur first and you can
+# only negotiat one or the other at a time otherwise in the event of a message
+# reject, you wouldn't be able to tell which message was the culpret.
+#
+mk_dtr:
+ test SCBARRAY+0,0xc0 jz return # NEEDWDTR|NEEDSDTR
+ test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit
+ or FLAGS, MAX_OFFSET # Force an offset of 15 or 8 if WIDE
-mk_sdtr1:
+mk_sdtr:
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
+ call sdtr_to_rate
+ mov DINDIR,RETURN_1 # REQ/ACK transfer period
+ test FLAGS, MAX_OFFSET jnz mk_sdtr_max_offset
+ and DINDIR,0x0f,SINDIR # Sync Offset
+
+mk_sdtr_done:
+ add MSG_LEN,-MSG_START+0,DINDEX ret # update message length
+
+mk_sdtr_max_offset:
+# We're initiating sync negotiation, so request the max offset we can (15 or 8)
+ xor FLAGS, MAX_OFFSET
+ test SCSIRATE, 0x80 jnz wmax_offset # Talking to a WIDE device?
+ mvi DINDIR, MAX_OFFSET_8BIT
+ jmp mk_sdtr_done
+
+wmax_offset:
+ mvi DINDIR, MAX_OFFSET_WIDE
+ jmp mk_sdtr_done
+
+mk_wdtr_16bit:
+ mvi ARG_1,BUS_16_BIT
+mk_wdtr:
+ mvi DINDIR,1 # extended message
+ mvi DINDIR,2 # extended message length = 2
+ mvi DINDIR,3 # WDTR code
+ mov DINDIR,ARG_1 # bus width
+ add MSG_LEN,-MSG_START+0,DINDEX ret # update message length
+
# 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
@@ -1027,3 +1280,21 @@
scsisig:
mov SIGSTATE,SINDEX
mov SCSISIGO,SINDEX ret
+
+sdtr_to_rate:
+ call ndx_dtr # index scratch space for target
+ shr A,SINDIR,0x4
+ dec SINDEX #Preserve SINDEX
+ and A,0x7
+ clr RETURN_1
+sdtr_to_rate_loop:
+ test A,0x0f jz sdtr_to_rate_done
+ add RETURN_1,0x18
+ dec A
+ jmp sdtr_to_rate_loop
+sdtr_to_rate_done:
+ shr RETURN_1,0x2
+ add RETURN_1,0x18 ret
+
+return:
+ 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