/* 
 * Mach Operating System
 * Copyright (c) 1992 Carnegie Mellon University
 * Copyright (c) 1992 Helsinki University of Technology
 * 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 AND HELSINKI UNIVERSITY OF TECHNOLOGY ALLOW FREE USE
 * OF THIS SOFTWARE IN ITS "AS IS" CONDITION.  CARNEGIE MELLON AND
 * HELSINKI UNIVERSITY OF TECHNOLOGY DISCLAIM ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon 
 * the rights to redistribute these changes.
 */
/*
 * HISTORY
 * 04-Aug-92  Ian Dall (ian) at University of Adelaide
 *	Clear interrupts immediately after phase mismatch interrupts are
 *	enabled in sdp_complete_arbitration.
 *
 * 03-Aug-92  Ian Dall (ian) at University of Adelaide
 *	There is no point in waiting for bus free in sdp_end_transaction()
 *	a) we might have missed it in a multi-initiator environment
 *	b) we shouldn't need to wait anyway.
 *	
 *	Don't mask interrupts in sdp_release_bus() since we do it in
 *	sdp_end_transaction().
 *
 * 03-Aug-92  Ian Dall (ian) at University of Adelaide
 *	Don't enable phase mis-match interrupts until selection
 *	successfully completed.
 *
 * 02-Aug-92  Ian Dall (ian) at University of Adelaide
 *	Remove spin wait for bus free in sdp_start_selection. Enhanced
 *	mode arbitration is supposed to do this for us.
 *
 * 01-Aug-92  Ian Dall (ian) at University of Adelaide
 *	Disable interrupts from everything except select when a command
 *	completes. We don't want to react to phase changes etc for other
 *	initiators.
 *
 * 01-Aug-92  Ian Dall (ian) at University of Adelaide
 *	Changed do_sdp_intr() to make full use of the enhanced mode ISR
 *	register.
 *
 * 31-Jul-92  Ian Dall (ian) at University of Adelaide
 *	Check more carefully for phase changes in data_in() and
 *	data_out(). Previously a phase change while waiting for REQ would
 *	result in an extra ACK being sent.
 *
 * 31-Jul-92  Ian Dall (ian) at University of Adelaide
 *	Check that a selection/reselection is for *this* initiator before
 *	proceeding.
 *	 
 * 11-May-92  Johannes Helander (jvh) at Helsinki University of Technology
 *	Created.
 *
 * $Log: scsi_dp8490_hdw.c,v $
 */
/*
 * 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
 *
 */

/*
 * 	File: scsi_dp8490_hdw.c
 *
 * 	Author: Johannes Helander, Helsinki University of Technology 1992.
 *
 * 	From spec: 	National Semiconductor
 *			DP8490 Enhanced Asynchronous SCSI Interface (EASI)
 *			PRELIMINARY, January 1989
 *
 * 	Based on the NCR 5380 driver by Alessandro Forin and the
 *	Fujitsu MB89352 driver by Daniel Stodolsky.
 */

/* This driver still needs work */

#define SDP_SOFT_DEGLITCH	/* Do de-glitching in software */

#include "sdp.h"
#include "scsi.h"
#include "mach_kdb.h"

#if	NSDP > 0
#include <platforms.h>

#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 DP(level, action) do { action; } while(0)

#include <scsi/adapters/scsi_dp8490.h>
#include <ns532/PC532/board.h>

#ifdef	PAD
typedef struct {
	volatile unsigned char	sdp_data;	/* r:  Current data */
/*#define		sdp_odata sdp_data	/* w:  Out data */
	PAD(pad0);

	volatile unsigned char	sdp_icmd;	/* rw: Initiator command */
	PAD(pad1);

	volatile unsigned char	sdp_mode;	/* rw: Mode */
	PAD(pad2);

	volatile unsigned char	sdp_tcmd;	/* rw: Target command */
	PAD(pad3);

	volatile unsigned char	sdp_bus_csr;	/* r:  Bus Status */
/*#define		sdp_sel_enb sdp_bus_csr	/* w:  Select enable */
	PAD(pad4);

	volatile unsigned char	sdp_csr;	/* r:  Status */
/*#define		sdp_dma_send sdp_csr	/* w:  Start dma send data */
	PAD(pad5);

	volatile unsigned char	sdp_idata;	/* r:  Input data */
/*#define		sdp_trecv sdp_idata	/* w:  Start dma receive, target */
	PAD(pad6);

	volatile unsigned char	sdp_iack;	/* r:  Interrupt Acknowledge  */
/*#define		sdp_irecv sdp_iack	/* w:  Start dma receive, initiator */
	PAD(pad7);

} sdp_padded_regmap_t;
#else
typedef sdp_regmap_t	sdp_padded_regmap_t;
#endif

#define delay_1p2_us()	delay(1)

/*
 * The 5380 can't tell you the scsi ID it uses, so
 * unless there is another way use the defaults
 */
#ifndef	my_scsi_id
#define	my_scsi_id(ctlr)	(scsi_initiator_id[(ctlr)])
#endif

/*
 * Statically partition the DMA buffer between targets.
 * This way we will eventually be able to attach/detach
 * drives on-fly.  And 18k/target is enough.
 */
#define PER_TGT_DMA_SIZE		((SDP_RAM_SIZE/7) & ~(sizeof(int)-1))

#ifdef PC532
/* Don't align; we like hard debugging and small buffers */
#define PER_TGT_BUFF_SIZE		(PER_TGT_DMA_SIZE)
#else PC532
/*
 * Round to 4k to make debug easier
 */
#define	PER_TGT_BUFF_SIZE		((PER_TGT_DMA_SIZE >> 12) << 12)
#define PER_TGT_BURST_SIZE		(PER_TGT_BUFF_SIZE>>1)
#endif PC532

/*
 * Macros to make certain things a little more readable
 */
#define	SDP_ACK(ptr,phase)	(ptr)->sdp_tcmd = (phase)

#define	SDP_CLR_INTR(regs) \
    MACRO_BEGIN \
    register int temp; \
    regs->sdp_emr |= SDP_EMR_FN_INT; \
    temp = regs->sdp_istatus; \
    regs->sdp_emr |= SDP_EMR_FN_RPI; \
    regs->sdp_emr |= SDP_EMR_FN_NOP; \
    MACRO_END

#define SDP_ISR_GET(regs, value) \
    MACRO_BEGIN \
    regs->sdp_emr |= SDP_EMR_FN_INT; \
    value = regs->sdp_istatus; \
    MACRO_END

#if NSDP == 1
char xxx_sdp_imr = 0;
#else
-- Do it some better way. --
#endif

#define SDP_IMR_PUT(regs, value) \
    MACRO_BEGIN \
    regs->sdp_emr |= SDP_EMR_FN_INT; \
    regs->sdp_imask = (value); \
    xxx_sdp_imr = (value); \
    MACRO_END

#define SDP_IMR_GET(regs, value) \
    MACRO_BEGIN \
    value = xxx_sdp_imr; \
    MACRO_END

/*
 * A script has a two parts: a pre-condition and an action.
 * The first triggers error handling if not satisfied and in
 * our case it is formed by the current bus phase and connected
 * condition as per bus status bits.  The action part is just a
 * function pointer, invoked in a standard way.  The script
 * pointer is advanced only if the action routine returns TRUE.
 * See sdp_intr() for how and where this is all done.
 */

typedef struct script {
	int	condition;	/* expected state at interrupt */
	int	(*action)();	/* action routine */
} *script_t;

#define	SCRIPT_MATCH(cs,bs)	(((bs)&SDP_BUS_BSY)|SDP_CUR_PHASE((bs)))

#define	SDP_PHASE_DISC	0x0	/* sort of .. */


/* forward decls of script actions */
boolean_t
  	sdp_issue_command(),
	sdp_xfer_in(),
	sdp_xfer_out(),
	sdp_get_status(),		/* get status from target */
	sdp_end_transaction(),		/* all come to an end */
	sdp_msg_in(),			/* get disconnect message(s) */
	sdp_disconnected();		/* current target disconnected */
/* forward decls of error handlers */
boolean_t
	sdp_err_generic(),		/* generic error handler */
	sdp_err_disconn();		/* when a target disconnects */

int	sdp_reset_scsibus();
boolean_t sdp_probe_target();

scsi_ret_t	sdp_select_target();
scsi_ret_t	sdp_start_select_target();
scsi_ret_t	sdp_complete_selection();

