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


/* universe.c - Tundra Universe chip VME-to-PCI bridge interface library */

/* Copyright 1984-1998 Wind River Systems, Inc. */
/* Copyright 1996,1997,1998 Motorola, Inc. All Rights Reserved */

/*
modification history
--------------------
02b,27may02,mbrandt added hooks for spurious and uninitialised VMEbus IRQs
02a,11apr02,mbrandt implemented sysVmeMailbox routines with mailboxes (not LM)
01y,20mar02,mbrandt added Location Monitor (sysMailboxXXX)
01x,18oct01,bm   adapted for pci auto-configuration support
01w,14mar00,bm   adapted to VG4 (SBS-or Industrial Computers)
                 from mv2700/universe.c ver 01v
*/

/*
DESCRIPTION

The routines addressed here include:

Initialization of Universe chip

Bus interrupt functions:
.IP "-"
enable/disable VMEbus interrupt levels
.IP "-"
enable/disable additional VME interrupts
.IP "-"
install handlers for the additional VME interrupts
.IP "-"
generate bus interrupts

Mailbox/locations monitor functions:
.IP "-"
enable mailbox/location monitor interrupts

All byte I/O is done via the macros UNIV_IN_BYTE and UNIV_OUT_BYTE which may be
redefined by the BSP.  By default, sysInByte() and sysOutByte() are used.  All
other I/O (32-bit) is handled by the macros UNIV_IN_LONG and UNIV_OUT_LONG
which may be redefined by the BSP.  By default, sysPciRead32() and
sysPciWrite32() are used.

CONTENTS:
	sysUniverseReset - reset the Universe VME chip
	sysUniverseInit - initialize registers of the Universe chip
	sysUniverseInit2 - enable local memory accesses from the VMEbus
	sysIntDisable - disable a bus interrupt level
	sysIntEnable - enable a bus interrupt level
	sysBusIntAck - acknowledge a bus interrupt
	sysBusIntGen - generate a bus interrupt
	sysUnivIntEnable - enable Universe supported interrupt(s)
	sysUnivIntDisable - disable Universe supported interrupt(s)
	sysUnivIntLevelSet - set a Universe supported interrupt level
	sysUnivIntConnect - connect an interrupt handler for an interrupt type
	sysUnivIntDisconnect - disconnect an interrupt handler for an interrupt type
	sysVmeMailboxAlloc - allocate a mailbox
	sysVmeMailboxFree - free a mailbox allocated with sysVmeMailboxAlloc()
	sysVmeMailboxEnable - enable mailbox interrupt
	sysVmeMailboxDisable - disable mailbox interrupt
	sysVmeMailboxConnect -  connect a routine to the mailbox interrupt
	sysVmeMailboxAddrGet - get VMEbus address of a mailbox
	sysVmeMailboxLocalAddrGet - get local address of a mailbox
	sysVmeMailboxDataGet - get mailbox data written to a mailbox
	sysMailboxConnect - connect a routine to the mailbox interrupt
	sysMailboxEnable - enable the mailbox interrupt
	sysMailboxDisable - disable the mailbox interrupt
	sysUnivVERRClr - Universe VMEbus Error Clear routine
	sysUnivLevelDecode - decode highest pending priority Universe interrupt
	sysVmeIntConnect - connect an interrupt handler for a VME interrupt vector
	sysVmeIntDisconnect - disconnect an interrupt handler for a VME interrupt vector
	sysVmeIntDisconnectAll - clear the entire linked-list of ISRs for a VME interrupt vector
	sysUnivVmeIntr - VMEbus interrupt handler
	sysBusRmwEnable - enable Read-Modify-Write (RMW) cycle on VMEbus
	sysBusRmwDisable - Disable Read-Modify-Write (RMW) cycle on the VMEbus.
	sysVmeDmaInit - Inititalize the Universe's DMA engine
	sysVmeDmaCnfgGet - Get DMA transfer configuration parameters
	sysVmeDmaCnfgSet -  Set DMA transfer configuration parameters
	sysVmeDmaStatusGet - Get DMA transfer Status
	sysVmeDmaL2VCopy - Copy data from local memory to VMEbus memory
	sysVmeDmaV2LCopy - Copy data from VMEbus memory to local memory
	sysVmeMasterMap - Display the VME Master mappings
	sysVmeSlaveMap - Display the VME Slave mappings to CPU
	sysVmeGetBusTrapFlag - report a VMEbus bus error (and clear the flag)
	sysVmeLock - locks the VMEbus
	sysVmeUnlock - unlocks the VMEbus
    sysSpuriousVmeIntConnect - sets handler for spurious VMEbus IRQs
    sysUninitVmeIntConnect - sets handler for uninitialised VMEbus IRQs

STATIC FUNCTIONS:
	sysMailboxInt - mailbox interrupt handler
	sysVmeDmaCopy - Copy data between local and VMEbus memory
*/

/* includes */

#include "vxWorks.h"
#include "type.h"
#include "vg4.h"
#include "config.h"
#include "vg4Intr.h"
#include "universe.h"
#include "ivppc.h"

/* defines */

#undef VME_DMA_INTERRUPT /* use interrupt for DMA transfer */

#ifndef UNIV_IN_BYTE
# define UNIV_IN_BYTE(adr,pVal) \
  *(volatile UCHAR *)(pVal)=sysInByte((volatile ULONG)(adr))
#endif

#ifndef UNIV_OUT_BYTE
# define UNIV_OUT_BYTE(adr,val) \
  sysOutByte((volatile ULONG)(adr),(volatile UCHAR)(val))
#endif

#ifndef UNIV_IN_LONG
# define UNIV_IN_LONG(adr,pVal) \
  sysPciRead32((UINT32)(adr),(UINT32 *)(pVal));
#endif

#ifndef UNIV_OUT_LONG
# define UNIV_OUT_LONG(adr,val) \
  sysPciWrite32((UINT32)(adr),(UINT32)(val));
#endif

# ifndef CPU_INT_LOCK
#   define CPU_INT_LOCK(pData) (*pData = intLock ())
# endif

# ifndef CPU_INT_UNLOCK
#   define CPU_INT_UNLOCK(data) (intUnlock (data))
# endif

/* forward declarations */
#ifdef INCLUDE_VME_MBOX
# ifndef DEFAULT_VRAI_BS
#if 0
#  define DEFAULT_VRAI_BS (VME_A32_SLV_BUS+(VME_A32_SLV_SIZE-0x1000))
#else
#  define DEFAULT_VRAI_BS (VME_A32_REG_BASE)
#endif
# endif
 LOCAL void sysMailboxInt(int mailboxId);
#endif

#ifdef  INCLUDE_VME_DMA

LOCAL STATUS sysVmeDmaCopy(UCHAR *, UCHAR *, UINT32, UINT32);

#ifdef  VME_DMA_DEBUG
unsigned long  univDmaIntCount = 0;
#endif /* VME_DMA_DEBUG */

#endif /* INCLUDE_VME_DMA */

STATUS sysVmeIntConnect
    (
     VOIDFUNCPTR * 	vector,		/* interrupt vector to attach */
     VOIDFUNCPTR   	routine,	/* routine to be called */
     int	        parameter	/* parameter to be passed to routine */
    );

STATUS sysVmeIntDisconnect
    (
     VOIDFUNCPTR * 	vector,		/* interrupt vector to attach */
     VOIDFUNCPTR   	routine,	/* routine to be called */
     int	        parameter	/* parameter to be passed to routine */
    );

/* extern declarations */

IMPORT int    intEnable (int);
IMPORT int    intDisable (int);
IMPORT void   sysOutByte (ULONG, UCHAR);
IMPORT UCHAR  sysInByte (ULONG);
IMPORT void   sysPciRead32  (UINT32, UINT32 *);
IMPORT void   sysPciWrite32 (UINT32, UINT32);
IMPORT void   sysUsDelay (UINT32);

IMPORT INT_HANDLER_DESC * sysIntTbl [256];

/* globals */

int    sysUnivIntsEnabled = 0;	/* currently enabled Universe interrupts */
int    sysUnivIntLevel    = 0;	/* current level at which ints are disabled */
UINT32 sysUnivVERRCnt     = 0;	/* #VME errors since power on */
UINT32 univBaseAdrs;
void            (*_func_spuriousVmeIntHook)(void *arg, UINT32 vecNum) = NULL;
LOCAL void *    spuriousVmeIntHandlerArg;
void            (*_func_uninitVmeIntHook)(void *arg, UINT32 vecNum) = NULL;
LOCAL void *    uninitVmeIntHandlerArg;

/*
 * Universe interrupt priority mapping table
 *
 * Interrupt priority level is equal to the index into the following array
 * where 0 is the lowest priority.  The prioritization scheme used here
 * is arbitrary.  If the scheme is changed, the interrupt masks (last column)
 * must be redefined accordingly.  See universe.h and the Universe Manual for
 * bit assignments and further information.
 */

static INT_LEVEL_MAP univIntTable[UNIV_NUM_INT + 1] =
  {
  /* Int Bit Mask	       Int Vector		 Int Level Mask */
  /* ------------------------  ------------------------  -------------- */
    {0,                        0,                         0x00FFF7FF},
    {UNIVERSE_VOWN_INT,        UNIV_VOWN_INT_VEC,         0x00FFF7FE},
    {UNIVERSE_LM1_INT,	       UNIV_LM1_INT_VEC,	  0x00DFF7FE},
    {UNIVERSE_LM2_INT,	       UNIV_LM2_INT_VEC,          0x009FF7FE},
    {UNIVERSE_LM3_INT,         UNIV_LM3_INT_VEC,          0x001FF7FE},
    {UNIVERSE_MBOX0_INT,       UNIV_MBOX0_INT_VEC,	  0x001EF7FE},
    {UNIVERSE_MBOX1_INT,       UNIV_MBOX1_INT_VEC,        0x001CF7FE},
    {UNIVERSE_MBOX2_INT,       UNIV_MBOX2_INT_VEC,        0x0018F7FE},
    {UNIVERSE_MBOX3_INT,       UNIV_MBOX3_INT_VEC,        0x0010F7FE},
    {VMEBUS_LVL1,              -1,                        0x0010F7FC},
    {VMEBUS_LVL2,              -1,                        0x0010F7F8},
    {VMEBUS_LVL3,              -1,                        0x0010F7F0},
    {VMEBUS_LVL4,              -1,                        0x0010F7E0},
    {VMEBUS_LVL5,              -1,                        0x0010F7C0},
    {VMEBUS_LVL6,              -1,                        0x0010F780},
    {VMEBUS_LVL7,              -1,                        0x0010F700},
    {UNIVERSE_DMA_INT,         UNIV_DMA_INT_VEC,          0x0010F600},
    {UNIVERSE_LM0_INT,	       UNIV_LM0_INT_VEC,	  0x0000F600},
    {UNIVERSE_VME_SW_IACK_INT, UNIV_VME_SW_IACK_INT_VEC,  0x0000E600},
    {UNIVERSE_PCI_SW_INT,      UNIV_PCI_SW_INT_VEC,       0x0000C600},
    {UNIVERSE_LERR_INT,        UNIV_LERR_INT_VEC,         0x0000C400},
    {UNIVERSE_VERR_INT,        UNIV_VERR_INT_VEC,         0x0000C000},
    {UNIVERSE_SYSFAIL_INT,     UNIV_SYSFAIL_INT_VEC,      0x00008000},
    {UNIVERSE_ACFAIL_INT,      UNIV_ACFAIL_INT_VEC,       0x00000000}
  };

/* locals */

#ifdef  INCLUDE_VME_DMA

LOCAL BOOL  sysVmeDmaReady = FALSE;

#endif /* INCLUDE_VME_DMA */


