www.pudn.com > vxworks0108.rar > vg4Intr.c


/* vg4Intr.c - MPC107 EPIC and M1543C-8259 driver */

/* Copyright 1984-1997 Wind River Systems, Inc. */
/*
Description:
    This module implements the Chaparral Epic and the ALI M1543C PCI-ISA Bridge
    (PIB) drivers.

    These merged drivers provide support for initializing their respective devices
    and handling interrupts from them.

/*---------------------------------------------------------------- +includes+-*/
#include "type.h"
#include "asm.h"
#include "reg.h"
#include "vg4Intr.h"
#include "vg4.h"
#include "mpc107.h"
#include "esf.h"
#include "config.h"
#include "syslib.h"
#include "ivppc.h"
#include "vxworks.h"
/*-----------------------------------------------------------------(includes)-*/


/*------------------------------------------------------------------+defines+-*/
#undef EPIC_INT_DEBUG
#undef IBC_INT_DEBUG


#define EPIC_EOI            sysPciWrite32(EPIC_ADDR(EPIC_CPU_EOI_REG), 0)

#define IBC_INT_PENDING 0x80
#define IBC_INT_LVL2    2
#define IBC_INT_LVL7    7
#define IBC_INT_LVL15   15
#define IBC_INT_PENDING     0x80

/* size of system interrupt table */
#define INT_TBL_SIZE        256
/*------------------------------------------------------------------(defines)-*/


/*-----------------------------------------------------------------+typedefs+-*/
/*-----------------------------------------------------------------(typedefs)-*/


/*------------------------------------------------------------------+globals+-*/
IMPORT UINT     sysVectorIRQ0;                  /* vector for IRQ0 */
INT_HANDLER_DESC * sysIntTbl [INT_TBL_SIZE];    /* system interrupt table */

/* Mask values are the currently disabled sources */

LOCAL UINT8     sysPicMask1 = 0xfb;             /* all levels disabled */
LOCAL UINT8     sysPicMask2 = 0xff;

/* Level values are the interrupt level masks */

LOCAL UINT8     sysPicLevel1;
LOCAL UINT8     sysPicLevel2;
LOCAL UINT8     sysPicLevelCur;         /* current priority level, 0 to 16 */

LOCAL UCHAR     sysIbcPhantomInt (UCHAR *intNum, int lvl7Int, int lvl15Int);

/* level values by real priority */

LOCAL UCHAR sysPicPriMask1[17] = {0xFB,0xFA,0xF8,0xF8,0xF0,0xE0,0xC0,0x80,
                                  0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0x0};
LOCAL UCHAR sysPicPriMask2[17] = {0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,
                                  0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x0};


LOCAL UCHAR pirtTable[16] = {0x00, 0x08, 0x00, 0x02,
                             0x04, 0x05, 0x07, 0x06,
                             0x00, 0x01, 0x03, 0x09,
                             0x0b, 0x00, 0x0d, 0x0f};

/* sysIbcIntHandler() already connected ? */
LOCAL BOOL ibcIntHandlerConnected = FALSE;

#if defined (IBC_INT_DEBUG)
UINT8       gIsr1,
            gIsr2,
            gImr1,
            gImr2,
            gIrr1,
            gIrr2;
void i8259RegsGet (void);
int  sysIbcIntCnt;
int  sysIbcPhantomIntCnt;
int  sysIbcAdjInt7Cnt;
int  sysIbcAdjInt15Cnt;
int  sysIbcIntCntTbl[16];
#endif /* defined (IBC_INT_DEBUG) */


void            (*_func_spuriousIntHook)(void *arg, UINT32 vecNum) = NULL;
LOCAL void *    spuriousHandlerArg;
void            (*_func_uninitIntHook)(void *arg, UINT32 vecNum) = NULL;
LOCAL void *    uninitIntHandlerArg;
#if defined (EPIC_INT_DEBUG)
unsigned long   sysEpicIntCnt = 0;
unsigned long   sysEpicUninitIntCnt = 0;
unsigned long   sysEpicSpuriousIntCnt = 0;
#endif /* defined (EPIC_INT_DEBUG) */

#if defined (EPIC_INT_DEBUG) || defined (IBC_INT_DEBUG)
unsigned long   sysIntVecCnt[INT_TBL_SIZE];
#endif /* defined (EPIC_INT_DEBUG) || defined (IBC_INT_DEBUG) */
/*------------------------------------------------------------------(globals)-*/


/*-------------------------------------------------------------+declarations+-*/
IMPORT STATUS   (*_func_intConnectRtn) (VOIDFUNCPTR *, VOIDFUNCPTR, int);
IMPORT int      (*_func_intEnableRtn) (int);
IMPORT int      (*_func_intDisableRtn) (int);
IMPORT void     sysPciRead32 (UINT32, UINT32 *);
IMPORT void     sysPciWrite32 (UINT32, UINT32);
IMPORT STATUS   excIntConnect (VOIDFUNCPTR *, VOIDFUNCPTR);
void            sysIbcIntEnable (int);
void            sysIbcIntDisable (int);
void            sysIbcIntHandler (void);
void            sysEpicIntHandler (void);
void            sysIbcIntLevelSet (int);
STATUS          sysSpuriousIntConnect (VOIDFUNCPTR routine, void *arg);

LOCAL int       getEpicVecOffset (int);
LOCAL STATUS    sysEpicIntConnect (VOIDFUNCPTR * vector, VOIDFUNCPTR routine,
                    int parameter);
LOCAL STATUS    sysEpicExtIntConnect (VOIDFUNCPTR * vector, VOIDFUNCPTR routine,
                    int parameter, int position);
LOCAL int       sysEpicIntEnable (int);
LOCAL int       sysEpicIntDisable (int);

LOCAL void      sysIbcEpicConnect (void);
LOCAL void      sysIbcEndOfInt (int);

/*-------------------------------------------------------------(declarations)-*/


void sysEpicReset (void)
{
    /* Reset the EPIC. */
    sysPciWrite32 ( EPIC_ADDR (EPIC_GLOBAL_CONFIG_REG), RESET_CNTRLR );
}

