forked from Imagelibrary/rtems
2507 lines
62 KiB
C
2507 lines
62 KiB
C
/* Driver for the Tundra Universe II pci-vme bridge */
|
|
|
|
/*
|
|
* Authorship
|
|
* ----------
|
|
* This software was created by
|
|
* Till Straumann <strauman@slac.stanford.edu>, 2000-2007,
|
|
* Stanford Linear Accelerator Center, Stanford University.
|
|
*
|
|
* Acknowledgement of sponsorship
|
|
* ------------------------------
|
|
* This software was produced by
|
|
* the Stanford Linear Accelerator Center, Stanford University,
|
|
* under Contract DE-AC03-76SFO0515 with the Department of Energy.
|
|
*
|
|
* Government disclaimer of liability
|
|
* ----------------------------------
|
|
* Neither the United States nor the United States Department of Energy,
|
|
* nor any of their employees, makes any warranty, express or implied, or
|
|
* assumes any legal liability or responsibility for the accuracy,
|
|
* completeness, or usefulness of any data, apparatus, product, or process
|
|
* disclosed, or represents that its use would not infringe privately owned
|
|
* rights.
|
|
*
|
|
* Stanford disclaimer of liability
|
|
* --------------------------------
|
|
* Stanford University makes no representations or warranties, express or
|
|
* implied, nor assumes any liability for the use of this software.
|
|
*
|
|
* Stanford disclaimer of copyright
|
|
* --------------------------------
|
|
* Stanford University, owner of the copyright, hereby disclaims its
|
|
* copyright and all other rights in this software. Hence, anyone may
|
|
* freely use it for any purpose without restriction.
|
|
*
|
|
* Maintenance of notices
|
|
* ----------------------
|
|
* In the interest of clarity regarding the origin and status of this
|
|
* SLAC software, this and all the preceding Stanford University notices
|
|
* are to remain affixed to any copy or derivative of this software made
|
|
* or distributed by the recipient and are to be affixed to any copy of
|
|
* software made or distributed by the recipient that contains a copy or
|
|
* derivative of this software.
|
|
*
|
|
* ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
|
|
#if defined(__rtems__)
|
|
#ifndef __INSIDE_RTEMS_BSP__
|
|
#define __INSIDE_RTEMS_BSP__
|
|
#endif
|
|
#endif
|
|
|
|
#include <bsp/vmeUniverse.h>
|
|
#include <bsp/vmeUniverseDMA.h>
|
|
|
|
#define UNIV_NUM_MPORTS 8 /* number of master ports */
|
|
#define UNIV_NUM_SPORTS 8 /* number of slave ports */
|
|
|
|
#define PCI_VENDOR_TUNDRA 0x10e3
|
|
#define PCI_DEVICE_UNIVERSEII 0
|
|
#define PCI_UNIVERSE_BASE0 0x10
|
|
#define PCI_UNIVERSE_BASE1 0x14
|
|
|
|
#define UNIV_REGOFF_PCITGT0_CTRL 0x100
|
|
#define UNIV_REGOFF_PCITGT4_CTRL 0x1a0
|
|
#define UNIV_REGOFF_VMESLV0_CTRL 0xf00
|
|
#define UNIV_REGOFF_VMESLV4_CTRL 0xf90
|
|
|
|
#define UNIV_CTL_VAS16 (0x00000000)
|
|
#define UNIV_CTL_VAS24 (0x00010000)
|
|
#define UNIV_CTL_VAS32 (0x00020000)
|
|
#define UNIV_MCTL_VASCSR (0x00050000)
|
|
#define UNIV_CTL_VAS (0x00070000)
|
|
|
|
#define UNIV_MCTL_EN (0x80000000)
|
|
#define UNIV_MCTL_PWEN (0x40000000)
|
|
#define UNIV_MCTL_PGM (0x00004000)
|
|
#define UNIV_MCTL_VCT (0x00000100)
|
|
#define UNIV_MCTL_SUPER (0x00001000)
|
|
#define UNIV_MCTL_VDW16 (0x00400000)
|
|
#define UNIV_MCTL_VDW32 (0x00800000)
|
|
#define UNIV_MCTL_VDW64 (0x00c00000)
|
|
|
|
#define UNIV_MCTL_AM_MASK (UNIV_CTL_VAS | UNIV_MCTL_PGM | UNIV_MCTL_SUPER)
|
|
|
|
#define UNIV_SCTL_EN (0x80000000)
|
|
#define UNIV_SCTL_PWEN (0x40000000)
|
|
#define UNIV_SCTL_PREN (0x20000000)
|
|
#define UNIV_SCTL_PGM (0x00800000)
|
|
#define UNIV_SCTL_DAT (0x00400000)
|
|
#define UNIV_SCTL_SUPER (0x00200000)
|
|
#define UNIV_SCTL_USER (0x00100000)
|
|
|
|
#define UNIV_SCTL_AM_MASK (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
|
|
|
|
#ifdef __rtems__
|
|
|
|
#include <stdlib.h>
|
|
#include <rtems/bspIo.h> /* printk */
|
|
#include <rtems/error.h>
|
|
#include <rtems/pci.h>
|
|
#include <bsp.h>
|
|
#include <libcpu/byteorder.h>
|
|
|
|
/* allow the BSP to override the default routines */
|
|
#ifndef BSP_PCI_FIND_DEVICE
|
|
#define BSP_PCI_FIND_DEVICE pci_find_device
|
|
#endif
|
|
#ifndef BSP_PCI_CONFIG_IN_LONG
|
|
#define BSP_PCI_CONFIG_IN_LONG pci_read_config_dword
|
|
#endif
|
|
#ifndef BSP_PCI_CONFIG_IN_BYTE
|
|
#define BSP_PCI_CONFIG_IN_BYTE pci_read_config_byte
|
|
#endif
|
|
#ifndef BSP_PCI_CONFIG_IN_SHORT
|
|
#define BSP_PCI_CONFIG_IN_SHORT pci_read_config_word
|
|
#endif
|
|
#ifndef BSP_PCI_CONFIG_OUT_SHORT
|
|
#define BSP_PCI_CONFIG_OUT_SHORT pci_write_config_word
|
|
#endif
|
|
|
|
/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses.
|
|
* Should be defined by the BSP.
|
|
*/
|
|
typedef uint32_t pci_ulong;
|
|
|
|
#ifndef BSP_PCI2LOCAL_ADDR
|
|
#ifndef PCI_MEM_BASE
|
|
#define PCI_MEM_BASE 0
|
|
#endif
|
|
#define BSP_PCI2LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)
|
|
#endif
|
|
|
|
#ifndef BSP_LOCAL2PCI_ADDR
|
|
#ifndef PCI_DRAM_OFFSET
|
|
#define PCI_DRAM_OFFSET 0
|
|
#endif
|
|
#define BSP_LOCAL2PCI_ADDR(pciaddr) ((uint32_t)(pciaddr) + PCI_DRAM_OFFSET)
|
|
#endif
|
|
|
|
|
|
#elif defined(__vxworks)
|
|
typedef unsigned long pci_ulong;
|
|
#define BSP_PCI2LOCAL_ADDR(memaddr) (memaddr)
|
|
#define BSP_PCI_FIND_DEVICE pciFindDevice
|
|
#define BSP_PCI_CONFIG_IN_LONG pciConfigInLong
|
|
#define BSP_PCI_CONFIG_IN_BYTE pciConfigInByte
|
|
#else
|
|
#error "vmeUniverse not ported to this architecture yet"
|
|
#endif
|
|
|
|
#ifndef PCI_INTERRUPT_LINE
|
|
#define PCI_INTERRUPT_LINE 0x3c
|
|
#endif
|
|
|
|
volatile LERegister *vmeUniverse0BaseAddr=0;
|
|
int vmeUniverse0PciIrqLine=-1;
|
|
|
|
#ifdef __rtems__
|
|
int vmeUniverseRegPort = -1;
|
|
int vmeUniverseRegCSR = 0;
|
|
#endif
|
|
|
|
#define DFLT_BASE volatile LERegister *base = vmeUniverse0BaseAddr
|
|
|
|
#define CHECK_DFLT_BASE(base) \
|
|
do { \
|
|
/* get the universe base address */ \
|
|
if (!base) { \
|
|
if (vmeUniverseInit()) { \
|
|
uprintf(stderr,"unable to find the universe in pci config space\n"); \
|
|
return -1; \
|
|
} else { \
|
|
base = vmeUniverse0BaseAddr; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#if 0
|
|
/* public access functions */
|
|
volatile LERegister *
|
|
vmeUniverseBaseAddr(void)
|
|
{
|
|
if (!vmeUniverse0BaseAddr) vmeUniverseInit();
|
|
return vmeUniverse0BaseAddr;
|
|
}
|
|
|
|
int
|
|
vmeUniversePciIrqLine(void)
|
|
{
|
|
if (vmeUniverse0PciIrqLine<0) vmeUniverseInit();
|
|
return vmeUniverse0PciIrqLine;
|
|
}
|
|
#endif
|
|
|
|
static inline void
|
|
WRITE_LE(
|
|
unsigned long val,
|
|
volatile LERegister *adrs,
|
|
unsigned long off)
|
|
{
|
|
#if (__LITTLE_ENDIAN__ == 1)
|
|
*(volatile unsigned long*)(((unsigned long)adrs)+off)=val;
|
|
#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
|
|
/* offset is in bytes and MUST not end up in r0 */
|
|
__asm__ __volatile__("stwbrx %1, %0, %2" :: "b"(off),"r"(val),"r"(adrs));
|
|
#elif defined(__rtems__)
|
|
st_le32((volatile uint32_t *)(((uint32_t)adrs)+off), val);
|
|
#else
|
|
#error "little endian register writing not implemented"
|
|
#endif
|
|
}
|
|
|
|
#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)
|
|
#define SYNC __asm__ __volatile__("sync")
|
|
#else
|
|
#define SYNC
|
|
#warning "SYNC instruction unknown for this architecture"
|
|
#endif
|
|
|
|
/* registers should be mapped to guarded, non-cached memory; hence
|
|
* subsequent stores are ordered. eieio is only needed to enforce
|
|
* ordering of loads with respect to stores.
|
|
*/
|
|
#define EIEIO_REG
|
|
|
|
static inline unsigned long
|
|
READ_LE0(volatile LERegister *adrs)
|
|
{
|
|
#if (__LITTLE_ENDIAN__ == 1)
|
|
return *(volatile unsigned long *)adrs;
|
|
#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
|
|
register unsigned long rval;
|
|
__asm__ __volatile__("lwbrx %0, 0, %1":"=r"(rval):"r"(adrs));
|
|
return rval;
|
|
#elif defined(__rtems__)
|
|
return ld_le32((volatile uint32_t*)adrs);
|
|
#else
|
|
#error "little endian register reading not implemented"
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned long
|
|
READ_LE(volatile LERegister *adrs, unsigned long off)
|
|
{
|
|
#if (__LITTLE_ENDIAN__ == 1)
|
|
return *((volatile LERegister *)(((unsigned long)adrs)+off));
|
|
#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
|
|
register unsigned long rval;
|
|
/* offset is in bytes and MUST not end up in r0 */
|
|
__asm__ __volatile__("lwbrx %0, %2, %1"
|
|
: "=r"(rval)
|
|
: "r"(adrs), "b"(off));
|
|
#if 0
|
|
__asm__ __volatile__("eieio");
|
|
#endif
|
|
return rval;
|
|
#else
|
|
return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off));
|
|
#endif
|
|
}
|
|
|
|
#define PORT_UNALIGNED(addr,port) \
|
|
( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
|
|
|
|
|
|
#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
|
|
|
|
#if defined(__rtems__) && 0
|
|
static int
|
|
uprintk(char *fmt, va_list ap)
|
|
{
|
|
int rval;
|
|
extern int k_vsprintf(char *, char *, va_list);
|
|
/* during bsp init, there is no malloc and no stdio,
|
|
* hence we assemble the message on the stack and revert
|
|
* to printk
|
|
*/
|
|
char buf[200];
|
|
rval = k_vsprintf(buf,fmt,ap);
|
|
if (rval > sizeof(buf))
|
|
rtems_panic("vmeUniverse/uprintk: buffer overrun");
|
|
printk(buf);
|
|
return rval;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* private printing wrapper */
|
|
static void
|
|
uprintf(FILE *f, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
#ifdef __rtems__
|
|
if (!f || !_impure_ptr->__sdidinit) {
|
|
/* Might be called at an early stage when
|
|
* stdio is not yet initialized.
|
|
* There is no vprintk, hence we must assemble
|
|
* to a buffer.
|
|
*/
|
|
vprintk(fmt,ap);
|
|
} else
|
|
#endif
|
|
{
|
|
vfprintf(f,fmt,ap);
|
|
}
|
|
va_end(ap);
|
|
}
|
|
|
|
static int
|
|
vmeUniverseFindPciBase(
|
|
int instance,
|
|
volatile LERegister **pbase
|
|
)
|
|
{
|
|
int bus,dev,fun;
|
|
unsigned short wrd;
|
|
pci_ulong busaddr;
|
|
unsigned char irqline;
|
|
|
|
if (BSP_PCI_FIND_DEVICE(
|
|
PCI_VENDOR_TUNDRA,
|
|
PCI_DEVICE_UNIVERSEII,
|
|
instance,
|
|
&bus,
|
|
&dev,
|
|
&fun))
|
|
return -1;
|
|
if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE0,&busaddr))
|
|
return -1;
|
|
if ((unsigned long)(busaddr) & 1) {
|
|
/* it's IO space, try BASE1 */
|
|
if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE1,&busaddr)
|
|
|| ((unsigned long)(busaddr) & 1))
|
|
return -1;
|
|
}
|
|
*pbase=(volatile LERegister*)BSP_PCI2LOCAL_ADDR(busaddr);
|
|
|
|
if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline))
|
|
return -1;
|
|
|
|
/* Enable PCI master and memory access */
|
|
BSP_PCI_CONFIG_IN_SHORT(bus, dev, fun, PCI_COMMAND, &wrd);
|
|
BSP_PCI_CONFIG_OUT_SHORT(bus, dev, fun, PCI_COMMAND, wrd | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
|
|
|
return irqline;
|
|
}
|
|
|
|
/* convert an address space selector to a corresponding
|
|
* universe control mode word
|
|
*/
|
|
|
|
static int
|
|
am2mode(int ismaster, unsigned long address_space, unsigned long *pmode)
|
|
{
|
|
unsigned long mode=0;
|
|
unsigned long vdw =0;
|
|
|
|
/* NOTE: reading the CY961 (Echotek ECDR814) with VDW32
|
|
* generated bus errors when reading 32-bit words
|
|
* - very weird, because the registers are 16-bit
|
|
* AFAIK.
|
|
* - 32-bit accesses worked fine on vxWorks which
|
|
* has the port set to 64-bit.
|
|
* ????????
|
|
*/
|
|
|
|
address_space &= ~VME_MODE_MATCH_MASK;
|
|
|
|
if (!ismaster) {
|
|
mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM;
|
|
mode |= UNIV_SCTL_USER;
|
|
if ( VME_AM_IS_MEMORY & address_space )
|
|
mode |= UNIV_SCTL_PWEN | UNIV_SCTL_PREN;
|
|
mode |= UNIV_SCTL_EN;
|
|
} else {
|
|
switch ( VME_MODE_DBW_MSK & address_space ) {
|
|
default:
|
|
vdw = UNIV_MCTL_VDW64;
|
|
break;
|
|
|
|
case VME_MODE_DBW8:
|
|
break;
|
|
|
|
case VME_MODE_DBW16:
|
|
vdw = UNIV_MCTL_VDW16;
|
|
break;
|
|
|
|
case VME_MODE_DBW32:
|
|
vdw = UNIV_MCTL_VDW32;
|
|
break;
|
|
}
|
|
if ( VME_AM_IS_MEMORY & address_space )
|
|
mode |= UNIV_MCTL_PWEN;
|
|
mode |= UNIV_MCTL_EN;
|
|
}
|
|
|
|
address_space &= ~VME_AM_IS_MEMORY;
|
|
|
|
switch (address_space & VME_AM_MASK) {
|
|
case VME_AM_STD_SUP_PGM:
|
|
case VME_AM_STD_USR_PGM:
|
|
if (ismaster)
|
|
mode |= UNIV_MCTL_PGM ;
|
|
else {
|
|
mode &= ~UNIV_SCTL_DAT;
|
|
}
|
|
|
|
/* fall thru */
|
|
|
|
case VME_AM_STD_SUP_DATA:
|
|
case VME_AM_STD_USR_DATA:
|
|
case VME_AM_STD_SUP_BLT:
|
|
case VME_AM_STD_SUP_MBLT:
|
|
case VME_AM_STD_USR_BLT:
|
|
case VME_AM_STD_USR_MBLT:
|
|
|
|
if ( ismaster ) {
|
|
switch ( address_space & 3 ) {
|
|
case 0: /* mblt */
|
|
if ( UNIV_MCTL_VDW64 != vdw )
|
|
return -1;
|
|
break;
|
|
|
|
case 3: /* blt */
|
|
mode |= UNIV_MCTL_VCT;
|
|
/* universe may do mblt anyways so go back to
|
|
* 32-bit width
|
|
*/
|
|
vdw = UNIV_MCTL_VDW32;
|
|
}
|
|
}
|
|
|
|
mode |= UNIV_CTL_VAS24;
|
|
break;
|
|
|
|
|
|
case VME_AM_EXT_SUP_PGM:
|
|
case VME_AM_EXT_USR_PGM:
|
|
if (ismaster)
|
|
mode |= UNIV_MCTL_PGM ;
|
|
else {
|
|
mode &= ~UNIV_SCTL_DAT;
|
|
}
|
|
/* fall thru */
|
|
|
|
case VME_AM_EXT_SUP_DATA:
|
|
case VME_AM_EXT_USR_DATA:
|
|
case VME_AM_EXT_SUP_BLT:
|
|
case VME_AM_EXT_SUP_MBLT:
|
|
case VME_AM_EXT_USR_BLT:
|
|
case VME_AM_EXT_USR_MBLT:
|
|
|
|
if ( ismaster ) {
|
|
switch ( address_space & 3 ) {
|
|
case 0: /* mblt */
|
|
if ( UNIV_MCTL_VDW64 != vdw )
|
|
return -1;
|
|
break;
|
|
|
|
case 3: /* blt */
|
|
mode |= UNIV_MCTL_VCT;
|
|
/* universe may do mblt anyways so go back to
|
|
* 32-bit width
|
|
*/
|
|
vdw = UNIV_MCTL_VDW32;
|
|
}
|
|
}
|
|
|
|
mode |= UNIV_CTL_VAS32;
|
|
|
|
break;
|
|
|
|
case VME_AM_SUP_SHORT_IO:
|
|
case VME_AM_USR_SHORT_IO:
|
|
mode |= UNIV_CTL_VAS16;
|
|
break;
|
|
|
|
case VME_AM_CSR:
|
|
if ( !ismaster )
|
|
return -1;
|
|
mode |= UNIV_MCTL_VASCSR;
|
|
break;
|
|
|
|
case 0: /* disable the port alltogether */
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
if ( VME_AM_IS_SUP(address_space) )
|
|
mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER);
|
|
|
|
mode |= vdw; /* vdw still 0 in slave mode */
|
|
*pmode = mode;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
disableUniversePort(int ismaster, int portno, volatile unsigned long *preg, void *param)
|
|
{
|
|
unsigned long cntrl;
|
|
cntrl=READ_LE0(preg);
|
|
cntrl &= ~(ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN);
|
|
WRITE_LE(cntrl,preg,0);
|
|
SYNC; /* make sure this command completed */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
cfgUniversePort(
|
|
volatile LERegister *base,
|
|
unsigned long ismaster,
|
|
unsigned long port,
|
|
unsigned long address_space,
|
|
unsigned long vme_address,
|
|
unsigned long local_address,
|
|
unsigned long length)
|
|
{
|
|
volatile LERegister *preg;
|
|
unsigned long p=port;
|
|
unsigned long mode=0;
|
|
|
|
CHECK_DFLT_BASE(base);
|
|
|
|
/* check parameters */
|
|
if (port >= (ismaster ? UNIV_NUM_MPORTS : UNIV_NUM_SPORTS)) {
|
|
uprintf(stderr,"invalid port\n");
|
|
return -1;
|
|
}
|
|
/* port start, bound addresses and offset must lie on 64k boundary
|
|
* (4k for port 0 and 4)
|
|
*/
|
|
if ( PORT_UNALIGNED(local_address,port) ) {
|
|
uprintf(stderr,"local address misaligned\n");
|
|
return -1;
|
|
}
|
|
if ( PORT_UNALIGNED(vme_address,port) ) {
|
|
uprintf(stderr,"vme address misaligned\n");
|
|
return -1;
|
|
}
|
|
if ( PORT_UNALIGNED(length,port) ) {
|
|
uprintf(stderr,"length misaligned\n");
|
|
return -1;
|
|
}
|
|
|
|
/* check address space validity */
|
|
if (am2mode(ismaster,address_space,&mode)) {
|
|
uprintf(stderr,"invalid address space\n");
|
|
return -1;
|
|
}
|
|
|
|
/* get the universe base address */
|
|
if (!base && vmeUniverseInit()) {
|
|
return -1;
|
|
}
|
|
|
|
preg=base;
|
|
|
|
/* find out if we have a rev. II chip */
|
|
if ( UNIV_REV(base) < 2 ) {
|
|
if (port>3) {
|
|
uprintf(stderr,"Universe rev. < 2 has only 4 ports\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* finally, configure the port */
|
|
|
|
/* find the register set for our port */
|
|
if (port<4) {
|
|
preg += (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister);
|
|
} else {
|
|
preg += (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister);
|
|
p-=4;
|
|
}
|
|
preg += 5 * p;
|
|
|
|
/* temporarily disable the port */
|
|
disableUniversePort(ismaster,port,preg,0);
|
|
|
|
/* address_space == 0 means disable */
|
|
if (address_space != 0) {
|
|
unsigned long start,offst;
|
|
/* set the port starting address;
|
|
* this is the local address for the master
|
|
* and the VME address for the slave
|
|
*/
|
|
if (ismaster) {
|
|
start=local_address;
|
|
/* let it overflow / wrap around 0 */
|
|
offst=vme_address-local_address;
|
|
} else {
|
|
start=vme_address;
|
|
/* let it overflow / wrap around 0 */
|
|
offst=local_address-vme_address;
|
|
}
|
|
#undef TSILL
|
|
#ifdef TSILL
|
|
uprintf(stderr,"writing 0x%08x to 0x%08x + 4\n",start,preg);
|
|
#else
|
|
WRITE_LE(start,preg,4);
|
|
#endif
|
|
/* set bound address */
|
|
length+=start;
|
|
#ifdef TSILL
|
|
uprintf(stderr,"writing 0x%08x to 0x%08x + 8\n",length,preg);
|
|
#else
|
|
WRITE_LE(length,preg,8);
|
|
#endif
|
|
/* set offset */
|
|
#ifdef TSILL
|
|
uprintf(stderr,"writing 0x%08x to 0x%08x + 12\n",offst,preg);
|
|
#else
|
|
WRITE_LE(offst,preg,12);
|
|
#endif
|
|
|
|
#ifdef TSILL
|
|
uprintf(stderr,"writing 0x%08x to 0x%08x + 0\n",mode,preg);
|
|
#else
|
|
EIEIO_REG; /* make sure mode is written last */
|
|
WRITE_LE(mode,preg,0);
|
|
SYNC; /* enforce completion */
|
|
#endif
|
|
|
|
#ifdef TSILL
|
|
uprintf(stderr,
|
|
"universe %s port %lu successfully configured\n",
|
|
ismaster ? "master" : "slave",
|
|
port);
|
|
#endif
|
|
|
|
#ifdef __vxworks
|
|
if (ismaster)
|
|
uprintf(stderr,
|
|
"WARNING: on the synergy, sysMasterPortsShow() may show incorrect settings (it uses cached values)\n");
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
showUniversePort(
|
|
int ismaster,
|
|
int portno,
|
|
volatile LERegister *preg,
|
|
void *parm)
|
|
{
|
|
FILE *f=parm ? (FILE *)parm : stdout;
|
|
unsigned long cntrl, start, bound, offst, mask;
|
|
|
|
cntrl = READ_LE0(preg++);
|
|
#undef TSILL
|
|
#ifdef TSILL
|
|
uprintf(stderr,"showUniversePort: *(0x%08x): 0x%08x\n",preg-1,cntrl);
|
|
#endif
|
|
#undef TSILL
|
|
|
|
/* skip this port if disabled */
|
|
if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
|
|
return 0;
|
|
|
|
/* for the master `start' is the PCI address,
|
|
* for the slave `start' is the VME address
|
|
*/
|
|
mask = ~PORT_UNALIGNED(0xffffffff,portno);
|
|
|
|
start = READ_LE0(preg++)&mask;
|
|
bound = READ_LE0(preg++)&mask;
|
|
offst = READ_LE0(preg++)&mask;
|
|
|
|
offst+=start; /* calc start on the other bus */
|
|
|
|
if (ismaster) {
|
|
uprintf(f,"%d: 0x%08lx 0x%08lx 0x%08lx ",
|
|
portno,offst,bound-start,start);
|
|
} else {
|
|
uprintf(f,"%d: 0x%08lx 0x%08lx 0x%08lx ",
|
|
portno,start,bound-start,offst);
|
|
}
|
|
|
|
switch (cntrl & UNIV_CTL_VAS) {
|
|
case UNIV_CTL_VAS16: uprintf(f,"A16, "); break;
|
|
case UNIV_CTL_VAS24: uprintf(f,"A24, "); break;
|
|
case UNIV_CTL_VAS32: uprintf(f,"A32, "); break;
|
|
case UNIV_MCTL_VASCSR: if ( ismaster ) { uprintf(f,"CSR, "); break; }
|
|
/* else fallthru */
|
|
default: uprintf(f,"A??, "); break;
|
|
}
|
|
|
|
if (ismaster) {
|
|
unsigned vdw;
|
|
switch ( cntrl & UNIV_MCTL_VDW64 ) {
|
|
case UNIV_MCTL_VDW64:
|
|
vdw = 64;
|
|
break;
|
|
|
|
case UNIV_MCTL_VDW32:
|
|
vdw = 32;
|
|
break;
|
|
|
|
case UNIV_MCTL_VDW16:
|
|
vdw = 16;
|
|
break;
|
|
|
|
default:
|
|
vdw = 8;
|
|
break;
|
|
}
|
|
|
|
if ( 64 == vdw ) {
|
|
switch ( UNIV_CTL_VAS & cntrl ) {
|
|
case UNIV_CTL_VAS24:
|
|
case UNIV_CTL_VAS32:
|
|
uprintf(f,"D64 [MBLT], ");
|
|
break;
|
|
|
|
default:
|
|
uprintf(f,"D64, ");
|
|
break;
|
|
}
|
|
} else {
|
|
uprintf(f, "D%u%s, ", vdw, (cntrl & UNIV_MCTL_VCT) ? " [BLT]" : "");
|
|
}
|
|
|
|
uprintf(f,"%s, %s",
|
|
cntrl&UNIV_MCTL_PGM ? "Pgm" : "Dat",
|
|
cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr");
|
|
if ( cntrl & UNIV_MCTL_PWEN )
|
|
uprintf(f,", PWEN");
|
|
} else {
|
|
uprintf(f,"%s %s %s %s",
|
|
cntrl&UNIV_SCTL_PGM ? "Pgm," : " ",
|
|
cntrl&UNIV_SCTL_DAT ? "Dat," : " ",
|
|
cntrl&UNIV_SCTL_SUPER ? "Sup," : " ",
|
|
cntrl&UNIV_SCTL_USER ? "Usr" : "");
|
|
if ( cntrl & UNIV_SCTL_PWEN )
|
|
uprintf(f,", PWEN");
|
|
if ( cntrl & UNIV_SCTL_PREN )
|
|
uprintf(f,", PREN");
|
|
}
|
|
uprintf(f,"\n");
|
|
return 0;
|
|
}
|
|
|
|
typedef struct XlatRec_ {
|
|
unsigned long address;
|
|
unsigned long aspace;
|
|
unsigned reverse; /* find reverse mapping of this port */
|
|
} XlatRec, *Xlat;
|
|
|
|
/* try to translate an address through the bridge
|
|
*
|
|
* IN: l->address, l->aspace
|
|
* OUT: l->address (translated address)
|
|
*
|
|
* RETURNS: -1: invalid space
|
|
* 0: invalid address (not found in range)
|
|
* port+1: success
|
|
*/
|
|
|
|
static int
|
|
xlatePort(int ismaster, int port, volatile LERegister *preg, void *parm)
|
|
{
|
|
Xlat l=(Xlat)parm;
|
|
unsigned long cntrl, start, bound, offst, mask, x;
|
|
|
|
cntrl = READ_LE0(preg++);
|
|
|
|
/* skip this port if disabled */
|
|
if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
|
|
return 0;
|
|
|
|
/* check for correct address space */
|
|
if ( am2mode(ismaster,l->aspace,&offst) ) {
|
|
uprintf(stderr,"vmeUniverse WARNING: invalid adressing mode 0x%x\n",
|
|
l->aspace);
|
|
return -1;
|
|
}
|
|
|
|
|
|
switch (VME_MODE_MATCH_MASK & l->aspace) {
|
|
case VME_MODE_EXACT_MATCH:
|
|
mask = -1 & ~VME_MODE_MATCH_MASK;
|
|
break;
|
|
|
|
case VME_MODE_AS_MATCH:
|
|
mask = UNIV_CTL_VAS;
|
|
break;
|
|
|
|
default:
|
|
mask = (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK);
|
|
break;
|
|
}
|
|
|
|
cntrl &= mask;
|
|
offst &= mask;
|
|
|
|
if ( cntrl != offst )
|
|
return 0; /* mode doesn't match requested AM */
|
|
|
|
/* OK, we found a matching mode, now we must check the address range */
|
|
mask = ~PORT_UNALIGNED(0xffffffff,port);
|
|
|
|
/* for the master `start' is the PCI address,
|
|
* for the slave `start' is the VME address
|
|
*/
|
|
start = READ_LE0(preg++) & mask;
|
|
bound = READ_LE0(preg++) & mask;
|
|
offst = READ_LE0(preg++) & mask;
|
|
|
|
/* translate address to the other bus */
|
|
if (l->reverse) {
|
|
/* reverse mapping, i.e. for master ports we map from
|
|
* VME to PCI, for slave ports we map from VME to PCI
|
|
*/
|
|
if (l->address >= start && l->address < bound) {
|
|
l->address+=offst;
|
|
return 1 + port;
|
|
}
|
|
} else {
|
|
x = l->address - offst;
|
|
|
|
if (x >= start && x < bound) {
|
|
/* valid address found */
|
|
l->address = x;
|
|
return 1 + port;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* check if there is any active window with write posting enabled */
|
|
static int
|
|
hasPWENWindow(
|
|
int ismaster,
|
|
int portno,
|
|
volatile LERegister *preg,
|
|
void *parm)
|
|
{
|
|
unsigned long cntrl = READ_LE0(preg);
|
|
unsigned long mask = ismaster ? (UNIV_MCTL_EN|UNIV_MCTL_PWEN) : (UNIV_SCTL_EN|UNIV_SCTL_PWEN);
|
|
return (cntrl & mask) == mask ? -1 : 0;
|
|
}
|
|
|
|
static int
|
|
mapOverAll(volatile LERegister *base, int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
|
|
{
|
|
volatile LERegister *rptr;
|
|
unsigned long port;
|
|
int rval;
|
|
|
|
CHECK_DFLT_BASE(base);
|
|
|
|
rptr = (base +
|
|
(ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister));
|
|
#undef TSILL
|
|
#ifdef TSILL
|
|
uprintf(stderr,"mapoverall: base is 0x%08x, rptr 0x%08x\n",base,rptr);
|
|
#endif
|
|
#undef TSILL
|
|
for (port=0; port<4; port++) {
|
|
if ((rval=func(ismaster,port,rptr,arg))) return rval;
|
|
rptr+=5; /* register block spacing */
|
|
}
|
|
|
|
/* only rev. 2 has 8 ports */
|
|
if (UNIV_REV(base)<2) return -1;
|
|
|
|
rptr = (base +
|
|
(ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister));
|
|
for (port=4; port<UNIV_NUM_MPORTS; port++) {
|
|
if ((rval=func(ismaster,port,rptr,arg))) return rval;
|
|
rptr+=5; /* register block spacing */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
showUniversePorts(volatile LERegister *base, int ismaster, FILE *f)
|
|
{
|
|
if (!f) f=stdout;
|
|
uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave");
|
|
uprintf(f,"Port VME-Addr Size PCI-Adrs Mode:\n");
|
|
mapOverAll(base,ismaster,showUniversePort,f);
|
|
}
|
|
|
|
static int
|
|
xlateFindPort(
|
|
volatile LERegister *base, /* Universe base address */
|
|
int master, /* look in the master windows */
|
|
int reverse, /* reverse mapping; for masters: map local to VME */
|
|
unsigned long as, /* address space */
|
|
unsigned long aIn, /* address to look up */
|
|
unsigned long *paOut/* where to put result */
|
|
)
|
|
{
|
|
int rval;
|
|
XlatRec l;
|
|
l.aspace = as;
|
|
l.address = aIn;
|
|
l.reverse = reverse;
|
|
/* map result -1/0/1 to -2/-1/0 with 0 on success */
|
|
rval = mapOverAll(base,master,xlatePort,(void*)&l) - 1;
|
|
*paOut = l.address;
|
|
return rval;
|
|
}
|
|
|
|
int
|
|
vmeUniverseXlateAddrXX(
|
|
volatile LERegister *base, /* Universe base address */
|
|
int master, /* look in the master windows */
|
|
int reverse, /* reverse mapping; for masters: map local to VME */
|
|
unsigned long as, /* address space */
|
|
unsigned long aIn, /* address to look up */
|
|
unsigned long *paOut/* where to put result */
|
|
)
|
|
{
|
|
return xlateFindPort(base, master, reverse, as, aIn, paOut) >= 0 ? 0 : -1;
|
|
}
|
|
|
|
int
|
|
vmeUniverseXlateAddr(
|
|
int master, /* look in the master windows */
|
|
int reverse, /* reverse mapping; for masters: map local to VME */
|
|
unsigned long as, /* address space */
|
|
unsigned long aIn, /* address to look up */
|
|
unsigned long *paOut/* where to put result */
|
|
)
|
|
{
|
|
DFLT_BASE;
|
|
return vmeUniverseXlateAddrXX(base, master, reverse, as, aIn, paOut);
|
|
}
|
|
|
|
|
|
void
|
|
vmeUniverseReset(void)
|
|
{
|
|
/* disable/reset special cycles (ADOH, RMW) */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_CTL);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_ADDR);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_EN);
|
|
|
|
/* set coupled window timeout to 0 (release VME after each transaction)
|
|
* CRT (coupled request timeout) is unused by Universe II
|
|
*/
|
|
vmeUniverseWriteReg(UNIV_LMISC_CRT_128_US, UNIV_REGOFF_LMISC);
|
|
|
|
/* disable/reset DMA engine */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_DCTL);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_DTBC);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_DLA);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_DVA);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_DCPP);
|
|
|
|
/* disable location monitor */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_LM_CTL);
|
|
|
|
/* disable universe register access from VME bus */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_VRAI_CTL);
|
|
|
|
#if 0 /* leave CSR bus image alone; IRQ manager can use it */
|
|
/* disable VME bus image of VME CSR */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);
|
|
#endif
|
|
|
|
|
|
/* I had problems with a Joerger vtr10012_8 card who would
|
|
* only be accessible after tweaking the U2SPEC register
|
|
* (the t27 parameter helped).
|
|
* I use the same settings here that are used by the
|
|
* Synergy VGM-powerpc BSP for vxWorks.
|
|
*/
|
|
if (2==UNIV_REV(vmeUniverse0BaseAddr))
|
|
vmeUniverseWriteReg(UNIV_U2SPEC_DTKFLTR |
|
|
UNIV_U2SPEC_MASt11 |
|
|
UNIV_U2SPEC_READt27_NODELAY |
|
|
UNIV_U2SPEC_POSt28_FAST |
|
|
UNIV_U2SPEC_PREt28_FAST,
|
|
UNIV_REGOFF_U2SPEC);
|
|
|
|
/* disable interrupts, reset routing */
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_EN);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP0);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP1);
|
|
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_EN);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP0);
|
|
vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP1);
|
|
|
|
vmeUniverseDisableAllSlaves();
|
|
|
|
vmeUniverseDisableAllMasters();
|
|
|
|
vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR);
|
|
|
|
/* clear interrupt status bits */
|
|
vmeUniverseWriteReg(UNIV_LINT_STAT_CLR, UNIV_REGOFF_LINT_STAT);
|
|
vmeUniverseWriteReg(UNIV_VINT_STAT_CLR, UNIV_REGOFF_VINT_STAT);
|
|
|
|
vmeUniverseWriteReg(UNIV_V_AMERR_V_STAT, UNIV_REGOFF_V_AMERR);
|
|
|
|
vmeUniverseWriteReg(
|
|
vmeUniverseReadReg(UNIV_REGOFF_PCI_CSR) |
|
|
UNIV_PCI_CSR_D_PE | UNIV_PCI_CSR_S_SERR | UNIV_PCI_CSR_R_MA |
|
|
UNIV_PCI_CSR_R_TA | UNIV_PCI_CSR_S_TA,
|
|
UNIV_REGOFF_PCI_CSR);
|
|
|
|
vmeUniverseWriteReg(UNIV_L_CMDERR_L_STAT, UNIV_REGOFF_L_CMDERR);
|
|
|
|
vmeUniverseWriteReg(
|
|
UNIV_DGCS_STOP | UNIV_DGCS_HALT | UNIV_DGCS_DONE |
|
|
UNIV_DGCS_LERR | UNIV_DGCS_VERR | UNIV_DGCS_P_ERR,
|
|
UNIV_REGOFF_DGCS);
|
|
}
|
|
|
|
int
|
|
vmeUniverseInit(void)
|
|
{
|
|
int rval;
|
|
if ( (rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr)) < 0 ) {
|
|
uprintf(stderr,"unable to find the universe in pci config space\n");
|
|
} else {
|
|
vmeUniverse0PciIrqLine = rval;
|
|
rval = 0;
|
|
uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %d\n",
|
|
(unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine);
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
void
|
|
vmeUniverseMasterPortsShowXX(volatile LERegister *base, FILE *f)
|
|
{
|
|
showUniversePorts(base,1,f);
|
|
}
|
|
|
|
void
|
|
vmeUniverseMasterPortsShow(FILE *f)
|
|
{
|
|
DFLT_BASE;
|
|
showUniversePorts(base,1,f);
|
|
}
|
|
|
|
void
|
|
vmeUniverseSlavePortsShowXX(volatile LERegister *base, FILE *f)
|
|
{
|
|
showUniversePorts(base,0,f);
|
|
}
|
|
|
|
void
|
|
vmeUniverseSlavePortsShow(FILE *f)
|
|
{
|
|
DFLT_BASE;
|
|
showUniversePorts(base,0,f);
|
|
}
|
|
|
|
int
|
|
vmeUniverseMasterPortCfgXX(
|
|
volatile LERegister *base,
|
|
unsigned long port,
|
|
unsigned long address_space,
|
|
unsigned long vme_address,
|
|
unsigned long local_address,
|
|
unsigned long length)
|
|
{
|
|
return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
|
|
}
|
|
|
|
int
|
|
vmeUniverseMasterPortCfg(
|
|
unsigned long port,
|
|
unsigned long address_space,
|
|
unsigned long vme_address,
|
|
unsigned long local_address,
|
|
unsigned long length)
|
|
{
|
|
DFLT_BASE;
|
|
return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
|
|
}
|
|
|
|
int
|
|
vmeUniverseSlavePortCfgXX(
|
|
volatile LERegister *base,
|
|
unsigned long port,
|
|
unsigned long address_space,
|
|
unsigned long vme_address,
|
|
unsigned long local_address,
|
|
unsigned long length)
|
|
{
|
|
return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
|
|
}
|
|
|
|
int
|
|
vmeUniverseSlavePortCfg(
|
|
unsigned long port,
|
|
unsigned long address_space,
|
|
unsigned long vme_address,
|
|
unsigned long local_address,
|
|
unsigned long length)
|
|
{
|
|
DFLT_BASE;
|
|
return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
|
|
}
|
|
|
|
|
|
void
|
|
vmeUniverseDisableAllSlavesXX(volatile LERegister *base)
|
|
{
|
|
mapOverAll(base,0,disableUniversePort,0);
|
|
}
|
|
|
|
void
|
|
vmeUniverseDisableAllSlaves(void)
|
|
{
|
|
DFLT_BASE;
|
|
mapOverAll(base,0,disableUniversePort,0);
|
|
}
|
|
|
|
void
|
|
vmeUniverseDisableAllMastersXX(volatile LERegister *base)
|
|
{
|
|
mapOverAll(base,1,disableUniversePort,0);
|
|
}
|
|
|
|
void
|
|
vmeUniverseDisableAllMasters(void)
|
|
{
|
|
DFLT_BASE;
|
|
mapOverAll(base,1,disableUniversePort,0);
|
|
}
|
|
|
|
unsigned long
|
|
vmeUniverseReadRegXX(volatile LERegister *base, unsigned long offset)
|
|
{
|
|
unsigned long rval;
|
|
rval = READ_LE(base,offset);
|
|
return rval;
|
|
}
|
|
|
|
|
|
unsigned long
|
|
vmeUniverseReadReg(unsigned long offset)
|
|
{
|
|
unsigned long rval;
|
|
rval = READ_LE(vmeUniverse0BaseAddr,offset);
|
|
return rval;
|
|
}
|
|
|
|
void
|
|
vmeUniverseWriteRegXX(volatile LERegister *base, unsigned long value, unsigned long offset)
|
|
{
|
|
WRITE_LE(value, base, offset);
|
|
}
|
|
|
|
void
|
|
vmeUniverseWriteReg(unsigned long value, unsigned long offset)
|
|
{
|
|
WRITE_LE(value, vmeUniverse0BaseAddr, offset);
|
|
}
|
|
|
|
void
|
|
vmeUniverseResetBus(void)
|
|
{
|
|
vmeUniverseWriteReg(
|
|
vmeUniverseReadReg(UNIV_REGOFF_MISC_CTL) | UNIV_MISC_CTL_SW_SYSRST,
|
|
UNIV_REGOFF_MISC_CTL);
|
|
}
|
|
|
|
void
|
|
vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num)
|
|
{
|
|
#if !defined(__LITTLE_ENDIAN__) || (__LITTLE_ENDIAN__ != 1)
|
|
register unsigned long *p=ptr+num;
|
|
while (p > ptr) {
|
|
#if (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
|
|
__asm__ __volatile__(
|
|
"lwzu 0, -4(%0)\n"
|
|
"stwbrx 0, 0, %0\n"
|
|
: "=r"(p) : "0"(p) : "r0"
|
|
);
|
|
#elif defined(__rtems__)
|
|
p--; st_le32(p, *p);
|
|
#else
|
|
#error "vmeUniverse: endian conversion not implemented for this architecture"
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntRaiseXX(volatile LERegister *base, int level, unsigned vector)
|
|
{
|
|
unsigned long v;
|
|
unsigned long b;
|
|
|
|
CHECK_DFLT_BASE(base);
|
|
|
|
if ( level < 1 || level > 7 || vector > 255 )
|
|
return -1; /* invalid argument */
|
|
|
|
if ( vector & 1 ) /* SW interrupts always ACK an even vector (pp 2-67) */
|
|
return -1;
|
|
|
|
|
|
/* Check if already asserted */
|
|
if ( vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_STAT ) & UNIV_VINT_STAT_SWINT(level) ) {
|
|
return -2; /* already asserted */
|
|
}
|
|
|
|
/* Write Vector */
|
|
vmeUniverseWriteRegXX(base, UNIV_VINT_STATID(vector), UNIV_REGOFF_VINT_STATID );
|
|
|
|
if ( UNIV_REV(base) >= 2 ) {
|
|
/* universe II has individual bits for individual levels */
|
|
b = UNIV_VINT_STAT_SWINT(level);
|
|
} else {
|
|
/* version that is compatible with universe I */
|
|
v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_MAP1);
|
|
v &= ~UNIV_VINT_MAP1_SWINT(0x7);
|
|
v |= UNIV_VINT_MAP1_SWINT(level);
|
|
vmeUniverseWriteRegXX(base, v, UNIV_REGOFF_VINT_MAP1);
|
|
b = UNIV_VINT_EN_SWINT;
|
|
}
|
|
v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_EN);
|
|
/* make sure it is clear, then assert */
|
|
vmeUniverseWriteRegXX(base, v & ~b, UNIV_REGOFF_VINT_EN );
|
|
vmeUniverseWriteRegXX(base, v | b, UNIV_REGOFF_VINT_EN );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntRaise(int level, unsigned vector)
|
|
{
|
|
return vmeUniverseIntRaiseXX(vmeUniverse0BaseAddr, level, vector);
|
|
}
|
|
|
|
|
|
/* Map internal register block to VME */
|
|
#define UNIV_CRG_SIZE (1<<12)
|
|
|
|
int
|
|
vmeUniverseMapCRGXX(volatile LERegister *base, unsigned long vme_base, unsigned long as )
|
|
{
|
|
uint32_t mode;
|
|
|
|
CHECK_DFLT_BASE(base);
|
|
|
|
#ifdef __rtems__
|
|
if ( vmeUniverseRegPort > -1 && ! vmeUniverseRegCSR ) {
|
|
uprintf(stderr,"vmeUniverse: CRG already mapped and in use by interrupt manager\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* enable all, SUP/USR/PGM/DATA accesses */
|
|
mode = UNIV_VRAI_CTL_EN | UNIV_VRAI_CTL_PGM | UNIV_VRAI_CTL_DATA | UNIV_VRAI_CTL_SUPER | UNIV_VRAI_CTL_USER;
|
|
|
|
if ( VME_AM_IS_SHORT(as) ) {
|
|
mode |= UNIV_VRAI_CTL_VAS_A16;
|
|
} else
|
|
if ( VME_AM_IS_STD(as) ) {
|
|
mode |= UNIV_VRAI_CTL_VAS_A24;
|
|
} else
|
|
if ( VME_AM_IS_EXT(as) ) {
|
|
mode |= UNIV_VRAI_CTL_VAS_A32;
|
|
} else {
|
|
return -2;
|
|
}
|
|
|
|
/* map CRG to VME bus */
|
|
WRITE_LE( (vme_base & ~(UNIV_CRG_SIZE-1)), base, UNIV_REGOFF_VRAI_BS );
|
|
WRITE_LE( mode, base, UNIV_REGOFF_VRAI_CTL );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vmeUniverseMapCRG(unsigned long vme_base, unsigned long as )
|
|
{
|
|
return vmeUniverseMapCRGXX( vmeUniverse0BaseAddr, vme_base, as );
|
|
}
|
|
|
|
#ifdef __rtems__
|
|
/* DMA Support -- including linked-list implementation */
|
|
#include "bspVmeDmaListP.h"
|
|
#include <bsp/vmeUniverseDMA.h>
|
|
|
|
/* Filter valid bits of DCTL */
|
|
#define DCTL_MODE_MASK \
|
|
( UNIV_DCTL_VDW_MSK | UNIV_DCTL_VAS_MSK | UNIV_DCTL_PGM | UNIV_DCTL_SUPER | UNIV_DCTL_VCT )
|
|
|
|
static uint32_t
|
|
xfer_mode2dctl(uint32_t xfer_mode)
|
|
{
|
|
uint32_t dctl;
|
|
unsigned long ul;
|
|
|
|
/* Check requested bus mode */
|
|
|
|
/* Universe does not support 'non-incrementing' DMA */
|
|
|
|
/* NOTE: Universe IIb/d *does* support NOINC_VME but states
|
|
* that the VME address needs to be reprogrammed
|
|
* when re-issuing a transfer
|
|
*/
|
|
if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_PCI )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
/* ignore memory hint */
|
|
xfer_mode &= ~VME_AM_IS_MEMORY;
|
|
|
|
if ( VME_AM_IS_2eSST(xfer_mode) )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
if ( ! VME_AM_IS_SHORT(xfer_mode) && ! VME_AM_IS_STD(xfer_mode) && ! VME_AM_IS_EXT(xfer_mode) )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
/* Luckily DCTL bits match MCTL bits so we can use am2mode */
|
|
if ( am2mode( 1, xfer_mode, &ul ) )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
dctl = (uint32_t) ul;
|
|
|
|
/* However, the book says that for DMA VAS==5 [which would
|
|
* be a CSR access] is reserved. Tests indicate that
|
|
* CSR access works on the IIb/d but not really (odd 32-bit
|
|
* addresses read 0) on the II.
|
|
* Nevertheless, we disallow DMA CSR access at this point
|
|
* in order to play it safe...
|
|
*/
|
|
switch ( UNIV_DCTL_VAS_MSK & dctl ) {
|
|
case UNIV_DCTL_VAS_A24:
|
|
case UNIV_DCTL_VAS_A32:
|
|
/* fixup the data width; universe may always use MBLT
|
|
* if data width is 64-bit so we go back to 32-bit
|
|
* if they didn't explicitely ask for MBLT cycles
|
|
*/
|
|
if ( (xfer_mode & 0xb) != 8 /* MBLT */
|
|
&& ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) ) {
|
|
dctl &= ~UNIV_DCTL_VDW_MSK;
|
|
dctl |= UNIV_DCTL_VDW_32;
|
|
}
|
|
break;
|
|
|
|
case UNIV_DCTL_VAS_A16:
|
|
break;
|
|
|
|
default:
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
}
|
|
|
|
/* Make sure other MCTL bits are masked */
|
|
dctl &= DCTL_MODE_MASK;
|
|
|
|
if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) {
|
|
/* If they want NOINC_VME then we have to do some
|
|
* fixup :-( ('errata' [in this case: feature addition] doc. pp. 11+)
|
|
*/
|
|
dctl &= ~UNIV_DCTL_VCT; /* clear block xfer flag */
|
|
dctl |= UNIV_DCTL_NO_VINC;
|
|
/* cannot do 64 bit transfers; go back to 32 */
|
|
if ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) {
|
|
dctl &= ~UNIV_DCTL_VDW_MSK;
|
|
dctl |= UNIV_DCTL_VDW_32;
|
|
}
|
|
}
|
|
|
|
/* Set direction flag */
|
|
|
|
if ( BSP_VMEDMA_MODE_PCI2VME & xfer_mode )
|
|
dctl |= UNIV_DCTL_L2V;
|
|
|
|
return dctl;
|
|
}
|
|
|
|
/* Convert canonical xfer_mode into Universe setup bits; return -1 if request
|
|
* cannot be satisfied (unsupported features)
|
|
*/
|
|
int
|
|
vmeUniverseDmaSetupXX(volatile LERegister *base, int channel, uint32_t mode, uint32_t xfer_mode, void *custom)
|
|
{
|
|
uint32_t dctl, dgcs;
|
|
|
|
if ( channel != 0 )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
dctl = xfer_mode2dctl(xfer_mode);
|
|
|
|
if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
/* Enable all interrupts at the controller */
|
|
dgcs = UNIV_DGCS_INT_MSK;
|
|
|
|
switch ( mode ) {
|
|
case BSP_VMEDMA_OPT_THROUGHPUT:
|
|
dgcs |= UNIV_DGCS_VON_1024 | UNIV_DGCS_VOFF_0_US;
|
|
/* VON counts are different in NO_VINC mode :-( */
|
|
dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
|
|
UNIV_DGCS_VON_2048 : UNIV_DGCS_VON_1024;
|
|
break;
|
|
|
|
case BSP_VMEDMA_OPT_LOWLATENCY:
|
|
dgcs |= UNIV_DGCS_VOFF_0_US;
|
|
/* VON counts are different in NO_VINC mode :-( */
|
|
dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
|
|
UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256;
|
|
break;
|
|
|
|
case BSP_VMEDMA_OPT_SHAREDBUS:
|
|
dgcs |= UNIV_DGCS_VOFF_512_US;
|
|
/* VON counts are different in NO_VINC mode :-( */
|
|
dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
|
|
UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256;
|
|
break;
|
|
|
|
case BSP_VMEDMA_OPT_CUSTOM:
|
|
dctl = ((uint32_t*)custom)[0];
|
|
dgcs = ((uint32_t*)custom)[1];
|
|
break;
|
|
|
|
default:
|
|
case BSP_VMEDMA_OPT_DEFAULT:
|
|
break;
|
|
}
|
|
|
|
/* clear status bits */
|
|
dgcs |= UNIV_DGCS_STATUS_CLEAR;
|
|
|
|
vmeUniverseWriteRegXX(base, dctl, UNIV_REGOFF_DCTL);
|
|
vmeUniverseWriteRegXX(base, dgcs, UNIV_REGOFF_DGCS);
|
|
|
|
return BSP_VMEDMA_STATUS_OK;
|
|
}
|
|
|
|
int
|
|
vmeUniverseDmaSetup(int channel, uint32_t mode, uint32_t xfer_mode, void *custom)
|
|
{
|
|
DFLT_BASE;
|
|
return vmeUniverseDmaSetupXX(base, channel, mode, xfer_mode, custom);
|
|
}
|
|
|
|
int
|
|
vmeUniverseDmaStartXX(volatile LERegister *base, int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes)
|
|
{
|
|
if ( channel != 0 )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
if ((pci_addr & 7) != (vme_addr & 7)) {
|
|
uprintf(stderr,"vmeUniverseDmaStartXX: misaligned addresses\n");
|
|
return -1;
|
|
}
|
|
|
|
{
|
|
/* help the compiler allocate registers */
|
|
register volatile LERegister *b=base;
|
|
register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs;
|
|
|
|
dgcs=READ_LE(b, dgcsoff);
|
|
|
|
/* clear status and make sure CHAIN is clear */
|
|
dgcs &= ~UNIV_DGCS_CHAIN;
|
|
WRITE_LE(dgcs,
|
|
b, dgcsoff);
|
|
WRITE_LE(pci_addr,
|
|
b, UNIV_REGOFF_DLA);
|
|
WRITE_LE(vme_addr,
|
|
b, UNIV_REGOFF_DVA);
|
|
WRITE_LE(n_bytes,
|
|
b, UNIV_REGOFF_DTBC);
|
|
dgcs |= UNIV_DGCS_GO;
|
|
EIEIO_REG; /* make sure GO is written after everything else */
|
|
WRITE_LE(dgcs,
|
|
b, dgcsoff);
|
|
}
|
|
SYNC; /* enforce command completion */
|
|
return 0;
|
|
}
|
|
|
|
/* This entry point is deprecated */
|
|
int
|
|
vmeUniverseStartDMAXX(
|
|
volatile LERegister *base,
|
|
unsigned long local_addr,
|
|
unsigned long vme_addr,
|
|
unsigned long count)
|
|
{
|
|
return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count);
|
|
}
|
|
|
|
int
|
|
vmeUniverseDmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes)
|
|
{
|
|
DFLT_BASE; /* vmeUniverseDmaStartXX doesn't check for a valid base address for efficiency reasons */
|
|
return vmeUniverseDmaStartXX(base, channel, pci_addr, vme_addr, n_bytes);
|
|
}
|
|
|
|
/* This entry point is deprecated */
|
|
int
|
|
vmeUniverseStartDMA(
|
|
unsigned long local_addr,
|
|
unsigned long vme_addr,
|
|
unsigned long count)
|
|
{
|
|
DFLT_BASE; /* vmeUniverseStartDMAXX doesn't check for a valid base address for efficiency reasons */
|
|
return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count);
|
|
}
|
|
|
|
uint32_t
|
|
vmeUniverseDmaStatusXX(volatile LERegister *base, int channel)
|
|
{
|
|
uint32_t dgcs;
|
|
if ( channel != 0 )
|
|
return BSP_VMEDMA_STATUS_UNSUP;
|
|
|
|
dgcs = vmeUniverseReadRegXX(base, UNIV_REGOFF_DGCS);
|
|
|
|
dgcs &= UNIV_DGCS_STATUS_CLEAR;
|
|
|
|
if ( 0 == dgcs || UNIV_DGCS_DONE == dgcs )
|
|
return BSP_VMEDMA_STATUS_OK;
|
|
|
|
if ( UNIV_DGCS_ACT & dgcs )
|
|
return BSP_VMEDMA_STATUS_BUSY;
|
|
|
|
if ( UNIV_DGCS_LERR & dgcs )
|
|
return BSP_VMEDMA_STATUS_BERR_PCI;
|
|
|
|
if ( UNIV_DGCS_VERR & dgcs )
|
|
return BSP_VMEDMA_STATUS_BERR_VME;
|
|
|
|
return BSP_VMEDMA_STATUS_OERR;
|
|
}
|
|
|
|
uint32_t
|
|
vmeUniverseDmaStatus(int channel)
|
|
{
|
|
DFLT_BASE;
|
|
return vmeUniverseDmaStatusXX(base, channel);
|
|
}
|
|
|
|
/* bspVmeDmaList driver interface implementation */
|
|
|
|
/* Cannot use VmeUniverseDMAPacketRec because st_le32 expects unsigned *
|
|
* and we get 'alias' warnings when we submit uint32_t *
|
|
*/
|
|
|
|
typedef volatile uint32_t LERegister1;
|
|
|
|
typedef struct VmeUniverseDmaListDescRec_ {
|
|
LERegister1 dctl;
|
|
LERegister1 dtbc;
|
|
LERegister1 dla;
|
|
LERegister1 dummy1;
|
|
LERegister1 dva;
|
|
LERegister1 dummy2;
|
|
LERegister1 dcpp;
|
|
LERegister1 dummy3;
|
|
} __attribute__((aligned(32), __may_alias__))
|
|
VmeUniverseDmaListDescRec;
|
|
|
|
typedef VmeUniverseDmaListDescRec *VmeUniverseDmaListDesc;
|
|
|
|
static void uni_desc_init (DmaDescriptor);
|
|
static int uni_desc_setup (DmaDescriptor, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
static void uni_desc_setnxt(DmaDescriptor, DmaDescriptor);
|
|
static void uni_desc_dump (DmaDescriptor);
|
|
static int uni_desc_start (volatile void *controller_addr, int channel, DmaDescriptor p);
|
|
|
|
VMEDmaListClassRec vmeUniverseDmaListClass = {
|
|
desc_size: sizeof(VmeUniverseDMAPacketRec),
|
|
desc_align: 32,
|
|
freeList: 0,
|
|
desc_alloc: 0,
|
|
desc_free: 0,
|
|
desc_init: uni_desc_init,
|
|
desc_setnxt:uni_desc_setnxt,
|
|
desc_setup: uni_desc_setup,
|
|
desc_start: uni_desc_start,
|
|
desc_refr: 0,
|
|
desc_dump: uni_desc_dump,
|
|
};
|
|
|
|
static void uni_desc_init (DmaDescriptor p)
|
|
{
|
|
VmeUniverseDmaListDesc d = p;
|
|
st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL );
|
|
}
|
|
|
|
static void uni_desc_setnxt(DmaDescriptor p, DmaDescriptor n)
|
|
{
|
|
VmeUniverseDmaListDesc d = p;
|
|
if ( 0 == n ) {
|
|
st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL );
|
|
} else {
|
|
st_le32( &d->dcpp, BSP_LOCAL2PCI_ADDR( (uint32_t)n));
|
|
}
|
|
}
|
|
|
|
static int
|
|
uni_desc_setup (
|
|
DmaDescriptor p,
|
|
uint32_t attr_mask,
|
|
uint32_t xfer_mode,
|
|
uint32_t pci_addr,
|
|
uint32_t vme_addr,
|
|
uint32_t n_bytes)
|
|
{
|
|
VmeUniverseDmaListDesc d = p;
|
|
LERegister1 dctl;
|
|
|
|
if ( BSP_VMEDMA_MSK_ATTR & attr_mask ) {
|
|
dctl = xfer_mode2dctl(xfer_mode);
|
|
|
|
if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl )
|
|
return -1;
|
|
|
|
st_le32( &d->dctl, dctl );
|
|
}
|
|
|
|
/* Last 3 bits of src & destination addresses must be the same.
|
|
* For sake of simplicity we enforce (stricter) 8-byte alignment
|
|
*/
|
|
|
|
if ( BSP_VMEDMA_MSK_PCIA & attr_mask ) {
|
|
if ( pci_addr & 0x7 )
|
|
return -1;
|
|
|
|
st_le32( &d->dla, pci_addr );
|
|
}
|
|
|
|
if ( BSP_VMEDMA_MSK_VMEA & attr_mask ) {
|
|
if ( vme_addr & 0x7 )
|
|
return -1;
|
|
|
|
st_le32( &d->dva, vme_addr );
|
|
}
|
|
|
|
if ( BSP_VMEDMA_MSK_BCNT & attr_mask ) {
|
|
st_le32( &d->dtbc, n_bytes );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int uni_desc_start
|
|
(volatile void *controller_addr, int channel, DmaDescriptor p)
|
|
{
|
|
volatile LERegister *base = controller_addr;
|
|
uint32_t dgcs;
|
|
|
|
if ( !base )
|
|
base = vmeUniverse0BaseAddr;
|
|
|
|
dgcs = vmeUniverseReadRegXX( base, UNIV_REGOFF_DGCS );
|
|
|
|
if ( UNIV_DGCS_ACT & dgcs )
|
|
return BSP_VMEDMA_STATUS_BUSY;
|
|
|
|
if ( !p ) {
|
|
/* Chain bit is cleared by non-linked-list start command
|
|
* but do this anyways...
|
|
*/
|
|
dgcs &= ~UNIV_DGCS_CHAIN;
|
|
vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs);
|
|
return 0;
|
|
}
|
|
|
|
/* clear status and set CHAIN bit */
|
|
dgcs |= UNIV_DGCS_CHAIN;
|
|
|
|
vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs);
|
|
|
|
/* make sure count is 0 for linked list DMA */
|
|
vmeUniverseWriteRegXX( base, 0x0, UNIV_REGOFF_DTBC);
|
|
|
|
/* set the address of the descriptor chain */
|
|
vmeUniverseWriteRegXX( base, BSP_LOCAL2PCI_ADDR((uint32_t)p), UNIV_REGOFF_DCPP);
|
|
|
|
/* and GO */
|
|
dgcs |= UNIV_DGCS_GO;
|
|
vmeUniverseWriteReg(dgcs, UNIV_REGOFF_DGCS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
uni_desc_dump(DmaDescriptor p)
|
|
{
|
|
VmeUniverseDmaListDesc d = p;
|
|
LERegister1 dcpp = ld_le32(&d->dcpp);
|
|
|
|
printf(" DLA: 0x%08x\n", ld_le32(&d->dla));
|
|
printf(" DVA: 0x%08x\n", ld_le32(&d->dva));
|
|
printf(" DCPP: 0x%08"PRIx32"%s\n", dcpp, (dcpp & UNIV_DCPP_IMG_NULL) ? " (LAST)" : "");
|
|
printf(" CTL: 0x%08x\n", ld_le32(&d->dctl));
|
|
printf(" TBC: 0x%08x\n", ld_le32(&d->dtbc));
|
|
}
|
|
|
|
/* RTEMS interrupt subsystem */
|
|
|
|
#include <bsp/irq.h>
|
|
|
|
typedef struct
|
|
UniverseIRQEntryRec_ {
|
|
VmeUniverseISR isr;
|
|
void *usrData;
|
|
} UniverseIRQEntryRec, *UniverseIRQEntry;
|
|
|
|
static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};
|
|
|
|
int vmeUniverseIrqMgrInstalled = 0;
|
|
|
|
volatile LERegister *vmeUniverseRegBase = 0;
|
|
|
|
/* We support 4 wires between universe + PIC */
|
|
|
|
#define UNIV_NUM_WIRES 4
|
|
|
|
static volatile unsigned long wire_mask[UNIV_NUM_WIRES] = {0};
|
|
/* wires are offset by 1 so we can initialize the wire table to all zeros */
|
|
static int universe_wire[UNIV_NUM_WIRES] = {0};
|
|
|
|
static int
|
|
lvl2bit(unsigned int level)
|
|
{
|
|
int shift = -1;
|
|
if ( level >= UNIV_DMA_INT_VEC && level <= UNIV_LM3_INT_VEC ) {
|
|
shift = 8 + (level-UNIV_DMA_INT_VEC);
|
|
} else if ( UNIV_VOWN_INT_VEC == level ) {
|
|
shift = 0;
|
|
} else if ( 1 <= level && level <=7 ) {
|
|
shift = level;
|
|
} else {
|
|
/* invalid level */
|
|
}
|
|
return shift;
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntRoute(unsigned int level, unsigned int pin)
|
|
{
|
|
int i, shift;
|
|
unsigned long mask, mapreg, flags, wire;
|
|
|
|
if ( pin >= UNIV_NUM_WIRES || ! universe_wire[pin] || !vmeUniverseIrqMgrInstalled )
|
|
return -1;
|
|
|
|
if ( (shift = lvl2bit(level)) < 0 ) {
|
|
return -1; /* invalid level */
|
|
}
|
|
|
|
mask = 1<<shift;
|
|
|
|
/* calculate the mapping register and contents */
|
|
if ( shift < 8 ) {
|
|
mapreg = UNIV_REGOFF_LINT_MAP0;
|
|
} else if ( shift < 16 ) {
|
|
shift -= 8;
|
|
mapreg = UNIV_REGOFF_LINT_MAP1;
|
|
} else if ( shift < 24 ) {
|
|
shift -= 16;
|
|
mapreg = UNIV_REGOFF_LINT_MAP2;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
shift <<=2;
|
|
|
|
/* wires are offset by 1 so we can initialize the wire table to all zeros */
|
|
wire = (universe_wire[pin]-1) << shift;
|
|
|
|
rtems_interrupt_disable(flags);
|
|
|
|
for ( i = 0; i<UNIV_NUM_WIRES; i++ ) {
|
|
wire_mask[i] &= ~mask;
|
|
}
|
|
wire_mask[pin] |= mask;
|
|
|
|
mask = vmeUniverseReadReg(mapreg) & ~ (0xf<<shift);
|
|
mask |= wire;
|
|
vmeUniverseWriteReg( mask, mapreg );
|
|
|
|
rtems_interrupt_enable(flags);
|
|
return 0;
|
|
}
|
|
|
|
VmeUniverseISR
|
|
vmeUniverseISRGet(unsigned long vector, void **parg)
|
|
{
|
|
unsigned long flags;
|
|
VmeUniverseISR rval = 0;
|
|
volatile UniverseIRQEntry *pe = universeHdlTbl + vector;
|
|
|
|
if ( vector>=UNIV_NUM_INT_VECS || ! *pe )
|
|
return 0;
|
|
|
|
rtems_interrupt_disable(flags);
|
|
if ( *pe ) {
|
|
if (parg)
|
|
*parg=(*pe)->usrData;
|
|
rval = (*pe)->isr;
|
|
}
|
|
rtems_interrupt_enable(flags);
|
|
return rval;
|
|
}
|
|
|
|
#define SPECIAL_IRQ_MSK ( ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1) )
|
|
|
|
static void
|
|
universeSpecialISR(unsigned long status)
|
|
{
|
|
register UniverseIRQEntry ip;
|
|
register unsigned vec;
|
|
register unsigned long s;
|
|
|
|
/* handle all LINT bits except for the 'normal' VME interrupts */
|
|
|
|
/* clear all detected special interrupts */
|
|
vmeUniverseWriteReg( (status & SPECIAL_IRQ_MSK), UNIV_REGOFF_LINT_STAT );
|
|
|
|
/* do VOWN first */
|
|
vec=UNIV_VOWN_INT_VEC;
|
|
if ( (status & UNIV_LINT_STAT_VOWN) && (ip=universeHdlTbl[vec]))
|
|
ip->isr(ip->usrData,vec);
|
|
|
|
/* now continue with DMA and scan through all bits;
|
|
* we assume the vectors are in the right order!
|
|
*
|
|
* The initial right shift brings the DMA bit into position 0;
|
|
* the loop is left early if there are no more bits set.
|
|
*/
|
|
for ( s = status>>8; s; s >>= 1) {
|
|
vec++;
|
|
if ( (s&1) && (ip=universeHdlTbl[vec]) )
|
|
ip->isr(ip->usrData,vec);
|
|
}
|
|
|
|
/*
|
|
* clear our line in the VINT_STAT register
|
|
* seems to be not neccessary...
|
|
vmeUniverseWriteReg(
|
|
UNIV_VINT_STAT_LINT(specialIrqUnivOut),
|
|
UNIV_REGOFF_VINT_STAT);
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* interrupts from VME to PCI seem to be processed more or less
|
|
* like this:
|
|
*
|
|
*
|
|
* VME IRQ ------
|
|
* & ----- LINT_STAT ----
|
|
* | & ---------- PCI LINE
|
|
* | |
|
|
* | |
|
|
* LINT_EN ---------------------------
|
|
*
|
|
* I.e.
|
|
* - if LINT_EN is disabled, a VME IRQ will not set LINT_STAT.
|
|
* - while LINT_STAT is set, it will pull the PCI line unless
|
|
* masked by LINT_EN.
|
|
* - VINT_STAT(lint_bit) seems to have no effect beyond giving
|
|
* status info.
|
|
*
|
|
* Hence, it is possible to
|
|
* - arm (set LINT_EN, routing etc.)
|
|
* - receive an irq (sets. LINT_STAT)
|
|
* - the ISR then:
|
|
* * clears LINT_EN, results in masking LINT_STAT (which
|
|
* is still set to prevent another VME irq at the same
|
|
* level to be ACKEd by the universe.
|
|
* * do PCI_EOI to allow nesting of higher VME irqs.
|
|
* (previous step also cleared LINT_EN of lower levels)
|
|
* * when the handler returns, clear LINT_STAT
|
|
* * re-enable setting LINT_EN.
|
|
*/
|
|
|
|
static void
|
|
universeVMEISR(rtems_irq_hdl_param arg)
|
|
{
|
|
int pin = (int)arg;
|
|
UniverseIRQEntry ip;
|
|
unsigned long msk,lintstat,status;
|
|
int lvl;
|
|
#if defined(BSP_PIC_DO_EOI)
|
|
unsigned long linten;
|
|
#endif
|
|
|
|
/* determine the highest priority IRQ source */
|
|
lintstat = vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
|
|
|
|
/* only handle interrupts routed to this pin */
|
|
lintstat &= wire_mask[pin];
|
|
|
|
#ifdef __PPC__
|
|
asm volatile("cntlzw %0, %1":"=r"(lvl):"r"(lintstat & ~SPECIAL_IRQ_MSK));
|
|
lvl = 31-lvl;
|
|
msk = 1<<lvl;
|
|
#else
|
|
for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7;
|
|
lvl>0;
|
|
lvl--, msk>>=1) {
|
|
if (lintstat & msk) break;
|
|
}
|
|
#endif
|
|
|
|
#ifndef BSP_PIC_DO_EOI /* Software priorities not supported */
|
|
|
|
if ( (status = (lintstat & SPECIAL_IRQ_MSK)) )
|
|
universeSpecialISR( status );
|
|
|
|
if ( lvl <= 0)
|
|
return;
|
|
|
|
#else
|
|
if ( lvl <= 0 ) {
|
|
/* try the special handler */
|
|
universeSpecialISR( lintstat & SPECIAL_IRQ_MSK );
|
|
|
|
/*
|
|
* let the pic end this cycle
|
|
*/
|
|
if ( 0 == pin )
|
|
BSP_PIC_DO_EOI;
|
|
|
|
return;
|
|
}
|
|
linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
|
|
|
|
/* mask this and all lower levels that are routed to the same pin */
|
|
vmeUniverseWriteReg(
|
|
linten & ~( ((msk<<1)-UNIV_LINT_STAT_VIRQ1) & wire_mask[pin]),
|
|
UNIV_REGOFF_LINT_EN
|
|
);
|
|
|
|
/* end this interrupt
|
|
* cycle on the PCI bus, so higher level interrupts can be
|
|
* caught from now on...
|
|
*/
|
|
if ( 0 == pin )
|
|
BSP_PIC_DO_EOI;
|
|
#endif
|
|
|
|
/* get vector and dispatch handler */
|
|
status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2));
|
|
/* determine the highest priority IRQ source */
|
|
|
|
if (status & UNIV_VIRQ_ERR) {
|
|
/* TODO: log error message - RTEMS has no logger :-( */
|
|
#ifdef BSP_PIC_DO_EOI
|
|
linten &= ~msk;
|
|
#else
|
|
vmeUniverseIntDisable(lvl);
|
|
#endif
|
|
printk("vmeUniverse ISR: error read from STATID register; (level: %i) STATID: 0x%08lx -- DISABLING\n", lvl, status);
|
|
} else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
|
|
#ifdef BSP_PIC_DO_EOI
|
|
linten &= ~msk;
|
|
#else
|
|
vmeUniverseIntDisable(lvl);
|
|
#endif
|
|
/* TODO: log error message - RTEMS has no logger :-( */
|
|
printk("vmeUniverse ISR: no handler installed for this vector; (level: %i) STATID: 0x%08lx -- DISABLING\n", lvl, status);
|
|
} else {
|
|
/* dispatch handler, it must clear the IRQ at the device */
|
|
ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
|
|
|
|
/* insert a VME read operation to flush fifo, making sure all user write-ops complete */
|
|
#ifdef __PPC__
|
|
/* courtesy to disobedient users who don't use I/O ops */
|
|
asm volatile("eieio");
|
|
#endif
|
|
READ_LE0(vmeUniverseRegBase);
|
|
#ifdef __PPC__
|
|
/* make sure this is ordered before re-enabling */
|
|
asm volatile("eieio");
|
|
#endif
|
|
}
|
|
|
|
/* clear this interrupt level; allow the universe to handler further interrupts */
|
|
vmeUniverseWriteReg(msk, UNIV_REGOFF_LINT_STAT);
|
|
|
|
/*
|
|
* this seems not to be necessary; we just leave the
|
|
* bit set to save a couple of instructions...
|
|
vmeUniverseWriteReg(
|
|
UNIV_VINT_STAT_LINT(vmeIrqUnivOut),
|
|
UNIV_REGOFF_VINT_STAT);
|
|
*/
|
|
|
|
#ifdef BSP_PIC_DO_EOI
|
|
|
|
/* re-enable the previous level */
|
|
vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* STUPID API */
|
|
static void
|
|
my_no_op(const rtems_irq_connect_data * arg)
|
|
{}
|
|
|
|
static int
|
|
my_isOn(const rtems_irq_connect_data *arg)
|
|
{
|
|
return (int)vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
|
|
}
|
|
|
|
typedef struct {
|
|
int uni_pin, pic_pin;
|
|
} IntRoute;
|
|
|
|
static void
|
|
connectIsr(int shared, rtems_irq_hdl isr, int pic_line, int pic_pin)
|
|
{
|
|
rtems_irq_connect_data aarrggh;
|
|
aarrggh.on = my_no_op; /* at _least_ they could check for a 0 pointer */
|
|
aarrggh.off = my_no_op;
|
|
aarrggh.isOn = my_isOn;
|
|
aarrggh.hdl = isr;
|
|
aarrggh.handle = (rtems_irq_hdl_param)pic_pin;
|
|
aarrggh.name = pic_line;
|
|
|
|
if ( shared ) {
|
|
#if BSP_SHARED_HANDLER_SUPPORT > 0
|
|
if (!BSP_install_rtems_shared_irq_handler(&aarrggh))
|
|
rtems_panic("unable to install vmeUniverse shared irq handler");
|
|
#else
|
|
uprintf(stderr,"vmeUniverse: WARNING: your BSP doesn't support sharing interrupts\n");
|
|
if (!BSP_install_rtems_irq_handler(&aarrggh))
|
|
rtems_panic("unable to install vmeUniverse irq handler");
|
|
#endif
|
|
} else {
|
|
if (!BSP_install_rtems_irq_handler(&aarrggh))
|
|
rtems_panic("unable to install vmeUniverse irq handler");
|
|
}
|
|
}
|
|
|
|
#ifndef BSP_EARLY_PROBE_VME
|
|
#define BSP_EARLY_PROBE_VME(addr) \
|
|
( \
|
|
((PCI_DEVICE_UNIVERSEII << 16) | PCI_VENDOR_TUNDRA ) == READ_LE( ((volatile LERegister*)(addr)), 0 ) \
|
|
)
|
|
#endif
|
|
|
|
/* Check if there is a vme address/as is mapped in any of the outbound windows
|
|
* and look for the PCI vendordevice ID there.
|
|
* RETURNS: -1 on error (no mapping or probe failure), outbound window # (0..7)
|
|
* on success. Address translated into CPU address is returned in *pcpu_addr.
|
|
*/
|
|
static int
|
|
mappedAndProbed(unsigned long vme_addr, unsigned as, unsigned long *pcpu_addr)
|
|
{
|
|
int j;
|
|
char *regtype = (as & VME_AM_MASK) == VME_AM_CSR ? "CSR" : "CRG";
|
|
|
|
/* try to find mapping */
|
|
if ( 0 > (j = xlateFindPort(
|
|
vmeUniverse0BaseAddr,
|
|
1, 0,
|
|
as | VME_MODE_AS_MATCH,
|
|
vme_addr,
|
|
pcpu_addr ) ) ) {
|
|
uprintf(stderr,"vmeUniverse - Unable to find mapping for %s VME base (0x%08x)\n", regtype, vme_addr);
|
|
uprintf(stderr," in outbound windows.\n");
|
|
} else {
|
|
/* found a slot number; probe it */
|
|
*pcpu_addr = BSP_PCI2LOCAL_ADDR( *pcpu_addr );
|
|
if ( BSP_EARLY_PROBE_VME(*pcpu_addr) ) {
|
|
uprintf(stderr,"vmeUniverse - IRQ manager using VME %s to flush FIFO\n", regtype);
|
|
return j;
|
|
} else {
|
|
uprintf(stderr,"vmeUniverse - Found slot info but detection of universe in VME %s space failed\n", regtype);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int
|
|
vmeUniverseInstallIrqMgrAlt(int flags, int uni_pin0, int pic_pin0, ...)
|
|
{
|
|
int rval;
|
|
va_list ap;
|
|
va_start(ap, pic_pin0);
|
|
rval = vmeUniverseInstallIrqMgrVa(flags, uni_pin0, pic_pin0, ap);
|
|
va_end(ap);
|
|
return rval;
|
|
}
|
|
|
|
int
|
|
vmeUniverseInstallIrqMgrVa(int flags, int uni_pin0, int pic_pin0, va_list ap)
|
|
{
|
|
int i,j, specialPin, uni_pin[UNIV_NUM_WIRES+1], pic_pin[UNIV_NUM_WIRES];
|
|
unsigned long cpu_base, vme_reg_base;
|
|
|
|
if (vmeUniverseIrqMgrInstalled) return -4;
|
|
|
|
/* check parameters */
|
|
|
|
if ( uni_pin0 < 0 || uni_pin0 > 7 ) return -1;
|
|
|
|
uni_pin[0] = uni_pin0;
|
|
pic_pin[0] = pic_pin0 < 0 ? vmeUniverse0PciIrqLine : pic_pin0;
|
|
i = 1;
|
|
while ( (uni_pin[i] = va_arg(ap, int)) >= 0 ) {
|
|
|
|
if ( i >= UNIV_NUM_WIRES ) {
|
|
return -5;
|
|
}
|
|
|
|
pic_pin[i] = va_arg(ap,int);
|
|
|
|
if ( uni_pin[i] > 7 ) {
|
|
return -2;
|
|
}
|
|
if ( pic_pin[i] < 0 ) {
|
|
return -3;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/* all routings must be different */
|
|
for ( i=0; uni_pin[i] >= 0; i++ ) {
|
|
for ( j=i+1; uni_pin[j] >= 0; j++ ) {
|
|
if ( uni_pin[j] == uni_pin[i] ) return -6;
|
|
if ( pic_pin[j] == pic_pin[i] ) return -7;
|
|
}
|
|
}
|
|
|
|
if ( flags & VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND ) {
|
|
|
|
/* Find registers on VME so the ISR can issue a read to flush the FIFO */
|
|
uprintf(stderr,"vmeUniverse IRQ manager: looking for registers on VME...\n");
|
|
|
|
/* NOTE: The universe [unlike the Tsi148] doesn't know about geographical
|
|
* addressing but the MotLoad firmware [mvme5500] is kind enough to
|
|
* program VCSR_BS based on the board's geographical address for us :-)
|
|
*/
|
|
if ( ( i = ((READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VCSR_BS ) >> 27) & 0x1f ) ) > 0 ) {
|
|
uprintf(stderr,"Trying to find CSR on VME...\n");
|
|
vme_reg_base = i*0x80000 + UNIV_CSR_OFFSET;
|
|
i = mappedAndProbed( vme_reg_base, VME_AM_CSR , &cpu_base);
|
|
if ( i >= 0 )
|
|
vmeUniverseRegCSR = 1;
|
|
} else {
|
|
i = -1;
|
|
}
|
|
|
|
if ( -1 == i ) {
|
|
|
|
uprintf(stderr,"Trying to find CRG on VME...\n");
|
|
|
|
/* Next we see if the CRG block is mapped to VME */
|
|
|
|
if ( UNIV_VRAI_CTL_EN & (j = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_CTL )) ) {
|
|
switch ( j & UNIV_VRAI_CTL_VAS_MSK ) {
|
|
case UNIV_VRAI_CTL_VAS_A16 : i = VME_AM_SUP_SHORT_IO; break;
|
|
case UNIV_VRAI_CTL_VAS_A24 : i = VME_AM_STD_SUP_DATA; break;
|
|
case UNIV_VRAI_CTL_VAS_A32 : i = VME_AM_EXT_SUP_DATA; break;
|
|
default:
|
|
break;
|
|
}
|
|
vme_reg_base = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_BS ) & ~(UNIV_CRG_SIZE - 1);
|
|
}
|
|
|
|
if ( -1 == i ) {
|
|
} else {
|
|
i = mappedAndProbed( vme_reg_base, (i & VME_AM_MASK), &cpu_base );
|
|
}
|
|
}
|
|
|
|
if ( i < 0 ) {
|
|
if ( mapOverAll( vmeUniverse0BaseAddr, 1, hasPWENWindow, 0 ) ) {
|
|
uprintf(stderr,"vmeUniverse IRQ manager - BSP configuration error: registers not found on VME\n");
|
|
uprintf(stderr,"(should open outbound window to CSR space or map CRG [vmeUniverseMapCRG()])\n");
|
|
uprintf(stderr,"Falling back to PCI but you might experience spurious VME interrupts; read a register\n");
|
|
uprintf(stderr,"back from user ISR to flush universe FIFO as a work-around or\n");
|
|
uprintf(stderr,"make sure ISR accesses device using a window with posted-writes disabled\n");
|
|
} else {
|
|
uprintf(stderr,"vmeUniverse IRQ manager - registers not found on VME; falling back to PCI\n");
|
|
}
|
|
vmeUniverseRegBase = vmeUniverse0BaseAddr;
|
|
vmeUniverseRegPort = -1;
|
|
} else {
|
|
vmeUniverseRegBase = (volatile LERegister*)cpu_base;
|
|
vmeUniverseRegPort = i;
|
|
}
|
|
} else {
|
|
vmeUniverseRegBase = vmeUniverse0BaseAddr;
|
|
vmeUniverseRegPort = -1;
|
|
}
|
|
|
|
/* give them a chance to override buggy PCI info */
|
|
if ( pic_pin[0] >= 0 && vmeUniverse0PciIrqLine != pic_pin[0] ) {
|
|
uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
|
|
pic_pin[0]);
|
|
vmeUniverse0PciIrqLine=pic_pin[0];
|
|
}
|
|
|
|
for ( i = 0; uni_pin[i] >= 0; i++ ) {
|
|
/* offset wire # by one so we can initialize to 0 == invalid */
|
|
universe_wire[i] = uni_pin[i] + 1;
|
|
connectIsr((flags & VMEUNIVERSE_IRQ_MGR_FLAG_SHARED), universeVMEISR, pic_pin[i], i);
|
|
}
|
|
|
|
specialPin = uni_pin[1] >= 0 ? 1 : 0;
|
|
|
|
/* setup routing */
|
|
|
|
/* IntRoute checks for mgr being installed */
|
|
vmeUniverseIrqMgrInstalled=1;
|
|
|
|
/* route 7 VME irqs to first / 'normal' pin */
|
|
for ( i=1; i<8; i++ )
|
|
vmeUniverseIntRoute( i, 0 );
|
|
for ( i=UNIV_VOWN_INT_VEC; i<=UNIV_LM3_INT_VEC; i++ ) {
|
|
if ( vmeUniverseIntRoute( i, specialPin ) )
|
|
printk("Routing lvl %i -> wire # %i failed\n", i, specialPin);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vmeUniverseInstallIrqMgr(int vmeIrqUnivOut,
|
|
int vmeIrqPicLine,
|
|
int specialIrqUnivOut,
|
|
int specialIrqPicLine)
|
|
{
|
|
return vmeUniverseInstallIrqMgrAlt(
|
|
0, /* bwds compat. */
|
|
vmeIrqUnivOut, vmeIrqPicLine,
|
|
specialIrqUnivOut, specialIrqPicLine,
|
|
-1);
|
|
}
|
|
|
|
int
|
|
vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
|
|
{
|
|
UniverseIRQEntry ip;
|
|
unsigned long flags;
|
|
volatile UniverseIRQEntry *pe;
|
|
|
|
if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
|
|
return -1;
|
|
|
|
pe = universeHdlTbl + vector;
|
|
|
|
if (*pe || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
|
|
return -1;
|
|
|
|
ip->isr=hdl;
|
|
ip->usrData=arg;
|
|
|
|
rtems_interrupt_disable(flags);
|
|
if ( *pe ) {
|
|
/* oops; someone intervened */
|
|
rtems_interrupt_enable(flags);
|
|
free(ip);
|
|
return -1;
|
|
}
|
|
*pe = ip;
|
|
rtems_interrupt_enable(flags);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
|
|
{
|
|
UniverseIRQEntry ip;
|
|
unsigned long flags;
|
|
volatile UniverseIRQEntry *pe;
|
|
|
|
if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
|
|
return -1;
|
|
|
|
pe = universeHdlTbl + vector;
|
|
|
|
rtems_interrupt_disable(flags);
|
|
ip = *pe;
|
|
if (!ip || ip->isr!=hdl || ip->usrData!=arg) {
|
|
rtems_interrupt_enable(flags);
|
|
return -1;
|
|
}
|
|
*pe = 0;
|
|
rtems_interrupt_enable(flags);
|
|
free(ip);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
intDoEnDis(unsigned int level, int dis)
|
|
{
|
|
unsigned long flags, v;
|
|
int shift;
|
|
|
|
if ( ! vmeUniverseIrqMgrInstalled || (shift = lvl2bit(level)) < 0 )
|
|
return -1;
|
|
|
|
v = 1<<shift;
|
|
|
|
if ( !dis )
|
|
return vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & v ? 1 : 0;
|
|
|
|
rtems_interrupt_disable(flags);
|
|
if ( dis<0 )
|
|
vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & ~v, UNIV_REGOFF_LINT_EN );
|
|
else {
|
|
vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) | v, UNIV_REGOFF_LINT_EN );
|
|
}
|
|
rtems_interrupt_enable(flags);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntEnable(unsigned int level)
|
|
{
|
|
return intDoEnDis(level, 1);
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntDisable(unsigned int level)
|
|
{
|
|
return intDoEnDis(level, -1);
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntIsEnabled(unsigned int level)
|
|
{
|
|
return intDoEnDis(level, 0);
|
|
}
|
|
|
|
/* Loopback test of VME/universe interrupts */
|
|
|
|
typedef struct {
|
|
rtems_id q;
|
|
int l;
|
|
} LoopbackTstArgs;
|
|
|
|
static void
|
|
loopbackTstIsr(void *arg, unsigned long vector)
|
|
{
|
|
LoopbackTstArgs *pa = arg;
|
|
if ( RTEMS_SUCCESSFUL != rtems_message_queue_send(pa->q, (void*)&vector, sizeof(vector)) ) {
|
|
/* Overrun ? */
|
|
printk("vmeUniverseIntLoopbackTst: (ISR) message queue full / overrun ? disabling IRQ level %i\n", pa->l);
|
|
vmeUniverseIntDisable(pa->l);
|
|
}
|
|
}
|
|
|
|
int
|
|
vmeUniverseIntLoopbackTst(int level, unsigned vector)
|
|
{
|
|
DFLT_BASE;
|
|
rtems_status_code sc;
|
|
rtems_id q = 0;
|
|
int installed = 0;
|
|
int i, err = 0;
|
|
int doDisable = 0;
|
|
size_t size;
|
|
unsigned long msg;
|
|
char * irqfmt = "VME IRQ @vector %3i %s";
|
|
char * iackfmt = "VME IACK %s";
|
|
LoopbackTstArgs a;
|
|
|
|
CHECK_DFLT_BASE(base);
|
|
|
|
/* arg check */
|
|
if ( level < 1 || level > 7 || vector > 255 ) {
|
|
fprintf(stderr,"Invalid level or vector argument\n");
|
|
return -1;
|
|
}
|
|
|
|
if ( (vector & 1) ) {
|
|
fprintf(stderr,"Software interrupts can only use even-numbered vectors, sorry.\n");
|
|
return -1;
|
|
}
|
|
|
|
if ( UNIV_REV(base) < 2 && vector != 0 ) {
|
|
fprintf(stderr,
|
|
"vmeUniverseIntLoopbackTst(): Universe 1 has a bug. IACK in response to\n");
|
|
fprintf(stderr,
|
|
"self-generated VME interrupt yields always a zero vector. As a workaround,\n");
|
|
fprintf(stderr,
|
|
"use vector 0, please.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Create message queue */
|
|
if ( RTEMS_SUCCESSFUL != (sc=rtems_message_queue_create(
|
|
rtems_build_name('t' ,'U','I','I'),
|
|
4,
|
|
sizeof(unsigned long),
|
|
0, /* default attributes: fifo, local */
|
|
&q)) ) {
|
|
rtems_error(sc, "vmeUniverseIntLoopbackTst: Unable to create message queue");
|
|
goto bail;
|
|
}
|
|
|
|
a.q = q;
|
|
a.l = level;
|
|
|
|
/* Install handlers */
|
|
if ( vmeUniverseInstallISR(vector, loopbackTstIsr, (void*)&a) ) {
|
|
fprintf(stderr,"Unable to install VME ISR to vector %i\n",vector);
|
|
goto bail;
|
|
}
|
|
installed++;
|
|
if ( vmeUniverseInstallISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a) ) {
|
|
fprintf(stderr,"Unable to install VME ISR to IACK special vector %i\n",UNIV_VME_SW_IACK_INT_VEC);
|
|
goto bail;
|
|
}
|
|
installed++;
|
|
|
|
if ( !vmeUniverseIntIsEnabled(level) && 0==vmeUniverseIntEnable(level) )
|
|
doDisable = 1;
|
|
|
|
/* make sure there are no pending interrupts */
|
|
vmeUniverseWriteReg( UNIV_LINT_STAT_SW_IACK, UNIV_REGOFF_LINT_STAT );
|
|
|
|
if ( vmeUniverseIntEnable( UNIV_VME_SW_IACK_INT_VEC ) ) {
|
|
fprintf(stderr,"Unable to enable IACK interrupt\n");
|
|
goto bail;
|
|
}
|
|
|
|
printf("vmeUniverse VME interrupt loopback test; STARTING...\n");
|
|
printf(" --> asserting VME IRQ level %i\n", level);
|
|
vmeUniverseIntRaise(level, vector);
|
|
|
|
for ( i = 0; i< 3; i++ ) {
|
|
sc = rtems_message_queue_receive(
|
|
q,
|
|
&msg,
|
|
&size,
|
|
RTEMS_WAIT,
|
|
20);
|
|
if ( sc ) {
|
|
if ( RTEMS_TIMEOUT == sc && i>1 ) {
|
|
/* OK; we dont' expect more to happen */
|
|
sc = 0;
|
|
} else {
|
|
rtems_error(sc,"Error waiting for interrupts");
|
|
}
|
|
break;
|
|
}
|
|
if ( msg == vector ) {
|
|
if ( !irqfmt ) {
|
|
printf("Excess VME IRQ received ?? -- BAD\n");
|
|
err = 1;
|
|
} else {
|
|
printf(irqfmt, vector, "received -- PASSED\n");
|
|
irqfmt = 0;
|
|
}
|
|
} else if ( msg == UNIV_VME_SW_IACK_INT_VEC ) {
|
|
if ( !iackfmt ) {
|
|
printf("Excess VME IACK received ?? -- BAD\n");
|
|
err = 1;
|
|
} else {
|
|
printf(iackfmt, "received -- PASSED\n");
|
|
iackfmt = 0;
|
|
}
|
|
} else {
|
|
printf("Unknown IRQ (vector %lu) received -- BAD\n", msg);
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Missing anything ? */
|
|
if ( irqfmt ) {
|
|
printf(irqfmt,vector, "MISSED -- BAD\n");
|
|
err = 1;
|
|
}
|
|
if ( iackfmt ) {
|
|
printf(iackfmt, "MISSED -- BAD\n");
|
|
err = 1;
|
|
}
|
|
|
|
printf("FINISHED.\n");
|
|
|
|
bail:
|
|
if ( doDisable )
|
|
vmeUniverseIntDisable(level);
|
|
vmeUniverseIntDisable( UNIV_VME_SW_IACK_INT_VEC );
|
|
if ( installed > 0 )
|
|
vmeUniverseRemoveISR(vector, loopbackTstIsr, (void*)&a);
|
|
if ( installed > 1 )
|
|
vmeUniverseRemoveISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a);
|
|
if ( q )
|
|
rtems_message_queue_delete(q);
|
|
|
|
return sc ? sc : err;
|
|
}
|
|
|
|
#endif
|