/************************************************************************
 *									*
 *				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 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

/*
 * Interphase 2190/Storager common driver routines.
 * Sam Cramer.
 */

#include <setjmp.h>		/* to prevent bus faults */
#include <sys/types.h>		/* handy types */
#include <sys/buf.h>
#include <sys/param.h>		/* NBBY, DEV_BSIZE */
#include <s32/dkio.h>		/* to help dklabel.h */
#include <s32/dklabel.h>	/* smd label definition */
#include "isreg.h"		/* Interphase 2190/Storager registers */
#include "isvar.h"		/* 2190/Storager data structure definitions */
#include "../s32/cpu.h"		/* 2190/Storager data structure definitions */

/*
 * Globals used by iscmd() and friends.
 */

struct is_uib uib;
struct dk_label dklabel;
struct is_uib *is_uib = (struct is_uib *) &uib;		/* global uib */
struct dk_label *label = (struct dk_label *) &dklabel;	/* disk label */

struct is_disk is_disk[NDISK * 2];    /* per disk info, for every possible disk */
struct is_tape is_tape[NTAPE * 2];    /* per tape info, ... */

struct dk_sizes *is_sizes;		/* partition table for current disk */

u_char iserr;		/* error code returned by last iscmd() */

/*
 * Per controller templates.
 *
 * These are necessary because we need to hand-load CRM as the
 * variables need to be out in multibus memoryland.
 * Seems like a crock to me...
 */

struct is_param iopb;

struct is_ctlr i_templates[] = {
	{
		(char) 0,				/* ic_storager*/
		(char) 0,				/* ic_init */
		(struct is_device *) 0xf0,		/* ic_addr */
		(struct is_param *)&iopb,		/* ic_param */
		(struct is_disk *) &is_disk[0],		/* ic_disk */
		(struct is_tape *) &is_tape[0],		/* ic_tape */
	},
	{
		(char) 0,				/* ic_storager*/
		(char) 0,				/* ic_init */
		(struct is_device *) 0xf8,		/* ic_addr */
		(struct is_param *) &iopb,		/* ic_param */
		(struct is_disk *) &is_disk[NDISK],	/* ic_disk */
		(struct is_tape *) &is_tape[NTAPE],	/* ic_tape */
	},
	{
		(char) 0,				/* ic_storager*/
		(char) 0,				/* ic_init */
		(struct is_device *) 0xe0,		/* ic_addr */
		(struct is_param *)&iopb,		/* ic_param */
		(struct is_disk *) &is_disk[0],		/* ic_disk */
		(struct is_tape *) &is_tape[0],		/* ic_tape */
	},
	{
		(char) 0,				/* ic_storager*/
		(char) 0,				/* ic_init */
		(struct is_device *) 0xe8,		/* ic_addr */
		(struct is_param *) &iopb,		/* ic_param */
		(struct is_disk *) &is_disk[NDISK],	/* ic_disk */
		(struct is_tape *) &is_tape[NTAPE],	/* ic_tape */
	}
};

#ifdef DEBUG
char idebug = 1;	/* Print sundry helpful messages. */
#else  DEBUG
char idebug = 0;	/* Don't print sundry uninteresting trash. */
#endif DEBUG

extern int *nofault;


/*
 * Probes for a controller at the given address.
 * Returns 1 if found, 0 if not.
 */
isprobe(is)
	struct is_device *is;
{
	register int i, *savefault = nofault;
	jmp_buf is_fault;
	register found = 0;

	if (idebug)
		printf("istouch(0x%x)\n", is);
	nofault = (int *)is_fault;
	if (!setjmp(is_fault)) {
		if (idebug)
			printf("trying to touch it\n");

		is->ispb2 = 0;	/* touch the board with a safe operation */
#ifdef M68020
		flushWriteQueue();
#endif M68020

		if (idebug)
			printf("istouch: found it\n");
		found++;

		/*
		 * If we got here, the register was found.
		 */
	} else {
		if (idebug)
			printf("istouch: didn't find it\n");
	}

