File:  [MW Coherent from dump] / coherent / d / kernel / USRSRC / i8086 / drv / dg.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * dg - device driver for Digiboard PC/Xe intelligent multiport controller
 *
 * $Header: /var/lib/cvsd/repos/coherent/coherent/d/kernel/USRSRC/i8086/drv/dg.c,v 1.1.1.1 2019/05/29 04:56:39 root Exp $
 *
 * $Log: dg.c,v $
 * Revision 1.1.1.1  2019/05/29 04:56:39  root
 * coherent
 *
 * Revision 1.3	91/03/05  12:23:42	root
 * Fix cast on dg_ram_base
 * 
 */
 
/*
 * Various notes:
 *
 *	FEP = front-end-processor (the 80186 on the Digiboard)
 *
 *	At port DG_IOB:
 *		the 2's bit is 1 to enable DPRAM, 0 to disable
 *		the 4's bit is 1 to reset FEP, 0 to clear reset 
 *
 *	There is a bug in the current ldlib.a version of setivec and clrivec:
 *	they only work during xxload() and xxunload() due to use of "ucs"
 *	instead of "getcs()" to determine the CS for the interrupt routine.
 */

/*
 * Definitions.
 *
 */
#define	DG_RAM_LENGTH	0x10000L
#define DG_MEMORY_SEG	0xF000		/* dual-port ram base on FEP side */
#define DG_BIOS_ADDR	0xF800
#define DG_FEPOS_ADDR	0x2000
#define DG_BIOS_LOADER	0x80		/* minor number to write to BIOS */
#define DG_FEPOS_LOADER	0x40		/* minor number to write to FEPOS */
#define DG_BIOS_LENGTH	0x800		/* PC/Xe BIOS is 2k bytes */
#define DG_BIOS_CONFIRM	0x0C00		/* look for "GD" here */
#define DG_FEP_CONFIRM	0x0D20		/* look for "OS" here */
#define DG_BIOS_REQ	0x0C40		/* Start FEP BIOS requests here */
#define BIOS_GOOD	('G' + ('D'<<8))	/* "GD" */
#define FEPOS_GOOD	('O' + ('S'<<8))	/* "OS" */

#define CSTART		0x400		/* start addr of command queue */
#define NPORT		0x0C22		/* addr of # of ports */
#define CIN		0x0D10		/* addr for command in pointer */
#define COUT		0x0D12		/* addr for command out pointer */
#define ISTART		0x800		/* start addr of event queue */
#define EIN		0x0D18		/* addr for event in pointer */
#define EOUT		0x0D1A		/* addr for event out pointer */
#define INTERVAL	0x0E04		/* addr for ticks between irpts */
#define EVENT_LEN	4		/* bytes per FEP event */
#define COMMAND_LEN	4		/* bytes per FEP command */

/*
 * Includes.
 */
#include "coherent.h" 
#include <sys/io.h>		/* IO */
#include <sys/sched.h>		/* [CIS]VPAUSE */
#include <sys/uproc.h>		/* u.u_error */
#include <sys/proc.h>		/* wakeup();
				   includes sys/types.h - faddr_t, paddr_t
				   and sys/timeout.h - TIM
				   needs coherent.h for KERNEL */
#include <sys/con.h>		/* CON */
#include <sys/stat.h>		/* minor(dev) */
#include <devices.h>		/* device major numbers, including PE_MAJOR */
#include <errno.h>

/*
 * Export Functions.
 */

/*
 * Export variables - these may be patched in order to configure the driver.
 */
long	DG_RAM = 0xF0000L;	/* segment for 64k of dual-port RAM */
int	DG_IOB = 0x200;		/* address of i/o byte for controller */
int	DG_INT = 15;		/* IRQ number for board's interrupt */

/*
 * Import Functions
 */
int	nulldev();
int	nonedev();

/*
 * Local functions.
 */
