static char rcsid[] = "$Header: is.c,v 820.1 86/12/04 19:55:42 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * Interphase 2190/Storager driver.
 *
 * Notes:
 * [1]	Sector size of DEV_BSIZE is assumed.
 *
 * TODO:
 *
 */

#include "is.h"
#if NIS > 0
#include "../h/param.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/inode.h"
#include "../h/ioctl.h"
#include "../h/user.h"
#include "../h/dk.h"
#include "../h/uio.h"
#include "../s32/dkio.h"
#include "../s32/dklabel.h"
#include "../s32/cpu.h"
#include "../s32dev/mbvar.h"
#include "../s32dev/isreg.h"
#include "../s32dev/isvar.h"

#include "../s32dev/is_ioctl.h"




	/* autoconfiguration interface */

isprobe(), isslave(), idattach();

/*
 * A short blab on controller addresses:
 *
 * We now only allow Interphase controllers at two addresses,
 * 8 bytes apart.  This is due to the fact that the Storager, has
 * two register sets of 4 bytes each.  We consider the Storager to
 * be one controller for probing, dedicating the second register set
 * to tape operations.
 *
 * 'Nuf said?
 *
 * Note: I believe that the Xylogics disk controller lives at address 0xe0
 * too.  If you have Storagers and Xylogics together in one system (about
 * as likely as Moslems and Jews in the same Middle East) problems with
 * device probing will certainly result -- Xylogics is the Libya of S32land.
 */

u_short isstd[] = { 0xf0, 0xf8, 0xe0, 0xe8, 0 }; /* ctlr addresses */
struct mb_ctlr *iscinfo[NIS];		/* controller information */
struct mb_driver isdriver =
	{0, isprobe, isslave, idattach, isstd, "id", idinfo, 
	 "is", iscinfo };

int isc_reset_failed[NIS];  		/* if true, tried to reset controller */
int isc_storager_reset;  		/* if true, tried to reset controller */

struct is_ctlr is_ctlr[NIS];		/* per controller structures */

struct buf iscbuf;			/* buffer for polled commands */



/*
 * Error message routine.
 */