/*******************************************************************************
*
* sysEpicInit - initialize the EPIC in the MPC107
*
* This function initializes the Embedded Programmable Interrupt Controller (EPIC)
* contained in the Chaparral.
*
* It first initializes the system vector table, connects the EPIC interrupt
* handler to the PPC external interrupt and attaches the local EPIC routines
* for interrupt connecting, enabling and disabling to the corresponding system
* routine pointers.
*
* It then initializes the EPIC registers, clears any pending EPIC interrupts,
* enables interrupt handling by the EPIC and enables external ISA interrupts
* (from the M1543C).
*
* RETURNS: OK always
*/
STATUS sysEpicInit (void)
{
    int             i;
    LOCAL_INT_DATA  init;


    /* Reset the EPIC. */
    sysPciWrite32 ( EPIC_ADDR (EPIC_GLOBAL_CONFIG_REG), RESET_CNTRLR );


    /* Initialize the interrupt table */

    for (i = 0; i < 256; i++)
        sysIntTbl[i] = NULL;

#if defined (EPIC_INT_DEBUG) || defined (IBC_INT_DEBUG)
    /* Reset interrupt vector count table (for debugging only) */
    for ( i = 0; i < INT_TBL_SIZE; i++ )
    {
        sysIntVecCnt[i] = 0;
    }
#endif /* defined (EPIC_INT_DEBUG) || defined (IBC_INT_DEBUG) */

    /* Connect the interrupt demultiplexer to the PowerPC external interrupt */

    excIntConnect ((VOIDFUNCPTR *) _EXC_OFF_INTR, sysEpicIntHandler);

    /*
    *  Set up the BSP specific routines
    *  Attach the local routines to the vxWorks system calls
    */

    _func_intConnectRtn = sysEpicIntConnect;
    _func_intEnableRtn  = sysEpicIntEnable;
    _func_intDisableRtn = sysEpicIntDisable;
    

    /*
     * Initialize the EPIC
     */

    /* generate a PCI IACK to clear any pending interrupts */

    sysPciRead32 ( EPIC_ADDR (EPIC_CPU_IACK_REG), &init.temp );


    /*
     * Set the timer frequency reporting register.
     * This register is used to report the frequency of the clock
     * source for the global timers (in ticks/seconds (Hz)) which is always the
     * SDRAM_CLK signal. The timers operate at 1/8th the speed of the
     * SDRAM_CLK signal.
     */

    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER_FREQ_REG), MEMORY_BUS_SPEED);

    /* Clear the timer counter register and setup the timer vector/priority registers */

    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER0_BASE_CT_REG), TIMER_INHIBIT );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER0_VEC_PRI_REG), INIT_GLOBAL_TIMER0 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER1_BASE_CT_REG), TIMER_INHIBIT );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER1_VEC_PRI_REG), INIT_GLOBAL_TIMER1 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER2_BASE_CT_REG), TIMER_INHIBIT );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER2_VEC_PRI_REG), INIT_GLOBAL_TIMER2 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER3_BASE_CT_REG), TIMER_INHIBIT );
    sysPciWrite32 ( EPIC_ADDR (EPIC_TIMER3_VEC_PRI_REG), INIT_GLOBAL_TIMER3 );

    /* setup the external source vector/priority registers */

    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC0_VEC_PRI_REG), INIT_EXT_SRC0 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC1_VEC_PRI_REG), INIT_EXT_SRC1 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC2_VEC_PRI_REG), INIT_EXT_SRC2 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC3_VEC_PRI_REG), INIT_EXT_SRC3 );
    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC4_VEC_PRI_REG), INIT_EXT_SRC4 );

    /* setup the Ext source 0 reg (8259 input) for normal operation */

    sysPciRead32 ( EPIC_ADDR (EPIC_EXT_SRC0_VEC_PRI_REG), &init.regVal );
    init.regVal |= PIB_INT_VEC;
    init.regVal &= (~INT_MASK_BIT);
    sysPciWrite32 ( EPIC_ADDR (EPIC_EXT_SRC0_VEC_PRI_REG), init.regVal );

    /* enable interrupts for this processor */

    sysPciWrite32 ( EPIC_ADDR (EPIC_CPU_CUR_TASK_PRI_REG), 1 );

    /* setup the EPIC to process the 8259 interrupts ( mixed mode ) */

    sysPciWrite32 ( EPIC_ADDR (EPIC_GLOBAL_CONFIG_REG), SET_MIXED_MODE );

    return (OK);
}

/******************************************************************************
*
* sysEpicIntConnect - connect an interrupt handler to the system vector table
*
* This function connects an interrupt handler to the system vector table.
*
* RETURNS: OK/ERROR.
*/

LOCAL STATUS sysEpicIntConnect
    (
     VOIDFUNCPTR *      vector,         /* interrupt vector to attach */
     VOIDFUNCPTR        routine,        /* routine to be called */
     int                parameter       /* parameter to be passed to routine */
    )
{
    INT_HANDLER_DESC * newHandler;
    INT_HANDLER_DESC * currHandler;
    LOCAL_INT_DATA     connect;

    if (((int)vector < 0) || ((int)vector > 0xff))      /* Out of Range? */
        return (ERROR);

    /* connect ibc interrupt handler */
    if (!ibcIntHandlerConnected)
    {
        ibcIntHandlerConnected = TRUE;
        sysIbcEpicConnect (); /* calls this rtn, recursively */
    }

    /* create a new interrupt handler */

    if ((newHandler = (INT_HANDLER_DESC *)calloc (1, sizeof (INT_HANDLER_DESC)))
        == NULL)
        return (ERROR);

    /* initialize the new handler */

    newHandler->vec  = routine;
    newHandler->arg  = parameter;
    newHandler->next = NULL;

    /* install the handler in the system interrupt table */

    if (sysIntTbl[(int) vector] == NULL)
    {
        sysIntTbl [(int ) vector] = newHandler; /* single int. handler case */
    }
    else
    {
        currHandler = sysIntTbl[(int) vector]; /* multiple int. handler case */
        while (currHandler->next != NULL)
        {
            currHandler = currHandler->next;
        }
        currHandler->next = newHandler;
    }

    /*
    * if the connect is for an EPIC interrupt,
    * then store the vector into the appropriate EPIC vector register
    */

    connect.regAddr = getEpicVecOffset ( (int)vector );
    if ( connect.regAddr > 0 )
    {
        /* read the vector register */

        sysPciRead32 ( EPIC_ADDR (connect.regAddr), &connect.regVal );

        /* store the interrupt vector number */

        connect.regVal |= (int)vector;

        /* write the contents of the vector register back */

        sysPciWrite32 ( EPIC_ADDR (connect.regAddr), connect.regVal );
    }

    return (OK);
}


/******************************************************************************
*
* sysEpicExtIntConnect - connect an interrupt handler to the system vector table
*
* This function connects an interrupt handler with extended options to the
* system vector table. This routine allows to add an interrupt handler at the
* specified position within the interrupt handler list.
* Special values for position parameter:
*   0: add handler as first handler in the list
*  -1: add handler as last handler in the list
*
* RETURNS: OK/ERROR.
*/