static dgload();
static dgunload();
static dgopen();
static dgclose();
static dgread();
static dgwrite();
static void dgdelay();
static dg_start_timing();
static dg_stop_timing();
static int dginit2();
static int dginit3();
static void dgintr();
 
/*
 * Local variables.
 */
static faddr_t	dg_ram_fp;	/* (far *) to access screen */
static paddr_t	dg_ram_base;	/* physical address of screen base */
static TIM	delay_tim;	/* needed for calls to timeout() */
static TIM	timeout_tim;	/* needed for calls to timeout() */
static int	dg_expired;	/* TRUE after local timeout */
static int	bios_loading;	/* TRUE if minor device for BIOS load open */
static int	fepos_loading;	/* TRUE if minor device for FEPOS load open */
static int	load_byte_ct;	/* place-holder for BIOS/FEPOS load */
static int	board_ready;	/* TRUE when all board initialization done */
static int	dg_bios_wait;	/* TRUE if waiting for BIOS to be loaded */
static int	dg_fepos_wait;	/* TRUE if waiting for FEPOS to be loaded */
static int	nport;		/* number of ports on the Digiboard */
static int	test_irq;	/* TRUE during startup */

/*
 * Configuration table - another export variable.
 */
CON dgcon ={
	DFCHR,				/* Flags */
	PE_MAJOR,			/* Major index */
	dgopen,				/* Open */
	dgclose,			/* Close */
	nulldev,			/* Block */
	dgread,				/* Read */
	dgwrite,			/* Write */
	nulldev,			/* Ioctl */
	nulldev,			/* Powerfail */
	nulldev,			/* Timeout */
	dgload,				/* Load */
	dgunload,			/* Unload */
	nulldev				/* Poll */
};

/*
 * Load Routine.
 */
static dgload()
{
	char v;

	setivec(DG_INT, dgintr);
	/*
	 * Allocate a selector to map onto the dual-port RAM.  ptov() will
	 * return the first available selector of the 8,192 possible.
	 */
	dg_ram_base = (paddr_t)((long)(unsigned)DG_RAM << 4);
	dg_ram_fp = ptov(dg_ram_base, (fsize_t)DG_RAM_LENGTH);
	
	/*
	 * Reset the board and wait.
	 * Actual delay is a tick = 0.01 sec; only need 1msec.
	 */
	outb(DG_IOB, 0x04);
	dgdelay(1);
	
	/*
	 * Read board ID.
	 */
	v = inb(DG_IOB);
	if ((v & 0x01) == 0x01) {
		printf("Error - board type is PC/Xi\n");
		return;
	} else {
		outb(DG_IOB, 0x05);	/* hold FEP reset */
		v = inb(DG_IOB);
		if ((v & 0x01) == 0x01) {
			printf("Error - board type is PC/Xm\n");
			return;
		} else
			printf("PC/Xe ID found\n");
	}
	
	/*
	 * Board Reset.
	 */
	outb(DG_IOB, 0x04);	/* reset board */
	dg_start_timing(100);	/* start 1-second timer */
	while ((inb(DG_IOB) & 0x0E) != 0x04) {
		if (dg_expired) {
			printf("Error - PC/Xe failed to reset\n");
			return;
		}
		dgdelay(10);
	}
	outb(DG_IOB, 0x06);	/* enable memory */
	printf("PC/Xe passed reset\n");

	/*
	 * Minimal test of PC/Xe's 64k of dual-ported RAM.
	 */
	sfword(dg_ram_fp, 0xA55A);		/* store a "far" word */
	sfword(dg_ram_fp + 2, 0x3CC3);
	sfword(dg_ram_fp + 0xFFFC, 0xA55A);
	sfword(dg_ram_fp + 0xFFFE, 0x3CC3);
	if (ffword(dg_ram_fp) != 0xA55A		/* fetch a "far" word */
	||  ffword(dg_ram_fp + 2) != 0x3CC3
	||  ffword(dg_ram_fp + 0xFFFC) != 0xA55A
	||  ffword(dg_ram_fp + 0xFFFE) != 0x3CC3) {
		printf("Error - PC/Xe failed memory test\n");
		return;
	} else
		printf("PC/Xe passed memory test\n");
		
	/*
	 * Load and execute the PC/Xe BIOS
	 */
	outb(DG_IOB, 0x06);	/* enable memory */
	dg_bios_wait = 1;
	printf("PC/Xe waiting for BIOS load\n");
}