char *iserror(e)
{
	static char *is_errlist[] = {
		0,						/* 0x0 */
		"End of tape encountered during copy command",	/* 0x1 */
		"File mark encountered",			/* 0x2 */
		0,						/* 0x3 */
		0,						/* 0x4 */
		0,						/* 0x5 */
		0,						/* 0x6 */
		0,						/* 0x7 */
		0,						/* 0x8 */
		0,						/* 0x9 */
		0,						/* 0xa */
		0,						/* 0xb */
		0,						/* 0xc */
		0,						/* 0xd */
		0,						/* 0xe */
		0,						/* 0xf */
		"Disk not ready",				/* 0x10 */
		"Invalid disk address",				/* 0x11 */
		"Seek error",					/* 0x12 */
		"ECC error - data field",			/* 0x13 */
		"Invalid command code",				/* 0x14 */
		"Invalid cylinder address in iopb",		/* 0x15 */
		"Invalid sector in command",			/* 0x16 */
		0,						/* 0x17 */
		"Bus timeout",					/* 0x18 */
		0,						/* 0x19 */
		"Disk write protected",				/* 0x1a */
		"Unit not selected",				/* 0x1b */
		"No address mark - header field",		/* 0x1c */
		"No address mark - data field",			/* 0x1d */
		"Drive faulted",				/* 0x1e */
		0,						/* 0x1f */
		"Disk surface overrun",				/* 0x20 */
		"ID field error - wrong sector",		/* 0x21 */
		"CRC error - ID field",				/* 0x22 */
		"Uncorrectable error",				/* 0x23 */
		0,						/* 0x24 */
		0,						/* 0x25 */
		"No sector pulse",				/* 0x26 */
		"Data overrun / format timeout",		/* 0x27 */
		"No index pulse on write format",		/* 0x28 */
		"Sector not found",				/* 0x29 */
		"ID field error - wrong head",			/* 0x2a */
		"Invalid sync in data field",			/* 0x2b */
		0,						/* 0x2c */
		"Seek timeout",					/* 0x2d */
		"Busy timeout",					/* 0x2e */
		"Not on cylinder",				/* 0x2f */
		"RTZ (restore) timeout",			/* 0x30 */
		"Format overrun on data",			/* 0x31 */
		0,						/* 0x32 */
		0,						/* 0x33 */
		0,						/* 0x34 */
		0,						/* 0x35 */
		0,						/* 0x36 */
		0,						/* 0x37 */
		0,						/* 0x38 */
		0,						/* 0x39 */
		0,						/* 0x3a */
		0,						/* 0x3b */
		0,						/* 0x3c */
		0,						/* 0x3d */
		0,						/* 0x3e */
		0,						/* 0x3f */
		"Unit not initialized",				/* 0x40 */
		0,						/* 0x41 */
		"Gap specification error",			/* 0x42 */
		0,						/* 0x43 */
		0,						/* 0x44 */
		0,						/* 0x45 */
		0,						/* 0x46 */
		0,						/* 0x47 */
		0,						/* 0x48 */
		0,						/* 0x49 */
		0,						/* 0x4a */
		"Seek error",					/* 0x4b */
		"Mapped header error",				/* 0x4c */
		0,						/* 0x4d */
		0,						/* 0x4e */
		0,						/* 0x4f */
		"Sector per track error",			/* 0x50 */
		"Bytes per sector specification error",		/* 0x51 */
		"Interleave factor specification error",	/* 0x52 */
		"Invalid head address",				/* 0x53 */
		0,						/* 0x54 */
		0,						/* 0x55 */
		0,						/* 0x56 */
		0,						/* 0x57 */
		0,						/* 0x58 */
		0,						/* 0x59 */
		0,						/* 0x5a */
		0,						/* 0x5b */
		0,						/* 0x5c */
		"Invalid DMA burst count",			/* 0x5d */
		0,						/* 0x5e */
		0,						/* 0x5f */
		"Protection timeout error",			/* 0x60 */
		"Maximum cylinder number specification error",	/* 0x61 */
		"Number of heads specification error",		/* 0x62 */
		"Step pulse specification error",		/* 0x63 */
		"Reserved byte specification error",		/* 0x64 */
		"Ram failure - odd byte",			/* 0x65 */
		"Ram failure - even byte",			/* 0x66 */
		"Event ram failure",				/* 0x67 */
		"Device not previously recalibrated",		/* 0x68 */
		"Controller error",				/* 0x69 */
		"Invalid sector number",			/* 0x6a */
		"Timer failure",				/* 0x6b */
		"Rom failure - odd byte",			/* 0x6c */
		"Rom failure - even byte",			/* 0x6d */
		0,						/* 0x6e */
		0,						/* 0x6f */
		0,						/* 0x70 */
		0,						/* 0x71 */
		0,						/* 0x72 */
		0,						/* 0x73 */
		0,						/* 0x74 */
		0,						/* 0x75 */
		0,						/* 0x76 */
		0,						/* 0x77 */
		0,						/* 0x78 */
		0,						/* 0x79 */
		0,						/* 0x7a */
		0,						/* 0x7b */
		0,						/* 0x7c */
		0,						/* 0x7d */
		0,						/* 0x7e */
		0,						/* 0x7f */
		"Tape drive not selected",			/* 0x80 */
		"Tape drive not ready",				/* 0x81 */
		"Tape drive not online",			/* 0x82 */
		"Cartridge not in place",			/* 0x83 */
		"Unexpected beginning of tape",			/* 0x84 */
		"Unexpected end of tape",			/* 0x85 */
		"Unexpected file mark encountered",		/* 0x86 */
		"Unrecoverable data error",			/* 0x87 */
		"Block in error not located",			/* 0x88 */
		"No data detected",				/* 0x89 */
		"Write protected",				/* 0x8a */
		"Illegal command",				/* 0x8b */
		"Command sequence timeout",			/* 0x8c */
		"Status sequence timeout",			/* 0x8d */
		"Data block transfer timeout",			/* 0x8e */
		"File mark search timeout",			/* 0x8f */
		"Unexpected exception",				/* 0x90 */
		"Invalid unit address - tape",			/* 0x91 */
		"Ready timeout",				/* 0x92 */
		"Tape timeout specification error",		/* 0x93 */
		"Invalid block count"				/* 0x94 */
	};

	static lastindex = (sizeof(is_errlist) / sizeof(caddr_t)) - 1;

	return(e < 0 || e > lastindex || is_errlist[e] == 0 
		? "Unknown error code" : is_errlist[e]);
}

	/* debug flags */