LOCAL STATUS sysEpicExtIntConnect
    (
     VOIDFUNCPTR *      vector,         /* interrupt vector to attach */
     VOIDFUNCPTR        routine,        /* routine to be called */
     int                parameter,      /* parameter to be passed to routine */
     int                position        /* position within interrupt handler list */
    )
{
    INT_HANDLER_DESC *  newHandler;
    INT_HANDLER_DESC *  currHandler;
    LOCAL_INT_DATA      connect;
    int                 index;

    if (((int)vector < 0) || ((int)vector > 0xff))      /* Out of Range? */
    {
        return (ERROR);
    }

    /* connect the PIB to EPIC, before any other connections */
    if (!ibcIntHandlerConnected)
    {
        ibcIntHandlerConnected = TRUE;
        sysIbcEpicConnect (); /* calls this rtn, recursively */
    }

    /* create a new interrupt handler */

    if ((newHandler = (INT_HANDLER_DESC *)calloc (1, sizeof (INT_HANDLER_DESC)))
        == NULL)
        return (ERROR);

    /* initialize the new handler */

    newHandler->vec  = routine;
    newHandler->arg  = parameter;
    newHandler->next = NULL;

    /* install the handler in the system interrupt table */

    if (sysIntTbl[(int) vector] == NULL)
    {
        /*
         * This is the first handler for this vector.
         * Check position parameter (only 0 and -1 allowed for first handler)
         * and insert new handler.
         */
        if (0 != position && -1 != position)
        {
            free (newHandler);
            return (ERROR);
        }
        else
        {
            sysIntTbl[(int) vector] = newHandler;
        }

        sysIntTbl [(int ) vector] = newHandler; /* single int. handler case */

        /*
        * if the connect is for an EPIC interrupt,
        * then store the vector into the appropriate EPIC vector register
        */
        connect.regAddr = getEpicVecOffset ( (int)vector );
        if ( connect.regAddr > 0 )
        {
            /* read the vector register */

            sysPciRead32 ( EPIC_ADDR (connect.regAddr), &connect.regVal );

            /* store the interrupt vector number */

            connect.regVal |= (int)vector;

            /* write the contents of the vector register back */

            sysPciWrite32 ( EPIC_ADDR (connect.regAddr), connect.regVal );
        }
    }
    else
    {
        /*
         * One or more handler already in list.
         */

        currHandler = sysIntTbl[(int) vector]; /* multiple int. handler case */

        /*
         * Special case: connect new handler as first handler in list.
         */

        if (0 == position)
        {
            newHandler->next = currHandler;
            sysIntTbl[(int) vector] = newHandler;
        }

        /*
         * Special case: connect new handler as last handler in list.
         * Search end of list and add new handler.
         */

        else if (-1 == position)
        {
            while (currHandler->next != NULL)
            {
                currHandler = currHandler->next;
            }
            currHandler->next = newHandler;
        }

        /*
         * Connect new handler at specified position in list.
         */

        else
        {
            /* Search for specified position in handler list. */

            index = 0;
            while (currHandler->next != NULL)
            {
                index++;
                if (index == position)
                {
                    /* Position found. Insert new handler */
                    newHandler->next = currHandler->next;
                    currHandler->next = newHandler;
                    break;
                }
                else
                {
                    currHandler = currHandler->next;
                }
            }

            /* If position not yet found, return error */

            if (index < position)
            {
                free (newHandler);
                return (ERROR);
            }
        }
    }

    return (OK);
}


/******************************************************************************
*
* sysEpicIntConnectFirst - connect an interrupt handler as first handler in list
*
* This function connects an interrupt handler as first handler in the interrupt
* handler list.
*
* RETURNS: OK/ERROR.
*/

STATUS sysEpicIntConnectFirst
    (
     VOIDFUNCPTR *      vector,         /* interrupt vector to attach */
     VOIDFUNCPTR        routine,        /* routine to be called */
     int                parameter       /* parameter to be passed to routine */
    )
{
    return (sysEpicExtIntConnect (vector, routine, parameter, 0));
}


/******************************************************************************
*
* sysEpicIntConnectLast - connect an interrupt handler as last handler in list
*
* This function connects an interrupt handler as first handler in the interrupt
* handler list.
*
* RETURNS: OK/ERROR.
*/

STATUS sysEpicIntConnectLast
    (
     VOIDFUNCPTR *      vector,         /* interrupt vector to attach */
     VOIDFUNCPTR        routine,        /* routine to be called */
     int                parameter       /* parameter to be passed to routine */
    )
{
    return (sysEpicExtIntConnect (vector, routine, parameter, -1));
}


/*******************************************************************************
*
* sysEpicIntEnable - enable an EPIC interrupt level
*
* This routine enables a specified EPIC interrupt level.
*
* RETURNS: OK or ERROR if interrupt level not supported
*/

LOCAL int sysEpicIntEnable
    (
    int intLevel        /* interrupt level to enable */
    )
{
    LOCAL_INT_DATA  enable;

    /*
    * if the int. level is not for ISA or EPIC, then it is not supported.
    * If not supported, just return.
    */

    if ((intLevel < 0) || (intLevel > ERR_INTERRUPT_BASE))
    {
        return (ERROR);
    }

    /* If ISA interrupt level,call the IBC driver enable routine, */

    if ( intLevel < EXT_INTERRUPT_BASE )
    {
        sysIbcIntEnable ( intLevel );
    }

    enable.regAddr = getEpicVecOffset ( intLevel );

    if ( enable.regAddr > 0 )
    {
        /* read the vector register */

        sysPciRead32 ( EPIC_ADDR (enable.regAddr), &enable.regVal );

        /* enable the interrupt */

        enable.regVal &= (~INT_MASK_BIT);

        /* write the contents of the vector register back */

        sysPciWrite32 ( EPIC_ADDR (enable.regAddr), enable.regVal );
    }

    return (OK);
}


/*******************************************************************************
*
* sysEpicIntDisable - disable an EPIC interrupt level
*
* This routine disables a specified EPIC interrupt level.
*
* RETURNS: OK or ERROR if interrupt level not supported
*/

LOCAL int sysEpicIntDisable
    (
    int intLevel        /* interrupt level to disable */
    )
{
    LOCAL_INT_DATA      disable;

    /*
    * if the int. level is not for ISA or EPIC, then it is not supported.
    * If not supported, just return.
    */

    if ((intLevel < 0) || (intLevel > ERR_INTERRUPT_BASE))
    {
        return (ERROR);
    }

    /* If ISA interrupt level, call the IBC driver disable routine, */

    if ( intLevel < EXT_INTERRUPT_BASE )
    {
        sysIbcIntDisable ( intLevel );
    }

    /* else, it is an EPIC interrupt level */
    else
    {
        /* get the vector reg. offset value */
        disable.regAddr = getEpicVecOffset ( intLevel );

        if ( disable.regAddr > 0 )
        {
            /* read the vector register */

            sysPciRead32 ( EPIC_ADDR (disable.regAddr), &disable.regVal );

            /* disable the interrupt */

            disable.regVal |= INT_MASK_BIT;

            /* write the contents of the vector register back */

            sysPciWrite32 ( EPIC_ADDR (disable.regAddr), disable.regVal );
        }
    }

    return (OK);
}