/*******************************************************************************
*
* sysUniverseReset - reset the Universe VME chip
*
* This routine performs the reseting of the Universe chip.  All functions
* and VME mapping are disabled.
*
* RETURNS: N/A
*/

void sysUniverseReset (void)
{
	UINT32  reg;

	/* initialize registers with defaults and disable mapping */

	UNIV_OUT_LONG (UNIVERSE_SCYC_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_SCYC_ADDR,	0);
	UNIV_OUT_LONG (UNIVERSE_SCYC_EN,  	0);
	UNIV_OUT_LONG (UNIVERSE_LMISC,    	LMISC_CRT_128_USEC);
	UNIV_OUT_LONG (UNIVERSE_DCTL,     	0);
	UNIV_OUT_LONG (UNIVERSE_DTBC,     	0);
	UNIV_OUT_LONG (UNIVERSE_DLA,      	0);
	UNIV_OUT_LONG (UNIVERSE_DVA,      	0);
	UNIV_OUT_LONG (UNIVERSE_DCPP,     	0);
	UNIV_OUT_LONG (UNIVERSE_LINT_EN,  	0);
	UNIV_OUT_LONG (UNIVERSE_LINT_MAP0,	0);
	UNIV_OUT_LONG (UNIVERSE_LINT_MAP1,	0);
	UNIV_OUT_LONG (UNIVERSE_LINT_MAP2,	0);
	UNIV_OUT_LONG (UNIVERSE_VINT_EN,  	0);
	UNIV_OUT_LONG (UNIVERSE_VINT_MAP0,	0);
	UNIV_OUT_LONG (UNIVERSE_VINT_MAP1,	0);
	UNIV_OUT_LONG (UNIVERSE_VINT_MAP2,	0);

	UNIV_OUT_LONG (UNIVERSE_VSI0_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI1_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI2_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI3_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI4_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI5_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI6_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_VSI7_CTL, 	0);

	UNIV_OUT_LONG (UNIVERSE_LSI0_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI1_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI2_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI3_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI4_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI5_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI6_CTL, 	0);
	UNIV_OUT_LONG (UNIVERSE_LSI7_CTL, 	0);

	/* Clear location monitor registers */

	UNIV_OUT_LONG (UNIVERSE_LM_CTL,		0);
	UNIV_OUT_LONG (UNIVERSE_LM_BS,		0);

	/* Clear emaphore registers */

	UNIV_OUT_LONG (UNIVERSE_SEMA0,		0);
	UNIV_OUT_LONG (UNIVERSE_SEMA1,		0);


	/* clear the SYSFAIL signal */

	UNIV_OUT_LONG (UNIVERSE_VCSR_CLR,  VCSR_CLR_SYSFAIL);

	/* clear any outstanding interrupts/error conditions */

	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, LINT_STAT_CLEAR);
	UNIV_OUT_LONG (UNIVERSE_VINT_STAT, VINT_STAT_CLEAR);
	UNIV_OUT_LONG (UNIVERSE_V_AMERR, V_AMERR_V_STAT);

	UNIV_IN_LONG  (UNIVERSE_PCI_CSR, ®);
	reg |= 	PCI_CSR_D_PE | PCI_CSR_S_SERR | PCI_CSR_R_MA |
			PCI_CSR_R_TA | PCI_CSR_S_TA;
	UNIV_OUT_LONG (UNIVERSE_PCI_CSR, reg);

	UNIV_OUT_LONG (UNIVERSE_L_CMDERR, L_CMDERR_L_ENABLE);
	UNIV_OUT_LONG (UNIVERSE_DGCS, DGCS_STOP | DGCS_HALT | DGCS_DONE |
			DGCS_LERR | DGCS_VERR | DGCS_P_ERR);


	sysUnivIntsEnabled = 0;
	sysUnivIntLevel    = 0;
}


/*******************************************************************************
*
* sysUniverseInit - initialize registers of the Universe chip
*
* This routine initializes registers of the Universe VME-to-PCI bridge and maps
* access to the VMEbus memory space.
*
* NOTE: The sysProcNumSet() routine maps the master node's local memory on the
* VMEbus.
*
* RETURNS: OK, always.
*/

STATUS sysUniverseInit (void)
{
	UINT32	temp_data;

	/* Put vme chip into a power-up/reset state */

	sysUniverseReset ();

	UNIV_OUT_LONG (UNIVERSE_MAST_CTL, (MAST_CTL_RTRY_FOREVER |
		MAST_CTL_PWON_4096    |
		MAST_CTL_VRL3         |
		MAST_CTL_VRM_FAIR     |
		MAST_CTL_VREL_ROR     |
		MAST_CTL_PABS_128     ));
	UNIV_IN_LONG (UNIVERSE_MISC_CTL, &temp_data);

	/* maintain power-up option bits */

	temp_data &= ( MISC_CTL_SYSCON | MISC_CTL_V64AUTO );
	temp_data |= ( MISC_CTL_VBTO_256USEC   |
		MISC_CTL_VARB_RROBIN    |
		MISC_CTL_VARBTO_256USEC |
		MISC_CTL_RESCIND        );
	UNIV_OUT_LONG (UNIVERSE_MISC_CTL, temp_data);
	UNIV_OUT_LONG (UNIVERSE_USER_AM,  0);
	UNIV_OUT_LONG (UNIVERSE_VRAI_CTL, 0);
	UNIV_OUT_LONG (UNIVERSE_VCSR_CTL, 0);

	/* clear the SYSFAIL signal */

	UNIV_OUT_LONG (UNIVERSE_VCSR_CLR, VCSR_CLR_SYSFAIL);

	/* set the latency timer to max value */

	UNIV_OUT_LONG (UNIVERSE_PCI_MISC0, PCI_MISC0_LATENCY_TIMER);

	/* Map to get to VMEbus  using A32 */

	UNIV_OUT_LONG (UNIVERSE_LSI1_BS,  VAL_LSI1_BS);
	UNIV_OUT_LONG (UNIVERSE_LSI1_BD,  VAL_LSI1_BD);
	UNIV_OUT_LONG (UNIVERSE_LSI1_TO,  VAL_LSI1_TO);
	UNIV_OUT_LONG (UNIVERSE_LSI1_CTL, VAL_LSI1_CTL);

	/* Map to get to VMEbus  using A24 */

	UNIV_OUT_LONG (UNIVERSE_LSI2_BS,  VAL_LSI2_BS);
	UNIV_OUT_LONG (UNIVERSE_LSI2_BD,  VAL_LSI2_BD);
	UNIV_OUT_LONG (UNIVERSE_LSI2_TO,  VAL_LSI2_TO);
	UNIV_OUT_LONG (UNIVERSE_LSI2_CTL, VAL_LSI2_CTL);

	/* Map to get to VMEbus using A16 */

	UNIV_OUT_LONG (UNIVERSE_LSI3_BS,  VAL_LSI3_BS);
	UNIV_OUT_LONG (UNIVERSE_LSI3_BD,  VAL_LSI3_BD);
	UNIV_OUT_LONG (UNIVERSE_LSI3_TO,  VAL_LSI3_TO);
	UNIV_OUT_LONG (UNIVERSE_LSI3_CTL, VAL_LSI3_CTL);

	/* Map to get to VMEbus LM/MBOX Registers using A32 */

	UNIV_OUT_LONG(UNIVERSE_LSI0_BS,  VAL_LSI0_BS);
	UNIV_OUT_LONG(UNIVERSE_LSI0_BD,  VAL_LSI0_BD);
	UNIV_OUT_LONG(UNIVERSE_LSI0_TO,  VAL_LSI0_TO);
	UNIV_OUT_LONG(UNIVERSE_LSI0_CTL, VAL_LSI0_CTL);

	return (OK);
}


/*******************************************************************************
*
* sysUniverseInit2 - enable local memory accesses from the VMEbus
*
* This routine enables local resources to be accessed from the VMEbus.
* All target boards have an A32 window opened to access the VMEbus LM/SIG
* registers.  However, only the master node has an A32 and an A24
* window open to its local memory space.
*
* RETURNS: N/A
*
* NOMANUAL
*/

void sysUniverseInit2
    (
    int         procNum                 /* processor number */
    )
{
#ifdef INCLUDE_VME_LM
	UNIV_OUT_LONG(UNIVERSE_LM_CTL, VAL_LM_CTL);
	/*
	 * VAL_LM_BASE is defined in vg4.h and is not a constant.
	 * 
	 * #define VAL_LM_BS	(VME_LM_MSTR_BUS + (sysProcNumGet() * \
	 *				 VME_LM_SLV_SIZE))
	 * 			= 0x40000000 + procNum * 0x00001000
	 */
	UNIV_OUT_LONG(UNIVERSE_LM_BS, VAL_LM_BS);
#endif /* INCLUDE_VME_LM */

#ifdef INCLUDE_VME_MBOX
	/* Setting VMEbus Register Access Image */

	UNIV_OUT_LONG (UNIVERSE_VRAI_CTL, (VRAI_CTL_EN |
				  VRAI_CTL_AM_DATA |
				  VRAI_CTL_AM_PGM |
				  VRAI_CTL_AM_SUPER |
				  VRAI_CTL_AM_USER |
				  VRAI_CTL_VAS_A32));

	/* UNIV_OUT_LONG (UNIVERSE_VRAI_BS, DEFAULT_VRAI_BS); */
	UNIV_OUT_LONG (UNIVERSE_VRAI_BS, VME_A32_REG_BASE +
					(procNum * VME_A32_REG_SIZE)); 
#endif /* INCLUDE_VME_MBOX */


	if ( procNum == 0 )
	{
		/* setup the A32 window */

		UNIV_OUT_LONG (UNIVERSE_VSI1_BS,  VAL_VSI1_BS);
		UNIV_OUT_LONG (UNIVERSE_VSI1_BD,  VAL_VSI1_BD);
		UNIV_OUT_LONG (UNIVERSE_VSI1_TO,  VAL_VSI1_TO);
		UNIV_OUT_LONG (UNIVERSE_VSI1_CTL, VAL_VSI1_CTL | VSI_CTL_PWEN);
#ifdef A24_SLV_WINDOW
		UNIV_OUT_LONG(UNIVERSE_VSI2_BS,  VAL_VSI2_BS);
		UNIV_OUT_LONG(UNIVERSE_VSI2_BD,  VAL_VSI2_BD);
		UNIV_OUT_LONG(UNIVERSE_VSI2_TO,  VAL_VSI2_TO);
#endif

		UNIV_OUT_LONG(UNIVERSE_VSI1_CTL, VAL_VSI1_CTL | VSI_CTL_PWEN);
#ifdef A24_SLV_WINDOW
		UNIV_OUT_LONG(UNIVERSE_VSI2_CTL, VAL_VSI2_CTL | VSI_CTL_PWEN);
#endif

#if	(SM_OFF_BOARD == FALSE)
#ifndef	ANY_BRDS_IN_CHASSIS_NOT_RMW

	       /*
		* All slave boards in the chassis generate a VMEbus RMW, and
		* the master is capable of translating an incoming RMW into
		* an atomic operation.
		*/

	       /*
		* The A32 access range is now divided into 3 separate windows.
		* The first window will allow normal access to the start of local
		* memory up to the Shared Memory Region.  It is defined above
		* thru the use of a conditional #define VAL_VSI1_BD in sbs_vg4.h.
		* The second window will allow Read-Modify-Write (RMW) access to
		* the Standard VxWorks and VxMP's Shared Memory Region.  The
		* third window will allow normal access to the local memory
		* starting after the Shared Memory Region up to the end of
		* physical memory.
		*/

		/* setup the second A32 window */

		UNIV_OUT_LONG(UNIVERSE_VSI4_BS,  VAL_VSI4_BS);
		UNIV_OUT_LONG(UNIVERSE_VSI4_BD,  VAL_VSI4_BD);
		UNIV_OUT_LONG(UNIVERSE_VSI4_TO,  VAL_VSI4_TO);
		UNIV_OUT_LONG(UNIVERSE_VSI4_CTL, VAL_VSI4_CTL);

		/* setup the third A32 window */

		UNIV_OUT_LONG(UNIVERSE_VSI5_BS,  VAL_VSI5_BS);
		UNIV_OUT_LONG(UNIVERSE_VSI5_BD,  VAL_VSI5_BD);
		UNIV_OUT_LONG(UNIVERSE_VSI5_TO,  VAL_VSI5_TO);
		UNIV_OUT_LONG(UNIVERSE_VSI5_CTL, VAL_VSI5_CTL);

#endif	/* ANY_BRDS_IN_CHASSIS_NOT_RMW */
#endif	/* SM_OFF_BAORD */
	} /* procNum == 0 */
}


