#include "sc.h"
#if	NSC > 0
#ifndef lint
static	char sccsid[] = "@(#)sc.c 1.1 94/10/31 SMI";
#endif

/*
 * Copyright (C) 1989, Sun Microsystems, Inc.
 */

/*
 *
 * TO DO:	1. reimplment PARITY support
 *		2. handle linked commands
 *
 */

#include <scsi/scsi.h>
#include <scsi/adapters/screg.h>
#include <sys/mman.h>
#include <machine/pte.h>
#include <machine/cpu.h>
#include <vm/seg.h>

/*
 * Definitions
 */

/*#define	SC_PARITY	/* Compile in (cough) Parity support */
/*#define	SC_LINKED	/* Compile in linked command support */
/*#define	SCDEBUG		/* Compile in debug code */

#ifdef	SC_PARITY
---- BLETCH ----- PARITY NOT SUPPORTED YET
#endif


#ifdef	SCDEBUG
#define	DEBUGGING	(scdebug || (scsi_options & SCSI_DEBUG_HA))
#define	DPRINTF(str)		if (DEBUGGING) sc_printf(sc, str)
#define	DPRINTF1(str, x)	if (DEBUGGING) sc_printf(sc, str, x)
#define	DPRINTF2(str, x, y)	if (DEBUGGING) sc_printf(sc, str, x, y)
#define	DPRINTF3(str, x, y, z)	if (DEBUGGING) sc_printf(sc, str, x, y, z)
#else
#define	DPRINTF
#define	DPRINTF1
#define	DPRINTF2
#define	DPRINTF3
#endif

#define	TRUE		1
#define	FALSE		0
#define	UNDEFINED	-1

#define	Tgt(sp)		((sp)->cmd_pkt.pkt_address.a_target)
#define	Lun(sp)		((sp)->cmd_pkt.pkt_address.a_lun)
#define	Nextcmd(sp)	((struct scsi_cmd *)((sp)->cmd_pkt.pkt_ha_private))

#define	CNAME	scdriver.mdr_cname
#define	CNUM	sc-sc_softc

#define	INTPENDING(reg) ((reg)->icr & (ICR_INTERRUPT_REQUEST | ICR_BUS_ERROR))


/*
 * SCSI Architecture Interface functions
 */

static int sc_start(), sc_abort(), sc_reset(), sc_getcap(), sc_setcap();

/*
 * Local && h/w related functions
 */


static void screset(), sc_watch(), sc_ustart(), sc_finish(), sc_abort_cmd();
static void sc_printf(), sc_flush();

/*
 * Local static data
 */

static struct scsitwo sc_softc[NSC];
static char *scbits =
	"\20\20Per\17Berr\16Odd\15IRQ\14REQ\13MSG\12CD\11IO\03Wrd\02Dma\01Ena";
static long sc_kment;
static int sc_mapins;

#ifdef	SCDEBUG
static int scdebug;
#endif	SCDEBUG

/*
 * External references
 */

extern struct seg kseg;

/*
 * Config dependencies
 */

static int scprobe(), scslave(), scattach(), scpoll();
int scintr();
struct	mb_driver scdriver = {
	scprobe, scslave, scattach, 0, 0, scpoll,
	sizeof (struct screg), "scsibus", 0, "sc", 0, MDR_BIODMA,
};

