summaryrefslogtreecommitdiff
path: root/scsi/adapters/scsi_7061_hdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'scsi/adapters/scsi_7061_hdw.c')
-rw-r--r--scsi/adapters/scsi_7061_hdw.c2603
1 files changed, 2603 insertions, 0 deletions
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
+