/*******************************************************************************
*
* sysIntDisable - disable a bus interrupt level
*
* This routine disables reception of a specified VMEbus interrupt level.
*
* NOTE: revision 1.0 Universe chips can fail and lockup if two or more interrupt
* levels are enabled.  For more details see Tundra Universe Errata sheet.
*
* RETURNS: OK, or ERROR if  is not in the range 1 - 7.
*
* SEE ALSO: sysIntEnable()
*/

STATUS sysIntDisable
    (
    int intLevel        /* interrupt level to disable (1-7) */
    )
{
    int mask;

    if ( intLevel < 1 || intLevel > 7 )
		return (ERROR);

	/* clear any pending intr. for this level */

	mask = 1 << intLevel;
	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, mask);

	/* disable the interrupt */

	sysUnivIntsEnabled &= ~mask;
	UNIV_OUT_LONG (UNIVERSE_LINT_EN, sysUnivIntsEnabled);

	return (OK);
}


/*******************************************************************************
*
* sysIntEnable - enable a bus interrupt level
*
* This routine enables reception of a specified VMEbus interrupt level.
*
* NOTE: revision 1.0 Universe chips can fail and lockup if two or more interrupt
* levels are enabled.  For more details see Tundra Universe Errata sheet.
*
* RETURNS: OK, or ERROR if  is not in the range 1 - 7.
*
* SEE ALSO: sysIntDisable()
*/

STATUS sysIntEnable
    (
    int intLevel        /* interrupt level to enable (1-7) */
    )
{
    int mask;

	if ( intLevel < 1 || intLevel > 7 )
    {
		return (ERROR);
    }

	/* clear any pending intr. for this level */

	mask = 1 << intLevel;
	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, mask);

	/* enable the interrupt */

	sysUnivIntsEnabled |= mask;
	UNIV_OUT_LONG (UNIVERSE_LINT_EN, sysUnivIntsEnabled);

	/* map the interrupt.  Currently all VME ints are mapped to LINT#0 */

	UNIV_OUT_LONG (UNIVERSE_LINT_MAP0, 0);

	return (OK);
}


/*******************************************************************************
*
* sysBusIntAck - acknowledge a bus interrupt
*
* This routine acknowledges a specified VMEbus interrupt level.
*
* NOTE: This routine is included for BSP compliance only.  Since VMEbus
* interrupts are re-enabled in the interrupt handler, and acknowledged
* automatically by hardware, this routine is a no-op.
*
* RETURNS: 0.
*
* SEE ALSO: sysBusIntGen()
*/

int sysBusIntAck
    (
    int intLevel        /* interrupt level to acknowledge */
    )
{
    return (0);
}


/*******************************************************************************
*
* sysBusIntGen - generate a bus interrupt
*
* This routine generates a VMEbus interrupt for a specified level with a
* specified vector.  Only one VME interrupt can be generated at a time and
* none can be generated if a previously generated VME interrupt has not been
* acknowledged, i.e., if no VME bus IACK cycle has completed.
*
* RETURNS: OK, or ERROR if  or  are out of range or if an
* interrupt is already in progress.
*
* SEE ALSO: sysBusIntAck()
*/

STATUS sysBusIntGen
    (
    int level,          /* interrupt level to generate    */
    int vector          /* interrupt vector for interrupt */
    )
{
    volatile ULONG enReg;
	volatile ULONG intReg;
	volatile ULONG statidReg;
	int            lockKey;

	/* Validate interrupt level */

	if ( level < 1 || level > 7 || vector > 255 || vector < 2 )
    {
		return (ERROR);
    }



	/* lock interrupts so there will be no interference */

	CPU_INT_LOCK (&lockKey);

	/* Ensure bus interrupt is not already asserted */

	UNIV_IN_LONG (UNIVERSE_VINT_STAT, &intReg);
	if ( intReg & ((1 << level) << 24) )
	{
		CPU_INT_UNLOCK (lockKey);
		return (ERROR);
	}

	/*
	* Clear Software Interrupt bit before setting it
	* It needs a 0 to 1 transition to generate an interrupt
	*/

	UNIV_IN_LONG (UNIVERSE_VINT_EN, &enReg);
	enReg &= ~((1 << level) << 24);
	UNIV_OUT_LONG (UNIVERSE_VINT_EN, enReg);

	/*
	* store vector number, wiping out LSB of vector number
	* This implies that only even number vectors should be used
	* (Universe FEATURE ?)
	*/

	UNIV_IN_LONG (UNIVERSE_STATID, &statidReg);
	vector >>= 1;
	statidReg = (statidReg & STATID_MASK) | (vector << 25);
	UNIV_OUT_LONG (UNIVERSE_STATID, statidReg);

	/* Generate a SW interrupt on the VMEbus at the requested level */

	UNIV_IN_LONG (UNIVERSE_VINT_EN, &enReg);
	enReg |= ((1 << level) << 24);
	UNIV_OUT_LONG (UNIVERSE_VINT_EN, enReg);

	/* unlock the interrupt */

	CPU_INT_UNLOCK (lockKey);

	return (OK);
}


/*******************************************************************************
*
* sysUnivIntEnable - enable Universe supported interrupt(s)
*
* This routine enables the specified type(s) of interrupt supported by the
* Universe VME-to-PCI bridge.
*
* RETURNS: OK, or ERROR if invalid interrupt type(s).
*
* SEE ALSO: sysUnivIntDisable()
*
*/

STATUS sysUnivIntEnable
    (
    int univIntType	/* interrupt type */
    )
{
    /* make sure the interrupt type is valid */

	if ( (univIntType & UNIVERSE_INT_MASK)     == 0 ||
		(univIntType & UNIVERSE_RESERVED_INT) != 0 )
    {
		return(ERROR);
    }

	/* clear any pending intr. for the type */

	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, univIntType);

	/* enable the interrupt */

	sysUnivIntsEnabled |= univIntType;
	UNIV_OUT_LONG (UNIVERSE_LINT_EN, sysUnivIntsEnabled);

	/* map the interrupt.  Currently all VME ints are mapped to LINT#0 */

	UNIV_OUT_LONG (UNIVERSE_LINT_MAP1, 0);

	return (OK);
}


/*******************************************************************************
*
* sysUnivIntDisable - disable Universe supported interrupt(s)
*
* This routine disables the specified type(s) of interrupt supported by the
* Universe VME-to-PCI bridge.
*
* RETURNS: OK, or ERROR if invalid interrupt type(s).
*
* SEE ALSO: sysUnivIntEnable()
*
*/

STATUS sysUnivIntDisable
    (
    int univIntType /* interrupt type */
    )
{
    /* make sure the interrupt type is valid */

	if ( (univIntType & UNIVERSE_INT_MASK)     == 0 ||
		(univIntType & UNIVERSE_RESERVED_INT) != 0 )
		return (ERROR);

	/* disable the interrupt */

	sysUnivIntsEnabled &= ~univIntType;
	UNIV_OUT_LONG (UNIVERSE_LINT_EN, sysUnivIntsEnabled);

	/* clear any pending intr. for the type */

	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, univIntType);

	return (OK);
}


/*******************************************************************************
*
* sysUnivIntLevelSet - set a Universe supported interrupt level
*
* This routine disables all interrupts supported by the Universe at and below
* the specified level.  The lowest level is 0, the highest is 15.  The
* priority mapping is:
* .CS
*    0		no interrupts masked
*    1		UNIVERSE_VOWN_INT
*    2		VMEBUS_LVL1
*    3		VMEBUS_LVL2
*    4		VMEBUS_LVL3
*    5		VMEBUS_LVL4
*    6		VMEBUS_LVL5
*    7		VMEBUS_LVL6
*    8		VMEBUS_LVL7
*    9		UNIVERSE_DMA_INT
*    10		UNIVERSE_LERR_INT
*    11		UNIVERSE_VERR_INT
*    12		UNIVERSE_VME_SW_IACK_INT
*    13		UNIVERSE_PCI_SW_INT
*    14		UNIVERSE_SYSFAIL_INT
*    15		UNIVERSE_ACFAIL_INT
* .CE
*
* If the level specified is -1, the level is not changed, just the current
* level is returned.
*
* RETURNS: previous interrupt level.
*
* SEE ALSO: sysUnivIntDisable(), sysUnivIntEnable()
*/

int sysUnivIntLevelSet
    (
    int  univIntLvl	/* Universe interrupt level */
    )
{
    int  intLvl;
	int  key;

	/* Just return current level if so requested */

	if ( univIntLvl == -1 )
		return (sysUnivIntLevel);

	/* Lock out interrupts during level change */

	CPU_INT_LOCK (&key);

	/* disable the interrupt levels at and below the current level */

	intLvl = univIntTable[univIntLvl].intMask;
	intLvl &= sysUnivIntsEnabled;
	UNIV_OUT_LONG (UNIVERSE_LINT_EN, intLvl);

	/* save current level for return */

	intLvl = sysUnivIntLevel;

	/* set new mask */

	sysUnivIntLevel = univIntLvl;

	/* Unlock interrupts */

	CPU_INT_UNLOCK (key);

	/* return previous mask */

	return (intLvl);
}


/******************************************************************************
*
* sysUnivIntConnect - connect an interrupt handler for an interrupt type
*
* This routine connects an interrupt handler for a specified interrupt type
* to the system vector table of the Universe VME-to-PCI bridge.
*
* RETURNS: OK, or ERROR if any argument is invalid or memory cannot be
* allocated.
*/

STATUS sysUnivIntConnect
    (
    int		    univIntType,  /* the interrupt type to connect with */
    VOIDFUNCPTR	routine,      /* routine to be called */
    int		    parameter     /* parameter to be passed to routine */
    )
{
    int			univVecNum = 0;

	/* make sure the interrupt type is valid */
	if ( (univIntType & UNIVERSE_INT_MASK) == 0 )
    {
		return (ERROR);
    }

	/* determine the vector number for the interrupt */
    switch ( univIntType )
    {
	    case UNIVERSE_DMA_INT:
		    univVecNum = UNIV_DMA_INT_VEC;
		    break ;
	    case UNIVERSE_LERR_INT:
		    univVecNum = UNIV_LERR_INT_VEC;
		    break ;
	    case UNIVERSE_VERR_INT:
		    univVecNum = UNIV_VERR_INT_VEC;
		    break ;
	    case UNIVERSE_SYSFAIL_INT:
		    univVecNum = UNIV_SYSFAIL_INT_VEC;
		    break ;
	    case UNIVERSE_ACFAIL_INT:
		    univVecNum = UNIV_ACFAIL_INT_VEC;
		    break ;
	    case UNIVERSE_PCI_SW_INT:
		    univVecNum = UNIV_PCI_SW_INT_VEC;
		    break ;
	    case UNIVERSE_VME_SW_IACK_INT:
		    univVecNum = UNIV_VME_SW_IACK_INT_VEC;
		    break ;
	    case UNIVERSE_VOWN_INT:
		    univVecNum = UNIV_VOWN_INT_VEC;
		    break ;
	    default:
		    /* doesn't match one of the intr. types, so return an error */
		    return (ERROR);
    }

	/* install the handler in the vector table */

	if ( sysVmeIntConnect (INUM_TO_IVEC (univVecNum), routine, parameter) == ERROR )
    {
		return(ERROR);
    }

	return (OK);
}