#ifdef DEBUG
char isdebug = 0;
char isidebug = 0;		/* print isintr() actions */
char issdebug = 0;		/* print operation in isustart() */
char isrdebug = 0;
char iscdebug = 0;
char isudebug = 0;

char isintdebug = 0;
char isint1debug = 0;
char isint2debug = 0;
/* 
 * Print actions while probing different 
 * flavors of ESDI protocol 
 */
char isaltdebug = 0;		

/*
 * for logging commands
 */

#define IS_NLOG 100
u_long is_log[IS_NLOG];
u_long *is_logp = is_log;
#endif	DEBUG

/* 
 * Tunable constants (you can tune a controller, but you cain't tuna...)
 */

#define RPS 	60			/* should be drive dependent */
#define IS_DMA	64			/* DMA burst */

#ifdef ISERRORLOGGING
char isautomap = 0;			/* don't map bad tracks */
#else  ISERRORLOGGING
char isautomap = 1;			/* automagically map bad tracks */
#endif ISERRORLOGGING

char isforce_open = 1;			/* allow opening unfmtted/nolabel */
/*
 * Retry count changed from 2 to 6 to comply with 
 * Ed Patriquin and Ross Andersen's disk qualification covenant.
 */
int isretrycnt = 6;			/* software retry count */
int isdelayamt = 50;			/* delay after csr read until CLRI */



/* 
 * See if device is there.
 * Tries to generate an interrupt.
 * If successful, returns size of device structure, otherwise zero.
 */

isprobe(addr, intr, ctlr)
	caddr_t addr;
	int (*intr)();
{
	register struct is_ctlr *ic = is_ctlr + ctlr;
	register i;
	int isintr();
	int x;
	static u_char firstreset[NIS];

	/* 
	 * avoid reprobing: 
	 * "if there's a controller entry and the controller's alive
	 * and it's our controller, return 0"
	 */

	for (i = 0; i < NIS; i++)
		if (iscinfo[i] != 0 && iscinfo[i]->mc_alive &&
		    iscinfo[i]->mc_addr == addr)
			return 0;

	if (intr != isintr)			/* bogus interrupt routine */
		return 0;

	if (isaltdebug) cdebugger("isprobe");

	ic->ic_addr = (struct is_device *)addr;
	ic->ic_addr1 = ((struct is_device *)addr) + 1;	/* 2nd register set */
	ic->ic_ctlr = ctlr;

	/*
	 * Note that we don't try to touch the csr.
	 * One of the iopb address registers is better, as it will
	 * not cause the controller to do anything weird, as
	 * an inappropriate write to the csr might.
	 */

	if (!istouch(&ic->ic_addr->ispb0))	/* no one at home */
		return(0);

	/*
	 * do first time reset here,
	 * rather than in the reset entry, as that gets called too
	 * many times.
	 */

	if (!firstreset[ctlr]) {
		if (isaltdebug)
			printf("isprobe: resetting ctlr %d\n", ctlr);
		firstreset[ctlr] = 1;

		if (istouch(&ic->ic_addr1->ispb0))
			/* 
			 * it's a storager, reset it pretty-like.
			 */
			isfirstreset(addr);

		/*
		 * plug in addresses of register sets iobps.
		 */

		i = (int) vtop((caddr_t)&IP);
		ic->ic_addr->ispb0 = i;
		ic->ic_addr->ispb1 = i >> NBBY;
		ic->ic_addr->ispb2 = i >> NBBY * 2;

		/*
		 * For storager...
		 */
		i = (int) vtop((caddr_t)&IP1);
		ic->ic_addr1->ispb0 = i;
		ic->ic_addr1->ispb1 = i >> NBBY;
		ic->ic_addr1->ispb2 = i >> NBBY * 2;
	}

	if (isrunconfigcmd(ic, ctlr)) {
		if ((IP.ip_flavor == IPFLAVOR_STRGR) ||
		    (IP.ip_flavor == IPFLAVOR_NEWSTRGR))
			ic->ic_storager = 1;
		else
			ic->ic_storager = 0;
		return sizeof (struct is_device);
	} else {
		/* NOTE: may want to put an error message here! */
		return 0;
	}
}