	nofault = savefault;
	return(found);
}

#ifdef XXX

/*
 * Probes for a controller at the given address.
 * Returns 1 if found, 0 if not.
 */
XXXisprobe(is)
	struct is_device *is;
{
	int i;
	if (idebug) {
		printf("ISPROBE: About to touch board at %x\n", &(is->ispb2));
		printf("is = %x\n", is);
	}
	ignerr = 1;	/* ignore buserrors */
	berrfound = 0;
	is->ispb2 = 0;	/* touch the board with a safe operation */
#ifdef M68020
	flushWriteQueue();
#endif M68020
	ignerr = 0;	/* DONT ignore buserrors */
	return(!berrfound);
}
#endif XXX

/*
 * Resets the controller, enabling it to accept commands.
 * Determines the controller type, and updates fields accordingly.
 */
isreset(ic)
	struct is_ctlr *ic;
{
	register struct is_device *ic_addr = 
				(struct is_device *) makeioaddr(ic->ic_addr);
	register struct is_device *ic_addr1 = ic_addr + 1;
	register i, j;
	jmp_buf is_fault;
	register int *savefault = nofault;

	if (idebug)
		printf("isreset\n");

	/* Test for storager by reading the 2nd register set */

	/* ignore buserrors */
	nofault = is_fault;
	if (!setjmp(is_fault)) {
		ic_addr1->iscsr = 0;	/* byte write */
		if (idebug)
			printf("about to flush write queue...\n");
#ifdef M68020
		flushWriteQueue();
#endif M68020
		/* must be a storager */
		ic->ic_storager = 1;
		nofault = savefault;
#ifdef DEBUG
		printf("\n  * storager * ");
#endif DEBUG
	} else {
		/* must be a 2190 */
		ic->ic_storager = 0;
		nofault = savefault;
#ifdef DEBUG
		printf("\n  * 2190 * ");
#endif DEBUG
		return 1;
	} 

	/* reset the controller board */

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

	 * TIMER was 10
	 */
#define TIMER 1000

	if (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI)) {
		/* Clear interrupt and wait for int bits to go away */
		ic_addr->iscsr = ISCSR_CLRI;
		for (i = 0x100000;
			(i != 0) && (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI));
			--i)
			;
	}

/* 1. Write reset - cb */
	ic_addr->iscsr = ISCSR_RESET;
/* 2. Delay. suggest 20000 - cb */
	for (i = 20000; --i >= 0;)
		;
/* 3. Write 0 to csr - cb */
	ic_addr->iscsr = 0;
/* 4. wait for done bit to go away - cb */
	for (i = 0x100000;
		(i != 0) && (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI)); 
		--i)
		;
/* 5. wait for done bit to come true */
	for (j = 0x100000;
		(j != 0) && !(ic_addr->iscsr & (ISCSR_SI | ISCSR_CI));
		--j)
		;
	if ((j == 0) || (i == 0)) {		/* timed out */
		printf("RESET TIMEOUT!\n");
		return 0;
	}
/* 6. clear register  */
	ic_addr->iscsr = ISCSR_CLRI;
/* 7. wait for done bit to go away again */
	for (i = 0x100000;
		(i != 0) && (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI));
		--i)
		;

	/*
	 * See what kind of controller it is.
	 */

	if (!iscmd(ic, 0, IPCMD_CONFIG, 0, 0, 0, 0, 0)) {
		printf("is: can't configure controller at addr 0x%x",
			(u_int)ic_addr);
		return(0);
	}

	if ((ic->ic_param->ip_flavor == IPFLAVOR_STRGR) ||
	    (ic->ic_param->ip_flavor == IPFLAVOR_NEWSTRGR))
		ic->ic_storager = 1;
	else
		ic->ic_storager = 0;

	return(1);
}

#ifdef XXX
/*
 * Resets the controller, enabling it to accept commands.
 * Determines the controller type, and updates fields accordingly.
 */