/******************************************************************************
*
* sysUnivIntDisconnect - disconnect an interrupt handler for an interrupt type
*
* This routine disconnects an interrupt handler for a specified interrupt type
* from the system vector table of the Universe VME-to-PCI bridge.
*
* RETURNS: OK, or ERROR if any argument is invalid
*/

STATUS sysUnivIntDisconnect
    (
    int		    univIntType,  /* the interrupt type to connect with */
    VOIDFUNCPTR	routine,      /* routine to be called */
    int		    parameter     /* parameter to be passed to routine */
    )
{
    int			univVecNum = 0;

	/* make sure the interrupt type is valid */
	if ( (univIntType & UNIVERSE_INT_MASK) == 0 )
    {
		return (ERROR);
    }

	/* determine the vector number for the interrupt */
    switch ( univIntType )
    {
	    case UNIVERSE_DMA_INT:
		    univVecNum = UNIV_DMA_INT_VEC;
		    break ;
	    case UNIVERSE_LERR_INT:
		    univVecNum = UNIV_LERR_INT_VEC;
		    break ;
	    case UNIVERSE_VERR_INT:
		    univVecNum = UNIV_VERR_INT_VEC;
		    break ;
	    case UNIVERSE_SYSFAIL_INT:
		    univVecNum = UNIV_SYSFAIL_INT_VEC;
		    break ;
	    case UNIVERSE_ACFAIL_INT:
		    univVecNum = UNIV_ACFAIL_INT_VEC;
		    break ;
	    case UNIVERSE_PCI_SW_INT:
		    univVecNum = UNIV_PCI_SW_INT_VEC;
		    break ;
	    case UNIVERSE_VME_SW_IACK_INT:
		    univVecNum = UNIV_VME_SW_IACK_INT_VEC;
		    break ;
	    case UNIVERSE_VOWN_INT:
		    univVecNum = UNIV_VOWN_INT_VEC;
		    break ;
	    default:
		    /* doesn't match one of the intr. types, so return an error */
		    return (ERROR);
    }

	/* install the handler in the vector table */

	if ( sysVmeIntDisconnect (INUM_TO_IVEC (univVecNum), routine, parameter) == ERROR )
    {
		return(ERROR);
    }

	return (OK);
}


#ifdef INCLUDE_VME_MBOX /* Mailbox interrupts are implemented as LM ints */
/*
 * XXX: mailbox routines are not thread safe.
 * 	appl. must ensure MT safety.
 */
#define VME_MAILBOX_NMAX 4	/* Universe II has 4 real mailboxes */
static unsigned int mbox_alloc_flags=1; /* mbox 0 allocated as system mailbox */

static struct mboxInfo {
	UINT32 regoff;       /* register offset to Universe base addr */
	UINT32 int_vec_num;  /* Universe interrupt vector number */
	VOIDFUNCPTR handler; /* mbox irq handler */
	void *arg;           /* irq handler arg */
} mboxInfo[VME_MAILBOX_NMAX] = {
	{ UNIVERSE_MBOX0_OFF, UNIV_MBOX0_INT_VEC, NULL, NULL},
	{ UNIVERSE_MBOX1_OFF, UNIV_MBOX1_INT_VEC, NULL, NULL},
	{ UNIVERSE_MBOX2_OFF, UNIV_MBOX2_INT_VEC, NULL, NULL},
	{ UNIVERSE_MBOX3_OFF, UNIV_MBOX3_INT_VEC, NULL, NULL}
};

