/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)kern-ml:pic.c	1.3.1.1"

#include "sys/cmn_err.h"
#include "sys/ipl.h"            /* interrupt priority levels */
#include "sys/pic.h"            /* PIC constants */

/*** 380 Support : begin ***/
#ifdef	AT380
#include "sys/types.h"
#include "sys/immu.h"
#include "sys/dma.h"
#endif
/*** 380 Support : end ***/

#include "sys/eisa.h"
extern int level_intr_mask;
extern int eisa_bus;

/* Programmable Interrupt Controllers */

/* defined in conf.c, which is generated by config: */
extern  int     (*ivect[])();           /* interrupt routines */
extern  unsigned char intpri[];         /* priority levels for interrupts */
extern  int     nintr;                  /* number of interrupts */

/* defined in modules/pic/space.c: */
extern  unsigned short cmdport[];       /* command port addrs for pics */
extern  unsigned short imrport[];       /* intr mask port addrs for pics */
extern  unsigned char masterpic[];      /* index of this pic's master */
extern  unsigned char masterline[];     /* line this pic connected to */
extern  unsigned char curmask[];        /* current masks for pics */
extern  unsigned char picbuffered;      /* true if pic buffered */
extern  int     npic;                   /* number of pics configured */
/*
 * iplmask[] contains the pic masks for each interrupt priority level.
 * It is effectively dimensioned iplmask[IPLHI][NPIC],
 * and is initialized from intpri[].
 */
extern  unsigned char iplmask[];

extern  int     intnull();              /* null interrupt routine */
extern	int	splvalid_flag;

int ipl;                        /* current interrupt priority level */
int picipl;                     /* interrupt priority level of pic masks */
int picmax;                     /* index of last pic */

/*
 * picinit() initializes the PICs to ipl 0 and leaves the IF flag off,
 * putting the system at interrupt priority level IPLHI,
 * the level set by splhi and spl7.
 *
 * Interrupts are enabled by spl'ing to a lower ipl.
 *
 * picinit() is called first thing in main().
 *
 * Failed logic check actions in picinit() require that the console driver
 * be initialized before the pics.  cmn_err() should not
 * be called because the sysdump() routine uses interrupts.
 */