static dgunload()
{
	if (board_ready) {
		board_ready = 0;
	}
	
	/*
	 * Turn off and unhook interrupts from FEPOS
	 */
	sfword(dg_ram_fp+INTERVAL, 0);	/* stop host interrupts */
	outb(DG_IOB, 0x04);		/* Disable DPRAM and hold FEP reset */
	clrivec(DG_INT);

	/*
	 * We have to free up the selector now that we're done using it.
	 */
	vrelse(dg_ram_fp);
}

/*
 * Open Routine.
 */
static dgopen( dev, mode )
dev_t dev;
{
	/*
	 * If minor number has 128's bit set to 1, this is an attempt to
	 * transfer the BIOS to the FEP.
	 */
	if (minor(dev) & DG_BIOS_LOADER) {
		if (bios_loading) {
			u.u_error = EDBUSY;
			return;
		} else {
			/*
			 * Only allow BIOS xfer if we are waiting for it.
			 */
			if (dg_bios_wait) {
				bios_loading = 1;
				load_byte_ct = 0;
			} else {
				u.u_error = EIO;
				return;
			}
		}
	/*
	 * If minor number has 64's bit set to 1, this is an attempt to
	 * transfer the FEPOS to the FEP.
	 */
	} else if (minor(dev) & DG_FEPOS_LOADER) {
		if (fepos_loading) {
			u.u_error = EDBUSY;
			return;
		} else {
			/*
			 * Only allow FEPOS xfer if we are waiting for it.
			 */
			if (dg_fepos_wait) {
				fepos_loading = 1;
				load_byte_ct = 0;
			} else {
				u.u_error = EIO;
				return;
			}
		}
	} else {
	}
}

/*
 * Close Routine.
 */
static dgclose( dev )
dev_t dev;
{
	/*
	 * If minor number has 128's bit set to 1, this is an attempt to
	 * transfer the BIOS to the FEP.
	 */
	if (minor(dev) & DG_BIOS_LOADER) {
		bios_loading = 0;
		/*
		 * Only set dg_fepos_wait if enough bytes got written.
		 */
		if (load_byte_ct == DG_BIOS_LENGTH) {
			/*
			 * After BIOS is xferred, try to finish
			 * PC/Xe initialization.
			 */
			if (dginit2()) {
				dg_bios_wait = 0;
				dg_fepos_wait = 1;
				printf("PC/Xe waiting for FEPOS load\n");
			}
		}
	/*
	 * If minor number has 64's bit set to 1, this is an attempt to
	 * transfer the FEPOS to the FEP.
	 */
	} else if (minor(dev) & DG_FEPOS_LOADER) {
		fepos_loading = 0;
		/*
		 * After BIOS is xferred, try to finish
		 * PC/Xe initialization.
		 */
		if (dginit3()) {
			dg_fepos_wait = 0;
			board_ready = 1;
			printf("PC/Xe ready for use\n");
		}
	} else {
	}
}

/*
 * Read Routine.
 */
static dgread( dev, iop )
dev_t dev;
register IO * iop;
{
#if 0
	static int offset;
	int c;
	/*
	 * Read a character code from video RAM
	 * Start reading RAM just after where previous read ended
	 *
	 * Note that "offset" is the value of the displacement into
	 * the screen RAM. Any expression which results in a value
	 * which is less than DG_RAM_LENGTH is OK here.
	 */
	while(iop->io_ioc) {
		c = ffbyte(dg_ram_fp + offset); /* fetch a "far" byte */
		if(ioputc(c, iop) == -1)
			break;
		offset += 2;
		offset %= DG_RAM_LENGTH;
	}
#endif	
}

