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;
}