/*
 * This should be somewhere else, and it was a
 * mistake to share this buffer across SCSIs.
 */
struct dmabuffer {
	volatile char	*base;
	char		*sbrk;
} dmab[1];

volatile char *
sdp_buffer_base(unit)
{
	return dmab[unit].base;
}

sdp_buffer_init(ram)
	volatile char	*ram;
{
	dmab[0].base = dmab[0].sbrk = (char *) ram;
	bzero((char *) ram, SDP_RAM_SIZE);
}

char *
sdp_buffer_sbrk(size)
{
	char	*ret = dmab[0].sbrk;

	dmab[0].sbrk += size;
	if ((dmab[0].sbrk - dmab[0].base) > SDP_RAM_SIZE)
		panic("sdpalloc");
	return ret;
}

/*
 * State descriptor for this layer.  There is one such structure
 * per (enabled) 5380 interface
 */
struct sdp_softc {
	watchdog_t	wd;
	sdp_padded_regmap_t	*regs;		/* 5380 registers */
	volatile char	*buff;		/* DMA buffer memory (I/O space) */
	script_t	script;
	int		(*error_handler)();
	int		in_count;	/* amnt we expect to receive */
	int		out_count;	/* amnt we are going to ship */

	volatile char	state;
#define	SDP_STATE_BUSY		0x01	/* selecting or currently connected */
#define SDP_STATE_TARGET	0x04	/* currently selected as target */
#define SDP_STATE_COLLISION	0x08	/* lost selection attempt */
#define SDP_STATE_DMA_IN	0x10	/* tgt --> initiator xfer */

	unsigned char	ntargets;	/* how many alive on this scsibus */
	unsigned char	done;

	scsi_softc_t	*sc;
	target_info_t	*active_target;

	target_info_t	*next_target;	/* trying to seize bus */
	queue_head_t	waiting_targets;/* other targets competing for bus */

} sdp_softc_data[NSCSI];

typedef struct sdp_softc *sdp_softc_t;

sdp_softc_t	sdp_softc[NSCSI];

/*
 * Definition of the controller for the auto-configuration program.
 */

int	sdp_probe(), scsi_slave(), scsi_attach(), sdp_go(), sdp_intr();

caddr_t	sdp_std[NSCSI] = { 0 };
struct	bus_device *sdp_dinfo[NSCSI*8];
struct	bus_ctlr *sdp_minfo[NSCSI];
struct	bus_driver sdp_driver = 
        { sdp_probe, scsi_slave, scsi_attach, sdp_go, sdp_std, "rz", sdp_dinfo,
	  "sdp", sdp_minfo, BUS_INTR_B4_PROBE};

/*
 * Scripts
 */
struct script
sdp_script_data_in[] = {
        { SCSI_PHASE_CMD|SDP_BUS_BSY, sdp_issue_command},
        { SCSI_PHASE_DATAI|SDP_BUS_BSY, sdp_xfer_in},
        { SCSI_PHASE_STATUS|SDP_BUS_BSY, sdp_get_status},
        { SCSI_PHASE_MSG_IN|SDP_BUS_BSY, sdp_end_transaction}
},

sdp_script_data_out[] = {
        { SCSI_PHASE_CMD|SDP_BUS_BSY, sdp_issue_command},
        { SCSI_PHASE_DATAO|SDP_BUS_BSY, sdp_xfer_out},
        { SCSI_PHASE_STATUS|SDP_BUS_BSY, sdp_get_status},
        { SCSI_PHASE_MSG_IN|SDP_BUS_BSY, sdp_end_transaction}
},

sdp_script_cmd[] = {
        { SCSI_PHASE_CMD|SDP_BUS_BSY, sdp_issue_command},
        { SCSI_PHASE_STATUS|SDP_BUS_BSY, sdp_get_status},
        { SCSI_PHASE_MSG_IN|SDP_BUS_BSY, sdp_end_transaction}
},

/* Disconnect sequence */

sdp_script_disconnect[] = {
	{ SDP_PHASE_DISC, sdp_disconnected}
};



#define	u_min(a,b)	(((a) < (b)) ? (a) : (b))

#if MACH_KDB

sdp_state(base)
	vm_offset_t	base;
{
	sdp_padded_regmap_t	*regs;
	extern char 	*sdp;
	unsigned	dmadr;
	int		cnt, i, old;
	
	if (base == 0)
		base = (vm_offset_t)sdp;

	regs = (sdp_regmap_t *)base;
	old = PC532_SCSI_SELECT(ICU_DP);

	db_printf("scsi(dp): ph %x (sb %x), mode %x, tph %x, csr %x, cmd %x, ",
		  (unsigned) SDP_CUR_PHASE(regs->sdp_bus_csr),
		  (unsigned) regs->sdp_bus_csr,
		  (unsigned) regs->sdp_mode,
		  (unsigned) regs->sdp_tcmd,
		  (unsigned) regs->sdp_csr,
		  (unsigned) regs->sdp_icmd);
	PC532_SCSI_SELECT(old);
	return 0;
}
sdp_target_state(tgt)
	target_info_t		*tgt;
{
	if (tgt == 0)
		tgt = sdp_softc[0]->active_target;
	if (tgt == 0)
		return 0;
	db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n",
		tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset,
		tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
		tgt->ior, tgt->done);
	if (tgt->flags & TGT_DISCONNECTED){
		script_t	spt;

		spt = tgt->transient_state.script;
		db_printf("disconnected at ");
		db_printsym(spt,1);
		db_printf(": %x ", spt->condition);
		db_printsym(spt->action,1);
		db_printf(", ");
		db_printsym(tgt->transient_state.handler, 1);
		db_printf("\n");
	}

	return 0;
}

sdp_all_targets(unit)
{
	int i;
	target_info_t	*tgt;
	for (i = 0; i < 8; i++) {
		tgt = sdp_softc[unit]->sc->target[i];
		if (tgt)
			sdp_target_state(tgt);
	}
}

sdp_script_state(unit)
{
	script_t	spt = sdp_softc[unit]->script;

	if (spt == 0) return 0;
	db_printsym(spt,1);
	db_printf(": %x ", spt->condition);
	db_printsym(spt->action,1);
	db_printf(", ");
	db_printsym(sdp_softc[unit]->error_handler, 1);
	return 0;

}

#define	PRINT(x)	if (scsi_debug) printf x

#define TRMAX 200
int tr[TRMAX+10];
char *trname[TRMAX+10];
int trpt, trpthi;
#define	TR(x)	do { trname[trpt] = "?"; tr[trpt++] = x; } while(0)
#define TRWRAP	trpthi = trpt; trpt = 0;
#define TRCHECK	if (trpt > TRMAX) {TRWRAP}

#define	TR2(name, x)	do { trname[trpt] = name; tr[trpt++] = x; } while(0)

sdp_print_trace(skip)
	int skip;
{
	int i;
	
	db_printf ("trpthi = 0x%x, trpt = 0x%x\n", trpthi, trpt);
	for (i = trpt; i < trpthi; i++) {
		if (skip > 0)
		    skip--;
		else
		    db_printf ("%x\t%s\t%x\n", i, trname[i], tr[i]);
	}
	for (i = 0; i < trpt; i++) {
		if (skip > 0)
		    skip--;
		else
		    db_printf ("%x\t%s\t%x\n", i, trname[i], tr[i]);
	}
}

#define TRACE

#ifdef TRACE

#define LOGSIZE 256
int sdp_logpt;
char sdp_log[LOGSIZE];

/* #define MAXLOG_VALUE	0x24 */
#define MAXLOG_VALUE 0x7f

struct {
	char *name;
	unsigned int count;
} logtbl[MAXLOG_VALUE];

static LOG(e,f)
	char *f;
{
	sdp_log[sdp_logpt++] = (e);
	if (sdp_logpt == LOGSIZE) sdp_logpt = 0;
	if ((e) < MAXLOG_VALUE) {
		logtbl[(e)].name = (f);
		logtbl[(e)].count++;
	}
}

sdp_print_log(skip)
	int skip;
{
	register int i, j;
	register unsigned char c;

	for (i = 0, j = sdp_logpt; i < LOGSIZE; i++) {
		c = sdp_log[j];
		if (++j == LOGSIZE) j = 0;
		if (skip-- > 0)
			continue;
		if (c < MAXLOG_VALUE)
			db_printf(" %s", logtbl[c].name);
		else
			db_printf("-%d", c & 0x7f);
	}
	db_printf("\n");
	return 0;
}