picinit()
{
	int mask, intno, bit, slaves, cmd, imr, pic, level;
	int i;

	asm("   cli");  /* disable interrupts just in case */

	picmax = npic - 1;

	/*
	 * Set up iplmask[][] from ivect[] and intpri[].
	 * iplmask[0][] is ORed into other levels
	 * so that unused interrupts are disabled.
	 * If an interupt number is not configured, set its bit in
	 * iplmask[0][]; otherwise set its bit in the masks at its
	 * priority level and higher.
	 *
	 * NOTE: master pic must always be pic zero.
	 *	 pic base vector is always PIC_VECTBASE + picno * 8.
	 */
	for (pic = 0; pic < npic; pic++) {    /* loop thru pics */
		mask = 0;
		intno = pic * 8;

		for (bit = 1; bit <= 0x80; bit <<= 1, intno++) {
			if (intno >= nintr || ivect[intno] == intnull)
			{
				/* For interrupts that we never expect, */
				/* give them a valid priority anyway.   */
				mask |= bit;
				for ( level = SPLTTY; level < IPLHI; level++)
					iplmask[level * npic + pic] |= bit;
			}
			else {
				if ((level = intpri[intno]) == 0
				    || level > IPLHI) {
					printf(
	"picinit: bad interrupt priority in intpri[]; can't continue\n");
					for(;;) ; /* nothing else to do */
				}
				for ( ; level < IPLHI; level++)
					iplmask[level * npic + pic] |= bit;
			}
		}
		iplmask[pic] = mask;    /* iplmask[0][pic] */
	}

	/* identify lines on master to which slaves are connected */
	slaves = 0;
	for (pic = 1; pic < npic; pic++)        /* for each slave */
		slaves |= 1 << masterline[pic];

	/* enable slave lines on master */
	iplmask[0] &= ~slaves;

	/*
	 * initialize master first
	 */
/*** 380 Support : begin ***/
#ifdef AT380
	/*
	 * Set the vector registers to match the 8259
	 */
	outb(VRB0, PIC_VECTBASE);
	outb(VRB1, PIC_VECTBASE + 1);
	outb(VRB3, PIC_VECTBASE + 3);
	outb(VRB4, PIC_VECTBASE + 4);
	outb(VRB5, PIC_VECTBASE + 5);
	outb(VRB6, PIC_VECTBASE + 6);
	outb(VRB7, PIC_VECTBASE + 7);
#endif
/*** 380 Support : end ***/
	
	/* ICW1: Edge-triggered, Cascaded, need ICW4 */
	outb(cmdport[0], PIC_ICW1BASE|PIC_NEEDICW4);
	
	/* ICW2: start master vectors at PIC_VECTBASE */
	outb(imrport[0], PIC_VECTBASE);
	
	/* ICW3: define which lines are connected to slaves */
	outb(imrport[0], slaves);

	/* ICW4: buffered master (?), norm eoi, mcs 86 */

/*** 380 Support : begin ***/
#ifdef AT380
	/*
	 * Use auto-EOI mode.
	 */
	outb(imrport[0], PIC_AUTOEOI|PIC_86MODE );
#else
	outb(imrport[0],
	     picbuffered ? PIC_MASTERBUF|PIC_86MODE : PIC_86MODE );
#endif /* AT380 */
#ifdef AT380
	inb(cmdport[0]);	/* to avoid a bug in the 380 */
	inb(cmdport[0] + 2);	/* read back ICW2 to reset IRQ1.5 */
#endif
/*** 380 Support : end ***/

	/* OCW1: Start the master with all interrupts off */
	outb(imrport[0], 0xFF);
	
	/* OCW3: set master into "read isr mode" */
	outb(cmdport[0], PIC_READISR);
	
	/*
	 * now initialize slaves
	 */

/*** 380 Support : begin ***/
#ifdef AT380
	/*
	 * Set the vector registers for bank C here. We know that
	 * bank C is the only slave.
	 */
	outb(VRC0, PIC_VECTBASE + 8);
	outb(VRC1, PIC_VECTBASE + 9);
	outb(VRC2, PIC_VECTBASE + 10);
	outb(VRC3, PIC_VECTBASE + 11);
	outb(VRC4, PIC_VECTBASE + 12);
	outb(VRC5, PIC_VECTBASE + 13);
	outb(VRC6, PIC_VECTBASE + 14);
	outb(VRC7, PIC_VECTBASE + 15);
#endif
/*** 380 Support : end ***/

	for (pic = 1; pic < npic; pic++) {
		cmd = cmdport[pic];
		imr = imrport[pic];

		/* ICW1: Edge-triggered, Cascaded, need ICW4 */
		outb(cmd, PIC_ICW1BASE|PIC_NEEDICW4);

		/* ICW2: set base of vectors */
		outb(imr, PIC_VECTBASE + pic * 8);

		/* ICW3: specify ID for this slave */
		outb(imr, masterline[pic]);

		/* ICW4: buffered slave (?), norm eoi, mcs 86 */
/*** 380 Support : begin ***/
#ifdef AT380
		outb(imr, PIC_AUTOEOI|PIC_86MODE );
#else
		outb(imr,
		     picbuffered ? PIC_SLAVEBUF|PIC_86MODE : PIC_86MODE );
#endif /* AT380 */
#ifdef AT380
		inb(cmd);	/* to avoid a bug in the 380 */
		inb(cmd + 2);	/* read back ICW2 to reset IRQ1.5 */
#endif
/*** 380 Support : end ***/

		/* OCW1: set interrupt mask for IPL zero */
		outb( imr, (curmask[pic] = iplmask[pic]));

		/* OCW3: set pic into "read isr mode" */
		outb(cmd, PIC_READISR);
	}

/*** 380 Support : begin ***/
#ifdef AT380
	/*
	 * Initialize bank A to make it transparent from here on.
	 * With auto-EOI mode, we never have to give it an EOI and
	 * it's transparent to the rest of the code.
	 */
	outb(ACMD_PORT, 0x1b);	/* ICW1 */
	outb(AIMR_PORT, 0x02);	/* ICW2 */
	outb(AIMR_PORT, 0x02);	/* ICW4 */
	outb(AIMR_PORT, 0xfb);	/* mask */
	outb(VRA0, 0x02);	/* shouldn't ever get these, so */
	outb(VRA1, 0x02);	/* have them come in as NMI's */
	outb(VRA1_5, 0x02);
	outb(VRA3, 0x02);
	outb(VRA4, 0x02);
	outb(VRA7, 0x02);
	inb(ACMD_PORT);		/* to avoid a bug in the 380 */
	inb(ACMD_PORT + 2);	/* read back ICW2 to reset IRQ1.5 */
#endif
/*** 380 Support : end ***/

	/* set master interrupt mask for IPL zero */
	outb( imrport[0], (curmask[0] = iplmask[0]));

	/* set interrupt priority levels */
	ipl = IPLHI;            /* interrupts are disabled */
	picipl = 0;
	splvalid_flag=1;

        if( eisa_bus == 1 ) {
                /**
                ** Set Level Mode for those IRQ's requiring it.
                **/
                for( i = 0; i < 16; ++i ) {
                        if( level_intr_mask & (1 << i) ) {
                                set_elt( i, LEVEL_TRIG );
                        }
                }
        }
}

/*
 * Search the interrupt handler vector for the specified interrupt routine,
 * starting at the specified interrupt number.
 * Returns -1 if the routine is not found.
 */

int
findintno(intrtn, start)
	int (*intrtn)();
{
	int i;

	for (i = start; i < nintr; i++)
		if (ivect[i] == intrtn)
			return i;
	return -1;
}


ipl0panic2()
{
	cmn_err(CE_PANIC,
  "ret_user: interrupt priority level not zero at return to user mode\n");
}

splpanic2()
{
	cmn_err(CE_PANIC,
		"spl*: logic error in misc.s\n");
}

#ifdef DEBUG
spl7panic2()
{
	cmn_err(CE_PANIC,
		"spl7: logic error in misc.s\n");
}

splintpanic2()
{
	cmn_err(CE_PANIC,
		"splint: logic error in misc.s\n");
}

splxintpanic2()
{
	cmn_err(CE_PANIC,
		"splxint: logic error in misc.s\n");
}

setpicmaskspanic2()
{
	cmn_err(CE_PANIC,
		"setpicmasks: logic error in misc.s\n");
}
#endif /* DEBUG */

set_elt(irq, mode)
int irq, mode;
{
	int i, port;

	switch (irq) {

	case 0:
	case 1:
	case 2:
	case 13:
		mode = EDGE_TRIG;
		break;
	}
	if (irq > 7) {
		irq -= 8;
		port = ELCR_PORT0;
	} else
		port = ELCR_PORT1;
	i = inb(port);
	i &= 1 << irq;
	i |= mode << irq;
	outb(port, mode);
	return;
}