/*******************************************************************************
*
* sysVmeMailboxAlloc - allocate a mailbox
*
* Allocate a mailbox. A opaque handle to the mailbox is returned.
*
* NOTE: Actually in the implementation for the SBS VG4 the handle is
* the number of the mailbox.
* mailbox 0 ist reserved as system mailbox (see sysMailboxConnect()).
* Three user mailboxes are available on the SBS VG4.
*
* RETURNS: mailbox handle (on VG4: integer values 1, 2 or 3), -1 otherwise.
*
* SEE ALSO: sysVmeMailboxFree().
*/
void * sysVmeMailboxAlloc
	(
	void
	)
{
	unsigned int bit=1;
	int i;

	for (i=0; i which was allocated by sysVmeMailboxAlloc() is freed.
*
* RETURNS: N/A.
* SEE ALSO: sysVmeMailboxAlloc().
*/
void sysVmeMailboxFree
	(
	void *mbox           /* mailbox handle. Here actually mbox number */
	)
{
	mbox_alloc_flags &= ~(1 << (unsigned int)mbox);
}

/*******************************************************************************
*
* sysVmeMailboxConnect - connect a routine to a VME mailbox interrupt
*
* This routine specifies the interrupt service routine to be called at each
* mailbox interrupt.
* The routine is indirectly called by the common mailbox interrupt handler
* installed with intConnect().
*
* PARAMETERS: mbox, routine, arg
*	mbox - mbox should be allocated with sysVmeMailboxAlloc().
*	routine - called by common mailbox interrupt handler.
*	arg - passed from common mailbox interrupt handler to handler routine.
*
* NOTE: Since VxWorks provides no intDisconnect(), there is
* no sysVmeMailboxDisconnect().
*
* RETURNS: OK, or ERROR if the routine cannot be connected to the interrupt. 
*
* SEE ALSO: sysVmeMailboxAlloc(), sysMailboxEnable(),
* and BSP-specific reference pages for this routine 
*/
STATUS sysVmeMailboxConnect
    (
    void *mbox,		  /* mailbox handle. Here actually the mbox number. */
    VOIDFUNCPTR routine,  /* routine called at each mailbox interrupt */
    void *arg             /* argument with which to call routine      */
    )
{
	int status;
	status = intConnect (INUM_TO_IVEC (mboxInfo[(int)mbox].int_vec_num),
		sysMailboxInt, (int) mbox);
	if (status != OK) {
		return status;
	}
	mboxInfo[(int)mbox].handler = routine;
	mboxInfo[(int)mbox].arg = arg;
	return status;
}

/*******************************************************************************
*
* sysVmeMailboxEnable - enable the mailbox interrupt
*
* This routine enables the mailbox interrupt.
*
* RETURNS: OK, or ERROR if the interrupt couldn't be enabled.
* An error can only occur if there is an implementation bug in the BSP,
* i.e. it should never happen.
*
* SEE ALSO: sysVmeMailboxDisable()
*/
STATUS sysVmeMailboxEnable
    (
    void *mbox           /* mailbox handle. Here actually mbox number */
    )
{
    if (sysUnivIntEnable(UNIVERSE_MBOX0_INT << (int)mbox) == OK)
        return (OK);
    else
	return (ERROR);
}

/*******************************************************************************
*
* sysVmeMailboxDisable - disable the mailbox interrupt
*
* This routine disables the mailbox interrupt.
*
* RETURNS: OK, or ERROR if the interrupt couldn't be disabled.
* An error can only occur if there is an implementation bug in the BSP,
* i.e. it should never happen.
*
* SEE ALSO: sysVmeMailboxEnable().
*/
STATUS sysVmeMailboxDisable
    (
    void *mbox           /* mailbox handle. Here actually mbox number */
    )
{
    if (sysUnivIntDisable(UNIVERSE_MBOX0_INT << (int)mbox) == OK)
        return (OK);
    else
	return (ERROR);
}

/*******************************************************************************
*
* sysVmeMailboxAddrGet - returns VMEbus address for given mbox
*
* sysVmeMailboxAddrGet() returns the VMEbus address for a given mbox.
* This routine makes it easier to pass the mailbox address to other CPU boards
* in the systems.
* On the VG4 the routine reads back the base address register for the register
* window of the Universe II (UNIVERSE_VRAI_BS) and adds the offset for the
* given mailbox.
*
* NOTE: It may be used for the SM network when SM_INT_TYPE is
* SM_INT_MAILBOX_X as SM_INT_ARG2. In that case the mbox parameter is 0,
* the system mailbox.
*
* BUGS: The address space for an implementation must be known.
* Usually it is VME_AM_EXT_SUP_DATA (0x0d).
*
* RETURNS: VMEbus address of given mailbox.
*/
UINT32 sysVmeMailboxAddrGet
	(
	void *mbox           /* mailbox handle. Here actually mbox number */
	)
{
	UINT32 addr;
	UNIV_IN_LONG(UNIVERSE_VRAI_BS, &addr);
	addr += (mboxInfo[(int)mbox].regoff);
	return addr;
}

/*******************************************************************************
*
* sysVmeMailboxLocalAddrGet - returns local address for given mbox
*
* sysVmeMailboxLocalAddrGet() returns the local address for a given mbox.
* This makes it possible to access and use a VME mailbox locally.
* On the VG4 the routine adds to the base address of the UNIVERSE
* the offset for the given mailbox.
*
* RETURNS: local address of given mailbox.
*/
UINT32 sysVmeMailboxLocalAddrGet
	(
	void *mbox           /* mailbox handle. Here actually mbox number */
	)
{
	UINT32 addr;
	addr = (UINT32) univBaseAdrs + (mboxInfo[(int)mbox].regoff);
	return addr;
}

/*******************************************************************************
*
* sysVmeMailboxDataGet - returns mailbox data
*
* might be used from user installed mailbox handler
*/
UINT32 sysVmeMailboxDataGet
	(
	void *mbox           /* mailbox handle. Here actually mbox number */
	)
{
	UINT32 *addr, data;
	addr = (UINT32 *)UNIVERSE_ADRS(mboxInfo[(int)mbox].regoff);
	UNIV_IN_LONG(addr, &data);
	/* LONGSWAP, because endianess for register remains little endian
	 * no automatically swapping is done for VMEbus accesses
	 */
	return LONGSWAP(data);
}
	
/*******************************************************************************
*
* sysMailboxInt - mailbox interrupt handler
*
* This routine calls the installed mailbox routine, if it exists.
*/

LOCAL void sysMailboxInt
	(
	int mailboxId	/* 0..3 (VME_MAILBOX_NMAX-1) */
	)
{
	VOIDFUNCPTR routine;
	void *arg;
	routine = mboxInfo[mailboxId].handler;
	arg = mboxInfo[mailboxId].arg;
	/*
	 * user handler may call sysVmeMailboxDataGet() function
	 * to get written mailbox data
	 */
	if (routine != NULL) {
		routine (arg);
	} else {
		logMsg("Default Mailbox handler: mailboxID = %d, data = 0x%x\n",
			mailboxId, (int) arg, 0,0,0,0);
	}
}


/*******************************************************************************
*
* sysMailboxConnect - connect a routine to the mailbox interrupt
*
* This routine specifies the interrupt service routine to be called at each
* mailbox interrupt.
*
* NOTE: The mailbox interrupt is the mailbox 0 interrupt on the
* UNIVERSE II chip. sysVmeMailboxAlloc() cannot be used,
* because for the SM network the mailbox address must be known
* (config.h: #define SM_INT_ARG2 sysVmeMailboxAddrGet(0))
*
* RETURNS: OK, or ERROR if the routine cannot be connected to the interrupt.
*
* SEE ALSO: intConnect(), sysMailboxEnable(), sysVmeMailboxConnect()
*/

STATUS sysMailboxConnect
    (
    FUNCPTR routine,    /* routine called at each mailbox interrupt */
    int arg             /* argument with which to call routine      */
    )
{
#if 0
	/* see note above */
	void *sysMailboxHandle = sysVmeMailboxAlloc();
#else
	void *sysMailboxHandle = (void *) 0;	/* mailbox 0 */
#endif
	return sysVmeMailboxConnect(sysMailboxHandle, (VOIDFUNCPTR) routine, (void *)arg);
}


/*******************************************************************************
*
* sysMailboxEnable - enable the mailbox interrupt
*
* This routine enables the mailbox interrupt.
*
* NOTE: The mailbox interrupt is the mailbox 0 interrupt on the
* UNIVERSE II chip.
*
* RETURNS: OK, or ERROR if the interrupt couldn't be disabled.
* An error can only occur if there is an implementation bug in the BSP,
* i.e. it should never happen.
*
* SEE ALSO: sysMailboxConnect(), sysMailboxDisable()
*/

STATUS sysMailboxEnable
    (
    char *mailboxAdrs           /* address of mailbox (ignored) */
    )
{
	return sysVmeMailboxEnable(0);
}


/*******************************************************************************
*
* sysMailboxDisable - disable the mailbox interrupt
*
* This routine disables the mailbox interrupt.
*
* NOTE: The mailbox interrupt is the mailbox 0 interrupt on the
* UNIVERSE II chip.
*
* RETURNS: OK, always.
*
* SEE ALSO: sysMailboxConnect(), sysMailboxEnable()
*/

STATUS sysMailboxDisable
    (
    char *mailboxAdrs           /* address of mailbox (ignored) */
    )
{
	return sysVmeMailboxDisable(0);
}
#endif /* INCLUDE_VME_MBOX */


/*******************************************************************************
*
* sysUnivVERRClr - Universe VMEbus Error Clear routine
*
* This is the VMEbus Error clear routine for the Tundra Universe PCI to VME
* Bridge.  It counts the ocuurances at the specified counter and clears the
* error condition in the three registers associated with VME Bus Errors:
* LINT_STAT, VINT_STAT, and V_AMERR.
*
* RETURNS: N/A
*/

void sysUnivVERRClr (void)
{
	UINT32 status;

	/* Count occurances */

	++sysUnivVERRCnt;

	/* Get current address error status */

	UNIV_IN_LONG (UNIVERSE_V_AMERR, &status);

	/* Clear any error */

	status &= V_AMERR_V_STAT;
	UNIV_OUT_LONG (UNIVERSE_V_AMERR, status);

	/* Get current VME error status */

	UNIV_IN_LONG (UNIVERSE_VINT_STAT, &status);

	/* Clear any error */

	status &= VINT_STAT_VERR;
	UNIV_OUT_LONG (UNIVERSE_VINT_STAT, status);

	/* Get current PCI error status */

	UNIV_IN_LONG (UNIVERSE_LINT_STAT, &status);

	/* Clear any error */

	status &= LINT_STAT_VERR;
	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, status);

	/* Force write due to Write-Posting and get updated status */

	UNIV_IN_LONG (UNIVERSE_PCI_CSR, &status);
}


/*******************************************************************************
*
* sysUnivLevelDecode - decode highest pending priority Universe interrupt
*
* This routine decodes the highest pending priority Universe interrupt from a
* bit field of interrupts and returns the associated interrupt vector, priority
* level and priority level bit mask.
*
* RETURNS: highest pending interrupt priority level bit mask
*
* SEE ALSO: register and bit field defs in universe.h
*/

int sysUnivLevelDecode
    (
    int   bitField,	/* one interrupt per bit, up to 15 bits */
    int * vecNum,	/* where to return associated vector */
    int * intLvl	/* where to return associated Universe int level */
    )
{
    int    bitLvlMsk;
    int    i;
    int    vector;

    /* Compare bits in order of highest priority first */

    for ( i = UNIV_NUM_INT; i >= 0; --i )
    {
        if ( bitField & univIntTable[i].bitMask )
            break;
    }

    /* Determine and return interrupt vector, priority level and level mask */

    bitLvlMsk = univIntTable[i].bitMask;
    if ( univIntTable[i].vector != -1 )
    {
        *vecNum = univIntTable[i].vector;
    }
    else
    {
        switch ( bitLvlMsk )
        {
            case VMEBUS_LVL7:
                UNIV_IN_LONG (UNIVERSE_V7_STATID, &vector);
                break ;

            case VMEBUS_LVL6:
                UNIV_IN_LONG (UNIVERSE_V6_STATID, &vector);
                break ;

            case VMEBUS_LVL5:
                UNIV_IN_LONG (UNIVERSE_V5_STATID, &vector);
                break ;

            case VMEBUS_LVL4:
                UNIV_IN_LONG (UNIVERSE_V4_STATID, &vector);
                break ;

            case VMEBUS_LVL3:
                UNIV_IN_LONG (UNIVERSE_V3_STATID, &vector);
                break ;

            case VMEBUS_LVL2:
                UNIV_IN_LONG (UNIVERSE_V2_STATID, &vector);
                break ;

            case VMEBUS_LVL1:
                UNIV_IN_LONG (UNIVERSE_V1_STATID, &vector);
                break ;
        }

        /*
         * Check for errors
         *   At this point, if a VME bus error has occured, it must be
         * cleared by the sysUnivVERRClr() routine and the vector discarded.
         * The vector returned during a VME bus error is bogus.
         */

        if ( (vector & V1_STATID_ERR) != 0 )
        {
            /* Clear VME bus error */

            sysUnivVERRClr ();

            /* Discard bad vector */

            *vecNum = 0;
        }
        else
        {
            *vecNum = vector & 0xFF;  /* only eight bits of useful info */
        }
    }
    *intLvl = i;

    return (bitLvlMsk);
}


/*******************************************************************************
*
* sysUnivVmeIntr - VMEbus interrupt handler
*
* This is the VMEbus interrupt handler for the Motorola MPC107 PCI Host
* Bridge (PHB) and Embedded-Programmable Interrupt Controller (EPIC).  It is
* connected to the single VMEbus interrupt from the Raven and examines the
* Universe to chip to determine the interrupt level and vector of the
* interrupt source.  Having obtained the vector number, this routine then
* vectors into the system vector table to the specified interrupt handling
* routine.
*
* RETURNS: N/A
*/

void sysUnivVmeIntr (void)
{
    int         pendingIntr;
    int         highestIntr;
    int			prevIntLvl;
    int			univIntLvl;
    int			vec_num = 0;
    INT_HANDLER_DESC *  currHandler;

    /* get pending interrupt level(s) */

    UNIV_IN_LONG (UNIVERSE_LINT_STAT, &pendingIntr);
    pendingIntr &= LINT_STAT_INT_MASK;
    pendingIntr &= sysUnivIntsEnabled;

    /*
    * Handle pending interrupts in order of highest priority.
    *
    * For each interrupt, determine the level, get vector associated
    * with the interrupt, set the appropriate interrupt level, clear the
    * interrupt in the Universe, and dispatch to all associated ISRs.
    */

    if ( pendingIntr != 0 )
    {
        /* Determine highest interrupt priority level and vector */

        highestIntr = sysUnivLevelDecode (pendingIntr, &vec_num, &univIntLvl);

        /* Set intr level: mask out all ints at and below this level */

        prevIntLvl = sysUnivIntLevelSet (univIntLvl);

        /* Clear interrupt request in Universe */

        UNIV_OUT_LONG (UNIVERSE_LINT_STAT, highestIntr);

        vec_num   &= 0xff;
        if ( vec_num == 0 )
        {
            if (_func_spuriousVmeIntHook != NULL) {
                _func_spuriousVmeIntHook (spuriousVmeIntHandlerArg, vec_num);
            } else {
                logMsg ("bad vme interrupt %d\n", vec_num, 0,0,0,0,0);
            }
        }
        else if ( (currHandler = sysIntTbl[vec_num]) == NULL )
        {
            if (_func_uninitVmeIntHook != NULL) {
                _func_uninitVmeIntHook (uninitVmeIntHandlerArg, vec_num);
            } else {
                logMsg ("uninitialized vme interrupt %d\n", vec_num, 0,0,0,0,0);
            }
        }
        else
        {
            while ( currHandler != NULL )
            {
                currHandler->vec (currHandler->arg);
                currHandler = currHandler->next;
            }
        }

        /* restore previous interrupt level */

        (void)sysUnivIntLevelSet (prevIntLvl);
    }

    return;
}


/******************************************************************************
*
* sysVmeIntConnect - connect an interrupt handler for a VME interrupt vector
*
* This function connects an interrupt handler for a given VMEbus interrupt vector
* to the system vector table.
* It is compatible to intConnect().
*
* RETURNS: OK/ERROR.
*/

STATUS
sysVmeIntConnect
    (
     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;
	int					intNum;


	intNum = IVEC_TO_INUM(vector);

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

    /* create a new interrupt handler */

    newHandler = malloc (sizeof (INT_HANDLER_DESC));

    /* check if the memory allocation succeed */

    if (newHandler == 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[intNum] == NULL)
    {
		sysIntTbl [(int ) intNum] = newHandler;	/* single int. handler case */
    }
    else
	{
		currHandler = sysIntTbl[intNum];	/* multiple int. handler case */
		while (currHandler->next != NULL)
		{
				currHandler = currHandler->next;
		}
		currHandler->next = newHandler;
	}

    return (OK);
}



/******************************************************************************
*
* sysVmeIntDisconnect - disconnect an interrupt handler for a VME interrupt vector
*
* This function disconnects an interrupt handler for a given VME interrupt vector
* from the system vector table
* It is the reverse function to sysVmeIntConnect().
*
* RETURNS: OK/ERROR.
*/

STATUS
sysVmeIntDisconnect
    (
     VOIDFUNCPTR * 	vector,		/* interrupt vector to attach */
     VOIDFUNCPTR   	routine,	/* routine to be called */
     int	        parameter	/* parameter to be passed to routine */
    )
{
    INT_HANDLER_DESC *	nextHandler;
    INT_HANDLER_DESC *	currHandler;
	int					intNum;


	intNum = IVEC_TO_INUM(vector);

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

    /* remove the handler */

    if (sysIntTbl[intNum] == NULL)
		return (ERROR);

	nextHandler = currHandler = sysIntTbl[intNum];

	/* handle case where handler is the first entry */
	if (currHandler->vec == routine)
	{
		sysIntTbl[intNum] = currHandler->next;
		free (currHandler);
		return (OK);
	}

	/* find the list entry to be removed */
	while (currHandler->next != NULL)
	{
		nextHandler = currHandler->next;
		if (nextHandler->vec == routine)
		{
			break;
		}
		currHandler = nextHandler;
	}

	if (currHandler->next != NULL)
	{
		currHandler->next = nextHandler->next;
		free(nextHandler);
		return (OK);
	}

	/* not entry found when we get here */
    return (ERROR);
}



/******************************************************************************
*
* sysVmeIntDisconnectAll - clear the entire linked-list of ISRs for a VME interrupt vector
*
*
* RETURNS: OK/ERROR.
*/

STATUS
sysVmeIntDisconnectAll
    (
     VOIDFUNCPTR * 	vector		/* interrupt vector to detach */
    )
{
    INT_HANDLER_DESC *	currHandler;
    INT_HANDLER_DESC *	previousHandler;
	int					intNum;


	intNum = IVEC_TO_INUM(vector);

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

	currHandler = sysIntTbl[intNum];	/* multiple int. handler case */
    sysIntTbl[intNum] = NULL;
	while (currHandler != NULL)
		{
			previousHandler = currHandler;
			currHandler = currHandler->next;
			free(previousHandler);
		}

    return (OK);
}



/******************************************************************************
*
* sysBusRmwEnable - enable Read-Modify-Write (RMW) cycle on VMEbus
*
* NOTE: These parameters are written to the SCG as Big-Endian values.
* This is probably a bug because all other registers in the
* Universe are Little-Endian.
*
* RETURNS: N/A
*/

void sysBusRmwEnable
    (
    UINT swapCompareEnable,	/* Enabled bits involved in compare and swap */
    UINT compareData,		/* Data to compare with read portion of RMW */
    UINT swapData,			/* Data to write during write portion of RMW */
    char *  rmwAddress		/* RMW address */
    )
{
    *UNIVERSE_SCYC_EN = swapCompareEnable;
	*UNIVERSE_SCYC_CMP = compareData;
	*UNIVERSE_SCYC_SWP = swapData;

	/* Convert RMW address to a PCI address */

	UNIV_OUT_LONG (UNIVERSE_SCYC_ADDR,  (UINT)CPU2PCI_PCI_MEM_ADRS((UINT)rmwAddress));

	/* Configure Special Cycle Generator to generate a RMW cycle */

	UNIV_OUT_LONG (UNIVERSE_SCYC_CTL, SCYC_CTL_RMW);
}


/******************************************************************************
*
* sysBusRmwDisable - Disable Read-Modify-Write (RMW) cycle on the VMEbus.
*
* Disable Read-Modify-Write (RMW) cycle on the VMEbus.
*
* RETURNS: N/A
*/

void sysBusRmwDisable ()
{
	UNIV_OUT_LONG (UNIVERSE_SCYC_CTL, SCYC_CTL_DISABLE);
}


#ifdef	INCLUDE_VME_DMA /* DMA not yet implemented! */

/*
 * The following routines whose names start with "sysVmeDma"
 * comprise the DMA driver.
 */

/******************************************************************************
*
* sysVmeDmaInit - Inititalize the Universe's DMA engine
*
* This routine will initialize the Universe's DMA engine.  This involves
* using the following #defines listed in config.h as defaults to configure
* the appropriate fields in the DMA's DCTL and DGCS registers.
* .CS
*	VME_DMA_XFER_TYPE
*	VME_DMA_ADDR_SPACE
*	VME_DMA_DATA_TYPE
*	VME_DMA_USER_TYPE
* .CE
* The DMA engine will also be permanently configured so that the PCI Bus
* will perform 64-bit transactions.
*
* NOTE: This low-level driver is strictly non-sharable; however,
* it contains no guards to prevent multiple tasks from calling
* it simultaneously.  It assumes that the application layer will
* provide atomic access to this driver through the use of a
* semaphore or similar guards.
*
* In addition, the internals of this driver only operates in a
* non-interrupt (polled) mode; it uses a busy loop to sample the
* DMA transfer status.  This implies that the calling task will
* wait until the DMA transfer has terminated.  As a precaution,
* it is recommended by the Tundra User's Manual that the calling
* task set up a background timer to prevent an infinite wait
* caused by a system problem.  Also, tasks transferring large
* blocks of data should lower their priority level to allow other
* tasks to run, and tasks transferring small blocks of data
* should use bcopy() instead of calling this driver.
*
* RETURNS: OK
*/

STATUS sysVmeDmaInit
    (
    void
    )
{
	/* Configure the DMA Transfer Control register */

	UNIV_OUT_LONG (UNIVERSE_DCTL, (VME_DMA_XFER_TYPE  |
		VME_DMA_ADDR_SPACE |
		VME_DMA_DATA_TYPE  |
		VME_DMA_USER_TYPE  |
		DCTL_LD64EN ));

	sysVmeDmaReady = TRUE;
	return (OK);
}

/******************************************************************************
*
* sysVmeDmaCnfgGet - Get DMA transfer configuration parameters
*
* This routine will get the DMA transfer parameters.
*
* RETURNS: OK or ERROR
*/

STATUS sysVmeDmaCnfgGet
    (
    UINT32 *xferType,	/* output: Ptr to VMEbus data transfer type	*/
			/* Valid range:					*/
			/* (DCTL_VDW_8  | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_16 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_32 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_64 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_32 | DCTL_VCT_BLK)	       ** BLT  	*/
			/* (DCTL_VDW_64 | DCTL_VCT_BLK)	       ** MBLT 	*/
    UINT32 *addrSpace,	/* output: Ptr to VMEbus Address Space type	*/
			/* Valid range:					*/
			/* DCTL_VAS_A16					*/
			/* DCTL_VAS_A24					*/
			/* DCTL_VAS_A32					*/
    UINT32 *dataType,	/* output: Ptr to Program/Data AM Code		*/
			/* Valid range:					*/
			/* DCTL_PGM_DATA				*/
			/* DCTL_PGM_PRGM				*/
    UINT32 *userType	/* output: Ptr to Supervisor/User AM Code	*/
			/* Valid range:					*/
			/* DCTL_SUPER_USER				*/
			/* DCTL_SUPER_SUP				*/
    )
{
    UINT32  dctlReg;

	if ( !sysVmeDmaReady )
	{
		return (ERROR);
	}

	/* Get contents of DMA Transfer Control register */

	UNIV_IN_LONG (UNIVERSE_DCTL, &dctlReg);

	/* Extract the VMEbus data transfer type */

	*xferType = (dctlReg & (DCTL_VDW_MSK | DCTL_VCT_MSK));

	/* Extract the VMEbus Address Space type */

	*addrSpace = (dctlReg & DCTL_VAS_MSK);

	/* Extract the Program/Data AM Code */

	*dataType = (dctlReg & DCTL_PGM_MSK);

	/* Extract the Supervisor/User AM Code */

	*userType = (dctlReg & DCTL_SUPER_MSK);

	return (OK);
}

/******************************************************************************
*
* sysVmeDmaCnfgSet -  Set DMA transfer configuration parameters
*
* This routine will configure the DMA transfer parameters.
*
* RETURNS: OK or ERROR
*/

STATUS sysVmeDmaCnfgSet
    (
    UINT32 xferType,	/* input: VMEbus data transfer type		*/
			/* Valid range:					*/
			/* (DCTL_VDW_8  | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_16 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_32 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_64 | DCTL_VCT_SINGLE)		*/
			/* (DCTL_VDW_32 | DCTL_VCT_BLK)	        ** BLT  */
			/* (DCTL_VDW_64 | DCTL_VCT_BLK)	        ** MBLT */
    UINT32 addrSpace,	/* input: VMEbus Address Space type		*/
			/* Valid range:					*/
			/* DCTL_VAS_A16					*/
			/* DCTL_VAS_A24					*/
			/* DCTL_VAS_A32					*/
    UINT32 dataType,	/* imput: Ptr to Program/Data AM Code		*/
			/* Valid range:					*/
			/* DCTL_PGM_DATA				*/
			/* DCTL_PGM_PRGM				*/
    UINT32 userType	/* input: Supervisor/User AM Code		*/
			/* Valid range:					*/
			/* DCTL_SUPER_USER				*/
			/* DCTL_SUPER_SUP				*/
    )
{
    UINT32  dctlReg;

	if ( !sysVmeDmaReady )
	{
		return (ERROR);
	}

	/* Get contents of DMA Transfer Control register */

	UNIV_IN_LONG (UNIVERSE_DCTL, &dctlReg);

	/* Setup the VMEbus data transfer type */

	dctlReg &= ~(DCTL_VDW_MSK | DCTL_VCT_MSK);
	dctlReg |= (xferType & (DCTL_VDW_MSK | DCTL_VCT_MSK));

	/* Setup the VMEbus Address Space type */

	dctlReg &= ~(DCTL_VAS_MSK);
	dctlReg |= (addrSpace & DCTL_VAS_MSK);

	/* Setup the Program/Data AM Code */

	dctlReg &= ~(DCTL_PGM_MSK);
	dctlReg |= (dataType & DCTL_PGM_MSK);

	/* Setup the Supervisor/User AM Code */

	dctlReg &= ~(DCTL_SUPER_MSK);
	dctlReg |= (userType & DCTL_SUPER_MSK);

	/* Update contents of DMA Transfer Control register */

	UNIV_OUT_LONG (UNIVERSE_DCTL, dctlReg);

	return (OK);
}

/******************************************************************************
*
* sysVmeDmaStatusGet - Get DMA transfer Status
*
* This routine will return the status of the DMA transfer.
*
* RETURNS: OK or ERROR
*/

STATUS sysVmeDmaStatusGet
    (
    UINT32  *transferStatus	/* State of DMA transfer defined as	*/
				/* DGCS register status bits		*/
				/* DGCS_ACT  | DGCS_STOP | DGCS_HALT |	*/
				/* DGCS_DONE | DGCS_LERR | DGCS_VERR |	*/
				/* DGCS_P_ERR				*/
    )
{
    UINT32  dgcsReg;

	if ( !sysVmeDmaReady )
	{
		return (ERROR);
	}

	/* Get contents of DMA General Control/Status register */

	UNIV_IN_LONG (UNIVERSE_DGCS, &dgcsReg);

	/* Return the status of the DMA transfer */

	*transferStatus = (dgcsReg & DGCS_STATUS_MSK);

	return (OK);
}


/******************************************************************************
*
* sysVmeDmaL2VCopy - Copy data from local memory to VMEbus memory
*
* This routine copies data from local memory to VMEbus memory using
* the Universe's DMA engine.
*
* NOTE: This routine assumes that the DMA transfer configuration
* parameters have been previously set up either at driver init time
* (sysVmeDmaInit()) with the default parameters in config.h or
* dynamically by calling sysVmeDmaCnfgSet().
*
* .CS
* RETURNS: OK,
*	   ERROR - driver not initialized or invalid argument,
*	   DGCS_LERR - PCI Bus Error,
*	   DGCS_VERR - VMEbus Error,
*          or
*	   DGCS_P_ERR - Protocol Error
* .CE
*/

STATUS sysVmeDmaL2VCopy
    (
    UCHAR  *localAddr,		/* Local Address as seen by the CPU */
    UCHAR  *localVmeAddr,	/* VMEbus Address as seen by the CPU */
    UINT32  nbytes
    )
{
    return (sysVmeDmaCopy (localAddr,
		localVmeAddr,
		nbytes,
		(UINT32)DCTL_L2V_PCI_VME));
}


/******************************************************************************
*
* sysVmeDmaV2LCopy - Copy data from VMEbus memory to local memory
*
* This routine copies data from VMEbus memory to local memory using
* the Universe's DMA engine.
*
* NOTE: This routine assumes that the DMA transfer configuration
* parameters have been previously set up either at driver init time
* (sysVmeDmaInit()) with the default parameters in config.h or
* dynamically by calling sysVmeDmaCnfgSet().
*
* .CS
* RETURNS: OK,
*	   ERROR - driver not initialized or invalid argument,
*	   DGCS_LERR - PCI Bus Error,
*	   DGCS_VERR - VMEbus Error,
*          or
*	   DGCS_P_ERR - Protocol Error
* .CE
*/

STATUS sysVmeDmaV2LCopy
    (
    UCHAR  *localVmeAddr,	/* VMEbus Address as seen by the CPU */
    UCHAR  *localAddr,		/* Local Address as seen by the CPU */
    UINT32  nbytes
    )
{
    return (sysVmeDmaCopy (localAddr,
		localVmeAddr,
		nbytes,
		(UINT32)DCTL_L2V_VME_PCI));
}


#ifdef  VME_DMA_DEBUG
/*-----------------------------------------------------------+univDmaIntHandler+--*/
/**
      univDmaIntHandler():  interrupt handler for VME DMA

      ARGUMENTS:
        IN  sysVmeDmaSem - semaphore for VME DMA
        OUT

      RETURN:

      DESCRIPTION:
        Gives the semaphore used for VME DMA.

      ATTENTION:

      NOTES:
        Only used for debug purposes. In normal operation the semGive() routine can
        be used directly as VME DMA interrupt handler.
*/
/*-----------------------------------------------------------(univDmaIntHandler)--*/
LOCAL void univDmaIntHandler(SEM_ID sysVmeDmaSem)
{
		univDmaIntCount++;
        semGive(sysVmeDmaSem);
}
#endif /* VME_DMA_DEBUG */


/******************************************************************************
*
* sysVmeDmaCopy - Copy data between local and VMEbus memory
*
* This routine copies data between local and VMEbus memory using
* the Universe's DMA engine.  In addition to the parameters passed in,
* this routine uses the macros VME_DMA_MAX_BURST and VME_DMA_MIN_TIME_OFF_BUS
* defined in config.h to set up VON and VOFF, respectfully.
*
* NOTE: This routine assumes that the DMA transfer configuration
* parameters have been previously set up either at driver init time
* (sysVmeDmaInit()) with the default parameters in config.h or
* dynamically by calling sysVmeDmaCnfgSet().
*
* The sampling rate to determine the DMA transfer status
* will directly affect the throughput of the DMA transfer.
*
* .CS
* RETURNS: OK,
*	   ERROR - driver not initialized or invalid argument,
*	   DGCS_LERR - PCI Bus Error,
*	   DGCS_VERR - VMEbus Error,
*          or
*	   DGCS_P_ERR - Protocol Error
* .CE
*/

LOCAL STATUS sysVmeDmaCopy
    (
    UCHAR  *localAddr,		/* Local Address as seen by the CPU */
    UCHAR  *localVmeAddr,	/* VMEbus Address as seen by the CPU */
    UINT32  nbytes,		/* number of bytes to copy: */
				/*	max = DTBC_VALID_BITS_MASK */
    UINT32  direction	/* Copy direction: DCTL_L2V_PCI_VME, DCTL_L2V_VME_PCI */
    )
{
    UCHAR  *pciAddr;
	UCHAR  *vmeAddr;
	UINT32  dctlReg,
	        dgcsReg;

#ifdef VME_DMA_INTERRUPT
    SEM_ID  sysVmeDmaSem;	/* Semaphore for use with VME DMA controller */
	UINT32  tmpReg;
#endif /* VME_DMA_INTERRUPT */

	if ( !sysVmeDmaReady )
	{
		return (ERROR);
	}

	/* Check addresses for 8-byte alignment to each other */

	if ( ((UINT32)localAddr & 0x7) != ((UINT32)localVmeAddr & 0x7) )
	{
		return (ERROR);
	}

	/* Check for too large a transfer byte count */

	if ( nbytes > DTBC_VALID_BITS_MASK )
	{
		return (ERROR);
	}

#ifdef VME_DMA_INTERRUPT
    /* Create DMA semaphore */
    sysVmeDmaSem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
    if (sysVmeDmaSem == (SEM_ID)0)
    {
        return (ERROR);
    }
    /* Connect DMA handler */
#ifdef  VME_DMA_DEBUG
    if (sysUnivIntConnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)univDmaIntHandler, (UINT)sysVmeDmaSem) != OK)