/*
 * Run the report configuration command.
 */
isrunconfigcmd(ic, ctlr)
	register struct is_ctlr *ic;
{
	register int i, x;

try_again:
	IP.ip_cmd = IPCMD_CONFIG;
	IP.ip_opt = IPOPT_NORMAL;
	IP.ip_drive = 0;
	i = (int)ic->ic_addr - MBIO_VA;
	IP.ip_ioaddr0 = i;
	IP.ip_ioaddr1 = i >> NBBY;
	i = (int) vtop((caddr_t)&IP);
	ic->ic_addr->ispb0 = i;
	ic->ic_addr->ispb1 = i >> NBBY;
	ic->ic_addr->ispb2 = i >> NBBY * 2;
	IP.ip_stat = 0;
	IP.ip_err = 0;
	IP.ip_flavor = 0;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	ic->ic_addr->iscsr = ISCSR_GO|ISCSR_BUS|ISCSR_CLRI;   /* Go! */

	/*
	 * Now look for a done status & csr not busy or timeout. 
	 * No matter what, we'll get an interrupt.
	 * We assume that after a CI, the iopb will be correct.
	 */

	iswaituntildone(&IP, &ic->ic_addr->iscsr);

	for (x = isdelayamt; --x > 0 ; ) /* waste time before CLRI */
		;

	ic->ic_addr->iscsr = ISCSR_CLRI;	/* clear int */

	i = ISTIMEOUTDELAY;
	while (ic->ic_addr->iscsr & ISCSR_CI && --i)	 /* wait for done */
		;
	
	if (i == 0 || IP.ip_stat != IPSTAT_DONE )
		return(0);
	else
		return(1);
}


/*
 * Is there a slave attached to this controller?
 */
isslave(md, mc)
	struct mb_device *md;
	struct mb_ctlr *mc;
{
	if (md->md_dk) {
		/*
		 * It's a disk.
		 */
		if (!idslave(md, mc))		/* not there... */
			return 0;
		/*
		 * Patch up the driver struct so that
		 * the autoconfig code will call the right attach
		 * routine with the right data structures.
		 */
		isdriver.mdr_dname[1] = 'd';
		isdriver.mdr_dinfo = idinfo;
		isdriver.mdr_attach = idattach;
	} else {
		if (!itslave(md, mc)) {
			/*
			 * Not there, patch it up for disk.
			 */
			isdriver.mdr_dname[1] = 'd';
			isdriver.mdr_dinfo = idinfo;
			isdriver.mdr_attach = idattach;
			return 0;
		}
		isdriver.mdr_dname[1] = 't';
		isdriver.mdr_dinfo = itinfo;
		isdriver.mdr_attach = itattach;
	}
	return 1;
}