static int
scprobe(reg, ctlr)
caddr_t reg;
int ctlr;
{
	static char scwstart = 0;
	register struct scsitwo *sc;
	register struct screg *btmp = (struct screg *) reg;

	/* probe for different scsi host adaptor interfaces */
	if (peek((short *)&btmp->dma_count) == -1) {
		return (0);
	}
	/* validate ctlr by write/read/cmp with a data pattern */
	btmp->dma_count = 0x6789;
	if (btmp->dma_count != 0x6789) {
		return (0);
	}

	if (ctlr >= NSC) {
		printf("%s%d: illegal controller number", CNAME, ctlr);
		return (0);
	}

	sc_kment = rmalloc(kernelmap, mmu_btopr(MMU_PAGESIZE));
	if (sc_kment == 0)
		return (0);
	sc = &sc_softc[ctlr];
	sc->sc_reg = btmp;
	sc->sc_tran.tran_start 		= sc_start;
	sc->sc_tran.tran_abort		= sc_abort;
	sc->sc_tran.tran_reset		= sc_reset;
	sc->sc_tran.tran_getcap		= sc_getcap;
	sc->sc_tran.tran_setcap		= sc_setcap;
	sc->sc_tran.tran_pktalloc 	= scsi_std_pktalloc;
	sc->sc_tran.tran_dmaget		= scsi_std_dmaget;
	sc->sc_tran.tran_pktfree	= scsi_std_pktfree;
	sc->sc_tran.tran_dmafree	= scsi_std_dmafree;
	screset(sc, 0);
	if (!scwstart) {
		scwstart++;
		timeout (sc_watch, (caddr_t) 0, hz);
	}
	return (sizeof (struct screg));
}

/*ARGSUSED1*/
static int
scslave(md, reg)
struct mb_device *md;
caddr_t reg;
{
	struct scsitwo *sc = &sc_softc[md->md_unit];

	md->md_slave = HOST_ID << 3;
#ifndef	SC_LINKED
	/*
	 * Disable linked commands for the moment...
	 */
	if (scsi_options & SCSI_OPTIONS_LINK) {
		scsi_options ^= SCSI_OPTIONS_LINK;
		sc_printf(sc, "disabling linked command support\n");
	}
#endif
	return (1);
}

static int
scattach(md)
struct mb_device *md;
{
	int i;
	static int sc_spl = 0;
	struct scsitwo *sc = &sc_softc[md->md_unit];

	/*
	 * Calculate max spl value so far seen
	 */
	sc_spl = MAX(sc_spl, pritospl(md->md_intpri));

	/*
	 * Make sure that everyone, including us, is locking at
	 * the same priority.
	 */

	for (i = 0; i <= md->md_unit; i++) {
		sc_softc[i].sc_tran.tran_spl = sc_spl;
	}

	/*
	 * Initialize interrupt register
	 */
	if (md->md_mc->mc_intr) {
		/* set up for vectored interrupts */
		sc->sc_reg->intvec = md->md_mc->mc_intr->v_vec;
		*(md->md_mc->mc_intr->v_vptr) = (int) sc;
	} else {
		/* use auto-vectoring */
		sc->sc_reg->intvec = AUTOBASE + md->md_mc->mc_intpri;
	}
	/*
	 * Now make ourselves known to the rest of the SCSI world
	 */
	scsi_config(&sc->sc_tran, md);
}


/*
 *
 *	Begin of external interface routines
 *
 */

