summaryrefslogtreecommitdiff
path: root/scsi/adapters
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /scsi/adapters
Initial source
Diffstat (limited to 'scsi/adapters')
-rw-r--r--scsi/adapters/README290
-rw-r--r--scsi/adapters/scsi_33C93.h396
-rw-r--r--scsi/adapters/scsi_33C93_hdw.c2078
-rw-r--r--scsi/adapters/scsi_5380.h126
-rw-r--r--scsi/adapters/scsi_5380_hdw.c2423
-rw-r--r--scsi/adapters/scsi_53C700.h327
-rw-r--r--scsi/adapters/scsi_53C700_hdw.c696
-rw-r--r--scsi/adapters/scsi_53C94.h253
-rw-r--r--scsi/adapters/scsi_53C94_hdw.c2840
-rw-r--r--scsi/adapters/scsi_7061.h230
-rw-r--r--scsi/adapters/scsi_7061_hdw.c2603
-rw-r--r--scsi/adapters/scsi_89352.h231
-rw-r--r--scsi/adapters/scsi_89352_hdw.c2192
-rw-r--r--scsi/adapters/scsi_aha15.h347
-rw-r--r--scsi/adapters/scsi_aha15_hdw.c1467
-rw-r--r--scsi/adapters/scsi_aha17_hdw.c1371
-rw-r--r--scsi/adapters/scsi_dma.h150
-rw-r--r--scsi/adapters/scsi_user_dma.c171
-rw-r--r--scsi/adapters/scsi_user_dma.h47
19 files changed, 18238 insertions, 0 deletions
diff --git a/scsi/adapters/README b/scsi/adapters/README
new file mode 100644
index 0000000..1bc7e7c
--- /dev/null
+++ b/scsi/adapters/README
@@ -0,0 +1,290 @@
+
+This directory contains various examples of HBA support code,
+among them:
+
+ Chip/Board File Machine tested By
+
+ NCR 53C94 scsi_53C94 DecStation 5000 af@cmu
+ DEC 7061 scsi_7061 DecStation 3100/2100 af@cmu
+ NCR 5380 scsi_5380 VaxStation 3100 af@cmu
+ Fujitsu 89352 scsi_89352 Omron Luna88k danner@cmu
+ Adaptec 1542B scsi_aha15 AT/PC af@cmu
+
+It should be trivial to modify them for some other machine that uses
+the same SCSI chips, hopefully by properly conditionalizing and macroizing
+the existing code.
+
+There are various rules and assumptions to keep in mind when designing/coding
+the support code for a new HBA, here is a list. Pls extend this with
+anything you find is not openly stated here and made your life miserable
+during the port, and send it back to CMU.
+
+
+AUTOCONF
+
+We assume the structures and procedures defined in chips/busses.*,
+e.g. someone will call configure_bus_master to get to our foo_probe()
+routine. This should make up its mind on how many targets it sees
+[see later for dynamic reconfig], allocate a descriptor for each
+one and leave the driver ready to accept commands (via foo_go()).
+
+ On raw chips you should use a test_unit_ready command,
+ selecting without ATN, and timing out on non-existant
+ devices. Use LUN 0.
+ On boards, there probably is a command to let the board do
+ it (see Adaptec), if not do as above.
+
+The typical autoconf descriptor might look like
+
+ caddr_t foo_std[NFOO] = { 0 };
+ struct bus_device *foo_dinfo[NFOO*8];
+ struct bus_ctlr *foo_minfo[NFOO];
+ struct bus_driver foo_driver =
+ { foo_probe, scsi_slave, scsi_attach, foo_go, foo_std, "rz",
+ foo_dinfo, "foo", foo_minfo, BUS_INTR_B4_PROBE};
+
+which indicates that foo_probe() and foo_go() are our interface functions,
+and we use the generic scsi_slave() and scsi_attach() for the rest.
+Their definition is
+
+ foo_probe(reg, ui)
+ vm_offset_t reg;
+ struct bus_ctlr *ui;
+
+[the "reg" argument might actually be something else on architectures that
+ do not use memory mapped I/O]
+
+ aha_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+
+The foo_go() routine is fairly common across chips, look at any example to
+see how to structure it. Basically, the arguments tell us how much data
+to expect in either direction, and whether (cmd_only) we think we should
+be selecting with ATN (cmd_only==FALSE) or not. The only gotcha is cmd_count
+actually includes the size of any parameters.
+
+The "go" field of the scsi_softc structure describing your HBA should be
+set to your foo_go() routine, by the foo_probe() routine.
+
+DATA DEPENDENCIES
+
+The upper layer assumes that tgt->cmd_ptr is a pointer to good memory
+[e.g. no funny padding] where it places the scsi command control blocks
+AND small (less than 255 bytes) parameters. It also expects small results
+in there (things like read_capacity, for instance). I think I cleaned
+up all the places that used to assume tgt->cmd_ptr was aligned, but do not
+be surprised if I missed one.
+
+It does NOT use the dma_ptr, or any of the transient_state fields.
+
+WATCHDOG
+
+There is an optional MI watchdog facility, which can be used quite simply by
+filling in the "watchdog" field of the scsi_softc structure describing
+your HBA. To disable it, leave the field zero (or, dynamically, zero the
+timeout value). You can use a watchdog of your own if you like, or more
+likely set this field to point to the MI scsi_watchdog().
+This requires that your foo_softc descriptor starts off with a watchdog_t
+structure, with the "reset" field pointing to a function that will
+reset the SCSI bus should the watchdog expire.
+
+When a new SCSI command is initiated you should
+ if (foo->wd.nactive++ == 0)
+ foo->wd.watchdog_state = SCSI_WD_ACTIVE;
+to activate the watchdog, on each interrupt [or other signal that all
+is proceeding well for the command and it is making progress] you should
+ if (foo->wd.nactive)
+ foo->wd.watchdog_state = SCSI_WD_ACTIVE;
+bump down the watchdog 'trigger level', and when the command terminates
+ if (aha->wd.nactive-- == 1)
+ aha->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+When you detect a SCSI bus reset (possibly we initiated it) you should
+ aha->wd.nactive = 0;
+and after cleaning up properly invoke
+ scsi_bus_was_reset(sc)
+ scsi_softc_t sc;
+
+The functiona that is invoked on watchdog expiry is
+ foo_reset_scsibus(foo)
+ register foo_softc_t foo;
+
+Note that this can be used for dumb chips that do not support select timeouts
+in hardware [see the 5380 or 7061 code], but its primary use is to detect
+instances where a target is holding on the SCSI bus way too long.
+
+The one drawback of resetting the bus is that some devices (like tapes)
+lose status in case of a reset, and the MI code does not (yet?) try to
+keep enough information around to be able to recover. If you want to
+add something very useful you might change the rz_tape.c code to do just
+that, e.g. on SCSI_RET_ABORTs wait a while for the tape to do whatever,
+then rewind, and seek forward where the tape should have been positioned at
+the beginning of the command that failed, then reissue the command.
+None of the examples so far tries to be 'smart' like making an attempt
+to get the bus unstuck without resetting it, send us ideas if you have
+some.
+
+
+DYNAMIC RECONFIG
+
+Your code should be ready to add/remove targets on the fly. To do so,
+notify the upper layer that a target went offline returning
+SCSI_RET_DEVICE_DOWN when e.g. the select timed out, and clear out
+the tgt->flags field.
+To find new devices, define a function
+
+ boolean_t
+ foo_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+
+and install it in the "probe" field of the scsi_softc_t structure describing
+the HBA to the upper layer. This function should finalize all HBA-specific
+info in the target_info structure, then do a scsi_inquiry and check the
+return code. If this is not SCSI_RET_DEVICE_DOWN the target should be
+marked TGT_ALIVE.
+
+
+COMMAND TERMINATION
+
+Generally, command termination should be reported to the upper layers
+by setting the tgt->done field to the proper value [it should remain
+SCSI_RET_IN_PROGRESS while the command is executing] and invoking the
+target's completion routine, like:
+ if (tgt->ior) {
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+Note that early on some commands will actually wait for completion
+by spinning on the tgt->done value, because autoconf happens when
+threads and the scheduler are not working.
+
+Return SCSI_RET_RETRY if the target was busy, the command will be retried
+as appropriate.
+
+Check the completion routines [in rz_disk.c and rz_tape.c for instance]
+if you are not sure what to return in a troubled case.
+
+HBA CHIPS GOTCHAS
+
+All of the examples so far use the idea of 'scripts': the interrupt routine
+matches the chip state with what is expected and if this is ok (it is
+in the common important case) it just calls a prepackaged function.
+We have found this to be _extremely_ simpler than using a state machine
+of various ridiculous and erroneous sorts, and much more handy for debugging
+also. Not to mention the saving on code.
+Nonetheless, there really are no restrictions on how to structure the HBA
+code, if you prefer state machines go ahead and use them!
+
+Scheduling of the bus among competing targets is one of the major missing
+pieces for simple HBAs. A winning strategy used so far is as follows.
+Allocate a queue_head_t of waiting_targets in your foo_softc, and two
+target_info_t pointers next_target and active_target. A three-valued
+state field is also needed. If you enter the foo_go() routine
+and find the state&BUSY simply enqueue_tail() your tgt on the waiting_targets
+queue. Otherwise mark the bus BUSY, set next_target to tgt, and proceed
+to a selection attempt.
+Note that the attempt might fail and a reselection win over it, therefore
+the attempt_selection() routine should just retrieve the next_target
+and install it in active_target, start the selection and let the interrupt
+routine take care of the rest [see scsi_5380 for a different setup].
+If a reselection wins we should note that we had a COLLISION in the state
+field, install the reconecting target and proceed to completion.
+When either a command is complete or a target disconnects you should invoke
+a foo_release_bus() routine, which might look like:
+
+boolean_t
+foo_release_bus(foo)
+ register foo_softc_t foo;
+{
+ boolean_t ret = TRUE;
+
+ LOG(9,"release");
+ if (foo->state & FOO_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ foo->state &= ~FOO_STATE_COLLISION;
+ foo_attempt_selection(foo);
+
+ } else if (queue_empty(&foo->waiting_targets)) {
+
+ foo->state &= ~FOO_STATE_BUSY;
+ foo->active_target = 0;
+ foo->script = 0;
+ ret = FALSE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ foo->next_target = (target_info_t *)
+ dequeue_head(&foo->waiting_targets);
+ foo_attempt_selection(foo);
+ }
+ return ret;
+}
+
+which indicates whether the bus has been handed off to a new target or not.
+This provides the desired FIFO scheduling of the bus and gives maximum
+parallelism when targets are allowed to (and infact do) disconnect.
+
+An area where there are problems most times is how to minimize the
+interaction of selections and reselections in, e.g. foo_attempt_selection().
+This is very much chip specific, but sneaking on the SCSI bus should
+be a viable alternative in most cases. Check in the specs what happens
+if you send a command while there is already a reselection pending:
+a well behaved chip would ignore the command and not screwup its status.
+[Keep in mind that even if _now_ there is no reselection indication
+ on the next cycle there might be and you won't see it!]
+
+RANDOM GOTCHAS
+
+A number of workstations do not provide real DMA support [do not ask me why]
+but rather a special 'buffer' more or less wide where you have to copy
+data to and from. This has been handled, see esp the 52C94 code which has
+even the extreme optimization of issuing the send command even before
+the data has been copied into the buffer! We have handled even machines
+where no DMA at all was provided.
+
+Speaking of DMA.. many of these chips 'prefetch' data, or have a FIFO
+on board (they have to if they do synch xfers), and when the target
+disconnects it is always a pain to find out how many bytes exactly did we
+xfer. Be advised that this hurdle exists, and that the best way to
+debug your code here is to use a tape. A safe way is to initially
+disable disconnects [so that you can get the system up from disk]
+and enable them only on the tape unit that you are using for testing.
+Later on enable disks but make sure you have some way to recover from
+a zapped disk !
+
+MOVING TO USER SPACE
+
+All examples have hooks for user-space versions of the driver, the
+ones for 54C94 and 7061 actually do work. Look in mapped_scsi.c
+for how this is done, it is fairly simple as far as the kernel is
+concerned. To keep the option of mapping to user space open you
+should structure your interrupt routine such that it does all the
+state gathering and clearing of the interrupt right away. This
+scheme gives you some assurance that your code will keep on working
+when the interrupt processing is actually delayed and you recover
+the interrupt state from the saved structure in the mapped area.
+
+
+IMPROVEMENTS
+
+There are a variety of things to be done still, for instance:
+
+- rewrite scsi_slave() and scsi_attach() to be fully SCSI-II compliant.
+ There are only comments right now as to how that should be done.
+
+- add enough machinery to the tape code to be able to recover from
+ bus resets. Do so in such a way that other devices might use the ideas.
+
+- add more devices, like printers scanners modems etc that are currently
+ missing
+
+- add a 'generic' set_status flavor which simply executes a scsi command
+ passed in from user. This seems the way many vendors and other people
+ have strutured their drivers, it would make it possible to have a common
+ user-level program to do special maintainance work like, for instance,
+ reformatting of disks.
+
diff --git a/scsi/adapters/scsi_33C93.h b/scsi/adapters/scsi_33C93.h
new file mode 100644
index 0000000..454a8eb
--- /dev/null
+++ b/scsi/adapters/scsi_33C93.h
@@ -0,0 +1,396 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_33C93.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/91
+ *
+ * Defines for the WD/AMD 33C93 SBIC (SCSI Bus Interface Controller)
+ */
+
+/*
+ * Register map, w mux addressing
+ */
+
+typedef struct {
+
+ volatile unsigned char sbic_myid; /* rw: My SCSI id */
+#define sbic_cdbsize sbic_myid /* w : size of CDB */
+
+ volatile unsigned char sbic_control; /* rw: Control register */
+
+ volatile unsigned char sbic_timeo; /* rw: Timeout period */
+
+ volatile unsigned char sbic_cdb1; /* rw: CDB, 1st byte */
+#define sbic_tsecs sbic_cdb1 /* rw: Xlate: nsectors */
+
+ volatile unsigned char sbic_cdb2; /* rw: CDB, 2nd byte */
+#define sbic_theads sbic_cdb2 /* rw: Xlate: nheads */
+
+ volatile unsigned char sbic_cdb3; /* rw: CDB, 3rd byte */
+#define sbic_tcyl_hi sbic_cdb3 /* rw: Xlate: ncyls, hi */
+
+ volatile unsigned char sbic_cdb4; /* rw: CDB, 4th byte */
+#define sbic_tcyl_lo sbic_cdb4 /* rw: Xlate: ncyls, lo */
+
+ volatile unsigned char sbic_cdb5; /* rw: CDB, 5th byte */
+#define sbic_addr_hi sbic_cdb5 /* rw: Xlate: address, hi */
+
+ volatile unsigned char sbic_cdb6; /* rw: CDB, 6th byte */
+#define sbic_addr_2 sbic_cdb6 /* rw: Xlate: address */
+
+ volatile unsigned char sbic_cdb7; /* rw: CDB, 7th byte */
+#define sbic_addr_3 sbic_cdb7 /* rw: Xlate: address */
+
+ volatile unsigned char sbic_cdb8; /* rw: CDB, 8th byte */
+#define sbic_addr_lo sbic_cdb8 /* rw: Xlate: address, lo */
+
+ volatile unsigned char sbic_cdb9; /* rw: CDB, 9th byte */
+#define sbic_secno sbic_cdb9 /* rw: Xlate: sector no */
+
+ volatile unsigned char sbic_cdb10; /* rw: CDB, 10th byte */
+#define sbic_headno sbic_cdb10 /* rw: Xlate: head no */
+
+ volatile unsigned char sbic_cdb11; /* rw: CDB, 11th byte */
+#define sbic_cylno_hi sbic_cdb11/* rw: Xlate: cyl no, hi */
+
+ volatile unsigned char sbic_cdb12; /* rw: CDB, 12th byte */
+#define sbic_cylno_lo sbic_cdb12/* rw: Xlate: cyl no, lo */
+
+ volatile unsigned char sbic_tlun; /* rw: Target LUN */
+
+ volatile unsigned char sbic_cmd_phase; /* rw: Command phase */
+
+ volatile unsigned char sbic_syn; /* rw: Synch xfer params */
+
+ volatile unsigned char sbic_count_hi; /* rw: Xfer count, hi */
+
+ volatile unsigned char sbic_count_med; /* rw: Xfer count, med */
+
+ volatile unsigned char sbic_count_lo; /* rw: Xfer count, lo */
+
+ volatile unsigned char sbic_selid; /* rw: Target ID (select) */
+
+ volatile unsigned char sbic_rselid; /* rw: Target ID (reselect) */
+
+ volatile unsigned char sbic_csr; /* r : Status register */
+
+ volatile unsigned char sbic_cmd; /* rw: Command register */
+
+ volatile unsigned char sbic_data; /* rw: FIFO top */
+
+ char u0; /* unused, padding */
+ char u1; /* unused, padding */
+ char u2; /* unused, padding */
+ char u3; /* unused, padding */
+ char u4; /* unused, padding */
+
+ volatile unsigned char sbic_asr; /* r : Aux Status Register */
+
+} sbic_mux_regmap_t;
+
+/*
+ * Register map, non mux (indirect) addressing
+ */
+typedef struct {
+ volatile unsigned char sbic_asr; /* r : Aux Status Register */
+#define sbic_address sbic_asr /* w : desired register no */
+
+ volatile unsigned char sbic_value; /* rw: register value */
+} sbic_ind_regmap_t;
+
+#define sbic_read_reg(regs,regno,val) { \
+ (regs)->sbic_address = (regno); \
+ wbflush(); \
+ (val) = (regs)->sbic_value; \
+ }
+
+#define sbic_write_reg(regs,regno,val) { \
+ (regs)->sbic_address = (regno); \
+ wbflush(); \
+ (regs)->sbic_value = (val); \
+ }
+
+#define SBIC_myid 0
+#define SBIC_cdbsize 0
+#define SBIC_control 1
+#define SBIC_timeo 2
+#define SBIC_cdb1 3
+#define SBIC_tsecs 3
+#define SBIC_cdb2 4
+#define SBIC_theads 4
+#define SBIC_cdb3 5
+#define SBIC_tcyl_hi 5
+#define SBIC_cdb4 6
+#define SBIC_tcyl_lo 6
+#define SBIC_cdb5 7
+#define SBIC_addr_hi 7
+#define SBIC_cdb6 8
+#define SBIC_addr_2 8
+#define SBIC_cdb7 9
+#define SBIC_addr_3 9
+#define SBIC_cdb8 10
+#define SBIC_addr_lo 10
+#define SBIC_cdb9 11
+#define SBIC_secno 11
+#define SBIC_cdb10 12
+#define SBIC_headno 12
+#define SBIC_cdb11 13
+#define SBIC_cylno_hi 13
+#define SBIC_cdb12 14
+#define SBIC_cylno_lo 14
+#define SBIC_tlun 15
+#define SBIC_cmd_phase 16
+#define SBIC_syn 17
+#define SBIC_count_hi 18
+#define SBIC_count_med 19
+#define SBIC_count_lo 20
+#define SBIC_selid 21
+#define SBIC_rselid 22
+#define SBIC_csr 23
+#define SBIC_cmd 24
+#define SBIC_data 25
+/* sbic_asr is addressed directly */
+
+/*
+ * Register defines
+ */
+
+/*
+ * Auxiliary Status Register
+ */
+
+#define SBIC_ASR_INT 0x80 /* Interrupt pending */
+#define SBIC_ASR_LCI 0x40 /* Last command ignored */
+#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */
+#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */
+#define SBIC_ASR_xxx 0x0c
+#define SBIC_ASR_PE 0x02 /* Parity error (even) */
+#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */
+
+/*
+ * My ID register, and/or CDB Size
+ */
+
+#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */
+ /* 11 Mhz is invalid */
+#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */
+#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */
+#define SBIC_ID_EHP 0x10 /* Enable host parity */
+#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */
+#define SBIC_ID_MASK 0x07
+#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */
+
+/*
+ * Control register
+ */
+
+#define SBIC_CTL_DMA 0x80 /* Single byte dma */
+#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/
+#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */
+#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */
+#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */
+#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */
+#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/
+#define SBIC_CTL_HA 0x02 /* Halt on ATN */
+#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */
+
+/*
+ * Timeout period register
+ * [val in msecs, input clk in Mhz]
+ */
+
+#define SBIC_TIMEOUT(val,clk) ((((val)*(clk))/80)+1)
+
+/*
+ * CDBn registers, note that
+ * cdb11 is used for status byte in target mode (send-status-and-cc)
+ * cdb12 sez if linked command complete, and w/flag if so
+ */
+
+/*
+ * Target LUN register
+ * [holds target status when select-and-xfer]
+ */
+
+#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */
+#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */
+#define SBIC_TLUN_xxx 0x38
+#define SBIC_TLUN_MASK 0x07
+
+/*
+ * Command Phase register
+ */
+
+#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */
+#define SBIC_CPH(p) ((p)&SBIC_CPH_MASK)
+
+/*
+ * FIFO register
+ */
+
+#define SBIC_FIFO_DEEP 12
+
+/*
+ * Synchronous xfer register
+ */
+
+#define SBIC_SYN_OFF_MASK 0x0f
+#define SBIC_SYN_MAX_OFFSET (SBIC_FIFO_DEEP-1)
+#define SBIC_SYN_PER_MASK 0x70
+#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */
+
+#define SBIC_SYN(o,p) (((o)&SBIC_SYN_OFF_MASK)|(((p)<<4)&SBIC_SYN_PER_MASK))
+
+/*
+ * Transfer count register
+ * optimal access macros depend on addressing
+ */
+
+/*
+ * Destination ID (selid) register
+ */
+
+#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */
+#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */
+# define SBIC_SID_FROM_SCSI 0x40
+# define SBIC_SID_TO_SCSI 0x00
+#define SBIC_SID_xxx 0x38
+#define SBIC_SID_IDMASK 0x07
+
+/*
+ * Source ID (rselid) register
+ */
+
+#define SBIC_RID_ER 0x80 /* Enable reselection */
+#define SBIC_RID_ES 0x40 /* Enable selection */
+#define SBIC_RID_DSP 0x20 /* Disable select parity */
+#define SBIC_RID_SIV 0x08 /* Source ID valid */
+#define SBIC_RID_MASK 0x07
+
+/*
+ * Status register
+ */
+
+#define SBIC_CSR_CAUSE 0xf0
+# define SBIC_CSR_RESET 0x00 /* chip was reset */
+# define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */
+# define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/
+# define SBIC_CSR_CMD_ERR 0x40 /* end with error */
+# define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */
+
+#define SBIC_CSR_QUALIFIER 0x0f
+
+ /* Reset State Interrupts */
+# define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/
+# define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/
+
+ /* Successful Completion Interrupts */
+# define SBIC_CSR_TARGET 0x10 /* reselect complete */
+# define SBIC_CSR_INITIATOR 0x11 /* select complete */
+# define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */
+# define SBIC_CSR_W_ATN 0x14 /* ditto */
+# define SBIC_CSR_XLATED 0x15 /* translate address cmd */
+# define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/
+# define SBIC_CSR_XFERRED 0x18 /* phase in low bits */
+
+ /* Paused or Aborted Interrupts */
+# define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/
+# define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */
+# define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */
+# define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */
+# define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */
+# define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */
+# define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */
+
+ /* Terminated Interrupts */
+# define SBIC_CSR_CMD_INVALID 0x40
+# define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */
+# define SBIC_CSR_SEL_TIMEO 0x42
+# define SBIC_CSR_PE 0x43 /* parity error */
+# define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */
+# define SBIC_CSR_XLATE_TOOBIG 0x45
+# define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */
+# define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */
+# define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */
+
+ /* Service Required Interrupts */
+# define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */
+# define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */
+# define SBIC_CSR_SLT 0x82 /* selected, no ATN */
+# define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */
+# define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */
+# define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */
+# define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */
+# define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */
+
+#define SBIC_PHASE(csr) SCSI_PHASE(csr)
+
+/*
+ * Command register (command codes)
+ */
+
+#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */
+#define SBIC_CMD_MASK 0x7f
+
+ /* Miscellaneous */
+#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */
+#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */
+#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */
+#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */
+#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */
+#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */
+
+ /* Initiator state */
+#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */
+#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */
+#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */
+
+ /* Target state */
+#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */
+#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */
+#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */
+#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */
+#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */
+#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */
+#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */
+#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */
+#define SBIC_CMD_SND 0x17 /* ( T ) lev II */
+
+ /* Disconnected state */
+#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */
+#define SBIC_CMD_SEL 0x07 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */
+#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */
+#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */
+#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */
+#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */
+
+
+/* approximate, but we won't do SBT on selects */
+#define sbic_isa_select(cmd) (((cmd)>0x5)&&((cmd)<0xa))
+
diff --git a/scsi/adapters/scsi_33C93_hdw.c b/scsi/adapters/scsi_33C93_hdw.c
new file mode 100644
index 0000000..169ccbf
--- /dev/null
+++ b/scsi/adapters/scsi_33C93_hdw.c
@@ -0,0 +1,2078 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS AS-IS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_33C93_hdw.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the WD/AMD 33C93
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+#if 0
+DISCLAIMER: THIS DOES NOT EVEN COMPILE YET, it went in by mistake.
+Code that probably makes some sense is from here to "TILL HERE"
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+#include <sbic.h>
+#if NSBIC > 0
+#include <platforms.h>
+
+#ifdef IRIS
+#define PAD(n) char n[3] /* or whatever */
+#define SBIC_MUX_ADDRESSING /* comment out if wrong */
+#define SBIC_CLOCK_FREQUENCY 20 /* FIXME FIXME FIXME */
+#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA /* FIXME FIXME FIXME */
+
+#define SBIC_SET_RST_ADDR /*SCSI_INIT_ADDR*/
+#define SBIC_CLR_RST_ADDR /*SCSI_RDY_ADDR*/
+#define SBIC_MACHINE_RESET_SCSIBUS(regs,per) \
+ { int temp; \
+ temp = *(volatile unsigned int *)SBIC_SET_RST_ADDR; \
+ delay(per); \
+ temp = *(volatile unsigned int *)SBIC_CLR_RST_ADDR; \
+ }
+
+#endif
+
+#include <machine/machspl.h> /* spl definitions */
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+
+#include <scsi/adapters/scsi_33C93.h>
+#include <scsi/scsi_defs.h>
+#include <scsi/adapters/scsi_dma.h>
+
+/*
+ * Spell out all combinations of padded/nopadded and mux/nomux
+ */
+#ifdef PAD
+typedef struct {
+
+ volatile unsigned char sbic_myid; /* rw: My SCSI id */
+/*#define sbic_cdbsize sbic_myid /* w : size of CDB */
+ PAD(pad0)
+ volatile unsigned char sbic_control; /* rw: Control register */
+ PAD(pad1)
+ volatile unsigned char sbic_timeo; /* rw: Timeout period */
+ PAD(pad2)
+ volatile unsigned char sbic_cdb1; /* rw: CDB, 1st byte */
+ PAD(pad3)
+ volatile unsigned char sbic_cdb2; /* rw: CDB, 2nd byte */
+ PAD(pad4)
+ volatile unsigned char sbic_cdb3; /* rw: CDB, 3rd byte */
+ PAD(pad5)
+ volatile unsigned char sbic_cdb4; /* rw: CDB, 4th byte */
+ PAD(pad6)
+ volatile unsigned char sbic_cdb5; /* rw: CDB, 5th byte */
+ PAD(pad7)
+ volatile unsigned char sbic_cdb6; /* rw: CDB, 6th byte */
+ PAD(pad8)
+ volatile unsigned char sbic_cdb7; /* rw: CDB, 7th byte */
+ PAD(pad9)
+ volatile unsigned char sbic_cdb8; /* rw: CDB, 8th byte */
+ PAD(pad10)
+ volatile unsigned char sbic_cdb9; /* rw: CDB, 9th byte */
+ PAD(pad11)
+ volatile unsigned char sbic_cdb10; /* rw: CDB, 10th byte */
+ PAD(pad12)
+ volatile unsigned char sbic_cdb11; /* rw: CDB, 11th byte */
+ PAD(pad13)
+ volatile unsigned char sbic_cdb12; /* rw: CDB, 12th byte */
+ PAD(pad14)
+ volatile unsigned char sbic_tlun; /* rw: Target LUN */
+ PAD(pad15)
+ volatile unsigned char sbic_cmd_phase; /* rw: Command phase */
+ PAD(pad16)
+ volatile unsigned char sbic_syn; /* rw: Synch xfer params */
+ PAD(pad17)
+ volatile unsigned char sbic_count_hi; /* rw: Xfer count, hi */
+ PAD(pad18)
+ volatile unsigned char sbic_count_med; /* rw: Xfer count, med */
+ PAD(pad19)
+ volatile unsigned char sbic_count_lo; /* rw: Xfer count, lo */
+ PAD(pad20)
+ volatile unsigned char sbic_selid; /* rw: Target ID (select) */
+ PAD(pad21)
+ volatile unsigned char sbic_rselid; /* rw: Target ID (reselect) */
+ PAD(pad22)
+ volatile unsigned char sbic_csr; /* r : Status register */
+ PAD(pad23)
+ volatile unsigned char sbic_cmd; /* rw: Command register */
+ PAD(pad24)
+ volatile unsigned char sbic_data; /* rw: FIFO top */
+ PAD(pad25)
+ char u0; /* unused, padding */
+ PAD(pad26)
+ char u1; /* unused, padding */
+ PAD(pad27)
+ char u2; /* unused, padding */
+ PAD(pad28)
+ char u3; /* unused, padding */
+ PAD(pad29)
+ char u4; /* unused, padding */
+ PAD(pad30)
+ volatile unsigned char sbic_asr; /* r : Aux Status Register */
+ PAD(pad31)
+
+} sbic_padded_mux_regmap_t;
+
+typedef struct {
+ volatile unsigned char sbic_asr; /* r : Aux Status Register */
+/*#define sbic_address sbic_asr /* w : desired register no */
+ PAD(pad0);
+ volatile unsigned char sbic_value; /* rw: register value */
+ PAD(pad1);
+} sbic_padded_ind_regmap_t;
+
+#else /* !PAD */
+
+typedef sbic_mux_regmap_t sbic_padded_mux_regmap_t;
+typedef sbic_ind_regmap_t sbic_padded_ind_regmap_t;
+
+#endif /* !PAD */
+
+/*
+ * Could have used some non-ANSIsm in the following :-))
+ */
+#ifdef SBIC_MUX_ADDRESSING
+
+typedef sbic_padded_mux_regmap_t sbic_padded_regmap_t;
+
+#define SET_SBIC_myid(regs,val) (regs)->sbic_myid = (val)
+#define GET_SBIC_myid(regs,val) (val) = (regs)->sbic_myid
+#define SET_SBIC_cdbsize(regs,val) (regs)->sbic_cdbsize = (val)
+#define GET_SBIC_cdbsize(regs,val) (val) = (regs)->sbic_cdbsize
+#define SET_SBIC_control(regs,val) (regs)->sbic_control = (val)
+#define GET_SBIC_control(regs,val) (val) = (regs)->sbic_control
+#define SET_SBIC_timeo(regs,val) (regs)->sbic_timeo = (val)
+#define GET_SBIC_timeo(regs,val) (val) = (regs)->sbic_timeo
+#define SET_SBIC_cdb1(regs,val) (regs)->sbic_cdb1 = (val)
+#define GET_SBIC_cdb1(regs,val) (val) = (regs)->sbic_cdb1
+#define SET_SBIC_cdb2(regs,val) (regs)->sbic_cdb2 = (val)
+#define GET_SBIC_cdb2(regs,val) (val) = (regs)->sbic_cdb2
+#define SET_SBIC_cdb3(regs,val) (regs)->sbic_cdb3 = (val)
+#define GET_SBIC_cdb3(regs,val) (val) = (regs)->sbic_cdb3
+#define SET_SBIC_cdb4(regs,val) (regs)->sbic_cdb4 = (val)
+#define GET_SBIC_cdb4(regs,val) (val) = (regs)->sbic_cdb4
+#define SET_SBIC_cdb5(regs,val) (regs)->sbic_cdb5 = (val)
+#define GET_SBIC_cdb5(regs,val) (val) = (regs)->sbic_cdb5
+#define SET_SBIC_cdb6(regs,val) (regs)->sbic_cdb6 = (val)
+#define GET_SBIC_cdb6(regs,val) (val) = (regs)->sbic_cdb6
+#define SET_SBIC_cdb7(regs,val) (regs)->sbic_cdb7 = (val)
+#define GET_SBIC_cdb7(regs,val) (val) = (regs)->sbic_cdb7
+#define SET_SBIC_cdb8(regs,val) (regs)->sbic_cdb8 = (val)
+#define GET_SBIC_cdb8(regs,val) (val) = (regs)->sbic_cdb8
+#define SET_SBIC_cdb9(regs,val) (regs)->sbic_cdb9 = (val)
+#define GET_SBIC_cdb9(regs,val) (val) = (regs)->sbic_cdb9
+#define SET_SBIC_cdb10(regs,val) (regs)->sbic_cdb10 = (val)
+#define GET_SBIC_cdb10(regs,val) (val) = (regs)->sbic_cdb10
+#define SET_SBIC_cdb11(regs,val) (regs)->sbic_cdb11 = (val)
+#define GET_SBIC_cdb11(regs,val) (val) = (regs)->sbic_cdb11
+#define SET_SBIC_cdb12(regs,val) (regs)->sbic_cdb12 = (val)
+#define GET_SBIC_cdb12(regs,val) (val) = (regs)->sbic_cdb12
+#define SET_SBIC_tlun(regs,val) (regs)->sbic_tlun = (val)
+#define GET_SBIC_tlun(regs,val) (val) = (regs)->sbic_tlun
+#define SET_SBIC_cmd_phase(regs,val) (regs)->sbic_cmd_phase = (val)
+#define GET_SBIC_cmd_phase(regs,val) (val) = (regs)->sbic_cmd_phase
+#define SET_SBIC_syn(regs,val) (regs)->sbic_syn = (val)
+#define GET_SBIC_syn(regs,val) (val) = (regs)->sbic_syn
+#define SET_SBIC_count_hi(regs,val) (regs)->sbic_count_hi = (val)
+#define GET_SBIC_count_hi(regs,val) (val) = (regs)->sbic_count_hi
+#define SET_SBIC_count_med(regs,val) (regs)->sbic_count_med = (val)
+#define GET_SBIC_count_med(regs,val) (val) = (regs)->sbic_count_med
+#define SET_SBIC_count_lo(regs,val) (regs)->sbic_count_lo = (val)
+#define GET_SBIC_count_lo(regs,val) (val) = (regs)->sbic_count_lo
+#define SET_SBIC_selid(regs,val) (regs)->sbic_selid = (val)
+#define GET_SBIC_selid(regs,val) (val) = (regs)->sbic_selid
+#define SET_SBIC_rselid(regs,val) (regs)->sbic_rselid = (val)
+#define GET_SBIC_rselid(regs,val) (val) = (regs)->sbic_rselid
+#define SET_SBIC_csr(regs,val) (regs)->sbic_csr = (val)
+#define GET_SBIC_csr(regs,val) (val) = (regs)->sbic_csr
+#define SET_SBIC_cmd(regs,val) (regs)->sbic_cmd = (val)
+#define GET_SBIC_cmd(regs,val) (val) = (regs)->sbic_cmd
+#define SET_SBIC_data(regs,val) (regs)->sbic_data = (val)
+#define GET_SBIC_data(regs,val) (val) = (regs)->sbic_data
+
+#define SBIC_TC_SET(regs,val) { \
+ (regs)->sbic_count_hi = ((val)>>16)); \
+ (regs)->sbic_count_med = (val)>>8; \
+ (regs)->sbic_count_lo = (val); \
+ }
+#define SBIC_TC_GET(regs,val) { \
+ (val) = ((regs)->sbic_count_hi << 16) | \
+ ((regs)->sbic_count_med << 8) | \
+ ((regs)->sbic_count_lo); \
+ }
+
+#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) { \
+ register char *ptr = (char*)(cmd); \
+ (regs)->cis_cdb1 = *ptr++; \
+ (regs)->cis_cdb2 = *ptr++; \
+ (regs)->cis_cdb3 = *ptr++; \
+ (regs)->cis_cdb4 = *ptr++; \
+ (regs)->cis_cdb5 = *ptr++; \
+ (regs)->cis_cdb6 = *ptr++; \
+ if (cmdsize > 6) { \
+ (regs)->cis_cdb7 = *ptr++; \
+ (regs)->cis_cdb8 = *ptr++; \
+ (regs)->cis_cdb9 = *ptr++; \
+ (regs)->cis_cdb10 = *ptr++; \
+ } \
+ if (cmdsize > 10) { \
+ (regs)->cis_cdb11 = *ptr++; \
+ (regs)->cis_cdb12 = *ptr; \
+ } \
+ }
+
+#else /*SBIC_MUX_ADDRESSING*/
+
+typedef sbic_padded_ind_regmap_t sbic_padded_regmap_t;
+
+#define SET_SBIC_myid(regs,val) sbic_write_reg(regs,SBIC_myid,val)
+#define GET_SBIC_myid(regs,val) sbic_read_reg(regs,SBIC_myid,val)
+#define SET_SBIC_cdbsize(regs,val) sbic_write_reg(regs,SBIC_cdbsize,val)
+#define GET_SBIC_cdbsize(regs,val) sbic_read_reg(regs,SBIC_cdbsize,val)
+#define SET_SBIC_control(regs,val) sbic_write_reg(regs,SBIC_control,val)
+#define GET_SBIC_control(regs,val) sbic_read_reg(regs,SBIC_control,val)
+#define SET_SBIC_timeo(regs,val) sbic_write_reg(regs,SBIC_timeo,val)
+#define GET_SBIC_timeo(regs,val) sbic_read_reg(regs,SBIC_timeo,val)
+#define SET_SBIC_cdb1(regs,val) sbic_write_reg(regs,SBIC_cdb1,val)
+#define GET_SBIC_cdb1(regs,val) sbic_read_reg(regs,SBIC_cdb1,val)
+#define SET_SBIC_cdb2(regs,val) sbic_write_reg(regs,SBIC_cdb2,val)
+#define GET_SBIC_cdb2(regs,val) sbic_read_reg(regs,SBIC_cdb2,val)
+#define SET_SBIC_cdb3(regs,val) sbic_write_reg(regs,SBIC_cdb3,val)
+#define GET_SBIC_cdb3(regs,val) sbic_read_reg(regs,SBIC_cdb3,val)
+#define SET_SBIC_cdb4(regs,val) sbic_write_reg(regs,SBIC_cdb4,val)
+#define GET_SBIC_cdb4(regs,val) sbic_read_reg(regs,SBIC_cdb4,val)
+#define SET_SBIC_cdb5(regs,val) sbic_write_reg(regs,SBIC_cdb5,val)
+#define GET_SBIC_cdb5(regs,val) sbic_read_reg(regs,SBIC_cdb5,val)
+#define SET_SBIC_cdb6(regs,val) sbic_write_reg(regs,SBIC_cdb6,val)
+#define GET_SBIC_cdb6(regs,val) sbic_read_reg(regs,SBIC_cdb6,val)
+#define SET_SBIC_cdb7(regs,val) sbic_write_reg(regs,SBIC_cdb7,val)
+#define GET_SBIC_cdb7(regs,val) sbic_read_reg(regs,SBIC_cdb7,val)
+#define SET_SBIC_cdb8(regs,val) sbic_write_reg(regs,SBIC_cdb8,val)
+#define GET_SBIC_cdb8(regs,val) sbic_read_reg(regs,SBIC_cdb8,val)
+#define SET_SBIC_cdb9(regs,val) sbic_write_reg(regs,SBIC_cdb9,val)
+#define GET_SBIC_cdb9(regs,val) sbic_read_reg(regs,SBIC_cdb9,val)
+#define SET_SBIC_cdb10(regs,val) sbic_write_reg(regs,SBIC_cdb10,val)
+#define GET_SBIC_cdb10(regs,val) sbic_read_reg(regs,SBIC_cdb10,val)
+#define SET_SBIC_cdb11(regs,val) sbic_write_reg(regs,SBIC_cdb11,val)
+#define GET_SBIC_cdb11(regs,val) sbic_read_reg(regs,SBIC_cdb11,val)
+#define SET_SBIC_cdb12(regs,val) sbic_write_reg(regs,SBIC_cdb12,val)
+#define GET_SBIC_cdb12(regs,val) sbic_read_reg(regs,SBIC_cdb12,val)
+#define SET_SBIC_tlun(regs,val) sbic_write_reg(regs,SBIC_tlun,val)
+#define GET_SBIC_tlun(regs,val) sbic_read_reg(regs,SBIC_tlun,val)
+#define SET_SBIC_cmd_phase(regs,val) sbic_write_reg(regs,SBIC_cmd_phase,val)
+#define GET_SBIC_cmd_phase(regs,val) sbic_read_reg(regs,SBIC_cmd_phase,val)
+#define SET_SBIC_syn(regs,val) sbic_write_reg(regs,SBIC_syn,val)
+#define GET_SBIC_syn(regs,val) sbic_read_reg(regs,SBIC_syn,val)
+#define SET_SBIC_count_hi(regs,val) sbic_write_reg(regs,SBIC_count_hi,val)
+#define GET_SBIC_count_hi(regs,val) sbic_read_reg(regs,SBIC_count_hi,val)
+#define SET_SBIC_count_med(regs,val) sbic_write_reg(regs,SBIC_count_med,val)
+#define GET_SBIC_count_med(regs,val) sbic_read_reg(regs,SBIC_count_med,val)
+#define SET_SBIC_count_lo(regs,val) sbic_write_reg(regs,SBIC_count_lo,val)
+#define GET_SBIC_count_lo(regs,val) sbic_read_reg(regs,SBIC_count_lo,val)
+#define SET_SBIC_selid(regs,val) sbic_write_reg(regs,SBIC_selid,val)
+#define GET_SBIC_selid(regs,val) sbic_read_reg(regs,SBIC_selid,val)
+#define SET_SBIC_rselid(regs,val) sbic_write_reg(regs,SBIC_rselid,val)
+#define GET_SBIC_rselid(regs,val) sbic_read_reg(regs,SBIC_rselid,val)
+#define SET_SBIC_csr(regs,val) sbic_write_reg(regs,SBIC_csr,val)
+#define GET_SBIC_csr(regs,val) sbic_read_reg(regs,SBIC_csr,val)
+#define SET_SBIC_cmd(regs,val) sbic_write_reg(regs,SBIC_cmd,val)
+#define GET_SBIC_cmd(regs,val) sbic_read_reg(regs,SBIC_cmd,val)
+#define SET_SBIC_data(regs,val) sbic_write_reg(regs,SBIC_data,val)
+#define GET_SBIC_data(regs,val) sbic_read_reg(regs,SBIC_data,val)
+
+#define SBIC_TC_SET(regs,val) { \
+ sbic_write_reg(regs,SBIC_count_hi,((val)>>16)); \
+ (regs)->sbic_value = (val)>>8; wbflush(); \
+ (regs)->sbic_value = (val); \
+ }
+#define SBIC_TC_GET(regs,val) { \
+ sbic_read_reg(regs,SBIC_count_hi,(val)); \
+ (val) = ((val)<<8) | (regs)->sbic_value; \
+ (val) = ((val)<<8) | (regs)->sbic_value; \
+ }
+
+#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) {
+ register int n=cmdsize-1; \
+ register char *ptr = (char*)(cmd); \
+ sbic_write_reg(regs,SBIC_cdb1,*ptr++); \
+ while (n-- > 0) (regs)->sbic_value = *ptr++; \
+ }
+
+#endif /*SBIC_MUX_ADDRESSING*/
+
+#define GET_SBIC_asr(regs,val) (val) = (regs)->sbic_asr
+
+
+/*
+ * If all goes well (cross fingers) the typical read/write operation
+ * should complete in just one interrupt. Therefore our scripts
+ * have only two parts: a pre-condition and an action. The first
+ * triggers error handling if not satisfied and in our case it is a match
+ * of ....
+ * The action part is just a function pointer, invoked in a standard way.
+ * The script proceeds only if the action routine returns TRUE.
+ * See sbic_intr() for how and where this is all done.
+ */
+
+typedef struct script {
+ struct { /* expected state at interrupt: */
+ unsigned char csr; /* interrupt cause */
+ unsigned char pha; /* command phase */
+ } condition;
+/* unsigned char unused[2]; /* unused padding */
+ boolean_t (*action)(); /* extra operations */
+} *script_t;
+
+/* Matching on the condition value */
+#define ANY 0xff
+#define SCRIPT_MATCH(csr,pha,cond) \
+ (((cond).csr == (csr)) && \
+ (((cond).pha == (pha)) || ((cond).pha==ANY)))
+
+
+/* forward decls of script actions */
+boolean_t
+ sbic_end(), /* all come to an end */
+ sbic_get_status(), /* get status from target */
+ sbic_dma_in(), /* get data from target via dma */
+ sbic_dma_in_r(), /* get data from target via dma (restartable)*/
+ sbic_dma_out(), /* send data to target via dma */
+ sbic_dma_out_r(), /* send data to target via dma (restartable) */
+ sbic_dosynch(), /* negotiate synch xfer */
+ sbic_msg_in(), /* receive the disconenct message */
+ sbic_disconnected(), /* target has disconnected */
+ sbic_reconnect(); /* target reconnected */
+
+/* forward decls of error handlers */
+boolean_t
+ sbic_err_generic(), /* generic handler */
+ sbic_err_disconn(), /* target disconnects amidst */
+ gimmeabreak(); /* drop into the debugger */
+
+int sbic_reset_scsibus();
+boolean_t sbic_probe_target();
+static sbic_wait();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) SCSI-33c93 interface
+ */
+struct sbic_softc {
+ watchdog_t wd;
+ sbic_padded_regmap_t *regs; /* 33c93 registers */
+
+ scsi_dma_ops_t *dma_ops; /* DMA operations and state */
+ opaque_t dma_state;
+
+ script_t script; /* what should happen next */
+ boolean_t (*error_handler)();/* what if something is wrong */
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define SBIC_STATE_BUSY 0x01 /* selecting or currently connected */
+#define SBIC_STATE_TARGET 0x04 /* currently selected as target */
+#define SBIC_STATE_COLLISION 0x08 /* lost selection attempt */
+#define SBIC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+#define SBIC_STATE_AM_MODE 0x20 /* 33c93A with advanced mode (AM) */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+ unsigned char unused;
+
+ scsi_softc_t *sc; /* HBA-indep info */
+ target_info_t *active_target; /* the current one */
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+
+} sbic_softc_data[NSBIC];
+
+typedef struct sbic_softc *sbic_softc_t;
+
+sbic_softc_t sbic_softc[NSBIC];
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
+int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
+
+int sbic_to_scsi_period(regs,a)
+{
+ unsigned int fs;
+
+ /* cycle = DIV / (2*CLK) */
+ /* DIV = FS+2 */
+ /* best we can do is 200ns at 20Mhz, 2 cycles */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */
+ if (a < 2) a = 8; /* map to Cycles */
+ return ((fs*a)>>2); /* in 4 ns units */
+}
+
+int scsi_period_to_sbic(regs,p)
+{
+ register unsigned int fs;
+
+ /* Just the inverse of the above */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */
+
+ ret = p << 2; /* in ns units */
+ ret = ret / fs; /* in Cycles */
+ if (ret < sbic_min_period)
+ return sbic_min_period;
+ /* verify rounding */
+ if (sbic_to_scsi_period(regs,ret) < p)
+ ret++;
+ return (ret >= 8) ? 0 : ret;
+}
+
+#define u_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int sbic_probe(), scsi_slave(), scsi_attach(), sbic_go(), sbic_intr();
+
+caddr_t sbic_std[NSBIC] = { 0 };
+struct bus_device *sbic_dinfo[NSBIC*8];
+struct bus_ctlr *sbic_minfo[NSBIC];
+struct bus_driver sbic_driver =
+ { sbic_probe, scsi_slave, scsi_attach, sbic_go, sbic_std, "rz", sbic_dinfo,
+ "sbic", sbic_minfo, BUS_INTR_B4_PROBE};
+
+
+sbic_set_dmaops(unit, dmaops)
+ unsigned int unit;
+ scsi_dma_ops_t *dmaops;
+{
+ if (unit < NSBIC)
+ sbic_std[unit] = (caddr_t)dmaops;
+}
+
+/*
+ * Scripts
+ */
+struct script
+sbic_script_any_cmd[] = { /* started with SEL & XFER */
+ {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status},
+},
+
+sbic_script_try_synch[] = { /* started with SEL */
+ {{SBIC_CSR_INITIATOR, ANY}, sbic_dosynch},
+ {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status},
+};
+
+
+#define DEBUG
+#ifdef DEBUG
+
+#define PRINT(x) if (scsi_debug) printf x
+
+sbic_state(regs, overrule)
+ sbic_padded_regmap_t *regs;
+{
+ register unsigned char asr,tmp;
+
+ if (regs == 0) {
+ if (sbic_softc[0])
+ regs = sbic_softc[0]->regs;
+ else
+ regs = (sbic_padded_regmap_t*)0xXXXXXXXX;
+ }
+
+ GET_SBIC_asr(regs,asr);
+
+ if ((asr & SBIC_ASR_BSY) && !overrule)
+ db_printf("-BUSY- ");
+ else {
+ unsigned char tlun,pha,selid,rselid;
+ unsigned int cnt;
+ GET_SBIC_tlun(regs,tlun);
+ GET_SBIC_cmd_phase(regs,pha);
+ GET_SBIC_selid(regs,selid);
+ GET_SBIC_rselid(regs,rselid);
+ SBIC_TC_GET(regs,cnt);
+ db_printf("tc %x tlun %x sel %x rsel %x pha %x ",
+ cnt, tlun, selid, rselid, pha);
+ }
+
+ if (asr & SBIC_ASR_INT)
+ db_printf("-INT- ");
+ else {
+ GET_SBIC_csr(regs,tmp);
+ db_printf("csr %x ", tmp);
+ }
+
+ if (asr & SBIC_ASR_CIP)
+ db_printf("-CIP-\n");
+ else {
+ GET_SBIC_cmd(regs,tmp);
+ db_printf("cmd %x\n", tmp);
+ }
+ return 0;
+}
+
+sbic_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = sbic_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n",
+ tgt,
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym(spt,1);
+ db_printf(": %x %x ", spt->condition.csr, spt->condition.pha);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+sbic_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = sbic_softc[unit]->sc->target[i];
+ if (tgt)
+ sbic_target_state(tgt);
+ }
+}
+
+sbic_script_state(unit)
+{
+ script_t spt = sbic_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym(spt,1);
+ db_printf(": %x %x ", spt->condition.csr, spt->condition.pha);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(sbic_softc[unit]->error_handler, 1);
+ return 0;
+}
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int sbic_logpt;
+char sbic_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x1e
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ sbic_log[sbic_logpt++] = (e);
+ if (sbic_logpt == LOGSIZE) sbic_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+sbic_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = sbic_logpt; i < LOGSIZE; i++) {
+ c = sbic_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-x%x", c & 0x7f);
+ }
+}
+
+sbic_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /*TRACE*/
+#define LOG(e,f)
+#define LOGSIZE
+#endif /*TRACE*/
+
+#else /*DEBUG*/
+#define PRINT(x)
+#define LOG(e,f)
+#define LOGSIZE
+
+#endif /*DEBUG*/
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send a test-unit-ready to each possible target on the bus
+ * except of course ourselves.
+ */
+sbic_probe(reg, ui)
+ unsigned reg;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ sbic_softc_t sbic = &sbic_softc_data[unit];
+ int target_id;
+ scsi_softc_t *sc;
+ register sbic_padded_regmap_t *regs;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+
+ /*
+ * We are only called if the right board is there,
+ * but make sure anyways..
+ */
+ if (check_memory(reg, 0))
+ return 0;
+
+#if MAPPABLE
+ /* Mappable version side */
+ SBIC_probe(reg, ui);
+#endif /*MAPPABLE*/
+
+ /*
+ * Initialize hw descriptor, cache some pointers
+ */
+ sbic_softc[unit] = sbic;
+ sbic->regs = (sbic_padded_regmap_t *) (reg);
+
+ if ((sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[unit]) == 0)
+ /* use same as unit 0 if undefined */
+ sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[0];
+
+ sbic->dma_state = (*sbic->dma_ops->init)(unit, reg);
+
+ queue_init(&sbic->waiting_targets);
+
+ sc = scsi_master_alloc(unit, sbic);
+ sbic->sc = sc;
+
+ sc->go = sbic_go;
+ sc->watchdog = scsi_watchdog;
+ sc->probe = sbic_probe_target;
+ sbic->wd.reset = sbic_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1;
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ regs = sbic->regs;
+
+ /*
+ * Reset chip, fully. Note that interrupts are already enabled.
+ */
+ s = splbio();
+ if (sbic_reset(regs, TRUE))
+ sbic->state |= SBIC_STATE_AM_MODE;
+
+ /*
+ * Our SCSI id on the bus.
+ * The user can probably set this via the prom.
+ * If not, it is easy to fix: make a default that
+ * can be changed as boot arg. Otherwise we keep
+ * what the prom used.
+ */
+#ifdef unneeded
+ SET_SBIC_myid(regs, (scsi_initiator_id[unit] & 0x7));
+ sbic_reset(regs, TRUE);
+#endif
+ GET_SBIC_myid(regs,sc->initiator_id);
+ sc->initiator_id &= 0x7;
+ printf("%s%d: SCSI id %d", ui->name, unit, sc->initiator_id);
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ for (target_id = 0; target_id < 8; target_id++) {
+ register unsigned char asr, csr, pha;
+ register scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ SBIC_TC_SET(regs,0);
+ SET_SBIC_selid(regs,target_id);
+ SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY));
+
+ /*
+ * See if the unit is ready.
+ * XXX SHOULD inquiry LUN 0 instead !!!
+ */
+ {
+ scsi_command_test_unit_ready_y c;
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY;
+ SBIC_LOAD_COMMAND(regs,&c,sizeof(c));
+ }
+
+ /* select and send it */
+ SET_SBIC_cmd(regs,SBIC_CMD_SEL_XFER);
+
+ /* wait for the chip to complete, or timeout */
+ asr = sbic_wait(regs, SBIC_ASR_INT);
+ GET_SBIC_csr(regs,csr);
+
+ /*
+ * Check if the select timed out
+ */
+ GET_SBIC_cmd_phase(regs,pha);
+ if ((SBIC_CPH(pha) == 0) && (csr & SBIC_CSR_CMD_ERR)) {
+ /* noone out there */
+#if notsure
+ SET_SBIC_cmd(regs,SBIC_CMD_DISC);
+ asr = sbic_wait(regs, SBIC_ASR_INT);
+ GET_SBIC_csr(regs,csr);
+#endif
+ continue;
+ }
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ if (SBIC_CPH(pha) < 0x60)
+ /* XXX recover by hand XXX */
+ panic(" target acts weirdo");
+
+ GET_SBIC_tlun(regs,status.bits);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD)
+ scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
+
+ /*
+ * Found a target
+ */
+ sbic->ntargets++;
+ {
+ register target_info_t *tgt;
+ tgt = scsi_slave_alloc(sc->masterno, target_id, sbic);
+
+ (*sbic->dma_ops->new_target)(sbic->dma_state, tgt);
+ }
+ }
+
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+sbic_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ sbic_softc_t sbic = sbic_softc[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ (*sbic->dma_ops->new_target)(sbic->dma_state, tgt);
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+static sbic_wait(regs, until)
+ sbic_padded_regmap_t *regs;
+ char until;
+{
+ register unsigned char val;
+ int timeo = 1000000;
+
+ GET_SBIC_asr(regs,val);
+ while ((val & until) == 0) {
+ if (!timeo--) {
+ printf("sbic_wait TIMEO with x%x\n", regs->sbic_csr);
+ break;
+ }
+ delay(1);
+ GET_SBIC_asr(regs,val);
+ }
+ return val;
+}
+
+boolean_t
+sbic_reset(regs, quick)
+ sbic_padded_regmap_t *regs;
+{
+ char my_id, csr;
+
+ /* preserve our ID for now */
+ GET_SBIC_myid(regs,my_id);
+ my_id &= SBIC_ID_MASK;
+
+ if (SBIC_CLOCK_FREQUENCY < 11)
+ my_id |= SBIC_ID_FS_8_10;
+ else if (SBIC_CLOCK_FREQUENCY < 16)
+ my_id |= SBIC_ID_FS_12_15;
+ else if (SBIC_CLOCK_FREQUENCY < 21)
+ my_id |= SBIC_ID_FS_16_20;
+
+ my_id |= SBIC_ID_EAF|SBIC_ID_EHP;
+
+ SET_SBIC_myid(regs,myid);
+ wbflush();
+
+ /*
+ * Reset chip and wait till done
+ */
+ SET_SBIC_cmd(regs,SBIC_CMD_RESET);
+ delay(25);
+
+ (void) sbic_wait(regs, SBIC_ASR_INT);
+ GET_SBIC_csr(regs,csr); /* clears interrupt also */
+
+ /*
+ * Set up various chip parameters
+ */
+ SET_SBIC_control(regs, SBIC_CTL_HHP|SBIC_CTL_EDI|SBIC_CTL_HSP|
+ SBIC_MACHINE_DMA_MODE);
+ /* will do IDI on the fly */
+ SET_SBIC_rselid(regs, SBIC_RID_ER|SBIC_RID_ES|SBIC_RID_DSP);
+ SET_SBIC_syn(regs,SBIC_SYN(0,sbic_min_period)); /* asynch for now */
+
+ /* anything else was zeroed by reset */
+
+ if (quick)
+ return (csr & SBIC_CSR_RESET_AM);
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call sbic_bus_reset().
+ */
+ /*
+ * Now HOW do I do this ? I just want to drive the SCSI "RST"
+ * signal true for about 25 usecs; But the chip has no notion
+ * of such a signal at all. The spec suggest that the chip's
+ * reset pin be connected to the RST signal, which makes this
+ * operation a machdep one.
+ */
+ SBIC_MACHINE_RESET_SCSIBUS(regs, 30);
+
+ return (csr & SBIC_CSR_RESET_AM);
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+sbic_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ sbic_softc_t sbic;
+ register spl_t s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+
+ LOG(1,"go");
+
+ sbic = (sbic_softc_t)tgt->hw_state;
+
+ tgt->transient_state.cmd_count = cmd_count; /* keep it here */
+
+ (*sbic->dma_ops->map)(sbic->dma_state, tgt);
+
+ disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id);
+ disconn = disconn && (sbic->ntargets > 1);
+ disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id);
+
+ /*
+ * Setup target state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ handler = (disconn) ? sbic_err_disconn : sbic_err_generic;
+ scp = sbic_script_any_cmd;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(2,"readop");
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x1a,"writeop");
+ break;
+ case SCSI_CMD_INQUIRY:
+ /* This is likely the first thing out:
+ do the synch neg if so */
+ if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
+ scp = sbic_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ break;
+ }
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ tgt->transient_state.cmd_count = sizeof(scsi_command_group_0);
+ tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0);
+ /* fall through */
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ /*
+ * Do the synch negotiation here, unless prohibited
+ * or done already
+ */
+ if ( ! (tgt->flags & TGT_DID_SYNCH)) {
+ scp = sbic_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ cmd_only = FALSE;
+ }
+ /* fall through */
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ }
+
+ tgt->transient_state.script = scp;
+ tgt->transient_state.handler = handler;
+ tgt->transient_state.identify = (cmd_only) ? 0xff :
+ (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
+ SCSI_IDENTIFY);
+
+ if (in_count)
+ tgt->transient_state.in_count =
+ (in_count < tgt->block_size) ? tgt->block_size : in_count;
+ else
+ tgt->transient_state.in_count = 0;
+
+ /*
+ * See if another target is currently selected on
+ * this SCSI bus, e.g. lock the sbic structure.
+ * Note that it is the strategy routine's job
+ * to serialize ops on the same target as appropriate.
+ * XXX here and everywhere, locks!
+ */
+ /*
+ * Protection viz reconnections makes it tricky.
+ */
+ s = splbio();
+
+ if (sbic->wd.nactive++ == 0)
+ sbic->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (sbic->state & SBIC_STATE_BUSY) {
+ /*
+ * Queue up this target, note that this takes care
+ * of proper FIFO scheduling of the scsi-bus.
+ */
+ LOG(3,"enqueue");
+ enqueue_tail(&sbic->waiting_targets, (queue_entry_t) tgt);
+ } else {
+ /*
+ * It is down to at most two contenders now,
+ * we will treat reconnections same as selections
+ * and let the scsi-bus arbitration process decide.
+ */
+ sbic->state |= SBIC_STATE_BUSY;
+ sbic->next_target = tgt;
+ sbic_attempt_selection(sbic);
+ /*
+ * Note that we might still lose arbitration..
+ */
+ }
+ splx(s);
+}
+
+sbic_attempt_selection(sbic)
+ sbic_softc_t sbic;
+{
+ target_info_t *tgt;
+ sbic_padded_regmap_t *regs;
+ register unsigned char val;
+ register int out_count;
+
+ regs = sbic->regs;
+ tgt = sbic->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * We own the bus now.. unless we lose arbitration
+ */
+ sbic->active_target = tgt;
+
+ /* Try to avoid reselect collisions */
+ GET_SBIC_asr(regs,val);
+ if (val & SBIC_ASR_INT)
+ return;
+
+ /*
+ * Init bus state variables
+ */
+ sbic->script = tgt->transient_state.script;
+ sbic->error_handler = tgt->transient_state.handler;
+ sbic->done = SCSI_RET_IN_PROGRESS;
+
+ sbic->out_count = 0;
+ sbic->in_count = 0;
+
+ /* Define how the identify msg should be built */
+ GET_SBIC_rselid(regs, val);
+ val &= ~(SBIC_RID_MASK|SBIC_RID_ER);
+ /* the enable reselection bit is used to build the identify msg */
+ if (tgt->transient_state.identify != 0xff)
+ val |= (tgt->transient_state.identify & SCSI_IFY_ENABLE_DISCONNECT) << 1;
+ SET_SBIC_rselid(regs, val);
+ SET_SBIC_tlun(regs, tgt->lun);
+
+ /*
+ * Start the chip going
+ */
+ out_count = (*sbic->dma_ops->start_cmd)(sbic->dma_state, tgt);
+ SBIC_TC_PUT(regs, out_count);
+
+ val = tgt->target_id;
+ if (tgt->transient_state.in_count)
+ val |= SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid(regs, val);
+
+ SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY));
+
+ SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period));
+
+ /* ugly little help for compiler */
+#define command out_count
+ if (tgt->flags & TGT_DID_SYNCH) {
+ command = (tgt->transient_state.identify == 0xff) ?
+ SBIC_CMD_SEL_XFER :
+ SBIC_CMD_SEL_ATN_XFER; /*preferred*/
+ } else if (tgt->flags & TGT_TRY_SYNCH)
+ command = SBIC_CMD_SEL_ATN;
+ else
+ command = SBIC_CMD_SEL_XFER;
+
+ /* load 10 bytes anyways, the chip knows how much to use */
+ SBIC_LOAD_COMMAND(regs, tgt->cmd_ptr, 10);
+
+ /* Try to avoid reselect collisions */
+ GET_SBIC_asr(regs,val);
+ if (val & SBIC_ASR_INT)
+ return;
+
+ SET_SBIC_cmd_phase(regs, 0); /* not a resume */
+ SET_SBIC_cmd(regs, command);
+#undef command
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+sbic_intr(unit, spllevel)
+ spl_t spllevel;
+{
+ register sbic_softc_t sbic;
+ register script_t scp;
+ register int asr, csr, pha;
+ register sbic_padded_regmap_t *regs;
+#if MAPPABLE
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return SBIC_intr(unit,spllevel);
+#endif /*MAPPABLE*/
+
+ sbic = sbic_softc[unit];
+ regs = sbic->regs;
+
+ LOG(5,"\n\tintr");
+
+ /* drop spurious interrupts */
+ GET_SBIC_asr(regs, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ return;
+
+ /* collect ephemeral information */
+ GET_SBIC_cmd_phase(regs, pha);
+ GET_SBIC_csr(regs, csr);
+
+TR(csr);TR(asr);TR(pha);TRCHECK
+
+ /* XXX verify this is indeed the case for a SCSI RST asserted */
+ if ((csr & SBIC_CSR_CAUSE) == SBIC_CSR_RESET)
+ return sbic_bus_reset(sbic);
+
+ /* we got an interrupt allright */
+ if (sbic->active_target)
+ sbic->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ splx(spllevel); /* drop priority */
+
+ if ((sbic->state & SBIC_STATE_TARGET) ||
+ (csr == SBIC_CSR_RSLT_AM) || (csr == SBIC_CSR_RSLT_NOAM) ||
+ (csr == SBIC_CSR_SLT) || (csr == SBIC_CSR_SLT_ATN))
+ return sbic_target_intr(sbic);
+
+ /*
+ * In attempt_selection() we gave the select command even if
+ * the chip might have been reconnected already.
+ */
+ if ((csr == SBIC_CSR_RSLT_NI) || (csr == SBIC_CSR_RSLT_IFY))
+ return sbic_reconnect(sbic, csr, pha);
+
+ /*
+ * Check for parity errors
+ */
+ if (asr & SBIC_ASR_PE) {
+ char *msg;
+printf("{PE %x,%x}", asr, pha);
+
+ msg = "SCSI bus parity error";
+ /* all we can do is to throw a reset on the bus */
+ printf( "sbic%d: %s%s", sbic - sbic_softc_data, msg,
+ ", attempting recovery.\n");
+ sbic_reset(regs, FALSE);
+ return;
+ }
+
+ if ((scp = sbic->script) == 0) /* sanity */
+ return;
+
+ LOG(6,"match");
+ if (SCRIPT_MATCH(csr,pha,scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(sbic, csr, pha)) {
+ sbic->script = scp + 1;
+ }
+ } else
+ return (*sbic->error_handler)(sbic, csr, pha);
+}
+
+sbic_target_intr()
+{
+ panic("SBIC: TARGET MODE !!!\n");
+}
+
+/*
+ * Routines that the interrupt code might switch to
+ */
+
+boolean_t
+sbic_end(sbic, csr, pha)
+ register sbic_softc_t sbic;
+{
+ register target_info_t *tgt;
+ register io_req_t ior;
+
+ LOG(8,"end");
+
+ tgt = sbic->active_target;
+ if ((tgt->done = sbic->done) == SCSI_RET_IN_PROGRESS)
+ tgt->done = SCSI_RET_SUCCESS;
+
+ sbic->script = 0;
+
+ if (sbic->wd.nactive-- == 1)
+ sbic->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+ sbic_release_bus(sbic);
+
+ if (ior = tgt->ior) {
+ (*sbic->dma_ops->end_cmd)(sbic->dma_state, tgt, ior);
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+
+ return FALSE;
+}
+
+boolean_t
+sbic_release_bus(sbic)
+ register sbic_softc_t sbic;
+{
+ boolean_t ret = TRUE;
+
+ LOG(9,"release");
+ if (sbic->state & SBIC_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ sbic->state &= ~SBIC_STATE_COLLISION;
+ sbic_attempt_selection(sbic);
+
+ } else if (queue_empty(&sbic->waiting_targets)) {
+
+ sbic->state &= ~SBIC_STATE_BUSY;
+ sbic->active_target = 0;
+ sbic->script = 0;
+ ret = FALSE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ sbic->next_target = (target_info_t *)
+ dequeue_head(&sbic->waiting_targets);
+ sbic_attempt_selection(sbic);
+ }
+ return ret;
+}
+
+boolean_t
+sbic_get_status(sbic, csr, pha)
+ register sbic_softc_t sbic;
+{
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register scsi2_status_byte_t status;
+ int len;
+ io_req_t ior;
+ register target_info_t *tgt = sbic->active_target;
+
+ LOG(0xD,"get_status");
+TRWRAP
+
+ sbic->state &= ~SBIC_STATE_DMA_IN;
+
+ /*
+ * Get the status byte
+ */
+ GET_SBIC_tlun(regs, status.bits);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(sbic->active_target, SCSI_ERR_STATUS, status.bits, 0);
+ sbic->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ sbic->done = SCSI_RET_SUCCESS;
+
+ /* Tell DMA engine we are done */
+ (*sbic->dma_ops->end_xfer)(sbic->dma_state, tgt, tgt->transient_state.in_count);
+
+ return sbic_end(sbic, csr, pha);
+
+}
+
+#if 0
+
+boolean_t
+sbic_dma_in(sbic, csr, ir)
+ register sbic_softc_t sbic;
+{
+ register target_info_t *tgt;
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register int count;
+ unsigned char ff = regs->sbic_flags;
+
+ LOG(0xE,"dma_in");
+ tgt = sbic->active_target;
+
+ sbic->state |= SBIC_STATE_DMA_IN;
+
+ count = (*sbic->dma_ops->start_datain)(sbic->dma_state, tgt);
+ SBIC_TC_PUT(regs, count);
+
+ if ((sbic->in_count = count) == tgt->transient_state.in_count)
+ return TRUE;
+ regs->sbic_cmd = sbic->script->command;
+ sbic->script = sbic_script_restart_data_in;
+ return FALSE;
+}
+
+sbic_dma_in_r(sbic, csr, ir)
+ register sbic_softc_t sbic;
+{
+ register target_info_t *tgt;
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register int count;
+ boolean_t advance_script = TRUE;
+
+
+ LOG(0xE,"dma_in");
+ tgt = sbic->active_target;
+
+ sbic->state |= SBIC_STATE_DMA_IN;
+
+ if (sbic->in_count == 0) {
+ /*
+ * Got nothing yet, we just reconnected.
+ */
+ register int avail;
+
+ /*
+ * Rather than using the messy RFB bit in cnfg2
+ * (which only works for synch xfer anyways)
+ * we just bump up the dma offset. We might
+ * endup with one more interrupt at the end,
+ * so what.
+ * This is done in sbic_err_disconn(), this
+ * way dma (of msg bytes too) is always aligned
+ */
+
+ count = (*sbic->dma_ops->restart_datain_1)
+ (sbic->dma_state, tgt);
+
+ /* common case of 8k-or-less read ? */
+ advance_script = (tgt->transient_state.in_count == count);
+
+ } else {
+
+ /*
+ * We received some data.
+ */
+ register int offset, xferred;
+
+ /*
+ * Problem: sometimes we get a 'spurious' interrupt
+ * right after a reconnect. It goes like disconnect,
+ * reconnect, dma_in_r, here but DMA is still rolling.
+ * Since there is no good reason we got here to begin with
+ * we just check for the case and dismiss it: we should
+ * get another interrupt when the TC goes to zero or the
+ * target disconnects.
+ */
+ SBIC_TC_GET(regs,xferred);
+ if (xferred != 0)
+ return FALSE;
+
+ xferred = sbic->in_count - xferred;
+ assert(xferred > 0);
+
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+
+ count = (*sbic->dma_ops->restart_datain_2)
+ (sbic->dma_state, tgt, xferred);
+
+ sbic->in_count = count;
+ SBIC_TC_PUT(regs, count);
+ regs->sbic_cmd = sbic->script->command;
+
+ (*sbic->dma_ops->restart_datain_3)
+ (sbic->dma_state, tgt);
+
+ /* last chunk ? */
+ if (count == tgt->transient_state.in_count)
+ sbic->script++;
+
+ return FALSE;
+ }
+
+ sbic->in_count = count;
+ SBIC_TC_PUT(regs, count);
+
+ if (!advance_script) {
+ regs->sbic_cmd = sbic->script->command;
+ }
+ return advance_script;
+}
+
+
+/* send data to target. Only called to start the xfer */
+
+boolean_t
+sbic_dma_out(sbic, csr, ir)
+ register sbic_softc_t sbic;
+{
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register int reload_count;
+ register target_info_t *tgt;
+ int command;
+
+ LOG(0xF,"dma_out");
+
+ SBIC_TC_GET(regs, reload_count);
+ sbic->extra_count = regs->sbic_flags & SBIC_FLAGS_FIFO_CNT;
+ reload_count += sbic->extra_count;
+ SBIC_TC_PUT(regs, reload_count);
+ sbic->state &= ~SBIC_STATE_DMA_IN;
+
+ tgt = sbic->active_target;
+
+ command = sbic->script->command;
+
+ if ((sbic->out_count = reload_count) >=
+ tgt->transient_state.out_count)
+ sbic->script++;
+ else
+ sbic->script = sbic_script_restart_data_out;
+
+ if ((*sbic->dma_ops->start_dataout)
+ (sbic->dma_state, tgt, &regs->sbic_cmd, command)) {
+ regs->sbic_cmd = command;
+ }
+
+ return FALSE;
+}
+
+/* send data to target. Called in two different ways:
+ (a) to restart a big transfer and
+ (b) after reconnection
+ */
+boolean_t
+sbic_dma_out_r(sbic, csr, ir)
+ register sbic_softc_t sbic;
+{
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register target_info_t *tgt;
+ boolean_t advance_script = TRUE;
+ int count;
+
+
+ LOG(0xF,"dma_out");
+
+ tgt = sbic->active_target;
+ sbic->state &= ~SBIC_STATE_DMA_IN;
+
+ if (sbic->out_count == 0) {
+ /*
+ * Nothing committed: we just got reconnected
+ */
+ count = (*sbic->dma_ops->restart_dataout_1)
+ (sbic->dma_state, tgt);
+
+ /* is this the last chunk ? */
+ advance_script = (tgt->transient_state.out_count == count);
+ } else {
+ /*
+ * We sent some data.
+ */
+ register int offset, xferred;
+
+ SBIC_TC_GET(regs,count);
+
+ /* see comment above */
+ if (count) {
+ return FALSE;
+ }
+
+ count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT);
+ count -= sbic->extra_count;
+ xferred = sbic->out_count - count;
+ assert(xferred > 0);
+
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ count = (*sbic->dma_ops->restart_dataout_2)
+ (sbic->dma_state, tgt, xferred);
+
+ /* last chunk ? */
+ if (tgt->transient_state.out_count == count)
+ goto quickie;
+
+ sbic->out_count = count;
+
+ sbic->extra_count = (*sbic->dma_ops->restart_dataout_3)
+ (sbic->dma_state, tgt, &regs->sbic_fifo);
+ SBIC_TC_PUT(regs, count);
+ regs->sbic_cmd = sbic->script->command;
+
+ (*sbic->dma_ops->restart_dataout_4)(sbic->dma_state, tgt);
+
+ return FALSE;
+ }
+
+quickie:
+ sbic->extra_count = (*sbic->dma_ops->restart_dataout_3)
+ (sbic->dma_state, tgt, &regs->sbic_fifo);
+
+ sbic->out_count = count;
+
+ SBIC_TC_PUT(regs, count);
+
+ if (!advance_script) {
+ regs->sbic_cmd = sbic->script->command;
+ }
+ return advance_script;
+}
+#endif /*0*/
+
+boolean_t
+sbic_dosynch(sbic, csr, pha)
+ register sbic_softc_t sbic;
+ register unsigned char csr, pha;
+{
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register unsigned char c;
+ int i, per, offs;
+ register target_info_t *tgt;
+
+ /*
+ * Try synch negotiation
+ * Phase is MSG_OUT here.
+ */
+ tgt = sbic->active_target;
+
+#if 0
+ regs->sbic_cmd = SBIC_CMD_FLUSH;
+ delay(2);
+
+ per = sbic_min_period;
+ if (BGET(scsi_no_synchronous_xfer,sbic->sc->masterno,tgt->target_id))
+ offs = 0;
+ else
+ offs = sbic_max_offset;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ regs->sbic_fifo = SCSI_EXTENDED_MESSAGE;
+ regs->sbic_fifo = 3;
+ regs->sbic_fifo = SCSI_SYNC_XFER_REQUEST;
+ regs->sbic_fifo = sbic_to_scsi_period(regs,sbic_min_period);
+ regs->sbic_fifo = offs;
+ regs->sbic_cmd = SBIC_CMD_XFER_INFO;
+ csr = sbic_wait(regs, SBIC_CSR_INT);
+ ir = regs->sbic_intr;
+
+ if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN)
+ gimmeabreak();
+
+ regs->sbic_cmd = SBIC_CMD_XFER_INFO;
+ csr = sbic_wait(regs, SBIC_CSR_INT);
+ ir = regs->sbic_intr;
+
+ while ((regs->sbic_flags & SBIC_FLAGS_FIFO_CNT) > 0)
+ c = regs->sbic_fifo; /* see what it says */
+
+ if (c == SCSI_MESSAGE_REJECT) {
+ printf(" did not like SYNCH xfer ");
+
+ /* Tk50s get in trouble with ATN, sigh. */
+ regs->sbic_cmd = SBIC_CMD_CLR_ATN;
+
+ goto cmd;
+ }
+
+ /*
+ * Receive the rest of the message
+ */
+ regs->sbic_cmd = SBIC_CMD_MSG_ACPT;
+ sbic_wait(regs, SBIC_CSR_INT);
+ ir = regs->sbic_intr;
+
+ if (c != SCSI_EXTENDED_MESSAGE)
+ gimmeabreak();
+
+ regs->sbic_cmd = SBIC_CMD_XFER_INFO;
+ sbic_wait(regs, SBIC_CSR_INT);
+ c = regs->sbic_intr;
+ if (regs->sbic_fifo != 3)
+ panic("sbic_dosynch");
+
+ for (i = 0; i < 3; i++) {
+ regs->sbic_cmd = SBIC_CMD_MSG_ACPT;
+ sbic_wait(regs, SBIC_CSR_INT);
+ c = regs->sbic_intr;
+
+ regs->sbic_cmd = SBIC_CMD_XFER_INFO;
+ sbic_wait(regs, SBIC_CSR_INT);
+ c = regs->sbic_intr;/*ack*/
+ c = regs->sbic_fifo;
+
+ if (i == 1) tgt->sync_period = scsi_period_to_sbic(regs,c);
+ if (i == 2) tgt->sync_offset = c;
+ }
+
+cmd:
+ regs->sbic_cmd = SBIC_CMD_MSG_ACPT;
+ csr = sbic_wait(regs, SBIC_CSR_INT);
+ c = regs->sbic_intr;
+
+ /* phase should normally be command here */
+ if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) {
+ /* test unit ready or what ? */
+ regs->sbic_fifo = 0;
+ regs->sbic_fifo = 0;
+ regs->sbic_fifo = 0;
+ regs->sbic_fifo = 0;
+ regs->sbic_fifo = 0;
+ regs->sbic_fifo = 0;
+ SBIC_TC_PUT(regs,0xff);
+ regs->sbic_cmd = SBIC_CMD_XFER_PAD; /*0x98*/
+ csr = sbic_wait(regs, SBIC_CSR_INT);
+ ir = regs->sbic_intr;/*ack*/
+ }
+
+status:
+ if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS)
+ gimmeabreak();
+
+#endif
+ return TRUE;
+}
+
+/*
+ * The bus was reset
+ */
+sbic_bus_reset(sbic)
+ register sbic_softc_t sbic;
+{
+ register sbic_padded_regmap_t *regs = sbic->regs;
+
+ LOG(0x1d,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ sbic->script = 0;
+ sbic->error_handler = 0;
+ sbic->active_target = 0;
+ sbic->next_target = 0;
+ sbic->state &= SBIC_STATE_AM_MODE; /* save this one bit only */
+ queue_init(&sbic->waiting_targets);
+ sbic->wd.nactive = 0;
+ (void) sbic_reset(regs, TRUE);
+
+ printf("sbic: (%d) bus reset ", ++sbic->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (sbic->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(sbic->sc);
+}
+
+/*
+ * Disconnect/reconnect mode ops
+ */
+
+/* save all relevant data, free the BUS */
+boolean_t
+sbic_disconnected(sbic, csr, pha)
+ register sbic_softc_t sbic;
+ register unsigned char csr, pha;
+
+{
+ register target_info_t *tgt;
+
+ LOG(0x11,"disconnected");
+
+ tgt = sbic->active_target;
+ tgt->flags |= TGT_DISCONNECTED;
+ tgt->transient_state.handler = sbic->error_handler;
+ /* anything else was saved in sbic_err_disconn() */
+
+ PRINT(("{D%d}", tgt->target_id));
+
+ sbic_release_bus(sbic);
+
+ return FALSE;
+}
+
+/* See who reconnected, restore BUS */
+boolean_t
+sbic_reconnect(sbic, csr, ir)
+ register sbic_softc_t sbic;
+ register unsigned char csr, ir;
+
+{
+ register target_info_t *tgt;
+ sbic_padded_regmap_t *regs;
+ int id, pha;
+
+ LOG(0x12,"reconnect");
+ /*
+ * See if this reconnection collided with a selection attempt
+ */
+ if (sbic->state & SBIC_STATE_BUSY)
+ sbic->state |= SBIC_STATE_COLLISION;
+
+ sbic->state |= SBIC_STATE_BUSY;
+
+ /* find tgt */
+ regs = sbic->regs;
+ GET_SBIC_rselid(regs,id);
+
+ id &= 0x7;
+
+ if ((sbic->state & SBIC_STATE_AM) == 0) {
+ /* Must pick the identify */
+ pha = 0x44;
+ } else
+ pha = 0x45;
+
+ tgt = sbic->sc->target[id];
+ if (id > 7 || tgt == 0) panic("sbic_reconnect");
+
+ /* synch things*/
+ SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period));
+
+ PRINT(("{R%d}", id));
+ if (sbic->state & SBIC_STATE_COLLISION)
+ PRINT(("[B %d-%d]", sbic->active_target->target_id, id));
+
+ LOG(0x80+id,0);
+
+ sbic->active_target = tgt;
+ tgt->flags &= ~TGT_DISCONNECTED;
+
+ sbic->script = tgt->transient_state.script;
+ sbic->error_handler = tgt->transient_state.handler;
+ sbic->in_count = 0;
+ sbic->out_count = 0;
+
+set counter and setup dma, then
+
+ /* Resume the command now */
+ SET_SBIC_cmd_phase(regs, pha);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_XFER);
+
+ return FALSE;
+}
+
+TILL HERE
+
+/*
+ * Error handlers
+ */
+
+/*
+ * Fall-back error handler.
+ */
+sbic_err_generic(sbic, csr, ir)
+ register sbic_softc_t sbic;
+{
+ LOG(0x13,"err_generic");
+
+ /* handle non-existant or powered off devices here */
+ if ((ir == SBIC_INT_DISC) &&
+ (sbic_isa_select(sbic->cmd_was)) &&
+ (SBIC_SS(sbic->ss_was) == 0)) {
+ /* Powered off ? */
+ if (sbic->active_target->flags & TGT_FULLY_PROBED)
+ sbic->active_target->flags = 0;
+ sbic->done = SCSI_RET_DEVICE_DOWN;
+ sbic_end(sbic, csr, ir);
+ return;
+ }
+
+ switch (SCSI_PHASE(csr)) {
+ case SCSI_PHASE_STATUS:
+ if (sbic->script[-1].condition == SCSI_PHASE_STATUS) {
+ /* some are just slow to get out.. */
+ } else
+ sbic_err_to_status(sbic, csr, ir);
+ return;
+ break;
+ case SCSI_PHASE_DATAI:
+ if (sbic->script->condition == SCSI_PHASE_STATUS) {
+/* printf("{P}");*/
+ return;
+ }
+ break;
+ case SCSI_PHASE_DATAO:
+ if (sbic->script->condition == SCSI_PHASE_STATUS) {
+ /*
+ * See comment above. Actually seen on hitachis.
+ */
+/* printf("{P}");*/
+ return;
+ }
+ }
+ gimmeabreak();
+}
+
+/*
+ * Handle disconnections as exceptions
+ */
+sbic_err_disconn(sbic, csr, ir)
+ register sbic_softc_t sbic;
+ register unsigned char csr, ir;
+{
+ register sbic_padded_regmap_t *regs;
+ register target_info_t *tgt;
+ int count;
+ boolean_t callback = FALSE;
+
+ LOG(0x16,"err_disconn");
+
+ if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN)
+ return sbic_err_generic(sbic, csr, ir);
+
+ regs = sbic->regs;
+ tgt = sbic->active_target;
+
+ switch (sbic->script->condition) {
+ case SCSI_PHASE_DATAO:
+ LOG(0x1b,"+DATAO");
+ if (sbic->out_count) {
+ register int xferred, offset;
+
+ SBIC_TC_GET(regs,xferred); /* temporary misnomer */
+ xferred += regs->sbic_flags & SBIC_FLAGS_FIFO_CNT;
+ xferred -= sbic->extra_count;
+ xferred = sbic->out_count - xferred; /* ok now */
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ callback = (*sbic->dma_ops->disconn_1)
+ (sbic->dma_state, tgt, xferred);
+
+ } else {
+
+ callback = (*sbic->dma_ops->disconn_2)
+ (sbic->dma_state, tgt);
+
+ }
+ sbic->extra_count = 0;
+ tgt->transient_state.script = sbic_script_restart_data_out;
+ break;
+
+
+ case SCSI_PHASE_DATAI:
+ LOG(0x17,"+DATAI");
+ if (sbic->in_count) {
+ register int offset, xferred;
+
+ SBIC_TC_GET(regs,count);
+ xferred = sbic->in_count - count;
+ assert(xferred > 0);
+
+if (regs->sbic_flags & 0xf)
+printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT);
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+
+ callback = (*sbic->dma_ops->disconn_3)
+ (sbic->dma_state, tgt, xferred);
+
+ tgt->transient_state.script = sbic_script_restart_data_in;
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ }
+ tgt->transient_state.script = sbic->script;
+ break;
+
+ case SCSI_PHASE_STATUS:
+ /* will have to restart dma */
+ SBIC_TC_GET(regs,count);
+ if (sbic->state & SBIC_STATE_DMA_IN) {
+ register int offset, xferred;
+
+ LOG(0x1a,"+STATUS+R");
+
+ xferred = sbic->in_count - count;
+ assert(xferred > 0);
+
+if (regs->sbic_flags & 0xf)
+printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT);
+ tgt->transient_state.in_count -= xferred;
+/* assert(tgt->transient_state.in_count > 0);*/
+
+ callback = (*sbic->dma_ops->disconn_4)
+ (sbic->dma_state, tgt, xferred);
+
+ tgt->transient_state.script = sbic_script_restart_data_in;
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else {
+
+ /* add what's left in the fifo */
+ count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT);
+ /* take back the extra we might have added */
+ count -= sbic->extra_count;
+ /* ..and drop that idea */
+ sbic->extra_count = 0;
+
+ LOG(0x19,"+STATUS+W");
+
+
+ if ((count == 0) && (tgt->transient_state.out_count == sbic->out_count)) {
+ /* all done */
+ tgt->transient_state.script = sbic->script;
+ tgt->transient_state.out_count = 0;
+ } else {
+ register int xferred, offset;
+
+ /* how much we xferred */
+ xferred = sbic->out_count - count;
+
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ callback = (*sbic->dma_ops->disconn_5)
+ (sbic->dma_state,tgt,xferred);
+
+ tgt->transient_state.script = sbic_script_restart_data_out;
+ }
+ sbic->out_count = 0;
+ }
+ break;
+ default:
+ gimmeabreak();
+ return;
+ }
+ sbic_msg_in(sbic,csr,ir);
+ sbic->script = sbic_script_disconnect;
+ regs->sbic_cmd = SBIC_CMD_XFER_INFO|SBIC_CMD_DMA;
+ if (callback)
+ (*sbic->dma_ops->disconn_callback)(sbic->dma_state, tgt);
+}
+
+/*
+ * Watchdog
+ *
+ * We know that some (name withdrawn) disks get
+ * stuck in the middle of dma phases...
+ */
+sbic_reset_scsibus(sbic)
+ register sbic_softc_t sbic;
+{
+ register target_info_t *tgt = sbic->active_target;
+ register sbic_padded_regmap_t *regs = sbic->regs;
+ register int ir;
+
+ if (scsi_debug && tgt) {
+ int dmalen;
+ SBIC_TC_GET(sbic->regs,dmalen);
+ printf("Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count, tgt->transient_state.out_count,
+ sbic->in_count, sbic->out_count,
+ dmalen);
+ }
+ ir = regs->sbic_intr;
+ if ((ir & SBIC_INT_RESEL) && (SCSI_PHASE(regs->sbic_csr) == SCSI_PHASE_MSG_IN)) {
+ /* getting it out of the woods is a bit tricky */
+ spl_t s = splbio();
+
+ (void) sbic_reconnect(sbic, regs->sbic_csr, ir);
+ sbic_wait(regs, SBIC_CSR_INT);
+ ir = regs->sbic_intr;
+ regs->sbic_cmd = SBIC_CMD_MSG_ACPT;
+ splx(s);
+ } else {
+ regs->sbic_cmd = SBIC_CMD_BUS_RESET;
+ delay(35);
+ }
+}
+
+#endif NSBIC > 0
+
+#endif 0
diff --git a/scsi/adapters/scsi_5380.h b/scsi/adapters/scsi_5380.h
new file mode 100644
index 0000000..12be922
--- /dev/null
+++ b/scsi/adapters/scsi_5380.h
@@ -0,0 +1,126 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_5380.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 5/91
+ *
+ * Defines for the NCR 5380 (SCSI chip), aka Am5380
+ */
+
+/*
+ * Register map
+ */
+
+typedef struct {
+ volatile unsigned char sci_data; /* r: Current data */
+#define sci_odata sci_data /* w: Out data */
+ volatile unsigned char sci_icmd; /* rw: Initiator command */
+ volatile unsigned char sci_mode; /* rw: Mode */
+ volatile unsigned char sci_tcmd; /* rw: Target command */
+ volatile unsigned char sci_bus_csr; /* r: Bus Status */
+#define sci_sel_enb sci_bus_csr /* w: Select enable */
+ volatile unsigned char sci_csr; /* r: Status */
+#define sci_dma_send sci_csr /* w: Start dma send data */
+ volatile unsigned char sci_idata; /* r: Input data */
+#define sci_trecv sci_idata /* w: Start dma receive, target */
+ volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */
+#define sci_irecv sci_iack /* w: Start dma receive, initiator */
+} sci_regmap_t;
+
+
+/*
+ * Initiator command register
+ */
+
+#define SCI_ICMD_DATA 0x01 /* rw: Assert data bus */
+#define SCI_ICMD_ATN 0x02 /* rw: Assert ATN signal */
+#define SCI_ICMD_SEL 0x04 /* rw: Assert SEL signal */
+#define SCI_ICMD_BSY 0x08 /* rw: Assert BSY signal */
+#define SCI_ICMD_ACK 0x10 /* rw: Assert ACK signal */
+#define SCI_ICMD_LST 0x20 /* r: Lost arbitration */
+#define SCI_ICMD_DIFF SCI_ICMD_LST /* w: Differential cable */
+#define SCI_ICMD_AIP 0x40 /* r: Arbitration in progress */
+#define SCI_ICMD_TEST SCI_ICMD_AIP /* w: Test mode */
+#define SCI_ICMD_RST 0x80 /* rw: Assert RST signal */
+
+
+/*
+ * Mode register
+ */
+
+#define SCI_MODE_ARB 0x01 /* rw: Start arbitration */
+#define SCI_MODE_DMA 0x02 /* rw: Enable DMA xfers */
+#define SCI_MODE_MONBSY 0x04 /* rw: Monitor BSY signal */
+#define SCI_MODE_DMA_IE 0x08 /* rw: Enable DMA complete interrupt */
+#define SCI_MODE_PERR_IE 0x10 /* rw: Interrupt on parity errors */
+#define SCI_MODE_PAR_CHK 0x20 /* rw: Check parity */
+#define SCI_MODE_TARGET 0x40 /* rw: Target mode (Initiator if 0) */
+#define SCI_MODE_BLOCKDMA 0x80 /* rw: Block-mode DMA handshake (MBZ) */
+
+
+/*
+ * Target command register
+ */
+
+#define SCI_TCMD_IO 0x01 /* rw: Assert I/O signal */
+#define SCI_TCMD_CD 0x02 /* rw: Assert C/D signal */
+#define SCI_TCMD_MSG 0x04 /* rw: Assert MSG signal */
+#define SCI_TCMD_PHASE_MASK 0x07 /* r: Mask for current bus phase */
+#define SCI_TCMD_REQ 0x08 /* rw: Assert REQ signal */
+#define SCI_TCMD_LAST_SENT 0x80 /* ro: Last byte was xferred
+ * (not on 5380/1) */
+
+#define SCI_PHASE(x) SCSI_PHASE(x)
+
+/*
+ * Current (SCSI) Bus status
+ */
+
+#define SCI_BUS_DBP 0x01 /* r: Data Bus parity */
+#define SCI_BUS_SEL 0x02 /* r: SEL signal */
+#define SCI_BUS_IO 0x04 /* r: I/O signal */
+#define SCI_BUS_CD 0x08 /* r: C/D signal */
+#define SCI_BUS_MSG 0x10 /* r: MSG signal */
+#define SCI_BUS_REQ 0x20 /* r: REQ signal */
+#define SCI_BUS_BSY 0x40 /* r: BSY signal */
+#define SCI_BUS_RST 0x80 /* r: RST signal */
+
+#define SCI_CUR_PHASE(x) SCSI_PHASE((x)>>2)
+
+/*
+ * Bus and Status register
+ */
+
+#define SCI_CSR_ACK 0x01 /* r: ACK signal */
+#define SCI_CSR_ATN 0x02 /* r: ATN signal */
+#define SCI_CSR_DISC 0x04 /* r: Disconnected (BSY==0) */
+#define SCI_CSR_PHASE_MATCH 0x08 /* r: Bus and SCI_TCMD match */
+#define SCI_CSR_INT 0x10 /* r: Interrupt request */
+#define SCI_CSR_PERR 0x20 /* r: Parity error */
+#define SCI_CSR_DREQ 0x40 /* r: DMA request */
+#define SCI_CSR_DONE 0x80 /* r: DMA count is zero */
+
diff --git a/scsi/adapters/scsi_5380_hdw.c b/scsi/adapters/scsi_5380_hdw.c
new file mode 100644
index 0000000..2fc7d89
--- /dev/null
+++ b/scsi/adapters/scsi_5380_hdw.c
@@ -0,0 +1,2423 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_5380_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 4/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the NCR 5380
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+#include <sci.h>
+#if NSCI > 0
+#include <platforms.h>
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+#include <machine/machspl.h>
+
+#include <sys/syslog.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#ifdef VAXSTATION
+#define PAD(n) char n[3]
+#endif
+
+#include <scsi/adapters/scsi_5380.h>
+
+#ifdef PAD
+typedef struct {
+ volatile unsigned char sci_data; /* r: Current data */
+/*#define sci_odata sci_data /* w: Out data */
+ PAD(pad0);
+
+ volatile unsigned char sci_icmd; /* rw: Initiator command */
+ PAD(pad1);
+
+ volatile unsigned char sci_mode; /* rw: Mode */
+ PAD(pad2);
+
+ volatile unsigned char sci_tcmd; /* rw: Target command */
+ PAD(pad3);
+
+ volatile unsigned char sci_bus_csr; /* r: Bus Status */
+/*#define sci_sel_enb sci_bus_csr /* w: Select enable */
+ PAD(pad4);
+
+ volatile unsigned char sci_csr; /* r: Status */
+/*#define sci_dma_send sci_csr /* w: Start dma send data */
+ PAD(pad5);
+
+ volatile unsigned char sci_idata; /* r: Input data */
+/*#define sci_trecv sci_idata /* w: Start dma receive, target */
+ PAD(pad6);
+
+ volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */
+/*#define sci_irecv sci_iack /* w: Start dma receive, initiator */
+ PAD(pad7);
+
+} sci_padded_regmap_t;
+#else
+typedef sci_regmap_t sci_padded_regmap_t;
+#endif
+
+#ifdef VAXSTATION
+#define check_memory(addr,dow) ((dow) ? wbadaddr(addr,4) : badaddr(addr,4))
+
+/* vax3100 */
+#include <chips/vs42x_rb.h>
+#define STC_5380_A VAX3100_STC_5380_A
+#define STC_5380_B VAX3100_STC_5380_B
+#define STC_DMAREG_OFF VAX3100_STC_DMAREG_OFF
+
+static int mem; /* mem++ seems to take approx 0.34 usecs */
+#define delay_1p2_us() {mem++;mem++;mem++;mem++;}
+#define my_scsi_id(ctlr) (ka3100_scsi_id((ctlr)))
+#endif /* VAXSTATION */
+
+
+#ifndef STC_5380_A /* cross compile check */
+typedef struct {
+ int sci_dma_dir, sci_dma_adr;
+} *sci_dmaregs_t;
+#define STC_DMAREG_OFF 0
+#define SCI_DMA_DIR_WRITE 0
+#define SCI_DMA_DIR_READ 1
+#define STC_5380_A 0
+#define STC_5380_B 0x100
+#define SCI_RAM_SIZE 0x10000
+#endif
+
+/*
+ * The 5380 can't tell you the scsi ID it uses, so
+ * unless there is another way use the defaults
+ */
+#ifndef my_scsi_id
+#define my_scsi_id(ctlr) (scsi_initiator_id[(ctlr)])
+#endif
+
+/*
+ * Statically partition the DMA buffer between targets.
+ * This way we will eventually be able to attach/detach
+ * drives on-fly. And 18k/target is enough.
+ */
+#define PER_TGT_DMA_SIZE ((SCI_RAM_SIZE/7) & ~(sizeof(int)-1))
+
+/*
+ * Round to 4k to make debug easier
+ */
+#define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12)
+#define PER_TGT_BURST_SIZE (PER_TGT_BUFF_SIZE>>1)
+
+/*
+ * Macros to make certain things a little more readable
+ */
+
+#define SCI_ACK(ptr,phase) (ptr)->sci_tcmd = (phase)
+#define SCI_CLR_INTR(regs) {register int temp = regs->sci_iack;}
+
+
+/*
+ * A script has a two parts: a pre-condition and an action.
+ * The first triggers error handling if not satisfied and in
+ * our case it is formed by the current bus phase and connected
+ * condition as per bus status bits. The action part is just a
+ * function pointer, invoked in a standard way. The script
+ * pointer is advanced only if the action routine returns TRUE.
+ * See sci_intr() for how and where this is all done.
+ */
+
+typedef struct script {
+ int condition; /* expected state at interrupt */
+ int (*action)(); /* action routine */
+} *script_t;
+
+#define SCRIPT_MATCH(cs,bs) (((bs)&SCI_BUS_BSY)|SCI_CUR_PHASE((bs)))
+
+#define SCI_PHASE_DISC 0x0 /* sort of .. */
+
+
+/* forward decls of script actions */
+boolean_t
+ sci_dosynch(), /* negotiate synch xfer */
+ sci_dma_in(), /* get data from target via dma */
+ sci_dma_out(), /* send data to target via dma */
+ sci_get_status(), /* get status from target */
+ sci_end_transaction(), /* all come to an end */
+ sci_msg_in(), /* get disconnect message(s) */
+ sci_disconnected(); /* current target disconnected */
+/* forward decls of error handlers */
+boolean_t
+ sci_err_generic(), /* generic error handler */
+ sci_err_disconn(), /* when a target disconnects */
+ gimmeabreak(); /* drop into the debugger */
+
+int sci_reset_scsibus();
+boolean_t sci_probe_target();
+
+scsi_ret_t sci_select_target();
+
+#ifdef VAXSTATION
+/*
+ * This should be somewhere else, and it was a
+ * mistake to share this buffer across SCSIs.
+ */
+struct dmabuffer {
+ volatile char *base;
+ char *sbrk;
+} dmab[1];
+
+volatile char *
+sci_buffer_base(unit)
+{
+ return dmab[unit].base;
+}
+
+sci_buffer_init(dmar, ram)
+ sci_dmaregs_t dmar;
+ volatile char *ram;
+{
+ dmar->sci_dma_rammode = SCI_RAM_EXPMODE;
+ dmab[0].base = dmab[0].sbrk = (char *) ram;
+ blkclr((char *) ram, SCI_RAM_SIZE);
+}
+char *
+sci_buffer_sbrk(size)
+{
+ char *ret = dmab[0].sbrk;
+
+ dmab[0].sbrk += size;
+ if ((dmab[0].sbrk - dmab[0].base) > SCI_RAM_SIZE)
+ panic("scialloc");
+ return ret;
+}
+
+#endif /* VAXSTATION */
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) 5380 interface
+ */
+struct sci_softc {
+ watchdog_t wd;
+ sci_padded_regmap_t *regs; /* 5380 registers */
+ sci_dmaregs_t dmar; /* DMA controller registers */
+ volatile char *buff; /* DMA buffer memory (I/O space) */
+ script_t script;
+ int (*error_handler)();
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define SCI_STATE_BUSY 0x01 /* selecting or currently connected */
+#define SCI_STATE_TARGET 0x04 /* currently selected as target */
+#define SCI_STATE_COLLISION 0x08 /* lost selection attempt */
+#define SCI_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+ unsigned char extra_byte;
+
+ scsi_softc_t *sc;
+ target_info_t *active_target;
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+
+} sci_softc_data[NSCI];
+
+typedef struct sci_softc *sci_softc_t;
+
+sci_softc_t sci_softc[NSCI];
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int sci_probe(), scsi_slave(), sci_go(), sci_intr();
+void scsi_attach();
+
+vm_offset_t sci_std[NSCI] = { 0 };
+struct bus_device *sci_dinfo[NSCI*8];
+struct bus_ctlr *sci_minfo[NSCI];
+struct bus_driver sci_driver =
+ { sci_probe, scsi_slave, scsi_attach, sci_go, sci_std, "rz", sci_dinfo,
+ "sci", sci_minfo, BUS_INTR_B4_PROBE};
+
+/*
+ * Scripts
+ */
+struct script
+sci_script_data_in[] = {
+ { SCSI_PHASE_DATAI|SCI_BUS_BSY, sci_dma_in},
+ { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status},
+ { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction}
+},
+
+sci_script_data_out[] = {
+ { SCSI_PHASE_DATAO|SCI_BUS_BSY, sci_dma_out},
+ { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status},
+ { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction}
+},
+
+sci_script_cmd[] = {
+ { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status},
+ { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction}
+},
+
+/* Synchronous transfer neg(oti)ation */
+
+sci_script_try_synch[] = {
+ { SCSI_PHASE_MSG_OUT|SCI_BUS_BSY, sci_dosynch}
+},
+
+/* Disconnect sequence */
+
+sci_script_disconnect[] = {
+ { SCI_PHASE_DISC, sci_disconnected}
+};
+
+
+
+#define u_min(a,b) (((a) < (b)) ? (a) : (b))
+
+
+#define DEBUG
+#ifdef DEBUG
+
+sci_state(base)
+ vm_offset_t base;
+{
+ sci_padded_regmap_t *regs;
+ sci_dmaregs_t dmar;
+ extern char *sci;
+ unsigned dmadr;
+ int cnt, i;
+
+ if (base == 0)
+ base = (vm_offset_t)sci;
+
+ for (i = 0; i < 2; i++) {
+ regs = (sci_padded_regmap_t*) (base +
+ (i ? STC_5380_B : STC_5380_A));
+ dmar = (sci_dmaregs_t) ((char*)regs + STC_DMAREG_OFF);
+ SCI_DMADR_GET(dmar,dmadr);
+ SCI_TC_GET(dmar,cnt);
+
+ db_printf("scsi%d: ph %x (sb %x), mode %x, tph %x, csr %x, cmd %x, ",
+ i,
+ (unsigned) SCI_CUR_PHASE(regs->sci_bus_csr),
+ (unsigned) regs->sci_bus_csr,
+ (unsigned) regs->sci_mode,
+ (unsigned) regs->sci_tcmd,
+ (unsigned) regs->sci_csr,
+ (unsigned) regs->sci_icmd);
+ db_printf("dma%c %x @ %x\n",
+ (dmar->sci_dma_dir) ? 'I' : 'O', cnt, dmadr);
+ }
+ return 0;
+}
+sci_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = sci_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n",
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym(spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+sci_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = sci_softc[unit]->sc->target[i];
+ if (tgt)
+ sci_target_state(tgt);
+ }
+}
+
+sci_script_state(unit)
+{
+ script_t spt = sci_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym(spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(sci_softc[unit]->error_handler, 1);
+ return 0;
+
+}
+
+#define PRINT(x) if (scsi_debug) printf x
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int sci_logpt;
+char sci_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x24
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ sci_log[sci_logpt++] = (e);
+ if (sci_logpt == LOGSIZE) sci_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+sci_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = sci_logpt; i < LOGSIZE; i++) {
+ c = sci_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-%d", c & 0x7f);
+ }
+ db_printf("\n");
+ return 0;
+}
+
+sci_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /* TRACE */
+#define LOG(e,f)
+#endif /* TRACE */
+
+#else /* DEBUG */
+#define PRINT(x)
+#define LOG(e,f)
+#define TR(x)
+#define TRCHECK
+#define TRWRAP
+#endif /* DEBUG */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send an identify msg to each possible target on the bus
+ * except of course ourselves.
+ */
+sci_probe(reg, ui)
+ char *reg;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ sci_softc_t sci = &sci_softc_data[unit];
+ int target_id, i;
+ scsi_softc_t *sc;
+ register sci_padded_regmap_t *regs;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+ char *cmd_ptr;
+ static char *here = "sci_probe";
+
+ /*
+ * We are only called if the chip is there,
+ * but make sure anyways..
+ */
+ regs = (sci_padded_regmap_t *) (reg);
+ if (check_memory(regs, 0))
+ return 0;
+
+#if notyet
+ /* Mappable version side */
+ SCI_probe(reg, ui);
+#endif
+
+ /*
+ * Initialize hw descriptor
+ */
+ sci_softc[unit] = sci;
+ sci->regs = regs;
+ sci->dmar = (sci_dmaregs_t)(reg + STC_DMAREG_OFF);
+ sci->buff = sci_buffer_base(0);
+
+ queue_init(&sci->waiting_targets);
+
+ sc = scsi_master_alloc(unit, sci);
+ sci->sc = sc;
+
+ sc->go = sci_go;
+ sc->probe = sci_probe_target;
+ sc->watchdog = scsi_watchdog;
+ sci->wd.reset = sci_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1; /* unlimited */
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ scsi_might_disconnect[unit] = 0; /* still true */
+
+ /*
+ * Reset chip
+ */
+ s = splbio();
+ sci_reset(sci, TRUE);
+ SCI_CLR_INTR(regs);
+
+ /*
+ * Our SCSI id on the bus.
+ */
+
+ sc->initiator_id = my_scsi_id(unit);
+ printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ cmd_ptr = sci_buffer_sbrk(0);
+ for (target_id = 0; target_id < 8; target_id++) {
+
+ register unsigned csr, dsr;
+ scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ if (sci_select_target( regs, sc->initiator_id, target_id, FALSE) == SCSI_RET_DEVICE_DOWN) {
+ SCI_CLR_INTR(regs);
+ continue;
+ }
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ /* should be command phase here: we selected wo ATN! */
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD)
+ ;
+
+ SCI_ACK(regs,SCSI_PHASE_CMD);
+
+ /* build command in dma area */
+ {
+ unsigned char *p = (unsigned char*) cmd_ptr;
+
+ p[0] = SCSI_CMD_TEST_UNIT_READY;
+ p[1] =
+ p[2] =
+ p[3] =
+ p[4] =
+ p[5] = 0;
+ }
+
+ sci_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr);
+
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_STATUS)
+ ;
+
+ SCI_ACK(regs,SCSI_PHASE_STATUS);
+
+ sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD)
+ scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
+
+ /* get cmd_complete message */
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_IN)
+ ;
+
+ SCI_ACK(regs,SCSI_PHASE_MSG_IN);
+
+ sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &i);
+
+ /* check disconnected, clear all intr bits */
+ while (regs->sci_bus_csr & SCI_BUS_BSY)
+ ;
+ SCI_ACK(regs,SCI_PHASE_DISC);
+
+ SCI_CLR_INTR(regs);
+
+ /* ... */
+
+ /*
+ * Found a target
+ */
+ sci->ntargets++;
+ {
+ register target_info_t *tgt;
+
+ tgt = scsi_slave_alloc(unit, target_id, sci);
+
+ /* "virtual" address for our use */
+ tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE);
+ /* "physical" address for dma engine */
+ tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff);
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+ }
+ }
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+sci_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ sci_softc_t sci = sci_softc[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+
+ /* "virtual" address for our use */
+ tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE);
+ /* "physical" address for dma engine */
+ tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff);
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+
+static sci_wait(preg, until)
+ volatile unsigned char *preg;
+{
+ int timeo = 1000000;
+ /* read it over to avoid bus glitches */
+ while ( ((*preg & until) != until) ||
+ ((*preg & until) != until) ||
+ ((*preg & until) != until)) {
+ delay(1);
+ if (!timeo--) {
+ printf("sci_wait TIMEO with x%x\n", *preg);
+ break;
+ }
+ }
+ return *preg;
+}
+
+scsi_ret_t
+sci_select_target(regs, myid, id, with_atn)
+ register sci_padded_regmap_t *regs;
+ unsigned char myid, id;
+ boolean_t with_atn;
+{
+ register unsigned char bid, icmd;
+ scsi_ret_t ret = SCSI_RET_RETRY;
+
+ if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) &&
+ (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) &&
+ (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)))
+ return ret;
+
+ /* for our purposes.. */
+ myid = 1 << myid;
+ id = 1 << id;
+
+ regs->sci_sel_enb = myid; /* if not there already */
+
+ regs->sci_odata = myid;
+ regs->sci_mode |= SCI_MODE_ARB;
+ /* AIP might not set if BSY went true after we checked */
+ for (bid = 0; bid < 20; bid++) /* 20usec circa */
+ if (regs->sci_icmd & SCI_ICMD_AIP)
+ break;
+ if ((regs->sci_icmd & SCI_ICMD_AIP) == 0) {
+ goto lost;
+ }
+
+ delay(2); /* 2.2us arb delay */
+
+ if (regs->sci_icmd & SCI_ICMD_LST) {
+ goto lost;
+ }
+
+ regs->sci_mode &= ~SCI_MODE_PAR_CHK;
+ bid = regs->sci_data;
+
+ if ((bid & ~myid) > myid) {
+ goto lost;
+ }
+ if (regs->sci_icmd & SCI_ICMD_LST) {
+ goto lost;
+ }
+
+ /* Won arbitration, enter selection phase now */
+ icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+ icmd |= (with_atn ? (SCI_ICMD_SEL|SCI_ICMD_ATN) : SCI_ICMD_SEL);
+ regs->sci_icmd = icmd;
+
+ if (regs->sci_icmd & SCI_ICMD_LST) {
+ goto nosel;
+ }
+
+ /* XXX a target that violates specs might still drive the bus XXX */
+ /* XXX should put our id out, and after the delay check nothi XXX */
+ /* XXX ng else is out there. XXX */
+
+ delay_1p2_us();
+
+ regs->sci_sel_enb = 0;
+
+ regs->sci_odata = myid | id;
+
+ icmd |= SCI_ICMD_BSY|SCI_ICMD_DATA;
+ regs->sci_icmd = icmd;
+
+ regs->sci_mode &= ~SCI_MODE_ARB; /* 2 deskew delays, too */
+
+ icmd &= ~SCI_ICMD_BSY;
+ regs->sci_icmd = icmd;
+
+ /* bus settle delay, 400ns */
+ delay(0); /* too much ? */
+
+ regs->sci_mode |= SCI_MODE_PAR_CHK;
+
+ {
+ register int timeo = 2500;/* 250 msecs in 100 usecs chunks */
+ while ((regs->sci_bus_csr & SCI_BUS_BSY) == 0)
+ if (--timeo > 0)
+ delay(100);
+ else {
+ goto nodev;
+ }
+ }
+
+ icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL);
+ regs->sci_icmd = icmd;
+/* regs->sci_sel_enb = myid;*/ /* looks like we should NOT have it */
+ return SCSI_RET_SUCCESS;
+nodev:
+ ret = SCSI_RET_DEVICE_DOWN;
+ regs->sci_sel_enb = myid;
+nosel:
+ icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL|SCI_ICMD_ATN);
+ regs->sci_icmd = icmd;
+lost:
+ bid = regs->sci_mode;
+ bid &= ~SCI_MODE_ARB;
+ bid |= SCI_MODE_PAR_CHK;
+ regs->sci_mode = bid;
+
+ return ret;
+}
+
+sci_data_out(regs, phase, count, data)
+ register sci_padded_regmap_t *regs;
+ unsigned char *data;
+{
+ register unsigned char icmd;
+
+ /* ..checks.. */
+
+ icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+loop:
+ if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase)
+ return count;
+
+ while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0))
+ ;
+ icmd |= SCI_ICMD_DATA;
+ regs->sci_icmd = icmd;
+ regs->sci_odata = *data++;
+ icmd |= SCI_ICMD_ACK;
+ regs->sci_icmd = icmd;
+
+ icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_ACK);
+ while ( (regs->sci_bus_csr & SCI_BUS_REQ) &&
+ (regs->sci_bus_csr & SCI_BUS_REQ) &&
+ (regs->sci_bus_csr & SCI_BUS_REQ))
+ ;
+ regs->sci_icmd = icmd;
+ if (--count > 0)
+ goto loop;
+ return 0;
+}
+
+sci_data_in(regs, phase, count, data)
+ register sci_padded_regmap_t *regs;
+ unsigned char *data;
+{
+ register unsigned char icmd;
+
+ /* ..checks.. */
+
+ icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+loop:
+ if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase)
+ return count;
+
+ while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0))
+ ;
+ *data++ = regs->sci_data;
+ icmd |= SCI_ICMD_ACK;
+ regs->sci_icmd = icmd;
+
+ icmd &= ~SCI_ICMD_ACK;
+ while ( (regs->sci_bus_csr & SCI_BUS_REQ) &&
+ (regs->sci_bus_csr & SCI_BUS_REQ) &&
+ (regs->sci_bus_csr & SCI_BUS_REQ))
+ ;
+ regs->sci_icmd = icmd;
+ if (--count > 0)
+ goto loop;
+ return 0;
+
+}
+
+sci_reset(sci, quickly)
+ sci_softc_t sci;
+ boolean_t quickly;
+{
+ register sci_padded_regmap_t *regs = sci->regs;
+ register sci_dmaregs_t dma = sci->dmar;
+ int dummy;
+
+ regs->sci_icmd = SCI_ICMD_TEST; /* don't drive outputs */
+ regs->sci_icmd = SCI_ICMD_TEST|SCI_ICMD_RST;
+ delay(25);
+ regs->sci_icmd = 0;
+
+ regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_PERR_IE;
+ regs->sci_tcmd = SCI_PHASE_DISC; /* make sure we do not miss transition */
+ regs->sci_sel_enb = 0;
+
+ /* idle the dma controller */
+ dma->sci_dma_adr = 0;
+ dma->sci_dma_dir = SCI_DMA_DIR_WRITE;
+ SCI_TC_PUT(dma,0);
+
+ /* clear interrupt (two might be queued?) */
+ SCI_CLR_INTR(regs);
+ SCI_CLR_INTR(regs);
+
+ if (quickly)
+ return;
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call sci_bus_reset().
+ */
+ regs->sci_icmd = SCI_ICMD_RST;
+
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+sci_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ sci_softc_t sci;
+ register spl_t s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+
+ LOG(1,"go");
+
+ sci = (sci_softc_t)tgt->hw_state;
+
+ /*
+ * We cannot do real DMA.
+ */
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ if (tgt->ior)
+ fdma_map(&tgt->fdma, tgt->ior);
+#endif /*MACH_KERNEL*/
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+
+ if (len > PER_TGT_BUFF_SIZE)
+ len = PER_TGT_BUFF_SIZE;
+ bcopy( ior->io_data,
+ tgt->cmd_ptr + cmd_count,
+ len);
+ tgt->transient_state.copy_count = len;
+
+ /* avoid leaks */
+ if (len < tgt->block_size) {
+ bzero( tgt->cmd_ptr + cmd_count + len,
+ tgt->block_size - len);
+ tgt->transient_state.out_count = tgt->block_size;
+ }
+ } else {
+ tgt->transient_state.out_count = 0;
+ tgt->transient_state.copy_count = 0;
+ }
+
+ tgt->transient_state.cmd_count = cmd_count;
+
+ disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id);
+ disconn = disconn && (sci->ntargets > 1);
+ disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id);
+
+ /*
+ * Setup target state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ handler = (disconn) ? sci_err_disconn : sci_err_generic;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(0x13,"readop");
+ scp = sci_script_data_in;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x14,"writeop");
+ scp = sci_script_data_out;
+ break;
+ case SCSI_CMD_INQUIRY:
+ /* This is likely the first thing out:
+ do the synch neg if so */
+ if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
+ scp = sci_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ break;
+ }
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ scp = sci_script_data_in;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.out_count =
+ cmd_count - tgt->transient_state.cmd_count;
+ scp = sci_script_data_out;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ /*
+ * Do the synch negotiation here, unless prohibited
+ * or done already
+ */
+ if (tgt->flags & TGT_DID_SYNCH) {
+ scp = sci_script_cmd;
+ } else {
+ scp = sci_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ cmd_only = FALSE;
+ }
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ scp = sci_script_cmd;
+ }
+
+ tgt->transient_state.script = scp;
+ tgt->transient_state.handler = handler;
+ tgt->transient_state.identify = (cmd_only) ? 0xff :
+ (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
+ SCSI_IDENTIFY);
+
+ if (in_count)
+ tgt->transient_state.in_count =
+ (in_count < tgt->block_size) ? tgt->block_size : in_count;
+ else
+ tgt->transient_state.in_count = 0;
+ tgt->transient_state.dma_offset = 0;
+
+ /*
+ * See if another target is currently selected on
+ * this SCSI bus, e.g. lock the sci structure.
+ * Note that it is the strategy routine's job
+ * to serialize ops on the same target as appropriate.
+ * XXX here and everywhere, locks!
+ */
+ /*
+ * Protection viz reconnections makes it tricky.
+ */
+/* s = splbio();*/
+ s = splhigh();
+
+ if (sci->wd.nactive++ == 0)
+ sci->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (sci->state & SCI_STATE_BUSY) {
+ /*
+ * Queue up this target, note that this takes care
+ * of proper FIFO scheduling of the scsi-bus.
+ */
+ LOG(3,"enqueue");
+ enqueue_tail(&sci->waiting_targets, (queue_entry_t) tgt);
+ } else {
+ /*
+ * It is down to at most two contenders now,
+ * we will treat reconnections same as selections
+ * and let the scsi-bus arbitration process decide.
+ */
+ sci->state |= SCI_STATE_BUSY;
+ sci->next_target = tgt;
+ sci_attempt_selection(sci);
+ /*
+ * Note that we might still lose arbitration..
+ */
+ }
+ splx(s);
+}
+
+sci_attempt_selection(sci)
+ sci_softc_t sci;
+{
+ target_info_t *tgt;
+ register int out_count;
+ sci_padded_regmap_t *regs;
+ sci_dmaregs_t dmar;
+ register int cmd;
+ boolean_t ok;
+ scsi_ret_t ret;
+
+ regs = sci->regs;
+ dmar = sci->dmar;
+ tgt = sci->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * Init bus state variables and set registers.
+ */
+ sci->active_target = tgt;
+
+ /* reselection pending ? */
+ if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) &&
+ (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) &&
+ (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)))
+ return;
+
+ sci->script = tgt->transient_state.script;
+ sci->error_handler = tgt->transient_state.handler;
+ sci->done = SCSI_RET_IN_PROGRESS;
+
+ sci->in_count = 0;
+ sci->out_count = 0;
+ sci->extra_byte = 0;
+
+ /*
+ * This is a bit involved, but the bottom line is we want to
+ * know after we selected with or w/o ATN if the selection
+ * went well (ret) and if it is (ok) to send the command.
+ */
+ ok = TRUE;
+ if (tgt->flags & TGT_DID_SYNCH) {
+ if (tgt->transient_state.identify == 0xff) {
+ /* Select w/o ATN */
+ ret = sci_select_target(regs, sci->sc->initiator_id,
+ tgt->target_id, FALSE);
+ } else {
+ /* Select with ATN */
+ ret = sci_select_target(regs, sci->sc->initiator_id,
+ tgt->target_id, TRUE);
+ if (ret == SCSI_RET_SUCCESS) {
+ register unsigned char icmd;
+
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT)
+ ;
+ icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+ icmd &= ~SCI_ICMD_ATN;
+ regs->sci_icmd = icmd;
+ SCI_ACK(regs,SCSI_PHASE_MSG_OUT);
+ ok = (sci_data_out(regs, SCSI_PHASE_MSG_OUT,
+ 1, &tgt->transient_state.identify) == 0);
+ }
+ }
+ } else if (tgt->flags & TGT_TRY_SYNCH) {
+ /* Select with ATN, do the synch xfer neg */
+ ret = sci_select_target(regs, sci->sc->initiator_id,
+ tgt->target_id, TRUE);
+ if (ret == SCSI_RET_SUCCESS) {
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT)
+ ;
+ ok = sci_dosynch( sci, regs->sci_csr, regs->sci_bus_csr);
+ }
+ } else {
+ ret = sci_select_target(regs, sci->sc->initiator_id,
+ tgt->target_id, FALSE);
+ }
+
+ if (ret == SCSI_RET_DEVICE_DOWN) {
+ sci->done = ret;
+ sci_end(sci, regs->sci_csr, regs->sci_bus_csr);
+ return;
+ }
+ if ((ret != SCSI_RET_SUCCESS) || !ok)
+ return;
+
+/* time this out or do it via dma !! */
+ while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD)
+ ;
+
+ /* set dma pointer and counter to xfer command */
+ out_count = tgt->transient_state.cmd_count;
+#if 0
+ SCI_ACK(regs,SCSI_PHASE_CMD);
+ sci_data_out(regs,SCSI_PHASE_CMD,out_count,tgt->cmd_ptr);
+ regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY;
+#else
+ SCI_DMADR_PUT(dmar,tgt->dma_ptr);
+ delay_1p2_us();
+ SCI_TC_PUT(dmar,out_count);
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;
+ SCI_ACK(regs,SCSI_PHASE_CMD);
+ SCI_CLR_INTR(regs);
+ regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY;
+ regs->sci_icmd = SCI_ICMD_DATA;
+ regs->sci_dma_send = 1;
+#endif
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+sci_intr(unit)
+{
+ register sci_softc_t sci;
+ register script_t scp;
+ register unsigned csr, bs, cmd;
+ register sci_padded_regmap_t *regs;
+ boolean_t try_match;
+#if notyet
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return SCI_intr(unit);
+#endif
+
+ LOG(5,"\n\tintr");
+
+ sci = sci_softc[unit];
+ regs = sci->regs;
+
+ /* ack interrupt */
+ csr = regs->sci_csr;
+ bs = regs->sci_bus_csr;
+ cmd = regs->sci_icmd;
+TR(regs->sci_mode);
+ SCI_CLR_INTR(regs);
+
+TR(csr);
+TR(bs);
+TR(cmd);
+TRCHECK;
+
+ if (cmd & SCI_ICMD_RST){
+ sci_bus_reset(sci);
+ return;
+ }
+
+ /* we got an interrupt allright */
+ if (sci->active_target)
+ sci->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ /* drop spurious calls */
+ if ((csr & SCI_CSR_INT) == 0) {
+ LOG(2,"SPURIOUS");
+ return;
+ }
+
+ /* Note: reselect has I/O asserted, select has not */
+ if ((sci->state & SCI_STATE_TARGET) ||
+ ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == SCI_BUS_SEL)) {
+ sci_target_intr(sci,csr,bs);
+ return;
+ }
+
+ scp = sci->script;
+
+ /* Race: disconnecting, we get the disconnected notification
+ (csr sez BSY dropped) at the same time a reselect is active */
+ if ((csr & SCI_CSR_DISC) &&
+ scp && (scp->condition == SCI_PHASE_DISC)) {
+ (void) (*scp->action)(sci, csr, bs);
+ /* takes care of calling reconnect if necessary */
+ return;
+ }
+
+ /* check who got the bus */
+ if ((scp == 0) || (cmd & SCI_ICMD_LST) ||
+ ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO))) {
+ sci_reconnect(sci, csr, bs);
+ return;
+ }
+
+ if (SCRIPT_MATCH(csr,bs) != scp->condition) {
+ if (try_match = (*sci->error_handler)(sci, csr, bs)) {
+ csr = regs->sci_csr;
+ bs = regs->sci_bus_csr;
+ }
+ } else
+ try_match = TRUE;
+
+ /* might have been side effected */
+ scp = sci->script;
+
+ if (try_match && (SCRIPT_MATCH(csr,bs) == scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(sci, csr, bs)) {
+ /* might have been side effected */
+ scp = sci->script;
+ sci->script = scp + 1;
+ }
+ }
+}
+
+
+sci_target_intr(sci)
+ register sci_softc_t sci;
+{
+ panic("SCI: TARGET MODE !!!\n");
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to
+ */
+boolean_t
+sci_end_transaction( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register sci_padded_regmap_t *regs = sci->regs;
+ char cmc;
+
+ LOG(0x1f,"end_t");
+
+ /* Stop dma, no interrupt on disconnect */
+ regs->sci_icmd = 0;
+ regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE);
+/* dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */
+
+ SCI_ACK(regs,SCSI_PHASE_MSG_IN);
+
+ regs->sci_sel_enb = (1 << sci->sc->initiator_id);
+
+ sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc);
+
+ if (cmc != SCSI_COMMAND_COMPLETE)
+ printf("{T%x}", cmc);
+
+ /* check disconnected, clear all intr bits */
+ while (regs->sci_bus_csr & SCI_BUS_BSY)
+ ;
+ SCI_CLR_INTR(regs);
+ SCI_ACK(regs,SCI_PHASE_DISC);
+
+ if (!sci_end(sci, csr, bs)) {
+ SCI_CLR_INTR(regs);
+ (void) sci_reconnect(sci, csr, bs);
+ }
+ return FALSE;
+}
+
+boolean_t
+sci_end( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ register io_req_t ior;
+ register sci_padded_regmap_t *regs = sci->regs;
+ boolean_t reconn_pending;
+
+ LOG(6,"end");
+
+ tgt = sci->active_target;
+
+ if ((tgt->done = sci->done) == SCSI_RET_IN_PROGRESS)
+ tgt->done = SCSI_RET_SUCCESS;
+
+ sci->script = 0;
+
+ if (sci->wd.nactive-- == 1)
+ sci->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+ /* check reconnection not pending */
+ bs = regs->sci_bus_csr;
+ reconn_pending = ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO));
+ if (!reconn_pending) {
+ sci_release_bus(sci);
+ } else {
+ sci->active_target = 0;
+/* sci->state &= ~SCI_STATE_BUSY; later */
+ }
+
+ if (ior = tgt->ior) {
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_unmap(&tgt->fdma, ior);
+#endif /*MACH_KERNEL*/
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ if (reconn_pending)
+ sci->state &= ~SCI_STATE_BUSY;
+ }
+
+ return (!reconn_pending);
+}
+
+boolean_t
+sci_release_bus(sci)
+ register sci_softc_t sci;
+{
+ boolean_t ret = FALSE;
+
+ LOG(9,"release");
+
+ sci->script = 0;
+
+ if (sci->state & SCI_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ sci->state &= ~SCI_STATE_COLLISION;
+ sci_attempt_selection(sci);
+
+ } else if (queue_empty(&sci->waiting_targets)) {
+
+ sci->state &= ~SCI_STATE_BUSY;
+ sci->active_target = 0;
+ ret = TRUE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ sci->next_target = (target_info_t *)
+ dequeue_head(&sci->waiting_targets);
+ sci_attempt_selection(sci);
+ }
+ return ret;
+}
+
+boolean_t
+sci_get_status( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register sci_padded_regmap_t *regs = sci->regs;
+ register sci_dmaregs_t dmar = sci->dmar;
+ scsi2_status_byte_t status;
+ register target_info_t *tgt;
+ unsigned int len, mode;
+
+ LOG(0xD,"get_status");
+TRWRAP;
+
+ /* Stop dma */
+ regs->sci_icmd = 0;
+ mode = regs->sci_mode;
+ regs->sci_mode = (mode & ~(SCI_MODE_DMA|SCI_MODE_DMA_IE));
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */
+
+ sci->state &= ~SCI_STATE_DMA_IN;
+
+ tgt = sci->active_target;
+
+ if (len = sci->in_count) {
+ register int count;
+ SCI_TC_GET(dmar,count);
+ if ((tgt->cur_cmd != SCSI_CMD_READ) &&
+ (tgt->cur_cmd != SCSI_CMD_LONG_READ)){
+ len -= count;
+ } else {
+ if (count) {
+#if 0
+ this is incorrect and besides..
+ tgt->ior->io_residual = count;
+#endif
+ len -= count;
+ }
+ sci_copyin( tgt, tgt->transient_state.dma_offset,
+ len, 0, 0);
+ }
+ }
+
+ /* to get the phase mismatch intr */
+ regs->sci_mode = mode;
+
+ SCI_ACK(regs,SCSI_PHASE_STATUS);
+
+ sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
+
+ SCI_TC_PUT(dmar,0);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(sci->active_target, SCSI_ERR_STATUS, status.bits, 0);
+ sci->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ sci->done = SCSI_RET_SUCCESS;
+
+ return TRUE;
+}
+
+boolean_t
+sci_dma_in( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ register sci_padded_regmap_t *regs = sci->regs;
+ register sci_dmaregs_t dmar = sci->dmar;
+ char *dma_ptr;
+ register int count;
+ boolean_t advance_script = TRUE;
+
+ LOG(0xE,"dma_in");
+
+ /*
+ * Problem: the 5380 pipelines xfers between the scsibus and
+ * itself and between itself and the DMA engine --> halting ?
+ * In the dmaout direction all is ok, except that (see NCR notes)
+ * the EOP interrupt is generated before the pipe is empty.
+ * In the dmain direction (here) the interrupt comes when
+ * one too many bytes have been xferred on chip!
+ *
+ * More specifically, IF we asked for count blindly and we had
+ * more than count bytes coming (double buffering) we would endup
+ * actually xferring count+1 from the scsibus, but only count
+ * to memory [hopefully the last byte sits in the sci_datai reg].
+ * This could be helped, except most times count is an exact multiple
+ * of the sector size which is where disks disconnect....
+ *
+ * INSTEAD, we recognize here that we expect more than count bytes
+ * coming and set the DMA count to count-1 but keep sci->in_count
+ * above to count. This will be wrong if the target disconnects
+ * amidst, but we can cure it.
+ *
+ * The places where this has an effect are marked by "EXTRA_BYTE"
+ */
+
+ tgt = sci->active_target;
+ sci->state |= SCI_STATE_DMA_IN;
+
+ /* ought to stop dma to start another */
+ regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ regs->sci_icmd = 0;
+
+ if (sci->in_count == 0) {
+ /*
+ * Got nothing yet: either just sent the command
+ * or just reconnected
+ */
+ register int avail;
+
+ count = tgt->transient_state.in_count;
+ count = u_min(count, (PER_TGT_BURST_SIZE));
+ avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, avail);
+
+ /* common case of 8k-or-less read ? */
+ advance_script = (tgt->transient_state.in_count == count);
+
+ } else {
+
+ /*
+ * We received some data.
+ */
+ register int offset, xferred, eb;
+ unsigned char extrab = regs->sci_idata; /* EXTRA_BYTE */
+
+ SCI_TC_GET(dmar,xferred);
+ assert(xferred == 0);
+if (scsi_debug) {
+printf("{B %x %x %x (%x)}",
+ sci->in_count, xferred, sci->extra_byte, extrab);
+}
+ /* ++EXTRA_BYTE */
+ xferred = sci->in_count - xferred;
+ eb = sci->extra_byte;
+ /* --EXTRA_BYTE */
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ count = u_min(tgt->transient_state.in_count, (PER_TGT_BURST_SIZE));
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) {
+ tgt->transient_state.dma_offset = 0;
+ } else {
+ register int avail;
+ avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, avail);
+ }
+ advance_script = (tgt->transient_state.in_count == count);
+
+ /* get some more */
+ dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset;
+ sci->in_count = count;
+ /* ++EXTRA_BYTE */
+ if (!advance_script) {
+ sci->extra_byte = 1; /* that's the cure.. */
+ count--;
+ } else
+ sci->extra_byte = 0;
+ /* --EXTRA_BYTE */
+ SCI_TC_PUT(dmar,count);
+/* regs->sci_icmd = 0;*/
+ SCI_DMADR_PUT(dmar,dma_ptr);
+ delay_1p2_us();
+ SCI_ACK(regs,SCSI_PHASE_DATAI);
+ SCI_CLR_INTR(regs);
+ regs->sci_mode |= (advance_script ? SCI_MODE_DMA
+ : (SCI_MODE_DMA|SCI_MODE_DMA_IE));
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;
+ regs->sci_irecv = 1;
+
+ /* copy what we got */
+ sci_copyin( tgt, offset, xferred, eb, extrab);
+
+ /* last chunk ? */
+ return advance_script;
+ }
+
+ sci->in_count = count;
+ dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset;
+
+ /* ++EXTRA_BYTE */
+ if (!advance_script) {
+ sci->extra_byte = 1; /* that's the cure.. */
+ count--;
+ } else
+ sci->extra_byte = 0;
+ /* --EXTRA_BYTE */
+
+ SCI_TC_PUT(dmar,count);
+/* regs->sci_icmd = 0;*/
+ SCI_DMADR_PUT(dmar,dma_ptr);
+ delay_1p2_us();
+ SCI_ACK(regs,SCSI_PHASE_DATAI);
+ SCI_CLR_INTR(regs);
+ regs->sci_mode |= (advance_script ? SCI_MODE_DMA
+ : (SCI_MODE_DMA|SCI_MODE_DMA_IE));
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;
+ regs->sci_irecv = 1;
+
+ return advance_script;
+}
+
+/* send data to target. Called in three different ways:
+ (a) to start transfer (b) to restart a bigger-than-8k
+ transfer (c) after reconnection
+ */
+int sci_delay = 1;
+
+boolean_t
+sci_dma_out( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register sci_padded_regmap_t *regs = sci->regs;
+ register sci_dmaregs_t dmar = sci->dmar;
+ register char *dma_ptr;
+ register target_info_t *tgt;
+ boolean_t advance_script = TRUE;
+ int count = sci->out_count;
+ spl_t s;
+ register int tmp;
+
+ LOG(0xF,"dma_out");
+
+ tgt = sci->active_target;
+ sci->state &= ~SCI_STATE_DMA_IN;
+
+ if (sci->out_count == 0) {
+ /*
+ * Nothing committed: either just sent the
+ * command or reconnected
+ */
+ register int remains;
+
+ /* ought to stop dma to start another */
+ regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */
+
+ regs->sci_icmd = SCI_ICMD_DATA;
+
+ SCI_ACK(regs,SCSI_PHASE_DATAO);
+
+ count = tgt->transient_state.out_count;
+ count = u_min(count, (PER_TGT_BURST_SIZE));
+ remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, remains);
+
+ /* common case of 8k-or-less write ? */
+ advance_script = (tgt->transient_state.out_count == count);
+ } else {
+ /*
+ * We sent some data.
+ * Also, take care of bogus interrupts
+ */
+ register int offset, xferred;
+
+if (sci_delay & 1) delay(1000);
+ /* ought to stop dma to start another */
+ regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */
+/* regs->sci_icmd = SCI_ICMD_DATA; */
+
+ SCI_TC_GET(dmar,xferred);
+if (xferred) printf("{A %x}", xferred);
+ xferred = sci->out_count - xferred;
+ assert(xferred > 0);
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ count = u_min(tgt->transient_state.out_count, (PER_TGT_BURST_SIZE));
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) {
+ tgt->transient_state.dma_offset = 0;
+ } else {
+ register int remains;
+ remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, remains);
+ }
+ /* last chunk ? */
+ if (tgt->transient_state.out_count == count)
+ goto quickie;
+
+ /* ship some more */
+ dma_ptr = tgt->dma_ptr +
+ tgt->transient_state.cmd_count + tgt->transient_state.dma_offset;
+ sci->out_count = count;
+ /*
+ * Mistery: sometimes the first byte
+ * of an 8k chunk is missing from the tape, it must
+ * be that somehow touching the 5380 registers
+ * after the dma engine is ready screws up: false DRQ?
+ */
+s = splhigh();
+ SCI_TC_PUT(dmar,count);
+/* SCI_CLR_INTR(regs);*/
+ regs->sci_mode = SCI_MODE_PAR_CHK | SCI_MODE_DMA |
+ SCI_MODE_MONBSY | SCI_MODE_DMA_IE;
+/* regs->sci_icmd = SCI_ICMD_DATA;*/
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;
+ SCI_DMADR_PUT(dmar,dma_ptr);
+ delay_1p2_us();
+
+ regs->sci_dma_send = 1;
+splx(s);
+ /* copy some more data */
+ sci_copyout(tgt, offset, xferred);
+ return FALSE;
+ }
+
+quickie:
+ sci->out_count = count;
+ dma_ptr = tgt->dma_ptr +
+ tgt->transient_state.cmd_count + tgt->transient_state.dma_offset;
+ tmp = (advance_script ?
+ SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY:
+ SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE);
+s = splhigh();
+ SCI_TC_PUT(dmar,count);
+/* SCI_CLR_INTR(regs);*/
+ regs->sci_mode = tmp;
+/* regs->sci_icmd = SCI_ICMD_DATA;*/
+ SCI_DMADR_PUT(dmar,dma_ptr);
+ delay_1p2_us();
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;
+ regs->sci_dma_send = 1;
+splx(s);
+
+ return advance_script;
+}
+
+/* disconnect-reconnect ops */
+
+/* get the message in via dma */
+boolean_t
+sci_msg_in(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ char *dma_ptr;
+ register sci_padded_regmap_t *regs = sci->regs;
+ register sci_dmaregs_t dmar = sci->dmar;
+
+ LOG(0x15,"msg_in");
+
+ tgt = sci->active_target;
+
+ dma_ptr = tgt->dma_ptr;
+ /* We would clobber the data for READs */
+ if (sci->state & SCI_STATE_DMA_IN) {
+ register int offset;
+ offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset;
+ dma_ptr += offset;
+ }
+
+ /* ought to stop dma to start another */
+ regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ regs->sci_icmd = 0;
+
+ /* We only really expect two bytes */
+ SCI_TC_PUT(dmar,sizeof(scsi_command_group_0));
+/* regs->sci_icmd = 0*/
+ SCI_DMADR_PUT(dmar,dma_ptr);
+ delay_1p2_us();
+ SCI_ACK(regs,SCSI_PHASE_MSG_IN);
+ SCI_CLR_INTR(regs);
+ regs->sci_mode |= SCI_MODE_DMA;
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;
+ regs->sci_irecv = 1;
+
+ return TRUE;
+}
+
+/* check the message is indeed a DISCONNECT */
+boolean_t
+sci_disconnect(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register int len;
+ boolean_t ok = FALSE;
+ register sci_dmaregs_t dmar = sci->dmar;
+ register char *msgs;
+ unsigned int offset;
+
+
+ SCI_TC_GET(dmar,len);
+ len = sizeof(scsi_command_group_0) - len;
+ PRINT(("{G%d}",len));
+
+ /* wherever it was, take it from there */
+ SCI_DMADR_GET(dmar,offset);
+ msgs = (char*)sci->buff + offset - len;
+
+ if ((len == 0) || (len > 2))
+ ok = FALSE;
+ else {
+ /* A SDP message preceeds it in non-completed READs */
+ ok = ((msgs[0] == SCSI_DISCONNECT) || /* completed op */
+ ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* incomplete */
+ (msgs[1] == SCSI_DISCONNECT)));
+ }
+ if (!ok)
+ printf("[tgt %d bad msg (%d): %x]",
+ sci->active_target->target_id, len, *msgs);
+
+ return TRUE;
+}
+
+/* save all relevant data, free the BUS */
+boolean_t
+sci_disconnected(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ sci_padded_regmap_t *regs = sci->regs;
+
+ regs->sci_mode &= ~(SCI_MODE_MONBSY|SCI_MODE_DMA);
+ SCI_CLR_INTR(regs);/*retriggered by MONBSY cuz intr routine did CLR */
+ SCI_ACK(regs,SCI_PHASE_DISC);
+
+ LOG(0x16,"disconnected");
+
+ sci_disconnect(sci,csr,bs);
+
+ tgt = sci->active_target;
+ tgt->flags |= TGT_DISCONNECTED;
+ tgt->transient_state.handler = sci->error_handler;
+ /* the rest has been saved in sci_err_disconn() */
+
+ PRINT(("{D%d}", tgt->target_id));
+
+ sci_release_bus(sci);
+
+ return FALSE;
+}
+
+/* get reconnect message, restore BUS */
+boolean_t
+sci_reconnect(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ sci_padded_regmap_t *regs;
+ register int id;
+ int msg;
+
+ LOG(0x17,"reconnect");
+
+ if (sci->wd.nactive == 0) {
+ LOG(2,"SPURIOUS");
+ return FALSE;
+ }
+
+ regs = sci->regs;
+
+ regs->sci_mode &= ~SCI_MODE_PAR_CHK;
+ id = regs->sci_data;/*parity!*/
+ regs->sci_mode |= SCI_MODE_PAR_CHK;
+
+ /* xxx check our id is in there */
+
+ id &= ~(1 << sci->sc->initiator_id);
+ {
+ register int i;
+ for (i = 0; i < 8; i++)
+ if (id & (1 << i)) break;
+if (i == 8) {printf("{P%x}", id);return;}
+ id = i;
+ }
+ regs->sci_icmd = SCI_ICMD_BSY;
+ while (regs->sci_bus_csr & SCI_BUS_SEL)
+ ;
+ regs->sci_icmd = 0;
+ delay_1p2_us();
+ while ( ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_BSY) == 0))
+ ;
+
+ regs->sci_mode |= SCI_MODE_MONBSY;
+
+ /* Now should wait for correct phase: REQ signals it */
+ while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) &&
+ ((regs->sci_bus_csr & SCI_BUS_REQ) == 0))
+ ;
+
+ /*
+ * See if this reconnection collided with a selection attempt
+ */
+ if (sci->state & SCI_STATE_BUSY)
+ sci->state |= SCI_STATE_COLLISION;
+
+ sci->state |= SCI_STATE_BUSY;
+
+ /* Get identify msg */
+ bs = regs->sci_bus_csr;
+if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak();
+ SCI_ACK(regs,SCSI_PHASE_MSG_IN);
+ msg = 0;
+ sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg);
+ regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY;
+ regs->sci_sel_enb = 0;
+
+ if (msg != SCSI_IDENTIFY)
+ printf("{I%x %x}", id, msg);
+
+ tgt = sci->sc->target[id];
+ if (id > 7 || tgt == 0) panic("sci_reconnect");
+
+ PRINT(("{R%d}", id));
+ if (sci->state & SCI_STATE_COLLISION)
+ PRINT(("[B %d-%d]", sci->active_target->target_id, id));
+
+ LOG(0x80+id,0);
+
+ sci->active_target = tgt;
+ tgt->flags &= ~TGT_DISCONNECTED;
+
+ sci->script = tgt->transient_state.script;
+ sci->error_handler = tgt->transient_state.handler;
+ sci->in_count = 0;
+ sci->out_count = 0;
+
+ /* Should get a phase mismatch when tgt changes phase */
+
+ return TRUE;
+}
+
+
+
+/* do the synch negotiation */
+boolean_t
+sci_dosynch( sci, csr, bs)
+ register sci_softc_t sci;
+{
+ /*
+ * Phase is MSG_OUT here, cmd has not been xferred
+ */
+ int len;
+ register target_info_t *tgt;
+ register sci_padded_regmap_t *regs = sci->regs;
+ unsigned char off, icmd;
+ register unsigned char *p;
+
+ regs->sci_mode |= SCI_MODE_MONBSY;
+
+ LOG(0x11,"dosync");
+
+ /* ATN still asserted */
+ SCI_ACK(regs,SCSI_PHASE_MSG_OUT);
+
+ tgt = sci->active_target;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ p = (unsigned char *)tgt->cmd_ptr + tgt->transient_state.cmd_count +
+ tgt->transient_state.dma_offset;
+ p[0] = SCSI_IDENTIFY;
+ p[1] = SCSI_EXTENDED_MESSAGE;
+ p[2] = 3;
+ p[3] = SCSI_SYNC_XFER_REQUEST;
+ /* We cannot run synchronous */
+#define sci_to_scsi_period(x) 0xff
+#define scsi_period_to_sci(x) (x)
+ off = 0;
+ p[4] = sci_to_scsi_period(sci_min_period);
+ p[5] = off;
+
+ /* xfer all but last byte with ATN set */
+ sci_data_out(regs, SCSI_PHASE_MSG_OUT,
+ sizeof(scsi_synch_xfer_req_t), p);
+ icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+ icmd &= ~SCI_ICMD_ATN;
+ regs->sci_icmd = icmd;
+ sci_data_out(regs, SCSI_PHASE_MSG_OUT, 1,
+ &p[sizeof(scsi_synch_xfer_req_t)]);
+
+ /* wait for phase change */
+ while (regs->sci_csr & SCI_CSR_PHASE_MATCH)
+ ;
+ bs = regs->sci_bus_csr;
+
+ /* The standard sez there nothing else the target can do but.. */
+ if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN)
+ panic("sci_dosync");/* XXX put offline */
+
+msgin:
+ /* ack */
+ SCI_ACK(regs,SCSI_PHASE_MSG_IN);
+
+ /* get answer */
+ len = sizeof(scsi_synch_xfer_req_t);
+ len = sci_data_in(regs, SCSI_PHASE_MSG_IN, len, p);
+
+ /* do not cancel the phase mismatch interrupt ! */
+
+ /* look at the answer and see if we like it */
+ if (len || (p[0] != SCSI_EXTENDED_MESSAGE)) {
+ /* did not like it at all */
+ printf(" did not like SYNCH xfer ");
+ } else {
+ /* will NOT do synch */
+ printf(" but we cannot do SYNCH xfer ");
+ tgt->sync_period = scsi_period_to_sci(p[3]);
+ tgt->sync_offset = p[4];
+ /* sanity */
+ if (tgt->sync_offset != 0)
+ printf(" ?OFFSET %x? ", tgt->sync_offset);
+ }
+
+ /* wait for phase change */
+ while (regs->sci_csr & SCI_CSR_PHASE_MATCH)
+ ;
+ bs = regs->sci_bus_csr;
+
+ /* phase should be command now */
+ /* continue with simple command script */
+ sci->error_handler = sci_err_generic;
+ sci->script = sci_script_cmd;
+
+ if (SCI_CUR_PHASE(bs) == SCSI_PHASE_CMD )
+ return TRUE;
+
+/* sci->script++;*/
+ if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS )
+ return TRUE; /* intr is pending */
+
+ sci->script++;
+ if (SCI_CUR_PHASE(bs) == SCSI_PHASE_MSG_IN )
+ return TRUE;
+
+ if ((bs & SCI_BUS_BSY) == 0) /* uhu? disconnected */
+ return sci_end_transaction(sci, regs->sci_csr, regs->sci_bus_csr);
+
+ panic("sci_dosynch");
+ return FALSE;
+}
+
+/*
+ * The bus was reset
+ */
+sci_bus_reset(sci)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt;
+ register sci_padded_regmap_t *regs = sci->regs;
+ int i;
+
+ LOG(0x21,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ sci->script = 0;
+ sci->error_handler = 0;
+ sci->active_target = 0;
+ sci->next_target = 0;
+ sci->state = 0;
+ queue_init(&sci->waiting_targets);
+ sci->wd.nactive = 0;
+ sci_reset(sci, TRUE);
+
+ printf("sci%d: (%d) bus reset ", sci->sc->masterno, ++sci->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (sci->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(sci->sc);
+}
+
+/*
+ * Error handlers
+ */
+
+/*
+ * Generic, default handler
+ */
+boolean_t
+sci_err_generic(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register int cond = sci->script->condition;
+
+ LOG(0x10,"err_generic");
+
+ if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS)
+ return sci_err_to_status(sci, csr, bs);
+ gimmeabreak();
+ return FALSE;
+}
+
+/*
+ * Handle generic errors that are reported as
+ * an unexpected change to STATUS phase
+ */
+sci_err_to_status(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ script_t scp = sci->script;
+
+ LOG(0x20,"err_tostatus");
+ while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS)
+ scp++;
+ sci->script = scp;
+#if 0
+ /*
+ * Normally, we would already be able to say the command
+ * is in error, e.g. the tape had a filemark or something.
+ * But in case we do disconnected mode WRITEs, it is quite
+ * common that the following happens:
+ * dma_out -> disconnect -> reconnect
+ * and our script might expect at this point that the dma
+ * had to be restarted (it didn't know it was completed
+ * because the tape record is shorter than we asked for).
+ * And in any event.. it is both correct and cleaner to
+ * declare error iff the STATUS byte says so.
+ */
+ sci->done = SCSI_RET_NEED_SENSE;
+#endif
+ return TRUE;
+}
+
+/*
+ * Watch for a disconnection
+ */
+boolean_t
+sci_err_disconn(sci, csr, bs)
+ register sci_softc_t sci;
+{
+ register sci_padded_regmap_t *regs;
+ register sci_dmaregs_t dmar = sci->dmar;
+ register target_info_t *tgt;
+ int count;
+
+ LOG(0x18,"err_disconn");
+
+ if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN)
+ return sci_err_generic(sci, csr, bs);
+
+ regs = sci->regs;
+
+ tgt = sci->active_target;
+
+ switch (SCSI_PHASE(sci->script->condition)) {
+ case SCSI_PHASE_DATAO:
+ LOG(0x1b,"+DATAO");
+
+if (sci_delay & 1) delay(1000);
+ /* Stop dma */
+ regs->sci_icmd = 0;
+ regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */
+
+ if (sci->out_count) {
+ register int xferred, offset;
+
+ SCI_TC_GET(dmar,xferred);
+if (scsi_debug)
+printf("{O %x %x}", xferred, sci->out_count);
+ /* 5380 prefetches */
+ xferred = sci->out_count - xferred - 1;
+/* assert(xferred > 0);*/
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ sci_copyout( tgt, offset, xferred);
+
+ }
+ tgt->transient_state.script = sci_script_data_out;
+ break;
+
+ case SCSI_PHASE_DATAI:
+ LOG(0x19,"+DATAI");
+
+ /* Stop dma */
+ regs->sci_icmd = 0;
+ regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */
+
+ if (sci->in_count) {
+ register int offset, xferred;
+/* unsigned char extrab = regs->sci_idata;*/
+
+ SCI_TC_GET(dmar,xferred);
+ /* ++EXTRA_BYTE */
+if (scsi_debug)
+printf("{A %x %x %x}", xferred, sci->in_count, sci->extra_byte);
+ xferred = sci->in_count - xferred - sci->extra_byte;
+ /* ++EXTRA_BYTE */
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ /* copy what we got */
+ sci_copyin( tgt, offset, xferred, 0, 0/*extrab*/);
+ }
+ tgt->transient_state.script = sci_script_data_in;
+ break;
+
+ case SCSI_PHASE_STATUS:
+ /* will have to restart dma */
+ SCI_TC_GET(dmar,count);
+ if (sci->state & SCI_STATE_DMA_IN) {
+ register int offset, xferred;
+/* unsigned char extrab = regs->sci_idata;*/
+
+ LOG(0x1a,"+STATUS+R");
+
+
+ /* Stop dma */
+ regs->sci_icmd = 0;
+ regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */
+
+ /* ++EXTRA_BYTE */
+if (scsi_debug)
+printf("{A %x %x %x}", count, sci->in_count, sci->extra_byte);
+ xferred = sci->in_count - count - sci->extra_byte;
+ /* ++EXTRA_BYTE */
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+/* assert(tgt->transient_state.in_count > 0);*/
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ /* copy what we got */
+ sci_copyin( tgt, offset, xferred, 0, 0/*/extrab*/);
+
+ tgt->transient_state.script = sci_script_data_in;
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else {
+
+ LOG(0x1d,"+STATUS+W");
+
+if (sci_delay & 1) delay(1000);
+ /* Stop dma */
+ regs->sci_icmd = 0;
+ regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE);
+ dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */
+
+if (scsi_debug)
+printf("{O %x %x}", count, sci->out_count);
+ if ((count == 0) && (tgt->transient_state.out_count == sci->out_count)) {
+ /* all done */
+ tgt->transient_state.script = &sci_script_data_out[1];
+ tgt->transient_state.out_count = 0;
+ } else {
+ register int xferred, offset;
+
+ /* how much we xferred */
+ xferred = sci->out_count - count - 1;/*prefetch*/
+
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ sci_copyout( tgt, offset, xferred);
+
+ tgt->transient_state.script = sci_script_data_out;
+ }
+ sci->out_count = 0;
+ }
+ break;
+ default:
+ gimmeabreak();
+ }
+ sci->extra_byte = 0;
+
+/* SCI_ACK(regs,SCSI_PHASE_MSG_IN); later */
+ (void) sci_msg_in(sci,csr,bs);
+
+ regs->sci_sel_enb = (1 << sci->sc->initiator_id);
+
+ sci->script = sci_script_disconnect;
+
+ return FALSE;
+}
+
+/*
+ * Watchdog
+ *
+ */
+sci_reset_scsibus(sci)
+ register sci_softc_t sci;
+{
+ register target_info_t *tgt = sci->active_target;
+ if (tgt) {
+ int cnt;
+ SCI_TC_GET(sci->dmar,cnt);
+ log( LOG_KERN,
+ "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count, tgt->transient_state.out_count,
+ sci->in_count, sci->out_count, cnt);
+ }
+ sci->regs->sci_icmd = SCI_ICMD_RST;
+ delay(25);
+}
+
+/*
+ * Copy routines
+ */
+/*static*/
+sci_copyin(tgt, offset, len, isaobb, obb)
+ register target_info_t *tgt;
+ unsigned char obb;
+{
+ register char *from, *to;
+ register int count;
+
+ count = tgt->transient_state.copy_count;
+
+ from = tgt->cmd_ptr + offset;
+ to = tgt->ior->io_data + count;
+ tgt->transient_state.copy_count = count + len;
+
+ bcopy( from, to, len);
+ /* check for last, poor little odd byte */
+ if (isaobb) {
+ to += len;
+ to[-1] = obb;
+ }
+}
+
+/*static*/
+sci_copyout( tgt, offset, len)
+ register target_info_t *tgt;
+{
+ register char *from, *to;
+ register int count, olen;
+ unsigned char c;
+ char *p;
+
+ count = tgt->ior->io_count - tgt->transient_state.copy_count;
+ if (count > 0) {
+
+ len = u_min(count, len);
+ offset += tgt->transient_state.cmd_count;
+
+ count = tgt->transient_state.copy_count;
+ tgt->transient_state.copy_count = count + len;
+
+ from = tgt->ior->io_data + count;
+ to = tgt->cmd_ptr + offset;
+
+ bcopy(from, to, len);
+
+ }
+}
+
+#endif /*NSCI > 0*/
+
diff --git a/scsi/adapters/scsi_53C700.h b/scsi/adapters/scsi_53C700.h
new file mode 100644
index 0000000..224fc5b
--- /dev/null
+++ b/scsi/adapters/scsi_53C700.h
@@ -0,0 +1,327 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_53C700.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/91
+ *
+ * Defines for the NCR 53C700 (SCSI I/O Processor)
+ */
+
+/*
+ * Register map
+ */
+
+typedef struct {
+ volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */
+ volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */
+ volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */
+ volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */
+ volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */
+ volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */
+ volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */
+ volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */
+ volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */
+ volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */
+ volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */
+ volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */
+ volatile unsigned char siop_dstat; /* ro: DMA status */
+ volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */
+ volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */
+ volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */
+ volatile unsigned char siop_res1;
+ volatile unsigned char siop_res2;
+ volatile unsigned char siop_res3;
+ volatile unsigned char siop_res4;
+ volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */
+ volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */
+ volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */
+ volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */
+ volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */
+ volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */
+ volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */
+ volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */
+ volatile unsigned char siop_temp0; /* rw: Temporary Stack reg */
+ volatile unsigned char siop_temp1;
+ volatile unsigned char siop_temp2;
+ volatile unsigned char siop_temp3;
+ volatile unsigned char siop_dfifo; /* rw: DMA FIFO */
+ volatile unsigned char siop_istat; /* rw: Interrupt Status reg */
+ volatile unsigned char siop_res5;
+ volatile unsigned char siop_res6;
+ volatile unsigned char siop_dbc0; /* rw: DMA Byte Counter reg */
+ volatile unsigned char siop_dbc1;
+ volatile unsigned char siop_dbc2;
+ volatile unsigned char siop_dcmd; /* rw: DMA Command Register */
+ volatile unsigned char siop_dnad0; /* rw: DMA Next Address */
+ volatile unsigned char siop_dnad1;
+ volatile unsigned char siop_dnad2;
+ volatile unsigned char siop_dnad3;
+ volatile unsigned char siop_dsp0; /* rw: DMA SCRIPTS Pointer reg */
+ volatile unsigned char siop_dsp1;
+ volatile unsigned char siop_dsp2;
+ volatile unsigned char siop_dsp3;
+ volatile unsigned char siop_dsps0; /* rw: DMA SCRIPTS Pointer Save reg */
+ volatile unsigned char siop_dsps1;
+ volatile unsigned char siop_dsps2;
+ volatile unsigned char siop_dsps3;
+ volatile unsigned char siop_dmode; /* rw: DMA Mode reg */
+ volatile unsigned char siop_res7;
+ volatile unsigned char siop_res8;
+ volatile unsigned char siop_res9;
+ volatile unsigned char siop_res10;
+ volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */
+ volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */
+ volatile unsigned char siop_dcntl; /* rw: DMA Control reg */
+ volatile unsigned char siop_res11;
+ volatile unsigned char siop_res12;
+ volatile unsigned char siop_res13;
+ volatile unsigned char siop_res14;
+
+} siop_regmap_t;
+
+/*
+ * Register defines
+ */
+
+/* Scsi control register 0 (scntl0) */
+
+#define SIOP_SCNTL0_ARB 0xc0 /* Arbitration mode */
+# define SIOP_ARB_SIMPLE 0x00
+# define SIOP_ARB_FULL 0xc0
+#define SIOP_SCNTL0_START 0x20 /* Start Sequence */
+#define SIOP_SCNTL0_WATN 0x10 /* (Select) With ATN */
+#define SIOP_SCNTL0_EPC 0x08 /* Enable Parity Checking */
+#define SIOP_SCNTL0_EPG 0x04 /* Enable Parity Generation */
+#define SIOP_SCNTL0_AAP 0x02 /* Assert ATN on Parity Error */
+#define SIOP_SCNTL0_TRG 0x01 /* Target Mode */
+
+/* Scsi control register 1 (scntl1) */
+
+#define SIOP_SCNTL1_EXC 0x80 /* Extra Clock Cycle of data setup */
+#define SIOP_SCNTL1_ADB 0x40 /* Assert Data Bus */
+#define SIOP_SCNTL1_ESR 0x20 /* Enable Selection/Reselection */
+#define SIOP_SCNTL1_CON 0x10 /* Connected */
+#define SIOP_SCNTL1_RST 0x08 /* Assert RST */
+#define SIOP_SCNTL1_PAR 0x04 /* Force bad Parity */
+#define SIOP_SCNTL1_SND 0x02 /* Start Send operation */
+#define SIOP_SCNTL1_RCV 0x01 /* Start Receive operation */
+
+/* Scsi interrupt enable register (sien) */
+
+#define SIOP_SIEN_M_A 0x80 /* Phase Mismatch or ATN active */
+#define SIOP_SIEN_FC 0x40 /* Function Complete */
+#define SIOP_SIEN_STO 0x20 /* (Re)Selection timeout */
+#define SIOP_SIEN_SEL 0x10 /* (Re)Selected */
+#define SIOP_SIEN_SGE 0x08 /* SCSI Gross Error */
+#define SIOP_SIEN_UDC 0x04 /* Unexpected Disconnect */
+#define SIOP_SIEN_RST 0x02 /* RST asserted */
+#define SIOP_SIEN_PAR 0x01 /* Parity Error */
+
+/* Scsi chip ID (scid) */
+
+#define SIOP_SCID_VALUE(i) (1<<i)
+
+/* Scsi transfer register (sxfer) */
+
+#define SIOP_SXFER_DHP 0x80 /* Disable Halt on Parity error/ ATN asserted */
+#define SIOP_SXFER_TP 0x70 /* Synch Transfer Period */
+ /* see specs for formulas:
+ Period = TCP * (4 + XFERP )
+ TCP = 1 + CLK + 1..2;
+ */
+#define SIOP_SXFER_MO 0x0f /* Synch Max Offset */
+# define SIOP_MAX_OFFSET 8
+
+/* Scsi output data latch register (sodl) */
+
+/* Scsi output control latch register (socl) */
+
+#define SIOP_REQ 0x80 /* SCSI signal <x> asserted */
+#define SIOP_ACK 0x40
+#define SIOP_BSY 0x20
+#define SIOP_SEL 0x10
+#define SIOP_ATN 0x08
+#define SIOP_MSG 0x04
+#define SIOP_CD 0x02
+#define SIOP_IO 0x01
+
+#define SIOP_PHASE(socl) SCSI_PHASE(socl)
+
+/* Scsi first byte received register (sfbr) */
+
+/* Scsi input data latch register (sidl) */
+
+/* Scsi bus data lines register (sbdl) */
+
+/* Scsi bus control lines register (sbcl). Same as socl */
+
+/* DMA status register (dstat) */
+
+#define SIOP_DSTAT_DFE 0x80 /* DMA FIFO empty */
+#define SIOP_DSTAT_RES 0x60
+#define SIOP_DSTAT_ABRT 0x10 /* Aborted */
+#define SIOP_DSTAT_SSI 0x08 /* SCRIPT Single Step */
+#define SIOP_DSTAT_SIR 0x04 /* SCRIPT Interrupt Instruction */
+#define SIOP_DSTAT_WTD 0x02 /* Watchdog Timeout Detected */
+#define SIOP_DSTAT_OPC 0x01 /* Invalid SCRIPTS Opcode */
+
+/* Scsi status register 0 (sstat0) */
+
+#define SIOP_SSTAT0_M_A 0x80 /* Phase Mismatch or ATN active */
+#define SIOP_SSTAT0_FC 0x40 /* Function Complete */
+#define SIOP_SSTAT0_STO 0x20 /* (Re)Selection timeout */
+#define SIOP_SSTAT0_SEL 0x10 /* (Re)Selected */
+#define SIOP_SSTAT0_SGE 0x08 /* SCSI Gross Error */
+#define SIOP_SSTAT0_UDC 0x04 /* Unexpected Disconnect */
+#define SIOP_SSTAT0_RST 0x02 /* RST asserted */
+#define SIOP_SSTAT0_PAR 0x01 /* Parity Error */
+
+/* Scsi status register 1 (sstat1) */
+
+#define SIOP_SSTAT1_ILF 0x80 /* Input latch (sidl) full */
+#define SIOP_SSTAT1_ORF 0x40 /* output reg (sodr) full */
+#define SIOP_SSTAT1_OLF 0x20 /* output latch (sodl) full */
+#define SIOP_SSTAT1_AIP 0x10 /* Arbitration in progress */
+#define SIOP_SSTAT1_LOA 0x08 /* Lost arbitration */
+#define SIOP_SSTAT1_WOA 0x04 /* Won arbitration */
+#define SIOP_SSTAT1_RST 0x02 /* SCSI RST current value */
+#define SIOP_SSTAT1_SDP 0x01 /* SCSI SDP current value */
+
+/* Scsi status register 2 (sstat2) */
+
+#define SIOP_SSTAT2_FF 0xf0 /* SCSI FIFO flags (bytecount) */
+# define SIOP_SCSI_FIFO_DEEP 8
+#define SIOP_SSTAT2_SDP 0x08 /* Latched (on REQ) SCSI SDP */
+#define SIOP_SSTAT2_MSG 0x04 /* Latched SCSI phase */
+#define SIOP_SSTAT2_CD 0x02
+#define SIOP_SSTAT2_IO 0x01
+
+/* Chip test register 0 (ctest0) */
+
+#define SIOP_CTEST0_RES 0xfc
+#define SIOP_CTEST0_RTRG 0x02 /* Real Target mode */
+#define SIOP_CTEST0_DDIR 0x01 /* Xfer direction (1-> from SCSI bus) */
+
+/* Chip test register 1 (ctest1) */
+
+#define SIOP_CTEST1_FMT 0xf0 /* Byte empty in DMA FIFO bottom (high->byte3) */
+#define SIOP_CTEST1_FFL 0x0f /* Byte full in DMA FIFO top, same */
+
+/* Chip test register 2 (ctest2) */
+
+#define SIOP_CTEST2_RES 0xc0
+#define SIOP_CTEST2_SOFF 0x20 /* Synch Offset compare (1-> zero Init, max Tgt */
+#define SIOP_CTEST2_SFP 0x10 /* SCSI FIFO Parity */
+#define SIOP_CTEST2_DFP 0x08 /* DMA FIFO Parity */
+#define SIOP_CTEST2_TEOP 0x04 /* True EOP (a-la 5380) */
+#define SIOP_CTEST2_DREQ 0x02 /* DREQ status */
+#define SIOP_CTEST2_DACK 0x01 /* DACK status */
+
+/* Chip test register 3 (ctest3) read-only, top of SCSI FIFO */
+
+/* Chip test register 4 (ctest4) */
+
+#define SIOP_CTEST4_RES 0x80
+#define SIOP_CTEST4_ZMOD 0x40 /* High-impedance outputs */
+#define SIOP_CTEST4_SZM 0x20 /* ditto, SCSI "outputs" */
+#define SIOP_CTEST4_SLBE 0x10 /* SCSI loobpack enable */
+#define SIOP_CTEST4_SFWR 0x08 /* SCSI FIFO write enable (from sodl) */
+#define SIOP_CTEST4_FBL 0x07 /* DMA FIFO Byte Lane select (from ctest6)
+ 4->0, .. 7->3 */
+
+/* Chip test register 5 (ctest5) */
+
+#define SIOP_CTEST5_ADCK 0x80 /* Clock Address Incrementor */
+#define SIOP_CTEST5_BBCK 0x40 /* Clock Byte counter */
+#define SIOP_CTEST5_ROFF 0x20 /* Reset SCSI offset */
+#define SIOP_CTEST5_MASR 0x10 /* Master set/reset pulses (of bits 3-0) */
+#define SIOP_CTEST5_DDIR 0x08 /* (re)set internal DMA direction */
+#define SIOP_CTEST5_EOP 0x04 /* (re)set internal EOP */
+#define SIOP_CTEST5_DREQ 0x02 /* (re)set internal REQ */
+#define SIOP_CTEST5_DACK 0x01 /* (re)set internal ACK */
+
+/* Chip test register 6 (ctest6) DMA FIFO access */
+
+/* Chip test register 7 (ctest7) */
+
+#define SIOP_CTEST7_RES 0xe0
+#define SIOP_CTEST7_STD 0x10 /* Disable selection timeout */
+#define SIOP_CTEST7_DFP 0x08 /* DMA FIFO parity bit */
+#define SIOP_CTEST7_EVP 0x04 /* Even parity (to host bus) */
+#define SIOP_CTEST7_DC 0x02 /* Drive DC pin low on SCRIPT fetches */
+#define SIOP_CTEST7_DIFF 0x01 /* Differential mode */
+
+/* DMA FIFO register (dfifo) */
+
+#define SIOP_DFIFO_FLF 0x80 /* Flush (spill) DMA FIFO */
+#define SIOP_DFIFO_CLF 0x40 /* Clear DMA and SCSI FIFOs */
+#define SIOP_DFIFO_BO 0x3f /* FIFO byte offset counter */
+
+/* Interrupt status register (istat) */
+
+#define SIOP_ISTAT_ABRT 0x80 /* Abort operation */
+#define SIOP_ISTAT_RES 0x70
+#define SIOP_ISTAT_CON 0x08 /* Connected */
+#define SIOP_ISTAT_PRE 0x04 /* Pointer register empty */
+#define SIOP_ISTAT_SIP 0x02 /* SCSI Interrupt pending */
+#define SIOP_ISTAT_DIP 0x01 /* DMA Interrupt pending */
+
+
+/* DMA Mode register (dmode) */
+
+#define SIOP_DMODE_BL_MASK 0xc0 /* 0->1 1->2 2->4 3->8 */
+#define SIOP_DMODE_BW16 0x20 /* Bus Width is 16 bits */
+#define SIOP_DMODE_286 0x10 /* 286 mode */
+#define SIOP_DMODE_IO_M 0x08 /* xfer data to memory or I/O space */
+#define SIOP_DMODE_FAM 0x04 /* fixed address mode */
+#define SIOP_DMODE_PIPE 0x02 /* SCRIPTS in Pipeline mode */
+#define SIOP_DMODE_MAN 0x01 /* SCRIPTS in Manual start mode */
+
+/* DMA interrupt enable register (dien) */
+
+#define SIOP_DIEN_RES 0xe0
+#define SIOP_DIEN_ABRT 0x10 /* On Abort */
+#define SIOP_DIEN_SSI 0x08 /* On SCRIPTS sstep */
+#define SIOP_DIEN_SIR 0x04 /* On SCRIPTS intr instruction */
+#define SIOP_DIEN_WTD 0x02 /* On watchdog timeout */
+#define SIOP_DIEN_OPC 0x01 /* On SCRIPTS illegal opcode */
+
+/* DMA control register (dcntl) */
+
+#define SIOP_DCNTL_CF_MASK 0xc0 /* Clock frequency dividers:
+ 0 --> 37.51..50.00 Mhz, div=2
+ 1 --> 25.01..37.50 Mhz, div=1.5
+ 2 --> 16.67..25.00 Mhz, div=1
+ 3 --> reserved
+ */
+#define SIOP_DCNTL_S16 0x20 /* SCRIPTS fetches 16bits at a time */
+#define SIOP_DCNTL_SSM 0x10 /* Single step mode */
+#define SIOP_DCNTL_LLM 0x08 /* Enable Low-level mode */
+#define SIOP_DCNTL_STD 0x04 /* Start SCRIPTS operation */
+#define SIOP_DCNTL_RES 0x02
+#define SIOP_DCNTL_RST 0x01 /* Software reset */
+
diff --git a/scsi/adapters/scsi_53C700_hdw.c b/scsi/adapters/scsi_53C700_hdw.c
new file mode 100644
index 0000000..61b5a3b
--- /dev/null
+++ b/scsi/adapters/scsi_53C700_hdw.c
@@ -0,0 +1,696 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_53C700_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the NCR 53C700
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+
+#include <siop.h>
+#if NSIOP > 0
+#include <platforms.h>
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+#include <machine/machspl.h>
+
+#include <sys/syslog.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#include <scsi/adapters/scsi_53C700.h>
+
+#ifdef PAD
+typedef struct {
+ volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */
+ PAD(pad0);
+ volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */
+ PAD(pad1);
+ volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */
+ PAD(pad2);
+ volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */
+ PAD(pad3);
+ volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */
+ PAD(pad4);
+ volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */
+ PAD(pad5);
+ volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */
+ PAD(pad6);
+ volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */
+ PAD(pad7);
+ volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */
+ PAD(pad8);
+ volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */
+ PAD(pad9);
+ volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */
+ PAD(pad10);
+ volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */
+ PAD(pad11);
+ volatile unsigned char siop_dstat; /* ro: DMA status */
+ PAD(pad12);
+ volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */
+ PAD(pad13);
+ volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */
+ PAD(pad14);
+ volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */
+ PAD(pad15);
+ volatile unsigned char siop_res1;
+ PAD(pad16);
+ volatile unsigned char siop_res2;
+ PAD(pad17);
+ volatile unsigned char siop_res3;
+ PAD(pad18);
+ volatile unsigned char siop_res4;
+ PAD(pad19);
+ volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */
+ PAD(pad20);
+ volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */
+ PAD(pad21);
+ volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */
+ PAD(pad22);
+ volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */
+ PAD(pad23);
+ volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */
+ PAD(pad24);
+ volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */
+ PAD(pad25);
+ volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */
+ PAD(pad26);
+ volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */
+ PAD(pad27);
+ volatile unsigned char siop_temp0; /* rw: Temporary Stack reg */
+ PAD(pad28);
+ volatile unsigned char siop_temp1;
+ PAD(pad29);
+ volatile unsigned char siop_temp2;
+ PAD(pad30);
+ volatile unsigned char siop_temp3;
+ PAD(pad31);
+ volatile unsigned char siop_dfifo; /* rw: DMA FIFO */
+ PAD(pad32);
+ volatile unsigned char siop_istat; /* rw: Interrupt Status reg */
+ PAD(pad33);
+ volatile unsigned char siop_res5;
+ PAD(pad34);
+ volatile unsigned char siop_res6;
+ PAD(pad35);
+ volatile unsigned char siop_dbc0; /* rw: DMA Byte Counter reg */
+ PAD(pad36);
+ volatile unsigned char siop_dbc1;
+ PAD(pad37);
+ volatile unsigned char siop_dbc2;
+ PAD(pad38);
+ volatile unsigned char siop_dcmd; /* rw: DMA Command Register */
+ PAD(pad39);
+ volatile unsigned char siop_dnad0; /* rw: DMA Next Address */
+ PAD(pad40);
+ volatile unsigned char siop_dnad1;
+ PAD(pad41);
+ volatile unsigned char siop_dnad2;
+ PAD(pad42);
+ volatile unsigned char siop_dnad3;
+ PAD(pad43);
+ volatile unsigned char siop_dsp0; /* rw: DMA SCRIPTS Pointer reg */
+ PAD(pad44);
+ volatile unsigned char siop_dsp1;
+ PAD(pad45);
+ volatile unsigned char siop_dsp2;
+ PAD(pad46);
+ volatile unsigned char siop_dsp3;
+ PAD(pad47);
+ volatile unsigned char siop_dsps0; /* rw: DMA SCRIPTS Pointer Save reg */
+ PAD(pad48);
+ volatile unsigned char siop_dsps1;
+ PAD(pad49);
+ volatile unsigned char siop_dsps2;
+ PAD(pad50);
+ volatile unsigned char siop_dsps3;
+ PAD(pad51);
+ volatile unsigned char siop_dmode; /* rw: DMA Mode reg */
+ PAD(pad52);
+ volatile unsigned char siop_res7;
+ PAD(pad53);
+ volatile unsigned char siop_res8;
+ PAD(pad54);
+ volatile unsigned char siop_res9;
+ PAD(pad55);
+ volatile unsigned char siop_res10;
+ PAD(pad56);
+ volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */
+ PAD(pad57);
+ volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */
+ PAD(pad58);
+ volatile unsigned char siop_dcntl; /* rw: DMA Control reg */
+ PAD(pad59);
+ volatile unsigned char siop_res11;
+ PAD(pad60);
+ volatile unsigned char siop_res12;
+ PAD(pad61);
+ volatile unsigned char siop_res13;
+ PAD(pad62);
+ volatile unsigned char siop_res14;
+ PAD(pad63);
+} siop_padded_regmap_t;
+#else
+typedef siop_regmap_t siop_padded_regmap_t;
+#endif
+
+/*
+ * Macros to make certain things a little more readable
+ */
+
+/* forward decls */
+
+int siop_reset_scsibus();
+boolean_t siop_probe_target();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) 53C700 interface
+ */
+struct siop_softc {
+ watchdog_t wd;
+ siop_padded_regmap_t *regs; /* 53C700 registers */
+ scsi_dma_ops_t *dma_ops; /* DMA operations and state */
+ opaque_t dma_state;
+
+ script_t script;
+ int (*error_handler)();
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define SIOP_STATE_BUSY 0x01 /* selecting or currently connected */
+#define SIOP_STATE_TARGET 0x04 /* currently selected as target */
+#define SIOP_STATE_COLLISION 0x08 /* lost selection attempt */
+#define SIOP_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+
+ scsi_softc_t *sc;
+ target_info_t *active_target;
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+
+} siop_softc_data[NSIOP];
+
+typedef struct siop_softc *siop_softc_t;
+
+siop_softc_t siop_softc[NSIOP];
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int siop_probe(), scsi_slave(), scsi_attach(), siop_go(), siop_intr();
+
+caddr_t siop_std[NSIOP] = { 0 };
+struct bus_device *siop_dinfo[NSIOP*8];
+struct bus_ctlr *siop_minfo[NSIOP];
+struct bus_driver siop_driver =
+ { siop_probe, scsi_slave, scsi_attach, siop_go, siop_std, "rz", siop_dinfo,
+ "siop", siop_minfo, BUS_INTR_B4_PROBE};
+
+/*
+ * Scripts
+ */
+struct script
+siop_script_data_in[] = {
+},
+
+siop_script_data_out[] = {
+},
+
+siop_script_cmd[] = {
+},
+
+/* Synchronous transfer neg(oti)ation */
+
+siop_script_try_synch[] = {
+},
+
+/* Disconnect sequence */
+
+siop_script_disconnect[] = {
+};
+
+
+#define DEBUG
+#ifdef DEBUG
+
+siop_state(base)
+ vm_offset_t base;
+{
+ siop_padded_regmap_t *regs;
+....
+ return 0;
+}
+siop_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = siop_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n",
+ tgt,
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym(spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+siop_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = siop_softc[unit]->sc->target[i];
+ if (tgt)
+ siop_target_state(tgt);
+ }
+}
+
+siop_script_state(unit)
+{
+ script_t spt = siop_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym(spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(siop_softc[unit]->error_handler, 1);
+ return 0;
+
+}
+
+#define PRINT(x) if (scsi_debug) printf x
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int siop_logpt;
+char siop_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x24
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ siop_log[siop_logpt++] = (e);
+ if (siop_logpt == LOGSIZE) siop_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+siop_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = siop_logpt; i < LOGSIZE; i++) {
+ c = siop_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-%d", c & 0x7f);
+ }
+ db_printf("\n");
+ return 0;
+}
+
+siop_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /* TRACE */
+#define LOG(e,f)
+#endif /* TRACE */
+
+#else /* DEBUG */
+#define PRINT(x)
+#define LOG(e,f)
+#define TR(x)
+#define TRCHECK
+#define TRWRAP
+#endif /* DEBUG */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send an identify msg to each possible target on the bus
+ * except of course ourselves.
+ */
+siop_probe(reg, ui)
+ char *reg;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ siop_softc_t siop = &siop_softc_data[unit];
+ int target_id, i;
+ scsi_softc_t *sc;
+ register siop_padded_regmap_t *regs;
+ int s;
+ boolean_t did_banner = FALSE;
+ char *cmd_ptr;
+ static char *here = "siop_probe";
+
+ /*
+ * We are only called if the chip is there,
+ * but make sure anyways..
+ */
+ regs = (siop_padded_regmap_t *) (reg);
+ if (check_memory(regs, 0))
+ return 0;
+
+#if notyet
+ /* Mappable version side */
+ SIOP_probe(reg, ui);
+#endif
+
+ /*
+ * Initialize hw descriptor
+ */
+ siop_softc[unit] = siop;
+ siop->regs = regs;
+
+ if ((siop->dma_ops = (scsi_dma_ops_t *)siop_std[unit]) == 0)
+ /* use same as unit 0 if undefined */
+ siop->dma_ops = (scsi_dma_ops_t *)siop_std[0];
+ siop->dma_state = (*siop->dma_ops->init)(unit, reg);
+
+ queue_init(&siop->waiting_targets);
+
+ sc = scsi_master_alloc(unit, siop);
+ siop->sc = sc;
+
+ sc->go = siop_go;
+ sc->probe = siop_probe_target;
+ sc->watchdog = scsi_watchdog;
+ siop->wd.reset = siop_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1; /* unlimited */
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ /*
+ * Reset chip
+ */
+ s = splbio();
+ siop_reset(siop, TRUE);
+
+ /*
+ * Our SCSI id on the bus.
+ */
+
+ sc->initiator_id = my_scsi_id(unit);
+ printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ for (target_id = 0; target_id < 8; target_id++) {
+
+ register unsigned csr, dsr;
+ scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ .....
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ .....
+
+
+ /*
+ * Found a target
+ */
+ siop->ntargets++;
+ {
+ register target_info_t *tgt;
+
+ tgt = scsi_slave_alloc(unit, target_id, siop);
+
+ tgt->cmd_ptr = ...
+ tgt->dma_ptr = ...
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+ }
+ }
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+siop_probe_target(sc, tgt, ior)
+ scsi_softc_t *sc;
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ siop_softc_t siop = siop_softc[sc->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+
+ tgt->cmd_ptr = ...
+ tgt->dma_ptr = ...
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+
+ }
+
+ if (scsi_inquiry(sc, tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+
+static siop_wait(preg, until)
+ volatile unsigned char *preg;
+{
+ int timeo = 1000000;
+ while ((*preg & until) != until) {
+ delay(1);
+ if (!timeo--) {
+ printf("siop_wait TIMEO with x%x\n", *preg);
+ break;
+ }
+ }
+ return *preg;
+}
+
+
+siop_reset(siop, quickly)
+ siop_softc_t siop;
+ boolean_t quickly;
+{
+ register siop_padded_regmap_t *regs = siop->regs;
+
+ ....
+
+ if (quickly)
+ return;
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call siop_bus_reset().
+ */
+ ....
+
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+siop_go(sc, tgt, cmd_count, in_count, cmd_only)
+ scsi_softc_t *sc;
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ siop_softc_t siop;
+ register int s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+
+ LOG(1,"go");
+
+ siop = (siop_softc_t)tgt->hw_state;
+
+ ....
+}
+
+siop_attempt_selection(siop)
+ siop_softc_t siop;
+{
+ target_info_t *tgt;
+ register int out_count;
+ siop_padded_regmap_t *regs;
+ register int cmd;
+ boolean_t ok;
+ scsi_ret_t ret;
+
+ regs = siop->regs;
+ tgt = siop->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * Init bus state variables and set registers.
+ */
+ siop->active_target = tgt;
+
+ /* reselection pending ? */
+ ......
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+siop_intr(unit)
+{
+ register siop_softc_t siop;
+ register script_t scp;
+ register unsigned csr, bs, cmd;
+ register siop_padded_regmap_t *regs;
+ boolean_t try_match;
+#if notyet
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return SIOP_intr(unit);
+#endif
+
+ LOG(5,"\n\tintr");
+
+ siop = siop_softc[unit];
+ regs = siop->regs;
+
+ /* ack interrupt */
+ ....
+}
+
+
+siop_target_intr(siop)
+ register siop_softc_t siop;
+{
+ panic("SIOP: TARGET MODE !!!\n");
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to
+ */
+
+#endif /*NSIOP > 0*/
+
diff --git a/scsi/adapters/scsi_53C94.h b/scsi/adapters/scsi_53C94.h
new file mode 100644
index 0000000..82891f3
--- /dev/null
+++ b/scsi/adapters/scsi_53C94.h
@@ -0,0 +1,253 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_53C94.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 9/90
+ *
+ * Defines for the NCR 53C94 ASC (SCSI interface)
+ * Some gotcha came from the "86C01/53C94 DMA lab work" written
+ * by Ken Stewart (NCR MED Logic Products Applications Engineer)
+ * courtesy of NCR. Thanks Ken !
+ */
+
+/*
+ * Register map
+ */
+
+typedef struct {
+ volatile unsigned char asc_tc_lsb; /* rw: Transfer Counter LSB */
+ volatile unsigned char asc_tc_msb; /* rw: Transfer Counter MSB */
+ volatile unsigned char asc_fifo; /* rw: FIFO top */
+ volatile unsigned char asc_cmd; /* rw: Command */
+ volatile unsigned char asc_csr; /* r: Status */
+#define asc_dbus_id asc_csr /* w: Destination Bus ID */
+ volatile unsigned char asc_intr; /* r: Interrupt */
+#define asc_sel_timo asc_intr /* w: (re)select timeout */
+ volatile unsigned char asc_ss; /* r: Sequence Step */
+#define asc_syn_p asc_ss /* w: synchronous period */
+ volatile unsigned char asc_flags; /* r: FIFO flags + seq step */
+#define asc_syn_o asc_flags /* w: synchronous offset */
+ volatile unsigned char asc_cnfg1; /* rw: Configuration 1 */
+ volatile unsigned char asc_ccf; /* w: Clock Conv. Factor */
+ volatile unsigned char asc_test; /* w: Test Mode */
+ volatile unsigned char asc_cnfg2; /* rw: Configuration 2 */
+ volatile unsigned char asc_cnfg3; /* rw: Configuration 3 */
+ volatile unsigned char asc_rfb; /* w: Reserve FIFO byte */
+} asc_regmap_t;
+
+
+/*
+ * Transfer Count: access macros
+ * That a NOP is required after loading the dma counter
+ * I learned on the NCR test code. Sic.
+ */
+
+#define ASC_TC_MAX 0x10000
+
+#define ASC_TC_GET(ptr,val) \
+ val = ((ptr)->asc_tc_lsb&0xff)|(((ptr)->asc_tc_msb&0xff)<<8)
+#define ASC_TC_PUT(ptr,val) \
+ (ptr)->asc_tc_lsb=(val); \
+ (ptr)->asc_tc_msb=(val)>>8; mb(); \
+ (ptr)->asc_cmd = ASC_CMD_NOP|ASC_CMD_DMA;
+
+/*
+ * FIFO register
+ */
+
+#define ASC_FIFO_DEEP 16
+
+
+/*
+ * Command register (command codes)
+ */
+
+#define ASC_CMD_DMA 0x80
+ /* Miscellaneous */
+#define ASC_CMD_NOP 0x00
+#define ASC_CMD_FLUSH 0x01
+#define ASC_CMD_RESET 0x02
+#define ASC_CMD_BUS_RESET 0x03
+ /* Initiator state */
+#define ASC_CMD_XFER_INFO 0x10
+#define ASC_CMD_I_COMPLETE 0x11
+#define ASC_CMD_MSG_ACPT 0x12
+#define ASC_CMD_XFER_PAD 0x18
+#define ASC_CMD_SET_ATN 0x1a
+#define ASC_CMD_CLR_ATN 0x1b
+ /* Target state */
+#define ASC_CMD_SND_MSG 0x20
+#define ASC_CMD_SND_STATUS 0x21
+#define ASC_CMD_SND_DATA 0x22
+#define ASC_CMD_DISC_SEQ 0x23
+#define ASC_CMD_TERM 0x24
+#define ASC_CMD_T_COMPLETE 0x25
+#define ASC_CMD_DISC 0x27
+#define ASC_CMD_RCV_MSG 0x28
+#define ASC_CMD_RCV_CDB 0x29
+#define ASC_CMD_RCV_DATA 0x2a
+#define ASC_CMD_RCV_CMD 0x2b
+#define ASC_CMD_ABRT_DMA 0x04
+ /* Disconnected state */
+#define ASC_CMD_RESELECT 0x40
+#define ASC_CMD_SEL 0x41
+#define ASC_CMD_SEL_ATN 0x42
+#define ASC_CMD_SEL_ATN_STOP 0x43
+#define ASC_CMD_ENABLE_SEL 0x44
+#define ASC_CMD_DISABLE_SEL 0x45
+#define ASC_CMD_SEL_ATN3 0x46
+
+/* this is approximate (no ATN3) but good enough */
+#define asc_isa_select(cmd) (((cmd)&0x7c)==0x40)
+
+/*
+ * Status register, and phase encoding
+ */
+
+#define ASC_CSR_INT 0x80
+#define ASC_CSR_GE 0x40
+#define ASC_CSR_PE 0x20
+#define ASC_CSR_TC 0x10
+#define ASC_CSR_VGC 0x08
+#define ASC_CSR_MSG 0x04
+#define ASC_CSR_CD 0x02
+#define ASC_CSR_IO 0x01
+
+#define ASC_PHASE(csr) SCSI_PHASE(csr)
+
+/*
+ * Destination Bus ID
+ */
+
+#define ASC_DEST_ID_MASK 0x07
+
+
+/*
+ * Interrupt register
+ */
+
+#define ASC_INT_RESET 0x80
+#define ASC_INT_ILL 0x40
+#define ASC_INT_DISC 0x20
+#define ASC_INT_BS 0x10
+#define ASC_INT_FC 0x08
+#define ASC_INT_RESEL 0x04
+#define ASC_INT_SEL_ATN 0x02
+#define ASC_INT_SEL 0x01
+
+
+/*
+ * Timeout register:
+ *
+ * val = (timeout * CLK_freq) / (8192 * CCF);
+ */
+
+#define asc_timeout_250(clk,ccf) ((31*clk)/ccf)
+
+/*
+ * Sequence Step register
+ */
+
+#define ASC_SS_XXXX 0xf0
+#define ASC_SS_SOM 0x80
+#define ASC_SS_MASK 0x07
+#define ASC_SS(ss) ((ss)&ASC_SS_MASK)
+
+/*
+ * Synchronous Transfer Period
+ */
+
+#define ASC_STP_MASK 0x1f
+#define ASC_STP_MIN 0x05 /* 5 clk per byte */
+#define ASC_STP_MAX 0x04 /* after ovfl, 35 clk/byte */
+
+/*
+ * FIFO flags
+ */
+
+#define ASC_FLAGS_SEQ_STEP 0xe0
+#define ASC_FLAGS_FIFO_CNT 0x1f
+
+/*
+ * Synchronous offset
+ */
+
+#define ASC_SYNO_MASK 0x0f /* 0 -> asyn */
+
+/*
+ * Configuration 1
+ */
+
+#define ASC_CNFG1_SLOW 0x80
+#define ASC_CNFG1_SRD 0x40
+#define ASC_CNFG1_P_TEST 0x20
+#define ASC_CNFG1_P_CHECK 0x10
+#define ASC_CNFG1_TEST 0x08
+#define ASC_CNFG1_MY_BUS_ID 0x07
+
+/*
+ * CCF register
+ */
+
+#define ASC_CCF_10MHz 0x2
+#define ASC_CCF_15MHz 0x3
+#define ASC_CCF_20MHz 0x4
+#define ASC_CCF_25MHz 0x5
+
+#define mhz_to_ccf(x) (((x-1)/5)+1) /* see specs for limits */
+
+/*
+ * Test register
+ */
+
+#define ASC_TEST_XXXX 0xf8
+#define ASC_TEST_HI_Z 0x04
+#define ASC_TEST_I 0x02
+#define ASC_TEST_T 0x01
+
+/*
+ * Configuration 2
+ */
+
+#define ASC_CNFG2_RFB 0x80
+#define ASC_CNFG2_EPL 0x40
+#define ASC_CNFG2_EBC 0x20
+#define ASC_CNFG2_DREQ_HIZ 0x10
+#define ASC_CNFG2_SCSI2 0x08
+#define ASC_CNFG2_BPA 0x04
+#define ASC_CNFG2_RPE 0x02
+#define ASC_CNFG2_DPE 0x01
+
+/*
+ * Configuration 3
+ */
+
+#define ASC_CNFG3_XXXX 0xf8
+#define ASC_CNFG3_SRB 0x04
+#define ASC_CNFG3_ALT_DMA 0x02
+#define ASC_CNFG3_T8 0x01
+
diff --git a/scsi/adapters/scsi_53C94_hdw.c b/scsi/adapters/scsi_53C94_hdw.c
new file mode 100644
index 0000000..dad9b22
--- /dev/null
+++ b/scsi/adapters/scsi_53C94_hdw.c
@@ -0,0 +1,2840 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS AS-IS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_53C94_hdw.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 9/90
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the NCR 53C94
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+#include <asc.h>
+#if NASC > 0
+#include <platforms.h>
+
+#ifdef DECSTATION
+typedef unsigned char asc_register_t;
+#define PAD(n) char n[3];
+#define mb()
+#ifdef MACH_KERNEL
+#define HAS_MAPPED_SCSI
+#endif
+#define ASC_PROBE_DYNAMICALLY FALSE /* established custom */
+#define DEBUG 1
+#define TRACE 1
+#endif
+
+#ifdef FLAMINGO
+typedef unsigned int asc_register_t;
+#define PAD(n) int n; /* sparse ! */
+#define mb() wbflush() /* memory barrier */
+#define ASC_PROBE_DYNAMICALLY TRUE
+#define DEBUG 1
+#define TRACE 1
+#endif
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+#include <machine/machspl.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+
+#include <scsi/adapters/scsi_53C94.h>
+#include <scsi/scsi_defs.h>
+#include <scsi/adapters/scsi_dma.h>
+
+#define private static
+
+#ifdef PAD
+typedef struct {
+ volatile asc_register_t asc_tc_lsb; /* rw: Transfer Counter LSB */
+ PAD(pad0)
+ volatile asc_register_t asc_tc_msb; /* rw: Transfer Counter MSB */
+ PAD(pad1)
+ volatile asc_register_t asc_fifo; /* rw: FIFO top */
+ PAD(pad2)
+ volatile asc_register_t asc_cmd; /* rw: Command */
+ PAD(pad3)
+ volatile asc_register_t asc_csr; /* r: Status */
+/*#define asc_dbus_id asc_csr /* w: Destination Bus ID */
+ PAD(pad4)
+ volatile asc_register_t asc_intr; /* r: Interrupt */
+/*#define asc_sel_timo asc_intr /* w: (re)select timeout */
+ PAD(pad5)
+ volatile asc_register_t asc_ss; /* r: Sequence Step */
+/*#define asc_syn_p asc_ss /* w: synchronous period */
+ PAD(pad6)
+ volatile asc_register_t asc_flags; /* r: FIFO flags + seq step */
+/*#define asc_syn_o asc_flags /* w: synchronous offset */
+ PAD(pad7)
+ volatile asc_register_t asc_cnfg1; /* rw: Configuration 1 */
+ PAD(pad8)
+ volatile asc_register_t asc_ccf; /* w: Clock Conv. Factor */
+ PAD(pad9)
+ volatile asc_register_t asc_test; /* w: Test Mode */
+ PAD(pad10)
+ volatile asc_register_t asc_cnfg2; /* rw: Configuration 2 */
+ PAD(pad11)
+ volatile asc_register_t asc_cnfg3; /* rw: Configuration 3 */
+ PAD(pad12)
+ volatile asc_register_t asc_rfb; /* w: Reserve FIFO byte */
+ PAD(pad13)
+} asc_padded_regmap_t;
+
+#else /* !PAD */
+
+typedef asc_regmap_t asc_padded_regmap_t;
+
+#endif /* !PAD */
+
+#define get_reg(r,x) ((unsigned char)((r)->x))
+
+#define fifo_count(r) ((r)->asc_flags & ASC_FLAGS_FIFO_CNT)
+#define get_fifo(r) get_reg(r,asc_fifo)
+
+boolean_t asc_probe_dynamically = ASC_PROBE_DYNAMICALLY;
+
+/*
+ * We might need to use some fields usually
+ * handled by the DMA engine, if asked to.
+ * These are "dma_ptr" and "hba_dep".
+ */
+#define has_oddb hba_dep[0]
+#define the_oddb hba_dep[1]
+
+/*
+ * A script has a three parts: a pre-condition, an action, and
+ * an optional command to the chip. The first triggers error
+ * handling if not satisfied and in our case it is a match
+ * of the expected and actual scsi-bus phases.
+ * The action part is just a function pointer, and the
+ * command is what the 53c90 should be told to do at the end
+ * of the action processing. This command is only issued and the
+ * script proceeds if the action routine returns TRUE.
+ * See asc_intr() for how and where this is all done.
+ */
+
+typedef struct script {
+ unsigned char condition; /* expected state at interrupt */
+ unsigned char command; /* command to the chip */
+ unsigned short flags; /* unused padding */
+ boolean_t (*action)(); /* extra operations */
+} *script_t;
+
+/* Matching on the condition value */
+#define ANY 0xff
+#define SCRIPT_MATCH(csr,ir,value) ((SCSI_PHASE(csr)==(value)) || \
+ (((value)==ANY) && \
+ ((ir)&(ASC_INT_DISC|ASC_INT_FC))))
+
+/* When no command is needed */
+#define SCRIPT_END -1
+
+/* forward decls of script actions */
+boolean_t
+ asc_end(), /* all come to an end */
+ asc_clean_fifo(), /* .. in preparation for status byte */
+ asc_get_status(), /* get status from target */
+ asc_put_status(), /* send status to initiator */
+ asc_dma_in(), /* get data from target via dma */
+ asc_dma_in_r(), /* get data from target via dma (restartable)*/
+ asc_dma_out(), /* send data to target via dma */
+ asc_dma_out_r(), /* send data to target via dma (restartable) */
+ asc_dosynch(), /* negotiate synch xfer */
+ asc_msg_in(), /* receive the disconenct message */
+ asc_disconnected(), /* target has disconnected */
+ asc_reconnect(); /* target reconnected */
+
+/* forward decls of error handlers */
+boolean_t
+ asc_err_generic(), /* generic handler */
+ asc_err_disconn(), /* target disconnects amidst */
+ gimmeabreak(); /* drop into the debugger */
+
+int asc_reset_scsibus();
+boolean_t asc_probe_target();
+private asc_wait();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) SCSI-53c90 interface
+ */
+struct asc_softc {
+ watchdog_t wd;
+ asc_padded_regmap_t *regs; /* 53c90 registers */
+
+ scsi_dma_ops_t *dma_ops; /* DMA operations and state */
+ opaque_t dma_state;
+
+ script_t script; /* what should happen next */
+ boolean_t (*error_handler)();/* what if something is wrong */
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define ASC_STATE_BUSY 0x01 /* selecting or currently connected */
+#define ASC_STATE_TARGET 0x04 /* currently selected as target */
+#define ASC_STATE_COLLISION 0x08 /* lost selection attempt */
+#define ASC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+#define ASC_STATE_SPEC_DMA 0x20 /* special, 8 byte threshold dma */
+#define ASC_STATE_DO_RFB 0x40 /* DMA engine cannot handle odd bytes */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+ unsigned char extra_count; /* sleazy trick to spare an interrupt */
+ int dmacnt_at_end;
+
+ scsi_softc_t *sc; /* HBA-indep info */
+ target_info_t *active_target; /* the current one */
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+
+ unsigned char ss_was; /* districate powered on/off devices */
+ unsigned char cmd_was;
+
+ unsigned char timeout; /* cache a couple numbers */
+ unsigned char ccf;
+ unsigned char clk;
+
+} asc_softc_data[NASC];
+
+typedef struct asc_softc *asc_softc_t;
+
+asc_softc_t asc_softc[NASC];
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int asc_min_period = 5; /* in CLKS/BYTE, e.g. 1 CLK = 40nsecs @25 Mhz */
+int asc_max_offset = 15; /* pure number */
+
+int asc_to_scsi_period(a,clk)
+{
+ /* Note: the SCSI unit is 4ns, hence
+ A_P * 1,000,000,000
+ ------------------- = S_P
+ C_Mhz * 4
+ */
+ return a * (250 / clk);
+
+}
+
+int scsi_period_to_asc(p,clk)
+{
+ register int ret;
+
+ ret = (p * clk) / 250;
+ if (ret < asc_min_period)
+ return asc_min_period;
+ if ((asc_to_scsi_period(ret,clk)) < p)
+ return ret + 1;
+ return ret;
+}
+
+#define readback(a) {register int foo; foo = a; mb();}
+
+#define u_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int asc_probe(), scsi_slave(), asc_go(), asc_intr();
+void scsi_attach();
+
+vm_offset_t asc_std[NASC] = { 0 };
+struct bus_device *asc_dinfo[NASC*8];
+struct bus_ctlr *asc_minfo[NASC];
+struct bus_driver asc_driver =
+ { asc_probe, scsi_slave, scsi_attach, asc_go, asc_std, "rz", asc_dinfo,
+ "asc", asc_minfo, BUS_INTR_B4_PROBE};
+
+
+int asc_clock_speed_in_mhz[NASC] = {25,25,25,25}; /* original 3max */
+
+asc_set_dmaops(unit, dmaops)
+ unsigned int unit;
+ scsi_dma_ops_t *dmaops;
+{
+ if (unit < NASC)
+ asc_std[unit] = (vm_offset_t)dmaops;
+}
+
+/*
+ * Scripts
+ */
+struct script
+asc_script_data_in[] = { /* started with SEL & DMA */
+ {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in},
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_data_out[] = { /* started with SEL & DMA */
+ {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out},
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_try_synch[] = {
+ {SCSI_PHASE_MSG_OUT, ASC_CMD_I_COMPLETE,0, asc_dosynch},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_simple_cmd[] = {
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_disconnect[] = {
+ {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected},
+/**/ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect}
+},
+
+asc_script_restart_data_in[] = { /* starts after disconnect */
+ {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in_r},
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_restart_data_out[] = { /* starts after disconnect */
+ {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out_r},
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+#if documentation
+/*
+ * This is what might happen during a read
+ * that disconnects
+ */
+asc_script_data_in_wd[] = { /* started with SEL & DMA & allow disconnect */
+ {SCSI_PHASE_MSG_IN, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_msg_in},
+ {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect},
+ {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in},
+ {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo},
+ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+#endif
+
+/*
+ * Target mode scripts
+ */
+asc_script_t_data_in[] = {
+ {SCSI_PHASE_CMD, ASC_CMD_RCV_DATA|ASC_CMD_DMA, 0, asc_dma_in_r},
+ {SCSI_PHASE_DATAO, ASC_CMD_TERM, 0, asc_put_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+},
+
+asc_script_t_data_out[] = {
+ {SCSI_PHASE_CMD, ASC_CMD_SND_DATA|ASC_CMD_DMA, 0, asc_dma_out_r},
+ {SCSI_PHASE_DATAI, ASC_CMD_TERM, 0, asc_put_status},
+ {ANY, SCRIPT_END, 0, asc_end}
+};
+
+
+#ifdef DEBUG
+
+#define PRINT(x) if (scsi_debug) printf x
+
+asc_state(regs)
+ asc_padded_regmap_t *regs;
+{
+ register unsigned char ff,csr,ir,d0,d1,cmd;
+
+ if (regs == 0) {
+ if (asc_softc[0])
+ regs = asc_softc[0]->regs;
+ else
+ regs = (asc_padded_regmap_t*)0xbf400000;
+ }
+ ff = get_reg(regs,asc_flags);
+ csr = get_reg(regs,asc_csr);
+/* ir = get_reg(regs,asc_intr); nope, clears interrupt */
+ d0 = get_reg(regs,asc_tc_lsb);
+ d1 = get_reg(regs,asc_tc_msb);
+ cmd = get_reg(regs,asc_cmd);
+ printf("dma %x ff %x csr %x cmd %x\n",
+ (d1 << 8) | d0, ff, csr, cmd);
+ return 0;
+}
+
+asc_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = asc_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n",
+ tgt,
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, (long)tgt->target_id,
+ (long)tgt->sync_period, (long)tgt->sync_offset,
+ tgt->ior, (long)tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym(spt,1);
+ db_printf(": %x %x ", spt->condition, spt->command);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+asc_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = asc_softc[unit]->sc->target[i];
+ if (tgt)
+ asc_target_state(tgt);
+ }
+}
+
+asc_script_state(unit)
+{
+ script_t spt = asc_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym(spt,1);
+ db_printf(": %x %x ", spt->condition, spt->command);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(asc_softc[unit]->error_handler, 1);
+ return 0;
+}
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int asc_logpt;
+char asc_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x42
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+/* private */ LOG(e,f)
+ char *f;
+{
+ asc_log[asc_logpt++] = (e);
+ if (asc_logpt == LOGSIZE) asc_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+asc_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = asc_logpt; i < LOGSIZE; i++) {
+ c = asc_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-x%x", c & 0x7f);
+ }
+}
+
+asc_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /*TRACE*/
+#define LOG(e,f)
+#define LOGSIZE
+#endif /*TRACE*/
+
+#else /*DEBUG*/
+#define PRINT(x)
+#define LOG(e,f)
+#define LOGSIZE
+#define TR(x)
+#define TRCHECK
+#define TRWRAP
+
+#endif /*DEBUG*/
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send a test-unit-ready to each possible target on the bus
+ * except of course ourselves.
+ */
+asc_probe(reg, ui)
+ vm_offset_t reg;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ asc_softc_t asc = &asc_softc_data[unit];
+ int target_id;
+ scsi_softc_t *sc;
+ register asc_padded_regmap_t *regs;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+
+ /*
+ * We are only called if the right board is there,
+ * but make sure anyways..
+ */
+ if (check_memory(reg, 0))
+ return 0;
+
+#if defined(HAS_MAPPED_SCSI)
+ /* Mappable version side */
+ ASC_probe(reg, ui);
+#endif
+
+ /*
+ * Initialize hw descriptor, cache some pointers
+ */
+ asc_softc[unit] = asc;
+ asc->regs = (asc_padded_regmap_t *) (reg);
+
+ if ((asc->dma_ops = (scsi_dma_ops_t *)asc_std[unit]) == 0)
+ /* use same as unit 0 if undefined */
+ asc->dma_ops = (scsi_dma_ops_t *)asc_std[0];
+ {
+ int dma_bsize = 16; /* bits, preferred */
+ boolean_t do_rfb = FALSE;
+
+ asc->dma_state = (*asc->dma_ops->init)(unit, reg, &dma_bsize, &do_rfb);
+ if (dma_bsize > 16)
+ asc->state |= ASC_STATE_SPEC_DMA;
+ if (do_rfb)
+ asc->state |= ASC_STATE_DO_RFB;
+ }
+
+ queue_init(&asc->waiting_targets);
+
+ asc->clk = asc_clock_speed_in_mhz[unit];
+ asc->ccf = mhz_to_ccf(asc->clk); /* see .h file */
+ asc->timeout = asc_timeout_250(asc->clk,asc->ccf);
+
+ sc = scsi_master_alloc(unit, asc);
+ asc->sc = sc;
+
+ sc->go = asc_go;
+ sc->watchdog = scsi_watchdog;
+ sc->probe = asc_probe_target;
+ asc->wd.reset = asc_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1;
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ regs = asc->regs;
+
+ /*
+ * Our SCSI id on the bus.
+ * The user can set this via the prom on 3maxen/pmaxen.
+ * If this changes it is easy to fix: make a default that
+ * can be changed as boot arg.
+ */
+ {
+ register unsigned char my_id;
+
+ my_id = scsi_initiator_id[unit] & 0x7;
+ if (my_id != 7)
+ regs->asc_cnfg1 = my_id; mb();
+ }
+
+ /*
+ * Reset chip, fully. Note that interrupts are already enabled.
+ */
+ s = splbio();
+ asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA);
+
+ sc->initiator_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
+ printf("%s%d: SCSI id %d", ui->name, unit, sc->initiator_id);
+
+ {
+ register target_info_t *tgt;
+
+ tgt = scsi_slave_alloc(sc->masterno, sc->initiator_id, asc);
+ (*asc->dma_ops->new_target)(asc->dma_state, tgt);
+ sccpu_new_initiator(tgt, tgt);
+ }
+
+ if (asc_probe_dynamically)
+ printf("%s", ", will probe targets on demand");
+ else {
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ for (target_id = 0; target_id < 8; target_id++) {
+ register unsigned char csr, ss, ir, ff;
+ register scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ regs->asc_cmd = ASC_CMD_FLUSH; /* empty fifo */
+ mb();
+ delay(2);
+
+ regs->asc_dbus_id = target_id; mb();
+ regs->asc_sel_timo = asc->timeout; mb();
+
+ /*
+ * See if the unit is ready.
+ * XXX SHOULD inquiry LUN 0 instead !!!
+ */
+ regs->asc_fifo = SCSI_CMD_TEST_UNIT_READY; mb();
+ regs->asc_fifo = 0; mb();
+ regs->asc_fifo = 0; mb();
+ regs->asc_fifo = 0; mb();
+ regs->asc_fifo = 0; mb();
+ regs->asc_fifo = 0; mb();
+
+ /* select and send it */
+ regs->asc_cmd = ASC_CMD_SEL; mb();
+
+ /* wait for the chip to complete, or timeout */
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ss = get_reg(regs,asc_ss);
+ ir = get_reg(regs,asc_intr);
+
+ /* empty fifo, there is garbage in it if timeout */
+ regs->asc_cmd = ASC_CMD_FLUSH; mb();
+ delay(2);
+
+ /*
+ * Check if the select timed out
+ */
+ if ((ASC_SS(ss) == 0) && (ir == ASC_INT_DISC))
+ /* noone out there */
+ continue;
+
+ if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) {
+ printf( " %s%d%s", "ignoring target at ", target_id,
+ " cuz it acts weirdo");
+ continue;
+ }
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ regs->asc_cmd = ASC_CMD_I_COMPLETE;
+ wbflush();
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); /* ack intr */
+ mb();
+
+ status.bits = get_fifo(regs); /* empty fifo */
+ mb();
+ ff = get_fifo(regs);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD)
+ scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
+
+ regs->asc_cmd = ASC_CMD_MSG_ACPT; mb();
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); /* ack intr */
+ mb();
+
+ /*
+ * Found a target
+ */
+ asc->ntargets++;
+ {
+ register target_info_t *tgt;
+ tgt = scsi_slave_alloc(sc->masterno, target_id, asc);
+
+ (*asc->dma_ops->new_target)(asc->dma_state, tgt);
+ }
+ }
+ } /* asc_probe_dynamically */
+
+ regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb();
+
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+asc_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ asc_softc_t asc = asc_softc[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ (*asc->dma_ops->new_target)(asc->dma_state, tgt);
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ asc->ntargets++;
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+private asc_wait(regs, until, complain)
+ asc_padded_regmap_t *regs;
+{
+ int timeo = 1000000;
+ while ((regs->asc_csr & until) == 0) {
+ mb();
+ delay(1);
+ if (!timeo--) {
+ if (complain)
+ printf("asc_wait TIMEO with x%x\n", get_reg(regs,asc_csr));
+ break;
+ }
+ }
+ return get_reg(regs,asc_csr);
+}
+
+asc_reset(asc, quick, special_dma)
+ asc_softc_t asc;
+{
+ char my_id;
+ int ccf;
+ asc_padded_regmap_t *regs;
+
+ regs = asc->regs;
+
+ /* preserve our ID for now */
+ my_id = (regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID);
+
+ /*
+ * Reset chip and wait till done
+ */
+ regs->asc_cmd = ASC_CMD_RESET;
+ wbflush(); delay(25);
+
+ /* spec says this is needed after reset */
+ regs->asc_cmd = ASC_CMD_NOP;
+ wbflush(); delay(25);
+
+ /*
+ * Set up various chip parameters
+ */
+ regs->asc_ccf = asc->ccf;
+ wbflush();
+ delay(25);
+ regs->asc_sel_timo = asc->timeout; mb();
+ /* restore our ID */
+ regs->asc_cnfg1 = my_id | ASC_CNFG1_P_CHECK; mb();
+ regs->asc_cnfg2 = ASC_CNFG2_SCSI2;
+ mb();
+ regs->asc_cnfg3 = special_dma ? (ASC_CNFG3_T8|ASC_CNFG3_ALT_DMA) : 0;
+ mb();
+ /* zero anything else */
+ ASC_TC_PUT(regs, 0); mb();
+ regs->asc_syn_p = asc_min_period; mb();
+ regs->asc_syn_o = 0; mb(); /* asynch for now */
+
+ regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb();
+
+ if (quick) return;
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call asc_bus_reset().
+ */
+ regs->asc_cmd = ASC_CMD_BUS_RESET; mb();
+}
+
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+asc_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ asc_softc_t asc;
+ register spl_t s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+
+ LOG(1,"go");
+
+ asc = (asc_softc_t)tgt->hw_state;
+
+ tgt->transient_state.cmd_count = cmd_count; /* keep it here */
+ tgt->transient_state.out_count = 0; /* default */
+
+ (*asc->dma_ops->map)(asc->dma_state, tgt);
+
+ disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id);
+ disconn = disconn && (asc->ntargets > 1);
+ disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id);
+
+ /*
+ * Setup target state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ handler = (disconn) ? asc_err_disconn : asc_err_generic;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(2,"readop");
+ scp = asc_script_data_in;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x18,"writeop");
+ scp = asc_script_data_out;
+ break;
+ case SCSI_CMD_INQUIRY:
+ /* This is likely the first thing out:
+ do the synch neg if so */
+ if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
+ scp = asc_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ disconn = FALSE;
+ break;
+ }
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ scp = asc_script_data_in;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.out_count =
+ cmd_count - tgt->transient_state.cmd_count;
+ scp = asc_script_data_out;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ /*
+ * Do the synch negotiation here, unless prohibited
+ * or done already
+ */
+ if (tgt->flags & TGT_DID_SYNCH) {
+ scp = asc_script_simple_cmd;
+ } else {
+ scp = asc_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ cmd_only = FALSE;
+ disconn = FALSE;
+ }
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ scp = asc_script_simple_cmd;
+ }
+
+ tgt->transient_state.script = scp;
+ tgt->transient_state.handler = handler;
+ tgt->transient_state.identify = (cmd_only) ? 0xff :
+ (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
+ SCSI_IDENTIFY);
+
+ if (in_count)
+ tgt->transient_state.in_count =
+ (in_count < tgt->block_size) ? tgt->block_size : in_count;
+ else
+ tgt->transient_state.in_count = 0;
+
+ /*
+ * See if another target is currently selected on
+ * this SCSI bus, e.g. lock the asc structure.
+ * Note that it is the strategy routine's job
+ * to serialize ops on the same target as appropriate.
+ * XXX here and everywhere, locks!
+ */
+ /*
+ * Protection viz reconnections makes it tricky.
+ */
+ s = splbio();
+
+ if (asc->wd.nactive++ == 0)
+ asc->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (asc->state & ASC_STATE_BUSY) {
+ /*
+ * Queue up this target, note that this takes care
+ * of proper FIFO scheduling of the scsi-bus.
+ */
+ LOG(3,"enqueue");
+ enqueue_tail(&asc->waiting_targets, (queue_entry_t) tgt);
+ } else {
+ /*
+ * It is down to at most two contenders now,
+ * we will treat reconnections same as selections
+ * and let the scsi-bus arbitration process decide.
+ */
+ asc->state |= ASC_STATE_BUSY;
+ asc->next_target = tgt;
+ asc_attempt_selection(asc);
+ /*
+ * Note that we might still lose arbitration..
+ */
+ }
+ splx(s);
+}
+
+asc_attempt_selection(asc)
+ asc_softc_t asc;
+{
+ target_info_t *tgt;
+ asc_padded_regmap_t *regs;
+ register int out_count;
+
+ regs = asc->regs;
+ tgt = asc->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * We own the bus now.. unless we lose arbitration
+ */
+ asc->active_target = tgt;
+
+ /* Try to avoid reselect collisions */
+ if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) ==
+ (ASC_CSR_INT|SCSI_PHASE_MSG_IN))
+ return;
+
+ /*
+ * Cleanup the FIFO
+ */
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ /*
+ * This value is not from spec, I have seen it failing
+ * without this delay and with logging disabled. That had
+ * about 42 extra instructions @25Mhz.
+ */
+ delay(2);/* XXX time & move later */
+
+
+ /*
+ * Init bus state variables
+ */
+ asc->script = tgt->transient_state.script;
+ asc->error_handler = tgt->transient_state.handler;
+ asc->done = SCSI_RET_IN_PROGRESS;
+
+ asc->out_count = 0;
+ asc->in_count = 0;
+ asc->extra_count = 0;
+
+ /*
+ * Start the chip going
+ */
+ out_count = (*asc->dma_ops->start_cmd)(asc->dma_state, tgt);
+ if (tgt->transient_state.identify != 0xff) {
+ regs->asc_fifo = tgt->transient_state.identify | tgt->lun;
+ mb();
+ }
+ ASC_TC_PUT(regs, out_count);
+ readback(regs->asc_cmd);
+
+ regs->asc_dbus_id = tgt->target_id;
+ readback(regs->asc_dbus_id);
+
+ regs->asc_sel_timo = asc->timeout;
+ readback(regs->asc_sel_timo);
+
+ regs->asc_syn_p = tgt->sync_period;
+ readback(regs->asc_syn_p);
+
+ regs->asc_syn_o = tgt->sync_offset;
+ readback(regs->asc_syn_o);
+
+ /* ugly little help for compiler */
+#define command out_count
+ if (tgt->flags & TGT_DID_SYNCH) {
+ command = (tgt->transient_state.identify == 0xff) ?
+ ASC_CMD_SEL | ASC_CMD_DMA :
+ ASC_CMD_SEL_ATN | ASC_CMD_DMA; /*preferred*/
+ } else if (tgt->flags & TGT_TRY_SYNCH)
+ command = ASC_CMD_SEL_ATN_STOP;
+ else
+ command = ASC_CMD_SEL | ASC_CMD_DMA;
+
+ /* Try to avoid reselect collisions */
+ if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) !=
+ (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) {
+ register int tmp;
+
+ regs->asc_cmd = command; mb();
+ /*
+ * Very nasty but infrequent problem here. We got/will get
+ * reconnected but the chip did not interrupt. The watchdog would
+ * fix it allright, but it stops the machine before it expires!
+ * Too bad we cannot just look at the interrupt register, sigh.
+ */
+ tmp = get_reg(regs,asc_cmd);
+ if ((tmp != command) && (tmp == (ASC_CMD_NOP|ASC_CMD_DMA))) {
+ if ((regs->asc_csr & ASC_CSR_INT) == 0) {
+ delay(250); /* increase if in trouble */
+
+ if (get_reg(regs,asc_csr) == SCSI_PHASE_MSG_IN) {
+ /* ok, take the risk of reading the ir */
+ tmp = get_reg(regs,asc_intr); mb();
+ if (tmp & ASC_INT_RESEL) {
+ (void) asc_reconnect(asc, get_reg(regs,asc_csr), tmp);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ tmp = get_reg(regs,asc_intr); mb();
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ } else /* does not happen, but who knows.. */
+ asc_reset(asc,FALSE,asc->state & ASC_STATE_SPEC_DMA);
+ }
+ }
+ }
+ }
+#undef command
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+asc_intr(unit, spllevel)
+ spl_t spllevel;
+{
+ register asc_softc_t asc;
+ register script_t scp;
+ register int ir, csr;
+ register asc_padded_regmap_t *regs;
+#if defined(HAS_MAPPED_SCSI)
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return ASC_intr(unit,spllevel);
+#endif
+
+ asc = asc_softc[unit];
+
+ LOG(5,"\n\tintr");
+ /* collect ephemeral information */
+ regs = asc->regs;
+ csr = get_reg(regs,asc_csr); mb();
+ asc->ss_was = get_reg(regs,asc_ss); mb();
+ asc->cmd_was = get_reg(regs,asc_cmd); mb();
+
+ /* drop spurious interrupts */
+ if ((csr & ASC_CSR_INT) == 0)
+ return;
+
+ ir = get_reg(regs,asc_intr); /* this re-latches CSR (and SSTEP) */
+ mb();
+
+TR(csr);TR(ir);TR(get_reg(regs,asc_cmd));TRCHECK
+
+ /* this must be done within 250msec of disconnect */
+ if (ir & ASC_INT_DISC) {
+ regs->asc_cmd = ASC_CMD_ENABLE_SEL;
+ readback(regs->asc_cmd);
+ }
+
+ if (ir & ASC_INT_RESET)
+ return asc_bus_reset(asc);
+
+ /* we got an interrupt allright */
+ if (asc->active_target)
+ asc->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+#if mips
+ splx(spllevel); /* drop priority */
+#endif
+
+ if ((asc->state & ASC_STATE_TARGET) ||
+ (ir & (ASC_INT_SEL|ASC_INT_SEL_ATN)))
+ return asc_target_intr(asc, csr, ir);
+
+ /*
+ * In attempt_selection() we could not check the asc_intr
+ * register to see if a reselection was in progress [else
+ * we would cancel the interrupt, and it would not be safe
+ * anyways]. So we gave the select command even if sometimes
+ * the chip might have been reconnected already. What
+ * happens then is that we get an illegal command interrupt,
+ * which is why the second clause. Sorry, I'd do it better
+ * if I knew of a better way.
+ */
+ if ((ir & ASC_INT_RESEL) ||
+ ((ir & ASC_INT_ILL) && (regs->asc_cmd & ASC_CMD_SEL_ATN)))
+ return asc_reconnect(asc, csr, ir);
+
+ /*
+ * Check for various errors
+ */
+ if ((csr & (ASC_CSR_GE|ASC_CSR_PE)) || (ir & ASC_INT_ILL)) {
+ char *msg;
+printf("{E%x,%x}", csr, ir);
+ if (csr & ASC_CSR_GE)
+ return;/* sit and prey? */
+
+ if (csr & ASC_CSR_PE)
+ msg = "SCSI bus parity error";
+ if (ir & ASC_INT_ILL)
+ msg = "Chip sez Illegal Command";
+ /* all we can do is to throw a reset on the bus */
+ printf( "asc%d: %s%s", asc - asc_softc_data, msg,
+ ", attempting recovery.\n");
+ asc_reset(asc, FALSE, asc->state & ASC_STATE_SPEC_DMA);
+ return;
+ }
+
+ if ((scp = asc->script) == 0) /* sanity */
+ return;
+
+ LOG(6,"match");
+ if (SCRIPT_MATCH(csr,ir,scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(asc, csr, ir)) {
+ asc->script = scp + 1;
+ regs->asc_cmd = scp->command; mb();
+ }
+ } else
+ (void) (*asc->error_handler)(asc, csr, ir);
+}
+
+asc_target_intr(asc, csr, ir)
+ register asc_softc_t asc;
+
+{
+ register script_t scp;
+
+ LOG(0x1e,"tmode");
+
+ if ((asc->state & ASC_STATE_TARGET) == 0) {
+
+ /*
+ * We just got selected
+ */
+ asc->state |= ASC_STATE_TARGET;
+
+ /*
+ * See if this selection collided with our selection attempt
+ */
+ if (asc->state & ASC_STATE_BUSY)
+ asc->state |= ASC_STATE_COLLISION;
+ asc->state |= ASC_STATE_BUSY;
+
+ return asc_selected(asc, csr, ir);
+
+ }
+ /* We must be executing a script here */
+ scp = asc->script;
+ assert(scp != 0);
+
+ LOG(6,"match");
+ if (SCRIPT_MATCH(csr,ir,scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(asc, csr, ir)) {
+ asc->script = scp + 1;
+ asc->regs->asc_cmd = scp->command; mb();
+ }
+ } else
+ (void) (*asc->error_handler)(asc, csr, ir);
+
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to
+ */
+boolean_t
+asc_clean_fifo(asc, csr, ir)
+ register asc_softc_t asc;
+
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register char ff;
+
+ ASC_TC_GET(regs,asc->dmacnt_at_end);
+
+ ASC_TC_PUT(regs,0); /* stop dma engine */
+ readback(regs->asc_cmd);
+
+ LOG(7,"clean_fifo");
+
+ while (fifo_count(regs)) {
+ ff = get_fifo(regs);
+ mb();
+ }
+ return TRUE;
+}
+
+boolean_t
+asc_end(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register target_info_t *tgt;
+ register io_req_t ior;
+
+ LOG(8,"end");
+
+ asc->state &= ~ASC_STATE_TARGET;
+ asc->regs->asc_syn_p = 0; mb();
+ asc->regs->asc_syn_o = 0; mb();
+
+ tgt = asc->active_target;
+ if ((tgt->done = asc->done) == SCSI_RET_IN_PROGRESS)
+ tgt->done = SCSI_RET_SUCCESS;
+
+ asc->script = 0;
+
+ if (asc->wd.nactive-- == 1)
+ asc->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+ asc_release_bus(asc);
+
+ if (ior = tgt->ior) {
+ /*
+ * WARNING: the above might have scheduled the
+ * DMA engine off to someone else. Keep it in
+ * mind in the following code
+ */
+ (*asc->dma_ops->end_cmd)(asc->dma_state, tgt, ior);
+
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+
+ return FALSE;
+}
+
+boolean_t
+asc_release_bus(asc)
+ register asc_softc_t asc;
+{
+ boolean_t ret = TRUE;
+
+ LOG(9,"release");
+ if (asc->state & ASC_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ asc->state &= ~ASC_STATE_COLLISION;
+ asc_attempt_selection(asc);
+
+ } else if (queue_empty(&asc->waiting_targets)) {
+
+ asc->state &= ~ASC_STATE_BUSY;
+ asc->active_target = 0;
+ asc->script = 0;
+ ret = FALSE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ asc->next_target = (target_info_t *)
+ dequeue_head(&asc->waiting_targets);
+ asc_attempt_selection(asc);
+ }
+ return ret;
+}
+
+boolean_t
+asc_get_status(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register scsi2_status_byte_t status;
+ int len;
+ boolean_t ret;
+ io_req_t ior;
+ register target_info_t *tgt = asc->active_target;
+
+ LOG(0xD,"get_status");
+TRWRAP
+
+ asc->state &= ~ASC_STATE_DMA_IN;
+
+ if (asc->state & ASC_STATE_DO_RFB) {
+ tgt->transient_state.has_oddb = FALSE;
+ regs->asc_cnfg2 = ASC_CNFG2_SCSI2;
+ }
+
+ /*
+ * Get the last two bytes in FIFO
+ */
+ while (fifo_count(regs) > 2) {
+ status.bits = get_fifo(regs); mb();
+ }
+
+ status.bits = get_fifo(regs); mb();
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(asc->active_target, SCSI_ERR_STATUS, status.bits, 0);
+ asc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ asc->done = SCSI_RET_SUCCESS;
+
+ status.bits = get_fifo(regs); /* just pop the command_complete */
+ mb();
+
+ /* if reading, move the last piece of data in main memory */
+ if (len = asc->in_count) {
+ register int count;
+
+ count = asc->dmacnt_at_end;
+ if (count) {
+#if 0
+ this is incorrect and besides..
+ tgt->ior->io_residual = count;
+#endif
+ len -= count;
+ }
+ regs->asc_cmd = asc->script->command;
+ readback(regs->asc_cmd);
+
+ ret = FALSE;
+ } else
+ ret = TRUE;
+
+ asc->dmacnt_at_end = 0;
+ (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len);
+ if (!ret)
+ asc->script++;
+ return ret;
+}
+
+boolean_t
+asc_put_status(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register scsi2_status_byte_t status;
+ register target_info_t *tgt = asc->active_target;
+ int len;
+
+ LOG(0x21,"put_status");
+
+ asc->state &= ~ASC_STATE_DMA_IN;
+
+ if (len = asc->in_count) {
+ register int count;
+
+ ASC_TC_GET(regs,count); mb();
+ if (count)
+ len -= count;
+ }
+ (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len);
+
+/* status.st.scsi_status_code = SCSI_ST_GOOD; */
+ regs->asc_fifo = 0; mb();
+ regs->asc_fifo = SCSI_COMMAND_COMPLETE; mb();
+
+ return TRUE;
+}
+
+
+boolean_t
+asc_dma_in(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register target_info_t *tgt;
+ register asc_padded_regmap_t *regs = asc->regs;
+ register int count;
+ unsigned char ff = get_reg(regs,asc_flags); mb();
+
+ LOG(0xE,"dma_in");
+ tgt = asc->active_target;
+
+ /*
+ * This seems to be needed on certain rudimentary targets
+ * (such as the DEC TK50 tape) which apparently only pick
+ * up 6 initial bytes: when you add the initial IDENTIFY
+ * you are left with 1 pending byte, which is left in the
+ * FIFO and would otherwise show up atop the data we are
+ * really requesting.
+ *
+ * This is only speculation, though, based on the fact the
+ * sequence step value of 3 out of select means the target
+ * changed phase too quick and some bytes have not been
+ * xferred (see NCR manuals). Counter to this theory goes
+ * the fact that the extra byte that shows up is not alwyas
+ * zero, and appears to be pretty random.
+ * Note that asc_flags say there is one byte in the FIFO
+ * even in the ok case, but the sstep value is the right one.
+ * Note finally that this might all be a sync/async issue:
+ * I have only checked the ok case on synch disks so far.
+ *
+ * Indeed it seems to be an asynch issue: exabytes do it too.
+ */
+ if ((tgt->sync_offset == 0) && ((ff & ASC_FLAGS_SEQ_STEP) != 0x80)) {
+ regs->asc_cmd = ASC_CMD_NOP;
+ wbflush();
+ PRINT(("[tgt %d: %x while %d]", tgt->target_id, ff, tgt->cur_cmd));
+ while ((ff & ASC_FLAGS_FIFO_CNT) != 0) {
+ ff = get_fifo(regs); mb();
+ ff = get_reg(regs,asc_flags); mb();
+ }
+ }
+
+ asc->state |= ASC_STATE_DMA_IN;
+
+ count = (*asc->dma_ops->start_datain)(asc->dma_state, tgt);
+ ASC_TC_PUT(regs, count);
+ readback(regs->asc_cmd);
+
+ if ((asc->in_count = count) == tgt->transient_state.in_count)
+ return TRUE;
+ regs->asc_cmd = asc->script->command; mb();
+ asc->script = asc_script_restart_data_in;
+ return FALSE;
+}
+
+asc_dma_in_r(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register target_info_t *tgt;
+ register asc_padded_regmap_t *regs = asc->regs;
+ register int count;
+ boolean_t advance_script = TRUE;
+
+
+ LOG(0x1f,"dma_in_r");
+ tgt = asc->active_target;
+
+ asc->state |= ASC_STATE_DMA_IN;
+
+ if (asc->in_count == 0) {
+ /*
+ * Got nothing yet, we just reconnected.
+ */
+ register int avail;
+
+ /*
+ * NOTE: if we have to handle the RFB (obb),
+ * the odd byte has been installed at reconnect
+ * time, before switching to data-in phase. Now
+ * we are already in data-in phase.
+ * It is up to the DMA engine to trim the dma_ptr
+ * down one byte.
+ */
+
+ count = (*asc->dma_ops->restart_datain_1)
+ (asc->dma_state, tgt);
+
+ /* common case of 8k-or-less read ? */
+ advance_script = (tgt->transient_state.in_count == count);
+
+ } else {
+
+ /*
+ * We received some data.
+ */
+ register int offset, xferred;
+
+ /*
+ * Problem: sometimes we get a 'spurious' interrupt
+ * right after a reconnect. It goes like disconnect,
+ * reconnect, dma_in_r, here but DMA is still rolling.
+ * Since there is no good reason we got here to begin with
+ * we just check for the case and dismiss it: we should
+ * get another interrupt when the TC goes to zero or the
+ * target disconnects.
+ */
+ ASC_TC_GET(regs,xferred); mb();
+ if (xferred != 0)
+ return FALSE;
+
+ xferred = asc->in_count - xferred;
+ assert(xferred > 0);
+
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+
+ /*
+ * There should NOT be any obb issues here,
+ * we would have no control anyways.
+ */
+ count = (*asc->dma_ops->restart_datain_2)
+ (asc->dma_state, tgt, xferred);
+
+ asc->in_count = count;
+ ASC_TC_PUT(regs, count);
+ readback(regs->asc_cmd);
+ regs->asc_cmd = asc->script->command; mb();
+
+ (*asc->dma_ops->restart_datain_3)
+ (asc->dma_state, tgt);
+
+ /* last chunk ? */
+ if (count == tgt->transient_state.in_count)
+ asc->script++;
+
+ return FALSE;
+ }
+
+ asc->in_count = count;
+ ASC_TC_PUT(regs, count);
+ readback(regs->asc_cmd);
+
+ if (!advance_script) {
+ regs->asc_cmd = asc->script->command;
+ readback(regs->asc_cmd);
+ }
+ return advance_script;
+}
+
+
+/* send data to target. Only called to start the xfer */
+
+boolean_t
+asc_dma_out(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register int reload_count;
+ register target_info_t *tgt;
+ int command;
+
+ LOG(0xF,"dma_out");
+
+ ASC_TC_GET(regs, reload_count); mb();
+ asc->extra_count = fifo_count(regs);
+ reload_count += asc->extra_count;
+ ASC_TC_PUT(regs, reload_count);
+ asc->state &= ~ASC_STATE_DMA_IN;
+ readback(regs->asc_cmd);
+
+ tgt = asc->active_target;
+
+ command = asc->script->command;
+
+ if (reload_count == 0) reload_count = ASC_TC_MAX;
+ asc->out_count = reload_count;
+
+ if (reload_count >= tgt->transient_state.out_count)
+ asc->script++;
+ else
+ asc->script = asc_script_restart_data_out;
+
+ if ((*asc->dma_ops->start_dataout)
+ (asc->dma_state, tgt, (volatile unsigned *)&regs->asc_cmd,
+ command, &asc->extra_count)) {
+ regs->asc_cmd = command;
+ readback(regs->asc_cmd);
+ }
+
+ return FALSE;
+}
+
+/* send data to target. Called in two different ways:
+ (a) to restart a big transfer and
+ (b) after reconnection
+ */
+boolean_t
+asc_dma_out_r(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register target_info_t *tgt;
+ boolean_t advance_script = TRUE;
+ int count;
+
+
+ LOG(0x20,"dma_out_r");
+
+ tgt = asc->active_target;
+ asc->state &= ~ASC_STATE_DMA_IN;
+
+ if (asc->out_count == 0) {
+ /*
+ * Nothing committed: we just got reconnected
+ */
+ count = (*asc->dma_ops->restart_dataout_1)
+ (asc->dma_state, tgt);
+
+ /* is this the last chunk ? */
+ advance_script = (tgt->transient_state.out_count == count);
+ } else {
+ /*
+ * We sent some data.
+ */
+ register int offset, xferred;
+
+ ASC_TC_GET(regs,count); mb();
+
+ /* see comment above */
+ if (count) {
+ return FALSE;
+ }
+
+ count += fifo_count(regs);
+ count -= asc->extra_count;
+ xferred = asc->out_count - count;
+ assert(xferred > 0);
+
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ count = (*asc->dma_ops->restart_dataout_2)
+ (asc->dma_state, tgt, xferred);
+
+ /* last chunk ? */
+ if (tgt->transient_state.out_count == count)
+ goto quickie;
+
+ asc->out_count = count;
+
+ asc->extra_count = (*asc->dma_ops->restart_dataout_3)
+ (asc->dma_state, tgt,
+ (volatile unsigned *)&regs->asc_fifo);
+ ASC_TC_PUT(regs, count);
+ readback(regs->asc_cmd);
+ regs->asc_cmd = asc->script->command; mb();
+
+ (*asc->dma_ops->restart_dataout_4)(asc->dma_state, tgt);
+
+ return FALSE;
+ }
+
+quickie:
+ asc->extra_count = (*asc->dma_ops->restart_dataout_3)
+ (asc->dma_state, tgt,
+ (volatile unsigned *)&regs->asc_fifo);
+
+ asc->out_count = count;
+
+ ASC_TC_PUT(regs, count);
+ readback(regs->asc_cmd);
+
+ if (!advance_script) {
+ regs->asc_cmd = asc->script->command;
+ }
+ return advance_script;
+}
+
+boolean_t
+asc_dosynch(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register unsigned char c;
+ int i, per, offs;
+ register target_info_t *tgt;
+
+ /*
+ * Phase is MSG_OUT here.
+ * Try to go synchronous, unless prohibited
+ */
+ tgt = asc->active_target;
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ delay(1);
+
+ per = asc_min_period;
+ if (BGET(scsi_no_synchronous_xfer,asc->sc->masterno,tgt->target_id))
+ offs = 0;
+ else
+ offs = asc_max_offset;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb();
+ regs->asc_fifo = 3; mb();
+ regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb();
+ regs->asc_fifo = asc_to_scsi_period(asc_min_period,asc->clk); mb();
+ regs->asc_fifo = offs; mb();
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); mb();
+
+ /* some targets might be slow to move to msg-in */
+
+ if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) {
+
+ /* wait for direction bit to flip */
+ csr = asc_wait(regs, SCSI_IO, 0);
+ ir = get_reg(regs,asc_intr); mb();
+ /* Some ugly targets go stright to command phase.
+ "You could at least say goodbye" */
+ if (SCSI_PHASE(csr) == SCSI_PHASE_CMD)
+ goto did_not;
+ if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN)
+ gimmeabreak();
+ }
+
+ regs->asc_cmd = ASC_CMD_XFER_INFO; mb();
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); mb();
+
+ /* some targets do not even take all the bytes out */
+ while (fifo_count(regs) > 0) {
+ c = get_fifo(regs); /* see what it says */
+ mb();
+ }
+
+ if (c == SCSI_MESSAGE_REJECT) {
+did_not:
+ printf(" did not like SYNCH xfer ");
+
+ /* Tk50s get in trouble with ATN, sigh. */
+ regs->asc_cmd = ASC_CMD_CLR_ATN;
+ readback(regs->asc_cmd);
+
+ goto cmd;
+ }
+
+ /*
+ * Receive the rest of the message
+ */
+ regs->asc_cmd = ASC_CMD_MSG_ACPT; mb();
+ asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); mb();
+
+ if (c != SCSI_EXTENDED_MESSAGE)
+ gimmeabreak();
+
+ regs->asc_cmd = ASC_CMD_XFER_INFO; mb();
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+ if (get_fifo(regs) != 3)
+ panic("asc_dosynch");
+
+ for (i = 0; i < 3; i++) {
+ regs->asc_cmd = ASC_CMD_MSG_ACPT; mb();
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+
+ regs->asc_cmd = ASC_CMD_XFER_INFO; mb();
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr);/*ack*/ mb();
+ c = get_fifo(regs); mb();
+
+ if (i == 1) tgt->sync_period = scsi_period_to_asc(c,asc->clk);
+ if (i == 2) tgt->sync_offset = c;
+ }
+
+cmd:
+ regs->asc_cmd = ASC_CMD_MSG_ACPT; mb();
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+
+ /* Might have to wait a bit longer for slow targets */
+ for (c = 0; SCSI_PHASE(get_reg(regs,asc_csr)) == SCSI_PHASE_MSG_IN; c++) {
+ mb();
+ delay(2);
+ if (c & 0x80) break; /* waited too long */
+ }
+ csr = get_reg(regs,asc_csr); mb();
+
+ /* phase should normally be command here */
+ if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) {
+ register char *cmd = tgt->cmd_ptr;
+
+ /* test unit ready or inquiry */
+ for (i = 0; i < sizeof(scsi_command_group_0); i++) {
+ regs->asc_fifo = *cmd++; mb();
+ }
+ ASC_TC_PUT(regs,0xff); mb();
+ regs->asc_cmd = ASC_CMD_XFER_PAD; /*0x18*/ mb();
+
+ if (tgt->cur_cmd == SCSI_CMD_INQUIRY) {
+ tgt->transient_state.script = asc_script_data_in;
+ asc->script = tgt->transient_state.script;
+ regs->asc_syn_p = tgt->sync_period;
+ regs->asc_syn_o = tgt->sync_offset; mb();
+ return FALSE;
+ }
+
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr);/*ack*/ mb();
+ }
+
+ if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS)
+ csr = asc_wait(regs, SCSI_IO, 1); /* direction flip */
+
+status:
+ if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS)
+ gimmeabreak();
+
+ return TRUE;
+}
+
+/* The other side of the coin.. */
+asc_odsynch(asc, initiator)
+ register asc_softc_t asc;
+ target_info_t *initiator;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register unsigned char c;
+ int len, per, offs;
+
+ /*
+ * Phase is MSG_OUT, we are the target and we have control.
+ * Any IDENTIFY messages have been handled already.
+ */
+ initiator->flags |= TGT_DID_SYNCH;
+ initiator->flags &= ~TGT_TRY_SYNCH;
+
+ /*
+ * We only understand synch negotiations
+ */
+ c = get_fifo(regs); mb();
+ if (c != SCSI_EXTENDED_MESSAGE) goto bad;
+
+ /*
+ * This is not in the specs, but apparently the chip knows
+ * enough about scsi to receive the length automatically.
+ * So there were two bytes in the fifo at function call.
+ */
+ len = get_fifo(regs); mb();
+ if (len != 3) goto bad;
+ while (len) {
+ if (fifo_count(regs) == 0) {
+ regs->asc_cmd = ASC_CMD_RCV_MSG;
+ readback(regs->asc_cmd);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+ }
+ c = get_fifo(regs); mb();
+ if (len == 1) offs = c;
+ if (len == 2) per = c;
+ len--;
+ }
+
+ /*
+ * Adjust the proposed parameters
+ */
+ c = scsi_period_to_asc(per,asc->clk);
+ initiator->sync_period = c;
+ per = asc_to_scsi_period(c,asc->clk);
+
+ if (offs > asc_max_offset) offs = asc_max_offset;
+ initiator->sync_offset = offs;
+
+ /*
+ * Tell him what the deal is
+ */
+ regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb();
+ regs->asc_fifo = 3; mb();
+ regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb();
+ regs->asc_fifo = per; mb();
+ regs->asc_fifo = offs; mb();
+ regs->asc_cmd = ASC_CMD_SND_MSG;
+ readback(regs->asc_cmd);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+
+ /*
+ * Exit conditions: fifo empty, phase undefined but non-command
+ */
+ return;
+
+ /*
+ * Something wrong, reject the message
+ */
+bad:
+ while (fifo_count(regs)) {
+ c = get_fifo(regs); mb();
+ }
+ regs->asc_fifo = SCSI_MESSAGE_REJECT; mb();
+ regs->asc_cmd = ASC_CMD_SND_MSG;
+ readback(regs->asc_cmd);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ c = get_reg(regs,asc_intr); mb();
+}
+
+/*
+ * The bus was reset
+ */
+asc_bus_reset(asc)
+ register asc_softc_t asc;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+
+ LOG(0x1d,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ asc->script = 0;
+ asc->error_handler = 0;
+ asc->active_target = 0;
+ asc->next_target = 0;
+ asc->state &= ASC_STATE_SPEC_DMA | ASC_STATE_DO_RFB;
+ queue_init(&asc->waiting_targets);
+ asc->wd.nactive = 0;
+ asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA);
+
+ printf("asc: (%d) bus reset ", ++asc->wd.reset_count);
+
+ /* some targets take long to reset */
+ delay( scsi_delay_after_reset +
+ asc->sc->initiator_id * 500000); /* if multiple initiators */
+
+ if (asc->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(asc->sc);
+}
+
+/*
+ * Disconnect/reconnect mode ops
+ */
+
+/* get the message in via dma */
+boolean_t
+asc_msg_in(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+{
+ register target_info_t *tgt;
+ register asc_padded_regmap_t *regs = asc->regs;
+ unsigned char ff;
+
+ LOG(0x10,"msg_in");
+ /* must clean FIFO as in asc_dma_in, sigh */
+ while (fifo_count(regs) != 0) {
+ ff = get_fifo(regs); mb();
+ }
+
+ (void) (*asc->dma_ops->start_msgin)(asc->dma_state, asc->active_target);
+ /* We only really expect two bytes, at tgt->cmd_ptr */
+ ASC_TC_PUT(regs, sizeof(scsi_command_group_0));
+ readback(regs->asc_cmd);
+
+ return TRUE;
+}
+
+/* check the message is indeed a DISCONNECT */
+boolean_t
+asc_disconnect(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+
+{
+ register char *msgs, ff;
+ register target_info_t *tgt;
+ asc_padded_regmap_t *regs;
+
+ tgt = asc->active_target;
+
+ (*asc->dma_ops->end_msgin)(asc->dma_state, tgt);
+
+ /*
+ * Do not do this. It is most likely a reconnection
+ * message that sits there already by the time we
+ * get here. The chip is smart enough to only dma
+ * the bytes that correctly came in as msg_in proper,
+ * the identify and selection bytes are not dma-ed.
+ * Yes, sometimes the hardware does the right thing.
+ */
+#if 0
+ /* First check message got out of the fifo */
+ regs = asc->regs;
+ while (fifo_count(regs) != 0) {
+ *msgs++ = get_fifo(regs);
+ }
+#endif
+ msgs = tgt->cmd_ptr;
+
+ /* A SDP message preceeds it in non-completed READs */
+ if ((msgs[0] == SCSI_DISCONNECT) || /* completed */
+ ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* non complete */
+ (msgs[1] == SCSI_DISCONNECT))) {
+ /* that's the ok case */
+ } else
+ printf("[tgt %d bad SDP: x%x]",
+ tgt->target_id, *((unsigned short *)msgs));
+
+ return TRUE;
+}
+
+/* save all relevant data, free the BUS */
+boolean_t
+asc_disconnected(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+
+{
+ register target_info_t *tgt;
+
+ LOG(0x11,"disconnected");
+ asc_disconnect(asc,csr,ir);
+
+ tgt = asc->active_target;
+ tgt->flags |= TGT_DISCONNECTED;
+ tgt->transient_state.handler = asc->error_handler;
+ /* anything else was saved in asc_err_disconn() */
+
+ PRINT(("{D%d}", tgt->target_id));
+
+ asc_release_bus(asc);
+
+ return FALSE;
+}
+
+int asc_restore_ptr = 1;
+
+/* get reconnect message out of fifo, restore BUS */
+boolean_t
+asc_reconnect(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+
+{
+ register target_info_t *tgt;
+ asc_padded_regmap_t *regs;
+ unsigned int id;
+
+ LOG(0x12,"reconnect");
+ /*
+ * See if this reconnection collided with a selection attempt
+ */
+ if (asc->state & ASC_STATE_BUSY)
+ asc->state |= ASC_STATE_COLLISION;
+
+ asc->state |= ASC_STATE_BUSY;
+
+ /* find tgt: first byte in fifo is (tgt_id|our_id) */
+ regs = asc->regs;
+ while (fifo_count(regs) > 2) /* sanity */ {
+ id = get_fifo(regs); mb();
+ }
+ if (fifo_count(regs) != 2)
+ gimmeabreak();
+
+ id = get_fifo(regs); /* must decode this now */
+ mb();
+ id &= ~(1 << asc->sc->initiator_id);
+ {
+ register int i;
+ for (i = 0; id > 1; i++)
+ id >>= 1;
+ id = i;
+ }
+
+ tgt = asc->sc->target[id];
+ if (tgt == 0) panic("asc_reconnect");
+
+ /* synch things*/
+ regs->asc_syn_p = tgt->sync_period;
+ regs->asc_syn_o = tgt->sync_offset;
+ readback(regs->asc_syn_o);
+
+ /* Get IDENTIFY message */
+ {
+ register int i = get_fifo(regs);
+ if ((i & SCSI_IDENTIFY) == 0)
+ gimmeabreak();
+ i &= 0x7;
+ /* If not LUN 0 see which target it is */
+ if (i) {
+ target_info_t *tgt1;
+ for (tgt1 = tgt->next_lun;
+ tgt1 && tgt1 != tgt;
+ tgt1 = tgt1->next_lun)
+ if (tgt1->lun == i) {
+ tgt = tgt1;
+ break;
+ }
+ }
+ }
+
+ if (asc->state & ASC_STATE_DO_RFB) {
+ if (tgt->transient_state.has_oddb) {
+ if (tgt->sync_period) {
+ regs->asc_cnfg2 = ASC_CNFG2_SCSI2 | ASC_CNFG2_RFB;
+ wbflush();
+ regs->asc_rfb = tgt->transient_state.the_oddb;
+ } else {
+ regs->asc_fifo = tgt->transient_state.the_oddb;
+ }
+ tgt->transient_state.has_oddb = FALSE;
+ } else {
+ regs->asc_cnfg2 = ASC_CNFG2_SCSI2;
+ }
+ wbflush();
+ }
+
+ PRINT(("{R%d}", id));
+ if (asc->state & ASC_STATE_COLLISION)
+ PRINT(("[B %d-%d]", asc->active_target->target_id, id));
+
+ LOG(0x80+id,0);
+
+ asc->active_target = tgt;
+ tgt->flags &= ~TGT_DISCONNECTED;
+
+ asc->script = tgt->transient_state.script;
+ asc->error_handler = tgt->transient_state.handler;
+ asc->in_count = 0;
+ asc->out_count = 0;
+
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+
+ /* What if there is a RESTORE_PTR msgin ? */
+ if (asc_restore_ptr) {
+more_msgin:
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+
+ if (SCSI_PHASE(csr) == SCSI_PHASE_MSG_IN) {
+ /* ack intr */
+ id = get_reg(regs,asc_intr); mb();
+
+ /* Ok, get msg */
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+ /* wait for xfer done */
+ csr = asc_wait(regs, ASC_CSR_INT, 1);
+
+ /* look at what we got */
+ id = get_fifo(regs);
+
+ if (id != SCSI_RESTORE_POINTERS)
+ printf("asc%d: uhu msg %x\n", asc->sc->masterno, id);
+ /* ack intr */
+ id = get_reg(regs,asc_intr); mb();
+
+ /* move on */
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ goto more_msgin;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* We have been selected as target */
+
+boolean_t
+asc_selected(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned csr, ir;
+{
+ register asc_padded_regmap_t *regs = asc->regs;
+ register unsigned char id;
+ target_info_t *self, *initiator;
+ unsigned int len;
+
+ /*
+ * Find initiator's id: the head of the fifo is (init_id|our_id)
+ */
+
+ id = get_fifo(regs); /* must decode this now */
+ mb();
+ id &= ~(1 << asc->sc->initiator_id);
+ {
+ register int i;
+ for (i = 0; id > 1; i++)
+ id >>= 1;
+ id = i;
+ }
+
+ /*
+ * See if we have seen him before
+ */
+ self = asc->sc->target[asc->sc->initiator_id];
+ initiator = asc->sc->target[id];
+ if (initiator == 0) {
+
+ initiator = scsi_slave_alloc(asc->sc->masterno, id, asc);
+ (*asc->dma_ops->new_target)(asc->dma_state, initiator);
+
+ }
+
+ if (! (initiator->flags & TGT_ONLINE) )
+ sccpu_new_initiator(self, initiator);
+
+ /*
+ * If selected with ATN the chip did the msg-out
+ * phase already, let us look at the message(s)
+ */
+ if (ir & ASC_INT_SEL_ATN) {
+ register unsigned char m;
+
+ m = get_fifo(regs); mb();
+ if ((m & SCSI_IDENTIFY) == 0)
+ gimmeabreak();
+
+ csr = get_reg(regs,asc_csr); mb();
+ if ((SCSI_PHASE(csr) == SCSI_PHASE_MSG_OUT) &&
+ fifo_count(regs))
+ asc_odsynch(asc, initiator);
+
+ /* Get the command now, unless we have it already */
+ mb();
+ if (fifo_count(regs) < sizeof(scsi_command_group_0)) {
+ regs->asc_cmd = ASC_CMD_RCV_CMD;
+ readback(regs->asc_cmd);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); mb();
+ csr = get_reg(regs,asc_csr); mb();
+ }
+ } else {
+ /*
+ * Pop away the null byte that follows the id
+ */
+ if (get_fifo(regs) != 0)
+ gimmeabreak();
+
+ }
+
+ /*
+ * Take rest of command out of fifo
+ */
+ self->dev_info.cpu.req_pending = TRUE;
+ self->dev_info.cpu.req_id = id;
+ self->dev_info.cpu.req_cmd = get_fifo(regs);
+ self->dev_info.cpu.req_lun = get_fifo(regs);
+
+ LOG(0x80+self->dev_info.cpu.req_cmd, 0);
+
+ switch ((self->dev_info.cpu.req_cmd & SCSI_CODE_GROUP) >> 5) {
+ case 0:
+ len = get_fifo(regs) << 16; mb();
+ len |= get_fifo(regs) << 8; mb();
+ len |= get_fifo(regs); mb();
+ break;
+ case 1:
+ case 2:
+ len = get_fifo(regs); /* xxx lba1 */ mb();
+ len = get_fifo(regs); /* xxx lba2 */ mb();
+ len = get_fifo(regs); /* xxx lba3 */ mb();
+ len = get_fifo(regs); /* xxx lba4 */ mb();
+ len = get_fifo(regs); /* xxx xxx */ mb();
+ len = get_fifo(regs) << 8; mb();
+ len |= get_fifo(regs); mb();
+ break;
+ case 5:
+ len = get_fifo(regs); /* xxx lba1 */ mb();
+ len = get_fifo(regs); /* xxx lba2 */ mb();
+ len = get_fifo(regs); /* xxx lba3 */ mb();
+ len = get_fifo(regs); /* xxx lba4 */ mb();
+ len = get_fifo(regs) << 24; mb();
+ len |= get_fifo(regs) << 16; mb();
+ len |= get_fifo(regs) << 8; mb();
+ len |= get_fifo(regs); mb();
+ if (get_fifo(regs) != 0) ;
+ break;
+ default:
+ panic("asc_selected");
+ }
+ self->dev_info.cpu.req_len = len;
+/*if (scsi_debug) printf("[L x%x]", len);*/
+
+ /* xxx pop the cntrl byte away */
+ if (get_fifo(regs) != 0)
+ gimmeabreak();
+ mb();
+
+ /*
+ * Setup state
+ */
+ asc->active_target = self;
+ asc->done = SCSI_RET_IN_PROGRESS;
+ asc->out_count = 0;
+ asc->in_count = 0;
+ asc->extra_count = 0;
+
+ /*
+ * Sync params.
+ */
+ regs->asc_syn_p = initiator->sync_period;
+ regs->asc_syn_o = initiator->sync_offset;
+ readback(regs->asc_syn_o);
+
+ /*
+ * Do the up-call
+ */
+ LOG(0xB,"tgt-mode-restart");
+ (*self->dev_ops->restart)( self, FALSE);
+
+ /* The call above has either prepared the data,
+ placing an ior on self, or it handled it some
+ other way */
+ if (self->ior == 0)
+ return FALSE;
+
+ /* sanity */
+ if (fifo_count(regs)) {
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ }
+
+ /* needed by some dma code to align things properly */
+ self->transient_state.cmd_count = sizeof(scsi_command_group_0);
+ self->transient_state.in_count = len; /* XXX */
+
+ (*asc->dma_ops->map)(asc->dma_state, self);
+
+ if (asc->wd.nactive++ == 0)
+ asc->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+
+ {
+ register script_t scp;
+ unsigned char command;
+
+ switch (self->dev_info.cpu.req_cmd) {
+ case SCSI_CMD_TEST_UNIT_READY:
+ scp = asc_script_t_data_in+1; break;
+ case SCSI_CMD_SEND:
+ scp = asc_script_t_data_in; break;
+ default:
+ scp = asc_script_t_data_out; break;
+ }
+
+ asc->script = scp;
+ command = scp->command;
+ if (!(*scp->action)(asc, csr, ir))
+ return FALSE;
+
+/*if (scsi_debug) printf("[F%x]", fifo_count(regs));*/
+
+ asc->script++;
+ regs->asc_cmd = command; mb();
+
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Other utilities
+ */
+private void
+pick_up_oddb(
+ target_info_t *tgt)
+{
+ register char *lastp;
+
+ /* State should have been updated before we get here */
+ lastp = tgt->dma_ptr + tgt->transient_state.dma_offset;
+
+ if ((vm_offset_t) lastp & 1) {
+ tgt->transient_state.has_oddb = TRUE;
+ tgt->transient_state.the_oddb = lastp[-1];
+ } else
+ tgt->transient_state.has_oddb = FALSE;
+}
+
+
+/*
+ * Error handlers
+ */
+
+/*
+ * Fall-back error handler.
+ */
+asc_err_generic(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ LOG(0x13,"err_generic");
+
+ /* handle non-existant or powered off devices here */
+ if ((ir == ASC_INT_DISC) &&
+ (asc_isa_select(asc->cmd_was)) &&
+ (ASC_SS(asc->ss_was) == 0)) {
+ /* Powered off ? */
+ target_info_t *tgt = asc->active_target;
+
+ tgt->flags = 0;
+ tgt->sync_period = 0;
+ tgt->sync_offset = 0; mb();
+ asc->done = SCSI_RET_DEVICE_DOWN;
+ asc_end(asc, csr, ir);
+ return;
+ }
+
+ switch (SCSI_PHASE(csr)) {
+ case SCSI_PHASE_STATUS:
+ if (asc->script[-1].condition == SCSI_PHASE_STATUS) {
+ /* some are just slow to get out.. */
+ } else
+ asc_err_to_status(asc, csr, ir);
+ return;
+ break;
+ case SCSI_PHASE_DATAI:
+ if (asc->script->condition == SCSI_PHASE_STATUS) {
+ /*
+ * Here is what I know about it. We reconnect to
+ * the target (csr 87, ir 0C, cmd 44), start dma in
+ * (81 10 12) and then get here with (81 10 90).
+ * Dma is rolling and keeps on rolling happily,
+ * the value in the counter indicates the interrupt
+ * was signalled right away: by the time we get
+ * here only 80-90 bytes have been shipped to an
+ * rz56 running synchronous at 3.57 Mb/sec.
+ */
+/* printf("{P}");*/
+ return;
+ }
+ break;
+ case SCSI_PHASE_DATAO:
+ if (asc->script->condition == SCSI_PHASE_STATUS) {
+ /*
+ * See comment above. Actually seen on hitachis.
+ */
+/* printf("{P}");*/
+ return;
+ }
+ break;
+ case SCSI_PHASE_CMD:
+ /* This can be the case with drives that are not
+ even scsi-1 compliant and do not like to be
+ selected with ATN (to do the synch negot) and
+ go stright to command phase, regardless */
+
+ if (asc->script == asc_script_try_synch) {
+
+ target_info_t *tgt = asc->active_target;
+ register asc_padded_regmap_t *regs = asc->regs;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ if (tgt->cur_cmd == SCSI_CMD_INQUIRY)
+ tgt->transient_state.script = asc_script_data_in;
+ else
+ tgt->transient_state.script = asc_script_simple_cmd;
+ asc->script = tgt->transient_state.script;
+ regs->asc_cmd = ASC_CMD_CLR_ATN;
+ readback(regs->asc_cmd);
+ asc->regs->asc_cmd = ASC_CMD_XFER_PAD|ASC_CMD_DMA; /*0x98*/ mb();
+ printf(" did not like SYNCH xfer ");
+ return;
+ }
+ /* fall through */
+ }
+ gimmeabreak();
+}
+
+/*
+ * Handle generic errors that are reported as
+ * an unexpected change to STATUS phase
+ */
+asc_err_to_status(asc, csr, ir)
+ register asc_softc_t asc;
+{
+ script_t scp = asc->script;
+
+ LOG(0x14,"err_tostatus");
+ while (scp->condition != SCSI_PHASE_STATUS)
+ scp++;
+ (*scp->action)(asc, csr, ir);
+ asc->script = scp + 1;
+ asc->regs->asc_cmd = scp->command; mb();
+#if 0
+ /*
+ * Normally, we would already be able to say the command
+ * is in error, e.g. the tape had a filemark or something.
+ * But in case we do disconnected mode WRITEs, it is quite
+ * common that the following happens:
+ * dma_out -> disconnect (xfer complete) -> reconnect
+ * and our script might expect at this point that the dma
+ * had to be restarted (it didn't notice it was completed).
+ * And in any event.. it is both correct and cleaner to
+ * declare error iff the STATUS byte says so.
+ */
+ asc->done = SCSI_RET_NEED_SENSE;
+#endif
+}
+
+/*
+ * Handle disconnections as exceptions
+ */
+asc_err_disconn(asc, csr, ir)
+ register asc_softc_t asc;
+ register unsigned char csr, ir;
+{
+ register asc_padded_regmap_t *regs;
+ register target_info_t *tgt;
+ int count;
+ boolean_t callback = FALSE;
+
+ LOG(0x16,"err_disconn");
+
+ /*
+ * We only do msg-in cases here
+ */
+ if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN)
+ return asc_err_generic(asc, csr, ir);
+
+ regs = asc->regs;
+ tgt = asc->active_target;
+
+ /*
+ * What did we expect to happen, and what did happen.
+ */
+ switch (asc->script->condition) {
+ case SCSI_PHASE_DATAO:
+ /*
+ * A data out phase was either about to be started,
+ * or it was in progress but more had to go out later
+ * [e.g. a big xfer for instance, or more than the
+ * DMA engine can take in one shot].
+ */
+ LOG(0x1b,"+DATAO");
+ if (asc->out_count) {
+ register int xferred, offset;
+
+ /*
+ * Xfer in progress. See where we stopped.
+ */
+ ASC_TC_GET(regs,xferred); /* temporary misnomer */
+ /*
+ * Account for prefetching, in its various forms
+ */
+ xferred += fifo_count(regs);
+ xferred -= asc->extra_count;
+ /*
+ * See how much went out, how much to go.
+ */
+ xferred = asc->out_count - xferred; /* ok now */
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ callback = (*asc->dma_ops->disconn_1)
+ (asc->dma_state, tgt, xferred);
+
+ } else {
+
+ /*
+ * A disconnection before DMA was (re)started.
+ */
+ callback = (*asc->dma_ops->disconn_2)
+ (asc->dma_state, tgt);
+
+ }
+ asc->extra_count = 0;
+ tgt->transient_state.script = asc_script_restart_data_out;
+ break;
+
+
+ case SCSI_PHASE_DATAI:
+ /*
+ * Same as above, the other way around
+ */
+ LOG(0x17,"+DATAI");
+ if (asc->in_count) {
+ register int offset, xferred;
+
+ /*
+ * How much did we expect, how much did we get
+ */
+ ASC_TC_GET(regs,count); mb();
+ xferred = asc->in_count - count;
+ assert(xferred > 0);
+
+if (regs->asc_flags & 0xf)
+printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs));
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+
+ callback = (*asc->dma_ops->disconn_3)
+ (asc->dma_state, tgt, xferred);
+
+ /*
+ * Handle obb if we have to. DMA code has
+ * updated pointers and flushed buffers.
+ */
+ if (asc->state & ASC_STATE_DO_RFB)
+ pick_up_oddb(tgt);
+
+ tgt->transient_state.script = asc_script_restart_data_in;
+ /*
+ * Some silly targets disconnect after they
+ * have given us all the data.
+ */
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else
+ tgt->transient_state.script = asc_script_restart_data_in;
+ break;
+
+ case SCSI_PHASE_STATUS:
+ /*
+ * Either one of the above cases here. Only diff
+ * the DMA engine was setup to run to completion
+ * and (most likely) did not.
+ */
+ ASC_TC_GET(regs,count); mb();
+ if (asc->state & ASC_STATE_DMA_IN) {
+ register int offset, xferred;
+
+ LOG(0x1a,"+STATUS+R");
+
+
+ /*
+ * Handle brain-dead sequence:
+ * 1-xfer all data, disconnect
+ * 2-reconnect, disconnect immediately ??
+ * 3-rept 2
+ * 4-reconnect,complete
+ */
+ if (asc->in_count) {
+
+ xferred = asc->in_count - count;
+ assert(xferred > 0);
+if (regs->asc_flags & 0xf)
+printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs));
+
+ tgt->transient_state.in_count -= xferred;
+
+ callback = (*asc->dma_ops->disconn_4)
+ (asc->dma_state, tgt, xferred);
+ }
+ /*
+ * Handle obb if we have to. DMA code has
+ * updated pointers and flushed buffers.
+ */
+ if (asc->state & ASC_STATE_DO_RFB)
+ pick_up_oddb(tgt);
+
+ tgt->transient_state.script = asc_script_restart_data_in;
+
+ /* see previous note */
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else {
+
+ /*
+ * Outgoing xfer, take care of prefetching.
+ */
+ /* add what's left in the fifo */
+ count += fifo_count(regs);
+ /* take back the extra we might have added */
+ count -= asc->extra_count;
+ /* ..and drop that idea */
+ asc->extra_count = 0;
+
+ LOG(0x19,"+STATUS+W");
+
+ /*
+ * All done ? This is less silly than with
+ * READs: some disks will only say "done" when
+ * the data is down on the platter, and some
+ * people like it much better that way.
+ */
+ if ((count == 0) && (tgt->transient_state.out_count == asc->out_count)) {
+ /* all done */
+ tgt->transient_state.script = asc->script;
+ tgt->transient_state.out_count = 0;
+ } else {
+ register int xferred, offset;
+
+ /* how much we xferred */
+ xferred = asc->out_count - count;
+
+ /* how much to go */
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+
+ callback = (*asc->dma_ops->disconn_5)
+ (asc->dma_state,tgt,xferred);
+
+ tgt->transient_state.script = asc_script_restart_data_out;
+ }
+ asc->out_count = 0;
+ }
+ break;
+ default:
+ gimmeabreak();
+ return;
+ }
+ asc_msg_in(asc,csr,ir);
+ asc->script = asc_script_disconnect;
+ regs->asc_cmd = ASC_CMD_XFER_INFO|ASC_CMD_DMA;
+ wbflush();
+ /*
+ * Prevent a race, now. If the reselection comes quickly
+ * the chip will prefetch and reload the transfer counter
+ * register. Make sure it will stop, by reloading a zero.
+ */
+ regs->asc_tc_lsb = 0;
+ regs->asc_tc_msb = 0;
+ if (callback)
+ (*asc->dma_ops->disconn_callback)(asc->dma_state, tgt);
+}
+
+/*
+ * Watchdog
+ *
+ * So far I have only seen the chip get stuck in a
+ * select/reselect conflict: the reselection did
+ * win and the interrupt register showed it but..
+ * no interrupt was generated.
+ * But we know that some (name withdrawn) disks get
+ * stuck in the middle of dma phases...
+ */
+asc_reset_scsibus(asc)
+ register asc_softc_t asc;
+{
+ register target_info_t *tgt = asc->active_target;
+ register asc_padded_regmap_t *regs = asc->regs;
+ register int ir;
+
+ if (scsi_debug && tgt) {
+ int dmalen;
+ ASC_TC_GET(asc->regs,dmalen); mb();
+ printf("Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count, tgt->transient_state.out_count,
+ asc->in_count, asc->out_count,
+ dmalen);
+ }
+ ir = get_reg(regs,asc_intr); mb();
+ if ((ir & ASC_INT_RESEL) && (SCSI_PHASE(regs->asc_csr) == SCSI_PHASE_MSG_IN)) {
+ /* getting it out of the woods is a bit tricky */
+ spl_t s = splbio();
+
+ (void) asc_reconnect(asc, get_reg(regs,asc_csr), ir);
+ asc_wait(regs, ASC_CSR_INT, 1);
+ ir = get_reg(regs,asc_intr); mb();
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ splx(s);
+ } else {
+ regs->asc_cmd = ASC_CMD_BUS_RESET; mb();
+ delay(35);
+ }
+}
+
+#endif NASC > 0
+
diff --git a/scsi/adapters/scsi_7061.h b/scsi/adapters/scsi_7061.h
new file mode 100644
index 0000000..8969f8b
--- /dev/null
+++ b/scsi/adapters/scsi_7061.h
@@ -0,0 +1,230 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_7061.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 9/90
+ *
+ * Defines for the DEC DC7061 SII gate array (SCSI interface)
+ */
+
+/*
+ * Register map
+ */
+
+typedef struct {
+ volatile unsigned short sii_sdb; /* rw: Data bus and parity */
+ volatile unsigned short sii_sc1; /* rw: scsi signals 1 */
+ volatile unsigned short sii_sc2; /* rw: scsi signals 2 */
+ volatile unsigned short sii_csr; /* rw: control and status */
+ volatile unsigned short sii_id; /* rw: scsi bus ID */
+ volatile unsigned short sii_sel_csr; /* rw: selection status */
+ volatile unsigned short sii_destat; /* ro: selection detector status */
+ volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */
+ volatile unsigned short sii_data; /* rw: data register */
+ volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */
+ volatile unsigned short sii_dma_len; /* rw: length of transfer */
+ volatile unsigned short sii_dma_adr_low;/* rw: low address */
+ volatile unsigned short sii_dma_adr_hi; /* rw: high address */
+ volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */
+ volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */
+ volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */
+ volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */
+ volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */
+ volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */
+ volatile unsigned short sii_data_csr; /* rc: data interrupt control */
+ volatile unsigned short sii_cmd; /* rw: command register */
+ volatile unsigned short sii_diag_csr; /* rw: disgnostic status */
+} sii_regmap_t;
+
+/*
+ * Data bus register (diag)
+ */
+
+#define SII_SDB_DATA 0x00ff /* data bits, assert high */
+#define SII_SDB_PARITY 0x0100 /* parity bit */
+
+/*
+ * Control signals one (diag)
+ */
+
+#define SII_CS1_IO 0x0001 /* I/O bit */
+#define SII_CS1_CD 0x0002 /* Control/Data bit */
+#define SII_CS1_MSG 0x0004 /* Message bit */
+#define SII_CS1_ATN 0x0008 /* Attention bit */
+#define SII_CS1_REQ 0x0010 /* Request bit */
+#define SII_CS1_ACK 0x0020 /* Acknowledge bit */
+#define SII_CS1_RST 0x0040 /* Reset bit */
+#define SII_CS1_SEL 0x0080 /* Selection bit */
+#define SII_CS1_BSY 0x0100 /* Busy bit */
+
+/*
+ * Control signals two (diag)
+ */
+
+#define SII_CS2_SBE 0x0001 /* Bus enable */
+#define SII_CS2_ARB 0x0002 /* arbitration enable */
+#define SII_CS2_TGS 0x0004 /* Target role steer */
+#define SII_CS2_IGS 0x0008 /* Initiator role steer */
+
+/*
+ * Control and status register
+ */
+
+#define SII_CSR_IE 0x0001 /* Interrupt enable */
+#define SII_CSR_PCE 0x0002 /* Parity check enable */
+#define SII_CSR_SLE 0x0004 /* Select enable */
+#define SII_CSR_RSE 0x0008 /* Reselect enable */
+#define SII_CSR_HPM 0x0010 /* Arbitration enable */
+
+/*
+ * SCSI bus ID register
+ */
+
+#define SII_ID_MASK 0x0007 /* The scsi ID */
+#define SII_ID_IO 0x8000 /* ID pins are in/out */
+
+/*
+ * Selector control and status register
+ */
+
+#define SII_SEL_ID 0x0003 /* Destination ID */
+
+/*
+ * Selection detector status register
+ */
+
+#define SII_DET_ID 0x0003 /* Selector's ID */
+
+/*
+ * Data register (silo)
+ */
+
+#define SII_DATA_VAL 0x00ff /* Lower byte */
+
+/*
+ * DMA control register
+ */
+
+#define SII_DMA_SYN_OFFSET 0x0003 /* 0 -> asynch */
+
+/*
+ * DMA counter
+ */
+
+#define SII_DMA_COUNT_MASK 0x1fff /* in bytes */
+
+/*
+ * DMA address registers
+ */
+
+#define SII_DMA_LOW_MASK 0xffff /* all bits */
+#define SII_DMA_HIGH_MASK 0x0003 /* unused ones mbz */
+
+/*
+ * DMA initial byte
+ */
+
+#define SII_DMA_IBYTE 0x00ff /* for odd address DMAs */
+
+/*
+ * Connection status register
+ */
+
+#define SII_CON_LST 0x0002 /* ro: lost arbitration */
+#define SII_CON_SIP 0x0004 /* ro: selection InProgress */
+#define SII_CON_SWA 0x0008 /* rc: selected with ATN */
+#define SII_CON_TGT 0x0010 /* ro: target role */
+#define SII_CON_DST 0x0020 /* ro: sii is destination */
+#define SII_CON_CON 0x0040 /* ro: sii is connected */
+#define SII_CON_SCH 0x0080 /* rci: state change */
+#define SII_CON_LDN 0x0100 /* ??i: dssi list elm done */
+#define SII_CON_BUF 0x0200 /* ??i: dssi buffer service */
+#define SII_CON_TZ 0x0400 /* ??: dssi target zero */
+#define SII_CON_OBC 0x0800 /* ??i: dssi outen bit clr */
+#define SII_CON_BERR 0x1000 /* rci: bus error */
+#define SII_CON_RST 0x2000 /* rci: RST asserted */
+#define SII_CON_DI 0x4000 /* ro: data_csr intr */
+#define SII_CON_CI 0x8000 /* ro: con_csr intr */
+
+/*
+ * Data transfer status register
+ */
+
+#define SII_DTR_IO 0x0001 /* ro: I/O asserted */
+#define SII_DTR_CD 0x0002 /* ro: CD asserted */
+#define SII_DTR_MSG 0x0004 /* ro: MSG asserted */
+#define SII_DTR_ATN 0x0008 /* rc: ATN found asserted */
+#define SII_DTR_MIS 0x0010 /* roi: phase mismatch */
+#define SII_DTR_OBB 0x0100 /* ro: odd byte boundry */
+#define SII_DTR_IPE 0x0200 /* ro: incoming parity err */
+#define SII_DTR_IBF 0x0400 /* roi: input buffer full */
+#define SII_DTR_TBE 0x0800 /* roi: xmt buffer empty */
+#define SII_DTR_TCZ 0x1000 /* ro: xfer counter zero */
+#define SII_DTR_DONE 0x2000 /* rci: xfer complete */
+#define SII_DTR_DI 0x4000 /* ro: data_csr intr */
+#define SII_DTR_CI 0x8000 /* ro: con_csr intr */
+
+#define SII_PHASE(dtr) SCSI_PHASE(dtr)
+
+
+/*
+ * Command register
+ *
+ * Certain bits are only valid in certain roles:
+ * I - Initiator D - Destination T - Target
+ * Bits 0-3 give the 'expected phase'
+ * Bits 4-6 give the 'expected state'
+ * Bits 7-11 are the 'command' proper
+ */
+
+#define SII_CMD_IO 0x0001 /* rw: (T) assert I/O */
+#define SII_CMD_CD 0x0002 /* rw: (T) assert CD */
+#define SII_CMD_MSG 0x0004 /* rw: (T) assert MSG */
+#define SII_CMD_ATN 0x0008 /* rw: (I) assert ATN */
+
+#define SII_CMD_TGT 0x0010 /* rw: (DIT) target */
+#define SII_CMD_DST 0x0020 /* rw: (DIT) destination */
+#define SII_CMD_CON 0x0040 /* rw: (DIT) connected */
+
+#define SII_CMD_RESET 0x0080 /* rw: (DIT) reset */
+#define SII_CMD_DIS 0x0100 /* rw: (DIT) disconnect */
+#define SII_CMD_REQ 0x0200 /* rw: (T) request data */
+#define SII_CMD_SEL 0x0400 /* rw: (D) select */
+#define SII_CMD_XFER 0x0800 /* rw: (IT) xfer information */
+
+#define SII_CMD_RSL 0x1000 /* rw: reselect target */
+#define SII_CMD_RST 0x4000 /* zw: assert RST */
+#define SII_CMD_DMA 0x8000 /* rw: command uses DMA */
+
+/*
+ * Diagnostic control register
+ */
+
+#define SII_DIAG_TEST 0x0001 /* rw: test mode */
+#define SII_DIAG_DIA 0x0002 /* rw: ext loopback mode */
+#define SII_DIAG_PORT_ENB 0x0004 /* rw: enable drivers */
+#define SII_DIAG_LPB 0x0008 /* rw: loopback reg writes */
diff --git a/scsi/adapters/scsi_7061_hdw.c b/scsi/adapters/scsi_7061_hdw.c
new file mode 100644
index 0000000..674e892
--- /dev/null
+++ b/scsi/adapters/scsi_7061_hdw.c
@@ -0,0 +1,2603 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_7061_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 10/90
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the DEC DC7061
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+#include <sii.h>
+#if NSII > 0
+
+#include <platforms.h>
+
+#ifdef DECSTATION
+#define PAD(n) short n
+#endif
+
+#include <machine/machspl.h> /* spl definitions */
+#include <mach/std_types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+#include <machine/machspl.h>
+
+#include <sys/syslog.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#define isa_oddbb hba_dep[0]
+#define oddbb hba_dep[1]
+
+#include <scsi/adapters/scsi_7061.h>
+
+#ifdef PAD
+
+typedef struct {
+ volatile unsigned short sii_sdb; /* rw: Data bus and parity */
+ PAD(pad0);
+ volatile unsigned short sii_sc1; /* rw: scsi signals 1 */
+ PAD(pad1);
+ volatile unsigned short sii_sc2; /* rw: scsi signals 2 */
+ PAD(pad2);
+ volatile unsigned short sii_csr; /* rw: control and status */
+ PAD(pad3);
+ volatile unsigned short sii_id; /* rw: scsi bus ID */
+ PAD(pad4);
+ volatile unsigned short sii_sel_csr; /* rw: selection status */
+ PAD(pad5);
+ volatile unsigned short sii_destat; /* ro: selection detector status */
+ PAD(pad6);
+ volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */
+ PAD(pad7);
+ volatile unsigned short sii_data; /* rw: data register */
+ PAD(pad8);
+ volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */
+ PAD(pad9);
+ volatile unsigned short sii_dma_len; /* rw: length of transfer */
+ PAD(pad10);
+ volatile unsigned short sii_dma_adr_low;/* rw: low address */
+ PAD(pad11);
+ volatile unsigned short sii_dma_adr_hi; /* rw: high address */
+ PAD(pad12);
+ volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */
+ PAD(pad13);
+ volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */
+ PAD(pad14);
+ volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */
+ PAD(pad15);
+ volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */
+ PAD(pad16);
+ volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */
+ PAD(pad17);
+ volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */
+ PAD(pad18);
+ volatile unsigned short sii_data_csr; /* rc: data interrupt control */
+ PAD(pad19);
+ volatile unsigned short sii_cmd; /* rw: command register */
+ PAD(pad20);
+ volatile unsigned short sii_diag_csr; /* rw: disgnostic status */
+ PAD(pad21);
+} sii_padded_regmap_t;
+
+#else /*!PAD*/
+
+typedef sii_regmap_t sii_padded_regmap_t;
+
+#endif /*!PAD*/
+
+
+#undef SII_CSR_SLE
+#define SII_CSR_SLE 0 /* for now */
+
+#ifdef DECSTATION
+#include <mips/PMAX/kn01.h>
+#define SII_OFFSET_RAM (KN01_SYS_SII_B_START-KN01_SYS_SII)
+#define SII_RAM_SIZE (KN01_SYS_SII_B_END-KN01_SYS_SII_B_START)
+/* 16 bits in 32 bit envelopes */
+#define SII_DMADR_LO(ptr) ((((unsigned)ptr)>>1)&SII_DMA_LOW_MASK)
+#define SII_DMADR_HI(ptr) ((((unsigned)ptr)>>(16+1))&SII_DMA_HIGH_MASK)
+#endif /* DECSTATION */
+
+#ifndef SII_OFFSET_RAM /* cross compile check */
+#define SII_OFFSET_RAM 0
+#define SII_RAM_SIZE 0x10000
+#define SII_DMADR_LO(ptr) (((unsigned)ptr)>>16)
+#define SII_DMADR_HI(ptr) (((unsigned)ptr)&0xffff)
+#endif
+
+/*
+ * Statically partition the DMA buffer between targets.
+ * This way we will eventually be able to attach/detach
+ * drives on-fly. And 18k/target is enough.
+ */
+#define PER_TGT_DMA_SIZE ((SII_RAM_SIZE/7) & ~(sizeof(int)-1))
+
+/*
+ * Round to 4k to make debug easier
+ */
+#define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12)
+
+/*
+ * Macros to make certain things a little more readable
+ */
+#define SII_COMMAND(regs,cs,ds,cmd) \
+ { \
+ (regs)->sii_cmd = ((cs) & 0x70) | \
+ ((ds) & 0x07) | (cmd); \
+ wbflush(); \
+ }
+#define SII_ACK(regs,cs,ds,cmd) \
+ { \
+ SII_COMMAND(regs,cs,ds,cmd); \
+ (regs)->sii_conn_csr = (cs); \
+ (regs)->sii_data_csr = (ds); \
+ }
+
+/*
+ * Deal with bogus pmax dma buffer
+ */
+
+static char decent_buffer[NSII*8][256];
+
+/*
+ * A script has a three parts: a pre-condition, an action, and
+ * an optional command to the chip. The first triggers error
+ * handling if not satisfied and in our case it is formed by the
+ * values of the sii_conn_csr and sii_data_csr register
+ * bits. The action part is just a function pointer, and the
+ * command is what the 7061 should be told to do at the end
+ * of the action processing. This command is only issued and the
+ * script proceeds if the action routine returns TRUE.
+ * See sii_intr() for how and where this is all done.
+ */
+
+typedef struct script {
+ int condition; /* expected state at interrupt */
+ int (*action)(); /* extra operations */
+ int command; /* command to the chip */
+} *script_t;
+
+#define SCRIPT_MATCH(cs,ds) ((cs)&0x70|SCSI_PHASE((ds)))
+
+#define SII_PHASE_DISC 0x4 /* sort of .. */
+
+/* When no command is needed */
+#define SCRIPT_END -1
+
+/* forward decls of script actions */
+boolean_t
+ sii_script_true(), /* when nothing needed */
+ sii_identify(), /* send identify msg */
+ sii_dosynch(), /* negotiate synch xfer */
+ sii_dma_in(), /* get data from target via dma */
+ sii_dma_out(), /* send data to target via dma */
+ sii_get_status(), /* get status from target */
+ sii_end_transaction(), /* all come to an end */
+ sii_msg_in(), /* get disconnect message(s) */
+ sii_disconnected(); /* current target disconnected */
+/* forward decls of error handlers */
+boolean_t
+ sii_err_generic(), /* generic error handler */
+ sii_err_disconn(), /* when a target disconnects */
+ sii_err_rdp(), /* in reconn, handle rdp mgs */
+ gimmeabreak(); /* drop into the debugger */
+
+int sii_reset_scsibus();
+boolean_t sii_probe_target();
+static sii_wait();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) SCSI-7061 interface
+ */
+struct sii_softc {
+ watchdog_t wd;
+ sii_padded_regmap_t *regs;
+ volatile char *buff;
+ script_t script;
+ int (*error_handler)();
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define SII_STATE_BUSY 0x01 /* selecting or currently connected */
+#define SII_STATE_TARGET 0x04 /* currently selected as target */
+#define SII_STATE_COLLISION 0x08 /* lost selection attempt */
+#define SII_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+ unsigned char cmd_count;
+
+ scsi_softc_t *sc;
+ target_info_t *active_target;
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+
+} sii_softc_data[NSII];
+
+typedef struct sii_softc *sii_softc_t;
+
+sii_softc_t sii_softc[NSII];
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int sii_min_period = 63; /* in 4 ns units */
+int sii_max_offset = 3; /* pure number */
+
+#define sii_to_scsi_period(a) (a)
+#define scsi_period_to_sii(p) (((p) < sii_min_period) ? sii_min_period : (p))
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int sii_probe(), scsi_slave(), sii_go(), sii_intr();
+extern void scsi_attach();
+
+vm_offset_t sii_std[NSII] = { 0 };
+struct bus_device *sii_dinfo[NSII*8];
+struct bus_ctlr *sii_minfo[NSII];
+struct bus_driver sii_driver =
+ { sii_probe, scsi_slave, scsi_attach, sii_go, sii_std, "rz", sii_dinfo,
+ "sii", sii_minfo, /*BUS_INTR_B4_PROBE?*/};
+
+/*
+ * Scripts
+ */
+struct script
+sii_script_data_in[] = {
+ { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD},
+ { SCSI_PHASE_DATAI|SII_CON_CON, sii_dma_in,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAI},
+ { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END}
+},
+
+sii_script_data_out[] = {
+ { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD},
+ { SCSI_PHASE_DATAO|SII_CON_CON, sii_dma_out,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAO},
+ { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END}
+},
+
+sii_script_cmd[] = {
+ { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD},
+ { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END}
+},
+
+/* Same, after a disconnect */
+
+sii_script_restart_data_in[] = {
+ { SCSI_PHASE_DATAI|SII_CON_CON|SII_CON_DST, sii_dma_in,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAI},
+ { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END}
+},
+
+sii_script_restart_data_out[] = {
+ { SCSI_PHASE_DATAO|SII_CON_CON|SII_CON_DST, sii_dma_out,
+ (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAO},
+ { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END}
+},
+
+sii_script_restart_cmd[] = {
+ { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status,
+ SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS},
+ { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END}
+},
+
+/* Synchronous transfer negotiation */
+
+sii_script_try_synch[] = {
+ { SCSI_PHASE_MSG_OUT|SII_CON_CON, sii_dosynch, SCRIPT_END}
+},
+
+/* Disconnect sequence */
+
+sii_script_disconnect[] = {
+ { SII_PHASE_DISC, sii_disconnected, SCRIPT_END}
+};
+
+
+
+#define u_min(a,b) (((a) < (b)) ? (a) : (b))
+
+
+#define DEBUG
+#ifdef DEBUG
+
+sii_state(regs)
+ sii_padded_regmap_t *regs;
+{
+ unsigned dmadr;
+
+ if (regs == 0)
+ regs = (sii_padded_regmap_t*) 0xba000000;
+
+ dmadr = regs->sii_dma_adr_low | (regs->sii_dma_adr_hi << 16);
+ db_printf("sc %x, dma %x @ x%X, cs %x, ds %x, cmd %x\n",
+ (unsigned) regs->sii_sc1,
+ (unsigned) regs->sii_dma_len, dmadr,
+ (unsigned) regs->sii_conn_csr,
+ (unsigned) regs->sii_data_csr,
+ (unsigned) regs->sii_cmd);
+
+}
+sii_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = sii_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("@x%x: fl %X dma %X+%X cmd %x@%X id %X per %X off %X ior %X ret %X\n",
+ tgt,
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym(spt,1);
+ db_printf(": %X %X ", spt->condition, spt->command);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+sii_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = sii_softc[unit]->sc->target[i];
+ if (tgt)
+ sii_target_state(tgt);
+ }
+}
+
+sii_script_state(unit)
+{
+ script_t spt = sii_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym(spt,1);
+ db_printf(": %X %X ", spt->condition, spt->command);
+ db_printsym(spt->action,1);
+ db_printf(", ");
+ db_printsym(sii_softc[unit]->error_handler, 1);
+ return 0;
+
+}
+
+#define PRINT(x) if (scsi_debug) printf x
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int sii_logpt;
+char sii_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x25
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ sii_log[sii_logpt++] = (e);
+ if (sii_logpt == LOGSIZE) sii_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+sii_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = sii_logpt; i < LOGSIZE; i++) {
+ c = sii_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-x%x", c & 0x7f);
+ }
+ db_printf("\n");
+ return 0;
+}
+
+sii_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /* TRACE */
+#define LOG(e,f)
+#endif /* TRACE */
+
+struct cnt {
+ unsigned int zeroes;
+ unsigned int usage;
+ unsigned int avg;
+ unsigned int min;
+ unsigned int max;
+};
+
+static bump(counter, value)
+ register struct cnt *counter;
+ register unsigned int value;
+{
+ register unsigned int n;
+
+ if (value == 0) {
+ counter->zeroes++;
+ return;
+ }
+ n = counter->usage + 1;
+ counter->usage = n;
+ if (n == 0) {
+ printf("{Counter at x%x overflowed with avg x%x}",
+ counter, counter->avg);
+ return;
+ } else
+ if (n == 1)
+ counter->min = 0xffffffff;
+
+ counter->avg = ((counter->avg * (n - 1)) + value) / n;
+ if (counter->min > value)
+ counter->min = value;
+ if (counter->max < value)
+ counter->max = value;
+}
+
+struct cnt
+ s_cnt;
+
+#else /* DEBUG */
+#define PRINT(x)
+#define LOG(e,f)
+#define TR(x)
+#define TRCHECK
+#define TRWRAP
+#endif /* DEBUG */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send an identify msg to each possible target on the bus
+ * except of course ourselves.
+ */
+sii_probe(reg, ui)
+ unsigned reg;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ sii_softc_t sii = &sii_softc_data[unit];
+ int target_id, i;
+ scsi_softc_t *sc;
+ register sii_padded_regmap_t *regs;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+ char *dma_ptr;
+ static char *here = "sii_probe";
+
+ /*
+ * We are only called if the chip is there,
+ * but make sure anyways..
+ */
+ if (check_memory(reg, 0))
+ return 0;
+
+#ifdef MACH_KERNEL
+ /* Mappable version side */
+ SII_probe(reg, ui);
+#endif /*MACH_KERNEL*/
+
+ /*
+ * Initialize hw descriptor
+ */
+ sii_softc[unit] = sii;
+ sii->regs = (sii_padded_regmap_t *) (reg);
+ sii->buff = (volatile char*) (reg + SII_OFFSET_RAM);
+
+ queue_init(&sii->waiting_targets);
+
+ sc = scsi_master_alloc(unit, sii);
+ sii->sc = sc;
+
+ sc->go = sii_go;
+ sc->watchdog = scsi_watchdog;
+ sc->probe = sii_probe_target;
+
+ sii->wd.reset = sii_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1; /* unlimited */
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ regs = sii->regs;
+
+ /*
+ * Clear out dma buffer
+ */
+ blkclr(sii->buff, SII_RAM_SIZE);
+
+ /*
+ * Reset chip, fully.
+ */
+ s = splbio();
+ sii_reset(regs, TRUE);
+
+ /*
+ * Our SCSI id on the bus.
+ * The user can set this via the prom on pmaxen/3maxen.
+ * If this changes it is easy to fix: make a default that
+ * can be changed as boot arg.
+ */
+#ifdef unneeded
+ regs->sii_id = (scsi_initiator_id[unit] & SII_ID_MASK)|SII_ID_IO;
+#endif
+ sc->initiator_id = regs->sii_id & SII_ID_MASK;
+ printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ for (target_id = 0, dma_ptr = (char*)sii->buff;
+ target_id < 8;
+ target_id++, dma_ptr += (PER_TGT_DMA_SIZE*2)) {
+
+ register unsigned csr, dsr;
+ register scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ regs->sii_sel_csr = target_id;
+ wbflush();
+
+ /* select */
+ regs->sii_cmd = SII_CMD_SEL;
+ wbflush();
+
+ /* wait for a selection timeout delay, and some more */
+ delay(251000);
+
+ dsr = regs->sii_data_csr;
+ csr = regs->sii_conn_csr;
+ if ((csr & SII_CON_CON) == 0) {
+
+ regs->sii_conn_csr = csr;/*wtc bits*/
+
+ /* abort sel in progress */
+ if (csr & SII_CON_SIP) {
+ regs->sii_cmd = SII_CMD_DIS;
+ wbflush();
+ csr = sii_wait(&regs->sii_conn_csr, SII_CON_SCH,1);
+ regs->sii_conn_csr = 0xffff;/*wtc bits */
+ regs->sii_data_csr = 0xffff;
+ regs->sii_cmd = 0;
+ wbflush();
+ }
+ continue;
+ }
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ /* should be command phase here */
+ if (SCSI_PHASE(dsr) != SCSI_PHASE_CMD)
+ panic(here);
+
+ /* acknowledge state change */
+ SII_ACK(regs,csr,dsr,0);
+
+ /* build command in (bogus) dma area */
+ {
+ unsigned int *p = (unsigned int*) dma_ptr;
+
+ p[0] = SCSI_CMD_TEST_UNIT_READY | (0 << 8);
+ p[1] = 0 | (0 << 8);
+ p[2] = 0 | (0 << 8);
+ }
+
+ /* set up dma xfer parameters */
+ regs->sii_dma_len = 6;
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ wbflush();
+
+ /* issue dma command */
+ SII_COMMAND(regs,csr,dsr,SII_CMD_XFER|SII_CMD_DMA);
+
+ /* wait till done */
+ dsr = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_cmd &= ~(SII_CMD_XFER|SII_CMD_DMA);
+ regs->sii_data_csr = SII_DTR_DONE;/* clear */
+ regs->sii_dma_len = 0;
+
+ /* move on to status phase */
+ dsr = sii_wait(&regs->sii_data_csr, SCSI_PHASE_STATUS,1);
+ csr = regs->sii_conn_csr;
+ SII_ACK(regs,csr,dsr,0);
+
+ if (SCSI_PHASE(dsr) != SCSI_PHASE_STATUS)
+ panic(here);
+
+ /* get status byte */
+ dsr = sii_wait(&regs->sii_data_csr, SII_DTR_IBF,1);
+ csr = regs->sii_conn_csr;
+
+ status.bits = regs->sii_data;
+ if (status.st.scsi_status_code != SCSI_ST_GOOD)
+ scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
+
+ /* get cmd_complete message */
+ SII_ACK(regs,csr,dsr,0);
+ SII_COMMAND(regs,csr,dsr,SII_CMD_XFER);
+ dsr = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+
+ dsr = sii_wait(&regs->sii_data_csr, SCSI_PHASE_MSG_IN,1);
+ csr = regs->sii_conn_csr;
+
+
+ SII_ACK(regs,csr,dsr,0);
+ i = regs->sii_data;
+ SII_COMMAND(regs,csr,dsr,SII_CMD_XFER);
+
+ /* check disconnected, clear all intr bits */
+ csr = sii_wait(&regs->sii_conn_csr, SII_CON_SCH,1);
+ if (regs->sii_conn_csr & SII_CON_CON)
+ panic(here);
+
+ regs->sii_data_csr = 0xffff;
+ regs->sii_conn_csr = 0xffff;
+ regs->sii_cmd = 0;
+
+ /*
+ * Found a target
+ */
+ sii->ntargets++;
+ {
+ register target_info_t *tgt;
+ tgt = scsi_slave_alloc(sc->masterno, target_id, sii);
+
+ tgt->dma_ptr = dma_ptr;
+ tgt->cmd_ptr = decent_buffer[unit*8 + target_id];
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+ }
+ }
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+sii_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ sii_softc_t sii = sii_softc[tgt->masterno];
+ boolean_t newlywed;
+ int sii_probe_timeout();
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+ char *dma_ptr = (char*)sii->buff;
+
+ dma_ptr += (PER_TGT_DMA_SIZE * tgt->target_id)*2;
+ tgt->dma_ptr = dma_ptr;
+ tgt->cmd_ptr = decent_buffer[tgt->masterno*8 + tgt->target_id];
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+
+ }
+
+ /* Unfortunately, the SII chip does not have timeout support
+ for selection */
+ timeout(sii_probe_timeout, tgt, hz);
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ untimeout(sii_probe_timeout, tgt);
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+sii_probe_timeout(tgt)
+ target_info_t *tgt;
+{
+ sii_softc_t sii = (sii_softc_t)tgt->hw_state;
+ register sii_padded_regmap_t *regs = sii->regs;
+ int cs, ds;
+ spl_t s;
+
+ /* cancelled ? */
+ if (tgt->done != SCSI_RET_IN_PROGRESS)
+ return;
+
+ s = splbio();
+
+ /* Someone else might be using the bus (rare) */
+ switch (regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) {
+ case SII_CON_SIP:
+ /* We really timed out */
+ break;
+ case SII_CON_SIP|SII_CON_LST:
+ /* Someone else is (still) using the bus */
+ sii->wd.watchdog_state = SCSI_WD_ACTIVE;
+ /* fall-through */
+ default:
+ /* Did not get a chance to the bus yet */
+ timeout(sii_probe_timeout, tgt, hz);
+ goto ret;
+ }
+ regs->sii_cmd = SII_CMD_DIS;
+ wbflush();
+ regs->sii_csr |= SII_CSR_RSE;
+ regs->sii_cmd = 0;
+ wbflush();
+
+ sii->done = SCSI_RET_DEVICE_DOWN;
+ cs = regs->sii_conn_csr;
+ ds = regs->sii_data_csr;
+ if (!sii_end(sii, cs, ds))
+ (void) sii_reconnect(sii, cs, ds);
+ret:
+ splx(s);
+}
+
+
+static sii_wait(preg, until, complain)
+ volatile unsigned short *preg;
+{
+ int timeo = 1000000;
+ while ((*preg & until) != until) {
+ delay(1);
+ if (!timeo--) {
+ if (complain) {
+ gimmeabreak();
+ printf("sii_wait TIMEO with x%x\n", *preg);
+ }
+ break;
+ }
+ }
+#ifdef DEBUG
+ bump(&s_cnt, 1000000-timeo);
+#endif
+ return *preg;
+}
+
+sii_reset(regs, quickly)
+ register sii_padded_regmap_t *regs;
+ boolean_t quickly;
+{
+ int my_id;
+
+ my_id = regs->sii_id & SII_ID_MASK;
+
+ regs->sii_cmd = SII_CMD_RESET;
+ wbflush();
+ delay(30);
+
+ /* clear them all random bitsy */
+ regs->sii_conn_csr = SII_CON_SWA|SII_CON_SCH|SII_CON_BERR|SII_CON_RST;
+ regs->sii_data_csr = SII_DTR_ATN|SII_DTR_DONE;
+
+ regs->sii_id = my_id | SII_ID_IO;
+
+ regs->sii_dma_ctrl = 0; /* asynch */
+
+ regs->sii_dma_len = 0;
+ regs->sii_dma_adr_low = 0;
+ regs->sii_dma_adr_hi = 0;
+
+ regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM;
+ /* later: SII_CSR_RSE */
+
+ regs->sii_diag_csr = SII_DIAG_PORT_ENB;
+ wbflush();
+
+ if (quickly)
+ return;
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call sii_bus_reset().
+ */
+ regs->sii_cmd = SII_CMD_RST;
+
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+sii_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ sii_softc_t sii;
+ register spl_t s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+
+ LOG(1,"go");
+
+ sii = (sii_softc_t)tgt->hw_state;
+
+ /*
+ * We cannot do real DMA.
+ */
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ if (tgt->ior)
+ fdma_map(&tgt->fdma, tgt->ior);
+#endif /*MACH_KERNEL*/
+
+ copyout_gap16(tgt->cmd_ptr, tgt->dma_ptr, cmd_count);
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+
+ if (len > PER_TGT_BUFF_SIZE)
+ len = PER_TGT_BUFF_SIZE;
+ copyout_gap16( ior->io_data,
+ tgt->dma_ptr + (cmd_count<<1),
+ len);
+ tgt->transient_state.copy_count = len;
+
+ /* avoid leaks */
+ if (len < tgt->block_size) {
+ bzero_gap16(tgt->dma_ptr + ((cmd_count + len)<<1),
+ len - tgt->block_size);
+ len = tgt->block_size;
+ tgt->transient_state.copy_count = len;
+ }
+
+ } else {
+ tgt->transient_state.out_count = 0;
+ tgt->transient_state.copy_count = 0;
+ }
+
+ tgt->transient_state.cmd_count = cmd_count;
+ tgt->transient_state.isa_oddbb = FALSE;
+
+ disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id);
+ disconn = disconn && (sii->ntargets > 1);
+ disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id);
+
+ /*
+ * Setup target state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ handler = (disconn) ? sii_err_disconn : sii_err_generic;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(0x13,"readop");
+ scp = sii_script_data_in;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x14,"writeop");
+ scp = sii_script_data_out;
+ break;
+ case SCSI_CMD_INQUIRY:
+ /* This is likely the first thing out:
+ do the synch neg if so */
+ if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
+ scp = sii_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ break;
+ }
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ scp = sii_script_data_in;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.out_count =
+ cmd_count - tgt->transient_state.cmd_count;
+ scp = sii_script_data_out;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ /*
+ * Do the synch negotiation here, unless done already
+ */
+ if (tgt->flags & TGT_DID_SYNCH) {
+ scp = sii_script_cmd;
+ } else {
+ scp = sii_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ }
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ scp = sii_script_cmd;
+ }
+
+ tgt->transient_state.script = scp;
+ tgt->transient_state.handler = handler;
+ tgt->transient_state.identify = (cmd_only) ? 0xff :
+ (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
+ SCSI_IDENTIFY);
+
+ if (in_count)
+ tgt->transient_state.in_count =
+ (in_count < tgt->block_size) ? tgt->block_size : in_count;
+ else
+ tgt->transient_state.in_count = 0;
+
+ tgt->transient_state.dma_offset = 0;
+
+ /*
+ * See if another target is currently selected on
+ * this SCSI bus, e.g. lock the sii structure.
+ * Note that it is the strategy routine's job
+ * to serialize ops on the same target as appropriate.
+ * XXX here and everywhere, locks!
+ */
+ /*
+ * Protection viz reconnections makes it tricky.
+ */
+/* s = splbio();*/
+ s = splhigh();
+
+ if (sii->wd.nactive++ == 0)
+ sii->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (sii->state & SII_STATE_BUSY) {
+ /*
+ * Queue up this target, note that this takes care
+ * of proper FIFO scheduling of the scsi-bus.
+ */
+ LOG(3,"enqueue");
+ LOG(0x80+tgt->target_id,0);
+ enqueue_tail(&sii->waiting_targets, (queue_entry_t) tgt);
+ } else {
+ /*
+ * It is down to at most two contenders now,
+ * we will treat reconnections same as selections
+ * and let the scsi-bus arbitration process decide.
+ */
+ sii->state |= SII_STATE_BUSY;
+ sii->next_target = tgt;
+ sii_attempt_selection(sii);
+ /*
+ * Note that we might still lose arbitration..
+ */
+ }
+ splx(s);
+}
+
+sii_attempt_selection(sii)
+ sii_softc_t sii;
+{
+ target_info_t *tgt;
+ register int out_count;
+ sii_padded_regmap_t *regs;
+ register int cmd;
+
+ regs = sii->regs;
+ tgt = sii->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * Init bus state variables and set registers.
+ * [They are intermixed to avoid wbflush()es]
+ */
+ sii->active_target = tgt;
+
+ out_count = tgt->transient_state.cmd_count;
+
+ /* set dma pointer and counter */
+ regs->sii_dma_len = out_count;
+ regs->sii_dma_adr_low = SII_DMADR_LO(tgt->dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(tgt->dma_ptr);
+
+ sii->error_handler = tgt->transient_state.handler;
+
+ regs->sii_sel_csr = tgt->target_id;
+
+ sii->done = SCSI_RET_IN_PROGRESS;
+
+ regs->sii_dma_ctrl = tgt->sync_offset;
+
+ sii->cmd_count = out_count;
+
+/* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST))*/
+ if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL))
+ return;
+ regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM;
+
+ sii->script = tgt->transient_state.script;
+ sii->in_count = 0;
+ sii->out_count = 0;
+
+ if (tgt->flags & TGT_DID_SYNCH) {
+ if (tgt->transient_state.identify == 0xff)
+ cmd = SII_CMD_SEL;
+ else {
+ cmd = SII_CMD_SEL | SII_CMD_ATN |
+ SII_CMD_CON | SII_CMD_XFER | SCSI_PHASE_MSG_OUT;
+ /* chain select and message out */
+/*??*/ regs->sii_dma_1st_byte = tgt->transient_state.identify;
+ }
+ } else if (tgt->flags & TGT_TRY_SYNCH)
+ cmd = SII_CMD_SEL | SII_CMD_ATN;
+ else
+ cmd = SII_CMD_SEL;
+
+/* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST)) { */
+ if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL)) {
+ /* let the reconnection attempt proceed */
+ regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|
+ SII_CSR_HPM|SII_CSR_RSE;
+ sii->script = 0;
+ LOG(0x8c,0);
+ } else {
+ regs->sii_cmd = cmd;
+ wbflush();
+ }
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+boolean_t sii_inside_sii_intr = FALSE;
+
+sii_intr(unit,spllevel)
+{
+ register sii_softc_t sii;
+ register script_t scp;
+ register int cs, ds;
+ register sii_padded_regmap_t *regs;
+ boolean_t try_match;
+#ifdef MACH_KERNEL
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return SII_intr(unit,spllevel);
+#endif /*MACH_KERNEL*/
+
+ /* interrupt code is NOT reentrant */
+ if (sii_inside_sii_intr) {
+ LOG(0x22,"!!attempted to reenter sii_intr!!");
+ return;
+ }
+ sii_inside_sii_intr = TRUE;
+
+ LOG(5,"\n\tintr");
+
+ sii = sii_softc[unit];
+
+ /* collect status information */
+ regs = sii->regs;
+ cs = regs->sii_conn_csr;
+ ds = regs->sii_data_csr;
+
+TR(cs);
+TR(ds);
+TR(regs->sii_cmd);
+TRCHECK;
+
+ if (cs & SII_CON_RST){
+ sii_bus_reset(sii);
+ goto getout;
+ }
+
+ /* we got an interrupt allright */
+ if (sii->active_target)
+ sii->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ /* rid of DONEs */
+ if (ds & SII_DTR_DONE) {
+ regs->sii_data_csr = SII_DTR_DONE;
+ LOG(0x1e,"done");
+ ds = regs->sii_data_csr;
+ cs = regs->sii_conn_csr;
+ }
+
+ /* drop spurious calls, note that sometimes
+ * ds and cs get out-of-sync */
+ if (((cs & SII_CON_CI) | (ds & SII_DTR_DI)) == 0) {
+ LOG(2,"SPURIOUS");
+ goto getout;
+ }
+
+ /* clear interrupt flags */
+
+ regs->sii_conn_csr = cs;
+ regs->sii_data_csr = cs;
+
+ /* drop priority */
+ splx(spllevel);
+
+ if ((sii->state & SII_STATE_TARGET) ||
+ (cs & SII_CON_TGT)) {
+ sii_target_intr(sii,cs,ds);
+ goto getout;
+ }
+
+ scp = sii->script;
+
+ /* check who got the bus */
+ if ((scp == 0) || (cs & SII_CON_LST)) {
+ if (cs & SII_CON_DST) {
+ sii_reconnect(sii, cs, ds);
+ goto getout;
+ }
+ LOG(0x12,"no-script");
+ goto getout;
+ }
+
+ if (SCRIPT_MATCH(cs,ds) != scp->condition) {
+ if (try_match = (*sii->error_handler)(sii, cs, ds)) {
+ cs = regs->sii_conn_csr;
+ ds = regs->sii_data_csr;
+ }
+ } else
+ try_match = TRUE;
+
+ /* might have been side effected */
+ scp = sii->script;
+
+ if (try_match && (SCRIPT_MATCH(cs,ds) == scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(sii, cs, ds)) {
+ /* might have been side effected */
+ scp = sii->script;
+ sii->script = scp + 1;
+ regs->sii_cmd = scp->command;
+ wbflush();
+ }
+ }
+getout:
+ sii_inside_sii_intr = FALSE;
+}
+
+
+sii_target_intr(sii)
+ register sii_softc_t sii;
+{
+ panic("SII: TARGET MODE !!!\n");
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to
+ */
+boolean_t
+sii_script_true(sii, cs, ds)
+ register sii_softc_t sii;
+
+{
+ SII_COMMAND(sii->regs,cs,ds,SII_CON_CON/*sanity*/);
+ LOG(7,"nop");
+ return TRUE;
+}
+
+boolean_t
+sii_end_transaction( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ SII_COMMAND(sii->regs,cs,ds,0);
+
+ LOG(0x1f,"end_t");
+
+ regs->sii_csr &= ~SII_CSR_RSE;
+
+ /* is the fifo really clean here ? */
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_IBF,1);
+
+ if (regs->sii_data != SCSI_COMMAND_COMPLETE)
+ printf("{T%x}", regs->sii_data);
+
+ regs->sii_cmd = SII_CMD_XFER | SII_CON_CON | SCSI_PHASE_MSG_IN |
+ (cs & SII_CON_DST);
+ wbflush();
+
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+
+ regs->sii_cmd = 0/*SII_PHASE_DISC*/;
+ wbflush();
+
+ cs = regs->sii_conn_csr;
+
+ if ((cs & SII_CON_SCH) == 0)
+ cs = sii_wait(&regs->sii_conn_csr, SII_CON_SCH,1);
+ regs->sii_conn_csr = SII_CON_SCH;
+
+ regs->sii_csr |= SII_CSR_RSE;
+
+ cs = regs->sii_conn_csr;
+
+ if (!sii_end(sii, cs, ds))
+ (void) sii_reconnect(sii, cs, ds);
+ return FALSE;
+}
+
+boolean_t
+sii_end( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register target_info_t *tgt;
+ register io_req_t ior;
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ LOG(6,"end");
+
+ tgt = sii->active_target;
+
+ if ((tgt->done = sii->done) == SCSI_RET_IN_PROGRESS)
+ tgt->done = SCSI_RET_SUCCESS;
+
+ sii->script = 0;
+
+ if (sii->wd.nactive-- == 1)
+ sii->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+ /* check reconnection not pending */
+ cs = regs->sii_conn_csr;
+ if ((cs & SII_CON_DST) == 0)
+ sii_release_bus(sii);
+ else {
+ sii->active_target = 0;
+/* sii->state &= ~SII_STATE_BUSY; later */
+ }
+
+ if (ior = tgt->ior) {
+ LOG(0xA,"ops->restart");
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_unmap(&tgt->fdma, ior);
+#endif /*MACH_KERNEL*/
+ (*tgt->dev_ops->restart)(tgt, TRUE);
+ if (cs & SII_CON_DST)
+ sii->state &= ~SII_STATE_BUSY;
+ }
+
+ return ((cs & SII_CON_DST) == 0);
+}
+
+boolean_t
+sii_release_bus(sii)
+ register sii_softc_t sii;
+{
+ boolean_t ret = FALSE;
+
+ LOG(9,"release");
+
+ sii->script = 0;
+
+ if (sii->state & SII_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ sii->state &= ~SII_STATE_COLLISION;
+ sii_attempt_selection(sii);
+
+ } else if (queue_empty(&sii->waiting_targets)) {
+
+ sii->state &= ~SII_STATE_BUSY;
+ sii->active_target = 0;
+ ret = TRUE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ sii->next_target = (target_info_t *)
+ dequeue_head(&sii->waiting_targets);
+ sii_attempt_selection(sii);
+ }
+ return ret;
+}
+
+boolean_t
+sii_get_status( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs = sii->regs;
+ register scsi2_status_byte_t status;
+ register target_info_t *tgt;
+ unsigned int len;
+ unsigned short cmd;
+
+ LOG(0xD,"get_status");
+TRWRAP;
+
+ sii->state &= ~SII_STATE_DMA_IN;
+
+ tgt = sii->active_target;
+ sii->error_handler = tgt->transient_state.handler;
+
+ if (len = sii->in_count) {
+ if ((tgt->cur_cmd != SCSI_CMD_READ) &&
+ (tgt->cur_cmd != SCSI_CMD_LONG_READ)){
+ len -= regs->sii_dma_len;
+ copyin_gap16(tgt->dma_ptr, tgt->cmd_ptr, len);
+ if (len & 0x1) /* odd byte, left in silo */
+ tgt->cmd_ptr[len - 1] = regs->sii_data;
+ } else {
+ if (regs->sii_dma_len) {
+#if 0
+ this is incorrect and besides..
+ tgt->ior->io_residual = regs->sii_dma_len;
+#endif
+ len -= regs->sii_dma_len;
+ }
+ careful_copyin_gap16( tgt, tgt->transient_state.dma_offset,
+ len, ds & SII_DTR_OBB,
+ regs->sii_dma_1st_byte);
+ }
+ sii->in_count = 0;
+ }
+
+ len = regs->sii_dma_len;
+ regs->sii_dma_len = 0;/*later?*/
+
+ /* if dma is still in progress we have to quiet it down */
+ cmd = regs->sii_cmd;
+ if (cmd & SII_CMD_DMA) {
+ regs->sii_cmd = cmd & ~(SII_CMD_DMA|SII_CMD_XFER);
+ wbflush();
+ /* DONE might NOT pop up. Sigh. */
+ delay(10);
+ regs->sii_data_csr = regs->sii_data_csr;
+ }
+
+ regs->sii_cmd = SCSI_PHASE_STATUS|SII_CON_CON|(cs & SII_CON_DST);
+ wbflush();
+
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_IBF,1);
+ status.bits = regs->sii_data;
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(sii->active_target, SCSI_ERR_STATUS, status.bits, 0);
+ sii->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ sii->done = SCSI_RET_SUCCESS;
+
+ return TRUE;
+}
+
+boolean_t
+sii_dma_in( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register target_info_t *tgt;
+ register sii_padded_regmap_t *regs = sii->regs;
+ char *dma_ptr;
+ register int count;
+ boolean_t advance_script = TRUE;
+
+ SII_COMMAND(regs,cs,ds,0);
+ LOG(0xE,"dma_in");
+
+ tgt = sii->active_target;
+ sii->error_handler = tgt->transient_state.handler;
+ sii->state |= SII_STATE_DMA_IN;
+
+ if (sii->in_count == 0) {
+ /*
+ * Got nothing yet: either just sent the command
+ * or just reconnected
+ */
+ register int avail;
+
+ if (tgt->transient_state.isa_oddbb) {
+ regs->sii_dma_1st_byte = tgt->transient_state.oddbb;
+ tgt->transient_state.isa_oddbb = FALSE;
+ }
+
+ count = tgt->transient_state.in_count;
+ count = u_min(count, (SII_DMA_COUNT_MASK+1));
+ avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, avail);
+
+ /* common case of 8k-or-less read ? */
+ advance_script = (tgt->transient_state.in_count == count);
+
+ } else {
+
+ /*
+ * We received some data.
+ * Also, take care of bogus interrupts
+ */
+ register int offset, xferred;
+ unsigned char obb = regs->sii_data;
+
+ xferred = sii->in_count - regs->sii_dma_len;
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ count = u_min(tgt->transient_state.in_count, (SII_DMA_COUNT_MASK+1));
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) {
+ tgt->transient_state.dma_offset = 0;
+ } else {
+ register int avail;
+ avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, avail);
+ }
+
+ /* get some more */
+ dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1);
+ sii->in_count = count;
+ regs->sii_dma_len = count;
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ wbflush();
+ regs->sii_cmd = sii->script->command;
+ wbflush();
+
+ /* copy what we got */
+ careful_copyin_gap16( tgt, offset, xferred, ds & SII_DTR_OBB, obb);
+
+ /* last chunk ? */
+ if (count == tgt->transient_state.in_count) {
+ sii->script++;
+ }
+ return FALSE;
+ }
+quickie:
+ sii->in_count = count;
+ dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1);
+ regs->sii_dma_len = count;
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ wbflush();
+
+ if (!advance_script) {
+ regs->sii_cmd = sii->script->command;
+ wbflush();
+ }
+ return advance_script;
+}
+
+/* send data to target. Called in three different ways:
+ (a) to start transfer (b) to restart a bigger-than-8k
+ transfer (c) after reconnection
+ */
+boolean_t
+sii_dma_out( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs = sii->regs;
+ register char *dma_ptr;
+ register target_info_t *tgt;
+ boolean_t advance_script = TRUE;
+ int count = sii->out_count;
+
+ SII_COMMAND(regs,cs,ds,0);
+ LOG(0xF,"dma_out");
+
+ tgt = sii->active_target;
+ sii->error_handler = tgt->transient_state.handler;
+ sii->state &= ~SII_STATE_DMA_IN;
+
+ if (sii->out_count == 0) {
+ /*
+ * Nothing committed: either just sent the
+ * command or reconnected
+ */
+ register int remains;
+
+ count = tgt->transient_state.out_count;
+ count = u_min(count, (SII_DMA_COUNT_MASK+1));
+ remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, remains);
+
+ /* common case of 8k-or-less write ? */
+ advance_script = (tgt->transient_state.out_count == count);
+ } else {
+ /*
+ * We sent some data.
+ * Also, take care of bogus interrupts
+ */
+ register int offset, xferred;
+
+ xferred = sii->out_count - regs->sii_dma_len;
+ assert(xferred > 0);
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ count = u_min(tgt->transient_state.out_count, (SII_DMA_COUNT_MASK+1));
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) {
+ tgt->transient_state.dma_offset = 0;
+ } else {
+ register int remains;
+ remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset;
+ count = u_min(count, remains);
+ }
+ /* last chunk ? */
+ if (tgt->transient_state.out_count == count)
+ goto quickie;
+
+ /* ship some more */
+ dma_ptr = tgt->dma_ptr +
+ ((tgt->transient_state.cmd_count + tgt->transient_state.dma_offset) << 1);
+ sii->out_count = count;
+ regs->sii_dma_len = count;
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ wbflush();
+ regs->sii_cmd = sii->script->command;
+
+ /* copy some more data */
+ careful_copyout_gap16(tgt, offset, xferred);
+ return FALSE;
+ }
+
+quickie:
+ sii->out_count = count;
+ dma_ptr = tgt->dma_ptr +
+ ((tgt->transient_state.cmd_count + tgt->transient_state.dma_offset) << 1);
+ regs->sii_dma_len = count;
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ wbflush();
+
+ if (!advance_script) {
+ regs->sii_cmd = sii->script->command;
+ wbflush();
+ }
+ return advance_script;
+}
+
+/* disconnect-reconnect ops */
+
+/* get the message in via dma */
+boolean_t
+sii_msg_in(sii, cs, ds)
+ register sii_softc_t sii;
+ register unsigned char cs, ds;
+{
+ register target_info_t *tgt;
+ char *dma_ptr;
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ LOG(0x15,"msg_in");
+
+ tgt = sii->active_target;
+
+ dma_ptr = tgt->dma_ptr;
+ /* We would clobber the data for READs */
+ if (sii->state & SII_STATE_DMA_IN) {
+ register int offset;
+ offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset;
+ if (offset & 1) offset++;
+ dma_ptr += (offset << 1);
+ }
+
+ regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr);
+ /* We only really expect two bytes */
+ regs->sii_dma_len = sizeof(scsi_command_group_0);
+ wbflush();
+
+ return TRUE;
+}
+
+/* check the message is indeed a DISCONNECT */
+boolean_t
+sii_disconnect(sii, cs, ds)
+ register sii_softc_t sii;
+ register unsigned char cs, ds;
+
+{
+ register target_info_t *tgt;
+ register int len;
+ boolean_t ok = FALSE;
+ unsigned int dmsg = 0;
+
+ tgt = sii->active_target;
+
+ len = sizeof(scsi_command_group_0) - sii->regs->sii_dma_len;
+ PRINT(("{G%d}",len));
+
+/* if (len == 0) ok = FALSE; */
+ if (len == 1) {
+ dmsg = sii->regs->sii_dma_1st_byte;
+ ok = (dmsg == SCSI_DISCONNECT);
+ } else if (len == 2) {
+ register char *msgs;
+ register unsigned int offset;
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ /* wherever it was, take it from there */
+ offset = regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16);
+ msgs = (char*)sii->buff + (offset << 1);
+ dmsg = *((unsigned short *)msgs);
+
+ /* A SDP message preceeds it in non-completed READs */
+ ok = (((dmsg & 0xff) == SCSI_DISCONNECT) ||
+ (dmsg == ((SCSI_DISCONNECT<<8)|SCSI_SAVE_DATA_POINTER)));
+ }
+ if (!ok)
+ printf("[tgt %d bad msg (%d): %x]", tgt->target_id, len, dmsg);
+
+ return TRUE;
+}
+
+/* save all relevant data, free the BUS */
+boolean_t
+sii_disconnected(sii, cs, ds)
+ register sii_softc_t sii;
+ register unsigned char cs, ds;
+
+{
+ register target_info_t *tgt;
+
+ SII_COMMAND(sii->regs,cs,ds,0);
+
+ sii->regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|
+ SII_CSR_HPM|SII_CSR_RSE;
+
+ LOG(0x16,"disconnected");
+
+ sii_disconnect(sii,cs,ds);
+
+ tgt = sii->active_target;
+ tgt->flags |= TGT_DISCONNECTED;
+ tgt->transient_state.handler = sii->error_handler;
+ /* the rest has been saved in sii_err_disconn() */
+
+ PRINT(("{D%d}", tgt->target_id));
+
+ sii_release_bus(sii);
+
+ return FALSE;
+}
+
+/* get reconnect message, restore BUS */
+boolean_t
+sii_reconnect(sii, cs, ds)
+ register sii_softc_t sii;
+ register unsigned char cs, ds;
+
+{
+ register target_info_t *tgt;
+ sii_padded_regmap_t *regs;
+ int id;
+
+ regs = sii->regs;
+ regs->sii_conn_csr = SII_CON_SCH;
+ regs->sii_cmd = SII_CON_CON|SII_CON_DST|SCSI_PHASE_MSG_IN;
+ wbflush();
+
+ LOG(0x17,"reconnect");
+
+ /*
+ * See if this reconnection collided with a selection attempt
+ */
+ if (sii->state & SII_STATE_BUSY)
+ sii->state |= SII_STATE_COLLISION;
+
+ sii->state |= SII_STATE_BUSY;
+
+ cs = regs->sii_conn_csr;
+
+ /* tk50s are slow */
+ if ((cs & SII_CON_CON) == 0)
+ cs = sii_wait(&regs->sii_conn_csr, SII_CON_CON,1);
+
+ /* ?? */
+ if (regs->sii_conn_csr & SII_CON_BERR)
+ regs->sii_conn_csr = SII_CON_BERR;
+
+ if ((ds & SII_DTR_IBF) == 0)
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_IBF,1);
+
+ if (regs->sii_data != SCSI_IDENTIFY)
+ printf("{I%x %x}", regs->sii_data, regs->sii_dma_1st_byte);
+
+ /* find tgt: id is in sii_destat */
+ id = regs->sii_destat;
+
+ tgt = sii->sc->target[id];
+ if (id > 7 || tgt == 0) panic("sii_reconnect");
+
+ PRINT(("{R%d}", id));
+ if (sii->state & SII_STATE_COLLISION)
+ PRINT(("[B %d-%d]", sii->active_target->target_id, id));
+
+ LOG(0x80+id,0);
+
+ sii->active_target = tgt;
+ tgt->flags &= ~TGT_DISCONNECTED;
+
+ /* synch params */
+ regs->sii_dma_ctrl = tgt->sync_offset;
+ regs->sii_dma_len = 0;
+
+ sii->script = tgt->transient_state.script;
+ sii->error_handler = sii_err_rdp;
+ sii->in_count = 0;
+ sii->out_count = 0;
+
+ regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN;
+ wbflush();
+
+ (void) sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+
+ return TRUE;
+}
+
+
+/* do the synch negotiation */
+boolean_t
+sii_dosynch( sii, cs, ds)
+ register sii_softc_t sii;
+{
+ /*
+ * Phase is MSG_OUT here, cmd has not been xferred
+ */
+ int *p, len;
+ unsigned short dmalo, dmahi, dmalen;
+ register target_info_t *tgt;
+ register sii_padded_regmap_t *regs = sii->regs;
+ unsigned char off;
+
+ regs->sii_cmd = SCSI_PHASE_MSG_OUT|SII_CMD_ATN|SII_CON_CON;
+ wbflush();
+
+ LOG(0x11,"dosync");
+
+ tgt = sii->active_target;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ p = (int*) (tgt->dma_ptr + (((regs->sii_dma_len<<1) + 2) & ~3));
+ p[0] = SCSI_IDENTIFY | (SCSI_EXTENDED_MESSAGE<<8);
+ p[1] = 3 | (SCSI_SYNC_XFER_REQUEST<<8);
+ if (BGET(scsi_no_synchronous_xfer,tgt->masterno,tgt->target_id))
+ off = 0;
+ else
+ off = sii_max_offset;
+ /* but we'll ship "off" manually */
+ p[2] = sii_to_scsi_period(sii_min_period) |(off << 8);
+
+ dmalen = regs->sii_dma_len;
+ dmalo = regs->sii_dma_adr_low;
+ dmahi = regs->sii_dma_adr_hi;
+ regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t) /* + 1 */;
+ regs->sii_dma_adr_low = SII_DMADR_LO(p);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(p);
+ wbflush();
+
+ regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CMD_ATN|
+ SII_CON_CON|SCSI_PHASE_MSG_OUT;
+ wbflush();
+
+ /* wait for either DONE or MIS */
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DI,1);
+
+ /* TK50s do not like xtended messages */
+ /* and some others just ignore the standard */
+ if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_OUT) {
+ /* disentangle FIFO */
+ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT;
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ if (SCSI_PHASE(ds) == SCSI_PHASE_MSG_IN)
+ goto msgin;
+ goto got_answer;
+ }
+
+ /* ack and stop dma */
+ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT|SII_CMD_ATN;
+ wbflush();
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+ wbflush();
+
+ /* last byte of message */
+ regs->sii_data = off;
+ wbflush();
+ regs->sii_cmd = SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_OUT;
+ wbflush();
+
+ /* Race here: who will interrupt first, the DMA
+ controller or the status watching machine ? */
+ delay(1000);
+ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT;
+ wbflush();
+
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+
+ /* The standard sez there nothing else the target can do but.. */
+ ds = sii_wait(&regs->sii_data_csr, SCSI_PHASE_MSG_IN,0);
+
+ /* Of course, what are standards for ? */
+ if (SCSI_PHASE(ds) == SCSI_PHASE_CMD)
+ goto cmdp;
+msgin:
+ /* ack */
+ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN;
+ wbflush();
+
+ /* set up dma to receive answer */
+ regs->sii_dma_adr_low = SII_DMADR_LO(p);
+ regs->sii_dma_adr_hi = SII_DMADR_HI(p);
+ regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t);
+ wbflush();
+ regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_IN;
+ wbflush();
+
+ /* wait for the answer, and look at it */
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_MIS,1);
+
+ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN;
+ wbflush();
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+
+got_answer:
+ /* do not cancel the phase mismatch */
+ regs->sii_data_csr = SII_DTR_DONE;
+
+ if (regs->sii_dma_len || ((p[0] & 0xff) != SCSI_EXTENDED_MESSAGE)) {
+ /* did not like it */
+ printf(" did not like SYNCH xfer ");
+ } else {
+ /* will do synch */
+ tgt->sync_period = scsi_period_to_sii((p[1]>>8)&0xff);
+ tgt->sync_offset = regs->sii_data; /* odd xfer, in silo */
+ /* sanity */
+ if (tgt->sync_offset > sii_max_offset)
+ tgt->sync_offset = sii_max_offset;
+ regs->sii_dma_ctrl = tgt->sync_offset;
+ }
+
+cmdp:
+ /* phase should be command now */
+ regs->sii_dma_len = dmalen;
+ regs->sii_dma_adr_low = dmalo;
+ regs->sii_dma_adr_hi = dmahi;
+ wbflush();
+
+ /* continue with simple command script */
+ sii->error_handler = sii_err_generic;
+
+ sii->script = (tgt->cur_cmd == SCSI_CMD_INQUIRY) ?
+ sii_script_data_in : sii_script_cmd;
+ if (SCSI_PHASE(ds) == SCSI_PHASE_CMD )
+ return TRUE;
+
+ sii->script++;
+ if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS )
+ return TRUE;
+
+ sii->script++; /* msgin? */
+ sii->script++;
+ if (SCSI_PHASE(ds) == SII_PHASE_DISC)
+ return TRUE;
+
+gimmeabreak();
+ panic("sii_dosynch");
+ return FALSE;
+}
+
+/*
+ * The bus was reset
+ */
+sii_bus_reset(sii)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ LOG(0x21,"bus_reset");
+
+ /*
+ * Clear interrupt bits
+ */
+ regs->sii_conn_csr = 0xffff;
+ regs->sii_data_csr = 0xffff;
+
+ /*
+ * Clear bus descriptor
+ */
+ sii->script = 0;
+ sii->error_handler = 0;
+ sii->active_target = 0;
+ sii->next_target = 0;
+ sii->state = 0;
+ queue_init(&sii->waiting_targets);
+ sii->wd.nactive = 0;
+ sii_reset(regs, TRUE);
+
+ log(LOG_KERN, "sii: (%d) bus reset ", ++sii->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (sii->sc == 0) /* sanity */
+ return;
+
+ sii_inside_sii_intr = FALSE;
+
+ scsi_bus_was_reset(sii->sc);
+}
+
+/*
+ * Error handlers
+ */
+
+/*
+ * Generic, default handler
+ */
+boolean_t
+sii_err_generic(sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register int cond = sii->script->condition;
+
+ LOG(0x10,"err_generic");
+
+ /*
+ * Note to DEC hardware people.
+ * Dropping the notion of interrupting on
+ * DMA completions (or at least make it optional)
+ * would save TWO interrupts out of the SEVEN that
+ * are currently requested for a non-disconnecting
+ * READ or WRITE operation.
+ */
+ if (ds & SII_DTR_DONE)
+ return TRUE;
+
+ /* this is a band-aid */
+ if ((SCSI_PHASE(cond) == SII_PHASE_DISC) &&
+ (cs & SII_CON_SCH)) {
+ ds &= ~7;
+ ds |= SII_PHASE_DISC;
+ (void) (*sii->script->action)(sii,cs,ds);
+ return FALSE;
+ }
+
+ /* TK50s are slow to connect, forgive em */
+ if ((SCSI_PHASE(ds) == SCSI_PHASE_MSG_OUT) ||
+ (SCSI_PHASE(cond) == SCSI_PHASE_MSG_OUT))
+ return TRUE;
+ if ((SCSI_PHASE(cond) == SCSI_PHASE_CMD) &&
+ ((SCSI_PHASE(ds) == 0) || (SCSI_PHASE(ds) == 4) || (SCSI_PHASE(ds) == 5)))
+ return TRUE;
+
+ /* transition to status ? */
+ if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS)
+ return sii_err_to_status(sii, cs, ds);
+
+ return sii_err_phase_mismatch(sii,cs,ds);
+}
+
+/*
+ * Handle generic errors that are reported as
+ * an unexpected change to STATUS phase
+ */
+sii_err_to_status(sii, cs, ds)
+ register sii_softc_t sii;
+{
+ script_t scp = sii->script;
+
+ LOG(0x20,"err_tostatus");
+ while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS)
+ scp++;
+ sii->script = scp;
+#if 0
+ /*
+ * Normally, we would already be able to say the command
+ * is in error, e.g. the tape had a filemark or something.
+ * But in case we do disconnected mode WRITEs, it is quite
+ * common that the following happens:
+ * dma_out -> disconnect -> reconnect
+ * and our script might expect at this point that the dma
+ * had to be restarted (it didn't know it was completed
+ * because the tape record is shorter than we asked for).
+ * And in any event.. it is both correct and cleaner to
+ * declare error iff the STATUS byte says so.
+ */
+ sii->done = SCSI_RET_NEED_SENSE;
+#endif
+ return TRUE;
+}
+
+/*
+ * Watch for a disconnection
+ */
+boolean_t
+sii_err_disconn(sii, cs, ds)
+ register sii_softc_t sii;
+ register unsigned cs, ds;
+{
+ register sii_padded_regmap_t *regs;
+ register target_info_t *tgt;
+ int count;
+ int from;
+ unsigned char obb;
+ int delayed_copy = 0;
+
+ LOG(0x18,"err_disconn");
+
+ if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN)
+ return sii_err_generic(sii, cs, ds);
+
+ regs = sii->regs;
+
+ if ((regs->sii_cmd & (SII_CMD_DMA|SII_CMD_XFER)) ==
+ (SII_CMD_DMA|SII_CMD_XFER)) {
+ /* stop dma and wait */
+ regs->sii_cmd &= ~(SII_CMD_DMA|SII_CMD_XFER);
+ (void) sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+/* later: regs->sii_data_csr = SII_DTR_DONE; */
+ }
+
+ SII_COMMAND(regs,cs,ds,0);
+
+ tgt = sii->active_target;
+ switch (SCSI_PHASE(sii->script->condition)) {
+ case SCSI_PHASE_DATAO:
+ LOG(0x1b,"+DATAO");
+ if (sii->out_count) {
+ register int xferred, offset;
+
+ xferred = sii->out_count - regs->sii_dma_len;
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ delayed_copy = 1;
+ from = offset;
+ count = xferred;
+
+ }
+ tgt->transient_state.script = sii_script_restart_data_out;
+ break;
+
+ case SCSI_PHASE_DATAI:
+ LOG(0x19,"+DATAI");
+ if (sii->in_count) {
+ register int offset, xferred;
+
+ obb = regs->sii_dma_1st_byte;
+
+ xferred = sii->in_count - regs->sii_dma_len;
+ assert(xferred > 0);
+ if (ds & SII_DTR_OBB) {
+ tgt->transient_state.isa_oddbb = TRUE;
+ tgt->transient_state.oddbb = obb;
+ }
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ /* copy what we got */
+
+ delayed_copy = 2;
+ from = offset;
+ count = xferred;
+
+ }
+ tgt->transient_state.script = sii_script_restart_data_in;
+ break;
+
+ case SCSI_PHASE_STATUS:
+ /* will have to restart dma */
+ if (count = regs->sii_dma_len) {
+ (void) sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+ }
+ if (sii->state & SII_STATE_DMA_IN) {
+ register int offset, xferred;
+
+ obb = regs->sii_dma_1st_byte;
+
+ LOG(0x1a,"+STATUS+R");
+
+ xferred = sii->in_count - count;
+ assert(xferred > 0);
+ if (ds & SII_DTR_OBB) {
+ tgt->transient_state.isa_oddbb = TRUE;
+ tgt->transient_state.oddbb = obb;
+ }
+ tgt->transient_state.in_count -= xferred;
+/* assert(tgt->transient_state.in_count > 0);*/
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ /* copy what we got */
+
+ delayed_copy = 2;
+ from = offset;
+ count = xferred;
+
+ tgt->transient_state.script = sii_script_restart_data_in;
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else {
+
+ LOG(0x1d,"+STATUS+W");
+
+ if ((count == 0) && (tgt->transient_state.out_count == sii->out_count)) {
+ /* all done */
+ tgt->transient_state.script = &sii_script_restart_data_out[1];
+ tgt->transient_state.out_count = 0;
+ } else {
+ register int xferred, offset;
+
+ /* how much we xferred */
+ xferred = sii->out_count - count;
+
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ offset = tgt->transient_state.dma_offset;
+ tgt->transient_state.dma_offset += xferred;
+ if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE)
+ tgt->transient_state.dma_offset = 0;
+
+ delayed_copy = 1;
+ from = offset;
+ count = xferred;
+
+ tgt->transient_state.script = sii_script_restart_data_out;
+ }
+ sii->out_count = 0;
+ }
+ break;
+ case SII_PHASE_DISC: /* sometimes disconnects and phase remains */
+ return sii_err_generic(sii, cs, ds);
+ default:
+ gimmeabreak();
+ }
+ regs->sii_csr &= ~SII_CSR_RSE;
+ sii_msg_in(sii,cs,ds);
+ sii->script = sii_script_disconnect;
+ regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SCSI_PHASE_MSG_IN|
+ SII_CON_CON|(regs->sii_conn_csr & SII_CON_DST);
+ wbflush();
+ if (delayed_copy == 2)
+ careful_copyin_gap16( tgt, from, count, ds & SII_DTR_OBB, obb);
+ else if (delayed_copy == 1)
+ careful_copyout_gap16( tgt, from, count);
+
+ return FALSE;
+}
+
+/*
+ * Suppose someone reads the specs as they read the Bible.
+ * They would send these unnecessary restore-pointer msgs
+ * in reconnect phases. If this was a SCSI-2 modify-pointer
+ * I could understand, but. Oh well.
+ */
+sii_err_rdp(sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs;
+ register target_info_t *tgt;
+
+ LOG(0x24,"err_drp");
+
+ /* One chance */
+ sii->error_handler = sii->active_target->transient_state.handler;
+
+ if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN)
+ return sii_err_generic(sii, cs, ds);
+
+ regs = sii->regs;
+
+ if ((ds & SII_DTR_IBF) == 0)
+ ds = sii_wait(&regs->sii_data_csr, SII_DTR_IBF,1);
+
+ if (regs->sii_data != SCSI_RESTORE_POINTERS)
+ return sii_err_disconn(sii, cs, ds);
+
+ regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN;
+ wbflush();
+
+ (void) sii_wait(&regs->sii_data_csr, SII_DTR_DONE,1);
+ regs->sii_data_csr = SII_DTR_DONE;
+
+ return FALSE;
+}
+
+/*
+ * Handle strange, yet unexplained interrupts and error
+ * situations which eventually I will be old and wise
+ * enough to deal with properly with preventive care.
+ */
+sii_err_phase_mismatch(sii, cs, ds)
+ register sii_softc_t sii;
+{
+ register sii_padded_regmap_t *regs = sii->regs;
+ register int match;
+
+ LOG(0x23,"err_mismatch");
+
+ match = SCSI_PHASE(sii->script->condition);
+
+ /* dmain interrupted */
+ if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAI)) {
+ register int xferred;
+ register char *p;
+
+ if (regs->sii_dma_len <= 1) {
+/*if (scsi_debug)*/
+printf("[DMAINZERO %x %x %x]", cs, ds, regs->sii_dma_len);
+ if (regs->sii_dma_len == 0) {
+ regs->sii_dma_len = sii->in_count;
+ wbflush();
+ regs->sii_cmd = sii->script[-1].command;
+ }
+ return FALSE;
+ }
+
+ /* This happens when you do not "init" the prom
+ and the fifo is screwed up */
+ xferred = sii->in_count - regs->sii_dma_len;
+ p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) );
+ p += xferred;
+if (scsi_debug)
+printf("[DMAIN %x %x %x]", cs, ds, xferred);
+ /* odd bytes are not xferred */
+ if (((unsigned)p) & 0x1){
+ register short *oddb;
+ oddb = (short*)(sii->buff) + ((unsigned)p-1);/*shifts*/
+ *oddb = regs->sii_dma_1st_byte;
+ }
+ regs->sii_dma_adr_low = ((unsigned)p);
+ regs->sii_dma_adr_hi = ((unsigned)p) << 16;
+ wbflush();
+ regs->sii_cmd = sii->script[-1].command;
+ wbflush();
+ return FALSE;
+ } else
+ /* dmaout interrupted */
+ if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAO)) {
+ register int xferred;
+ register char *p;
+
+ if (regs->sii_dma_len <= 1) {
+/*if (scsi_debug)*/
+printf("[DMAOUTZERO %x %x %x]", cs, ds, regs->sii_dma_len);
+gimmeabreak();
+ if (regs->sii_dma_len == 0) {
+ regs->sii_dma_len = sii->out_count;
+ wbflush();
+ regs->sii_cmd = sii->script[-1].command;
+ }
+ return FALSE;
+ }
+
+ xferred = sii->out_count - regs->sii_dma_len;
+/*if (scsi_debug)*/
+printf("[DMAOUT %x %x %x %x]", cs, ds, regs->sii_dma_len, sii->out_count);
+ sii->out_count -= xferred;
+ p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) );
+ p += xferred;
+ regs->sii_dma_adr_low = ((unsigned)p);
+ regs->sii_dma_adr_hi = ((unsigned)p) << 16;
+ wbflush();
+ regs->sii_cmd = sii->script[-1].command;
+ wbflush();
+ return FALSE;
+ }
+#if 1 /* ?? */
+ /* stuck in cmd phase */
+ else if ((SCSI_PHASE(ds) == SCSI_PHASE_CMD) &&
+ ((match == SCSI_PHASE_DATAI) || (match == SCSI_PHASE_DATAO))) {
+/*if (scsi_debug)*/
+printf("[CMD %x %x %x %x]", cs, ds, sii->cmd_count, regs->sii_dma_len);
+ if (regs->sii_dma_len != 0) {
+ /* ouch, this hurts */
+ register int xferred;
+ register char *p;
+
+ xferred = sii->cmd_count - regs->sii_dma_len;
+ sii->cmd_count -= xferred;
+ p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) );
+ p += xferred;
+ regs->sii_dma_adr_low = ((unsigned)p);
+ regs->sii_dma_adr_hi = ((unsigned)p) << 16;
+ wbflush();
+ regs->sii_cmd = 0x8842;
+ wbflush();
+ return FALSE;;
+
+ }
+ SII_ACK(regs,cs,ds,0/*match*/);
+ wbflush();
+ return FALSE;;
+ }
+#endif
+ else {
+ printf("{D%x %x}", cs, ds);
+/* if (scsi_debug)*/ gimmeabreak();
+ }
+ return FALSE;
+}
+
+/*
+ * Watchdog
+ *
+ * There are two ways in which I have seen the chip
+ * get stuck: a target never reconnected, or the
+ * selection deadlocked. Both cases involved a tk50,
+ * but elsewhere it showed up with hitachi disks too.
+ */
+sii_reset_scsibus(sii)
+ register sii_softc_t sii;
+{
+ register target_info_t *tgt = sii->active_target;
+ register sii_padded_regmap_t *regs = sii->regs;
+
+ /* see if SIP still --> device down or non-existant */
+ if ((regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) == SII_CON_SIP){
+ if (tgt) {
+ log(LOG_KERN, "Target %d went offline\n",
+ tgt->target_id);
+ tgt->flags = 0;
+ return sii_probe_timeout(tgt);
+ }
+ /* else fall through */
+ }
+
+ if (tgt)
+ log(LOG_KERN, "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count, tgt->transient_state.out_count,
+ sii->in_count, sii->out_count,
+ sii->regs->sii_dma_len);
+
+ sii->regs->sii_cmd = SII_CMD_RST;
+ delay(25);
+}
+
+/*
+ * Copy routines that avoid odd pointers
+ */
+boolean_t nocopyin = FALSE;
+careful_copyin_gap16(tgt, offset, len, isaobb, obb)
+ register target_info_t *tgt;
+ unsigned char obb;
+{
+ register char *from, *to;
+ register int count;
+
+ count = tgt->transient_state.copy_count;
+
+ from = tgt->dma_ptr + (offset << 1);
+ to = tgt->ior->io_data + count;
+ tgt->transient_state.copy_count = count + len;
+ if (count & 1) {
+ from -= (1 << 1);
+ to -= 1;
+ len += 1;
+ }
+if (nocopyin) return;/*timing*/
+ copyin_gap16( from, to, len);
+ /* check for last, poor little odd byte */
+ if (isaobb) {
+ to += len;
+ to[-1] = obb;
+ }
+}
+
+careful_copyout_gap16( tgt, offset, len)
+ register target_info_t *tgt;
+{
+ register char *from, *to;
+ register int count, olen;
+ unsigned char c;
+ char *p;
+
+ count = tgt->ior->io_count - tgt->transient_state.copy_count;
+ if (count > 0) {
+
+ len = u_min(count, len);
+ offset += tgt->transient_state.cmd_count;
+
+ count = tgt->transient_state.copy_count;
+ tgt->transient_state.copy_count = count + len;
+
+ from = tgt->ior->io_data + count;
+ to = tgt->dma_ptr + (offset << 1);
+
+ /* the scsi buffer acts weirdo at times */
+ if ((olen=len) & 1) {
+ p = tgt->dma_ptr + ((offset + olen - 1)<<1);
+ c = (*(unsigned short*)p) >> 8;/*!MSF*/
+ }
+
+ if (count & 1) {
+ from -= 1;
+ to -= (1 << 1);
+ len += 1;
+ }
+
+ count = copyout_gap16(from, to, len);
+
+ /* the scsi buffer acts weirdo at times */
+ if (olen & 1) {
+ unsigned char cv;
+ cv = (*(unsigned short*)p) >> 8;/*!MSF*/
+ if (c != cv) {
+ /*
+ * Scott Fahlman would say
+ * "Use a big plier!"
+ */
+ unsigned short s;
+ volatile unsigned short *pp;
+ pp = (volatile unsigned short*)p;
+ s = (c << 8) | (from[len-1] & 0xff);
+ do {
+ *pp = s;
+ } while (*pp != s);
+ }
+ }
+ }
+}
+
+#endif NSII > 0
+
diff --git a/scsi/adapters/scsi_89352.h b/scsi/adapters/scsi_89352.h
new file mode 100644
index 0000000..85c579f
--- /dev/null
+++ b/scsi/adapters/scsi_89352.h
@@ -0,0 +1,231 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_89352.h
+ * Author: Daniel Stodolsky, Carnegie Mellon University
+ * Date: 06/91
+ *
+ * Defines for the Fujitsu MB89352 SCSI Protocol Controller (HBA)
+ * The definitions herein also cover the 89351, 87035/36, 87033B
+ */
+
+/*
+ * Register map, padded as needed. Matches Hardware, do not screw up.
+ */
+
+#define vuc volatile unsigned char
+
+typedef struct
+{
+ vuc spc_bdid; /* Bus Device ID (R/W) */
+#define spc_myid spc_bdid
+ char pad0[3];
+ vuc spc_sctl; /* SPC Control register (R/W) */
+ char pad1[3];
+ vuc spc_scmd; /* Command Register (R/W) */
+ char pad2[3];
+ vuc spc_tmod; /* Transmit Mode Register (synch models) */
+ char pad3[3];
+ vuc spc_ints; /* Interrupt sense (R); Interrupt Reset (w) */
+ char pad4[3];
+ vuc spc_psns; /* Phase Sense (R); SPC Diagnostic Control (w) */
+#define spc_phase spc_psns
+ char pad5[3];
+ vuc spc_ssts; /* SPC status (R/O) */
+ char pad6[3];
+ vuc spc_serr; /* SPC error status (R/O) */
+ char pad7[3];
+ vuc spc_pctl; /* Phase Control (R/W) */
+ char pad8[3];
+ vuc spc_mbc; /* Modifed Byte Counter (R/O) */
+ char pad9[3];
+ vuc spc_dreg; /* Data Register (R/W) */
+ char pad10[3];
+ vuc spc_temp; /* Temporary Register (R/W) */
+ char pad11[3];
+ vuc spc_tch; /* Transfer Counter High (R/W) */
+ char pad12[3];
+ vuc spc_tcm; /* Transfer Counter Middle (R/W) */
+ char pad13[3];
+ vuc spc_tcl; /* Transfer Counter Low (R/W) */
+ char pad14[3];
+ vuc spc_exbf; /* External Buffer (synch models) */
+ char pad15[3];
+} spc_regmap_t;
+
+#undef vuc
+
+/*
+ * Control register
+ */
+
+#define SPC_SCTL_DISABLE 0x80
+#define SPC_SCTL_RESET 0x40
+#define SPC_SCTL_DIAGMODE 0x20
+#define SPC_SCTL_ARB_EBL 0x10
+#define SPC_SCTL_PAR_EBL 0x08
+#define SPC_SCTL_SEL_EBL 0x04
+#define SPC_SCTL_RSEL_EBL 0x02
+#define SPC_SCTL_IE 0x01
+
+/*
+ * Command register
+ */
+
+#define SPC_SCMD_CMDMASK 0xe0
+# define SPC_SCMD_C_ACKREQ_S 0xe0
+# define SPC_SCMD_C_ACKREQ_C 0xc0
+# define SPC_SCMD_C_STOP_X 0xa0
+# define SPC_SCMD_C_XFER 0x80
+# define SPC_SCMD_C_ATN_S 0x60
+# define SPC_SCMD_C_ATN_C 0x40
+# define SPC_SCMD_C_SELECT 0x20
+# define SPC_SCMD_C_BUS_RLSE 0x00
+#define SPC_SCMD_BUSRST 0x10
+#define SPC_SCMD_INTERCEPT_X 0x08
+#define SPC_SCMD_PROGRAMMED_X 0x04
+#define SPC_SCMD_PAD_X 0x01
+
+/*
+ * Transfer mode register (MB87033B/35/36)
+ */
+
+#define SPC_TMOD_SYNC_X 0x80
+#define SPC_TMOD_OFFSET_MASK 0x70
+# define SPC_OFFSET(x) (((x)<<4)&SPC_TMOD_OFFSET_MASK)
+#define SPC_TMOD_PERIOD_MASK 0xc0
+# define SPC_PERIOD(x) (((x)<<2)&SPC_TMOD_PERIOD_MASK)
+#define SPC_TMOD_EXP_COUNTER 0x01
+
+/*
+ * Interrupt cause register
+ */
+
+#define SPC_INTS_SELECTED 0x80
+#define SPC_INTS_RESELECTED 0x40
+#define SPC_INTS_DISC 0x20
+#define SPC_INTS_DONE 0x10
+#define SPC_INTS_BUSREQ 0x08
+#define SPC_INTS_TIMEOUT 0x04
+#define SPC_INTS_ERROR 0x02
+#define SPC_INTS_RESET 0x01
+
+/*
+ * SCSI Bus signals ("phase")
+ */
+
+#define SPC_BUS_REQ 0x80 /* rw */
+#define SPC_BUS_ACK 0x40 /* rw */
+#define SPC_BUS_ATN 0x20 /* ro */
+# define SPC_DIAG_ENBL_XFER 0x20 /* wo */
+#define SPC_BUS_SEL 0x10 /* ro */
+#define SPC_BUS_BSY 0x08 /* rw */
+#define SPC_BUS_MSG 0x04 /* rw */
+#define SPC_BUS_CD 0x02 /* rw */
+#define SPC_BUS_IO 0x01 /* rw */
+
+#define SPC_CUR_PHASE(x) SCSI_PHASE(x)
+
+#define SPC_BSY(r) (r->spc_phase & SPC_BUS_BSY)
+
+/*
+ * Chip status register
+ */
+
+#define SPC_SSTS_INI_CON 0x80
+#define SPC_SSTS_TGT_CON 0x40
+#define SPC_SSTS_BUSY 0x20
+#define SPC_SSTS_XIP 0x10
+#define SPC_SSTS_RST 0x08
+#define SPC_SSTS_TC0 0x04
+#define SPC_SSTS_FIFO_FULL 0x02
+#define SPC_SSTS_FIFO_EMPTY 0x01
+
+/*
+ * Error register
+ */
+
+#define SPC_SERR_SEL 0x80 /* Selected */
+#define SPC_SERR_RSEL 0x40 /* Reselected */
+#define SPC_SERR_DISC 0x20 /* Disconnected */
+#define SPC_SERR_CMDC 0x10 /* Command Complete */
+#define SPC_SERR_SRVQ 0x08 /* Service Required */
+#define SPC_SERR_TIMO 0x04 /* Timeout */
+#define SPC_SERR_HARDERR 0x02 /* SPC Hard Error */
+#define SPC_SERR_RSTC 0x01 /* Reset Condition */
+
+/*
+ * Phase control register
+ *
+ * [use SPC_CUR_PHASE() here too]
+ */
+
+#define SPC_PCTL_BFREE_IE 0x80 /* Bus free (disconnected) */
+#define SPC_PCTL_LST_IE 0x40 /* lost arbit (87033) */
+#define SPC_PCTL_ATN_IE 0x20 /* ATN set (87033) */
+#define SPC_PCTL_RST_DIS 0x10 /* RST asserted */
+
+/*
+ * Modified byte counter register
+ */
+
+#define SPC_MBC_ECNT_MASK 0xf0 /* 87033 only */
+# define SPC_MBC_ECNT_GET(x) (((x)&SPC_MBC_ECNT_MASK)>>4)
+# define SPC_MBC_ECNT_PUT(x) (((x)<<4)&SPC_MBC_ECNT_MASK)
+#define SPC_MBC_MBC_MASK 0x0f
+# define SPC_MBC_GET(x) ((x)&SPC_MBC_MBC_MASK)
+# define SPC_MBC_PUT(x) ((x)&SPC_MBC_MBC_MASK)
+
+/*
+ * Transfer counter register(s)
+ */
+
+#define SPC_TC_PUT(ptr,val) { \
+ (ptr)->spc_tch = (((val)>>16)&0xff); \
+ (ptr)->spc_tcm = (((val)>> 8)&0xff); \
+ (ptr)->spc_tcl = (((val) )&0xff); \
+ }
+
+#define SPC_TC_GET(ptr,val) { \
+ (val) = (((ptr)->spc_tch & 0xff )<<16) |\
+ (((ptr)->spc_tcm 0 0xff )<<8) |\
+ ((ptr)->spc_tcl 0xff);\
+ }
+
+/* 87033 in expanded mode */
+#define SPC_XTC_PUT(ptr,val) { \
+ (ptr)->spc_mbc = SPC_MBC_ECNT_PUT(((val)>>24));\
+ (ptr)->spc_tch = (((val)>>16)&0xff); \
+ (ptr)->spc_tcm = (((val)>> 8)&0xff); \
+ (ptr)->spc_tcl = (((val) )&0xff); \
+ }
+
+#define SPC_XTC_GET(ptr,val) { \
+ (val) = (SPC_MBC_ECNT_GET((ptr)->spc_mbc)<<24)|\
+ (((ptr)->spc_tch)<<16)|(((ptr)->spc_tcm)<<8)|\
+ ((ptr)->spc_tcl);\
+ }
+
diff --git a/scsi/adapters/scsi_89352_hdw.c b/scsi/adapters/scsi_89352_hdw.c
new file mode 100644
index 0000000..5672cb6
--- /dev/null
+++ b/scsi/adapters/scsi_89352_hdw.c
@@ -0,0 +1,2192 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_89352_hdw.c
+ * Author: Daniel Stodolsky, Carnegie Mellon University
+ * Date: 06/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the Fujitsu MB89352
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+/*
+ *
+ *
+ * Known Headaches/Features with this chip.
+ *
+ * (1) After the interrupt raised by select, the phase sense (psns)
+ * and SPC status (ssts) registers do not display the correct values
+ * until the REQ line (via psns) is high. (danner@cs.cmu.edu 6/11/91)
+ *
+ * (2) After a data in phase, the command complete interrupt may be raised
+ * before the psns, ssts, and transfer counter registers settle. The reset
+ * acknowledge or request command should not be issued until they settle.
+ * (danner@cs.cmu.edu 6/14/91)
+ *
+ * (3) In general, an interrupt can be raised before the psns and ssts have
+ * meaningful values. One should wait for the psns to show the REQ bit (0x80)
+ * set before expecting meaningful values, with the exception of (2) above.
+ * Currently this is handled by spc_err_generic ("Late REQ"). (This problem
+ * is really a refinement of (1)). (danner@cs.cmu.edu 6/14/91)
+ *
+ * (4) When issuing a multibyte command after a select with attention,
+ * The chip will automatically drop ATN before sending the last byte of the
+ * message, in accordance with the ANSI SCSI standard. This requires, of course,
+ * the transfer counter be an accurate representation of the amount of data to be
+ * transfered. (danner@cs.cmu.edu 6/14/91)
+ *
+ */
+
+#if 0
+
+#include <platforms.h>
+
+#include <scsi.h>
+
+#if NSCSI > 0
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+#include <sys/syslog.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+#include <scsi/adapters/scsi_89352.h>
+
+#include <machine/db_machdep.h> /*4proto*/
+#include <ddb/db_sym.h> /*4proto*/
+
+#ifdef LUNA88K
+#include <luna88k/board.h>
+#define SPC_DEFAULT_ADDRESS (caddr_t) SCSI_ADDR
+#endif
+
+#ifndef SPC_DEFAULT_ADDRESS /* cross compile check */
+#define SPC_DEFAULT_ADDRESS (caddr_t) 0
+#endif
+
+
+/* external/forward declarations */
+int spc_probe(), spc_slave(), spc_attach(), scsi_go();
+void spc_reset(), spc_attempt_selection(), spc_target_intr(), spc_bus_reset();
+/*
+ * Statically allocated command & temp buffers
+ * This way we can attach/detach drives on-fly
+ */
+#define PER_TGT_BUFF_DATA 256
+
+static char spc_buffer[NSCSI * 8 * PER_TGT_BUFF_DATA];
+
+/*
+ * Macros to make certain things a little more readable
+ */
+
+/*
+ wait for the desired phase to appear, but make sure the REQ bit set in the psns
+ (otherwise the values tend to float/be garbage.
+*/
+
+#define SPC_WAIT_PHASE(p) while(((regs->spc_psns & (SPC_BUS_REQ|SCSI_PHASE_MASK))) \
+ != (SPC_BUS_REQ|(p)))
+
+/*
+ wait until a phase different than p appears in the psns. Since it is only valid
+ when the REQ bit is set, don't test unless REQ bit is set. So spin until
+ REQ is high or the phase is not p.
+*/
+
+#define SPC_WAIT_PHASE_VANISH(p) while(1) { int _psns_ = regs->spc_psns; \
+ if ((_psns_ & SPC_BUS_REQ) && (_psns_ & SCSI_PHASE_MASK)!=p) break; }
+
+
+
+/* ?? */
+/* #define SPC_ACK(ptr,phase) (ptr)->spc_pctl = (phase) */
+
+/*
+ * A script has a two parts: a pre-condition and an action.
+ * The first triggers error handling if not satisfied and in
+ * our case it is formed by the current bus phase and connected
+ * condition as per bus status bits. The action part is just a
+ * function pointer, invoked in a standard way. The script
+ * pointer is advanced only if the action routine returns TRUE.
+ * See spc_intr() for how and where this is all done.
+ */
+
+typedef struct script {
+ char condition; /* expected state at interrupt */
+ int (*action)(); /* action routine */
+} *script_t;
+
+#define SCRIPT_MATCH(psns) (SPC_CUR_PHASE((psns))|((psns) & SPC_BUS_BSY))
+
+/* ?? */
+#define SPC_PHASE_DISC 0x0 /* sort of .. */
+
+/* The active script is in the state expected right after the issue of a select */
+
+#define SCRIPT_SELECT(scp) (scp->action == spc_issue_command || \
+ scp->action == spc_issue_ident_and_command)
+
+/* forward decls of script actions */
+boolean_t
+ spc_dosynch(), /* negotiate synch xfer */
+ spc_xfer_in(), /* get data from target via dma */
+ spc_xfer_out(), /* send data to target via dma */
+ spc_get_status(), /* get status from target */
+ spc_end_transaction(), /* all come to an end */
+ spc_msg_in(), /* get disconnect message(s) */
+ spc_issue_command(), /* spit on the bus */
+ spc_issue_ident_and_command(), /* spit on the bus (with ATN) */
+ spc_disconnected(); /* current target disconnected */
+/* forward decls of error handlers */
+boolean_t
+ spc_err_generic(), /* generic error handler */
+ spc_err_disconn(); /* when a target disconnects */
+void gimmeabreak(); /* drop into the debugger */
+
+void spc_reset_scsibus();
+boolean_t spc_probe_target();
+
+scsi_ret_t spc_select_target();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) 89352 chip
+ */
+struct spc_softc {
+ watchdog_t wd;
+ spc_regmap_t *regs; /* 5380 registers */
+ char *buff; /* scratch buffer memory */
+ char *data_ptr; /* orig/dest memory */
+ script_t script;
+ int (*error_handler)();
+ int in_count; /* amnt we expect to receive */
+ int out_count; /* amnt we are going to ship */
+
+ volatile char state;
+#define SPC_STATE_BUSY 0x01 /* selecting or currently connected */
+#define SPC_STATE_TARGET 0x04 /* currently selected as target */
+#define SPC_STATE_COLLISION 0x08 /* lost selection attempt */
+#define SPC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
+
+ unsigned char ntargets; /* how many alive on this scsibus */
+ unsigned char done;
+ unsigned char xxxx;
+
+ scsi_softc_t *sc;
+ target_info_t *active_target;
+
+ target_info_t *next_target; /* trying to seize bus */
+ queue_head_t waiting_targets;/* other targets competing for bus */
+ decl_simple_lock_data(,chiplock) /* Interlock */
+} spc_softc_data[NSCSI];
+
+typedef struct spc_softc *spc_softc_t;
+
+spc_softc_t spc_softc[NSCSI];
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+int spc_probe(), scsi_slave(), spc_go();
+void spc_intr();
+void scsi_attach();
+
+vm_offset_t spc_std[NSCSI] = { SPC_DEFAULT_ADDRESS };
+
+struct bus_device *spc_dinfo[NSCSI*8];
+struct bus_ctlr *spc_minfo[NSCSI];
+struct bus_driver spc_driver =
+ { spc_probe, scsi_slave, scsi_attach, spc_go, spc_std, "rz", spc_dinfo,
+ "spc", spc_minfo, BUS_INTR_B4_PROBE};
+
+/*
+ * Scripts
+ */
+
+struct script
+spc_script_data_in[] = {
+ { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
+ { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+spc_script_late_data_in[] = {
+ { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
+ { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+spc_script_data_out[] = {
+ { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
+ { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+
+spc_script_late_data_out[] = {
+ { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
+ { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+
+spc_script_cmd[] = {
+ { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+spc_script_late_cmd[] = {
+ { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
+ { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
+ { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
+},
+
+/* Synchronous transfer neg(oti)ation */
+
+spc_script_try_synch[] = {
+ { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_dosynch}
+},
+
+/* Disconnect sequence */
+
+spc_script_disconnect[] = {
+ { SPC_PHASE_DISC, spc_disconnected}
+};
+
+
+
+#define u_min(a,b) (((a) < (b)) ? (a) : (b))
+
+
+#define DEBUG
+#ifdef DEBUG
+
+int spc_state(base)
+ vm_offset_t base;
+{
+ register spc_regmap_t *regs;
+
+ if (base == 0)
+ base = (vm_offset_t) SPC_DEFAULT_ADDRESS;
+
+ regs = (spc_regmap_t*) (base);
+
+ db_printf("spc_bdid (bus device #): %x\n",regs->spc_bdid);
+ db_printf("spc_sctl (spc internal control): %x\n",regs->spc_sctl);
+ db_printf("spc_scmd (scp command): %x\n",regs->spc_scmd);
+ db_printf("spc_ints (spc interrupt): %x\n",regs->spc_ints);
+ db_printf("spc_psns (scsi bus phase): %x\n",regs->spc_psns);
+ db_printf("spc_ssts (spc internal status): %x\n",regs->spc_ssts);
+ db_printf("spc_serr (spc internal err stat): %x\n",regs->spc_serr);
+ db_printf("spc_pctl (scsi transfer phase): %x\n",regs->spc_pctl);
+ db_printf("spc_mbc (spc transfer data ct): %x\n",regs->spc_mbc);
+/* db_printf("spc_dreg (spc transfer data r/w): %x\n",regs->spc_dreg);*/
+ db_printf("spc_temp (scsi data bus control): %x\n",regs->spc_temp);
+ db_printf("spc_tch (transfer byte ct (MSB): %x\n",regs->spc_tch);
+ db_printf("spc_tcm (transfer byte ct (2nd): %x\n",regs->spc_tcm);
+ db_printf("spc_tcl (transfer byte ct (LSB): %x\n",regs->spc_tcl);
+
+ return 0;
+}
+
+int spc_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = spc_softc[0]->active_target;
+ if (tgt == 0)
+ return 0;
+ db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n",
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+ if (tgt->flags & TGT_DISCONNECTED){
+ script_t spt;
+
+ spt = tgt->transient_state.script;
+ db_printf("disconnected at ");
+ db_printsym((db_expr_t)spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym((db_expr_t)spt->action,1);
+ db_printf(", ");
+ db_printsym((db_expr_t)tgt->transient_state.handler, 1);
+ db_printf("\n");
+ }
+
+ return 0;
+}
+
+void spc_all_targets(unit)
+int unit;
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = spc_softc[unit]->sc->target[i];
+ if (tgt)
+ spc_target_state(tgt);
+ }
+}
+
+int spc_script_state(unit)
+int unit;
+{
+ script_t spt = spc_softc[unit]->script;
+
+ if (spt == 0) return 0;
+ db_printsym((db_expr_t)spt,1);
+ db_printf(": %x ", spt->condition);
+ db_printsym((db_expr_t)spt->action,1);
+ db_printf(", ");
+ db_printsym((db_expr_t)spc_softc[unit]->error_handler, 1);
+ return 0;
+}
+
+#define PRINT(x) if (scsi_debug) printf x
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+int spc_logpt;
+int spc_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x30
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static void LOG(e,f)
+ int e;
+ char *f;
+{
+ spc_log[spc_logpt++] = (e);
+ if (spc_logpt == LOGSIZE) spc_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+int spc_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned int c;
+
+ for (i = 0, j = spc_logpt; i < LOGSIZE; i++) {
+ c = spc_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-0x%x", c - 0x80);
+ }
+ db_printf("\n");
+ return 0;
+}
+
+void spc_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /* TRACE */
+#define LOG(e,f)
+#endif /* TRACE */
+
+#else /* DEBUG */
+#define PRINT(x)
+#define LOG(e,f)
+#define TR(x)
+#define TRCHECK
+#define TRWRAP
+#endif /* DEBUG */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Send an identify msg to each possible target on the bus
+ * except of course ourselves.
+ */
+int spc_probe(reg, ui)
+ char *reg;
+ struct bus_ctlr *ui;
+{
+ int tmp;
+ int unit = ui->unit;
+ spc_softc_t spc = &spc_softc_data[unit];
+ int target_id, i;
+ scsi_softc_t *sc;
+ register spc_regmap_t *regs;
+ int s;
+ boolean_t did_banner = FALSE;
+ char *cmd_ptr;
+
+ /*
+ * We are only called if the chip is there,
+ * but make sure anyways..
+ */
+ regs = (spc_regmap_t *) (reg);
+ if (check_memory((unsigned)regs, 0))
+ return 0;
+
+#if notyet
+ /* Mappable version side */
+ SPC_probe(reg, ui);
+#endif
+
+ /*
+ * Initialize hw descriptor
+ */
+ spc_softc[unit] = spc;
+ spc->regs = regs;
+ spc->buff = spc_buffer;
+
+ queue_init(&spc->waiting_targets);
+
+ simple_lock_init(&spc->chiplock);
+
+ sc = scsi_master_alloc(unit, (char*)spc);
+ spc->sc = sc;
+
+ sc->go = spc_go;
+ sc->probe = spc_probe_target;
+ sc->watchdog = scsi_watchdog;
+ spc->wd.reset = spc_reset_scsibus;
+
+#ifdef MACH_KERNEL
+ sc->max_dma_data = -1; /* unlimited */
+#else
+ sc->max_dma_data = scsi_per_target_virtual;
+#endif
+
+ scsi_might_disconnect[unit] = 0; /* XXX for now */
+
+ /*
+ * Reset chip
+ */
+ s = splbio();
+ spc_reset(regs, TRUE);
+ tmp = regs->spc_ints = regs->spc_ints;
+
+ /*
+ * Our SCSI id on the bus.
+ */
+
+ sc->initiator_id = bdid_to_id(regs->spc_bdid);
+ printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ */
+ cmd_ptr = spc_buffer;
+ for (target_id = 0; target_id < 8; target_id++, cmd_ptr += PER_TGT_BUFF_DATA) {
+
+ register unsigned csr, ints;
+ scsi_status_byte_t status;
+
+ /* except of course ourselves */
+ if (target_id == sc->initiator_id)
+ continue;
+
+ if (spc_select_target( regs, sc->initiator_id, target_id, FALSE)
+ == SCSI_RET_DEVICE_DOWN) {
+ tmp = regs->spc_ints = regs->spc_ints;
+ continue;
+ }
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ /* should be command phase here: we selected wo ATN! */
+ SPC_WAIT_PHASE(SCSI_PHASE_CMD);
+
+ SPC_ACK(regs,SCSI_PHASE_CMD);
+
+ /* build command in buffer */
+ {
+ unsigned char *p = (unsigned char*) cmd_ptr;
+
+ p[0] = SCSI_CMD_TEST_UNIT_READY;
+ p[1] =
+ p[2] =
+ p[3] =
+ p[4] =
+ p[5] = 0;
+ }
+
+ spc_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr);
+
+ SPC_WAIT_PHASE(SCSI_PHASE_STATUS);
+
+ /* should have recieved a Command Complete Interrupt */
+ while (!(regs->spc_ints))
+ delay(1);
+ ints = regs->spc_ints;
+ if (ints != (SPC_INTS_DONE))
+ gimmeabreak();
+ regs->spc_ints = ints;
+
+ SPC_ACK(regs,SCSI_PHASE_STATUS);
+
+ csr = spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
+ LOG(0x25,"din_count");
+ LOG(0x80+csr,0);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD)
+ scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
+
+ /* expect command complete interupt */
+ while (!(regs->spc_ints & SPC_INTS_DONE))
+ delay(1);
+
+ /* clear all intr bits */
+ tmp = regs->spc_ints;
+ LOG(0x26,"ints");
+ LOG(0x80+tmp,0);
+ regs->spc_ints = SPC_INTS_DONE;
+
+ /* get cmd_complete message */
+ SPC_WAIT_PHASE(SCSI_PHASE_MSG_IN);
+
+ SPC_ACK(regs,SCSI_PHASE_MSG_IN);
+
+ csr = spc_data_in(regs,SCSI_PHASE_MSG_IN, 1,(unsigned char*)&i);
+ LOG(0x25,"din_count");
+ LOG(0x80+csr,0);
+
+ while (!(regs->spc_ints & SPC_INTS_DONE))
+ delay(1);
+
+ /* clear all done intr */
+ tmp = regs->spc_ints;
+ LOG(0x26,"ints");
+ LOG(0x80+tmp,0);
+ regs->spc_ints = SPC_INTS_DONE;
+
+ SPC_ACK(regs,SPC_PHASE_DISC);
+
+ /* release the bus */
+ regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC;
+ /* regs->spc_scmd = 0; only in TARGET mode */
+
+ /* wait for disconnected interrupt */
+ while (!(regs->spc_ints & SPC_INTS_DISC))
+ delay(1);
+
+ tmp = regs->spc_ints;
+ LOG(0x26,"ints");
+ LOG(0x80+tmp,0);
+ regs->spc_ints = tmp;
+ LOG(0x29,"Probed\n");
+
+ /*
+ * Found a target
+ */
+ spc->ntargets++;
+ {
+ register target_info_t *tgt;
+
+ tgt = scsi_slave_alloc(unit, target_id, (char*)spc);
+
+ /* "virtual" address for our use */
+ tgt->cmd_ptr = cmd_ptr;
+ /* "physical" address for dma engine (??) */
+ tgt->dma_ptr = 0;
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+ }
+ }
+ printf(".\n");
+
+ splx(s);
+ return 1;
+}
+
+boolean_t
+spc_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+
+ /* "virtual" address for our use */
+ tgt->cmd_ptr = &spc_buffer[PER_TGT_BUFF_DATA*tgt->target_id +
+ (tgt->masterno*8*PER_TGT_BUFF_DATA) ];
+ /* "physical" address for dma engine */
+ tgt->dma_ptr = 0;
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_init(&tgt->fdma, scsi_per_target_virtual);
+#endif /*MACH_KERNEL*/
+
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+int bdid_to_id(bdid)
+ register int bdid;
+{
+ register int i;
+ for (i = 0; i < 8; i++)
+ if (bdid == (1 << i)) break;
+ return i;
+}
+
+scsi_ret_t
+spc_select_target(regs, myid, id, with_atn)
+ register spc_regmap_t *regs;
+ unsigned myid, id;
+ boolean_t with_atn;
+{
+ scsi_ret_t ret = SCSI_RET_RETRY;
+ int mask;
+
+ if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+#ifdef MIPS
+ && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+ && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+#endif
+ )
+ return ret;
+
+ /* setup for for select:
+
+#if 0
+ (1) Toggle the Enable transfer bit (turning on the chips
+ SCSI bus drivers).
+#endif
+ (2) Enable arbitration, parity, reselect display, but
+ disable interrupt generation to the CPU (we are polling).
+ (3) Disable the bus free interrupt and set I/O direction
+ (4) If doing a select with attention, write the Set attention command.
+ Then delay 1 microsecond to avoid command races.
+
+ (5) Temp register gets 1<<target | 1<<initiator ids
+ (6) Timeout clocked into transfer registers
+ (7) Drive select (and optionally attention) onto the bus
+ (8) Wait 1/4 second for timeout.
+ */
+
+#if 0
+ regs->spc_psns = SPC_DIAG_ENBL_XFER; /* (1) */
+#endif
+
+ regs->spc_sctl = SPC_SCTL_ARB_EBL|
+ SPC_SCTL_PAR_EBL|
+ SPC_SCTL_RSEL_EBL; /* (2) */
+
+
+
+ mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl;
+ mask &= ~1; /* set I/O direction to be out */
+
+ regs->spc_pctl = mask; /* (3) */
+
+ if (with_atn)
+ {
+ regs->spc_scmd = SPC_SCMD_C_ATN_S; /* (4) */
+ delay(1);
+ }
+
+ regs->spc_temp = (1<<myid) | (1<<id); /* (5) */
+
+ SPC_TC_PUT(regs,0xfa004); /* (6) */
+
+ regs->spc_scmd = (SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X); /* (7) */
+
+ {
+ int count = 2500;
+
+ /* wait for an interrupt */
+ while ((regs->spc_ints)==0)
+ {
+ if (--count > 0)
+ delay(100);
+ else
+ {
+ goto nodev;
+ }
+ }
+
+ count = regs->spc_ints;
+ if (count & SPC_INTS_TIMEOUT)
+ {
+ /* sanity check. The ssts should have the busy bit set */
+ if (regs->spc_ssts & SPC_SSTS_BUSY)
+ goto nodev;
+ else
+ panic("spc_select_target: timeout");
+ }
+
+ /* otherwise, we should have received a
+ command complete interrupt */
+
+ if (count & ~SPC_INTS_DONE)
+ panic("spc_select_target");
+
+ } /* (8) */
+
+ /* we got a response - now connected; bus is in COMMAND phase */
+
+ regs->spc_ints = regs->spc_ints;
+ /* regs->spc_scmd = 0; target only */
+ return SCSI_RET_SUCCESS;
+nodev:
+ SPC_TC_PUT(regs,0); /* play it safe */
+ regs->spc_ints = regs->spc_ints;
+ /* regs->spc_scmd = 0; target only */
+ ret = SCSI_RET_DEVICE_DOWN;
+ return ret;
+}
+
+int spc_data_out(regs, phase, count, data)
+ int phase, count;
+ register spc_regmap_t *regs;
+ unsigned char *data;
+{
+ /* This is the one that sends data out. returns how many
+ bytes it did NOT xfer: */
+
+ if (SPC_CUR_PHASE(regs->spc_phase) != phase)
+ return count;
+
+ /* check that the fifo is empty. If not, cry */
+ if (!(regs->spc_ssts & SPC_SSTS_FIFO_EMPTY))
+ panic("spc_data_out: junk in fifo\n");
+
+ SPC_TC_PUT(regs,count);
+ regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X;
+
+ /* wait for the SPC to start processing the command */
+ while ((regs->spc_ssts & (SPC_SSTS_INI_CON|SPC_SSTS_TGT_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP))
+ != (SPC_SSTS_INI_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP))
+ delay(1);
+
+ /* shovel out the data */
+
+ while (count)
+ {
+ /* check if interrupt is pending */
+ int ints = regs->spc_ints;
+ int ssts;
+
+ if (ints) /* something has gone wrong */
+ break;
+
+ ssts = regs->spc_ssts;
+ if (ssts & SPC_SSTS_FIFO_FULL) /* full fifo - can't write */
+ delay(1);
+ else
+ { /* spit out a byte */
+ regs->spc_dreg = *data;
+ data++;
+ count--;
+ }
+ }
+
+
+ if (count != 0)
+ {
+ /* need some sort of fifo cleanup if failed */
+ gimmeabreak(); /* Bytes stranded in the fifo */
+ }
+
+ return count;
+}
+
+int spc_data_in(regs, phase, count, data)
+ int phase, count;
+ register spc_regmap_t *regs;
+ unsigned char *data;
+{
+ if (SPC_CUR_PHASE(regs->spc_phase) != phase)
+ return count;
+
+ SPC_TC_PUT(regs,count);
+ regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X;
+
+ /* The Fujistu code sample suggests waiting for the top nibble of the SSTS to
+ become 0xb (ssts & 0xf0) = 0xb. This state, however is transient. If the
+ message is short (say , 1 byte), it can get sucked into the fifo before
+ we ever get to look at the state. So instead, we are going to wait for
+ the fifo to become nonempty.
+ */
+
+ while ((regs->spc_ssts & SPC_SSTS_FIFO_EMPTY))
+ delay(1);
+
+ while (count)
+ {
+ int ints = regs->spc_ints;
+ int ssts;
+
+ /* If there is an interrupt pending besides command complete or
+ phase mismatch, give up */
+
+ if (ints & ~(SPC_INTS_DONE|SPC_INTS_BUSREQ))
+ break;
+
+ /* see if there is any data in the fifo */
+ ssts = regs->spc_ssts;
+ if ((ssts & SPC_SSTS_FIFO_EMPTY) == 0)
+ {
+ *data = regs->spc_dreg;
+ data++;
+ count--;
+ continue;
+ }
+
+ /* if empty, check if phase has changed */
+ if (SPC_CUR_PHASE(regs->spc_phase) != phase)
+ break;
+
+ }
+
+ if ((count==0) && (phase == SCSI_PHASE_MSG_IN))
+ {
+ while (!(regs->spc_ints & SPC_INTS_DONE))
+ delay(1);
+
+ /*
+ So the command complete interrupt has arrived. Now check that the
+ other two conditions we expect - The psns to be in ack|busy|message_in phase
+ and ssts to indicate connected|xfer in progress|busy|xfer counter 0|empty fifo
+ are true.
+ */
+ while (1)
+ {
+ register int psns = regs->spc_psns;
+ register int ssts = regs->spc_ssts;
+ register int sscon = ssts & (SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON);
+ register int ssncon = ssts & ~(SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON);
+
+ if (psns == (SPC_BUS_ACK | SPC_BUS_BSY | SCSI_PHASE_MSG_IN) &&
+ ssncon == (SPC_SSTS_BUSY | SPC_SSTS_XIP | SPC_SSTS_TC0 | SPC_SSTS_FIFO_EMPTY) &&
+ sscon)
+ break;
+ }
+
+ regs->spc_scmd = SPC_SCMD_C_ACKREQ_C;
+ }
+
+ return count;
+}
+
+void spc_reset(regs, quickly)
+ register spc_regmap_t *regs;
+ boolean_t quickly;
+{
+ register char myid;
+
+ /* save our id across reset */
+ myid = bdid_to_id(regs->spc_bdid);
+
+ /* wait for Reset In signal to go low */
+ while (regs->spc_ssts & SPC_SSTS_RST)
+ delay(1);
+
+ /* reset chip */
+ regs->spc_sctl = SPC_SCTL_RESET;
+ delay(25);
+
+ regs->spc_myid = myid;
+ regs->spc_sctl = SPC_SCTL_ARB_EBL|SPC_SCTL_PAR_EBL|SPC_SCTL_SEL_EBL|
+ SPC_SCTL_RSEL_EBL|SPC_SCTL_IE;
+ regs->spc_scmd = SPC_SCMD_C_BUS_RLSE;
+ /* regs->spc_tmod = 0; - SANDRO ? */
+ regs->spc_ints = 0xff;/* clear off any pending */
+#if 0
+ regs->spc_pctl = SPC_PCTL_LST_IE; /* useful only on 87033 */
+#else
+ regs->spc_pctl = 0;
+#endif
+ regs->spc_mbc = 0;
+ SPC_TC_PUT(regs,0);
+
+ if (quickly)
+ return;
+
+ /*
+ * reset the scsi bus, the interrupt routine does the rest
+ * or you can call spc_bus_reset().
+ */
+ regs->spc_scmd = SPC_SCMD_BUSRST|SPC_SCMD_C_STOP_X;/*?*/
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+spc_go(tgt, cmd_count, in_count, cmd_only)
+ int cmd_count, in_count;
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ spc_softc_t spc;
+ register int s;
+ boolean_t disconn;
+ script_t scp;
+ boolean_t (*handler)();
+ int late;
+
+ LOG(1,"\n\tgo");
+
+ spc = (spc_softc_t)tgt->hw_state;
+
+ /*
+ * We cannot do real DMA.
+ */
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ if (tgt->ior)
+ fdma_map(&tgt->fdma, tgt->ior);
+#endif /*MACH_KERNEL*/
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+ tgt->transient_state.copy_count = 0;
+
+ if (len < tgt->block_size) {
+ gimmeabreak();
+
+ /* avoid leaks */
+#if 0
+you`ll have to special case this
+#endif
+ tgt->transient_state.out_count = tgt->block_size;
+ }
+ } else {
+ tgt->transient_state.out_count = 0;
+ tgt->transient_state.copy_count = 0;
+ }
+
+ tgt->transient_state.cmd_count = cmd_count;
+
+ disconn =
+ BGET(scsi_might_disconnect,(unsigned)tgt->masterno, tgt->target_id);
+ disconn = disconn && (spc->ntargets > 1);
+ disconn |=
+ BGET(scsi_should_disconnect,(unsigned)tgt->masterno, tgt->target_id);
+
+ /*
+ * Setup target state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ handler = (disconn) ? spc_err_disconn : spc_err_generic;
+
+ /* determine wether or not to use the late forms of the scripts */
+ late = cmd_only ? FALSE : (tgt->flags & TGT_DID_SYNCH);
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(0x13,"readop");
+ scp = late ? spc_script_late_data_in : spc_script_data_in;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x14,"writeop");
+ scp = late ? spc_script_late_data_out : spc_script_data_out;
+ break;
+ case SCSI_CMD_INQUIRY:
+ /* This is likely the first thing out:
+ do the synch neg if so */
+ if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
+ scp = spc_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ break;
+ }
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */
+ case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ scp = late ? spc_script_late_data_in : spc_script_data_in;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.out_count =
+ cmd_count - tgt->transient_state.cmd_count;
+ scp = late ? spc_script_late_data_out : spc_script_data_out;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ case SCSI_CMD_TEST_UNIT_READY:
+ /*
+ * Do the synch negotiation here, unless prohibited
+ * or done already
+ */
+ if (tgt->flags & TGT_DID_SYNCH) {
+ scp = late ? spc_script_late_cmd : spc_script_cmd;
+ } else {
+ scp = spc_script_try_synch;
+ tgt->flags |= TGT_TRY_SYNCH;
+ cmd_only = FALSE;
+ }
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ scp = late ? spc_script_late_cmd : spc_script_cmd;
+ }
+
+ tgt->transient_state.script = scp;
+ tgt->transient_state.handler = handler;
+ tgt->transient_state.identify = (cmd_only) ? 0xff :
+ (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
+ SCSI_IDENTIFY);
+
+ if (in_count)
+ tgt->transient_state.in_count =
+ (in_count < tgt->block_size) ? tgt->block_size : in_count;
+ else
+ tgt->transient_state.in_count = 0;
+ tgt->transient_state.dma_offset = 0;
+
+ /*
+ * See if another target is currently selected on
+ * this SCSI bus, e.g. lock the spc structure.
+ * Note that it is the strategy routine's job
+ * to serialize ops on the same target as appropriate.
+ */
+#if 0
+locking code here
+#endif
+ s = splbio();
+
+ if (spc->wd.nactive++ == 0)
+ spc->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (spc->state & SPC_STATE_BUSY) {
+ /*
+ * Queue up this target, note that this takes care
+ * of proper FIFO scheduling of the scsi-bus.
+ */
+ LOG(3,"enqueue");
+ enqueue_tail(&spc->waiting_targets, (queue_entry_t) tgt);
+ } else {
+ /*
+ * It is down to at most two contenders now,
+ * we will treat reconnections same as selections
+ * and let the scsi-bus arbitration process decide.
+ */
+ spc->state |= SPC_STATE_BUSY;
+ spc->next_target = tgt;
+ spc_attempt_selection(spc);
+ /*
+ * Note that we might still lose arbitration..
+ */
+ }
+ splx(s);
+}
+
+void spc_attempt_selection(spc)
+ spc_softc_t spc;
+{
+ target_info_t *tgt;
+ spc_regmap_t *regs;
+ register int cmd;
+ int atn=0;
+
+ /* This is about your select code */
+
+ regs = spc->regs;
+ tgt = spc->next_target;
+
+ LOG(4,"select");
+ LOG(0x80+tgt->target_id,0);
+
+ /*
+ * Init bus state variables and set registers.
+ */
+ spc->active_target = tgt;
+
+ /* reselection pending ? */
+ if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+#ifdef MIPS
+ && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+ && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
+#endif
+ )
+ return;
+
+ spc->script = tgt->transient_state.script;
+ spc->error_handler = tgt->transient_state.handler;
+ spc->done = SCSI_RET_IN_PROGRESS;
+
+ spc->in_count = 0;
+ spc->out_count = 0;
+
+ cmd = SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X;
+ if (tgt->flags & TGT_DID_SYNCH)
+ {
+ if (tgt->transient_state.identify != 0xff)
+ atn = 1;
+ }
+ else
+ if (tgt->flags & TGT_TRY_SYNCH)
+ atn = 1;
+
+#if 0
+ regs->spc_psns = SPC_DIAG_ENBL_XFER;
+#endif
+
+ regs->spc_sctl = SPC_SCTL_ARB_EBL | SPC_SCTL_PAR_EBL |
+ SPC_SCTL_RSEL_EBL | SPC_SCTL_IE;
+
+
+ { int mask;
+ mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl;
+ regs->spc_pctl = mask;
+ }
+
+ regs->spc_temp = (1<<(spc->sc->initiator_id)) | (1<<(tgt->target_id));
+
+ SPC_TC_PUT(regs,0xfa004);
+
+ if (atn)
+ {
+ regs->spc_scmd = SPC_SCMD_C_ATN_S;
+ /* delay 1us to avoid races */
+ delay(1);
+ }
+
+ regs->spc_scmd = cmd;
+ return;
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+void spc_intr(unit)
+int unit;
+{
+ register spc_softc_t spc;
+ register script_t scp;
+ register unsigned ints, psns, ssts;
+ register spc_regmap_t *regs;
+ boolean_t try_match;
+#if notyet
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ {
+ SPC_intr(unit);
+ return;
+ }
+#endif
+
+ spc = spc_softc[unit];
+ regs = spc->regs;
+
+ /* read the interrupt status register */
+ ints = regs->spc_ints;
+
+ LOG(5,"\n\tintr");
+ LOG(0x80+ints,0);
+
+TR(ints);
+TRCHECK;
+
+ if (ints & SPC_INTS_RESET)
+ {
+ /* does its own interrupt reset when ready */
+ spc_bus_reset(spc);
+ return;
+ }
+
+ /* we got an interrupt allright */
+ if (spc->active_target)
+ spc->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+
+ if (ints == 0)
+ { /* no obvious cause */
+ LOG(2,"SPURIOUS");
+ gimmeabreak();
+ return;
+ }
+
+
+ /* reset the interrupt */
+ regs->spc_ints = ints;
+
+ /* go get the phase, and status. We can't trust the
+ phase until REQ is asserted in the psns. Only do
+ this is we received a command complete or service
+ required interrupt. Otherwise, just read them once
+ and trust. */
+
+
+
+ if (ints & (SPC_INTS_DONE|SPC_INTS_BUSREQ))
+ while(1)
+ {
+ psns = regs->spc_psns;
+ if (psns & SPC_BUS_REQ)
+ break;
+ delay(1); /* don't hog the bus */
+ }
+ else
+ psns = regs->spc_psns;
+
+ ssts = regs->spc_psns;
+
+TR(psns);
+TR(ssts);
+TRCHECK;
+
+ if ((spc->state & SPC_STATE_TARGET) ||
+ (ints & SPC_INTS_SELECTED))
+ spc_target_intr(spc /**, ints, psns, ssts **/);
+
+ scp = spc->script;
+
+ if ((scp == 0) || (ints & SPC_INTS_RESELECTED))
+ {
+ gimmeabreak();
+ spc_reconnect(spc, ints, psns, ssts);
+ return;
+ }
+
+ if (SCRIPT_MATCH(psns) != scp->condition) {
+ if (try_match = (*spc->error_handler)(spc, ints, psns, ssts)) {
+ psns = regs->spc_psns;
+ ssts = regs->spc_ssts;
+ }
+ } else
+ try_match = TRUE;
+
+
+ /* might have been side effected */
+ scp = spc->script;
+
+ if (try_match && (SCRIPT_MATCH(psns) == scp->condition)) {
+ /*
+ * Perform the appropriate operation,
+ * then proceed
+ */
+ if ((*scp->action)(spc, ints, psns, ssts)) {
+ /* might have been side effected */
+ scp = spc->script;
+ spc->script = scp + 1;
+ }
+ }
+}
+
+void spc_target_intr(spc)
+ register spc_softc_t spc;
+{
+ panic("SPC: TARGET MODE !!!\n");
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to
+ */
+boolean_t
+spc_issue_command(spc, ints, psns, ssts)
+ spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register spc_regmap_t *regs = spc->regs;
+
+ LOG(0x12, "cmd_issue");
+ /* we have just done a select;
+ Bus is in CMD phase;
+ need to phase match */
+ SPC_ACK(regs, SCSI_PHASE_CMD);
+
+ return spc_data_out(regs, SCSI_PHASE_CMD,
+ spc->active_target->transient_state.cmd_count,
+ spc->active_target->cmd_ptr) ? FALSE : TRUE;
+}
+
+boolean_t
+spc_issue_ident_and_command(spc, ints, psns, ssts)
+ spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register spc_regmap_t *regs = spc->regs;
+
+ LOG(0x22, "ident_and_cmd");
+ /* we have just done a select with atn Bus is in MSG_OUT phase;
+ need to phase match */
+ SPC_ACK(regs, SCSI_PHASE_MSG_OUT);
+
+ spc_data_out(regs, SCSI_PHASE_MSG_OUT, 1,
+ &spc->active_target->transient_state.identify);
+
+ /* wait to go to command phase */
+ SPC_WAIT_PHASE(SCSI_PHASE_CMD);
+
+ /* ack */
+ SPC_ACK(regs, SCSI_PHASE_CMD);
+
+ /* should be a command complete intr pending. Eat it */
+ if (regs->spc_ints != SPC_INTS_DONE)
+ gimmeabreak();
+ regs->spc_ints = SPC_INTS_DONE;
+
+ /* spit */
+ return spc_data_out(regs, SCSI_PHASE_CMD,
+ spc->active_target->transient_state.cmd_count,
+ spc->active_target->cmd_ptr) ? FALSE : TRUE;
+}
+
+
+boolean_t
+spc_end_transaction( spc, ints, psns, serr)
+ register spc_softc_t spc;
+ int ints, psns, serr;
+{
+ register spc_regmap_t *regs = spc->regs;
+ char cmc;
+ int tmp;
+
+ LOG(0x1f,"end_t");
+
+ SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/);
+
+ spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc);
+
+ if (cmc != SCSI_COMMAND_COMPLETE)
+ printf("{T%x}", cmc);
+
+ while (regs->spc_ints != (SPC_INTS_DONE|SPC_INTS_DISC));
+
+ SPC_ACK(regs,SPC_PHASE_DISC);
+
+ /* going to disconnect */
+ regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC;
+ /* regs->spc_scmd = 0; */
+
+ /* clear all intr bits? */
+ tmp = regs->spc_ints;
+ regs->spc_ints = tmp;
+
+
+ if (!spc_end(spc, ints, psns, serr))
+ (void) spc_reconnect(spc, ints, psns, serr);
+ return FALSE;
+}
+
+boolean_t
+spc_end( spc, ints, psns, serr)
+ register spc_softc_t spc;
+ int ints, psns, serr;
+{
+ register target_info_t *tgt;
+ register io_req_t ior;
+ register spc_regmap_t *regs = spc->regs;
+ int csr;
+
+ LOG(6,"end");
+
+ tgt = spc->active_target;
+
+ if ((tgt->done = spc->done) == SCSI_RET_IN_PROGRESS)
+ tgt->done = SCSI_RET_SUCCESS;
+
+ spc->script = 0;
+
+ if (spc->wd.nactive-- == 1)
+ spc->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+ /* check reconnection not pending */
+ csr = SPC_INTS_RESELECTED & regs->spc_ints;
+ if (!csr)
+ spc_release_bus(spc);
+ else
+ {
+ spc->active_target = 0;
+ /* spc->state &= ~SPC_STATE_BUSY; later */
+ }
+ if (ior = tgt->ior) {
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ fdma_unmap(&tgt->fdma, ior);
+#endif /*MACH_KERNEL*/
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ if (csr)
+ spc->state &= ~SPC_STATE_BUSY;
+ }
+
+ /* return not reselected */
+ return (csr & SPC_INTS_RESELECTED) ? 0 : 1;
+}
+
+boolean_t
+spc_release_bus(spc)
+ register spc_softc_t spc;
+{
+ boolean_t ret = FALSE;
+
+ LOG(9,"release");
+
+ spc->script = 0;
+
+ if (spc->state & SPC_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ spc->state &= ~SPC_STATE_COLLISION;
+ spc_attempt_selection(spc);
+
+ } else if (queue_empty(&spc->waiting_targets)) {
+
+ spc->state &= ~SPC_STATE_BUSY;
+ spc->active_target = 0;
+ ret = TRUE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ spc->next_target = (target_info_t *)
+ dequeue_head(&spc->waiting_targets);
+ spc_attempt_selection(spc);
+ }
+ return ret;
+}
+
+boolean_t
+spc_get_status( spc, ints, psns, serr)
+ register spc_softc_t spc;
+ int ints, psns, serr;
+{
+ register spc_regmap_t *regs = spc->regs;
+ scsi2_status_byte_t status;
+ register target_info_t *tgt;
+
+ LOG(0xD,"get_status");
+TRWRAP;
+
+ spc->state &= ~SPC_STATE_DMA_IN;
+
+ tgt = spc->active_target;
+
+ SPC_ACK(regs,SCSI_PHASE_STATUS /*,1*/);
+
+ spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
+
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(spc->active_target, SCSI_ERR_STATUS, status.bits, 0);
+ spc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ spc->done = SCSI_RET_SUCCESS;
+
+ return TRUE;
+}
+
+boolean_t
+spc_xfer_in( spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register target_info_t *tgt;
+ register spc_regmap_t *regs = spc->regs;
+ register int count;
+ boolean_t advance_script = TRUE;
+
+ LOG(0xE,"xfer_in");
+
+ tgt = spc->active_target;
+ spc->state |= SPC_STATE_DMA_IN;
+
+ count = tgt->transient_state.in_count;
+
+ SPC_ACK(regs, SCSI_PHASE_DATAI);
+
+ if ((tgt->cur_cmd != SCSI_CMD_READ) &&
+ (tgt->cur_cmd != SCSI_CMD_LONG_READ))
+ spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->cmd_ptr);
+ else
+ {
+ spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->ior->io_data);
+ }
+
+ return advance_script;
+}
+
+boolean_t
+spc_xfer_out( spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register spc_regmap_t *regs = spc->regs;
+ register target_info_t *tgt;
+ boolean_t advance_script = TRUE;
+ int count = spc->out_count;
+
+ LOG(0xF,"xfer_out");
+
+ tgt = spc->active_target;
+ spc->state &= ~SPC_STATE_DMA_IN;
+
+ count = tgt->transient_state.out_count;
+
+ SPC_ACK(regs, SCSI_PHASE_DATAO);
+
+ if ((tgt->cur_cmd != SCSI_CMD_WRITE) &&
+ (tgt->cur_cmd != SCSI_CMD_LONG_WRITE))
+ spc_data_out(regs, SCSI_PHASE_DATAO, count,
+ tgt->cmd_ptr + tgt->transient_state.cmd_count);
+ else
+ spc_data_out(regs, SCSI_PHASE_DATAO, count, tgt->ior->io_data);
+
+ return advance_script;
+}
+
+/* disconnect-reconnect ops */
+
+/* get the message in via dma ?? */
+boolean_t
+spc_msg_in(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register target_info_t *tgt;
+
+ LOG(0x15,"msg_in");
+ gimmeabreak();
+
+ tgt = spc->active_target;
+
+#if 0
+You can do this by hand, just leave an interrupt pending at the end
+#endif
+
+ /* We only really expect two bytes */
+#if 0
+ SPC_PUT(dmar,sizeof(scsi_command_group_0));
+ ....
+#endif
+ return TRUE;
+}
+
+/* check the message is indeed a DISCONNECT */
+boolean_t
+spc_disconnect(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register int len = 0;
+ boolean_t ok = FALSE;
+ register char *msgs = 0;
+
+
+/* SPC_TC_GET(dmar,len); */
+ len = sizeof(scsi_command_group_0) - len;
+
+/* msgs = tgt->cmd_ptr; */ /* I think */
+
+ if ((len == 0) || (len > 2) || msgs == 0)
+ ok = FALSE;
+ else {
+ /* A SDP message preceeds it in non-completed READs */
+ ok = ((msgs[0] == SCSI_DISCONNECT) || /* completed op */
+ ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* incomplete */
+ (msgs[1] == SCSI_DISCONNECT)));
+ }
+ if (!ok)
+ printf("[tgt %d bad msg (%d): %x]",
+ spc->active_target->target_id, len, *msgs);
+
+ return TRUE;
+}
+
+/* save all relevant data, free the BUS */
+boolean_t
+spc_disconnected(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register target_info_t *tgt;
+
+/* make sure reselects will work */
+
+ LOG(0x16,"disconnected");
+
+ spc_disconnect(spc,ints, psns, ssts);
+
+ tgt = spc->active_target;
+ tgt->flags |= TGT_DISCONNECTED;
+ tgt->transient_state.handler = spc->error_handler;
+ /* the rest has been saved in spc_err_disconn() */
+
+ PRINT(("{D%d}", tgt->target_id));
+
+ spc_release_bus(spc);
+
+ return FALSE;
+}
+
+/* get reconnect message, restore BUS */
+boolean_t
+spc_reconnect(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+
+ LOG(0x17,"reconnect");
+
+ if (spc->wd.nactive == 0) {
+ LOG(2,"SPURIOUS");
+ return FALSE;
+ }
+
+#if 0
+This is the 5380 code, for reference:
+ spc_regmap_t *regs = spc->regs;
+ register target_info_t *tgt;
+ register int id;
+ int msg;
+
+
+ id = regs->spc_data;/*parity?*/
+ /* xxx check our id is in there */
+
+ id &= ~(1 << spc->sc->initiator_id);
+ {
+ register int i;
+ for (i = 0; i < 8; i++)
+ if (id & (1 << i)) break;
+if (i == 8) {printf("{P%x}", id);return;}
+ id = i;
+ }
+ regs->spc_icmd = SPC_ICMD_BSY;
+ while (regs->spc_bus_csr & SPC_BUS_SEL)
+ ;
+ regs->spc_icmd = 0;
+ delay_1p2_us();
+ while ( ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) &&
+ ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) &&
+ ((regs->spc_bus_csr & SPC_BUS_BSY) == 0))
+ ;
+
+ /* Now should wait for correct phase: REQ signals it */
+ while ( ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) &&
+ ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) &&
+ ((regs->spc_bus_csr & SPC_BUS_REQ) == 0))
+ ;
+
+ regs->spc_mode |= SPC_MODE_MONBSY;
+
+ /*
+ * See if this reconnection collided with a selection attempt
+ */
+ if (spc->state & SPC_STATE_BUSY)
+ spc->state |= SPC_STATE_COLLISION;
+
+ spc->state |= SPC_STATE_BUSY;
+
+ /* Get identify msg */
+ bs = regs->spc_phase;
+if (SPC_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak();
+ SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/);
+ msg = 0;
+ spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg);
+ regs->spc_mode = SPC_MODE_PAR_CHK|SPC_MODE_DMA|SPC_MODE_MONBSY;
+
+ if (msg != SCSI_IDENTIFY)
+ printf("{I%x %x}", id, msg);
+
+ tgt = spc->sc->target[id];
+ if (id > 7 || tgt == 0) panic("spc_reconnect");
+
+ PRINT(("{R%d}", id));
+ if (spc->state & SPC_STATE_COLLISION)
+ PRINT(("[B %d-%d]", spc->active_target->target_id, id));
+
+ LOG(0x80+id,0);
+
+ spc->active_target = tgt;
+ tgt->flags &= ~TGT_DISCONNECTED;
+
+ spc->script = tgt->transient_state.script;
+ spc->error_handler = tgt->transient_state.handler;
+ spc->in_count = 0;
+ spc->out_count = 0;
+
+ /* Should get a phase mismatch when tgt changes phase */
+#endif
+ return TRUE;
+}
+
+
+
+/* do the synch negotiation */
+boolean_t
+spc_dosynch( spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ /*
+ * Phase is MSG_OUT here, cmd has not been xferred
+ */
+ int len;
+ register target_info_t *tgt;
+ register spc_regmap_t *regs = spc->regs;
+ unsigned char off;
+ unsigned char p[6];
+
+ LOG(0x11,"dosync");
+
+ /* ATN still asserted */
+ SPC_ACK(regs,SCSI_PHASE_MSG_OUT);
+
+ tgt = spc->active_target;
+
+ tgt->flags |= TGT_DID_SYNCH; /* only one chance */
+ tgt->flags &= ~TGT_TRY_SYNCH;
+
+ /*p = some scratch buffer, on the stack */
+
+ p[0] = SCSI_IDENTIFY;
+ p[1] = SCSI_EXTENDED_MESSAGE;
+ p[2] = 3;
+ p[3] = SCSI_SYNC_XFER_REQUEST;
+ /* We cannot run synchronous */
+#define spc_to_scsi_period(x) 0x7
+#define scsi_period_to_spc(x) (x)
+ off = 0;
+ p[4] = spc_to_scsi_period(spc_min_period);
+ p[5] = off;
+
+ /* The transfer is started with ATN still set. The
+ chip will automagically drop ATN before it transfers the
+ last byte. Pretty neat. */
+ spc_data_out(regs, SCSI_PHASE_MSG_OUT,
+ sizeof(scsi_synch_xfer_req_t)+1, p);
+
+ /* wait for phase change to status phase */
+ SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_OUT);
+
+
+ psns = regs->spc_phase;
+
+ /* The standard sez there nothing else the target can do but.. */
+ if (SPC_CUR_PHASE(psns) != SCSI_PHASE_MSG_IN)
+ panic("spc_dosync");/* XXX put offline */
+
+ /*
+ msgin:
+ */
+ /* ack */
+ SPC_ACK(regs,SCSI_PHASE_MSG_IN);
+
+ /* clear any pending interrupts */
+ regs->spc_ints = regs->spc_ints;
+
+ /* get answer */
+ len = sizeof(scsi_synch_xfer_req_t);
+ len = spc_data_in(regs, SCSI_PHASE_MSG_IN, len, p);
+
+ /* do not cancel the phase mismatch interrupt ! */
+
+ /* look at the answer and see if we like it */
+ if (len || (p[0] != SCSI_EXTENDED_MESSAGE)) {
+ /* did not like it at all */
+ printf(" did not like SYNCH xfer ");
+ } else {
+ /* will NOT do synch */
+ printf(" but we cannot do SYNCH xfer ");
+ tgt->sync_period = scsi_period_to_spc(p[3]);
+ tgt->sync_offset = p[4];
+ /* sanity */
+ if (tgt->sync_offset != 0)
+ printf(" ?OFFSET %x? ", tgt->sync_offset);
+ }
+
+ /* wait for phase change */
+ SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_IN);
+
+ psns = regs->spc_phase;
+
+ /* phase should be command now */
+ /* continue with simple command script */
+ spc->error_handler = spc_err_generic;
+ spc->script = spc_script_cmd;
+
+/* Make sure you get out right here, esp the script pointer and/or pending intr */
+
+ if (SPC_CUR_PHASE(psns) == SCSI_PHASE_CMD )
+ return FALSE;
+
+ if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS ) /* jump to get_status */
+ return TRUE; /* intr is pending */
+
+ spc->script++;
+ if (SPC_CUR_PHASE(psns) == SCSI_PHASE_MSG_IN )
+ return TRUE;
+
+ if ((psns & SPC_BUS_BSY) == 0) /* uhu? disconnected */
+ return TRUE;
+
+ gimmeabreak();
+ return FALSE;
+}
+
+/*
+ * The bus was reset
+ */
+void spc_bus_reset(spc)
+ register spc_softc_t spc;
+{
+ register spc_regmap_t *regs = spc->regs;
+
+ LOG(0x21,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ spc->script = 0;
+ spc->error_handler = 0;
+ spc->active_target = 0;
+ spc->next_target = 0;
+ spc->state = 0;
+ queue_init(&spc->waiting_targets);
+ spc->wd.nactive = 0;
+ spc_reset(regs, TRUE);
+
+ printf("spc%d: (%d) bus reset ", spc->sc->masterno, ++spc->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (spc->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(spc->sc);
+}
+
+/*
+ * Error handlers
+ */
+
+/*
+ * Generic, default handler
+ */
+boolean_t
+spc_err_generic(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ register spc_regmap_t *regs = spc->regs;
+ LOG(0x10,"err_generic");
+
+ if (ints & SPC_INTS_TIMEOUT) /* we timed out */
+ if ((regs->spc_scmd & SPC_SCMD_CMDMASK) == SPC_SCMD_C_SELECT)
+ {
+ /* Powered off ? */
+ if (spc->active_target->flags & TGT_FULLY_PROBED)
+ {
+ spc->active_target->flags = 0;
+ LOG(0x1e,"Device Down");
+ }
+ spc->done = SCSI_RET_DEVICE_DOWN;
+ spc_end(spc, ints, psns, ssts);
+ return FALSE; /* don't retry - just report missing device */
+ }
+ else
+ { /* timed out - but not on a select. What is going on? */
+ gimmeabreak();
+ }
+
+ if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS)
+ return spc_err_to_status(spc, ints, psns, ssts);
+ gimmeabreak();
+ return FALSE;
+}
+
+/*
+ * Handle generic errors that are reported as
+ * an unexpected change to STATUS phase
+ */
+boolean_t
+spc_err_to_status(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+ script_t scp = spc->script;
+
+ LOG(0x20,"err_tostatus");
+ while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS)
+ scp++;
+ spc->script = scp;
+#if 0
+ /*
+ * Normally, we would already be able to say the command
+ * is in error, e.g. the tape had a filemark or something.
+ * But in case we do disconnected mode WRITEs, it is quite
+ * common that the following happens:
+ * dma_out -> disconnect -> reconnect
+ * and our script might expect at this point that the dma
+ * had to be restarted (it didn't know it was completed
+ * because the tape record is shorter than we asked for).
+ * And in any event.. it is both correct and cleaner to
+ * declare error iff the STATUS byte says so.
+ */
+ spc->done = SCSI_RET_NEED_SENSE;
+#endif
+ return TRUE;
+}
+
+/*
+ * Watch for a disconnection
+ */
+boolean_t
+spc_err_disconn(spc, ints, psns, ssts)
+ register spc_softc_t spc;
+ int ints, psns, ssts;
+{
+#if 1
+/*
+ * THIS ROUTINE CAN'T POSSIBLY WORK...
+ * FOR EXAMPLE, THE VARIABLE 'xferred' IS NEVER INITIALIZED.
+ */
+ return FALSE;
+#else
+ register spc_regmap_t *regs;
+ register target_info_t *tgt;
+ int xferred;
+
+ LOG(0x18,"err_disconn");
+
+ if (SPC_CUR_PHASE(ints) != SCSI_PHASE_MSG_IN)
+ return spc_err_generic(spc, ints, psns, ssts);
+
+ regs = spc->regs;
+
+ tgt = spc->active_target;
+
+ switch (SCSI_PHASE(spc->script->condition)) {
+ case SCSI_PHASE_DATAO:
+ LOG(0x1b,"+DATAO");
+/*updatecounters:*/
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ tgt->transient_state.dma_offset += xferred;
+
+ tgt->transient_state.script = spc_script_data_out;
+ break;
+
+ case SCSI_PHASE_DATAI:
+ LOG(0x19,"+DATAI");
+
+/*update counters: */
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+ assert(tgt->transient_state.in_count > 0);
+ tgt->transient_state.dma_offset += xferred;
+
+ tgt->transient_state.script = spc_script_data_in;
+ break;
+
+ case SCSI_PHASE_STATUS:
+
+ if (spc->state & SPC_STATE_DMA_IN) {
+
+ LOG(0x1a,"+STATUS+R");
+
+/*same as above.. */
+ assert(xferred > 0);
+ tgt->transient_state.in_count -= xferred;
+/* assert(tgt->transient_state.in_count > 0);*/
+ tgt->transient_state.dma_offset += xferred;
+
+ tgt->transient_state.script = spc_script_data_in;
+ if (tgt->transient_state.in_count == 0)
+ tgt->transient_state.script++;
+
+ } else {
+
+ LOG(0x1d,"+STATUS+W");
+
+ if ((tgt->transient_state.out_count == spc->out_count)) {
+ /* all done */
+ tgt->transient_state.script = &spc_script_data_out[1];
+ tgt->transient_state.out_count = 0;
+ } else {
+
+/*.. */
+ tgt->transient_state.out_count -= xferred;
+ assert(tgt->transient_state.out_count > 0);
+ tgt->transient_state.dma_offset += xferred;
+
+ tgt->transient_state.script = spc_script_data_out;
+ }
+ spc->out_count = 0;
+ }
+ break;
+ default:
+ gimmeabreak();
+ }
+ /* spc->xxx = 0; */
+
+/* SPC_ACK(regs,SCSI_PHASE_MSG_IN); later */
+ (void) spc_msg_in(spc, ints, psns, ssts);
+
+ spc->script = spc_script_disconnect;
+
+ return FALSE;
+#endif
+}
+
+/*
+ * Watchdog
+ *
+ */
+void spc_reset_scsibus(spc)
+ register spc_softc_t spc;
+{
+ register target_info_t *tgt = spc->active_target;
+ if (tgt) {
+ int cnt = 0;
+ /* SPC_TC_GET(spc->dmar,cnt); */
+ log( LOG_KERN,
+ "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count, tgt->transient_state.out_count,
+ spc->in_count, spc->out_count, cnt);
+ }
+#if 0
+ spc->regs->.....
+#endif
+ delay(25);
+}
+
+int SPC_ACK(regs, phase)
+register spc_regmap_t *regs;
+unsigned phase;
+{
+ /* we want to switch into the specified phase -
+
+ The calling routine should already dismissed
+ any pending interrupts (spc_ints)
+ */
+
+ regs->spc_psns = 0;
+ regs->spc_pctl = phase | SPC_PCTL_BFREE_IE;
+ return 0;
+}
+#endif /*NSCSI > 0*/
+
+#endif 0
diff --git a/scsi/adapters/scsi_aha15.h b/scsi/adapters/scsi_aha15.h
new file mode 100644
index 0000000..52cd936
--- /dev/null
+++ b/scsi/adapters/scsi_aha15.h
@@ -0,0 +1,347 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_aha15.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 6/91
+ *
+ * Definitions for the Adaptec AHA-15xx family
+ * of Intelligent SCSI Host Adapter boards
+ */
+
+#ifndef _SCSI_AHA15_H_
+#define _SCSI_AHA15_H_
+
+/*
+ * Addresses/length in 24 bits
+ *
+ * BEWARE: your compiler must pack these correctly,
+ * e.g. without gaps between two such contiguous structs
+ * (GCC does)
+ */
+typedef struct {
+ unsigned char msb;
+ unsigned char mid;
+ unsigned char lsb;
+} aha_address_t;
+
+#define AHA_ADDRESS_SET(addr,val) {\
+ (addr).msb = ((val) >> 16);\
+ (addr).mid = ((val) >> 8);\
+ (addr).lsb = (val) ;\
+ }
+#define AHA_ADDRESS_GET(addr,val) {\
+ (val) = ((addr).msb << 16) |\
+ ((addr).mid << 8) |\
+ ((addr).lsb ) ;\
+ }
+
+#define aha_length_t aha_address_t
+#define AHA_LENGTH_SET AHA_ADDRESS_SET
+#define AHA_LENGTH_GET AHA_ADDRESS_GET
+
+/*
+ * Register map
+ */
+
+typedef struct {
+ volatile unsigned char aha_status; /* r: Status Register */
+#define aha_ctl aha_status /* w: Control Register */
+
+ volatile unsigned char aha_data; /* rw: Data Port */
+#define aha_cmd aha_data /* w: Command register */
+
+ volatile unsigned char aha_intr; /* ro: Interrupt Register */
+} aha_regmap_t;
+
+/* ..but on the 386 I/O is not memory mapped */
+#define AHA_STATUS_PORT(port) ((port))
+#define AHA_CONTROL_PORT(port) ((port))
+#define AHA_COMMAND_PORT(port) ((port)+1)
+#define AHA_DATA_PORT(port) ((port)+1)
+#define AHA_INTR_PORT(port) ((port)+2)
+
+/* Status Register */
+#define AHA_CSR_CMD_ERR 0x01 /* Invalid command */
+#define AHA_CSR_xxx 0x02 /* undefined */
+#define AHA_CSR_DATAI_FULL 0x04 /* In-port full */
+#define AHA_CSR_DATAO_FULL 0x08 /* Out-port full */
+#define AHA_CSR_IDLE 0x10 /* doin nuthin */
+#define AHA_CSR_INIT_REQ 0x20 /* initialization required */
+#define AHA_CSR_DIAG_FAIL 0x40 /* selftest failed */
+#define AHA_CSR_SELF_TEST 0x80 /* selftesting */
+
+/* Control Register */
+#define AHA_CTL_xxx 0x0f /* undefined */
+#define AHA_CTL_SCSI_RST 0x10 /* reset SCSIbus */
+#define AHA_CTL_INTR_CLR 0x20 /* Clear interrupt reg */
+#define AHA_CTL_SOFT_RESET 0x40 /* Board only, no selftest */
+#define AHA_CTL_HARD_RESET 0x80 /* Full reset, and SCSIbus */
+
+/* Interrupt Flags register */
+#define AHA_INTR_MBI_FULL 0x01 /* scan the In mboxes */
+#define AHA_INTR_MBO_AVAIL 0x02 /* scan the Out mboxes */
+#define AHA_INTR_DONE 0x04 /* command complete */
+#define AHA_INTR_RST 0x08 /* saw a SCSIbus reset */
+#define AHA_INTR_xxx 0x70 /* undefined */
+#define AHA_INTR_PENDING 0x80 /* Any interrupt bit set */
+
+/*
+ * Command register
+ */
+#define AHA_CMD_NOP 0x00 /* */
+#define AHA_CMD_INIT 0x01 /* mbox initialization */
+ /* 4 bytes follow: # of Out mboxes (x2->total), and
+ msb, mid, lsb of mbox address */
+struct aha_init {
+ unsigned char mb_count;
+ aha_address_t mb_ptr;
+};
+#define AHA_CMD_START 0x02 /* start SCSI cmd */
+#define AHA_CMD_BIOS 0x03
+#define AHA_CMD_INQUIRY 0x04
+ /* returns 4 bytes: */
+struct aha_inq {
+ unsigned char board_id;
+# define AHA_BID_1540_B16 0x00
+# define AHA_BID_1540_B64 0x30
+# define AHA_BID_1540B 0x41
+# define AHA_BID_1640 0x42
+# define AHA_BID_1740 0x43
+# define AHA_BID_1542C 0x44
+# define AHA_BID_1542CF 0x45 /* BIOS v2.0x */
+
+ unsigned char options;
+# define AHA_BOPT_STD 0x41 /* in 154x, standard model */
+
+ unsigned char frl_1; /* rev level */
+ unsigned char frl_2;
+};
+#define AHA_CMD_MBO_IE 0x05
+ /* 1 byte follows: */
+# define AHA_MBO_DISABLE 0x00
+# define AHA_MBO_ENABLE 0x01
+
+#define AHA_CMD_SET_SELTO 0x06 /* select timeout */
+ /* 4 bytes follow: */
+struct aha_selto {
+ unsigned char enable;
+ char xxx;
+ unsigned char timeo_msb;
+ unsigned char timeo_lsb;
+};
+#define AHA_CMD_SET_BUSON 0x07
+ /* 1 byte value follows: 2..15 default 11 usecs */
+#define AHA_CMD_SET_BUSOFF 0x08
+ /* 1 byte value follows: 1..64 default 4 usecs */
+#define AHA_CMD_SET_XSPEED 0x09
+ /* 1 byte value follows: */
+# define AHA_DMASPEED_5Mb 0x00
+# define AHA_DMASPEED_7Mb 0x01
+# define AHA_DMASPEED_8Mb 0x02
+# define AHA_DMASPEED_10Mb 0x03
+# define AHA_DMASPEED_6Mb 0x04
+ /* values in the range 80..ff encoded as follows:
+ bit 7 on --> custom speed
+ bits 6..4 read pulse width
+ 0 100ns
+ 1 150
+ 2 200
+ 3 250
+ 4 300
+ 5 350
+ 6 400
+ 7 450
+ bit 3 strobe off time
+ 0 100ns
+ 1 150ns
+ bits 2..0 write pulse width
+ <same as read pulse>
+ */
+#define AHA_CMD_FIND_DEVICES 0x0a
+ /* returns 8 bytes, each one is a bitmask of the LUNs
+ available for the given target ID */
+struct aha_devs {
+ unsigned char tgt_luns[8];
+};
+#define AHA_CMD_GET_CONFIG 0x0b
+ /* returns 3 bytes: */
+struct aha_conf {
+ unsigned char dma_arbitration;/* bit N -> channel N */
+ unsigned char intr_ch;/* bit N -> intr 9+N (but 13,16)*/
+ unsigned char my_scsi_id; /* both of I and T role */
+};
+#define AHA_CMD_ENB_TGT_MODE 0x0c
+ /* 2 bytes follow: */
+struct aha_tgt {
+ unsigned char enable;
+ unsigned char luns; /* bitmask */
+};
+
+#define AHA_CMD_GET_SETUP 0x0d
+ /* 1 byte follows: allocation len (N) */
+ /* returns N bytes, 17 significant: */
+struct aha_setup {
+ BITFIELD_3( unsigned char,
+ initiate_SDT:1,
+ enable_parity:1,
+ res:6);
+ unsigned char xspeed; /* see above */
+ unsigned char buson;
+ unsigned char busoff;
+ unsigned char n_mboxes;/* 0 if not initialized */
+ aha_address_t mb_ptr; /* garbage if not inited */
+ struct {
+ BITFIELD_3( unsigned char,
+ offset: 4,
+ period: 3, /* 200 + 50 * N */
+ negotiated: 1);
+ } SDT_params[8];
+ unsigned char no_disconnect; /* bitmask */
+};
+
+#define AHA_CMD_WRITE_CH2 0x1a
+ /* 3 bytes (aha_address_t) follow for the buffer pointer */
+#define AHA_CMD_READ_CH2 0x1b
+ /* 3 bytes (aha_address_t) follow for the buffer pointer */
+#define AHA_CMD_WRITE_FIFO 0x1c
+ /* 3 bytes (aha_address_t) follow for the buffer pointer */
+#define AHA_CMD_READ_FIFO 0x1d
+ /* 3 bytes (aha_address_t) follow for the buffer pointer */
+#define AHA_CMD_ECHO 0x1f
+ /* 1 byte follows, which should then be read back */
+#define AHA_CMD_DIAG 0x20
+#define AHA_CMD_SET_OPT 0x21
+ /* 2+ bytes follow: */
+struct aha_diag {
+ unsigned char parmlen; /* bytes to follow */
+ unsigned char no_disconnect; /* bitmask */
+ /* rest is undefined */
+};
+
+#define AHA_EXT_BIOS 0x28 /* return extended bios info */
+#define AHA_MBX_ENABLE 0x29 /* enable mail box interface */
+struct aha_extbios {
+ unsigned char flags; /* Bit 3 == 1 extended bios enabled */
+ unsigned char mailboxlock; /* mail box lock code to unlock it */
+};
+
+/*
+ * Command Control Block
+ */
+typedef struct {
+ unsigned char ccb_code;
+# define AHA_CCB_I_CMD 0x00
+# define AHA_CCB_T_CMD 0x01
+# define AHA_CCB_I_CMD_SG 0x02
+# define AHA_CCB_ICMD_R 0x03
+# define AHA_CCB_ICMD_SG_R 0x04
+# define AHA_CCB_BDEV_RST 0x81
+ BITFIELD_4( unsigned char,
+ ccb_lun:3,
+ ccb_in:1,
+ ccb_out:1,
+ ccb_scsi_id:3);
+ unsigned char ccb_cmd_len;
+ unsigned char ccb_reqsns_len; /* if 1 no automatic reqsns*/
+ aha_length_t ccb_datalen;
+ aha_address_t ccb_dataptr;
+ aha_address_t ccb_linkptr;
+ unsigned char ccb_linkid;
+ unsigned char ccb_hstatus;
+# define AHA_HST_SUCCESS 0x00
+# define AHA_HST_SEL_TIMEO 0x11
+# define AHA_HST_DATA_OVRUN 0x12
+# define AHA_HST_BAD_DISCONN 0x13
+# define AHA_HST_BAD_PHASE_SEQ 0x14
+# define AHA_HST_BAD_OPCODE 0x16
+# define AHA_HST_BAD_LINK_LUN 0x17
+# define AHA_HST_INVALID_TDIR 0x18
+# define AHA_HST_DUPLICATED_CCB 0x19
+# define AHA_HST_BAD_PARAM 0x1a
+
+ scsi2_status_byte_t ccb_status;
+ unsigned char ccb_xxx;
+ unsigned char ccb_xxx1;
+ scsi_command_group_5 ccb_scsi_cmd; /* cast as needed */
+} aha_ccb_t;
+
+/* For scatter/gather use a list of (len,ptr) segments, each field
+ is 3 bytes (aha_address_t) long. Max 17 segments, min 1 */
+
+/*
+ * Ring descriptor, aka Mailbox
+ */
+typedef union {
+
+ struct {
+ volatile unsigned char mb_cmd; /* Out mbox */
+# define mb_status mb_cmd /* In mbox */
+
+ aha_address_t mb_ptr;
+#define AHA_MB_SET_PTR(mbx,val) AHA_ADDRESS_SET((mbx)->mb.mb_ptr,(val))
+#define AHA_MB_GET_PTR(mbx,val) AHA_ADDRESS_GET((mbx)->mb.mb_ptr,(val))
+
+ } mb;
+
+ struct { /* ccb required In mbox */
+ volatile unsigned char mb_cmd;
+ BITFIELD_4( unsigned char,
+ mb_lun : 3,
+ mb_isa_send : 1,
+ mb_isa_recv : 1,
+ mb_initiator_id : 3);
+ unsigned char mb_data_len_msb;
+ unsigned char mb_data_len_mid;
+ } mbt;
+
+ unsigned int bits; /* quick access */
+
+} aha_mbox_t;
+
+/* Out mbox, values for the mb_cmd field */
+#define AHA_MBO_FREE 0x00
+#define AHA_MBO_START 0x01
+#define AHA_MBO_ABORT 0x02
+
+/* In mbox, values for the mb_status field */
+#define AHA_MBI_FREE 0x00
+#define AHA_MBI_SUCCESS 0x01
+#define AHA_MBI_ABORTED 0x02
+#define AHA_MBI_NOT_FOUND 0x03
+#define AHA_MBI_ERROR 0x04
+#define AHA_MBI_NEED_CCB 0x10
+
+/*
+ * Scatter/gather segment lists
+ */
+typedef struct {
+ aha_length_t len;
+ aha_address_t ptr;
+} aha_seglist_t;
+
+#define AHA_MAX_SEGLIST 17 /* which means max 64Kb */
+#endif /*_SCSI_AHA15_H_*/
diff --git a/scsi/adapters/scsi_aha15_hdw.c b/scsi/adapters/scsi_aha15_hdw.c
new file mode 100644
index 0000000..5514bc5
--- /dev/null
+++ b/scsi/adapters/scsi_aha15_hdw.c
@@ -0,0 +1,1467 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_aha15_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 6/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the Adaptec
+ * AHA-15xx family of Intelligent SCSI Host Adapter boards:
+ * probing, start operation, and interrupt routine.
+ */
+
+/*
+ * Since the board is "Intelligent" we do not need scripts like
+ * other simpler HBAs. Maybe.
+ */
+#include <cpus.h>
+#include <platforms.h>
+
+#include <aha.h>
+#if NAHA > 0
+
+#include <mach/std_types.h>
+#include <machine/machspl.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+
+/* #include <sys/syslog.h> */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#include <scsi/adapters/scsi_aha15.h>
+
+#ifdef AT386
+#define MACHINE_PGBYTES I386_PGBYTES
+#define MAPPABLE 0
+#define gimmeabreak() asm("int3")
+#include <i386/pio.h> /* inlining of outb and inb */
+#endif /*AT386*/
+
+#ifdef CBUS /* For the Corollary machine, physical */
+#include <i386at/mp/mp.h>
+#include <cbus/cbus.h>
+
+#define aha_cbus_window transient_state.hba_dep[0]
+ /* must use windows for phys addresses */
+ /* greater than 16 megs */
+
+#define kvtoAT cbus_kvtoAT
+#else /* CBUS */
+#define kvtoAT kvtophys
+#endif /* CBUS */
+
+#ifndef MACHINE_PGBYTES /* cross compile check */
+#define MACHINE_PGBYTES 0x1000
+#define MAPPABLE 1
+#define gimmeabreak() Debugger("gimmeabreak");
+#endif
+
+/*
+ * Data structures: ring, ccbs, a per target buffer
+ */
+
+#define AHA_NMBOXES 2 /* no need for more, I think */
+struct aha_mb_ctl {
+ aha_mbox_t omb[AHA_NMBOXES];
+ aha_mbox_t imb[AHA_NMBOXES];
+ unsigned char iidx, oidx; /* roving ptrs into */
+};
+#define next_mbx_idx(i) ((((i)+1)==AHA_NMBOXES)?0:((i)+1))
+
+#define AHA_NCCB 8 /* for now */
+struct aha_ccb_raw {
+ target_info_t *active_target;
+ aha_ccb_t ccb;
+ char buffer[256]; /* separate out this ? */
+};
+#define rccb_to_cmdptr(rccb) ((char*)&((rccb)->ccb.ccb_scsi_cmd))
+
+/* forward decls */
+int aha_reset_scsibus();
+boolean_t aha_probe_target();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) board
+ */
+struct aha_softc {
+ watchdog_t wd;
+ decl_simple_lock_data(, aha_lock)
+ unsigned int port; /* I/O port */
+
+ int ntargets; /* how many alive on this scsibus */
+
+ scsi_softc_t *sc; /* HBA-indep info */
+
+ struct aha_mb_ctl mb; /* mailbox structures */
+
+ /* This chicanery is for mapping back the phys address
+ of a CCB (which we get in an MBI) to its virtual */
+ /* [we could use phystokv(), but it isn't standard] */
+ vm_offset_t I_hold_my_phys_address;
+ struct aha_ccb_raw aha_ccbs[AHA_NCCB];
+
+} aha_softc_data[NAHA];
+
+typedef struct aha_softc *aha_softc_t;
+
+aha_softc_t aha_softc[NAHA];
+
+struct aha_ccb_raw *
+mb_to_rccb(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ vm_offset_t addr;
+
+ AHA_MB_GET_PTR(&mbi,addr); /* phys address of ccb */
+
+ /* make virtual */
+ addr = ((vm_offset_t)&aha->I_hold_my_phys_address) +
+ (addr - aha->I_hold_my_phys_address);
+
+ /* adjust by proper offset to get base */
+ addr -= (vm_offset_t)&(((struct aha_ccb_raw *)0)->ccb);
+
+ return (struct aha_ccb_raw *)addr;
+}
+
+target_info_t *
+aha_tgt_alloc(aha, id, sns_len, tgt)
+ aha_softc_t aha;
+ target_info_t *tgt;
+{
+ struct aha_ccb_raw *rccb;
+
+ aha->ntargets++;
+
+ if (tgt == 0)
+ tgt = scsi_slave_alloc(aha - aha_softc_data, id, aha);
+
+ rccb = &(aha->aha_ccbs[id]);
+ rccb->ccb.ccb_reqsns_len = sns_len;
+ tgt->cmd_ptr = rccb_to_cmdptr(rccb);
+ tgt->dma_ptr = 0;
+#ifdef CBUS
+ tgt->aha_cbus_window = 0;
+#endif /* CBUS */
+ return tgt;
+}
+
+/*
+ * Synch xfer timing conversions
+ */
+#define aha_to_scsi_period(a) ((200 + ((a) * 50)) >> 2)
+#define scsi_period_to_aha(p) ((((p) << 2) - 200) / 50)
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+/* DOCUMENTATION */
+/* base ports can be:
+ 0x334, 0x330 (default), 0x234, 0x230, 0x134, 0x130
+ possible interrupt channels are:
+ 9, 10, 11 (default), 12, 14, 15
+ DMA channels can be:
+ 7, 6, 5 (default), 0
+/* DOCUMENTATION */
+
+int aha_probe(), scsi_slave(), aha_go(), aha_intr();
+void scsi_attach();
+
+vm_offset_t aha_std[NAHA] = { 0 };
+struct bus_device *aha_dinfo[NAHA*8];
+struct bus_ctlr *aha_minfo[NAHA];
+struct bus_driver aha_driver =
+ { aha_probe, scsi_slave, scsi_attach, aha_go, aha_std, "rz", aha_dinfo,
+ "ahac", aha_minfo, BUS_INTR_B4_PROBE};
+
+#define DEBUG 1
+#if DEBUG
+
+#define PRINT(x) if (scsi_debug) printf x
+
+aha_state(port)
+{
+ register unsigned char st, intr;
+
+ if (port == 0)
+ port = 0x330;
+ st = inb(AHA_STATUS_PORT(port));
+ intr = inb(AHA_INTR_PORT(port));
+
+ printf("status %x intr %x\n", st, intr);
+ return 0;
+}
+
+aha_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = aha_softc[0]->sc->target[0];
+ if (tgt == 0)
+ return 0;
+ printf("fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n",
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+
+ return 0;
+}
+
+aha_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = aha_softc[unit]->sc->target[i];
+ if (tgt)
+ aha_target_state(tgt);
+ }
+}
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+#define LOG_KERN 0<<3 /* from syslog.h */
+
+int aha_logpt;
+char aha_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x1e
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ aha_log[aha_logpt++] = (e);
+ if (aha_logpt == LOGSIZE) aha_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+aha_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = aha_logpt; i < LOGSIZE; i++) {
+ c = aha_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ printf(" %s", logtbl[c].name);
+ else
+ printf("-%x", c & 0x7f);
+ }
+ return 0;
+}
+
+aha_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /*TRACE*/
+#define LOG(e,f)
+#define LOGSIZE
+#endif /*TRACE*/
+
+#else /*DEBUG*/
+#define PRINT(x)
+#define LOG(e,f)
+#define LOGSIZE
+#define TRCHECK
+#define TR(a)
+
+#endif /*DEBUG*/
+
+/* Utility functions at end */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+int aha_dotarget = 1; /* somehow on some boards this is trouble */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Just ask the board to do it
+ */
+aha_probe(port, ui)
+ register port;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ aha_softc_t aha = &aha_softc_data[unit];
+ int target_id;
+ scsi_softc_t *sc;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+ struct aha_devs installed;
+ struct aha_conf conf;
+
+ /* No interrupts yet */
+ s = splbio();
+
+ /*
+ * We should be called with a sensible port, but you never know.
+ * Send an echo command and see that we get it back properly
+ */
+ {
+ register unsigned char st;
+
+ st = inb(AHA_STATUS_PORT(port));
+
+ /*
+ * There is no board reset in case of reboot with
+ * no power-on/power-off sequence. Test it and do
+ * the reset if necessary.
+ */
+
+ if (!(st & AHA_CSR_INIT_REQ)) {
+ outb(AHA_CONTROL_PORT(port),
+ AHA_CTL_SOFT_RESET|AHA_CTL_HARD_RESET);
+ while ((st = inb(AHA_STATUS_PORT(port))) &
+ AHA_CSR_SELF_TEST);
+ }
+ if ((st & AHA_CSR_DATAO_FULL) ||
+ !(st & AHA_CSR_INIT_REQ))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO);
+ delay(1000);/*?*/
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), 0x5e);
+ delay(1000);
+
+ st = inb(AHA_STATUS_PORT(port));
+ if ((st & AHA_CSR_CMD_ERR) ||
+ ((st & AHA_CSR_DATAI_FULL) == 0))
+ goto fail;
+
+ st = inb(AHA_DATA_PORT(port));
+ if (st != 0x5e) {
+fail: splx(s);
+ return 0;
+ }
+ /*
+ * augment test with check for echoing inverse and with
+ * test for enhanced adapter with standard ports enabled.
+ */
+
+ /* Check that 0xa1 echoed as well as 0x5e */
+
+ outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO);
+ delay(1000);/*?*/
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), 0xa1);
+ delay(1000);
+
+ st = inb(AHA_STATUS_PORT(port));
+ if ((st & AHA_CSR_CMD_ERR) ||
+ ((st & AHA_CSR_DATAI_FULL) == 0))
+ goto fail;
+
+ st = inb(AHA_DATA_PORT(port));
+ if (st != 0xa1)
+ goto fail ;
+
+ { /* Check that port isn't 174x in enhanced mode
+ with standard mode ports enabled. This should be
+ ignored because it will be caught and correctly
+ handled by eaha_probe(). See TRM4-11..13.
+ dph
+ */
+ unsigned z ;
+ static unsigned port_table[] =
+ {0,0,0x130,0x134,0x230,0x234,0x330,0x334};
+ for (z= 0x1000; z<= 0xF000; z+= 0x1000)
+ if (inb(z+0xC80) == 0x04 &&
+ inb(z+0xC81) == 0x90 &&
+ inb(z+0xCC0) & 0x80 == 0x80 &&
+ port_table [inb(z+0xCC0) & 0x07] == port)
+ goto fail ;
+ }
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+ }
+
+#if MAPPABLE
+ /* Mappable version side */
+ AHA_probe(port, ui);
+#endif /*MAPPABLE*/
+
+ /*
+ * Initialize hw descriptor, cache some pointers
+ */
+ aha_softc[unit] = aha;
+ aha->port = port;
+
+ sc = scsi_master_alloc(unit, aha);
+ aha->sc = sc;
+
+ simple_lock_init(&aha->aha_lock);
+ sc->go = aha_go;
+ sc->watchdog = scsi_watchdog;
+ sc->probe = aha_probe_target;
+ aha->wd.reset = aha_reset_scsibus;
+
+ /* Stupid limitation, no way around it */
+ sc->max_dma_data = (AHA_MAX_SEGLIST-1) * MACHINE_PGBYTES;
+
+
+ /* XXX
+ * I'm not sure how much use this bit of code is really.
+ * On the 1542CF we don't really want to try and initialize
+ * the mailboxes before unlocking them in any case, and
+ * resetting the card is done above.
+ */
+#if 0
+#if 0
+ /*
+ * Reset board.
+ */
+ aha_reset(port, TRUE);
+#else
+ /*
+ * Initialize mailboxes
+ */
+ aha_init_1(aha);
+#endif
+#endif
+
+ /*
+ * Who are we ?
+ */
+ {
+ struct aha_inq inq;
+ struct aha_extbios extbios;
+ char *id;
+
+ aha_command(port, AHA_CMD_INQUIRY, 0, 0, &inq, sizeof(inq), TRUE);
+
+ switch (inq.board_id) {
+ case AHA_BID_1540_B16:
+ case AHA_BID_1540_B64:
+ id = "1540"; break;
+ case AHA_BID_1540B:
+ id = "1540B/1542B"; break;
+ case AHA_BID_1640:
+ id = "1640"; break;
+ case AHA_BID_1740:
+ id = "1740 Unsupported!!"; break;
+ case AHA_BID_1542C:
+ id = "1542C"; aha_dotarget = 0; break;
+ case AHA_BID_1542CF:
+ id = "1542CF"; break;
+ default:
+ id = 0; break;
+ }
+
+ printf("Adaptec %s [id %x], rev %c%c, options x%x\n",
+ id ? id : "Board",
+ inq.board_id, inq.frl_1, inq.frl_2, inq.options);
+
+ /*
+ * If we are a 1542C or 1542CF disable the extended bios
+ * so that the mailbox interface is unlocked.
+ * No need to check the extended bios flags as some of the
+ * extensions that cause us problems are not flagged in
+ * that byte.
+ */
+ if (inq.board_id == 0x44 || inq.board_id == 0x45) {
+ aha_command(port, AHA_EXT_BIOS, 0, 0, &extbios,
+ sizeof(extbios), TRUE);
+#ifdef AHADEBUG
+ printf("aha: extended bios flags 0x%x\n", extbios.flags);
+ printf("aha: mailboxlock 0x%x\n", extbios.mblock);
+#endif /* AHADEBUG */
+
+ printf("aha: 1542C/CF detected, unlocking mailbox\n");
+
+ /* XXX - This sends the mailboxlock code out to the
+ * controller. We need to output a 0, then the
+ * code...so since we don't care about the flags
+ * anyway, we just zero out that field and re-use
+ * the struct.
+ */
+ extbios.flags = 0;
+ aha_command(port, AHA_MBX_ENABLE, &extbios,
+ sizeof(extbios), 0, 0, TRUE);
+ }
+
+ }
+doconf:
+ /*
+ * Readin conf data
+ */
+ aha_command(port, AHA_CMD_GET_CONFIG, 0, 0, &conf, sizeof(conf), TRUE);
+
+ {
+ unsigned char args;
+
+ /*
+ * Change the bus on/off times to not clash with
+ * other dma users.
+ */
+ args = 7;
+ aha_command(port, AHA_CMD_SET_BUSON, &args, 1, 0, 0, TRUE);
+ args = 5;
+ aha_command(port, AHA_CMD_SET_BUSOFF, &args, 1, 0, 0, TRUE);
+ }
+
+ /* XXX - This is _REALLY_ sickening. */
+ /*
+ * Set up the DMA channel we'll be using.
+ */
+ {
+ register int d, i;
+ static struct {
+ unsigned char port;
+ unsigned char init_data;
+ } aha_dma_init[8][2] = {
+ {{0x0b,0x0c}, {0x0a,0x00}}, /* channel 0 */
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0xd6,0xc1}, {0xd4,0x01}}, /* channel 5 (def) */
+ {{0xd6,0xc2}, {0xd4,0x02}}, /* channel 6 */
+ {{0xd6,0xc3}, {0xd4,0x03}} /* channel 7 */
+ };
+
+
+ for (i = 0; i < 8; i++)
+ if ((1 << i) & conf.intr_ch) break;
+ i += 9;
+
+#if there_was_a_way
+ /*
+ * On second unit, avoid clashes with first
+ */
+ if ((unit > 0) && (ui->sysdep1 != i)) {
+ printf("Reprogramming irq and dma ch..\n");
+ ....
+ goto doconf;
+ }
+#endif
+
+ /*
+ * Initialize the DMA controller viz the channel we'll use
+ */
+ for (d = 0; d < 8; d++)
+ if ((1 << d) & conf.dma_arbitration) break;
+
+ outb(aha_dma_init[d][0].port, aha_dma_init[d][0].init_data);
+ outb(aha_dma_init[d][1].port, aha_dma_init[d][1].init_data);
+
+ /* make mapping phys->virt possible for CCBs */
+ aha->I_hold_my_phys_address =
+ kvtoAT((vm_offset_t)&aha->I_hold_my_phys_address);
+
+ /*
+ * Our SCSI ID. (xxx) On some boards this is SW programmable.
+ */
+ sc->initiator_id = conf.my_scsi_id;
+
+ printf("%s%d: [dma ch %d intr ch %d] my SCSI id is %d",
+ ui->name, unit, d, i, sc->initiator_id);
+
+ /* Interrupt vector setup */
+ ui->sysdep1 = i;
+ take_ctlr_irq(ui);
+ }
+
+ /*
+ * More initializations
+ */
+ {
+ register target_info_t *tgt;
+
+ aha_init(aha);
+
+ /* allocate a desc for tgt mode role */
+ tgt = aha_tgt_alloc(aha, sc->initiator_id, 1, 0);
+ sccpu_new_initiator(tgt, tgt); /* self */
+
+ }
+
+ /* Now we could take interrupts, BUT we do not want to
+ be selected as targets by some other host just yet */
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ * This includes ourselves, when acting as target
+ */
+ aha_command( port, AHA_CMD_FIND_DEVICES, 0, 0, &installed, sizeof(installed), TRUE);
+ for (target_id = 0; target_id < 8; target_id++) {
+
+ if (target_id == sc->initiator_id) /* done already */
+ continue;
+
+ if (installed.tgt_luns[target_id] == 0)
+ continue;
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ /* Normally, only LUN 0 */
+ if (installed.tgt_luns[target_id] != 1)
+ printf("(%x)", installed.tgt_luns[target_id]);
+ /*
+ * Found a target
+ */
+ (void) aha_tgt_alloc(aha, target_id, 1/*no REQSNS*/, 0);
+
+ }
+ printf(".\n");
+ splx(s);
+
+ return 1;
+}
+
+boolean_t
+aha_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ aha_softc_t aha = aha_softc[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+ (void) aha_tgt_alloc(aha,tgt->target_id, 1/*no REQSNS*/, tgt);
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+aha_reset(port, quick)
+{
+ register unsigned char st;
+
+ /*
+ * Reset board and wait till done
+ */
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_SOFT_RESET);
+ do {
+ delay(25);
+ st = inb(AHA_STATUS_PORT(port));
+ } while ((st & (AHA_CSR_IDLE|AHA_CSR_INIT_REQ)) == 0);
+
+ if (quick) return;
+
+ /*
+ * reset the scsi bus. Does NOT generate an interrupt (bozos)
+ */
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_SCSI_RST);
+}
+
+aha_init_1(aha)
+ aha_softc_t aha;
+{
+ struct aha_init a;
+ vm_offset_t phys;
+
+ bzero(&aha->mb, sizeof(aha->mb)); /* also means all free */
+ a.mb_count = AHA_NMBOXES;
+ phys = kvtoAT((vm_offset_t)&aha->mb);
+ AHA_ADDRESS_SET(a.mb_ptr, phys);
+ aha_command(aha->port, AHA_CMD_INIT, &a, sizeof(a), 0, 0, TRUE);
+}
+
+aha_init_2(port)
+{
+ unsigned char disable = AHA_MBO_DISABLE;
+ struct aha_tgt role;
+
+ /* Disable MBO available interrupt */
+ aha_command(port, AHA_CMD_MBO_IE, &disable, 1, 0,0, FALSE);
+
+ if (aha_dotarget) {
+ /* Enable target mode role */
+ role.enable = 1;
+ role.luns = 1; /* only LUN 0 */
+ aha_command(port, AHA_CMD_ENB_TGT_MODE, &role, sizeof(role), 0, 0, TRUE);
+ }
+}
+
+aha_init(aha)
+ aha_softc_t aha;
+{
+ aha_init_1(aha);
+ aha_init_2(aha->port);
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+aha_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ aha_softc_t aha;
+ spl_t s;
+ struct aha_ccb_raw *rccb;
+ int len;
+ vm_offset_t virt, phys;
+
+#if CBUS
+ at386_io_lock_state();
+#endif
+
+ LOG(1,"go");
+
+ aha = (aha_softc_t)tgt->hw_state;
+
+/* XXX delay the handling of the ccb till later */
+ rccb = &(aha->aha_ccbs[tgt->target_id]);
+ rccb->active_target = tgt;
+
+ /*
+ * We can do real DMA.
+ */
+/* tgt->transient_state.copy_count = 0; unused */
+/* tgt->transient_state.dma_offset = 0; unused */
+
+ tgt->transient_state.cmd_count = cmd_count;
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+
+ /* How do we avoid leaks here ? Trust the board
+ will do zero-padding, for now. XXX CHECKME */
+#if 0
+ if (len < tgt->block_size) {
+ bzero(to + len, tgt->block_size - len);
+ len = tgt->block_size;
+ tgt->transient_state.out_count = len;
+ }
+#endif
+ } else {
+ tgt->transient_state.out_count = 0;
+ }
+
+ /* See above for in_count < block_size */
+ tgt->transient_state.in_count = in_count;
+
+ /*
+ * Setup CCB state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(9,"readop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.in_count;
+ rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x1a,"writeop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.out_count;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1;
+ break;
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */
+ case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = (vm_offset_t)tgt->cmd_ptr;
+ len = tgt->transient_state.in_count;
+ rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0;
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ { register int cs = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.cmd_count = cs;
+ len =
+ tgt->transient_state.out_count = cmd_count - cs;
+ virt = (vm_offset_t)tgt->cmd_ptr + cs;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ }
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = 0;
+ len = 0;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 0;
+ }
+
+#if CBUS
+ at386_io_lock(MP_DEV_WAIT);
+#endif
+ aha_prepare_rccb(tgt, rccb, virt, len);
+
+ rccb->ccb.ccb_lun = tgt->lun;
+ rccb->ccb.ccb_scsi_id = tgt->target_id;
+
+/* AHA_LENGTH_SET(rccb->ccb.ccb_linkptr, 0); unused */
+/* rccb->ccb.ccb_linkid = 0; unused */
+
+#if !CBUS
+ s = splbio();
+#endif
+
+ LOG(3,"enqueue");
+
+ aha_start_scsi(aha, &rccb->ccb);
+
+#if CBUS
+ at386_io_unlock();
+#else
+ splx(s);
+#endif
+}
+
+aha_prepare_rccb(tgt, rccb, virt, len)
+ target_info_t *tgt;
+ struct aha_ccb_raw *rccb;
+ vm_offset_t virt;
+ vm_size_t len;
+{
+ vm_offset_t phys;
+#ifdef CBUS
+ int cbus_window;
+#endif /* CBUS */
+
+ rccb->ccb.ccb_cmd_len = tgt->transient_state.cmd_count;
+
+ /* this opcode is refused, grrrr. */
+/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_R; /* default common case */
+ rccb->ccb.ccb_code = AHA_CCB_I_CMD; /* default common case */
+ AHA_LENGTH_SET(rccb->ccb.ccb_datalen, len);/* default common case */
+
+#ifdef CBUS
+ if (tgt->aha_cbus_window == 0)
+ tgt->aha_cbus_window = cbus_alloc_win(AHA_MAX_SEGLIST+1);
+ cbus_window = tgt->aha_cbus_window;
+#endif /* CBUS */
+
+ if (virt == 0) {
+ /* no xfers */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, 0);
+ } else if (len <= MACHINE_PGBYTES) {
+/* INCORRECT: what if across two pages :INCORRECT */
+ /* simple xfer */
+#ifdef CBUS
+ phys = cbus_kvtoAT_ww(virt, cbus_window);
+#else /* CBUS */
+ phys = kvtophys(virt);
+#endif /* CBUS */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys);
+ } else {
+ /* messy xfer */
+ aha_seglist_t *seglist;
+ vm_offset_t ph1, off;
+ vm_size_t l1;
+
+ /* this opcode does not work, grrrrr */
+/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG_R;*/
+ rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG;
+
+ if (tgt->dma_ptr == 0)
+ aha_alloc_segment_list(tgt);
+ seglist = (aha_seglist_t *) tgt->dma_ptr;
+#ifdef CBUS
+ phys = cbus_kvtoAT_ww(seglist, cbus_window);
+ cbus_window++;
+#else /* CBUS */
+ phys = kvtophys((vm_offset_t) seglist);
+#endif /* CBUS */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys);
+
+ ph1 = /*i386_trunc_page*/ virt & ~(MACHINE_PGBYTES - 1);
+ off = virt & (MACHINE_PGBYTES - 1);
+#ifdef CBUS
+ ph1 = cbus_kvtoAT_ww(ph1, cbus_window) + off;
+ cbus_window++;
+#else /* CBUS */
+ ph1 = kvtophys(ph1) + off;
+#endif /* CBUS */
+ l1 = MACHINE_PGBYTES - off;
+
+ off = 1;/* now #pages */
+ while (1) {
+ AHA_ADDRESS_SET(seglist->ptr, ph1);
+ AHA_LENGTH_SET(seglist->len, l1);
+ seglist++;
+
+ if ((len -= l1) <= 0)
+ break;
+ virt += l1; off++;
+
+#ifdef CBUS
+ ph1 = cbus_kvtoAT_ww(virt, cbus_window);
+ cbus_window++;
+#else /* CBUS */
+ ph1 = kvtophys(virt);
+#endif /* CBUS */
+ l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len;
+ }
+ l1 = off * sizeof(*seglist);
+ AHA_LENGTH_SET(rccb->ccb.ccb_datalen, l1);
+ }
+}
+
+aha_start_scsi(aha, ccb)
+ aha_softc_t aha;
+ aha_ccb_t *ccb;
+{
+ register aha_mbox_t *mb;
+ register idx;
+ vm_offset_t phys;
+ aha_mbox_t mbo;
+ spl_t s;
+
+ LOG(4,"start");
+ LOG(0x80+ccb->ccb_scsi_id,0);
+
+ /*
+ * Get an MBO, spin if necessary (takes little time)
+ */
+ s = splbio();
+ phys = kvtoAT((vm_offset_t)ccb);
+ /* might cross pages, but should be ok (kernel is contig) */
+ AHA_MB_SET_PTR(&mbo,phys);
+ mbo.mb.mb_cmd = AHA_MBO_START;
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive++ == 0)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ idx = aha->mb.oidx;
+ aha->mb.oidx = next_mbx_idx(idx);
+ mb = &aha->mb.omb[idx];
+ while (mb->mb.mb_status != AHA_MBO_FREE)
+ delay(1);
+ mb->bits = mbo.bits;
+ simple_unlock(&aha->aha_lock);
+
+ /*
+ * Start the board going
+ */
+ aha_command(aha->port, AHA_CMD_START, 0, 0, 0, 0, FALSE);
+ splx(s);
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the board
+ *
+ * Implementation:
+ * TBD
+ */
+aha_intr(unit)
+{
+ register aha_softc_t aha;
+ register port;
+ register csr, intr;
+#if MAPPABLE
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return AHA_intr(unit);
+#endif /*MAPPABLE*/
+
+ aha = aha_softc[unit];
+ port = aha->port;
+
+ LOG(5,"\n\tintr");
+gotintr:
+ /* collect ephemeral information */
+ csr = inb(AHA_STATUS_PORT(port));
+ intr = inb(AHA_INTR_PORT(port));
+
+ /*
+ * Check for errors
+ */
+ if (csr & (AHA_CSR_DIAG_FAIL|AHA_CSR_CMD_ERR)) {
+/* XXX */ gimmeabreak();
+ }
+
+ /* drop spurious interrupts */
+ if ((intr & AHA_INTR_PENDING) == 0) {
+ LOG(2,"SPURIOUS");
+ return;
+ }
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+
+TR(csr);TR(intr);TRCHECK
+
+ if (intr & AHA_INTR_RST)
+ return aha_bus_reset(aha);
+
+ /* we got an interrupt allright */
+ if (aha->wd.nactive)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (intr == AHA_INTR_DONE) {
+ /* csr & AHA_CSR_CMD_ERR --> with error */
+ LOG(6,"done");
+ return;
+ }
+
+/* if (intr & AHA_INTR_MBO_AVAIL) will not happen */
+
+ /* Some real work today ? */
+ if (intr & AHA_INTR_MBI_FULL) {
+ register int idx;
+ register aha_mbox_t *mb;
+ int nscan = 0;
+ aha_mbox_t mbi;
+rescan:
+ simple_lock(&aha->aha_lock);
+ idx = aha->mb.iidx;
+ aha->mb.iidx = next_mbx_idx(idx);
+ mb = &aha->mb.imb[idx];
+ mbi.bits = mb->bits;
+ mb->mb.mb_status = AHA_MBI_FREE;
+ simple_unlock(&aha->aha_lock);
+
+ nscan++;
+
+ switch (mbi.mb.mb_status) {
+
+ case AHA_MBI_FREE:
+ if (nscan >= AHA_NMBOXES)
+ return;
+ goto rescan;
+ break;
+
+ case AHA_MBI_SUCCESS:
+ case AHA_MBI_ERROR:
+ aha_initiator_intr(aha, mbi);
+ break;
+
+ case AHA_MBI_NEED_CCB:
+ aha_target_intr(aha, mbi);
+ break;
+
+/* case AHA_MBI_ABORTED: /* this we wont see */
+/* case AHA_MBI_NOT_FOUND: /* this we wont see */
+ default:
+ log( LOG_KERN,
+ "aha%d: Bogus status (x%x) in MBI\n",
+ unit, mbi.mb.mb_status);
+ break;
+ }
+
+ /* peek ahead */
+ if (aha->mb.imb[aha->mb.iidx].mb.mb_status != AHA_MBI_FREE)
+ goto rescan;
+ }
+
+ /* See if more work ready */
+ if (inb(AHA_INTR_PORT(port)) & AHA_INTR_PENDING) {
+ LOG(7,"\n\tre-intr");
+ goto gotintr;
+ }
+}
+
+/*
+ * The interrupt routine turns to one of these two
+ * functions, depending on the incoming mbi's role
+ */
+aha_target_intr(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ target_info_t *initiator; /* this is the caller */
+ target_info_t *self; /* this is us */
+ int len;
+
+ if (mbi.mbt.mb_cmd != AHA_MBI_NEED_CCB)
+ gimmeabreak();
+
+ /* If we got here this is not zero .. */
+ self = aha->sc->target[aha->sc->initiator_id];
+
+ initiator = aha->sc->target[mbi.mbt.mb_initiator_id];
+ /* ..but initiators are not required to answer to our inquiry */
+ if (initiator == 0) {
+ /* allocate */
+ initiator = aha_tgt_alloc(aha, mbi.mbt.mb_initiator_id,
+ sizeof(scsi_sense_data_t) + 5, 0);
+
+ /* We do not know here wether the host was down when
+ we inquired, or it refused the connection. Leave
+ the decision on how we will talk to it to higher
+ level code */
+ LOG(0xC, "new_initiator");
+ sccpu_new_initiator(self, initiator);
+ }
+
+ /* The right thing to do would be build an ior
+ and call the self->dev_ops->strategy routine,
+ but we cannot allocate it at interrupt level.
+ Also note that we are now disconnected from the
+ initiator, no way to do anything else with it
+ but reconnect and do what it wants us to do */
+
+ /* obviously, this needs both spl and MP protection */
+ self->dev_info.cpu.req_pending = TRUE;
+ self->dev_info.cpu.req_id = mbi.mbt.mb_initiator_id;
+ self->dev_info.cpu.req_lun = mbi.mbt.mb_lun;
+ self->dev_info.cpu.req_cmd =
+ mbi.mbt.mb_isa_send ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE;
+ len = (mbi.mbt.mb_data_len_msb << 16) |
+ (mbi.mbt.mb_data_len_mid << 8 );
+ len += 0x100;/* truncation problem */
+ self->dev_info.cpu.req_len = len;
+
+ LOG(0xB,"tgt-mode-restart");
+ (*self->dev_ops->restart)( self, FALSE);
+
+ /* The call above has either prepared the data,
+ placing an ior on self, or it handled it some
+ other way */
+ if (self->ior == 0)
+ return; /* I guess we'll do it later */
+
+ {
+ struct aha_ccb_raw *rccb;
+
+ rccb = &(aha->aha_ccbs[initiator->target_id]);
+ rccb->active_target = initiator;
+ if (self->dev_info.cpu.req_cmd == SCSI_CMD_SEND) {
+ rccb->ccb.ccb_in = 1;
+ rccb->ccb.ccb_out = 0;
+ } else {
+ rccb->ccb.ccb_in = 0;
+ rccb->ccb.ccb_out = 1;
+ }
+
+ aha_prepare_rccb(initiator, rccb,
+ (vm_offset_t)self->ior->io_data, self->ior->io_count);
+ rccb->ccb.ccb_code = AHA_CCB_T_CMD;
+ rccb->ccb.ccb_lun = initiator->lun;
+ rccb->ccb.ccb_scsi_id = initiator->target_id;
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive++ == 0)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ simple_unlock(&aha->aha_lock);
+
+ aha_start_scsi(aha, &rccb->ccb);
+ }
+}
+
+aha_initiator_intr(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ struct aha_ccb_raw *rccb;
+ scsi2_status_byte_t status;
+ target_info_t *tgt;
+
+ rccb = mb_to_rccb(aha,mbi);
+ tgt = rccb->active_target;
+ rccb->active_target = 0;
+
+ /* shortcut (sic!) */
+ if (mbi.mb.mb_status == AHA_MBI_SUCCESS)
+ goto allok;
+
+ switch (rccb->ccb.ccb_hstatus) {
+ case AHA_HST_SUCCESS:
+allok:
+ status = rccb->ccb.ccb_status;
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0);
+ tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ tgt->done = SCSI_RET_SUCCESS;
+ break;
+ case AHA_HST_SEL_TIMEO:
+ if (tgt->flags & TGT_FULLY_PROBED)
+ tgt->flags = 0; /* went offline */
+ tgt->done = SCSI_RET_DEVICE_DOWN;
+ break;
+ case AHA_HST_DATA_OVRUN:
+ /* BUT we don't know if this is an underrun.
+ It is ok if we get less data than we asked
+ for, in a number of cases. Most boards do not
+ seem to generate this anyways, but some do. */
+ { register int cmd = tgt->cur_cmd;
+ switch (cmd) {
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ break;
+ default:
+ printf("%sx%x\n",
+ "aha: U/OVRUN on scsi command x%x\n",
+ cmd);
+ gimmeabreak();
+ }
+ }
+ goto allok;
+ case AHA_HST_BAD_DISCONN:
+ printf("aha: bad disconnect\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case AHA_HST_BAD_PHASE_SEQ:
+ /* we'll get an interrupt soon */
+ printf("aha: bad PHASE sequencing\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case AHA_HST_BAD_OPCODE: /* fall through */
+ case AHA_HST_BAD_PARAM:
+printf("aha: BADCCB\n");gimmeabreak();
+ tgt->done = SCSI_RET_RETRY;
+ break;
+ case AHA_HST_BAD_LINK_LUN: /* these should not happen */
+ case AHA_HST_INVALID_TDIR:
+ case AHA_HST_DUPLICATED_CCB:
+ printf("aha: bad hstatus (x%x)\n", rccb->ccb.ccb_hstatus);
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ }
+
+ LOG(8,"end");
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive-- == 1)
+ aha->wd.watchdog_state = SCSI_WD_INACTIVE;
+ simple_unlock(&aha->aha_lock);
+
+ if (tgt->ior) {
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+
+ return FALSE;
+}
+
+/*
+ * The bus was reset
+ */
+aha_bus_reset(aha)
+ register aha_softc_t aha;
+{
+ register port = aha->port;
+
+ LOG(0x1d,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ aha->wd.nactive = 0;
+ aha_reset(port, TRUE);
+ aha_init(aha);
+
+ printf("aha: (%d) bus reset ", ++aha->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (aha->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(aha->sc);
+}
+
+/*
+ * Watchdog
+ *
+ * We know that some (name withdrawn) disks get
+ * stuck in the middle of dma phases...
+ */
+aha_reset_scsibus(aha)
+ register aha_softc_t aha;
+{
+ register target_info_t *tgt;
+ register port = aha->port;
+ register int i;
+
+ for (i = 0; i < AHA_NCCB; i++) {
+ tgt = aha->aha_ccbs[i].active_target;
+ if (/*scsi_debug &&*/ tgt)
+ printf("Target %d was active, cmd x%x in x%x out x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count,
+ tgt->transient_state.out_count);
+ }
+ aha_reset(port, FALSE);
+ delay(35);
+ /* no interrupt will come */
+ aha_bus_reset(aha);
+}
+
+/*
+ * Utilities
+ */
+
+/*
+ * Send a command to the board along with some
+ * optional parameters, optionally receive the
+ * results at command completion, returns how
+ * many bytes we did NOT get back.
+ */
+aha_command(port, cmd, outp, outc, inp, inc, clear_interrupt)
+ unsigned char *outp, *inp;
+{
+ register unsigned char st;
+ boolean_t failed = TRUE;
+
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ } while (st & AHA_CSR_DATAO_FULL);
+
+ /* Output command and any data */
+ outb(AHA_COMMAND_PORT(port), cmd);
+ while (outc--) {
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & AHA_CSR_CMD_ERR) goto out;
+ } while (st & AHA_CSR_DATAO_FULL);
+
+ outb(AHA_COMMAND_PORT(port), *outp++);
+ }
+
+ /* get any data */
+ while (inc--) {
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & AHA_CSR_CMD_ERR) goto out;
+ } while ((st & AHA_CSR_DATAI_FULL) == 0);
+
+ *inp++ = inb(AHA_DATA_PORT(port));
+ }
+ ++inc;
+ failed = FALSE;
+
+ /* wait command complete */
+ if (clear_interrupt) do {
+ delay(1);
+ st = inb(AHA_INTR_PORT(port));
+ } while ((st & AHA_INTR_DONE) == 0);
+
+out:
+ if (clear_interrupt)
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+ if (failed)
+ printf("aha_command: error on (%x %x %x %x %x %x), status %x\n",
+ port, cmd, outp, outc, inp, inc, st);
+ return inc;
+}
+
+#include <vm/vm_kern.h>
+
+/*
+ * Allocate dynamically segment lists to
+ * targets (for scatter/gather)
+ * Its a max of 17*6=102 bytes per target.
+ */
+vm_offset_t aha_seglist_next, aha_seglist_end;
+
+aha_alloc_segment_list(tgt)
+ target_info_t *tgt;
+{
+#define ALLOC_SIZE (AHA_MAX_SEGLIST * sizeof(aha_seglist_t))
+
+/* XXX locking */
+ if ((aha_seglist_next + ALLOC_SIZE) > aha_seglist_end) {
+ (void) kmem_alloc_wired(kernel_map, &aha_seglist_next, PAGE_SIZE);
+ aha_seglist_end = aha_seglist_next + PAGE_SIZE;
+ }
+ tgt->dma_ptr = (char *)aha_seglist_next;
+ aha_seglist_next += ALLOC_SIZE;
+/* XXX locking */
+}
+
+#endif /* NAHA > 0 */
+
diff --git a/scsi/adapters/scsi_aha17_hdw.c b/scsi/adapters/scsi_aha17_hdw.c
new file mode 100644
index 0000000..d8afe6a
--- /dev/null
+++ b/scsi/adapters/scsi_aha17_hdw.c
@@ -0,0 +1,1371 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * Copyright (c) 1993 University of Dublin
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and the following permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON AND THE UNIVERSITY OF DUBLIN ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON AND THE
+ * UNIVERSITY OF DUBLIN DISCLAIM ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Support for AHA-174x in enhanced mode. Dominic Herity (dherity@cs.tcd.ie)
+ * Will refer to "Adaptec AHA-1740A/1742A/1744 Technical Reference Manual"
+ * page x-y as TRMx-y in comments below.
+ */
+
+#include <eaha.h>
+#if NEAHA > 0
+
+#define db_printf printf
+
+#include <cpus.h>
+#include <platforms.h>
+#include <aha.h>
+
+#ifdef OSF
+#include <eisa.h>
+#else
+#include <i386at/eisa.h>
+#endif
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#include <scsi/adapters/scsi_aha15.h>
+#include <vm/vm_kern.h>
+
+#ifdef AT386
+#define MACHINE_PGBYTES I386_PGBYTES
+#define MAPPABLE 0
+#define gimmeabreak() asm("int3")
+
+
+#include <i386/pio.h> /* inlining of outb and inb */
+#ifdef OSF
+#include <machine/mp/mp.h>
+#endif
+#endif /*AT386*/
+
+#ifdef CBUS
+#include <cbus/cbus.h>
+#endif
+
+
+#ifndef MACHINE_PGBYTES /* cross compile check */
+#define MACHINE_PGBYTES 0x1000
+#define MAPPABLE 1
+#define gimmeabreak() Debugger("gimmeabreak");
+#endif
+
+int eaha_probe(), scsi_slave(), eaha_go(), eaha_intr();
+void scsi_attach();
+
+vm_offset_t eaha_std[NEAHA] = { 0 };
+struct bus_device *eaha_dinfo[NEAHA*8];
+struct bus_ctlr *eaha_minfo[NEAHA];
+struct bus_driver eaha_driver =
+ { eaha_probe, scsi_slave, scsi_attach, eaha_go, eaha_std, "rz",
+ eaha_dinfo, "eahac", eaha_minfo, BUS_INTR_B4_PROBE};
+
+
+#define TRACE
+#ifdef TRACE
+
+#define LOGSIZE 256
+int eaha_logpt;
+char eaha_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x1e
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(
+ int e,
+ char *f)
+{
+ eaha_log[eaha_logpt++] = (e);
+ if (eaha_logpt == LOGSIZE) eaha_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+eaha_print_log(
+ int skip)
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = eaha_logpt; i < LOGSIZE; i++) {
+ c = eaha_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ db_printf(" %s", logtbl[c].name);
+ else
+ db_printf("-%x", c & 0x7f);
+ }
+ return 0;
+}
+
+eaha_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /*TRACE*/
+#define LOG(e,f)
+#define LOGSIZE
+#endif /*TRACE*/
+
+#ifdef DEBUG
+#define ASSERT(x) { if (!(x)) gimmeabreak() ; }
+#define MARK() gimmeabreak()
+#else
+#define ASSERT(x)
+#define MARK()
+#endif
+
+/*
+ * Notes :
+ *
+ * do each host command TRM6-4
+ * find targets in probe
+ * disable SCSI writes
+ * matching port with structs, eaha_go with port, eaha_intr with port
+ *
+ */
+
+/* eaha registers. See TRM4-11..23. dph */
+
+#define HID0(z) ((z)+0xC80)
+#define HID1(z) ((z)+0xC81)
+#define HID2(z) ((z)+0xC82)
+#define HID3(z) ((z)+0xC83)
+#define EBCTRL(z) ((z)+0xC84)
+#define PORTADDR(z) ((z)+0xCC0)
+#define BIOSADDR(z) ((z)+0xCC1)
+#define INTDEF(z) ((z)+0xCC2)
+#define SCSIDEF(z) ((z)+0xCC3)
+#define MBOXOUT0(z) ((z)+0xCD0)
+#define MBOXOUT1(z) ((z)+0xCD1)
+#define MBOXOUT2(z) ((z)+0xCD2)
+#define MBOXOUT3(z) ((z)+0xCD3)
+#define MBOXIN0(z) ((z)+0xCD8)
+#define MBOXIN1(z) ((z)+0xCD9)
+#define MBOXIN2(z) ((z)+0xCDA)
+#define MBOXIN3(z) ((z)+0xCDB)
+#define ATTN(z) ((z)+0xCD4)
+#define G2CNTRL(z) ((z)+0xCD5)
+#define G2INTST(z) ((z)+0xCD6)
+#define G2STAT(z) ((z)+0xCD7)
+#define G2STAT2(z) ((z)+0xCDC)
+
+/*
+ * Enhanced mode data structures: ring, enhanced ccbs, a per target buffer
+ */
+
+#define SCSI_TARGETS 8 /* Allow for SCSI-2 */
+
+
+/* Extended Command Control Block Format. See TRM6-3..12. */
+
+typedef struct {
+ unsigned short command ;
+# define EAHA_CMD_NOP 0
+# define EAHA_CMD_INIT_CMD 1
+# define EAHA_CMD_DIAG 5
+# define EAHA_CMD_INIT_SCSI 6
+# define EAHA_CMD_READ_SENS 8
+# define EAHA_CMD_DOWNLOAD 9
+# define EAHA_CMD_HOST_INQ 0x0a
+# define EAHA_CMD_TARG_CMD 0x10
+
+ /*
+ * It appears to be customary to tackle the endian-ness of
+ * bit fields as follows, so I won't deviate. However, nothing in
+ * K&R implies that bit fields are implemented so that the fields
+ * of an unsigned char are allocated lsb first. Indeed, K&R _warns_
+ * _against_ using bit fields to describe storage allocation.
+ * This issue is separate from endian-ness. dph
+ * And this is exactly the reason macros are used. If your compiler
+ * is weird just override the macros and we will all be happy. af
+ */
+ BITFIELD_3(unsigned char,
+ cne:1,
+ xxx0:6,
+ di:1) ;
+ BITFIELD_7(unsigned char,
+ xxx1:2,
+ ses:1,
+ xxx2:1,
+ sg:1,
+ xxx3:1,
+ dsb:1,
+ ars:1) ;
+
+ BITFIELD_5(unsigned char,
+ lun:3,
+ tag:1,
+ tt:2,
+ nd:1,
+ xxx4:1) ;
+ BITFIELD_7(unsigned char,
+ dat:1,
+ dir:1,
+ st:1,
+ chk:1,
+ xxx5:2,
+ rec:1,
+ nbr:1) ;
+
+ unsigned short xxx6 ;
+
+ vm_offset_t scather ; /* scatter/gather */
+ unsigned scathlen ;
+ vm_offset_t status ;
+ vm_offset_t chain ;
+ int xxx7 ;
+
+ vm_offset_t sense_p ;
+ unsigned char sense_len ;
+ unsigned char cdb_len ;
+ unsigned short checksum ;
+ scsi_command_group_5 cdb ;
+ unsigned char buffer[256] ; /* space for data returned. */
+
+} eccb ;
+
+#define NTARGETS (8)
+#define NECCBS (NTARGETS+2) /* Targets + 2 to allow for temporaries. */
+ /* Can be up to 64 (TRM6-2), but that entails lots of bss usage */
+
+typedef struct { /* Status Block Format. See TRM6-13..19. */
+ BITFIELD_8(unsigned char,
+ don:1,
+ du:1,
+ xxx0:1,
+ qf:1,
+ sc:1,
+ dover:1,
+ ch:1,
+ inti:1) ;
+ BITFIELD_8(unsigned char,
+ asa:1, /* Error in TRM6-15..16 says both asa and sns */
+ sns:1, /* bit 9. Bits 8 and 10 are not mentioned. */
+ xxx1:1,
+ ini:1,
+ me:1,
+ xxx2:1,
+ eca:1,
+ xxx3:1) ;
+
+ unsigned char ha_status ;
+# define HA_STATUS_SUCCESS 0x00
+# define HA_STATUS_HOST_ABORTED 0x04
+# define HA_STATUS_ADP_ABORTED 0x05
+# define HA_STATUS_NO_FIRM 0x08
+# define HA_STATUS_NOT_TARGET 0x0a
+# define HA_STATUS_SEL_TIMEOUT 0x11
+# define HA_STATUS_OVRUN 0x12
+# define HA_STATUS_BUS_FREE 0x13
+# define HA_STATUS_PHASE_ERROR 0x14
+# define HA_STATUS_BAD_OPCODE 0x16
+# define HA_STATUS_INVALID_LINK 0x17
+# define HA_STATUS_BAD_CBLOCK 0x18
+# define HA_STATUS_DUP_CBLOCK 0x19
+# define HA_STATUS_BAD_SCATHER 0x1a
+# define HA_STATUS_RSENSE_FAIL 0x1b
+# define HA_STATUS_TAG_REJECT 0x1c
+# define HA_STATUS_HARD_ERROR 0x20
+# define HA_STATUS_TARGET_NOATTN 0x21
+# define HA_STATUS_HOST_RESET 0x22
+# define HA_STATUS_OTHER_RESET 0x23
+# define HA_STATUS_PROG_BAD_SUM 0x80
+
+ scsi2_status_byte_t target_status ;
+
+ unsigned residue ;
+ vm_offset_t residue_buffer ;
+ unsigned short add_stat_len ;
+ unsigned char sense_len ;
+ char xxx4[9] ;
+ unsigned char cdb[6] ;
+
+} status_block ;
+
+typedef struct {
+ vm_offset_t ptr ;
+ unsigned len ;
+} scather_entry ;
+
+#define SCATHER_ENTRIES 128 /* TRM 6-11 */
+
+struct erccbx {
+ target_info_t *active_target;
+ eccb _eccb;
+ status_block status ;
+ struct erccbx *next ;
+} ;
+
+typedef struct erccbx erccb ;
+
+/* forward decls */
+int eaha_reset_scsibus();
+boolean_t eaha_probe_target();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) board
+ */
+typedef struct {
+ watchdog_t wd;
+ decl_simple_lock_data(, aha_lock)
+ int port; /* I/O port */
+
+ int has_sense_info [NTARGETS];
+ int sense_info_lun [NTARGETS];
+ /* 1742 enhanced mode will hang if target has
+ * sense info and host doesn't request it (TRM6-34).
+ * This sometimes happens in the scsi driver.
+ * These flags indicate when a target has sense
+ * info to disgorge.
+ * If set, eaha_go reads and discards sense info
+ * before running any command except request sense.
+ * dph
+ */
+
+ scsi_softc_t *sc; /* HBA-indep info */
+
+ erccb _erccbs[NECCBS] ; /* mailboxes */
+ erccb *toperccb ;
+
+ /* This chicanery is for mapping back the phys address
+ of a CCB (which we get in an MBI) to its virtual */
+ /* [we could use phystokv(), but it isn't standard] */
+ vm_offset_t I_hold_my_phys_address;
+
+ char host_inquiry_data[256] ; /* Check out ../scsi2.h */
+
+} eaha_softc ;
+
+eaha_softc eaha_softc_data[NEAHA];
+
+typedef eaha_softc *eaha_softc_t;
+
+eaha_softc_t eaha_softc_pool[NEAHA];
+
+int eaha_quiet ;
+
+erccb *erccb_alloc(
+ eaha_softc *eaha)
+{
+ erccb *e ;
+ int x ;
+
+ do {
+ while (eaha->toperccb == 0) ;/* Shouldn't be often or long, */
+ /* BUT should use a semaphore */
+ x = splbio() ;
+ e = eaha->toperccb ;
+ if (e == 0)
+ splx(x) ;
+ } while (!e) ;
+ eaha->toperccb = e->next ;
+ splx(x) ;
+ bzero(e,sizeof(*e)) ;
+ e->_eccb.status = kvtophys((vm_offset_t)&e->status) ;
+ return e ;
+}
+
+void erccb_free(
+ eaha_softc *eaha,
+ erccb *e)
+{
+ int x ;
+ ASSERT ( e >= eaha->_erccbs && e < eaha->_erccbs+NECCBS) ;
+ x = splbio() ;
+ e->next = eaha->toperccb ;
+ eaha->toperccb = e ;
+ splx(x) ;
+}
+
+void eaha_mboxout(
+ int port,
+ vm_offset_t phys)
+{
+ outb(MBOXOUT0(port),phys) ;
+ outb(MBOXOUT1(port),phys>>8) ;
+ outb(MBOXOUT2(port),phys>>16) ;
+ outb(MBOXOUT3(port),phys>>24) ;
+}
+
+void eaha_command( /* start a command */
+ int port,
+ erccb *_erccb)
+{
+ int s ;
+ vm_offset_t phys = kvtophys((vm_offset_t) &_erccb->_eccb) ;
+ while ((inb(G2STAT(port)) & 0x04)==0); /*While MBO busy. TRM6-1 */
+ s = splbio() ;
+ eaha_mboxout(port,phys) ;
+ while (inb(G2STAT(port)) & 1) ; /* While adapter busy. TRM6-2 */
+ outb(ATTN(port),0x40 | _erccb->active_target->target_id) ; /* TRM6-20 */
+ /* (Should use target id for intitiator command) */
+ splx(s) ;
+}
+
+eaha_reset(
+ eaha_softc_t eaha,
+ boolean_t quick)
+{
+ /*
+ * Reset board and wait till done
+ */
+ unsigned st ;
+ int target_id ;
+ int port = eaha->port ;
+
+ /* Reset adapter, maybe with SCSIbus */
+ eaha_mboxout(port, quick ? 0x00080080 : 0x00000080 ) ; /* TRM 6-43..45 */
+ outb(ATTN(port), 0x10 | inb(SCSIDEF(port)) & 0x0f) ;
+ outb(G2CNTRL(port),0x20) ; /* TRM 4-22 */
+
+ do {
+ st = inb(G2INTST(port)) >> 4 ;
+ } while (st == 0) ;
+ /* TRM 4-22 implies that 1 should not be returned in G2INTST, but
+ in practise, it is. So this code takes 0 to mean non-completion. */
+
+ for (target_id = 0 ; target_id < NTARGETS; target_id++)
+ eaha->has_sense_info[target_id] = FALSE ;
+
+}
+
+void eaha_init(
+ eaha_softc_t eaha)
+{
+ /* Do nothing - I guess */
+}
+
+void eaha_bus_reset(
+ eaha_softc_t eaha)
+
+{
+ LOG(0x1d,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ eaha->wd.nactive = 0;
+ eaha_reset(eaha, TRUE);
+ eaha_init(eaha);
+
+ printf("eaha: (%d) bus reset ", ++eaha->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (eaha->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(eaha->sc);
+}
+
+#ifdef notdef
+ /* functions added to complete 1742 support, but not used. Untested. */
+
+ void eaha_download(port, data, len)
+ int port ;
+ char *data ;
+ unsigned len ;
+ {
+ /* 1744 firmware download. Not implemented. TRM6-21 */
+ }
+
+ void eaha_initscsi(data, len)
+ char *data ;
+ unsigned len ;
+ {
+ /* initialize SCSI subsystem. Presume BIOS does it.
+ Not implemented. TRM6-23 */
+ }
+
+ void eaha_noop()
+ {
+ /* Not implemented. TRM6-27 */
+ }
+
+ erccb *eaha_host_adapter_inquiry(eaha) /* Returns a promise */
+ eaha_softc *eaha ; /* TRM6-31..33 */
+ {
+ erccb *_erccb = erccb_alloc(eaha) ;
+ _erccb->_eccb.scather = (vm_offset_t) kvtophys(eaha->host_inquiry_data) ;
+ _erccb->_eccb.scathlen = sizeof(eaha->host_inquiry_data) ;
+ _erccb->_eccb.ses = 1 ;
+ _erccb->_eccb.command = EAHA_CMD_HOST_INQ ;
+ eaha_command(eaha->port,_erccb->_eccb,0) ; /* Is scsi_id used */
+ return _erccb ;
+ }
+
+ erccb *eaha_read_sense_info(eaha, target, lun) /* TRM 6-33..35 */
+ eaha_softc *eaha ;
+ unsigned target, lun ;
+ { /* Don't think we need this because its done in scsi_alldevs.c */
+ #ifdef notdef
+ erccb *_erccb = erccb_alloc(eaha) ;
+ _erccb->_eccb.command = EAHA_CMD_READ_SENS ;
+ _erccb->_eccb.lun = lun ;
+ eaha_command(eaha->port,_erccb->_eccb, target) ;/*Wrong # args*/
+ return _erccb ;
+ #else
+ return 0 ;
+ #endif
+ }
+
+ void eaha_diagnostic(eaha)
+ eaha_softc *eaha ;
+ {
+ /* Not implemented. TRM6-36..37 */
+ }
+
+ erccb *eaha_target_cmd(eaha, target, lun, data, len) /* TRM6-38..39 */
+ eaha_softc *eaha ;
+ unsigned target, lun ;
+ char *data ;
+ unsigned len ;
+ {
+ erccb *_erccb = erccb_alloc(eaha) ;
+ _erccb->_eccb.command = EAHA_CMD_TARG_CMD ;
+ _erccb->_eccb.lun = lun ;
+ eaha_command(eaha->port,_erccb->_eccb,target);/*Wrong # args*/
+ return _erccb ;
+ }
+
+ erccb *eaha_init_cmd(port) /* SHOULD RETURN TOKEN. i.e. ptr to eccb */
+ /* Need list of free eccbs */
+ { /* to be continued,. possibly. */
+ }
+
+#endif /* notdef */
+
+target_info_t *
+eaha_tgt_alloc(
+ eaha_softc_t eaha,
+ int id,
+ target_info_t *tgt)
+{
+ erccb *_erccb;
+
+ if (tgt == 0)
+ tgt = scsi_slave_alloc(eaha - eaha_softc_data, id, eaha);
+
+ _erccb = erccb_alloc(eaha) ; /* This is very dodgy */
+ tgt->cmd_ptr = (char *)& _erccb->_eccb.cdb ;
+ tgt->dma_ptr = 0;
+ return tgt;
+}
+
+
+struct {
+ scsi_sense_data_t sns ;
+ unsigned char extra
+ [254-sizeof(scsi_sense_data_t)] ;
+} eaha_xsns [NTARGETS] ;/*must be bss to be contiguous*/
+
+
+/* Enhanced adapter probe routine */
+
+eaha_probe(
+ register int port,
+ struct bus_ctlr *ui)
+{
+ int unit = ui->unit;
+ eaha_softc_t eaha = &eaha_softc_data[unit] ;
+ int target_id ;
+ scsi_softc_t *sc ;
+ int s;
+ boolean_t did_banner = FALSE ;
+ struct aha_devs installed;
+ unsigned char my_scsi_id, my_interrupt ;
+
+ if (unit >= NEAHA)
+ return(0);
+
+ /* No interrupts yet */
+ s = splbio();
+
+ /*
+ * Detect prescence of 174x in enhanced mode. Ignore HID2 and HID3
+ * on the assumption that compatibility will be preserved. dph
+ */
+ if (inb(HID0(port)) != 0x04 || inb(HID1(port)) != 0x90 ||
+ (inb(PORTADDR(port)) & 0x80) != 0x80) {
+ splx(s);
+ return 0 ;
+ }
+
+ /* Issue RESET in case this is a reboot */
+
+ outb(EBCTRL(port),0x04) ; /* Disable board. TRM4-12 */
+ outb(PORTADDR(port),0x80) ; /* Disable standard mode ports. TRM4-13. */
+ my_interrupt = inb(INTDEF(port)) & 0x07 ;
+ outb(INTDEF(port), my_interrupt | 0x00) ;
+ /* Disable interrupts. TRM4-15 */
+ my_scsi_id = inb(SCSIDEF(port)) & 0x0f ;
+ outb(SCSIDEF(port), my_scsi_id | 0x10) ;
+ /* Force SCSI reset on hard reset. TRM4-16 */
+ outb(G2CNTRL(port),0xe0) ; /* Reset board, clear interrupt */
+ /* and set 'host ready'. */
+ delay(10*10) ; /* HRST must remain set for 10us. TRM4-22 */
+ /* (I don't believe the delay loop is slow enough.) */
+ outb(G2CNTRL(port),0x60);/*Un-reset board, set 'host ready'. TRM4-22*/
+
+ printf("Adaptec 1740A/1742A/1744 enhanced mode\n");
+
+ /* Get host inquiry data */
+
+ eaha_softc_pool[unit] = eaha ;
+ bzero(eaha,sizeof(*eaha)) ;
+ eaha->port = port ;
+
+ sc = scsi_master_alloc(unit, eaha) ;
+ eaha->sc = sc ;
+ sc->go = eaha_go ;
+ sc->watchdog = scsi_watchdog ;
+ sc->probe = eaha_probe_target ;
+ eaha->wd.reset = eaha_reset_scsibus ;
+ sc->max_dma_data = -1 ; /* Lets be optimistic */
+ sc->initiator_id = my_scsi_id ;
+ eaha_reset(eaha,TRUE) ;
+ eaha->I_hold_my_phys_address =
+ kvtophys((vm_offset_t)&eaha->I_hold_my_phys_address) ;
+ {
+ erccb *e ;
+ eaha->toperccb = eaha->_erccbs ;
+ for (e=eaha->_erccbs; e < eaha->_erccbs+NECCBS; e++) {
+ e->next = e+1 ;
+ e->_eccb.status =
+ kvtophys((vm_offset_t) &e->status) ;
+ }
+ eaha->_erccbs[NECCBS-1].next = 0 ;
+
+ }
+
+ ui->sysdep1 = my_interrupt + 9 ;
+ take_ctlr_irq(ui) ;
+
+ printf("%s%d: [port 0x%x intr ch %d] my SCSI id is %d",
+ ui->name, unit, port, my_interrupt + 9, my_scsi_id) ;
+
+ outb(INTDEF(port), my_interrupt | 0x10) ;
+ /* Enable interrupts. TRM4-15 */
+ outb(EBCTRL(port),0x01) ; /* Enable board. TRM4-12 */
+
+ { target_info_t *t = eaha_tgt_alloc(eaha, my_scsi_id, 0) ;
+ /* Haven't enabled target mode a la standard mode, because */
+ /* it doesn't seem to be necessary. */
+ sccpu_new_initiator(t, t) ;
+ }
+
+ /* Find targets, incl. ourselves. */
+
+ for (target_id=0; target_id < SCSI_TARGETS; target_id++)
+ if (target_id != sc->initiator_id) {
+ scsi_cmd_test_unit_ready_t *cmd;
+ erccb *_erccb = erccb_alloc(eaha) ;
+ unsigned attempts = 0 ;
+#define MAX_ATTEMPTS 2
+ target_info_t temp_targ ;
+
+ temp_targ.ior = 0 ;
+ temp_targ.hw_state = (char *) eaha ;
+ temp_targ.cmd_ptr = (char *) &_erccb->_eccb.cdb ;
+ temp_targ.target_id = target_id ;
+ temp_targ.lun = 0 ;
+ temp_targ.cur_cmd = SCSI_CMD_TEST_UNIT_READY;
+
+ cmd = (scsi_cmd_test_unit_ready_t *) temp_targ.cmd_ptr;
+
+ do {
+ cmd->scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY;
+ cmd->scsi_cmd_lun_and_lba1 = 0; /*assume 1 lun?*/
+ cmd->scsi_cmd_lba2 = 0;
+ cmd->scsi_cmd_lba3 = 0;
+ cmd->scsi_cmd_ss_flags = 0;
+ cmd->scsi_cmd_ctrl_byte = 0; /* not linked */
+
+ eaha_go( &temp_targ,
+ sizeof(scsi_cmd_test_unit_ready_t),0,0);
+ /* ints disabled, so call isr yourself. */
+ while (temp_targ.done == SCSI_RET_IN_PROGRESS)
+ if (inb(G2STAT(eaha->port)) & 0x02) {
+ eaha_quiet = 1 ;
+ eaha_intr(unit) ;
+ eaha_quiet = 0 ;
+ }
+ if (temp_targ.done == SCSI_RET_NEED_SENSE) {
+ /* MUST get sense info : TRM6-34 */
+ if (eaha_retrieve_sense_info(
+ eaha, temp_targ.target_id,
+ temp_targ.lun) &&
+ attempts == MAX_ATTEMPTS-1) {
+
+ printf(
+ "\nTarget %d Check Condition : "
+ ,temp_targ.target_id) ;
+ scsi_print_sense_data(&eaha_xsns
+ [temp_targ.target_id]);
+ printf("\n") ;
+ }
+ }
+ } while (temp_targ.done != SCSI_RET_SUCCESS &&
+ temp_targ.done != SCSI_RET_ABORTED &&
+ ++attempts < MAX_ATTEMPTS) ;
+
+ /*
+ * Recognize target which is present, whether or not
+ * it is ready, e.g. drive with removable media.
+ */
+ if (temp_targ.done == SCSI_RET_SUCCESS ||
+ temp_targ.done == SCSI_RET_NEED_SENSE &&
+ _erccb->status.target_status.bits != 0) { /* Eureka */
+ installed.tgt_luns[target_id]=1;/*Assume 1 lun?*/
+ printf(", %s%d",
+ did_banner++ ? "" : "target(s) at ",
+ target_id);
+
+ erccb_free(eaha, _erccb) ;
+
+ /* Normally, only LUN 0 */
+ if (installed.tgt_luns[target_id] != 1)
+ printf("(%x)", installed.tgt_luns[target_id]);
+ /*
+ * Found a target
+ */
+ (void) eaha_tgt_alloc(eaha, target_id, 0);
+ /* Why discard ? */
+ } else
+ installed.tgt_luns[target_id]=0;
+ }
+
+ printf(".\n") ;
+ splx(s);
+ return 1 ;
+}
+
+int eaha_retrieve_sense_info (
+ eaha_softc_t eaha,
+ int tid,
+ int lun)
+{
+ int result ;
+ int s ;
+ target_info_t dummy_target ; /* Keeps eaha_command() happy. HACK */
+ erccb *_erccb1 = erccb_alloc(eaha) ;
+
+ _erccb1->active_target = &dummy_target ;
+ dummy_target.target_id = tid ;
+ _erccb1->_eccb.command =
+ EAHA_CMD_READ_SENS ;
+ _erccb1->_eccb.lun = lun ;
+ _erccb1->_eccb.sense_p = kvtophys((vm_offset_t) &eaha_xsns [tid]);
+ _erccb1->_eccb.sense_len = sizeof(eaha_xsns [tid]);
+ _erccb1->_eccb.ses = 1 ;
+ s = splbio() ;
+ eaha_command(eaha->port,_erccb1) ;
+ while ((inb(G2STAT(eaha->port)) & 0x02) == 0) ;
+ outb(G2CNTRL(eaha->port),0x40);/* Clear int */
+ splx(s) ;
+ result = _erccb1->status.target_status.bits != 0 ;
+ erccb_free(eaha,_erccb1) ;
+ return result ;
+}
+
+/*
+ * Start a SCSI command on a target (enhanced mode)
+ */
+eaha_go(
+ target_info_t *tgt,
+ int cmd_count,
+ int in_count,
+ boolean_t cmd_only)/*lint: unused*/
+{
+ eaha_softc_t eaha;
+ int s;
+ erccb *_erccb;
+ int len;
+ vm_offset_t virt;
+ int tid = tgt->target_id ;
+
+#ifdef CBUS
+ at386_io_lock_state();
+#endif
+ LOG(1,"go");
+
+#ifdef CBUS
+ at386_io_lock(MP_DEV_WAIT);
+#endif
+ eaha = (eaha_softc_t)tgt->hw_state;
+
+ if(eaha->has_sense_info[tid]) {
+ (void) eaha_retrieve_sense_info
+ (eaha, tid, eaha->sense_info_lun[tid]) ;
+ eaha->has_sense_info[tid] = FALSE ;
+ if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) {
+ bcopy(&eaha_xsns[tid],tgt->cmd_ptr,in_count) ;
+ tgt->done = SCSI_RET_SUCCESS;
+ tgt->transient_state.cmd_count = cmd_count;
+ tgt->transient_state.out_count = 0;
+ tgt->transient_state.in_count = in_count;
+ /* Fake up interrupt */
+ /* Highlights from eaha_initiator_intr(), */
+ /* ignoring errors */
+ if (tgt->ior)
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+#ifdef CBUS
+ at386_io_unlock();
+#endif
+ return ;
+ }
+ }
+
+/* XXX delay the handling of the ccb till later */
+ _erccb = (erccb *)
+ ((unsigned)tgt->cmd_ptr - (unsigned) &((erccb *) 0)->_eccb.cdb);
+ /* Tell *rccb about target, eg. id ? */
+ _erccb->active_target = tgt;
+
+ /*
+ * We can do real DMA.
+ */
+/* tgt->transient_state.copy_count = 0; unused */
+/* tgt->transient_state.dma_offset = 0; unused */
+
+ tgt->transient_state.cmd_count = cmd_count;
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+
+ /* How do we avoid leaks here ? Trust the board
+ will do zero-padding, for now. XXX CHECKME */
+#if 0
+ if (len < tgt->block_size) {
+ bzero(to + len, tgt->block_size - len);
+ len = tgt->block_size;
+ tgt->transient_state.out_count = len;
+ }
+#endif
+ } else {
+ tgt->transient_state.out_count = 0;
+ }
+
+ /* See above for in_count < block_size */
+ tgt->transient_state.in_count = in_count;
+
+ /*
+ * Setup CCB state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(9,"readop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.in_count;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x1a,"writeop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.out_count;
+ break;
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = (vm_offset_t)tgt->cmd_ptr;
+ len = tgt->transient_state.in_count;
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ tgt->transient_state.cmd_count = sizeof(scsi_command_group_0);
+ len =
+ tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0);
+ virt = (vm_offset_t)tgt->cmd_ptr+sizeof(scsi_command_group_0);
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = 0;
+ len = 0;
+ }
+
+ eaha_prepare_rccb(tgt, _erccb, virt, len);
+
+ _erccb->_eccb.lun = tgt->lun;
+
+ /*
+ * XXX here and everywhere, locks!
+ */
+ s = splbio();
+
+ simple_lock(&eaha->aha_lock);
+ if (eaha->wd.nactive++ == 0)
+ eaha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ simple_unlock(&eaha->aha_lock);
+
+ LOG(3,"enqueue");
+
+ eaha_command(eaha->port, _erccb) ;
+
+ splx(s);
+#ifdef CBUS
+ at386_io_unlock();
+#endif
+}
+
+eaha_prepare_rccb(
+ target_info_t *tgt,
+ erccb *_erccb,
+ vm_offset_t virt,
+ vm_size_t len)
+{
+ _erccb->_eccb.cdb_len = tgt->transient_state.cmd_count;
+
+ _erccb->_eccb.command = EAHA_CMD_INIT_CMD;/* default common case */
+
+ if (virt == 0) {
+ /* no xfers */
+ _erccb->_eccb.scather = 0 ;
+ _erccb->_eccb.scathlen = 0 ;
+ _erccb->_eccb.sg = 0 ;
+ } else {
+ /* messy xfer */
+ scather_entry *seglist;
+ vm_size_t l1, off;
+
+ _erccb->_eccb.sg = 1 ;
+
+ if (tgt->dma_ptr == 0)
+ eaha_alloc_segment_list(tgt);
+ seglist = (scather_entry *) tgt->dma_ptr;
+
+ _erccb->_eccb.scather = kvtophys((vm_offset_t) seglist);
+
+ l1 = MACHINE_PGBYTES - (virt & (MACHINE_PGBYTES - 1));
+ if (l1 > len)
+ l1 = len ;
+
+ off = 1;/* now #pages */
+ while (1) {
+ seglist->ptr = kvtophys(virt) ;
+ seglist->len = l1 ;
+ seglist++;
+
+ if (len <= l1)
+ break ;
+ len-= l1 ;
+ virt += l1; off++;
+
+ l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len;
+ }
+ _erccb->_eccb.scathlen = off * sizeof(*seglist);
+ }
+}
+
+/*
+ * Allocate dynamically segment lists to
+ * targets (for scatter/gather)
+ */
+vm_offset_t eaha_seglist_next = 0, eaha_seglist_end = 0 ;
+#define EALLOC_SIZE (SCATHER_ENTRIES * sizeof(scather_entry))
+
+eaha_alloc_segment_list(
+ target_info_t *tgt)
+{
+
+/* XXX locking */
+/* ? Can't spl() for unknown duration */
+ if ((eaha_seglist_next + EALLOC_SIZE) > eaha_seglist_end) {
+ (void)kmem_alloc_wired(kernel_map,&eaha_seglist_next,PAGE_SIZE);
+ eaha_seglist_end = eaha_seglist_next + PAGE_SIZE;
+ }
+ tgt->dma_ptr = (char *)eaha_seglist_next;
+ eaha_seglist_next += EALLOC_SIZE;
+/* XXX locking */
+}
+
+/*
+ *
+ * shameless copy from above
+ */
+eaha_reset_scsibus(
+ register eaha_softc_t eaha)
+{
+ register target_info_t *tgt;
+ register port = eaha->port;
+ register int i;
+
+ for (i = 0; i < NECCBS; i++) {
+ tgt = eaha->_erccbs[i].active_target;
+ if (/*scsi_debug &&*/ tgt)
+ printf("Target %d was active, cmd x%x in x%x out x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count,
+ tgt->transient_state.out_count);
+ }
+ eaha_reset(eaha, FALSE);
+ delay(35);
+ /* no interrupt will come */
+ eaha_bus_reset(eaha);
+}
+
+boolean_t
+eaha_probe_target(
+ target_info_t *tgt,
+ io_req_t ior)
+{
+ eaha_softc_t eaha = eaha_softc_pool[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+ (void) eaha_tgt_alloc(eaha,tgt->target_id, tgt);
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+
+/*
+ * Interrupt routine (enhanced mode)
+ * Take interrupts from the board
+ *
+ * Implementation:
+ * TBD
+ */
+eaha_intr(
+ int unit)
+{
+ register eaha_softc_t eaha;
+ register port;
+ unsigned g2intst, g2stat, g2stat2 ;
+ vm_offset_t mbi ;
+ erccb *_erccb ;
+ status_block *status ;
+
+#if MAPPABLE
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface) {
+ EAHA_intr(unit);
+ return ;
+ }
+#endif /*MAPPABLE*/
+
+ eaha = eaha_softc_pool[unit];
+ port = eaha->port;
+
+ LOG(5,"\n\tintr");
+gotintr:
+ /* collect ephemeral information */
+
+ g2intst = inb(G2INTST(port)) ; /* See TRM4-22..23 */
+ g2stat = inb(G2STAT(port)) ; /*lint:set,not used*/
+ g2stat2 = inb(G2STAT2(port)) ; /*lint:set,not used*/
+ mbi = (vm_offset_t) inb(MBOXIN0(port)) + (inb(MBOXIN1(port))<<8) +
+ (inb(MBOXIN2(port))<<16) + (inb(MBOXIN3(port))<<24) ;
+
+ /* we got an interrupt allright */
+ if (eaha->wd.nactive)
+ eaha->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ outb(G2CNTRL(port),0x40) ; /* Clear EISA interrupt */
+
+ switch(g2intst>>4) {
+ case 0x07 : /* hardware error ? */
+ case 0x0a : /* immediate command complete - don't expect */
+ case 0x0e : /* ditto with failure */
+ default :
+ printf( "aha%d: Bogus status (x%x) in MBI\n",
+ unit, mbi);
+ gimmeabreak() ; /* Any of above is disaster */
+ break;
+
+ case 0x0d : /* Asynchronous event TRM6-41 */
+ if ((g2intst & 0x0f) == (inb(SCSIDEF(eaha->port)) & 0x0f))
+ eaha_reset_scsibus(eaha) ;
+ else
+ eaha_target_intr(eaha, mbi, g2intst & 0x0f);
+ break;
+
+ case 0x0c : /* ccb complete with error */
+ case 0x01 : /* ccb completed with success */
+ case 0x05 : /* ccb complete with success after retry */
+
+ _erccb = (erccb *)
+ ( ((vm_offset_t)&eaha->I_hold_my_phys_address) +
+ (mbi - eaha->I_hold_my_phys_address) -
+ (vm_offset_t)&(((erccb *)0)->_eccb) ) ;
+ /* That ain't necessary. As kernel (must be) */
+ /* contiguous, only need delta to translate */
+
+ status = &_erccb->status ;
+
+#ifdef NOTDEF
+ if (!eaha_quiet && (!status->don || status->qf ||
+ status->sc || status->dover ||
+ status->ini || status->me)) {
+ printf("\nccb complete error G2INTST=%02X\n",
+ g2intst) ;
+ DUMP(*_erccb) ;
+ gimmeabreak() ;
+ }
+#endif
+
+ eaha_initiator_intr(eaha, _erccb);
+ break;
+ }
+
+ /* See if more work ready */
+ if (inb(G2STAT(port)) & 0x02) {
+ LOG(7,"\n\tre-intr");
+ goto gotintr;
+ }
+}
+
+/*
+ * The interrupt routine turns to one of these two
+ * functions, depending on the incoming mbi's role
+ */
+eaha_target_intr(
+ eaha_softc_t eaha,
+ unsigned int mbi,
+ unsigned int peer)
+{
+ target_info_t *initiator; /* this is the caller */
+ target_info_t *self; /* this is us */
+ int len;
+
+ self = eaha->sc->target[eaha->sc->initiator_id];
+
+ initiator = eaha->sc->target[peer];
+
+ /* ..but initiators are not required to answer to our inquiry */
+ if (initiator == 0) {
+ /* allocate */
+ initiator = eaha_tgt_alloc(eaha, peer, 0);
+
+ /* We do not know here wether the host was down when
+ we inquired, or it refused the connection. Leave
+ the decision on how we will talk to it to higher
+ level code */
+ LOG(0xC, "new_initiator");
+ sccpu_new_initiator(self, initiator);
+ /* Bug fix: was (aha->sc, self, initiator); dph */
+ }
+
+ /* The right thing to do would be build an ior
+ and call the self->dev_ops->strategy routine,
+ but we cannot allocate it at interrupt level.
+ Also note that we are now disconnected from the
+ initiator, no way to do anything else with it
+ but reconnect and do what it wants us to do */
+
+ /* obviously, this needs both spl and MP protection */
+ self->dev_info.cpu.req_pending = TRUE;
+ self->dev_info.cpu.req_id = peer ;
+ self->dev_info.cpu.req_lun = (mbi>>24) & 0x07 ;
+ self->dev_info.cpu.req_cmd =
+ (mbi & 0x80000000) ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE;
+ len = mbi & 0x00ffffff ;
+
+ self->dev_info.cpu.req_len = len;
+
+ LOG(0xB,"tgt-mode-restart");
+ (*self->dev_ops->restart)( self, FALSE);
+
+ /* The call above has either prepared the data,
+ placing an ior on self, or it handled it some
+ other way */
+ if (self->ior == 0)
+ return; /* I guess we'll do it later */
+
+ {
+ erccb *_erccb ;
+
+ _erccb = erccb_alloc(eaha) ;
+ _erccb->active_target = initiator;
+ _erccb->_eccb.command = EAHA_CMD_TARG_CMD ;
+ _erccb->_eccb.ses = 1 ;
+ _erccb->_eccb.dir = (self->cur_cmd == SCSI_CMD_SEND) ? 1 : 0 ;
+
+ eaha_prepare_rccb(initiator, _erccb,
+ (vm_offset_t)self->ior->io_data, self->ior->io_count);
+ _erccb->_eccb.lun = initiator->lun;
+
+ simple_lock(&eaha->aha_lock);
+ if (eaha->wd.nactive++ == 0)
+ eaha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ simple_unlock(&eaha->aha_lock);
+
+ eaha_command(eaha->port, _erccb);
+ }
+}
+
+eaha_initiator_intr(
+ eaha_softc_t eaha,
+ erccb *_erccb)
+{
+ scsi2_status_byte_t status;
+ target_info_t *tgt;
+
+ tgt = _erccb->active_target;
+ _erccb->active_target = 0;
+
+ /* shortcut (sic!) */
+ if (_erccb->status.ha_status == HA_STATUS_SUCCESS)
+ goto allok;
+
+ switch (_erccb->status.ha_status) { /* TRM6-17 */
+ case HA_STATUS_SUCCESS :
+allok:
+ status = _erccb->status.target_status ;
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0);
+ tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ tgt->done = SCSI_RET_SUCCESS;
+ break;
+
+ case HA_STATUS_SEL_TIMEOUT :
+ if (tgt->flags & TGT_FULLY_PROBED)
+ tgt->flags = 0; /* went offline */
+ tgt->done = SCSI_RET_DEVICE_DOWN;
+ break;
+
+ case HA_STATUS_OVRUN :
+ /* BUT we don't know if this is an underrun.
+ It is ok if we get less data than we asked
+ for, in a number of cases. Most boards do not
+ seem to generate this anyways, but some do. */
+ { register int cmd = tgt->cur_cmd;
+ switch (cmd) {
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_MODE_SENSE:
+ if (_erccb->status.du) /*Ignore underrun only*/
+ break;
+ default:
+ printf("eaha: U/OVRUN on scsi command x%x\n",cmd);
+ gimmeabreak();
+ }
+ }
+ goto allok;
+ case HA_STATUS_BUS_FREE :
+ printf("aha: bad disconnect\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case HA_STATUS_PHASE_ERROR :
+ /* we'll get an interrupt soon */
+ printf("aha: bad PHASE sequencing\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case HA_STATUS_BAD_OPCODE :
+printf("aha: BADCCB\n");gimmeabreak();
+ tgt->done = SCSI_RET_RETRY;
+ break;
+
+ case HA_STATUS_HOST_ABORTED :
+ case HA_STATUS_ADP_ABORTED :
+ case HA_STATUS_NO_FIRM :
+ case HA_STATUS_NOT_TARGET :
+ case HA_STATUS_INVALID_LINK : /* These aren't expected. */
+ case HA_STATUS_BAD_CBLOCK :
+ case HA_STATUS_DUP_CBLOCK :
+ case HA_STATUS_BAD_SCATHER :
+ case HA_STATUS_RSENSE_FAIL :
+ case HA_STATUS_TAG_REJECT :
+ case HA_STATUS_HARD_ERROR :
+ case HA_STATUS_TARGET_NOATTN :
+ case HA_STATUS_HOST_RESET :
+ case HA_STATUS_OTHER_RESET :
+ case HA_STATUS_PROG_BAD_SUM :
+ default :
+ printf("aha: bad ha_status (x%x)\n", _erccb->status.ha_status);
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ }
+
+ eaha->has_sense_info [tgt->target_id] =
+ (tgt->done == SCSI_RET_NEED_SENSE) ;
+ if (eaha->has_sense_info [tgt->target_id])
+ eaha->sense_info_lun [tgt->target_id] = tgt->lun ;
+
+ LOG(8,"end");
+
+ simple_lock(&eaha->aha_lock);
+ if (eaha->wd.nactive-- == 1)
+ eaha->wd.watchdog_state = SCSI_WD_INACTIVE;
+ simple_unlock(&eaha->aha_lock);
+
+ if (tgt->ior) {
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+
+ return FALSE;/*lint: Always returns FALSE. ignored. */
+}
+
+#endif /* NEAHA > 0 */
diff --git a/scsi/adapters/scsi_dma.h b/scsi/adapters/scsi_dma.h
new file mode 100644
index 0000000..9401a16
--- /dev/null
+++ b/scsi/adapters/scsi_dma.h
@@ -0,0 +1,150 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS AS-IS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_dma.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 7/91
+ *
+ * DMA operations that an HBA driver might invoke.
+ *
+ */
+
+/*
+ * This defines much more than usually needed, mainly
+ * to cover for the case of no DMA at all and/or only
+ * DMA from/to a specialized buffer ( which means the
+ * CPU has to copy data into/outof it ).
+ */
+
+typedef struct {
+ opaque_t (*init)(
+ int dev_unit,
+ vm_offset_t base,
+ int *dma_bsizep,
+ boolean_t *oddbp);
+
+ void (*new_target)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ void (*map)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ int (*start_cmd)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ void (*end_xfer)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ void (*end_cmd)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ io_req_t ior);
+
+ int (*start_datain)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ int (*start_msgin)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ void (*end_msgin)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ boolean_t (*start_dataout)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ volatile unsigned *regp,
+ unsigned value,
+ unsigned char *prefetch_count);
+
+ int (*restart_datain_1)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ int (*restart_datain_2)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ void (*restart_datain_3)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ int (*restart_dataout_1)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ int (*restart_dataout_2)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ int (*restart_dataout_3)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ volatile unsigned *regp);
+
+ void (*restart_dataout_4)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ boolean_t (*disconn_1)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ boolean_t (*disconn_2)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+ boolean_t (*disconn_3)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ boolean_t (*disconn_4)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ boolean_t (*disconn_5)(
+ opaque_t dma_state,
+ target_info_t *tgt,
+ int xferred);
+
+ void (*disconn_callback)(
+ opaque_t dma_state,
+ target_info_t *tgt);
+
+} scsi_dma_ops_t;
+
diff --git a/scsi/adapters/scsi_user_dma.c b/scsi/adapters/scsi_user_dma.c
new file mode 100644
index 0000000..5fb98d6
--- /dev/null
+++ b/scsi/adapters/scsi_user_dma.c
@@ -0,0 +1,171 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_user_dma.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 4/91
+ *
+ * Mach 2.5 compat file, to handle case of DMA to user space
+ * [e.g. fsck and other raw device accesses]
+ */
+
+#ifdef MACH_KERNEL
+/* We do not need this in 3.0 */
+#else /*MACH_KERNEL*/
+
+#include <mach/std_types.h>
+#include <scsi/adapters/scsi_user_dma.h>
+
+#include <kern/assert.h>
+
+#include <vm/vm_kern.h>
+#include <mach/vm_param.h> /* round_page() */
+
+/* bp -> pmap */
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+/*
+ * Initialization, called once per device
+ */
+fdma_init(fdma, size)
+ fdma_t fdma;
+ vm_size_t size;
+{
+ vm_offset_t addr;
+
+ size = round_page(size);
+ addr = kmem_alloc_pageable(kernel_map, size);
+ if (addr == 0) panic("fdma_init");
+
+ fdma->kernel_virtual = addr;
+ fdma->max_data = size;
+ fdma->user_virtual = -1;
+
+}
+
+/*
+ * Remap a buffer from user space to kernel space.
+ * Note that physio() has already validated
+ * and wired the user's address range.
+ */
+fdma_map(fdma, bp)
+ fdma_t fdma;
+ struct buf *bp;
+{
+ pmap_t pmap;
+ vm_offset_t user_addr;
+ vm_size_t size;
+ vm_offset_t kernel_addr;
+ vm_offset_t off;
+ vm_prot_t prot;
+
+ /*
+ * If this is not to user space, or no data xfer is
+ * involved, no need to do anything.
+ */
+ user_addr = (vm_offset_t)bp->b_un.b_addr;
+ if (!(bp->b_flags & B_PHYS) || (user_addr == 0)) {
+ fdma->user_virtual = -1;
+ return;
+ }
+ /*
+ * We are going to clobber the buffer pointer, so
+ * remember what it was to restore it later.
+ */
+ fdma->user_virtual = user_addr;
+
+ /*
+ * Account for initial offset into phys page
+ */
+ off = user_addr - trunc_page(user_addr);
+
+ /*
+ * Check xfer size makes sense, note how many pages we'll remap
+ */
+ size = bp->b_bcount + off;
+ assert((size <= fdma->max_data));
+ fdma->xfer_size_rnd = round_page(size);
+
+ pmap = bp->b_proc->task->map->pmap;
+
+ /*
+ * Use minimal protection possible
+ */
+ prot = VM_PROT_READ;
+ if (bp->b_flags & B_READ)
+ prot |= VM_PROT_WRITE;
+
+ /*
+ * Loop through all phys pages, taking them from the
+ * user pmap (they are wired) and inserting them into
+ * the kernel pmap.
+ */
+ user_addr -= off;
+ kernel_addr = fdma->kernel_virtual;
+ bp->b_un.b_addr = (char *)kernel_addr + off;
+
+ for (size = fdma->xfer_size_rnd; size; size -= PAGE_SIZE) {
+ register vm_offset_t phys;
+
+ phys = pmap_extract(pmap, user_addr);
+ pmap_enter(kernel_pmap, kernel_addr, phys, prot, TRUE);
+ user_addr += PAGE_SIZE;
+ kernel_addr += PAGE_SIZE;
+ }
+}
+
+/*
+ * Called at end of xfer, to restore the buffer
+ */
+fdma_unmap(fdma, bp)
+ fdma_t fdma;
+ struct buf *bp;
+{
+ register vm_offset_t end_addr;
+
+ /*
+ * Check we actually did remap it
+ */
+ if (fdma->user_virtual == -1)
+ return;
+
+ /*
+ * Restore the buffer
+ */
+ bp->b_un.b_addr = (char *)fdma->user_virtual;
+ fdma->user_virtual = -1;
+
+ /*
+ * Eliminate the mapping, pmap module might mess up
+ * the pv list otherwise. Some might actually tolerate it.
+ */
+ end_addr = fdma->kernel_virtual + fdma->xfer_size_rnd;
+ pmap_remove(kernel_pmap, fdma->kernel_virtual, end_addr);
+
+}
+
+#endif /*MACH_KERNEL*/
diff --git a/scsi/adapters/scsi_user_dma.h b/scsi/adapters/scsi_user_dma.h
new file mode 100644
index 0000000..ff2682c
--- /dev/null
+++ b/scsi/adapters/scsi_user_dma.h
@@ -0,0 +1,47 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * File: scsi_user_dma.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 4/91
+ *
+ * Defines for Mach 2.5 compat, user-space DMA routines
+ */
+
+/* There is one such structure per I/O device
+ that needs to xfer data to/from user space */
+
+typedef struct fdma {
+ vm_offset_t kernel_virtual;
+ vm_size_t max_data;
+ vm_offset_t user_virtual;
+ int xfer_size_rnd;
+} *fdma_t;
+
+extern int
+ fdma_init(/* fdma_t, vm_size_t */),
+ fdma_map(/* fdma_t, struct buf* */),
+ fdma_unmap(/* fdma_t, struct buf* */);