isintr(ctlr)
{
	register struct is_ctlr *ic = is_ctlr + ctlr;
	register u_char csr0, csr1;
	register u_char storagerflag = ic->ic_storager;

	csr0 = ic->ic_addr->iscsr;
	csr1 = storagerflag ? ic->ic_addr1->iscsr : 0;

	/*
	 * If we are not the source of the interrupt, return.
	 */

#define IS_HAVE_INT(csr) ((csr) & (ISCSR_SI|ISCSR_CI))

	if ( (!storagerflag && !IS_HAVE_INT(csr0)) || 
	     (storagerflag && !IS_HAVE_INT(csr0) && !IS_HAVE_INT(csr1)) )
		return(0);

	/*
	 * Note that we observe the convention that
	 * disk drive commands are run on the first
	 * register set, and tape commands run on the second...
	 */

	if (IS_HAVE_INT(csr0)) {
		ISLOG('i', ic->ic_ctlr, csr0);
		idintr(ctlr, csr0);
	}

	if (IS_HAVE_INT(csr1)) {
		ISLOG('I', ic->ic_ctlr, csr1);
		itintr(ctlr, csr1);
	}

#undef IS_HAVE_INT

	return 1;
}


/*
 * Resets the csr in a safe, non-wedging manner.
 * Returns zero if non-sucessful, non-zero otherwise.
 */
isresetcsr(csr)
	u_char *csr;
{
	int i;

	*csr = ISCSR_CLRI;

	i = ISTIMEOUTDELAY;
	while (*csr & (ISCSR_BUSY|ISCSR_SI|ISCSR_CI) && i-- )
		;

	return !(*csr & (ISCSR_BUSY|ISCSR_SI|ISCSR_CI));
}

/*
 * waits untl the board comes ready..
 * returns true if board not busy (ready), false if board is busy.
 */
iswaitready(csr)
	u_char *csr;
{
	int i;

	if (isaltdebug) 
		cdebugger("iswaitready");

	i = ISTIMEOUTDELAY;
	while ((*csr & ISCSR_BUSY) && i-- )
		;

	return(!(*csr & ISCSR_BUSY));
}

/*
 * Resets the board.
 */
isboardreset(csr)
	u_char *csr;
{
	int i;

	if (isaltdebug) 
		cdebugger("isboardreset");

	/*
	 * What we have to do is to raise RESET for at least
	 * 2 microseconds, and wait for BUSY to rise and fall.
	 * Since we can't guarantee to be able to see BUSY
	 * (if another multibus master takes the bus for a long time),
	 * we just delay long enough for it to come on and wait
	 * for the falling edge.
	 */
	*csr = ISCSR_RESET;

	i = (chipType == CHIPTYPE_68020) ? isdelayamt * 10: isdelayamt;

	while (--i)
		;

	*csr = 0;
	while (!(*csr & ISCSR_BUSY))	/* spin while not busy */
		;

	i = ISTIMEOUTDELAY;
	while (!(*csr & ISCSR_CI) && i-- )
		;

	isresetcsr(csr);
}


/*
 * Reset the controller upon probe.
 * Do nothing (ie, don't fault) if the controller isn't there.
 */
isfirstreset(is_dev)
	register struct is_device *is_dev;
{
	struct is_device *is_dev1 = is_dev + 1 ;	/* 2nd register set */
	int i;

	/*
	 * Is there a controller at all?
	 */
	if (isaltdebug)
		cdebugger("isfirstreset");

	/*
	 * We have empirically determined that when the storager comes up,
	 * it presents interrupts in both register banks.
	 * To clear these interrupts, we must write a zero to the csr,
	 * then clear the interrupt.
	 */

	isboardreset(&is_dev->iscsr);

	if (isaltdebug) cdebugger("ready to reset 2nd csr");

	/*
	 * Reset the second register set.
	 */

	if (!isresetcsr(&is_dev1->iscsr))
		printf("ic at 0x%x: reset 2nd set failed, csr 0x%b.\n",
			  (u_int)is_dev1, is_dev1->iscsr, ISCSR_BITS);
}


/*
 * Safely touches a device register.
 * Returns true if it's really there, false otherwise.
 */