sdp_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 */

/* Call this from kdb */
sdp_padded_regmap_t	*last_sc = 0;

sdp_dump()
{
	int old;

	old = PC532_SCSI_SELECT(ICU_DP);

	if (last_sc) {
		int foo;
		db_printf("\n");
		db_printf("CSD	0x%x\n", last_sc->sdp_data);
		db_printf("ICR	0x%x\n", last_sc->sdp_icmd);
		db_printf("MR2	0x%x\n", last_sc->sdp_mode);
		db_printf("TCR	0x%x\n", last_sc->sdp_tcmd);
		db_printf("CSB	0x%x\n", last_sc->sdp_bus_csr);
		db_printf("BSR	0x%x\n", last_sc->sdp_csr);
		db_printf("IDR	0x%x\n", last_sc->sdp_idata);
		db_printf("EMR	0x%x\n", last_sc->sdp_emr);
		SDP_ISR_GET (last_sc, foo);
		db_printf("ISR	0x%x\n", foo);
	}
	PC532_SCSI_SELECT(old);
}

#else	MACH_KDB

#define	PRINT(x)
#define	LOG(e,f)
#define TR(x)
#define TR2(x,y)
#define TRCHECK
#define TRWRAP

#endif	MACH_KDB


sdp_break()
{
}
sdp_break1()
{
}
sdp_break2()
{
}
sdp_break3()
{
}
sdp_break4()
{
}
sdp_break5()
{
}

/*
 *	Probe/Slave/Attach functions
 */

int sdp_skip_target = 0xef;
int sdp_delay_after_intr = 0;	/* XXX */

/*
 * 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.
 */
sdp_probe(reg, ui)
	char		*reg;
	struct bus_ctlr	*ui;
{
	int             unit = ui->unit;
	sdp_softc_t     sdp = &sdp_softc_data[unit];
	int		target_id, i;
	scsi_softc_t	*sc;
	register sdp_padded_regmap_t	*regs;
	int		s, old;
	boolean_t	did_banner = FALSE;
	char		*cmd_ptr;
	static char	*here = "sdp_probe";

	/*
	 * We are only called if the chip is there,
	 * but make sure anyways..
	 */
	regs = (sdp_padded_regmap_t *) (reg);

#if MACH_KDB
	last_sc = regs;
#endif MACH_KDB

#if	notyet
	/* Mappable version side */
	SDP_probe(reg, ui);
#endif

	/*
	 * Initialize hw descriptor
	 */
	sdp_softc[unit] = sdp;
	sdp->regs = regs;

	sdp->buff = sdp_buffer_base(0);

	queue_init(&sdp->waiting_targets);

	sc = scsi_master_alloc(unit, sdp);
	sdp->sc = sc;

	sc->go = sdp_go;
	sc->probe = sdp_probe_target;
	sc->watchdog = scsi_watchdog;
	sdp->wd.reset = sdp_reset_scsibus;

#ifdef	MACH_KERNEL
	sc->max_dma_data = -1;				/* unlimited */
#else
	sc->max_dma_data = scsi_per_target_virtual;
#endif

	/*
	 * Reset chip
	 */
#if defined(PC532)
	s = splhi();
#else
	s = splbio();
#endif PC532
	old = PC532_SCSI_SELECT(ICU_DP);

	sdp_reset(sdp, TRUE);
	SDP_CLR_INTR(regs);

	/*
	 * Our SCSI id on the bus.
	 */

	sc->initiator_id = my_scsi_id(unit);
	printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);

	/*
	 * For all possible targets, see if there is one and allocate
	 * a descriptor for it if it is there.
	 */
	cmd_ptr = sdp_buffer_sbrk(0);

	for (target_id = 0; target_id < 8; target_id++) {
		register unsigned csr, dsr;
		scsi_status_byte_t	status;

		/* except of course ourselves */
		if (target_id == sc->initiator_id)
			continue;

		if (sdp_skip_target & (1 << target_id))
		{
			printf ("sdp: target id %d will not be probed\n",
				target_id);
			continue;
		}

		if (sdp_select_target (regs,
				       sc->initiator_id,
				       target_id,
				       FALSE) == SCSI_RET_DEVICE_DOWN) {
			SDP_CLR_INTR(regs);
			SDP_CLR_INTR(regs);
			continue;
		}

		printf(",%s%d", did_banner++ ? " " : " target(s) at ",
				target_id);

		/* should be command phase here: we selected wo ATN! */
		while (SDP_CUR_PHASE(regs->sdp_bus_csr) != SCSI_PHASE_CMD)
			;

		SDP_ACK(regs,SCSI_PHASE_CMD);

		/* build command in dma area */
		{
			unsigned char	*p = (unsigned char*) cmd_ptr;

			p[0] = SCSI_CMD_TEST_UNIT_READY;
			p[1] = 
			p[2] = 
			p[3] = 
			p[4] = 
			p[5] = 0;
		}

		sdp_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr);

		while (SDP_CUR_PHASE(regs->sdp_bus_csr) != SCSI_PHASE_STATUS)
			;

		SDP_ACK(regs,SCSI_PHASE_STATUS);

		sdp_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);

		if (status.st.scsi_status_code != SCSI_ST_GOOD)
			scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);

		/* get cmd_complete message */
		while (SDP_CUR_PHASE(regs->sdp_bus_csr) != SCSI_PHASE_MSG_IN)
			;

		SDP_ACK(regs,SCSI_PHASE_MSG_IN);

		sdp_data_in(regs, SCSI_PHASE_MSG_IN, 1, &i);

		/* check disconnected, clear all intr bits */
		while (regs->sdp_bus_csr & SDP_BUS_BSY)
			;
		SDP_ACK(regs, SDP_PHASE_DISC);

		SDP_CLR_INTR(regs);

		/* ... */

		/*
		 * Found a target
		 */
		sdp->ntargets++;
		{
			register target_info_t	*tgt;

			tgt = scsi_slave_alloc(unit, target_id, sdp);

			/* "virtual" address for our use */
			tgt->cmd_ptr = sdp_buffer_sbrk(PER_TGT_DMA_SIZE);

			/* pc532 does not have dma */
			tgt->dma_ptr = (char*)0xdeadbeef;
		}
	}
	printf(".\n");

	PC532_SCSI_SELECT(old);
	splx(s);

	return 1;
}

boolean_t
sdp_probe_target(tgt, ior)
	target_info_t		*tgt;
	io_req_t		ior;
{
	sdp_softc_t     sdp = sdp_softc[tgt->masterno];
	boolean_t	newlywed;

	newlywed = (tgt->cmd_ptr == 0);
	if (newlywed) {
		/* desc was allocated afresh */

		/* "virtual" address for our use */
		tgt->cmd_ptr = sdp_buffer_sbrk(PER_TGT_DMA_SIZE);

		/* pc532 does not have dma */
		tgt->dma_ptr = (char*)0xdeadbeef;
	}

	if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
		return FALSE;

	tgt->flags = TGT_ALIVE;
	return TRUE;
}


/* XXX Nobody calls this? /jtv */
#ifndef PC532
static sdp_wait(preg, until)
	volatile unsigned char	*preg;
{
	int timeo = 1000000;
	/* read it over to avoid bus glitches */
	while ( ((*preg & until) != until) ||
		((*preg & until) != until) ||
		((*preg & until) != until)) {
		delay(1);
		if (!timeo--) {
			printf("sdp_wait TIMEO with x%x\n", *preg);
			break;
		}
	}
	return *preg;
}
#endif PC532

scsi_ret_t
sdp_select_target(regs, myid, id, with_atn)
	register sdp_padded_regmap_t	*regs;
	unsigned char		myid, id;
	boolean_t		with_atn;
{
	register unsigned char	isr, bid, icmd, imr;
	scsi_ret_t		ret = SCSI_RET_RETRY;
	scsi_ret_t 		r;
	int i;

	for (i = 0; regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL);
	     i = (i == 10)? 0: i + 1) {
	  if (i == 0)
	    DP(2, printf ("sdp: bus busy, waiting to select\n"));
	}

	/* for our purposes.. */
	myid = 1 << myid;
	id = 1 << id;

#if 0
	regs->sdp_sel_enb = myid;	/* if not there already */
#endif

#if 1
	regs->sdp_tcmd = 0;	/* in case it wasn't */
	regs->sdp_icmd = SDP_ICMD_ENHANCED;