#else
    if (sysUnivIntConnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)semGive, (UINT)sysVmeDmaSem) != OK)
#endif /* VME_DMA_DEBUG */
    {
        semDelete(sysVmeDmaSem);
        return (ERROR);
    }
#endif /* VME_DMA_INTERRUPT */


#ifdef VME_DMA_INTERRUPT
    /* Clear DMA Interrupt Status */
	UNIV_IN_LONG (UNIVERSE_LINT_STAT, &tmpReg);
    tmpReg |= LINT_STAT_DMA;
	UNIV_OUT_LONG (UNIVERSE_LINT_STAT, tmpReg);
#endif /* VME_DMA_INTERRUPT */


	/*
	* Clearing the chain bit and setting the GO bit at the same time
	* does not work.  So, clearing the chain bit and setting up VON
	* and VOFF is done before setting up the source and destination
	* register as called out in the Universe manual.
	*/

	UNIV_IN_LONG (UNIVERSE_DGCS, &dgcsReg);
	dgcsReg &= ~(DGCS_CHAIN_MSK | DGCS_VON_MSK | DGCS_VOFF_MSK);
#ifdef VME_DMA_INTERRUPT
	dgcsReg |= (VME_DMA_MAX_BURST | VME_DMA_MIN_TIME_OFF_BUS |
                DGCS_INT_STOP | DGCS_INT_HALT | DGCS_INT_DONE |
                DGCS_INT_LERR | DGCS_INT_VERR | DGCS_INT_P_ERR);