/*
 * Write Routine.
 */
static dgwrite( dev, iop )
dev_t dev;
register IO * iop;
{
	/*
	 * If minor number has 128's bit set to 1, this is an attempt to
	 * transfer the BIOS to the FEP.
	 */
	if (minor(dev) & DG_BIOS_LOADER) {
		int c;

		while ((c = iogetc(iop)) >= 0 && load_byte_ct < DG_BIOS_LENGTH) {
			sfbyte(dg_ram_fp + DG_BIOS_ADDR + load_byte_ct, c);
			load_byte_ct++;
		}
	/*
	 * If minor number has 64's bit set to 1, this is an attempt to
	 * transfer the FEPOS to the FEP.
	 */
	} else if (minor(dev) & DG_FEPOS_LOADER) {
		int c;

		while ((c = iogetc(iop)) >= 0) {
			sfbyte(dg_ram_fp + DG_FEPOS_ADDR + load_byte_ct, c);
			load_byte_ct++;
		}
	} else {
	}
}

/*
 * Delay for some number of clock ticks.
 * 286/386 kernel ticks are at 100Hz
 * Use kernel function sleep(), which is NOT the system call by that name.
 */
static void dgdelay(ticks)
int ticks;
{
	timeout(&delay_tim, ticks, wakeup, (int)&delay_tim);
	sleep((char *)&delay_tim, CVPAUSE, IVPAUSE, SVPAUSE);
}

/*
 * Start a timeout for some number of ticks.
 * Caller knows timer has expired when "dg_expired" goes to 1.
 *
 * Sample invocation:
 *	dg_start_timing(n);
 *	while (check for desired event fails) {
 *		if (dg_expired) {
 *			...failure stuff..
 *			break;
 *		}
 *		dgdelay(m); <= needed to allow kernel to update timers
 *	}
 */
static dg_start_timing(ticks)
int ticks;
{
	dg_expired = 0;
	timeout(&timeout_tim, ticks, dg_stop_timing, 1);
}

/*
 * Stub function called only by dg_start_timing()
 */
static dg_stop_timing(flagval)
int flagval;
{
	dg_expired = flagval;
}

/*
 * Second part of PC/Xe initialization - done after the BIOS has been
 * written to dual-ported RAM.
 */
static int dginit2()
{
	/*
	 * Execute FEP BIOS
	 */
	sfword(dg_ram_fp + DG_BIOS_CONFIRM, 0);	/* clear confirm word */
	outb(DG_IOB, 0x02);			/* Release reset */
	dg_start_timing(1000);			/* start 10-second timer */

	while (ffword(dg_ram_fp + DG_BIOS_CONFIRM) != BIOS_GOOD) {
		if (dg_expired) {
			printf("Error - PC/Xe BIOS won't start\n");
			return 0;
		}
		dgdelay(10);
	}
	printf("PC/Xe BIOS started\n");

	return 1;
}

/*
 * Third part of PC/Xe initialization - done after the FEPOS has been
 * written to dual-ported RAM.
 */