#endif
	SDP_IMR_GET(regs, imr);
	imr &= ~(SDP_INT_PHASE | SDP_INT_ARB);
	SDP_IMR_PUT(regs, imr);

	regs->sdp_odata = myid;

	/* arbitrate in enhanced mode see figure on page 34, A1 */
	while (1) {
		regs->sdp_emr |= SDP_EMR_ARB;
		do {
			delay(1); /* save electricity and the chip's mind */
			SDP_ISR_GET(regs, isr);
		} while (!(isr & SDP_INT_ARB));

		regs->sdp_emr |= SDP_EMR_FN_RPI;
		regs->sdp_emr |= SDP_EMR_FN_NOP;

		DP(1, sdp_break());

		if (!(regs->sdp_icmd & SDP_ICMD_LST)) {
			register u_char csd;

			/* we won? */
			csd = regs->sdp_data;
			if ((myid & csd) && (myid > (csd & ~myid)))
			    break;
		}
		DP(1, printf("sdp: lost arb\n"));
		/* reset ARB since arbitration failed. see 4.4.2 */
		regs->sdp_emr = regs->sdp_emr & ~SDP_EMR_ARB;
		SDP_CLR_INTR(regs);
	}

#if 0
	icmd = regs->sdp_icmd & ~(SDP_ICMD_DIFF);
	icmd |= SDP_ICMD_ENHANCED | SDP_ICMD_SEL
	    	| (with_atn ? SDP_ICMD_ATN : 0);
#else
	icmd = SDP_ICMD_ENHANCED | SDP_ICMD_SEL 
	    | (with_atn ? SDP_ICMD_ATN : 0);
#endif
	regs->sdp_sel_enb = 0;
	regs->sdp_tcmd = 0;

	regs->sdp_icmd = icmd;

#if 0
        if (regs->sdp_icmd & SDP_ICMD_LST)
	    DP (1, printf ("sd: sdp_icmd & ICMD_LST failed\n"));
#endif

	/* XXX a target that violates specs might still drive the bus XXX */
	/* XXX should put our id out, and after the delay check nothi XXX */
	/* XXX ng else is out there.				      XXX */

	delay_1p2_us();
	
#if 0
	regs->sdp_sel_enb = 0; 
#endif

	regs->sdp_odata = myid | id;
	

	icmd |= SDP_ICMD_DATA;

	regs->sdp_icmd = icmd;

	regs->sdp_sel_enb = 0;

	regs->sdp_emr &= ~SDP_EMR_ARB;	/* 2 deskew delays, too */
	regs->sdp_mode &= ~SDP_MODE_ARB; /* shouldn't be on */

	SDP_CLR_INTR(regs);

	/* bus settle delay, 400ns */
	delay(0); /* too much ? */

	{
		/* 250 msecs in 100 usecs chunks */
		register int timeo  = 5000; /* 2500 XXX */
		while ((regs->sdp_bus_csr & SDP_BUS_BSY) == 0)
			if (--timeo > 0)
				delay(100);
			else {
				goto nodev;
			}
	}

	icmd &= ~(SDP_ICMD_DATA | SDP_ICMD_SEL);
	regs->sdp_icmd = icmd;
	DP(1, sdp_break());
	return SCSI_RET_SUCCESS;

nodev:
	DP(2, printf("sdp: selection failed (1)\n"));
	regs->sdp_sel_enb = 0;	/* myid not yet */
	regs->sdp_icmd = SDP_ICMD_ENHANCED;
	regs->sdp_emr &= ~SDP_EMR_ARB;
	regs->sdp_mode &= ~SDP_MODE_ARB; /* shouldn't be on */

	return SCSI_RET_DEVICE_DOWN;
}