XXXisreset(ic)
	struct is_ctlr *ic;
{
	register struct is_device *ic_addr = 
				(struct is_device *) makeioaddr(ic->ic_addr);
	register struct is_device *ic_addr1 = ic_addr + 1;
	register i, j;

	if (idebug)
		printf("isreset\n");

	/* Test for storager by reading the 2nd register set */

	ignerr = 1;	/* ignore buserrors */
	berrfound = 0;
	printf("Here we go again.....\n");
	ic_addr1->iscsr = 0;	/* byte write */
	printf("about to flush write queue...\n");
#ifdef M68020
	flushWriteQueue();
#endif M68020
	ignerr = 0;	/* DONT ignore buserrs */

	printf("testing berrfound flag .....\n");
	if (berrfound) {
		/* must be a 2190 */
		printf("\n  * 2190 * ");
		return 1;
	} 

	/* must be a storager or we would have had a buserr */
	printf(" * storager * ");

	/* reset the controller board */

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

	 * TIMER was 10
	 */
#define TIMER 1000

	if (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI)) {
		/* Clear interrupt and wait for int bits to go away */
		ic_addr->iscsr = ISCSR_CLRI;
		for (i = 0x100000;
			(i != 0) && (ic_addr->iscsr & (ISCSR_SI
			| ISCSR_CI));
			--i)
			;
	}

/* 1. Write reset - cb */
	ic_addr->iscsr = ISCSR_RESET;
/* 2. Delay. suggest 20000 - cb */
	for (i = 20000; --i >= 0;)
		;
/* 3. Write 0 to csr - cb */
	ic_addr->iscsr = 0;
/* 4. wait for done bit to go away - cb */
	for (i = 0x100000;
		(i != 0) && (ic_addr->iscsr & (ISCSR_SI
		| ISCSR_CI)); --i)
		;
/* 5. wait for done bit to come true */
	for (j = 0x100000;
		(j != 0) && !(ic_addr->iscsr & (ISCSR_SI | ISCSR_CI));
		--j)
		;
	if ((j == 0) || (i == 0)) {		/* timed out */
		printf("RESET TIMEOUT!\n");
		return 0;
	}
/* 6. clear register  */
	ic_addr->iscsr = ISCSR_CLRI;
/* 7. wait for done bit to go away again */
	for (i = 0x100000;
		(i != 0) && (ic_addr->iscsr & (ISCSR_SI | ISCSR_CI));
		--i)
		;

	/*
	 * See what kind of controller it is.
	 */

	if (!iscmd(ic, 0, IPCMD_CONFIG, 0, 0, 0, 0, 0)) {
		printf("is: can't configure controller at addr 0x%x",
			(u_int)ic_addr);
		return(0);
	}

	ic->ic_storager = (ic->ic_param->ip_flavor == IPFLAVOR_STRGR);
	return(1);
}
#endif XXX


/*
 * Common open stuff.
 */

isopen(ic, ctlr)
	register struct is_ctlr *ic;
{
	register struct is_device *ic_addr;

	bcopy(&(i_templates[ctlr]), ic, sizeof(struct is_ctlr));
	ic_addr = (struct is_device *) makeioaddr(ic->ic_addr);

	if (ic->ic_init) return 1;
	/*
	 * Check to see if the Interphase controller is there by
	 * trying to touch the csr.
	 */

	if (!isprobe(ic_addr)) {
		printf("No ctlr at 0x%x\n", ic->ic_addr);
		return 0;
	}

	if (!isreset(ic)) {
		printf("Can't reset ctlr at 0x%x\n", ic->ic_addr);
		return 0;
	}

	/*
	 * Clear out the iopb for this controller, and the uib.
	 * Note that we have one uib for all controllers, pointed to
	 * by is_uib.
	 */

	bclear((char *)ic->ic_param, sizeof(struct is_param)); 
	bclear((char *)is_uib, sizeof(struct is_uib));
#ifdef	SA_ADVANCED
	ic->ic_init++;
#endif	SA_ADVANCED
	return(1);
}




