/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * MkLinux
 */
/* CMU_HIST */
/*
 * Revision 2.14.2.1  92/03/28  10:14:58  jeffreyh
 * 	Pick up changes from MK71
 * 	[92/03/20  13:31:03  jeffreyh]
 * 
 * Revision 2.15  92/02/23  22:43:55  elf
 * 	Reflected changes in interface functions: probe_target,
 * 	go and restart-callup routines do not take a scsi_softc
 * 	descriptor any more.
 * 	[92/02/22  19:35:41  af]
 * 
 * Revision 2.14  91/10/09  16:15:50  af
 * 	 Revision 2.13.1.1  91/09/22  14:56:46  rpd
 * 	 	Fix from Sandro for a race in sii_dosynch.
 * 	 	[91/09/18            rpd]
 * 
 * Revision 2.13.1.1  91/09/22  14:56:46  rpd
 * 	Fix from Sandro for a race in sii_dosynch.
 * 	[91/09/18            rpd]
 * 
 * Revision 2.13  91/08/24  12:26:00  af
 * 	Padding of regmap is done here.  Renames for uniformity.
 * 	Do the synch neg on enquiry too.  Disabled answering to
 * 	selection, we live on the same bus with other machines
 * 	now.
 * 	[91/08/02  04:20:22  af]
 * 
 * Revision 2.12  91/06/25  20:55:51  rpd
 * 	Tweaks to make gcc happy.
 * 
 * Revision 2.11  91/06/19  16:29:16  rvb
 * 	File moved here from mips/PMAX since it is now "MI" code, also
 * 	used by Vax3100 and soon -- the omron luna88k.
 * 	[91/06/04            rvb]
 * 
 * Revision 2.10  91/05/14  17:29:24  mrt
 * 	Correcting copyright
 * 
 * Revision 2.9  91/05/13  06:05:20  af
 * 	Removed restriction on max read/write xfers, with noticeable
 * 	performance benefits.
 * 	Handle odd-byte-boundary conditions, unified dma_out routine,
 * 	removed useless counters but kept a watchout on sii_wait,
 * 	Added functions to copy data more carefully between main
 * 	memory and the sii buffer, added back (sigh) sii_err_phase_mismatch
 * 	to cope with bogus no-init-to-prom problem [I know it just screws
 * 	the fifo, but how can I clean it ?].
 * 
 * 	Fixed one more race in sii_dosynch().
 * 
 * 	Added disconnect-reconnect capability.  Tapes work.
 * 	Also:  added watchdog() to cope with hitachi disk problems,
 * 	rid of thread_block()ing and do proper scsi bus allocation
 * 	among multiple targets in FIFO order, fixed the way we do
 * 	the synch negotiation, made no_synch option work for real,
 * 	added debug and tracing and stats, added understanding of
 * 	scsi bus resets, added support for >1Gb disks, rid of a
 * 	lot of crap in the generic error handler, guard fiercely
 * 	against re-entering the interrupt routine, watched many
 * 	incredible races happen, do command chaining in select phase
 * 	which saves an interrupt, generally try to do as much work
 * 	as possible on each interrupt to minimize their number,
 * 	get status properly and do request_sense if bad, 
 * 	checked we understand RZ56, WrenV, Hitachi, HP, TK50, Exabyte,
 * 	[91/05/12  16:17:47  af]
 * 
 * Revision 2.7.2.3  91/04/05  13:22:43  af
 * 	Removed restriction on max read/write xfers, with noticeable
 * 	performance benefits.
 * 	Handle odd-byte-boundary conditions, unified dma_out routine,
 * 	removed useless counters but kept a watchout on sii_wait,
 * 	Added functions to copy data more carefully between main
 * 	memory and the sii buffer, added back (sigh) sii_err_phase_mismatch
 * 	to cope with bogus no-init-to-prom problem [I know it just screws
 * 	the fifo, but how can I clean it ?].
 * 
 * Revision 2.7.2.2  91/03/30  12:29:26  af
 * 	Fixed one more race in sii_dosynch().
 * 	[91/03/30            af]
 * 
 * Revision 2.7.2.1  91/03/29  16:56:47  af
 * 	Added disconnect-reconnect capability.  Tapes work.
 * 	Also:  added watchdog() to cope with hitachi disk problems,
 * 	rid of thread_block()ing and do proper scsi bus allocation
 * 	among multiple targets in FIFO order, fixed the way we do
 * 	the synch negotiation, made no_synch option work for real,
 * 	added debug and tracing and stats, added understanding of
 * 	scsi bus resets, added support for >1Gb disks, rid of a
 * 	lot of crap in the generic error handler, guard fiercely
 * 	against re-entering the interrupt routine, watched many
 * 	incredible races happen, do command chaining in select phase
 * 	which saves an interrupt, generally try to do as much work
 * 	as possible on each interrupt to minimize their number,
 * 	get status properly and do request_sense if bad, 
 * 	checked we understand RZ56, WrenV, Hitachi, HP, TK50, Exabyte,
 * 
 * Revision 2.7  91/02/14  14:37:18  mrt
 * 	In interrupt routine, drop priority as now required.
 * 	[91/02/12  13:18:18  af]
 * 
 * Revision 2.6  91/02/05  17:45:19  mrt
 * 	Added author notices
 * 	[91/02/04  11:18:59  mrt]
 * 
 * 	Changed to use new Mach copyright
 * 	[91/02/02  12:17:41  mrt]
 * 
 * Revision 2.5  91/01/08  15:48:33  rpd
 * 	Catched hitachi's problem: spurious interrupt _before_
 * 	dmain/out even got started.
 * 	[90/12/29            af]
 * 	Added continuation argument to thread_block.
 * 	[90/12/27            rpd]
 * 
 * Revision 2.4  90/12/20  17:03:53  jeffreyh
 * 	More hacks to understand rz55, and now hitachis boot.
 * 	There is still one problem for hitachis at large,
 * 	will fix in the next merge.
 * 	[90/12/19  15:17:48  af]
 * 
 * Revision 2.3  90/12/05  23:34:54  af
 * 	Works, but in a crazy-horse-like fashion.  Known problems:
 * 	- phase changes are apparently unreliable, needs sometimes
 * 	  a kick
 * 	- spurious 'bus error' interrupts from chip confuse the issue
 * 	- two big spin loops are still there
 * 	- repeatedly interrupted DMA xfers can loop (happens if you
 * 	  do not "init" the prom).
 * 	Missing features:
 * 	- should disconnect when more than one target on the bus
 * 	- do chaining of select&identify-msg.
 * 	[90/12/03  23:46:05  af]
 * 
 * Revision 2.1.1.1  90/11/01  03:39:29  af
 * 	Created, from the DEC specs:
 * 	"DECstation 3100 Desktop Workstation Functional Specification"
 * 	Workstation Systems Engineering, Palo Alto, CA. Aug 28, 1990.
 * 	[90/10/17            af]
 */
/* CMU_ENDHIST */
/* 
 * 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/machparam.h>		/* spl definitions */
#include <mach/std_types.h>
#include <sys/types.h>
#include <chips/busses.h>
#include <scsi/compat_30.h>
#include <machine/machparam.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 */
	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(), scsi_attach(), sii_go(), sii_intr();

caddr_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	0x24
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);
				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);
		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);
		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);
		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);

		dsr = sii_wait(&regs->sii_data_csr, SCSI_PHASE_MSG_IN);
		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);
		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)
	volatile unsigned short	*preg;
{
	int timeo = 1000000;
	while ((*preg & until) != until) {
		delay(1);
		if (!timeo--) {
			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);

	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);
	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);
	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;

	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);
	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->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->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);

	/* ?? */
	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);

	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 = tgt->transient_state.handler;
	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);
	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);

	/* 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);
		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);
	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);
	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);

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);

	regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN;
	wbflush();
	ds = sii_wait(&regs->sii_data_csr, SII_DTR_DONE);

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;
	}

	/* 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);
/* 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);
			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;
}

/*
 * 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(0x22,"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