/******************************************************************************
*
* sysEpicIntHandler - handle an interrupt received at the EPIC
*
* This routine will process interrupts received from PCI or ISA devices as
* these interrupts arrive via the EPIC.  This routine supports EPIC interrupt
* nesting.
*
* RETURNS: N/A
*/

void sysEpicIntHandler (void)
{
    INT_HANDLER_DESC *  currHandler;
    UINT32      vecNum;
    int         dontCare;

#ifdef EPIC_INT_DEBUG
    sysEpicIntCnt++;
#endif

    /* get the vector from the EPIC IACK reg. */

    sysPciRead32 (EPIC_ADDR (EPIC_CPU_IACK_REG), &vecNum);
    vecNum &= VECTOR_MASK;

    /* Ignore spurious interrupts */

    if (vecNum == 0xFF)
    {
        if (_func_spuriousIntHook != NULL) {
            _func_spuriousIntHook (spuriousHandlerArg, vecNum);
        }
#if defined (EPIC_INT_DEBUG)
        sysEpicSpuriousIntCnt++;
        printf ("EPIC Spurious Interrupt!\n", 0,0,0,0,0,0);
#endif /* defined (EPIC_INT_DEBUG) */
        return;
    }

    /*
    * Allow maskable interrupts to the CPU.  EPIC will hold off
    * lower and equal interrupts until EPIC_EOI is performed.
    */

    CPU_INT_UNLOCK (_PPC_MSR_EE);

    /* call the necessary interrupt handlers */

    if ((currHandler = sysIntTbl [vecNum]) == NULL)
    {
        if (_func_uninitIntHook != NULL) {
            _func_uninitIntHook (uninitIntHandlerArg, vecNum);
        }
#if defined (EPIC_INT_DEBUG)
        sysEpicUninitIntCnt++;
        printf ("uninitialized EPIC interrupt %d\r\n", vecNum, 0,0,0,0,0);
#endif /* defined (EPIC_INT_DEBUG) */
    }
    else
    {
#if defined (EPIC_INT_DEBUG)
        sysIntVecCnt[vecNum]++;
#endif /* defined (EPIC_INT_DEBUG) */


        /* Call EACH respective chained interrupt handler */

        while (currHandler != NULL)
        {
            currHandler->vec (currHandler->arg);
            currHandler = currHandler->next;
        }
    }

    CPU_INT_LOCK (&dontCare);

    /* issue an end-of-interrupt to the EPIC */

    EPIC_EOI;

    return;
}


/*******************************************************************************
*
* getEpicVecOffset - get the vector offset value of an EPIC register
*
* This routine calculates the appropriate EPIC register offset based on the
* specified EPIC interrupt level.
*
* RETURNS: EPIC register offset or zero if not a supported level.
*/

LOCAL int getEpicVecOffset
    (
    int     intLevel
    )
{
    int     offset = 0;

    /* check for external interrupt level */

    if ((intLevel >= EXT_INTERRUPT_BASE) && (intLevel < TIMER_INTERRUPT_BASE))
    {
        offset = intLevel - EXT_INTERRUPT_BASE;
        offset = EPIC_EXT_SRC0_VEC_PRI_REG + ( offset * EPIC_REG_OFFSET * 2 );
    }

    /* check for a timer interrupt level */

    if ((intLevel >= TIMER_INTERRUPT_BASE) && (intLevel < ERR_INTERRUPT_BASE))
    {
        offset = intLevel - TIMER_INTERRUPT_BASE;
        offset =  EPIC_TIMER0_VEC_PRI_REG + ( offset * EPIC_REG_OFFSET * 4 );
    }

    return (offset);
}


/*******************************************************************************
*
* sysPciExtIbcInit - initialize the extended portion of the IBC PCI header
*
* This routine initializes the extended portion of the ISA Bridge Controller
* (IBC) PCI header.
*
* RETURNS: OK or ERROR.
*
* SEE ALSO:
*/

STATUS sysPciExtIbcInit
    (
    int         pciBusNo,               /* PCI bus number */
    int         pciDevNo,               /* PCI device number */
    int         pciFuncNo               /* PCI function number */
    )
{
    /*
     * route M1543C PCI interrupt inputs to 8259 ISA IRQs
     * PCI INTA: routed to Z85230_INT_LVL
     * PCI INTB: routed to Z8536_INT_LVL
     */

    pciConfigOutByte (pciBusNo, pciDevNo, pciFuncNo, PCI_CFG_IBC_PIRT1,
            (UINT8)(pirtTable[Z8536_INT_LVL] << 4) | pirtTable[Z85230_INT_LVL]);

    /*
     * M1543C PCI interrupts to ISA IRQ edge/level control
     * INTA..INTD level triggered: 0x00
     */
    pciConfigOutByte (pciBusNo, pciDevNo, pciFuncNo, PCI_CFG_IBC_PILET, 0x00);

    /*
     * M1543C PCI interface
     * 
     * Bit 5= BusRequest only when ISA-Master asserts Command
     * Bit 4= Pass.Release ;         \
     * Bit 3= Delayed Transaction ;  / needs SMCCI (5E) bit0=1
     * Bit 2= PCI to ISA posted WR buffer
     * Bit 1= ISA Master Line Buffer
     * Bit 0= DMA Line Buffer Enable
     */
    pciConfigOutByte (pciBusNo, pciDevNo, pciFuncNo, M1543C_PIC, 0x03);

    /*
     * M1543C Suspend Mode Clock Control I
     * 
     * Bit 0= Passive Release control during delayed transaction cycle (0=disable, 1=enable).
     */
    pciConfigModifyByte (pciBusNo, pciDevNo, pciFuncNo, M1543C_SMCCI, 0x01, 0x01);

    /*
     * M1543C I/O recovery control
     * 
     * Bit 5-2= I/O Recovery Period  (0001b = 0.25us)
     * Bit   1= On-Chip I/O Recovery Feature (0=disable, 1=enable)
     * Bit   0= ISA I/O Recovery Feature (0=disable, 1=enable)
     */
    pciConfigOutByte (pciBusNo, pciDevNo, pciFuncNo, M1543C_IORC, 0x07);

    return (OK);
}


/*******************************************************************************
*
* sysIbcInit - Initialize the IBC
*
* This routine initializes the non-PCI Header configuration registers of the
* IBC within the W83C553 PIB.
*
* RETURNS: OK always
*/