static int
sc_start(sp)
register struct scsi_cmd *sp;
{
	auto int s;
	register struct scsitwo *sc;
	register struct scsi_cmd *xp;

	sc = (struct scsitwo *) sp->cmd_pkt.pkt_address.a_cookie;
	s = splr(sc->sc_tran.tran_spl);
	if (xp = sc->sc_que) {
		for (;;) {
			if (Tgt(xp) == Tgt(sp) && Lun(xp) == Lun(sp)) {
				/* Queueing error */
				(void) splx(s);
				return (FALSE);
			}
			if (Nextcmd(xp) == 0)
				break;
			else
				xp = Nextcmd(xp);
		}
		Nextcmd(xp) = sp;
	} else {
		sc->sc_que = sp;
	}
	Nextcmd(sp) = 0;

	/*
	 * reinitialize some fields that need it...
	 */

	sp->cmd_pkt.pkt_resid = 0;
	sp->cmd_pkt.pkt_reason = sp->cmd_pkt.pkt_state =
		sp->cmd_pkt.pkt_statistics = 0;
	sp->cmd_cdbp = (caddr_t) sp->cmd_pkt.pkt_cdbp;
	sp->cmd_scbp = sp->cmd_pkt.pkt_scbp; *sp->cmd_scbp = 0;
	if (sp->cmd_timeout = sp->cmd_pkt.pkt_time)
		sp->cmd_flags |= CFLAG_WATCH;
	else
		sp->cmd_flags &= ~CFLAG_WATCH;
	sp->cmd_data = sp->cmd_saved_data = sp->cmd_mapping;
	sp->cmd_cursubseg = &sp->cmd_subseg;
	sp->cmd_subseg.d_base = sp->cmd_mapping;
	sp->cmd_subseg.d_count = 0;
	sp->cmd_subseg.d_next = (struct dataseg *) 0;
	sp->cmd_flags &= ~(CFLAG_NEEDSEG|CFLAG_CMDDISC);



	/*
	 * Okay- if this command is a polling command,
	 * and we're not the head of the queue, we have to
	 * wait for the commands ahead of us to finish.
	 */
	if (sp->cmd_pkt.pkt_flags & FLAG_NOINTR) {
		int id;
		while (sc->sc_que != sp) {
			DPRINTF ("polled command waiting\n");
			sc_dopoll(sc, sc->sc_cmdid);
		}
		id = sc->sc_cmdid;
		DPRINTF1 ("starting polling cmd #%d\n", id);
		sc_ustart(sc);
		DPRINTF ("waiting polled cmd completion\n");
		sc_dopoll(sc, id);
		DPRINTF1 ("polled cmd #%d complete\n", id);
		if (sc->sc_que)
			sc_ustart(sc);
	} else if (sc->sc_que == sp) {
		sc_ustart(sc);
	}
	(void) splx(s);
	return (TRUE);
}

/*ARGSUSED*/
static int
sc_abort(ap, pkt)
struct scsi_address *ap;
struct scsi_pkt *pkt;
{
	if (pkt != (struct scsi_pkt *) 0)
		return (FALSE);
	else
		return (FALSE);
}

static int
sc_reset(ap, level)
struct scsi_address *ap;
int level;
{
	struct scsitwo *sc = (struct scsitwo *) ap->a_cookie;
	struct scsi_cmd *sp = sc->sc_que;

	screset(sc, 0);

	if (sp) {
		/*
		 * If we're resetting everything, remove
		 * the current wait queue from reconsideration
		 * after we reset and blow away the current
		 * command.
		 */

		if (level == RESET_ALL) {
			sp = Nextcmd(sc->sc_que);
			Nextcmd(sc->sc_que) = 0;
		}

		sp->cmd_pkt.pkt_reason = CMD_RESET;
		sc_finish(sc);

		/*
		 * walk down the unrooted wait queue, 'finishing' off
		 * all the commands therein...
		 */
		if (level == RESET_ALL) {
			while (sp) {
				struct scsi_cmd *xp;
				xp = Nextcmd(sp);
				sp->cmd_pkt.pkt_reason = CMD_RESET;
				(*sp->cmd_pkt.pkt_comp)(sp);
				sp = xp;
			}
		}
		return (TRUE);
	} else {
		return (FALSE);
	}
}

/*ARGSUSED*/
static int
sc_getcap(ap, cap)
struct scsi_address *ap;
char *cap;
{
	if (cap == (char *) 0)
		return (UNDEFINED);
	else if (strncmp("dma_max", cap, 7) == 0)
		return (1<<16);
	else if (strncmp("msg_out", cap, 7) == 0)
		return (0);
	else if (strncmp("disconnect", cap, 10) == 0)
		return (0);
	else if (strncmp("synchronous", cap, 11) == 0)
		return (0);
	else if (strncmp("parity", cap, 6) == 0)
		return (0);
	else if (strncmp("initiator-id", cap, 12) == 0)
		return (HOST_ID);
	else
		return (UNDEFINED);
}

/*ARGSUSED*/
static int
sc_setcap(ap, cap, value)
struct scsi_address *ap;
char *cap;
int value;
{
	/*
	 * Cannot set any values yet
	 */
	return (UNDEFINED);
}