static int dginit3()
{
	int cmd;

	/*
	 * Ask FEP BIOS to move FEPOS into host memory.
	 */
	sfword(dg_ram_fp + DG_BIOS_REQ, 0x0002);
	sfword(dg_ram_fp + DG_BIOS_REQ+2, DG_MEMORY_SEG+0x200);
	sfword(dg_ram_fp + DG_BIOS_REQ+4, 0x0000);
	sfword(dg_ram_fp + DG_BIOS_REQ+6, 0x0200);
	sfword(dg_ram_fp + DG_BIOS_REQ+8, 0x0000);
	sfword(dg_ram_fp + DG_BIOS_REQ+0xA, 0x2000);
	outb(DG_IOB, 0x0A);			/* Toggle interrupt */
	outb(DG_IOB, 0x02);
	dg_start_timing(100);			/* start 1-second timer */
	while (ffword(dg_ram_fp + DG_BIOS_REQ) != 0) {
		if (dg_expired) {
			printf("Error - PC/Xe FEPOS move failed\n");
			return;
		}
		dgdelay(10);
	}
	printf("PC/Xe FEPOS relocated to host RAM\n");

	/*
	 * Execute FEPOS
	 */
	sfword(dg_ram_fp + DG_BIOS_REQ, 0x0001);
	sfword(dg_ram_fp + DG_BIOS_REQ+2, 0x0200);
	sfword(dg_ram_fp + DG_BIOS_REQ+4, 0x0004);
	sfword(dg_ram_fp + DG_FEP_CONFIRM, 0);	/* clear confirm word */
	outb(DG_IOB, 0x0A);			/* Toggle interrupt */
	outb(DG_IOB, 0x02);
	dg_start_timing(500);			/* start 5-second timer */
	while (ffword(dg_ram_fp + DG_FEP_CONFIRM) != FEPOS_GOOD) {
		if (dg_expired) {
			printf("Error - PC/Xe FEPOS won't start\n");
			printf("Failure code (%x)\n",
				ffword(dg_ram_fp + DG_BIOS_REQ));
			return 0;
		}
		dgdelay(10);
	}
	printf("PC/Xe FEPOS started\n");
	nport = ffbyte(dg_ram_fp+NPORT);

	/*
	 * Enable and test interrupts from FEP
	 */
	sfword(dg_ram_fp+INTERVAL, 1);		/* request host interrupts */
	cmd = ffword(dg_ram_fp+CIN);		/* get command pointer */
	sfword(dg_ram_fp+CSTART+cmd, 0xA1FF);	/* send an invalid command */
	sfword(dg_ram_fp+CSTART+cmd+2, 0xC3B2);
	sfword(dg_ram_fp+CIN, (cmd+4)&0x3ff);	/* update command pointer */
	dg_start_timing(100);			/* start 1-second timer */
	test_irq = 1;
	while (test_irq) {
		if (dg_expired) {
			printf("Error - PC/Xe no FEPOS interrupts\n");
			return 0;
		}
		dgdelay(10);
	}
	printf("PC/Xe interrupts working\n");
	
	return 1;
}

/*
 * Interrupt handler.
 *
 * No specific action is needed to clear the interrupt
 * as it was a pulse sent from the FEPOS to host's IRQ line.
 */
static void dgintr()
{
	int cin, cout, ein, eout;
	unsigned char event[EVENT_LEN];
	int i;

	cin = ffword(dg_ram_fp+CIN);
	cout = ffword(dg_ram_fp+COUT);
	ein = ffword(dg_ram_fp+EIN);
	eout = ffword(dg_ram_fp+EOUT);

	if (board_ready) {
		/*
		 * Remove all packets from event queue.
		 */
		while (ffword(dg_ram_fp+EIN) != ffword(dg_ram_fp+EOUT)) {
			eout = ffword(dg_ram_fp+EOUT);
			for (i = 0; i < EVENT_LEN; i++)
				event[i] = ffbyte(dg_ram_fp+ISTART+eout+i);
			printf("%x %x %x %x\n", event[0],event[1],event[2],event[3]);	
			sfword(dg_ram_fp+EOUT, (eout+4)&0x3ff);
		}
	} else {	/* e.g., if test_irq is TRUE */
		test_irq = 0;
		/*
		 * Attempt to clear the IRQ condition in the FEP.
		 */
		sfword(dg_ram_fp+EOUT, ffword(dg_ram_fp+EIN));
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.