STATUS sysIbcInit (void)
{
    UCHAR   intNum;
    BOOL    spurious;


    /* Initialize the Interrupt Controller #1 */

    IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR),0x11);          /* ICW1 */
    IBC_BYTE_OUT (PIC_port2 (PIC1_BASE_ADR),sysVectorIRQ0); /* ICW2 */
    IBC_BYTE_OUT (PIC_port2 (PIC1_BASE_ADR),0x04);          /* ICW3 */
    IBC_BYTE_OUT (PIC_port2 (PIC1_BASE_ADR),0x01);          /* ICW4 */

    /*
    *   Mask interrupts IRQ 0, 1, and 3-7 by writing to OCW1 register
    *   IRQ 2 is the cascade input
    */

    IBC_BYTE_OUT (PIC_IMASK (PIC1_BASE_ADR),0xfb);

    /* Insure that all controller interrupts are edge sensitive */

    IBC_BYTE_OUT (M1543C_INT1_ELC, 0x00);

    /* Initialize the Interrupt Controller #2 */

    IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR),0x11);              /* ICW1 */
    IBC_BYTE_OUT (PIC_port2 (PIC2_BASE_ADR),sysVectorIRQ0+8);   /* ICW2 */
    IBC_BYTE_OUT (PIC_port2 (PIC2_BASE_ADR),0x02);              /* ICW3 */
    IBC_BYTE_OUT (PIC_port2 (PIC2_BASE_ADR),0x01);              /* ICW4 */

    /* Mask interrupts IRQ 8-15 by writing to OCW1 register */

    IBC_BYTE_OUT (PIC_IMASK (PIC2_BASE_ADR),0xff);

    /* Make IRQ 11, and 10 level sensitive */

    IBC_BYTE_OUT (M1543C_INT2_ELC, 0x08 | 0x04);


    /*  Perform the PCI Interrupt Ack cycle */


    do
    {
        intNum = (*(char *)ISA_INTR_ACK_REG);
        spurious = sysIbcPhantomInt (&intNum, 0, 0);

        if (!spurious)
        {
            /* Perform the end of interrupt procedure */
            sysIbcEndOfInt (15);
        }
    }
    while(!spurious);


    sysIbcIntLevelSet (16);

#if defined (IBC_INT_DEBUG)
    {
    int i;

    sysIbcPhantomIntCnt = 0;
    sysIbcIntCnt = 0;

    for (i = 0; i < 16; i++)
        {
        sysIbcIntCntTbl[i] = 0;
        }
    }
#endif /* defined (IBC_INT_DEBUG) */

    return (OK);
}


/*******************************************************************************
*
* sysIbcIntEnable - enable a IBC interrupt level
*
* This routine enables a specified IBC interrupt level.
*
* RETURNS: N/A
*/

void sysIbcIntEnable
    (
    int intNum        /* interrupt level to enable */
    )
{
    if (intNum < 8)
    {
        sysPicMask1 &= ~(1 << intNum);
        IBC_BYTE_OUT (PIC_IMASK (PIC1_BASE_ADR), sysPicMask1 | sysPicLevel1);
    }
    else
    {
        sysPicMask2 &= ~(1 << (intNum - 8));
        IBC_BYTE_OUT (PIC_IMASK (PIC2_BASE_ADR), sysPicMask2 | sysPicLevel2);
    }
}


/*******************************************************************************
*
* sysIbcIntDisable - disable a IBC interrupt level
*
* This routine disables a specified IBC interrupt level.
*
* RETURNS: N/A
*/

void sysIbcIntDisable
    (
    int intNum        /* interrupt level to disable */
    )
{
    if (intNum < 8)
    {
        sysPicMask1 |= (1 << intNum);
        IBC_BYTE_OUT (PIC_IMASK (PIC1_BASE_ADR), sysPicMask1 | sysPicLevel1 );
    }
    else
    {
        sysPicMask2 |= (1 << (intNum - 8));
        IBC_BYTE_OUT (PIC_IMASK (PIC2_BASE_ADR), sysPicMask2 | sysPicLevel2);
    }
}

/******************************************************************************
*
* sysIbcIntHandler - handler of the sl82565 IBC interrupt.
*
* This routine handles interrupts originating from the M1543C PIB ISA Bus
* Controller (IBC).  This device implements the functional equivalent of two
* cascaded 8259 PICs.
*
* This routine is entered with CPU external interrupts enabled.
*
* Because the ISA bus is only accessible via the PCI bus, this driver first
* initiates a PCI interrupt acknowledge cycle to read the interrupt number
* (vector) coming from the IBC.
*
* This routine then processes the interrupt by calling all interrupt service
* routines chained to the vector.
*
* Finally, this routine re-arms the interrupt at the IBC by performing a
* IBC EOI.
*
* RETURNS: N/A
*/

void sysIbcIntHandler (void)
{
    UCHAR       intNum;
    INT_HANDLER_DESC *  currHandler;
    static int  lvl7Int = 0,
                lvl15Int = 0;

#if defined (IBC_INT_DEBUG)
    sysIbcIntCnt++;
#endif /* defined (IBC_INT_DEBUG) */

    /*
     * Reading ISA_INTR_ACK_REG would generate PCI IACK cycles on the
     * PCI bus which would cause the IBC to put out vector on the PCI bus.
     */
    intNum = (*(char *)ISA_INTR_ACK_REG);

#if defined (IBC_INT_DEBUG)
    sysIbcIntCntTbl[intNum]++;
#endif /* defined (IBC_INT_DEBUG) */

    /* Special check for phantom interrupts */
    if ((intNum & IBC_INT_LVL7) == IBC_INT_LVL7)
    {
        if (sysIbcPhantomInt (&intNum, lvl7Int, lvl15Int) == TRUE)
        {
#if defined (IBC_INT_DEBUG)
            sysIbcPhantomIntCnt++;
#endif /* defined (IBC_INT_DEBUG) */

            return;     /* It's phantom so just return. */
        }
    }

    /*
     * If cascade from IBC2 just EIO and return. It has been seen on
     * certain PCI ISA bridge devices, that it is possible to see the
     * cascade IRQ2.  In particular, this has been observed on the
     * Winbond W83C553F on certain revs of the chip prior to Rev G.
     */

    if (intNum == IBC_INT_LVL2)
    {
        sysIbcEndOfInt (intNum);
        return;
    }

    /* Keep track of level 7 and 15 nesting for phantom interrupt handling */

    if (intNum == IBC_INT_LVL7)
    {
        lvl7Int++;
    }
    else if (intNum == IBC_INT_LVL15)
    {
        lvl15Int++;
    }

    if ((currHandler = sysIntTbl [intNum]) == NULL)
    {
        printf ("uninitialized IBC interrupt level %x\r\n", intNum, 0,0,0,0,0);
    }
    else
    {
#if defined (IBC_INT_DEBUG)
        sysIntVecCnt[intNum]++;
#endif /* defined (IBC_INT_DEBUG) */

        /* Call EACH respective chained interrupt handler */

        while (currHandler != NULL)
        {
            currHandler->vec (currHandler->arg);
            currHandler = currHandler->next;
        }
    }

    /* Keep track of level 7 and 15 nesting for phantom interrupt handling */

    if (intNum == IBC_INT_LVL7)
    {
        lvl7Int--;
    }
    else if (intNum == IBC_INT_LVL15)
    {
        lvl15Int--;
    }


    /* Re-arm (enable) the interrupt on the IBC */

    sysIbcEndOfInt (intNum);
}