/*
 *
 * Internal start routine
 *
 */

static void
sc_ustart(sc)
register struct scsitwo *sc;
{
	register i;
	register struct scsi_cmd *sp = sc->sc_que;
	char dkn;
	int r;

	if (!sp)
		return;
	else if ((dkn = sp->cmd_pkt.pkt_pmon) >= 0) {
		dk_busy |= (1<<dkn);
		dk_xfer[dkn]++;
		if ((sp->cmd_flags & CFLAG_DMASEND) == 0)
			dk_read[dkn]++;
		dk_wds[dkn] += sp->cmd_dmacount >> 6;
	}

	/*
	 * sc_select will wait for the scsi bus to be free, attempt
	 * to select the target, will set up the dma engine for any
	 * possible dma transfer, and will set the icr bits for having
	 * interrupts enabled if this is an interrupting command.
	 *
	 */

	sc->sc_busy = 1;
	if (sc_select(sc) == FALSE) {
		sc_finish(sc);
		return;
	}

	/*
	 * At this point, we should either ge into STATUS phase or into
	 * COMMAND phase.
	 */
#ifdef	SCDEBUG
	if (DEBUGGING) {
		sc_printf(sc, "%d.%d cmd=", Tgt(sp), Lun(sp));
		for (i = 0; i < sp->cmd_cdblen; i++) {
			printf(" 0x%x", sp->cmd_cdbp[i]);
		}
		printf("\n");
	}
#endif	SCDEBUG
	for (r = i = 0; i < sp->cmd_cdblen; i++) {
		if ((r = sc_putbyte(sc, ICR_COMMAND, sp->cmd_cdbp[i])) <= 0) {
			break;
		}
	}
	if (r < 0) {
		sc_printf(sc, "couldn't send command\n");
		sc_abort_cmd(sc);
	} else if (i > 0) {
		sp->cmd_pkt.pkt_state |= STATE_SENT_CMD;
		sp->cmd_cdbp = (caddr_t) (((u_long)sp->cmd_cdbp) + i);
	}
	/*
	 * We will either let interrupts drive us the rest of the
	 * way, or call a polling routine to do that for us
	 */
}

static int
sc_select(sc)
register struct scsitwo *sc;
{
	register struct scsi_cmd *sp = sc->sc_que;
	register int i;
	u_short icr_mode;

	DPRINTF2 ("select %d.%d\n", Tgt(sp), Lun(sp));

	/*
	 * make sure scsi bus is not continuously busy
	 */

	for (i = SC_WAIT_COUNT; i > 0; i--) {
		if ((sc->sc_reg->icr & ICR_BUSY) == 0)
			break;
		DELAY(10);
	}

	if (i == 0) {
		sc_printf(sc, "scsi bus continuously busy: ICR=0x%b\n",
			sc->sc_reg->icr, scbits);
		sp->cmd_pkt.pkt_reason = CMD_INCOMPLETE;
		screset(sc, 1); /* XXX? */
		return (FALSE);
	}

	/*
	 * Got the bus...
	 */

	sp->cmd_pkt.pkt_state |= STATE_GOT_BUS;

	/*
	 * select target and wait for response
	 */

	sc->sc_reg->icr = 0; /* Make sure SECONDBYTE flag is clear */

	/*
	 * Since we aren't an arbitrating initiator, we only
	 * put in the target address and *not* our own as well.
	 */

	sc->sc_reg->data = 1 << Tgt(sp);
	sc->sc_reg->icr = ICR_SELECT;

	/*
	 * See if target responds to selection
	 */

	if (sc_wait((u_short *)&sc->sc_reg->icr, SC_SHORT_WAIT, ICR_BUSY)) {
		DPRINTF2 ("%d.%d didn't select\n", Tgt(sp), Lun(sp));
		sc->sc_reg->data = 0;
		sc->sc_reg->icr = 0;
		sp->cmd_pkt.pkt_reason = CMD_INCOMPLETE;
		return (FALSE);
	}

	/*
	 * Got the target...
	 */

	sp->cmd_pkt.pkt_state |= STATE_GOT_TARGET;

	icr_mode = 0;
	if ((sp->cmd_pkt.pkt_flags & FLAG_NOINTR) == 0) {
		icr_mode |= ICR_INTERRUPT_ENABLE;
	}
	/*
	 * If a data transfer is expected, set it up here...
	 */

	if (sp->cmd_dmacount && (sp->cmd_flags&CFLAG_DMAVALID)) {
		SET_DMA_ADDR (sc->sc_reg, ((int)sp->cmd_data));
		sc->sc_reg->dma_count = ~sp->cmd_dmacount;
		if (((int)sp->cmd_data)&0x1) {
			DPRINTF ("odd byte DMA address\n");
		} else {
			icr_mode |= ICR_WORD_MODE;
		}
		icr_mode |= ICR_DMA_ENABLE;
	}

	/*
	 * set the interrupt register
	 */

	sc->sc_reg->icr = icr_mode;

	return (TRUE);
}