istouch(devreg)
	register u_char *devreg;
{
	label_t		jb;		/* our fault handler */
	label_t		*saved_jb;
	extern label_t	*nofault;	/* old fault handler */
	register found = 0;
	u_char junk;

	saved_jb = nofault;		/* Save old setjmp value for later */

	if (setjmp(&jb)) {
		/* 
		 * If we get here, we've had a bus fault,
		 * so the second register bank wasn't there.
		 */

		nofault = saved_jb;	/* restore old value */
	} else {
		/*
		 * Normal return from setjmp.
		 * Let's try to touch the second register bank.
		 */

		nofault = &jb;

		*devreg = 0; 
		flushWriteQueue();
		/*
		 * If we get here, the attempt to touch the controller
		 * was successful.
		 */

		found++;	/* it's there! */
		nofault = saved_jb;
	}

	return(found);
}



/*
 * Reset controller.
 * We also issue a reset to each drive.
 * If the controller was active, then the operation is restarted.
 *
 * NOTE: this seems to break the disk subsystem if called via "dk -r".
 */
isreset(ic)
	register struct is_ctlr *ic;
{
	int s = splIS();
	register struct is_disk *id;
	register struct is_tape *it;
	register i;

	isboardreset(&ic->ic_addr->iscsr);
	
	/*
	 * Initialize all units and restart any pending operations.
	 */

	/*
	 * Remind our register set where the IOPB lives.
	 */

	if (!isresetcsr(&ic->ic_addr->iscsr)) {
		printf("is%d: can't reset csr\n", ic->ic_ctlr);
		goto bad;
	}
	if (ic->ic_storager && !isresetcsr(&ic->ic_addr1->iscsr)) {
		printf("is%d: can't reset alternate csr\n", ic->ic_ctlr);
		goto bad;
	}

	/*
	 * Remind controller where iopb(s) live(s).
	 */

	i = (int) vtop((caddr_t) &IP);
	ic->ic_addr->ispb0 = i;
	ic->ic_addr->ispb1 = i >> NBBY;
	ic->ic_addr->ispb2 = i >> (NBBY * 2);

	if (ic->ic_storager) {
		/* second verse, same as the first... */
		i = (int) vtop((caddr_t) &IP1);
		ic->ic_addr1->ispb0 = i;
		ic->ic_addr1->ispb1 = i >> NBBY;
		ic->ic_addr1->ispb2 = i >> (NBBY * 2);
	}

#ifdef M68020
	flushWriteQueue();
#endif M68020

	if (ic->ic_ndisk)
		idreset(ic);

	if (ic->ic_ntape)
		itreset(ic);

	if (ic->ic_busy) {
		ic->ic_busy = 0;
		idstart(is_disk + ic->ic_unit);
	}

	if (ic->ic_busy1) {
		ic->ic_busy1 = 0;
		itstart(is_tape + ic->ic_unit1);
	}

	splx(s);
	return 0;
bad:
	splx(s);
	return -1;
}

iswaituntildone(ip, csr)
	register struct is_param *ip;
	register u_char *csr;
{
	int i;

	i = ISTIMEOUTDELAY;

	while ((ip->ip_stat == 0 || ip->ip_stat == IPSTAT_BUSY) && i )
		i--;
	
	if (i == 0)
		printf("iswaituntildone: time out0, ip_stat 0x%x, csr 0x%x\n",
			ip->ip_stat, *csr);
	
	for (i = isdelayamt; i-- ; )
		;

	i = ISTIMEOUTDELAY;
	while (!(*csr & (ISCSR_CI|ISCSR_SI)) && i)
		i--;

	if (i == 0)
		printf("iswaituntildone: time out1, ip_stat 0x%x, csr 0x%x\n",
			ip->ip_stat, *csr);
#ifdef notdef
	else {
		if (isaltdebug)
			printf("isaltdebug: csr 0x%x\n", *csr);
	}	
#endif notdef
}
#endif NIS > 0