/*******************************************************************************
*
* sysIbcPhantomInt - Determine if IRQ interrupt number 7 or 15 is "phantom".
*
* This routine determines if an IRQ number of 7 or 15 is a phantom
* interrupt.  According to Intel 82C59A-2 documentation, the IR (interrupt
* request) inputs must remain high until after the falling edge of the first
* INTA (interrupt acknowledge).  If the IR goes low before this, a DEFAULT
* (phantom) IRQ7 will occur when the CPU acknowledges the interrupt.  Note
* that if an IRQ7 is generated it may really be another interrupt, IRQ4 for
* example.  IRQ 7 is associated  with the master 8259, IRQ 15 is associated
* with the slave 8259.  This function should only be called if the
* acknowledged IRQ number is 7 or 15 but does behave sanely if called with
* other IRQ numbers.
*
* As mentioned above, IRQ 7 is supposed to be associated with the master
* 8259. However, it has been observed that a phantom IRQ 7 was caused by
* an interrupt on the slave 8259. Thus, the algorithm now implemented is
* to scan both the master and the slave 8259 in the correct priority order
* (from highest to lowest: 0 1 8 9 10 11 12 13 14 15 3 4 5 6 7).
*
* RETURNS: TRUE if phantom IRQ, *intNum unaltered.
* FALSE if no phantom interrupt, *intNum is "real" IRQ number.
*/

LOCAL UCHAR sysIbcPhantomInt
    (
    UCHAR *intNum,      /* interrupt number received on acknowledge */
    int   lvl7Int,  /* interrupt 7 nesting level */
    int   lvl15Int  /* interrupt 15 nesting level */
    )
    {
    UCHAR irqBit;
    UINT  irqNum;
#if defined (IBC_INT_DEBUG)
    UCHAR isr;
#endif /* defined (IBC_INT_DEBUG) */

    /* Read the master in-service register (ISR) */

    IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);

    IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &irqBit);

#if defined (IBC_INT_DEBUG)
    isr = irqBit;
#endif /* defined (IBC_INT_DEBUG) */

    if (irqBit == 0)
        {
        return (TRUE);  /* No in-service int so it MUST be phantom */
        }

    for (irqNum = 0; ((irqBit & 1) == 0) ; irqNum++, irqBit >>= 1)
        ;

    if (irqNum == IBC_INT_LVL7)
        if (lvl7Int > 1)
            {
            return (TRUE);  /* We're nested so it MUST be phantom */
            }

    /* if irqNum isn't 2 we have an interrupt on the master */

    if (irqNum != IBC_INT_LVL2)
        {
        *intNum = irqNum;
#if defined (IBC_INT_DEBUG)
        sysIbcAdjInt7Cnt++;
        printf ("sysIbcPhantomInt: IRQ adjusted to %d (master ISR: %02x)\n", *intNum, isr,0,0,0,0);
#endif /* defined (IBC_INT_DEBUG) */

        return (FALSE);
        }

    /* Read the slave in-service register (ISR) */

    IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);

    IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &irqBit);

#if defined (IBC_INT_DEBUG)
    isr = irqBit;
#endif /* defined (IBC_INT_DEBUG) */

    if (irqBit == 0)    /* should never happen, really */
        {
#if defined (IBC_INT_DEBUG)
        printf ("sysIbcPhantomInt: no interrupt in slave\n", 0,0,0,0,0,0);
#endif /* defined (IBC_INT_DEBUG) */

        return (TRUE);  /* No in-service int so it MUST be phantom */
        }

    for (irqNum = 8; ((irqBit & 1) == 0) ; irqNum++, irqBit >>= 1)
        ;

    if (irqNum == 15)
        if (lvl15Int > 1)
            {
#if defined (IBC_INT_DEBUG)
            printf ("sysIbcPhantomInt: nested interrupt in slave\n",
                    0,0,0,0,0,0);
#endif /* defined (IBC_INT_DEBUG) */

            return (TRUE);  /* We're nested so it MUST be phantom */
            }

    *intNum = irqNum;
#if defined (IBC_INT_DEBUG)
    sysIbcAdjInt15Cnt++;
    printf ("sysIbcPhantomInt: IRQ adjusted to %d (slave ISR: %02x)\n", *intNum, isr,0,0,0,0);
#endif /* defined (IBC_INT_DEBUG) */

    return (FALSE);
    }



/*******************************************************************************
*
* sysIbcEndOfInt - send EOI (end of interrupt) signal.
*
* This routine is called at the end of the interrupt handler to
* send a non-specific end of interrupt (EOI) signal.
*
* The second PIC is acked only if the interrupt came from that PIC.
* The first PIC is always acked.
*/

LOCAL void sysIbcEndOfInt
    (
    int intNum
    )
{
    if (intNum > IBC_INT_LVL7)
        {
        IBC_BYTE_OUT (PIC_IACK (PIC2_BASE_ADR), 0x20);
        }
    IBC_BYTE_OUT (PIC_IACK (PIC1_BASE_ADR), 0x20);

#if defined (IBC_INT_DEBUG)
    {
    UINT8   isr1,
            isr2;
    int     irqBit;


    if (intNum > IBC_INT_LVL7)
        {
        IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &isr2);

        irqBit = 1 << (intNum & 0x7);

        if (isr2 & irqBit)
            {
            printf ("sysIbcEndOfInt: In-service bit for IRQ %d not reset (ISR2: %02x)\n", intNum,isr2,0,0,0,0);
            }
        }
    else if (intNum != IBC_INT_LVL2)
        {
        IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &isr1);

        irqBit = 1 << intNum;

        if (isr1 & irqBit)
            {
            printf ("sysIbcEndOfInt: In-service bit for IRQ %d not reset (ISR1: %02x)\n", intNum,isr1,0,0,0,0);
            }
        }
    }
#endif /* defined (IBC_INT_DEBUG) */
}

/*******************************************************************************
*
* sysIbcIntLevelSet - set the interrupt priority level
*
* This routine masks interrupts with real priority equal to or lower than
* .  The special
* value 16 indicates all interrupts are enabled. Individual interrupt
* numbers have to be specifically enabled by sysIbcIntEnable() before they
* are ever enabled by setting the interrupt level value.
*
* Note because of the IBM cascade scheme, the actual priority order for
* interrupt numbers is (high to low) 0, 1, 8, 9, 10, 11, 12, 13, 14, 15,
* 3, 4, 5, 6, 7, 16 (all enabled)
*
* INTERNAL: It is possible that we need to determine if we are raising
* or lowering our priority level.  It may be that the order of loading the
* two mask registers is dependent upon raising or lowering the priority.
*
* RETURNS: N/A
*/