/*
 *
 * finish routine
 *
 */


static void
sc_finish(sc)
register struct scsitwo *sc;
{
	register struct scsi_cmd *sp = sc->sc_que;
	char dkn, wasintr;

	sc->sc_reg->icr = 0;	/* clear any pending interrupts */
	sc->sc_busy = 0;

	if ((dkn = sp->cmd_pkt.pkt_pmon) >= 0) {
		dk_busy &= ~(1<<dkn);
	}
	DPRINTF1 ("finishing command %d\n", sc->sc_cmdid);
	sc->sc_cmdid++;
	sc->sc_que = Nextcmd(sp);
	if (sp->cmd_pkt.pkt_state & STATE_XFERRED_DATA) {
		/*
		 * Since we cannot disconnect, we cannot have had more
		 * than one data segment
		 */
		if (sp->cmd_subseg.d_next != (struct dataseg *) 0) {
			panic("sc_finish: more than one segment with data");
			/* NOTREACHED */
		}

		sp->cmd_pkt.pkt_resid =
			sp->cmd_dmacount - sp->cmd_subseg.d_count;

	}

	wasintr = (sp->cmd_pkt.pkt_flags & FLAG_NOINTR)? 0: 1;

	(*sp->cmd_pkt.pkt_comp)(sp);

	if (wasintr && sc->sc_que && sc->sc_busy == 0) {
		sc_ustart(sc);
	}
}


/*
 * Interrupt service section
 */

static int
sc_dopoll(sc, id)
register struct scsitwo *sc;
register id;
{
	register i;
	struct scsi_cmd *sp = sc->sc_que;

	for (i = 0; i < 300000 && id == sc->sc_cmdid; i++) {
		if (INTPENDING(sc->sc_reg)) {
			scintr(sc);
			i = 0;
		} else {
			DELAY(500);
		}
	}
	if (i >= 300000) {
		sc_printf(sc, "polled command timeout\n");
		sp->cmd_pkt.pkt_reason = CMD_TIMEOUT;
		screset(sc, 0);
		sc_finish(sc);
	}
}


/*
 * Handle a polling (autovectored) SCSI bus interrupt.
 */

static int
scpoll()
{
	register struct scsitwo *sc;
	register int serviced = 0;

	for (sc = &sc_softc[0]; sc < &sc_softc[NSC]; sc++) {
		if (sc->sc_reg && INTPENDING(sc->sc_reg)) {
			serviced += scintr(sc);
		}
	}
	return (serviced);
}


/*
 * Handle a scsi bus interrupt.
 */