#else
	dgcsReg |= (VME_DMA_MAX_BURST | VME_DMA_MIN_TIME_OFF_BUS);
#endif /* VME_DMA_INTERRUPT */
	UNIV_OUT_LONG (UNIVERSE_DGCS, dgcsReg);

	/* Setup transfer direction */

	UNIV_IN_LONG (UNIVERSE_DCTL, &dctlReg);
	dctlReg &= ~(DCTL_L2V_MSK);
	dctlReg |= (direction & DCTL_L2V_MSK);
	UNIV_OUT_LONG (UNIVERSE_DCTL, dctlReg);

	/*
	* Convert the VME address as seen by the CPU to a
	* VMEbus address as seen on the VMEbus, and store
	* it in the DMA engine's VMEbus address register.
	*/

	switch ( dctlReg & DCTL_VAS_MSK )
	{
	case DCTL_VAS_A32:
		vmeAddr = (UCHAR *)((UINT32)localVmeAddr -
		(UINT32)VME_A32_MSTR_LOCAL +
		(UINT32)VME_A32_MSTR_BUS);
		UNIV_OUT_LONG (UNIVERSE_DVA, vmeAddr);
		break;

	case DCTL_VAS_A24:
		vmeAddr = (UCHAR *)((UINT32)localVmeAddr -
		(UINT32)VME_A24_MSTR_LOCAL +
		(UINT32)VME_A24_MSTR_BUS);
		UNIV_OUT_LONG (UNIVERSE_DVA, vmeAddr);
		break;

	case DCTL_VAS_A16:
		vmeAddr = (UCHAR *)((UINT32)localVmeAddr -
		(UINT32)VME_A16_MSTR_LOCAL +
		(UINT32)VME_A16_MSTR_BUS);
		UNIV_OUT_LONG (UNIVERSE_DVA, vmeAddr);
		break;

	default:
#ifdef VME_DMA_INTERRUPT

        /* Disconnect VME DMA interrupt handler */
#ifdef  VME_DMA_DEBUG
        sysUnivIntDisconnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)univDmaIntHandler, (UINT)sysVmeDmaSem);
#else
        sysUnivIntDisconnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)semGive, (UINT)sysVmeDmaSem);
#endif /* VME_DMA_DEBUG */

        /* Delete VME DMA semaphore */
        semDelete(sysVmeDmaSem);
#endif /* VME_DMA_INTERRUPT */
		return (ERROR);
	}

	/*
	* Convert the local address to a PCI address as seen
	* on the PCI Bus, and store it in the DMA engine's
	* PCI Bus address register.
	*/

	pciAddr = (UCHAR *)((UINT32)localAddr + (UINT32)PCI2DRAM_BASE_ADRS);
	UNIV_OUT_LONG (UNIVERSE_DLA, pciAddr);

	/* Configure DMA engine's byte count */

	UNIV_OUT_LONG (UNIVERSE_DTBC, nbytes);

#ifdef VME_DMA_INTERRUPT
    /* Enable DMA PCI interrupt */
    sysUnivIntEnable(UNIVERSE_DMA_INT);
#endif /* VME_DMA_INTERRUPT */

	/* Start transfer by clearing status and setting GO bit */

	dgcsReg &= ~(DGCS_STATUS_MSK);
	dgcsReg |= DGCS_GO;
	UNIV_OUT_LONG (UNIVERSE_DGCS, dgcsReg);

	/* Wait for transfer to terminate */

#ifdef VME_DMA_INTERRUPT
    semTake(sysVmeDmaSem, WAIT_FOREVER);
#else
	UNIV_IN_LONG (UNIVERSE_DGCS, &dgcsReg);
	while ( dgcsReg & DGCS_ACT )
	{
		sysUsDelay (25);
		UNIV_IN_LONG (UNIVERSE_DGCS, &dgcsReg);
	}
#endif /* VME_DMA_INTERRUPT */

#ifdef VME_DMA_INTERRUPT
    /* Disable DMA PCI interrupt */
    sysUnivIntDisable(UNIVERSE_DMA_INT);

#ifdef  VME_DMA_DEBUG
    sysUnivIntDisconnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)univDmaIntHandler, (UINT)sysVmeDmaSem);
#else
    sysUnivIntDisconnect (UNIVERSE_DMA_INT, (VOIDFUNCPTR)semGive, (UINT)sysVmeDmaSem);
#endif /* VME_DMA_DEBUG */

    /* Delete VME DMA semaphore */
    semDelete(sysVmeDmaSem);
#endif /* VME_DMA_INTERRUPT */

	if ( dgcsReg & DGCS_DONE )
	{
		return (OK);
	}
	else
	{
		return (dgcsReg & (DGCS_LERR | DGCS_VERR | DGCS_P_ERR));
	}
}


#endif	/* INCLUDE_VME_DMA */