void sysIbcIntLevelSet
    (
    int intNum  /* interrupt level to implement */
    )
{
    if (intNum > 16)
        intNum = 16;

    sysPicLevelCur = intNum;

    if (sysPicLevel2 != sysPicPriMask2[intNum])
    {
        sysPicLevel2 = sysPicPriMask2[intNum];
        IBC_BYTE_OUT (PIC_IMASK (PIC2_BASE_ADR), sysPicMask2 | sysPicLevel2);
    }

    if (sysPicLevel1 != sysPicPriMask1[intNum])
    {
        sysPicLevel1 = sysPicPriMask1[intNum];
        IBC_BYTE_OUT (PIC_IMASK (PIC1_BASE_ADR), sysPicMask1 | sysPicLevel1);
    }
}

/*******************************************************************************
*
* sysIbcEpicConnect - routine to connect IBC interrupts to EPIC
*
* This function is called from sysHwInit2 and sets the IBC interrupt
* handler into the EPIC interrupt vector table.
*
* RETURNS: N/A
*/

LOCAL void sysIbcEpicConnect (void)
{
    intConnect (INUM_TO_IVEC (PIB_INT_VEC), sysIbcIntHandler, 0);
    intEnable (PIB_INT_LVL);
}



/******************************************************************************
*
* intTableShow - display the interrupt routine for a vector.
*
* This function displays the interrupt routines for the associated vector.
*
* RETURNS: OK always.
*/

STATUS sysIntTableShow
    (
     int vector     /* display interrupt vector to attach */
    )
{
    INT_HANDLER_DESC * currHandler;

    currHandler = sysIntTbl[(int) vector];
    if ( sysIntTbl[(int) vector] == NULL )
    {
        printf ("empty list \n");
        return (OK);
    }
    else
    {
        do
        {
            printf ("\nRoutine address : 0x%x \n", (unsigned int) currHandler->vec);
            printf ("Routine parameter : 0x%x \n",  currHandler->arg);
            currHandler = currHandler->next;
        }
        while ( currHandler != NULL );
    }
    return (OK);
}

/******************************************************************************
*
* intTableShowAll - display the interrupt routines for a all vectors.
*
* This function displays the interrupt routines for the all vectors in the
* system interrupt table.
*
* RETURNS: OK always.
*/

STATUS sysIntTableShowAll
    (
     void
    )
{
    INT_HANDLER_DESC    *currHandler;
    int                 vector,
                        first,
                        last,
                        i;

    printf ("system interrupt table:\n\n");
    printf ("vector         routine address   parameter(s) \n");
    printf ("--------------------------------------------- \n");


    for (vector = 0, first = last = -1; vector < INT_TBL_SIZE; vector++)
    {
        currHandler = sysIntTbl[vector];
        if ( sysIntTbl[vector] == NULL )
        {
            if (-1 == first)
            {
                first = vector;
            }
            else
            {
                last = vector;
            }
        }
        else
        {
            /* Print empty vector(s) */
            if (-1 != first)
            {
                if (-1 != last)
                {
                    printf (" 0x%02x - 0x%02x   ", first, last);
                }
                else
                {
                    printf (" 0x%02x          ", first);
                }
                printf ("       ---             ---    \n");
                first = last = -1;
            }

            /* Print linked list of interrupt handlers for this vector */
            printf (" 0x%02x          ", vector);
            i = 0;
            do
            {
                if (i > 0)
                {
                    printf ("   **          ");
                }
                printf ("   0x%08x    ", (unsigned int) currHandler->vec);
                printf ("  0x%08x  \n",  currHandler->arg);
                currHandler = currHandler->next;
                i++;
            }
            while ( currHandler != NULL );
        }
    }
    return (OK);
}

/******************************************************************************
*
* sysEpicIntDisconnect - disconnect an interrupt handler fromthe system vector table
*
* This function disconnects an interrupt handler from the system vector table.
* If a multiple calls were previously made to intConnect() with the same
* ,  combination then only the first matched * found would be 
* removed.
* Note that at the moment only the  parameter and not the  
* must match the arguments used at a previous sysEpicConnect call.
*
* RETURNS: OK/ERROR if either routine or parameter doesn't match.
*/

STATUS sysEpicIntDisconnect
    (
     VOIDFUNCPTR *      vector,         /* disconnect interrupt vector to attach */
     VOIDFUNCPTR        routine,        /* routine to be called */
     int                parameter       /* parameter to be passed to routine (currently not used) */
    )
{
    UINT lockKey;
    INT_HANDLER_DESC * currHandler;
    INT_HANDLER_DESC * tempHandler=0;

    if ( ((int)vector < 0) || ((int)vector > 0xff) )      /* Out of Range? */
    {
        return (ERROR);
    }

    if ( sysIntTbl[(int) vector] == NULL )
    {
        return (OK);
    }
    else
    {
        tempHandler = NULL;
        currHandler = sysIntTbl[(int) vector];

        do
        {
            if ( ((UINT) routine == (UINT) currHandler->vec) 
#if 0
    /* The check of the parameter has been commented out, since the 
       SYS_INT_DISCONNECT macro in target/h/drv/end/fei82557End.h calls the 
       disconnect routine without the third argument (=parameter).*/
                &&  (parameter == currHandler->arg) 
#endif
                )
            {
                lockKey = intLock ();
                if (NULL != tempHandler)
                {
                    tempHandler->next = currHandler->next;
                }
                else
                {
                    sysIntTbl[(int) vector] = currHandler->next;
                }

                /* Disable interupt if all handlers removed */
                if (NULL == sysIntTbl[(int) vector])
                {
                    (void)sysEpicIntDisable ((int) vector);
                }

                intUnlock (lockKey);
                free (currHandler);
                return (OK);
            }
            tempHandler = currHandler;
            currHandler = currHandler->next;
        }
        while ( currHandler != NULL );
    }
    return (ERROR);
}



/******************************************************************************
*
* sysEpicExtSourceShow - display all external interrupt sources adjustments.
*
* This function displays vector, mask, activity, polarity, sense and
* priority adjustments of the EPIC external interrupt sources.
*
* RETURNS: OK always.
*/

void
sysEpicExtSourceShow(void)
{
    int     ix;
    UINT32  regVal,
            epicReg;


    printf("\nEPIC External Direct/Serial Interrupt Sources:\n");
    printf(" #  address  value    vector  mask  act.  pol.  sense  priority\n");

    epicReg = EPIC_ADDR (EPIC_EXT_SRC0_VEC_PRI_REG);
    for (ix = 0; ix < EPIC_EXT_SRC_CNT; ix++)
    {
        sysPciRead32 (epicReg, ®Val);

        printf("%2d  %8x %08x  %3d  %4d  %4d  %4d  %5d  %8d\n",
                            ix,
                            epicReg,
                            regVal,
                            regVal & 0x000000FF,
                            regVal >> 31,
                            (regVal & 40000000) >> 30,
                            (regVal & 0x00800000) >> 23,
                            (regVal & 0x00400000) >> 22,
                            (regVal & 0x000F0000) >> 16
                            );

        epicReg += EPIC_REG_OFFSET * 2;
    }
}