int
scintr(sc)
register struct scsitwo *sc;
{
	register struct scsi_cmd *sp = sc->sc_que;

	if (sp == 0 || !sc->sc_reg || !(INTPENDING(sc->sc_reg))) {
		sc_printf(sc, "spurious interrupt\n");
		return (0);
	}

	DPRINTF2 ("scintr: ICR=0x%b\n", sc->sc_reg->icr, scbits);

	/*
	 * End of Data phase? Check for bus error first...
	 */
	if (sc->sc_reg->icr & ICR_BUS_ERROR) {
		sc_printf(sc, "Dma BUS Error on address 0x%x\n", sp->cmd_data);
		sc_abort_cmd(sc);
		return (1);
	} else if (sp->cmd_dmacount && (sp->cmd_flags & CFLAG_DMAVALID)) {
		int resid, reqamt, xfer_amt;

		resid = (u_short) (~sc->sc_reg->dma_count);
		reqamt = sp->cmd_dmacount;

		if (sc->sc_reg->icr & ICR_ODD_LENGTH) {
			if (sp->cmd_flags & CFLAG_DMASEND) {
				resid++;
			} else if (reqamt) {
				sc_flush(sc, (u_long)
				    (sp->cmd_data + reqamt - resid));
				resid--;
			}
		}
		if (xfer_amt = reqamt - resid) {
			DPRINTF1 ("xferred %d\n", xfer_amt);
			sp->cmd_data += xfer_amt;
			sp->cmd_subseg.d_count += xfer_amt;
			sp->cmd_pkt.pkt_state |= STATE_XFERRED_DATA;
		}
	}
	/*
	 * Now, get status and/or message...
	 */
	while (sc->sc_reg->icr & (ICR_BUSY|ICR_REQUEST)) {
		register u_short pbits;

		while (((pbits = sc->sc_reg->icr) & ICR_REQUEST) == 0) {
			if ((pbits & ICR_BUSY) == 0) {
				sc_printf(sc, "Target %d dropped BUSY\n",
				    Tgt(sp));
				sc_abort_cmd(sc);
				return (1);
			}
		}

		switch (pbits & ICR_BITS) {
		case ICR_STATUS:
			*sp->cmd_scbp = sc->sc_reg->cmd_stat;
			sp->cmd_pkt.pkt_state |= STATE_GOT_STATUS;
			break;
		case ICR_MESSAGE_IN:
		{
			static char *bad =
				"Bad Message '0x%x' from Target %d, Lun %d\n";

			switch (sc->sc_msgin = sc->sc_reg->cmd_stat) {
			case MSG_COMMAND_COMPLETE:
			case MSG_LINK_CMPLT:
			case MSG_LINK_CMPLT_FLAG:
				sp->cmd_pkt.pkt_reason = CMD_CMPLT;
				sc_finish(sc);
				break;
			default:
				sc_printf(sc, bad, sc->sc_msgin,
					Tgt(sp), Lun(sp));
				sc_abort_cmd(sc);
				break;
			}
			return (1);
			break;
		}
		default:
			sc_printf(sc, "Bad phase: ICR=0x%b\n", pbits, scbits);
			sc_abort_cmd(sc);
			return (1);
			break;
		}
	}
	return (1);
}

/*
 * Flush the last byte of an odd length transfer
 */

static void
sc_flush(sc, offset)
register struct scsitwo *sc;
register u_long offset;
{
	register u_int pv;
	u_char *mapaddr;

	if (MBI_MR(offset) < dvmasize) {
		DVMA[offset] = sc->sc_reg->data;
		return;
	}

#ifdef	sun3x
	pv = btop (VME24D16_BASE + (offset & VME24D16_MASK));
#else	sun3x
	pv = PGT_VME_D16 | VME24_BASE | btop(offset & VME24_MASK);
#endif	sun3x

	mapaddr = (u_char *) ((u_long) kmxtob(sc_kment) |
	    (u_long) MBI_OFFSET(offset));

	segkmem_mapin(&kseg, (addr_t) (((int)mapaddr) & PAGEMASK),
	    (u_int) mmu_ptob(1), PROT_READ | PROT_WRITE, pv, 0);

	*mapaddr = sc->sc_reg->data;

	segkmem_mapout(&kseg,
	    (addr_t) (((int)mapaddr) & PAGEMASK), (u_int) mmu_ptob(1));

	segkmem_mapout(&kseg, (addr_t) mapaddr, (u_int) mmu_btop(1));
}