/******************************************************************************
*
* sysVmeMasterMap - Display the VME Master mappings
*
* This routine displays the current address mapping from the CPU, through the
* PCI, and to the VME address corresponding to each virtual CPU/PCI address.
*
* RETURNS: N/A
*
* SEE ALSO: sysVmeSlaveMap()
*
*/
void sysVmeMasterMap (void)
{
	int i;

	static struct
	{
		UINT  vmeSpace;        /* VME Address Space */
		UINT  pciBase;	       /* PCI Base Address */
		UINT  pciBound;        /* PCI Bound Address */
		UINT  vmeBase;         /* VME Base Address */
		UINT  vmeBound;        /* VME Bound Address */
		UINT  cpuBase;         /* CPU Base Address */
		UINT  cpuBound;	       /* CPU Bound Address */
		UINT  uCtrl;           /* Image Control */
		UINT  uTo;             /* Translation Offset */

	} vmeImage;

	static struct IMG_CTRL
	{
		UINT ctrlReg;
		UINT baseReg;
		UINT boundReg;
		UINT toReg;
	} *pImgCtrl;

	static UINT vmeImgCtrlRegMap[8] =
	{
        UNIVERSE_LSI0_CTL_OFF,
        UNIVERSE_LSI1_CTL_OFF,
        UNIVERSE_LSI2_CTL_OFF,
        UNIVERSE_LSI3_CTL_OFF,
        UNIVERSE_LSI4_CTL_OFF,
        UNIVERSE_LSI5_CTL_OFF,
        UNIVERSE_LSI6_CTL_OFF,
        UNIVERSE_LSI7_CTL_OFF
	};


	if (sysVmePresent == FALSE)
	{
		printf ("sysVmeMasterMap : VME interface not available\n");
		return;
	}

	for (i = 0; i <= 7; i++)
	{
        pImgCtrl = (struct IMG_CTRL*)UNIVERSE_ADRS(vmeImgCtrlRegMap[i]);

		vmeImage.uCtrl    = PSWAP (pImgCtrl->ctrlReg);

		/* display the VME mappings only the window is enable */
		if ((vmeImage.uCtrl & 0x80000000) == 0x80000000)
		{
		    vmeImage.uTo      = PSWAP (pImgCtrl->toReg);
            vmeImage.pciBase  = PSWAP (pImgCtrl->baseReg);
		    vmeImage.cpuBase  = PCI2CPU_PCI_MEM_ADRS (vmeImage.pciBase);
            vmeImage.pciBound = PSWAP (pImgCtrl->boundReg);
		    vmeImage.cpuBound = vmeImage.cpuBase + (vmeImage.pciBound - vmeImage.pciBase);
            vmeImage.vmeBase  = vmeImage.pciBase + vmeImage.uTo;

			if ((vmeImage.uCtrl & 0xf0000) == 0x00000)
            {
                vmeImage.vmeSpace  = 16;
			    vmeImage.vmeBase &= 0xffff;
            }
			else if ((vmeImage.uCtrl & 0xf0000) == 0x10000)
            {
				vmeImage.vmeSpace  = 24;
			    vmeImage.vmeBase &= 0xffffff;
            }
			else if ((vmeImage.uCtrl & 0xf0000) == 0x20000)
            {
				vmeImage.vmeSpace = 32;
            }

            vmeImage.vmeBound = vmeImage.vmeBase + (vmeImage.pciBound - vmeImage.pciBase);

			printf ("\n*VME MASTER MAPPING OF VME MEMORY FOR WINDOW %d FROM A%2d \n\n", i, vmeImage.vmeSpace );
			printf ("LSI%d_CTL:  0x%08x\n",   i, vmeImage.uCtrl);
			printf ("LSI%d_BS :  0x%08x\n",   i, vmeImage.pciBase);
			printf ("LSI%d_BD :  0x%08x\n",   i, vmeImage.pciBound);
			printf ("LSI%d_TO :  0x%08x\n\n", i, vmeImage.uTo);
			printf ("CPU A%2d BASE = 0x%08x\n", vmeImage.vmeSpace, vmeImage.cpuBase);
			printf ("|\n");
			printf ("|                    PCI A%2d BASE  = 0x%08x\n", vmeImage.vmeSpace, vmeImage.pciBase);
			printf ("|                    |\n");
			printf ("|                    |                    VME A%2d BASE  = 0x%08x\n", vmeImage.vmeSpace, vmeImage.vmeBase);
			printf ("|                    |                    |\n");
			printf ("v                    v                    v\n");
			printf (" -------------        -------------        -------------        \n");
			printf ("|             |      |             |      |             |\n");
			printf ("|             |      |             |      |             |\n");
			printf ("|    CPU      | PCI  |   Universe  | VME  |    VME      |\n");
			printf ("| A%2d Address | ===> | A%2d Address | ===> | A%2d Address |\n", vmeImage.vmeSpace, vmeImage.vmeSpace, vmeImage.vmeSpace);
			printf ("|    Range    |      |    Range    |      |    Range    |\n");
			printf ("|             |      |             |      |             |\n");
			printf (" -------------        -------------        -------------        \n");
			printf ("^                    ^                    ^\n");
			printf ("|                    |                    |\n");
			printf ("|                    |                    VME A%2d BOUND = 0x%08x\n", vmeImage.vmeSpace, vmeImage.vmeBound);
			printf ("|                    |\n");
			printf ("|                    PCI A%2d BOUND = 0x%08x\n",vmeImage.vmeSpace, vmeImage.pciBound);
			printf ("|\n");
			printf ("CPU A%2d BOUND = 0x%08x\n", vmeImage.vmeSpace, vmeImage.cpuBound);
			printf ("\n");
		}
	}
}

/******************************************************************************
*
* sysVmeSlaveMap - Display the VME Slave mappings to CPU
*
* This routine displays the current address mapping from the VME, through the
* PCI, and to the CPU address corresponding to each virtual VME\PCI address.
*
* RETURNS: N\A
*
* SEE ALSO: sysVmeMasterMap()
*/
void sysVmeSlaveMap (void)
{
	int i;

	static struct
	{
		UINT  vmeSpace;        /* VME Address Space */
		UINT  pciBase;	       /* PCI Base Address */
		UINT  pciBound;        /* PCI Bound Address */
		UINT  vmeBase;         /* VME Base Address */
		UINT  vmeBound;        /* VME Bound Address */
		UINT  cpuBase;         /* CPU Base Address */
		UINT  cpuBound;	       /* CPU Bound Address */
		UINT  uCtrl;           /* Image Control */
		UINT  uTo;             /* Translation Offset */

	} vmeImage;

	static struct IMG_CTRL
	{
		UINT ctrlReg;
		UINT baseReg;
		UINT boundReg;
		UINT toReg;
	} *pImgCtrl;

	static UINT vmeImgCtrlRegMap[8] =
	{
        UNIVERSE_VSI0_CTL_OFF,
        UNIVERSE_VSI1_CTL_OFF,
        UNIVERSE_VSI2_CTL_OFF,
        UNIVERSE_VSI3_CTL_OFF,
        UNIVERSE_VSI4_CTL_OFF,
        UNIVERSE_VSI5_CTL_OFF,
        UNIVERSE_VSI6_CTL_OFF,
        UNIVERSE_VSI7_CTL_OFF
	};

	if (sysVmePresent == FALSE)
	{
		printf ("sysVmeSlaveMap : VME interface not available\n");
		return;
	}

	for (i = 0; i <= 7; i++)
	{
        pImgCtrl = (struct IMG_CTRL*)UNIVERSE_ADRS(vmeImgCtrlRegMap[i]);

		vmeImage.uCtrl    = PSWAP (pImgCtrl->ctrlReg);

		/* display the VME mappings only the window is enable */
		if ((vmeImage.uCtrl & 0x80000000) == 0x80000000)
        {
		    vmeImage.uTo      = PSWAP (pImgCtrl->toReg);
            vmeImage.vmeBase  = PSWAP (pImgCtrl->baseReg);
            vmeImage.vmeBound = PSWAP (pImgCtrl->boundReg);

			if ((vmeImage.uCtrl & 0xf0000) == 0x00000)
            {
                vmeImage.vmeSpace  = 16;
            }
			else if ((vmeImage.uCtrl & 0xf0000) == 0x10000)
            {
				vmeImage.vmeSpace  = 24;
            }
			else if ((vmeImage.uCtrl & 0xf0000) == 0x20000)
            {
				vmeImage.vmeSpace = 32;
            }

            vmeImage.pciBase  = vmeImage.vmeBase + vmeImage.uTo;
		    vmeImage.cpuBase  = TRANSLATE (vmeImage.pciBase, PCI_SLV_MEM_BUS, LOCAL_MEM_LOCAL_ADRS);
            vmeImage.pciBound = vmeImage.pciBase + (vmeImage.vmeBound - vmeImage.vmeBase);
            vmeImage.cpuBound = vmeImage.cpuBase + (vmeImage.vmeBound - vmeImage.vmeBase);


			printf ("\n*VME SLAVE MAPPING OF LOCAL MEMORY FOR WINDOW %d FROM A%2d \n\n", i, vmeImage.vmeSpace);
			printf ("VSI%d_CTL:  0x%08x\n",   i, vmeImage.uCtrl);
			printf ("VSI%d_BS :  0x%08x\n",   i, vmeImage.vmeBase);
			printf ("VSI%d_BD :  0x%08x\n",   i, vmeImage.vmeBound);
			printf ("VSI%d_TO :  0x%08x\n\n", i, vmeImage.uTo);
			printf ("VME A%2d BASE = 0x%08x\n", vmeImage.vmeSpace, vmeImage.vmeBase);
			printf ("|\n");
			printf ("|                    PCI A%2d BASE  = 0x%08x\n", vmeImage.vmeSpace, vmeImage.pciBase);
			printf ("|                    |\n");
			printf ("|                    |                    CPU A%2d BASE  = 0x%08x\n", vmeImage.vmeSpace, vmeImage.cpuBase);
			printf ("|                    |                    |\n");
			printf ("v                    v                    v\n");
			printf (" -------------        -------------        -------------        \n");
			printf ("|             |      |             |      |             |\n");
			printf ("|    VME      | VME  |   Universe  | PCI  |    CPU      |\n");
			printf ("| A%2d Address | ===> | A%2d Address | ===> | A%2d Address |\n",vmeImage.vmeSpace, vmeImage.vmeSpace, vmeImage.vmeSpace);
			printf ("|    Range    |      |    Range    |      |    Range    |\n");
			printf ("|             |      |             |      |             |\n");
			printf (" -------------        -------------        -------------        \n");
			printf ("^                    ^                    ^\n");
			printf ("|                    |                    |\n");
			printf ("|                    |                    CPU A%2d BOUND = 0x%08x\n", vmeImage.vmeSpace, vmeImage.cpuBound);
			printf ("|                    |\n");
			printf ("|                    PCI A%2d BOUND = 0x%08x\n",vmeImage.vmeSpace,vmeImage.pciBound);
			printf ("|\n");
			printf ("VME A%2d BOUND = 0x%08x\n", vmeImage.vmeSpace, vmeImage.vmeBound);
			printf ("\n");
		}
	}
}


/******************************************************************************
*
* sysVmeGetBusTrapFlag - report a VMEbus bus error (and clear the flag)
*
* RETURNS: TRUE if a VMEbus bus error has occured, FALSE else
*
* NOTE: The corresponding flag is always cleared
*/
BOOL
sysVmeGetBusTrapFlag()
{
	ULONG csr;


	csr = PSWAP(*UNIVERSE_PCI_CSR);
	/* Do not inadvertantly clear bits 31-28 by writing a '1' back */
	*UNIVERSE_PCI_CSR = PSWAP(csr & 0x0fffffff);

	return (csr & PCI_CSR_S_TA ? TRUE : FALSE);
}


/******************************************************************************
*
* sysVmeLock - locks the VMEbus
*
* RETURNS: N\A
*
* NOTE: The routine waits actively until the VMEbus is locked.
*/
void
sysVmeLock(void)
{
	*UNIVERSE_MAST_CTL = *UNIVERSE_MAST_CTL | PSWAP(MAST_CTL_VOWN);
	while (!(*UNIVERSE_MAST_CTL & PSWAP(MAST_CTL_VOWN_ACK)))
		;
}



/******************************************************************************
*
* sysVmeUnlock - unlocks the VMEbus
*
* RETURNS: N\A
*
*/
void
sysVmeUnlock(void)
{
	*UNIVERSE_MAST_CTL = PSWAP(PSWAP(*UNIVERSE_MAST_CTL) & ~MAST_CTL_VOWN);
	while (*UNIVERSE_MAST_CTL & PSWAP(MAST_CTL_VOWN_ACK))
		;
}

/******************************************************************************
*
* sysSpuriousVmeIntConnect - connect handler for spurious VME interrupts
*
* This function connects an interrupt handler for spurious 
* interrupts. It is done by setting the _func_spuriousVmeIntHook.
* 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 sysSpuriousVmeIntConnect
    (
    VOIDFUNCPTR routine,    /* routine called upon spurious or un-initialised */
    void *arg               /* argument with which to call routine      */
    )
{
    _func_spuriousVmeIntHook = routine;
    spuriousVmeIntHandlerArg = arg;

    return OK;
}

/******************************************************************************
*
* sysUninitVmeIntConnect - connect handler for uninitialised VME interrupts
*
* This function connects an interrupt handler for un-initialised
* interrupts. It is done by setting the _func_uninitVmeIntHook.
* 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 sysUninitVmeIntConnect
    (
    VOIDFUNCPTR routine,    /* routine called upon un-initialised IRQs */
    void *arg               /* argument with which to call routine      */
    )
{
    _func_uninitVmeIntHook = routine;
    uninitVmeIntHandlerArg = arg;

    return OK;
}