#if defined (IBC_INT_DEBUG)
void sysIbcIntShow (void)

    {
    UINT8   isr1,
            isr2,
            imr1,
            imr2,
            irr1,
            irr2;
    int     oldLevel;
    int     retry,
            i;

    oldLevel = intLock ();

    for (retry=0; retry < 1; retry ++)
    {
        IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_IRR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &irr1);

        IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_IRR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &irr2);

        IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &isr1);

        IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &isr2);

        IBC_BYTE_IN (PIC_port2 (PIC1_BASE_ADR), &imr1);
        IBC_BYTE_IN (PIC_port2 (PIC2_BASE_ADR), &imr2);
    }

    intUnlock (oldLevel);

    printf ("\nCascaded i8259 Registers:\n\n");
    printf ("       PIC1   PIC2\n");
    printf ("------------------\n");
    printf ("IMR     %02x     %02x\n", imr1, imr2);
    printf ("IRR     %02x     %02x\n", irr1, irr2);
    printf ("ISR     %02x     %02x\n", isr1, isr2);
    printf ("\ni8259 interrupt count:\n");
    printf ("# of i8259 interrupts: %d\n", sysIbcIntCnt);
    printf ("# of i8259 spurious interrupts: %d\n", sysIbcPhantomIntCnt);
    printf ("# of adjusted IRQ7  interrupts: %d\n", sysIbcAdjInt7Cnt);
    printf ("# of adjusted IRQ15 interrupts: %d\n", sysIbcAdjInt15Cnt);


    for (i = 0; i < 16; i++)
        {
        printf ("IRQ%02d: %d\n", i, sysIbcIntCntTbl[i]);
        }
    }


/******************************************************************************
*
* sysIbcIntShow - display interrupt debug information for IBC (i8259)
*
* This function displays the number of interrupts handled by the IBC interrupt
* handler. The macro IBC_INT_DEBUG must be defined to include this routine.
*
* RETURNS: none
*/

void i8259RegsGet (void)

    {
    int     oldLevel;
    int     retry;

    oldLevel = intLock ();

    for (retry=0; retry < 1; retry ++)
    {
        IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_IRR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &gIrr1);

        IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_IRR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &gIrr2);

        IBC_BYTE_OUT (PIC_port1 (PIC1_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC1_BASE_ADR), &gIsr1);

        IBC_BYTE_OUT (PIC_port1 (PIC2_BASE_ADR), PIC_OCW3_SEL + PIC_ISR_READ);
        IBC_BYTE_IN (PIC_port1 (PIC2_BASE_ADR), &gIsr2);

        IBC_BYTE_IN (PIC_port2 (PIC1_BASE_ADR), &gImr1);
        IBC_BYTE_IN (PIC_port2 (PIC2_BASE_ADR), &gImr2);
    }

    intUnlock (oldLevel);
    }



void i8259RegsShow (void)

    {
    printf ("\nCascaded i8259 Registers:\n\n");
    printf ("       PIC1   PIC2\n");
    printf ("------------------\n");
    printf ("IMR     %02x     %02x\n", gImr1, gImr2);
    printf ("IRR     %02x     %02x\n", gIrr1, gIrr2);
    printf ("ISR     %02x     %02x\n", gIsr1, gIsr2);
    printf ("\n\n");
    }
#endif /* defined (IBC_INT_DEBUG) */



#if defined (EPIC_INT_DEBUG)
/******************************************************************************
*
* sysEpicIntShow - display interrupt debug information for EPIC
*
* This function displays the number of interrupts handled by the EPIC interrupt
* handler. The macro EPIC_INT_DEBUG must be defined to include this routine.
*
* RETURNS: none
*/

void
sysEpicIntShow (void)
{
    int     lockKey,
            i;

    unsigned long   localEpicIntCnt = 0;
    unsigned long   localEpicSpuriousIntCnt = 0;
    unsigned long   localEpicUninitIntCnt = 0;

    unsigned long   localIntVecCnt[INT_TBL_SIZE];

    lockKey = intLock ();

    localEpicIntCnt = sysEpicIntCnt;
    localEpicSpuriousIntCnt = sysEpicSpuriousIntCnt;
    localEpicUninitIntCnt = sysEpicUninitIntCnt;

    memcpy (&localIntVecCnt, &sysIntVecCnt, sizeof(localIntVecCnt));

    intUnlock (lockKey);

    printf ("\nEPIC Interrupt Count Summary:\n");
    printf (" # of total EPIC interrupts:            %8ld\n", localEpicIntCnt);
    printf (" # of spurious EPIC interrupts:         %8ld\n", localEpicSpuriousIntCnt);
    printf (" # of uninitialized EPIC interrupts:    %8ld\n", localEpicUninitIntCnt);

    /* Print interrupt vector count table */
    printf ("\nInterrupt Vector Count Summary:\n");
    for ( i = 0; i < INT_TBL_SIZE; i++ )
    {
        if (localIntVecCnt[i] != 0)
        {
            printf (" # of interrupts for vector 0x%02x:       %8ld\n", i, localIntVecCnt[i]);
        }
    }
}
#endif /* defined (EPIC_INT_DEBUG) */

/******************************************************************************
*
* sysSpuriousIntConnect - connect handler for spurious interrupts
*
* This function connects an interrupt handler for spurious 
* interrupts. It is done by setting the _func_spuriousIntHook.
* Upon a spurious interrupt the handler will be called with 2 parameter:
* the supplied  and the interrupt vector number.
*
* RETURNS: OK, or ERROR if the cannot be connected.
*/

STATUS sysSpuriousIntConnect
    (
    VOIDFUNCPTR routine,    /* routine called upon spurious or un-initialised */
    void *arg               /* argument with which to call routine */
    )
{
    _func_spuriousIntHook = routine;
    spuriousHandlerArg = arg;

    return OK;
}

/******************************************************************************
*
* sysUninitIntConnect - connect handler for uninitialised interrupts
*
* This function connects an interrupt handler for un-initialised
* interrupts. It is done by setting the _func_uninitIntHook.
* Upon an un-intialised interrupt the handler will be called with 2 parameter:
* the supplied  and the interrupt vector number.
*
* RETURNS: OK, or ERROR if the cannot be connected.
*/

STATUS sysUninitIntConnect
    (
    VOIDFUNCPTR routine,    /* routine called upon un-initialised IRQs */
    void *arg               /* argument with which to call routine */
    )
{
    _func_uninitIntHook = routine;
    uninitIntHandlerArg = arg;

    return OK;
}