/*
 * Miscellaneous functions
 */


static void
sc_abort_cmd(sc)
struct scsitwo *sc;
{
	sc->sc_que->cmd_pkt.pkt_reason = CMD_RESET;
	screset(sc, 1);
	sc_finish(sc);
}

/*
 * Wait for a condition to be (de)asserted on the scsi bus.
 * Returns 0, if successful, else -1;
 */

static int
sc_wait(icrp, wait_count, cond)
	register u_short *icrp;
	register int wait_count;
{
	register int i;
	register u_short icr;

	for (i = 0; i < wait_count; i++) {
		icr = *icrp;
		if ((icr & cond) == cond) {
			return (0);
		} else if (icr & ICR_BUS_ERROR) {
			break;
		} else
			DELAY(10);
	}
	return (-1);
}

/*
 * Put data byte onto the scsi bus if phase match.
 * Returns:	1  if successful (phase matched)
 *		0  if incorrect phase, but REQ is present
 *		-1 if no REQ present
 */

static
sc_putbyte(sc, bits, data)
register struct scsitwo *sc;
register u_short bits;
register u_char data;
{
	register u_short icr;

	if (sc_wait((u_short *)&sc->sc_reg->icr, SC_WAIT_COUNT, ICR_REQUEST)) {
		DPRINTF ("sc_putbyte: no REQ\n");
		return (-1);
	}
	icr = sc->sc_reg->icr;
	if ((icr & ICR_BITS) != bits) {
		DPRINTF2 ("sc_putbyte: ICR=0x%b\n", icr, scbits);
		return (0);
	}
	sc->sc_reg->cmd_stat = data;
	return (1);
}

/*
 * Reset SCSI control logic and bus.
 */
static void
screset(sc, msg_enable)
struct scsitwo *sc;
int msg_enable;
{

	if (msg_enable) {
		sc_printf(sc, "Resetting SCSI bus\n");
	}

	sc->sc_reg->icr = ICR_RESET;
	DELAY(50);
	sc->sc_reg->icr = 0;

	/* give reset scsi devices time to recover (> 2 Sec) */
	DELAY(SC_RESET_DELAY);
}

/*ARGSUSED*/
static void
sc_watch(arg)
caddr_t arg;
{
	register s;
	register struct scsitwo *sc;
	register struct scsi_cmd *sp;

	for (sc = &sc_softc[0]; sc < &sc_softc[NSC]; sc++) {
		if (sc->sc_reg) {
			s = splr(sc->sc_tran.tran_spl);
			sp = sc->sc_que;
			if (sp && (sp->cmd_flags & CFLAG_WATCH)) {
				if (sp->cmd_timeout == 0) {
					/*
					 * A pending interrupt
					 * defers the sentence
					 * of death.
					 */
					if (INTPENDING(sc->sc_reg)) {
						sp->cmd_timeout++;
						(void) splx(s);
						continue;
					}
					sc_printf(sc, "command timeout\n");
					sp->cmd_pkt.pkt_reason = CMD_TIMEOUT;
					sc_reset(sc, 0);
					sc_finish(sc);
					(void) splx(s);
					continue;
				} else {
					sp->cmd_timeout -= 1;
				}
			}
			(void) splx(s);
		}
	}
	timeout(sc_watch, (caddr_t) 0, hz);
}

/*VARARGS*/
static void
sc_printf(sc, fmt, a, b, c, d, e, f, g, h)
struct scsitwo *sc;
char *fmt;
int a, b, c, d, e, f, g, h;
{
	printf("%s%d: ", CNAME, CNUM);
	printf(fmt, a, b, c, d, e, f, g, h);
}

#endif	(NSC > 0)