int readcount = 0;

/*
 * iscmd/itcmd -- set up an IS command block and execute it
 * Note that itcmd is a one-liner which calls iscmd().
 *
 *	    returns 1 if successful, zero otherwise.
 */
iscmd(ic, unit, cmd, head,    cyl,   sec,  nsec, addr)
	struct is_ctlr *ic;
	caddr_t addr;
{
	register struct is_device *is = (struct is_device *)
						makeioaddr(ic->ic_addr);
	register struct is_param *ip;
	int i;
#ifdef DEBUG
	int retrycnt = 0;
#endif DEBUG
	if (idebug)
		printf("iscmd(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n",
			ic, unit, cmd, head,    cyl,   sec,  nsec, addr);
	

	/*
	** be sure "unit" doesn't include ctlr info:
	*/

	unit &= 0xf;

	/*
	** Craig says check for done and not busy
	*/

	if (is->iscsr & ISCSR_BUSY) {
		printf("iscmd: ctlr busy at top\n");
	}


	/*
	 * readcount code.
	 */

	if (cmd == IPCMD_INIT)
		readcount = 0;
	else if (cmd == IPCMD_READ)
		readcount++;

	/* 
	 * Tell the controller where the iopb lives.
	 */

	ip = ic->ic_param;
	is->ispb0 = (u_int) ip;
	is->ispb1 = (u_char)((u_int) ip >> NBBY);
	is->ispb2 = (u_char)((u_int) ip >> (2 * NBBY));

	/*
	 * Load up the IOPB.  Assume that all non-loaded fields are zero.
	 */

retry:
	ip->ip_cmd = cmd;
	ip->ip_opt = IPOPT_NORMAL;
	ip->ip_head = head;
	ip->ip_drive = unit;
	ip->ip_cyl0 = cyl;
	ip->ip_cyl1 = cyl >> NBBY;
	ip->ip_sec0 = sec;
	ip->ip_sec1 = sec >> NBBY;
	ip->ip_nsec0 = nsec;
	ip->ip_nsec1 = nsec >> NBBY;
	ip->ip_buf0 = (u_int) addr;
	ip->ip_buf1 = (u_int) addr >> NBBY;
	ip->ip_buf2 = (u_int) addr >> (2 * NBBY);
	ip->ip_dma = (cmd == IPCMD_TAPECONF) ? IT_TAPETIMEOUT: IS_DMA;
	ip->ip_ioaddr0 = (u_int) ic->ic_addr;
	ip->ip_ioaddr1 = (u_int) ic->ic_addr >> NBBY;
	ip->ip_stat = 0;

	/*
	** The following is a gift from Ross:
	*/

	/* Clear error and status bytes before issuing command... */
	ip->ip_err = ip->ip_stat = 0;


	is->iscsr = ISCSR_BUS | ISCSR_GO | ISCSR_CLRI;    /* Go! */

	/*
	** the following bit is extracted from fcn iswaitfordone(ic)
	** in crm/is.c
	*/

	/*
	 * It takes about 30 microseconds from the time that the SMD 2190
	 * recognizes a "GO" command until the "BUSY" bit goes true.
	 * - Sayings of the 2190 User's Guide rev 1.1 p24.
	 */

	/*
	** The true sequence of events is as follows:
	** time		event
	**   0 us		GO and CLRI issued
	**   2 us		csr goes busy
	**  30 us		stat goes busy
	** 150 us		stat goes ready
	** 165 us		csr goes done intr
	**
	** Therefore, we want to wait until this sequence is complete, so wait
	** until csr shows done.
	** But there is a rub: at time == 0, the csr still shows the
	** done intr bit set from the last command.
	** To avoid this problem, first wait until stat goes non-zero
	** (don't forget to clear stat before issuing command)
	** then wait for either done or status change intr in csr.
	*/

	/* isdelay(); is no longer necessary */

	while (!ip->ip_stat)	/* ipstat nonzero? */
		;
	while (ip->ip_stat == IPSTAT_BUSY)	/* loop while busy */
		;
	while (!(is->iscsr & (ISCSR_CI | ISCSR_SI)))	/* until SI or CI */
		;
	/*
	 * Sanity check: if any unit interrupts, check that it's us.
	 */
	if (ic->ic_storager && is->isintunit != ip->ip_drive)
		printf("units interrupting: 0x%x (not unit 0x%x)\n",
			is->isintunit, ip->ip_drive);

	is->iscsr = ISCSR_CLRI;

	i = 0x8000;
	while ((is->iscsr & ISCSR_CI) && i--)		/* until CI goes away */
		;

	iserr = ip->ip_stat;

	if (ip->ip_stat == IPSTAT_DONE)
		return 1;
	else
	{
		switch (ip->ip_stat)
		{
			default:
				printf("id%x: unknown status 0x%x.\n",
					ip->ip_drive);
				break;
			case IPSTAT_BUSY:
				/*
				 * Famous last words:
				 * "It'll never happen."
				 */
				printf("id%x: disk went busy again\n",
					ip->ip_drive);
				break;
			case IPSTAT_ERR:
				if (cmd == IPCMD_READTAPE && 
				   (ip->ip_err == IPERR_FILEMARK ||
				    ip->ip_err == IPERR_FM))
					break;
				/* 
				 * Error! 
				 * Dump out iopb, run command to dump out uib.
	 			 */

				/*
				 * Don't let the user know that you screwed up, 
				 * cause you'll get hollered at.
				 */
			   if (idebug) {
				register int i;
				register u_char *p = (u_char *)ip;
				struct is_uib uib;
				register u_char *u = (u_char *)&uib;

				printf("Error! read number %d\niopb:\n", 
					readcount);

				for (i = 0; i < 0x18; i++)
					printf("%x ", *p++);
				printf("\n");


				for (i = 0; i < 0x20; i++)
					*u++ = 0;

				if (unit < ID_NSLAVE) {
					iscmd(ic,unit,IPCMD_READUIB,
					    0,0,0,0, &uib);
					u = (u_char *)&uib;
					printf("uib:\n");
					for (i = 0; i < 0x20; i++) 
					{
						if (i == 0x10)
							printf("\n");
						printf("%x ", *u++);
					}
					printf("\n");
				}
			   }

			printf("is: ERR 0x%x cmd 0x%x hd %d cyl %d sec %d nsec %d, retrying\n",
				ip->ip_err,cmd,head,cyl,sec,nsec);
			break;
		} /* switch */
		return 0;
	} /* if */
}


/*
 * Delay loop to allow the 2190 or Storager to settle after CLRIing, etc.
 */

isdelay()
{
	int i;

	for (i = 10 * ISDELAYAMT; i > 0; i-- )
		;
}


#ifdef notdef
/*
 * isdstatus -- print out the current IS status from the IOPB
 */
isdstatus(print)
{
	register struct is_param *ip = dcontroller->ic_param;
	register int returnval = 0;
	register struct is_device *ic_addr = makeioaddr(ic->ic_addr);

	if (print) {
		printfdiskstatmsg);
		wrtstatus(iscstatus, id->iscsr, 7);
	}

	switch(ip->ip_stat) {
	case IPSTAT_DONE:
		if (print)
			printf("RDY)");
		returnval++;
		break;
	case IPSTAT_BUSY:
		if (print)
			printf("BSY)");
		returnval++;
		break;
	case IPSTAT_ERR:
		if (print) {
			printf("E) ERR ");
			wrthex(ip->ip_err, 2);
		}
		break;
	default:
		if (print) {
			printf("???: ");
			wrthex(ip->ip_stat, 2);
			printf(")");
		}
		break;
	}
	printfnewline);
	return(returnval);
}
#endif notdef