sdp_data_out(regs, phase, count, data)
	register sdp_padded_regmap_t	*regs;
	unsigned char		*data;
{
	register unsigned char	icmd;
	register int csr;

	/* ..checks.. */

	icmd = (regs->sdp_icmd & ~SDP_ICMD_DIFF) | SDP_ICMD_ENHANCED;
loop:
        while ((((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		== SDP_CSR_PHASE(phase))
#ifdef SDP_SOFT_DEGLITCH
	       || (((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == SDP_CSR_PHASE(phase))
	       || (((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == SDP_CSR_PHASE(phase))
#endif
	       );
	if ((csr & SDP_PHASE_MASK) != SDP_CSR_PHASE(phase)){
	  return count;
	}
	      
	icmd |= SDP_ICMD_DATA;
	regs->sdp_icmd = icmd;
	regs->sdp_odata = *data++;
	icmd |= SDP_ICMD_ACK;
	regs->sdp_icmd = icmd;

	icmd &= ~(SDP_ICMD_DATA|SDP_ICMD_ACK);
        while (((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		== (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
#ifdef SDP_SOFT_DEGLITCH
	       || ((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
	       || ((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
#endif
	       );
	regs->sdp_icmd = icmd;
	if (--count > 0)
		goto loop;
	return 0;
}

sdp_data_in(regs, phase, count, data)
	register sdp_padded_regmap_t	*regs;
	unsigned char		*data;
{
	register unsigned char	icmd;
	register int csr;
	int s;

	/* ..checks.. */

	icmd = (regs->sdp_icmd & ~SDP_ICMD_DIFF) | SDP_ICMD_ENHANCED;
	s = splhi();		/* protect against 32532 prefetch */
loop:
        while ((((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		== SDP_CSR_PHASE(phase))
#ifdef SDP_SOFT_DEGLITCH
	       || (((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == SDP_CSR_PHASE(phase))
	       || (((csr = regs->sdp_bus_csr) & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == SDP_CSR_PHASE(phase))
#endif
	       ) ;
	if ((csr & SDP_PHASE_MASK) != SDP_CSR_PHASE(phase)){
	  splx(s);
	  return count;
	}

	*data++ = regs->sdp_data;
	icmd |= SDP_ICMD_ACK;
	regs->sdp_icmd = icmd;

	icmd &= ~SDP_ICMD_ACK;
        while (((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		== (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
#ifdef SDP_SOFT_DEGLITCH
	       || ((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
	       || ((regs->sdp_bus_csr & (SDP_BUS_REQ | SDP_PHASE_MASK))
		   == (SDP_BUS_REQ | SDP_CSR_PHASE(phase)))
#endif
	       ) ;
	
	regs->sdp_icmd = icmd;
	if (--count > 0)
		goto loop;
	splx(s);
	return 0;

}

sdp_reset(sdp, quickly)
	sdp_softc_t		sdp;
	boolean_t		quickly;
{
	register sdp_padded_regmap_t	*regs = sdp->regs;
	int ss;

	ss = splhi();		/* I guess we are there already. Make sure */

	DP(1, printf("sdp: resetting chip\n"));
	regs->sdp_icmd = 0;
	regs->sdp_mode = 0;
	regs->sdp_sel_enb = 0;
	regs->sdp_bus_csr = 0;
	regs->sdp_odata = 0;
	regs->sdp_tcmd = 0;

	regs->sdp_icmd = SDP_ICMD_ENHANCED;
	SDP_CLR_INTR(regs);
	SDP_IMR_PUT(regs, 0xff); /* mask all interrupts first */
	SDP_CLR_INTR(regs);
	SDP_CLR_INTR(regs);

	regs->sdp_mode = 0;
	regs->sdp_bus_csr = 0;
	regs->sdp_icmd = SDP_ICMD_ENHANCED;
	regs->sdp_tcmd = 0;

	/* reset interrupt latch (see 3.0: ISR */
	SDP_CLR_INTR(regs);
	
	/* set interrupt mask */
#if 0
	/* 
	 * enable interrupts: 
	 * ARB 	 for arbitration
	 * PHASE for script transitions
	 * BSY	 for busy loss detection
	 */
	SDP_IMR_PUT(regs, SDP_INT_MPE | SDP_INT_PARITY | SDP_INT_SEL 
		    | SDP_INT_DMA_PHASE | SDP_INT_EDMA);
#else
	/* Enable the selection interrupts. Other interrupts enabled
         * at the start of each command and diabled at the end.
         */
	SDP_IMR_PUT(regs, ~SDP_INT_SEL );
#endif
	/* make sure we do not miss transition */
	regs->sdp_tcmd = SDP_PHASE_DISC;

	/* don't listen to target intrs */
	regs->sdp_sel_enb = 0;

	/* get phase mismatch signals. */
	regs->sdp_emr = SDP_EMR_PHASE_CHECK;

	/* clear interrupt (two might be queued?) */
	SDP_CLR_INTR(regs);
	SDP_CLR_INTR(regs);

	splx(ss);

        if (quickly)
                return;

        /*
         * reset the scsi bus, the interrupt routine does the rest
         * or you can call sdp_bus_reset().
         */
        regs->sdp_icmd = SDP_ICMD_RST;
}

/*
 *	Operational functions
 */

/*
 * Start a SCSI command on a target
 */
sdp_go(tgt, cmd_count, in_count, cmd_only)
	target_info_t		*tgt;
	boolean_t		cmd_only;
{
	sdp_softc_t		sdp;
	register int		s;
	boolean_t		disconn;
	boolean_t		synch;
	script_t		scp;
	boolean_t		(*handler)();
	int			late;

	LOG(1,"\ngo");

	sdp = (sdp_softc_t)tgt->hw_state;

	/*
	 * We cannot do real DMA.
	 */

	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;
#ifdef  PC532
		tgt->transient_state.copy_count = 0;
#else PC532
		if (len > PER_TGT_BUFF_SIZE)
			len = PER_TGT_BUFF_SIZE;
		bcopy(	ior->io_data,
			tgt->cmd_ptr + cmd_count,
			len);
		tgt->transient_state.copy_count = len;
#endif PC532
		/* avoid leaks */
		if (len < tgt->block_size) {
#ifndef PC532
			bzero(	tgt->cmd_ptr + cmd_count + len,
				tgt->block_size - len);
#endif PC532
			tgt->transient_state.out_count = tgt->block_size;
		}
	} else {
		tgt->transient_state.out_count = 0;
		tgt->transient_state.copy_count = 0;
	}

	tgt->transient_state.cmd_count = cmd_count;

	disconn  = BGET(scsi_might_disconnect,sdp->sc->masterno,tgt->target_id);
	disconn  = disconn && (sdp->ntargets > 1);
	disconn |= BGET(scsi_should_disconnect,sdp->sc->masterno,tgt->target_id);
#ifdef PC532
	/* Debug first without disconnect */
	disconn = 0;
	synch = 0;
#endif PC532


	/*
	 * Setup target state
	 */
	tgt->done = SCSI_RET_IN_PROGRESS;

	handler = (disconn) ? sdp_err_disconn : sdp_err_generic;

        /* determine wether or not to use the late forms of the scripts */
        late = cmd_only ? FALSE : (tgt->flags & TGT_DID_SYNCH);

	switch (tgt->cur_cmd) {
	    case SCSI_CMD_READ:
	    case SCSI_CMD_LONG_READ:
		LOG(0x13,"readop");
		scp = sdp_script_data_in;
		break;
	    case SCSI_CMD_WRITE:
	    case SCSI_CMD_LONG_WRITE:
		LOG(0x14,"writeop");
		scp = sdp_script_data_out;
		break;
	    case SCSI_CMD_INQUIRY:
		/* This is likely the first thing out:
		   do the synch neg if so 
		   NO SYNC on dp8490 */
	    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:
		scp = sdp_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:
		tgt->transient_state.cmd_count = sizeof(scsi_command_group_0);
		tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0);
		scp = sdp_script_data_out;
		LOG(0x1c,"cmdop");
		LOG(0x80+tgt->cur_cmd,0);
		break;
	    case SCSI_CMD_TEST_UNIT_READY:
		/*
		 * Do the synch negotiation here, unless prohibited
		 * or done already
		 * NO SYNC supported on dp8490.
		 */
		scp = sdp_script_cmd;
		LOG(0x1c,"cmdop");
		LOG(0x80+tgt->cur_cmd,0);
		break;
	    default:
		LOG(0x1c,"cmdop");
		LOG(0x80+tgt->cur_cmd,0);
		scp = sdp_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 sdp structure.
	 * Note that it is the strategy routine's job
	 * to serialize ops on the same target as appropriate.
	 */
#if 0
locking code here
#endif
	s = splbio();

	if (sdp->wd.nactive++ == 0)
		sdp->wd.watchdog_state = SCSI_WD_ACTIVE;

	if (sdp->state & SDP_STATE_BUSY) {
		/*
		 * Queue up this target, note that this takes care
		 * of proper FIFO scheduling of the scsi-bus.
		 */
		LOG(3,"enqueue");
		enqueue_tail(&sdp->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.
		 */
		sdp->state |= SDP_STATE_BUSY;
		sdp->next_target = tgt;
		sdp_attempt_selection(sdp);
		/*
		 * Note that we might still lose arbitration..
		 */
	}
	splx(s);
}

#if 1				/* DEBUG */
int sdp_old_ipl = 0;
#include <ns532/ipl.h>		/* XXX */
#endif

sdp_attempt_selection(sdp)
	sdp_softc_t	sdp;
{
	target_info_t	*tgt;
	register int	out_count;
	sdp_padded_regmap_t	*regs;
	register int	cmd;
	boolean_t	ok;
	int		old;
	scsi_ret_t	ret;

	regs = sdp->regs;

	tgt = sdp->next_target;

	LOG(4,"select");
	LOG(0x80+tgt->target_id,0);

	/*
	 * Init bus state variables and set registers.
	 */
	sdp->active_target = tgt;

#if 1				/* DEBUG */
	{
		extern int curr_ipl;

		sdp_old_ipl = curr_ipl;
		if (curr_ipl < SPLSCSI)
		    panic ("sdp_attempt_selection: bad ipl");
	}
#endif	
	old = PC532_SCSI_SELECT(ICU_DP);

	/* reselection pending ? */
#if 0
	if ((regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL))
	    && (regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL))
	    && (regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL))
	    )
	  {
	    printf ("sdp: Reselection conflict? csr = 0x%x, sdp->done = 0x%x, tgt->done = 0x%x\n", regs->sdp_bus_csr, sdp->done, tgt->done);
#if 0
	    return;
#endif
#else
	/* Why should we want to make this check? If there really is a
	 * re-select we should handle it with an interrupt. See if the
	 * printf ever executes.
	 */
	if (((regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL|SDP_BUS_IO)) == (SDP_BUS_SEL|SDP_BUS_IO))
#ifdef SDP_SOFT_DEGLITCH
	    && ((regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL|SDP_BUS_IO)) == (SDP_BUS_SEL|SDP_BUS_IO))
	    && ((regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL|SDP_BUS_IO)) == (SDP_BUS_SEL|SDP_BUS_IO))
#endif
	    )
	  {
	    int id;
	    regs->sdp_mode &= ~SDP_MODE_PAR_CHK;
	    id = regs->sdp_data;/*parity!*/
	    regs->sdp_mode |= SDP_MODE_PAR_CHK;
		
	    /* xxx check our id is in there and that there is one and only one
	     * target id */
		
	    if (id & (1 << sdp->sc->initiator_id))
	      {
		register int i;
		id &= ~(1 << sdp->sc->initiator_id);
		{
		  register int tid;
		  for (i = 0, tid = 1; i < 8; i++, tid <<= 1)
		    if (id == tid) break;
		  if (i < 8)
		    {
		      printf ("sdp: Reselection conflict? csr = 0x%x, id = 0x%x\n", regs->sdp_bus_csr, id);
#if 0
		      return;
#endif
		    }
		}
	      }
#endif
	  }

	sdp->script = tgt->transient_state.script;
	sdp->error_handler = tgt->transient_state.handler;
	sdp->done = SCSI_RET_IN_PROGRESS;

	sdp->in_count = 0;
	sdp->out_count = 0;

	/*
	 * This is a bit involved, but the bottom line is we want to
	 * know after we selected with or w/o ATN if the selection
	 * went well (ret) and if it is (ok) to send the command.
	 */
	ret = sdp_start_select_target(regs, sdp->sc->initiator_id,
				      tgt->target_id, FALSE);

	PC532_SCSI_SELECT(old);

	/* the interrupt routine takes care of the rest */
	return;

#if 0
	if (ret == SCSI_RET_DEVICE_DOWN) {
		DP(1, printf("sdp_attempt_selection: device down\n"));
		sdp->done = ret;
		sdp_end(sdp, regs->sdp_csr, regs->sdp_bus_csr);
		return;
	}

	if (ret != SCSI_RET_SUCCESS) {
		DP(1, printf("sdp: sdp_attempt_selection: ret == x%x\n", ret));
		return;
	}
	if (sdp_xxx)
/* time this out or do it via dma !! */
	    while (SDP_CUR_PHASE(regs->sdp_bus_csr) != SCSI_PHASE_CMD)
		;
#endif 0
}

scsi_ret_t
sdp_start_select_target(regs, myid, id, with_atn)
	register sdp_padded_regmap_t	*regs;
	unsigned char		myid, id;
	boolean_t		with_atn;
{	
	unsigned char imr;
	int i;

	LOG(0x62, "start_arb");
#if 0
	for (i = 0; regs->sdp_bus_csr & (SDP_BUS_BSY|SDP_BUS_SEL);
	     i = (i == 10)? 0: i + 1) {
	  if (i == 0)
	    DP(2, printf ("sdp: bus busy, waiting to select\n"));
	}
#endif

	/* for our purposes.. */
	myid = 1 << myid;
	id = 1 << id;

	regs->sdp_tcmd = 0;	/* in case it wasn't */
	regs->sdp_icmd = SDP_ICMD_ENHANCED;

	SDP_IMR_GET(regs, imr);
	imr &= ~SDP_INT_ARB; /* mask */
	SDP_IMR_PUT(regs, imr);

	regs->sdp_odata = myid;

	/* arbitrate in enhanced mode see figure on page 34, A1 */

	regs->sdp_emr |= SDP_EMR_ARB;
	return SCSI_RET_IN_PROGRESS;
}

sdp_retry_arbitration(regs, myid, id, with_atn)
	register sdp_padded_regmap_t	*regs;
	unsigned char		myid, id;
	boolean_t		with_atn;
{
	LOG(0x61, "retry_arb");
	/* reset ARB since arbitration failed. see 4.4.2 */
	regs->sdp_mode &= ~SDP_MODE_ARB; /* shouldn't be on */
	regs->sdp_emr = regs->sdp_emr & ~SDP_EMR_ARB;
	SDP_CLR_INTR(regs);
	return sdp_start_select_target(regs, myid, id, with_atn);
}

scsi_ret_t
sdp_complete_selection(sdp)
	sdp_softc_t sdp;
{
	sdp_padded_regmap_t	*regs;
	unsigned char		myid, id;
	boolean_t		with_atn = FALSE;
	register unsigned char	csd, icmd, imr;
        register target_info_t  *tgt = sdp->active_target;

	LOG(0x60, "sel_comp");
	if (!tgt) {
		printf("sdp_complete_selection: no active target!\n");
		gimmeabreak();
		SDP_CLR_INTR(regs);
		return KERN_FAILURE;
	}
	regs = sdp->regs;

	myid = 1 << sdp->sc->initiator_id;
	id = 1 << tgt->target_id;

	if (regs->sdp_icmd & SDP_ICMD_LST) /* what about AIP? */
	    return sdp_retry_arbitration(regs, sdp->sc->initiator_id,
					 tgt->target_id, with_atn);

	/* did we win? */
	csd = regs->sdp_data;
	if (!(myid & csd) || (myid < (csd & ~myid))) {
		return sdp_retry_arbitration(regs, sdp->sc->initiator_id,
					     tgt->target_id, with_atn);
	}

	regs->sdp_sel_enb = 0;
	regs->sdp_tcmd = 0;

	icmd = SDP_ICMD_ENHANCED;

	TR2("---------------- sel", 0);
	TR2("id", myid | id);
	TR2("BSR", regs->sdp_csr);
	TR2("CSB", regs->sdp_bus_csr);
	TR2("MR2", regs->sdp_mode);
	TR2("ICR", regs->sdp_icmd);
	TRCHECK;

	icmd |= SDP_ICMD_SEL;
	regs->sdp_icmd = icmd;

	delay(2);		/* 1.2 us */

	TR2("---------------- sel", 10);
	TR2("BSR", regs->sdp_csr);
	TR2("CSB", regs->sdp_bus_csr);
	TR2("MR2", regs->sdp_mode);
	TR2("ICR", regs->sdp_icmd);
	TRCHECK;

	regs->sdp_odata = myid | id;
	icmd |= SDP_ICMD_DATA | (with_atn ? SDP_ICMD_ATN : 0);
	regs->sdp_icmd = icmd;
	
	TR2("---------------- sel", 1);
	TR2("BSR", regs->sdp_csr);
	TR2("CSB", regs->sdp_bus_csr);
	TR2("MR2", regs->sdp_mode);
	TR2("ICR", regs->sdp_icmd);
	TRCHECK;
	{int i = 0; i++; i++;}	/* 2 deskew delay each 45 ns */
	
	regs->sdp_emr &= ~SDP_EMR_ARB;
#if 0
	regs->sdp_mode &= ~SDP_MODE_ARB; /* shouldn't be on */
#endif
	TR2("---------------- sel", 2);
	TR2("BSR", regs->sdp_csr);
	TR2("CSB", regs->sdp_bus_csr);
	TR2("MR2", regs->sdp_mode);
	TR2("ICR", regs->sdp_icmd);
	TRCHECK;

	/* I guess this would be an appropriate time to clear the ARB intr */
	SDP_CLR_INTR(regs);

/*	regs->sdp_sel_enb = 0; */

	/* bus settle delay, 400ns */
	delay(0 + 1); /* too much ? */

	TR2("---------------- sel", 3);
	TR2("BSR", regs->sdp_csr);
	TR2("CSB", regs->sdp_bus_csr);
	TR2("MR2", regs->sdp_mode);
	TR2("ICR", regs->sdp_icmd);
	TRCHECK;

	{
		/* 250 msecs in 100 usecs chunks */
		register int timeo  = 50000; /* 2500 XXX */
		while ((regs->sdp_bus_csr & SDP_BUS_BSY) == 0)
			if (--timeo > 0)
				delay(100);
			else {
				goto nodev;
			}
	}

	{int i = 0; i++; i++;}	/* 2 deskew delays */

	/* Enable phase change interrupts for the data transfer phases */
	SDP_IMR_GET(regs, imr);
	imr &= ~SDP_INT_PHASE; /* mask */
	SDP_IMR_PUT(regs, imr);
	/* Clear interrupts. This shouldn't be necessary but enabling
	 * phase change interrupts seems to cause an interrupt from some
	 * past phase change event. The chip specs don't say this happens,
	 * but then, they don't say it *doesn't* happen either!
	 */
	SDP_CLR_INTR(regs);

	icmd &= ~(SDP_ICMD_DATA | SDP_ICMD_SEL);
	regs->sdp_icmd = icmd;
	DP(1, sdp_break3());
	return SCSI_RET_SUCCESS;

nodev:
	DP(2, printf("sdp: selection failed (2)\n"));
	DP(1, sdp_break2());
	regs->sdp_sel_enb = 0;
	regs->sdp_icmd = SDP_ICMD_ENHANCED;

	return SCSI_RET_DEVICE_DOWN;
}

/*
 * Interrupt routine
 *	Take interrupts from the chip
 *
 * Implementation:
 *	Move along the current command's script if
 *	all is well, invoke error handler if not.
 */
sdp_intr(unit)
	int	unit;
{
	int	old;

	/*
	 * Take care of the pc532 brain damage that the two SCSI
	 * controllers are in the same physical addresses.
	 * And selected by the ICU select signal.
	 */
	old = PC532_SCSI_SELECT(ICU_DP);

	do_sdp_intr(unit);

	PC532_SCSI_SELECT(old);
}

do_sdp_intr(unit)
	int	unit;
{
	register sdp_softc_t	sdp;
	register script_t	scp;
	register unsigned	csr, bs, cmd, isr;
	register sdp_padded_regmap_t	*regs;
	boolean_t		try_match;

#if	notyet
	extern boolean_t	rz_use_mapped_interface;

	if (rz_use_mapped_interface)
		return SDP_intr(unit);
#endif

	sdp = sdp_softc[unit];
	regs = sdp->regs;


	LOG(5,"\n\tintr");

	{	/* XXX for testing purposes XXX */
		int i;
		for (i = 0; i < sdp_delay_after_intr; i++)
		    delay(1);	/* XXX */
	}
	/* ack interrupt */
	csr = regs->sdp_csr;
	bs = regs->sdp_bus_csr;
	cmd = regs->sdp_icmd;
	SDP_ISR_GET(regs, isr);

	TR2("---------intr--------", 0);
	TR2("isr  7 ISR", isr);
	TR2("mode 2 MR2", regs->sdp_mode);

	TR2("csr  5 BSR", csr);
	TR2("bs   4 CSB", bs);
	TR2("tcmd 3 TCR", regs->sdp_tcmd);
	TR2("cmd  1 ICR", cmd);
	TRCHECK;

	if (isr & SDP_INT_ARB) {
		LOG(7, "iarb");
		/* XXX check return value! (better check) */
		if (sdp_complete_selection(sdp) == SCSI_RET_DEVICE_DOWN) {
			sdp->done = SCSI_RET_DEVICE_DOWN;
			sdp_end(sdp, csr, bs);
		}
		/* interrupt cleared in called routine */
		return;
	}

	SDP_CLR_INTR(regs);

	if (cmd & SDP_ICMD_RST){
	  assert(isr == 0);
		sdp_bus_reset(sdp);
		return;
	}

	/* we got an interrupt allright */
	if (sdp->active_target)
		sdp->wd.watchdog_state = SCSI_WD_ACTIVE;

	/* drop spurious calls */
	if ((csr & SDP_CSR_INT) == 0) {
		LOG(2, "SPURIOUS");
		return;
	}

	if (isr == 0){
		sdp_bus_reset(sdp);
		return;
	}


        /* Any interrupt if we are a target means call sdp_target_intr.
	 * A selection interrupt means call sdp_target_intr also
	 * (and become a target?).
	 * Note: reselect has I/O asserted, select has not */
	if ((sdp->state & SDP_STATE_TARGET) ||
	    ((isr & SDP_INT_SEL) && !(bs & SDP_BUS_IO))) {
		sdp_target_intr(sdp,csr,bs);
		return;
	}

	scp = sdp->script;

	/* BSY lost implies disconnect. What if we don't expect it? */
	if (isr & SDP_INT_BSY)
	   if (scp && (scp->condition == SDP_PHASE_DISC)) {
		(void) (*scp->action)(sdp, csr, bs);
		/* takes care of calling reconnect if necessary */
		return;
	      }
	   else
	     {
	       printf ("sdp: Unexpexpected disconnect\n");
	       gimmeabreak();
	       return;
	     }
	  

	/* check who got the bus */
	if ((isr & SDP_INT_SEL) && (bs & SDP_BUS_IO)) {
	  sdp_reconnect(sdp, csr, bs);
	  return;
	}

	if (! (isr & (SDP_INT_PHASE | SDP_INT_DMA_PHASE | SDP_INT_EDMA))) {
	  /* Can do parity checks etc here */
		printf ("sdp: unexpected intr");
		gimmeabreak();
		return;
	}

	if (! scp) {
	  /* This could be interrupts due to bus activity which
	   * doesn't concern us. We attempt to avoid these with
           * the right IMR value. See sdp_end_transaction.
	   */
		printf ("sdp: unexpected intr, null scp.\n");
		gimmeabreak();
		return;
	}


	if (SCRIPT_MATCH(csr,bs) != scp->condition) {
		if (try_match = (*sdp->error_handler)(sdp, csr, bs)) {
			csr = regs->sdp_csr;
			bs = regs->sdp_bus_csr;
		}
	} else
		try_match = TRUE;

	/* might have been side effected */
	scp = sdp->script;

	if (try_match && (SCRIPT_MATCH(csr,bs) == scp->condition)) {
		/*
		 * Perform the appropriate operation,
		 * then proceed
		 */
		if ((*scp->action)(sdp, csr, bs)) {
			/* might have been side effected */
			scp = sdp->script;
			sdp->script = scp + 1;
		}
	}
}


sdp_target_intr(sdp)
	register sdp_softc_t	sdp;
{
	panic("SDP: TARGET MODE !!!\n");
}

/*
 * All the many little things that the interrupt
 * routine might switch to
 */
boolean_t
sdp_end_transaction( sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	register sdp_padded_regmap_t	*regs = sdp->regs;
	char			cmc;
	unsigned char imr;

	LOG(0x1f,"end_t");

	regs->sdp_icmd = SDP_ICMD_ENHANCED;
	regs->sdp_mode &= ~(SDP_MODE_MONBSY);

        /* Turn off interrupts due to other activity on the bus.
	 * We only want select (as a target) interrupts. */
	SDP_IMR_GET(regs, imr);
	imr = ~SDP_INT_SEL;
	SDP_IMR_PUT(regs, imr);

	SDP_ACK(regs,SCSI_PHASE_MSG_IN);

	sdp_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc);

	if (cmc != SCSI_COMMAND_COMPLETE)
	  {
	    TR2("bad msg in", cmc);
	    printf("sdp:{T%x}\n", cmc);
	  }

TR2("sdp_bus_csr", regs->sdp_bus_csr);

#if 0				/* There is no point in waiting for bus free.
				 * a) we might have missed it in a multi-
				 *    initiator environment and
				 * b) we shouldn't need to wait anyway.
				 */
	while (regs->sdp_bus_csr & SDP_BUS_BSY)
	    ;
#endif

	/* set disconnected, clear all intr bits */
	SDP_CLR_INTR (regs);
	SDP_ACK(regs,SDP_PHASE_DISC);

	if (!sdp_end(sdp, csr, bs)) {
		SDP_CLR_INTR(regs);
		(void) sdp_reconnect(sdp, csr, bs);
	}
	return FALSE;
}

boolean_t
sdp_end( sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	register target_info_t	*tgt;
	register io_req_t	ior;
	register sdp_padded_regmap_t	*regs = sdp->regs;
	boolean_t		reconn_pending;

	LOG(6,"end");

	tgt = sdp->active_target;

	if ((tgt->done = sdp->done) == SCSI_RET_IN_PROGRESS)
		tgt->done = SCSI_RET_SUCCESS;

	sdp->script = 0;

	if (sdp->wd.nactive-- == 1)
		sdp->wd.watchdog_state = SCSI_WD_INACTIVE;

	/* check reconnection not pending */
	bs = regs->sdp_bus_csr;
	reconn_pending = ((bs & (SDP_BUS_BSY|SDP_BUS_SEL|SDP_BUS_IO)) == (SDP_BUS_SEL|SDP_BUS_IO));
	DP (1, sdp_break());
	if (!reconn_pending) {
		sdp_release_bus(sdp);
	} else {
		sdp->active_target = 0;
/*		sdp->state &= ~SDP_STATE_BUSY; later */
	}

	if (tgt->ior) {
		LOG(0xA,"ops->restart");
		(*tgt->dev_ops->restart)(tgt, TRUE);
		if (reconn_pending)
			sdp->state &= ~SDP_STATE_BUSY;
	}

	return (!reconn_pending);
}

boolean_t
sdp_release_bus(sdp)
	register sdp_softc_t	sdp;
{
	boolean_t	ret = FALSE;

	LOG(9,"release");

	sdp->script = 0;

	if (sdp->state & SDP_STATE_COLLISION) {

		LOG(0xB,"collided");
		sdp->state &= ~SDP_STATE_COLLISION;
		sdp_attempt_selection(sdp);

	} else if (queue_empty(&sdp->waiting_targets)) {
#if 0
		char imr;

		SDP_IMR_GET(sdp->regs, imr);
		imr |= SDP_INT_PHASE | SDP_INT_DMA_PHASE;
		SDP_IMR_PUT(sdp->regs, imr);
#endif

		sdp->state &= ~SDP_STATE_BUSY;
		sdp->active_target = 0;
		ret = TRUE;

	} else {

		LOG(0xC,"dequeue");
		sdp->next_target = (target_info_t *)
				dequeue_head(&sdp->waiting_targets);
		sdp_attempt_selection(sdp);
	}
	return ret;
}

boolean_t
sdp_get_status( sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	register sdp_padded_regmap_t	*regs = sdp->regs;
	scsi2_status_byte_t	status;
	register target_info_t	*tgt;
	unsigned int		len, mode;

	LOG(0xD,"get_status");
TRWRAP;

	regs->sdp_icmd = SDP_ICMD_ENHANCED;

	sdp->state &= ~SDP_STATE_DMA_IN;

	tgt = sdp->active_target;

	SDP_ACK(regs,SCSI_PHASE_STATUS);

	if (sdp_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits))
	  {
	    TR2("---------get_status--------", 0);
	    TR2("mode 2 MR2", regs->sdp_mode);
	    TR2("csr  5 BSR", regs->sdp_csr);
	    TR2("bs   4 CSB", regs->sdp_bus_csr);
	    TR2("tcmd 3 TCR", regs->sdp_tcmd);
	    TRCHECK;
	    panic("sdp_get_status: couldn't get status");
	  }

	TR2("---------get_status--------", 1);
	TR2("mode 2 MR2", regs->sdp_mode);
	TR2("csr  5 BSR", regs->sdp_csr);
	TR2("bs   4 CSB", regs->sdp_bus_csr);
	TR2("tcmd 3 TCR", regs->sdp_tcmd);
	TRCHECK;

	if (status.st.scsi_status_code != SCSI_ST_GOOD) {
		DP(1, printf("sdp_get_status: not good\n"));
		scsi_error(sdp->active_target, SCSI_ERR_STATUS, status.bits, 0);
		sdp->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
			SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
	} else
		sdp->done = SCSI_RET_SUCCESS;

	return TRUE;
}

boolean_t
sdp_issue_command(sdp, cst, bs)
     sdp_softc_t        sdp;
{
        register sdp_padded_regmap_t   *regs = sdp->regs;

        LOG(0x12, "cmd_issue");
        /* we have just done a select;
           Bus is in CMD phase;
           need to phase match */
        SDP_ACK(regs, SCSI_PHASE_CMD);

        return sdp_data_out(regs, SCSI_PHASE_CMD,
                     sdp->active_target->transient_state.cmd_count,
                     sdp->active_target->cmd_ptr) ? FALSE : TRUE;
}


boolean_t
sdp_xfer_in( sdp, cst, bs)
        register sdp_softc_t    sdp;
{
        register target_info_t  *tgt;
	register sdp_padded_regmap_t	*regs = sdp->regs;
        register int            count;
        boolean_t               advance_script = TRUE;

        LOG(0xE,"xfer_in");

        tgt = sdp->active_target;
        sdp->state |= SDP_STATE_DMA_IN;

        count = tgt->transient_state.in_count;

        SDP_ACK(regs, SCSI_PHASE_DATAI);

        if ((tgt->cur_cmd != SCSI_CMD_READ) &&
            (tgt->cur_cmd != SCSI_CMD_LONG_READ))
          sdp_data_in(regs, SCSI_PHASE_DATAI, count, tgt->cmd_ptr);
        else
          {
            sdp_data_in(regs, SCSI_PHASE_DATAI, count, tgt->ior->io_data);
          }

        return advance_script;
}

boolean_t
sdp_xfer_out( sdp, cst, bs)
        register sdp_softc_t    sdp;
{
        register sdp_padded_regmap_t   *regs = sdp->regs;
        register target_info_t  *tgt;
        boolean_t               advance_script = TRUE;
        int                     count = sdp->out_count;

        LOG(0xF,"xfer_out");

        tgt = sdp->active_target;
        sdp->state &= ~SDP_STATE_DMA_IN;

        count = tgt->transient_state.out_count;

        SDP_ACK(regs, SCSI_PHASE_DATAO);

        if ((tgt->cur_cmd != SCSI_CMD_WRITE) &&
            (tgt->cur_cmd != SCSI_CMD_LONG_WRITE))
          sdp_data_out(regs, SCSI_PHASE_DATAO, count,
                       tgt->cmd_ptr + tgt->transient_state.cmd_count);
        else
          sdp_data_out(regs, SCSI_PHASE_DATAO, count, tgt->ior->io_data);

        return advance_script;
}

/* disconnect-reconnect ops */

/* get the message in via dma ?? */
boolean_t
sdp_msg_in(sdp, csr, bs)
        register sdp_softc_t    sdp;
{
        register target_info_t  *tgt;
        register sdp_padded_regmap_t   *regs = sdp->regs;

        LOG(0x15,"msg_in");
        gimmeabreak();

        tgt = sdp->active_target;

#if 0
You can do this by hand, just leave an interrupt pending at the end
#endif

        /* We only really expect two bytes */
#if 0
        SPC_PUT(dmar,sizeof(scsi_command_group_0));
        ....
#endif
        return TRUE;
}

/* save all relevant data, free the BUS */
boolean_t
sdp_disconnected(sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	panic("sdp_disconnected");
}

/* get reconnect message, restore BUS */
boolean_t
sdp_reconnect(sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	register target_info_t	*tgt;
	sdp_padded_regmap_t		*regs;
	register int		id;
	int			msg;

	regs = sdp->regs;

	regs->sdp_mode &= ~SDP_MODE_PAR_CHK;
	id = regs->sdp_data;/*parity!*/
	regs->sdp_mode |= SDP_MODE_PAR_CHK;
		
	/* xxx check our id is in there and there is one and only one target id */
	
	if ((id & (1 << sdp->sc->initiator_id)) == 0)
	  return FALSE;
	else
	  {
	    register int i;
	    id &= ~(1 << sdp->sc->initiator_id);
	    {
	      register int tid;
	      for (i = 0, tid = 1; i < 8; i++, tid <<= 1)
		if (id == tid) break;
	      if (i == 8)
		{
		  return FALSE;
		}
	    }
	  }

	LOG(0x17,"reconnect");

	DP(1, printf("sdp_reconnect. id = 0x%x\n", id & (1 << sdp->sc->initiator_id)));

	return FALSE;		/* XXX */
}

/*
 * The bus was reset
 */
sdp_bus_reset(sdp)
	register sdp_softc_t	sdp;
{
	register target_info_t	*tgt;
	register sdp_padded_regmap_t	*regs = sdp->regs;
	int			i;

	LOG(0x21,"bus_reset");

	/*
	 * Clear bus descriptor
	 */
	sdp->script = 0;
	sdp->error_handler = 0;
	sdp->active_target = 0;
	sdp->next_target = 0;
	sdp->state = 0;
	queue_init(&sdp->waiting_targets);
	sdp->wd.nactive = 0;
	sdp_reset(sdp, TRUE);

	printf("sdp%d: (%d) bus reset ", sdp->sc->masterno, ++sdp->wd.reset_count);
	delay(scsi_delay_after_reset); /* some targets take long to reset */

	if (sdp->sc == 0)	/* sanity */
		return;

	scsi_bus_was_reset(sdp->sc);
}

/*
 * Error handlers
 */

/*
 * Generic, default handler
 */
boolean_t
sdp_err_generic(sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	register int		cond = sdp->script->condition;

	LOG(0x10,"err_generic");

	if (SDP_CUR_PHASE(bs) == SCSI_PHASE_STATUS)
		return sdp_err_to_status(sdp, csr, bs);
#if 1
	if (SDP_CUR_PHASE(bs) == SCSI_PHASE_MSG_IN) {
		DP(1, printf ("sdp: premature MSG IN phase\n"));
		LOG(0x64, "pre_MSG_IN");
		sdp_end_transaction (sdp, csr, bs);
		return FALSE;
	}
#endif 1
	gimmeabreak();
	return FALSE;
}

/*
 * Handle generic errors that are reported as
 * an unexpected change to STATUS phase
 */
sdp_err_to_status(sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	script_t		scp = sdp->script;

	LOG(0x20,"err_tostatus");
	while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS)
		scp++;
	sdp->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.
	 */
	sdp->done = SCSI_RET_NEED_SENSE;
#endif
	return TRUE;
}

/*
 * Watch for a disconnection
 */
boolean_t
sdp_err_disconn(sdp, csr, bs)
	register sdp_softc_t	sdp;
{
	panic("sdp_err_disconn");
}

/*
 * Watchdog
 *
 */
sdp_reset_scsibus(sdp)
        register sdp_softc_t    sdp;
{
        register target_info_t  *tgt = sdp->active_target;
	int old, s;

        if (tgt) {
		int cnt;

                log(	LOG_KERN,
			"Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
                        tgt->target_id, tgt->cur_cmd,
                        tgt->transient_state.in_count, tgt->transient_state.out_count,
                        sdp->in_count, sdp->out_count, cnt);
		gimmeabreak();
	}
#if 1
	s = splhi();
	old = PC532_SCSI_SELECT(ICU_DP);

	sdp_reset(sdp, FALSE);

	PC532_SCSI_SELECT(old);
	splx(s);
#else
        sdp->regs->sdp_icmd = SDP_ICMD_RST;
        delay(25);
        sdp->regs->sdp_icmd = SDP_ICMD_ENHANCED; /* CHECK SOMEDAY */
#endif
}

#endif	/*NSDP > 0*/

