summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--c/src/lib/libbsp/ChangeLog23
-rw-r--r--c/src/lib/libbsp/Makefile.am8
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/README.universe18
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.c1649
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.h533
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c1075
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h364
-rw-r--r--c/src/lib/libbsp/shared/vmeUniverse/vme_am_defs.h67
8 files changed, 3524 insertions, 213 deletions
diff --git a/c/src/lib/libbsp/ChangeLog b/c/src/lib/libbsp/ChangeLog
index 42bf9dd820..70df9e0915 100644
--- a/c/src/lib/libbsp/ChangeLog
+++ b/c/src/lib/libbsp/ChangeLog
@@ -1,3 +1,26 @@
+2005-11-04 Till Straumann <strauman@slac.stanford.edu>
+
+ * shared/vmeUniverse/vmeTsi148.c, shared/vmeUniverse/vmeTsi148.h,
+ shared/vmeUniverse/vme_am_defs.h: New files.
+ * Makefile.am, shared/vmeUniverse/README.universe,
+ shared/vmeUniverse/vmeUniverse.c, shared/vmeUniverse/vmeUniverse.h:
+ Several VME related upgrades and fixes, hopefully addressing PR#835:
+ vmeUniverse driver now supports shared interrupts. vmeUniverse now
+ supports up to four wires between universe and PIC. A new irq mgr
+ installation routine has been added allowing to use the new features.
+ (old version is still present for bwd compatibility). Calls have been
+ added to change interrupt routing (e.g., if wires have different
+ priorities at the PIC this feature can be used to configure
+ priorites). Routine for testing VME interrupts has been added (useful
+ during BSP development). A new header defining standard VME address
+ modes has been added so that the VME API doesn't have to #include a
+ particular bridge driver header. For all driver entry points, a 'XX'
+ variant has been added which allows to pass the chip's base address
+ [in case a second universe is sitting on a VME card :-)]. Driver now
+ uses interrupt line as read from PCI config. space (without offset)
+ BSP needs to use PCI fixup if necessary. Added a driver for the
+ tundra tsi148 VME bridge.
+
2004-02-17 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* bsp.am: Remove everything but CPPASCOMPILE.
diff --git a/c/src/lib/libbsp/Makefile.am b/c/src/lib/libbsp/Makefile.am
index 4c698dab4b..3629f7128b 100644
--- a/c/src/lib/libbsp/Makefile.am
+++ b/c/src/lib/libbsp/Makefile.am
@@ -15,7 +15,13 @@ EXTRA_DIST += shared/bootcard.c shared/bspclean.c shared/bsplibc.c \
shared/gnatinstallhandler.c shared/main.c shared/sbrk.c shared/tod.c \
shared/tod.h
EXTRA_DIST += shared/vmeUniverse/vmeUniverse.c \
- shared/vmeUniverse/vmeUniverse.h
+ shared/vmeUniverse/vmeUniverse.h \
+ shared/vmeUniverse/vmeTsi148.c \
+ shared/vmeUniverse/vmeTsi148.h \
+ shared/vmeUniverse/vme_am_defs.h \
+ shared/vmeUniverse/README.porting \
+ shared/vmeUniverse/README.universe
+
EXTRA_DIST += shared/include/coverhd.h
EXTRA_DIST += shared/gdbstub/rtems-stub-glue.c
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/README.universe b/c/src/lib/libbsp/shared/vmeUniverse/README.universe
index a5f02c8bbc..2983d1911a 100644
--- a/c/src/lib/libbsp/shared/vmeUniverse/README.universe
+++ b/c/src/lib/libbsp/shared/vmeUniverse/README.universe
@@ -1,19 +1,21 @@
-The universe II driver is in a separate subdir
-because it is maintained at SSRL outside of the
-rtems CVS tree (it supports other OSes as well)
+The tundra drivers are in a separate subdir
+because they are maintained at SSRL outside of the
+rtems CVS tree. The directory is called 'vmeUniverse'
+for historic reasons. 'tundra' would be better
+since we now support the tundra tsi148 as well...
-Till Straumann <strauman@slac.stanford.edu> 1/2002
+Till Straumann <strauman@slac.stanford.edu> 1/2002, 2005
NOTES:
-This driver is maintained _outside_ rtems.
+These driver are maintained _outside_ rtems.
Please forward future modifications to me.
-A BSP that wants to use the vmeUniverse driver
+A BSP that wants to use these drivers
must implement the following headers / functionality:
- <bsp/pci.h> offering an API like 'libbsp/powerpc/shared/pci'
- <bsp/irq.h> offering the 'new style' RTEMS irq API
(like 'libbsp/powerpc/shared/irq').
The BSP should then use "VPATH magic" (to use Joel's
-words :-) to reach the vmeUniverse.* files in the
-universe subdir.
+words :-) to reach the vmeUniverse.* / vmeTsi148.* files
+in this subdir.
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.c b/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.c
new file mode 100644
index 0000000000..f786e9db93
--- /dev/null
+++ b/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.c
@@ -0,0 +1,1649 @@
+/* $Id$ */
+
+/* Routines to configure the VME interface
+ * Author: Till Straumann <strauman@slac.stanford.edu>
+ * Nov 2000, Oct 2001, Jan 2002
+ */
+
+#include <rtems.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <bsp/irq.h>
+#include <stdlib.h>
+#include <rtems/bspIo.h> /* printk */
+#include <rtems/error.h> /* printk */
+#include <bsp/pci.h>
+#include <bsp.h>
+#include <libcpu/byteorder.h>
+
+#include "vmeTsi148.h"
+
+#define STATIC static
+
+/* The tsi has 4 'local' wires that can be hooked to a PIC */
+
+#define TSI_NUM_WIRES 4
+
+#define TSI148_NUM_OPORTS 8 /* number of outbound ports */
+#define TSI148_NUM_IPORTS 8 /* number of inbound ports */
+
+#define NUM_TSI_DEVS 2 /* number of instances supported */
+
+#define PCI_VENDOR_TUNDRA 0x10e3
+#define PCI_DEVICE_TSI148 0x0148
+
+#define TSI_OTSAU_SPACING 0x020
+
+#define TSI_OTSAU0_REG 0x100
+#define TSI_OTSAL0_REG 0x104
+#define TSI_OTEAU0_REG 0x108
+#define TSI_OTEAL0_REG 0x10c
+#define TSI_OTOFU0_REG 0x110
+#define TSI_OTOFL0_REG 0x114
+#define TSI_OTBS0_REG 0x118 /* 2eSST broadcast select */
+#define TSI_OTAT0_REG 0x11c
+#define TSI_OTSAU_REG(port) (TSI_OTSAU0_REG + ((port)<<5))
+#define TSI_OTSAL_REG(port) (TSI_OTSAL0_REG + ((port)<<5))
+#define TSI_OTEAU_REG(port) (TSI_OTEAU0_REG + ((port)<<5))
+#define TSI_OTEAL_REG(port) (TSI_OTEAL0_REG + ((port)<<5))
+#define TSI_OTOFU_REG(port) (TSI_OTOFU0_REG + ((port)<<5))
+#define TSI_OTOFL_REG(port) (TSI_OTOFL0_REG + ((port)<<5))
+#define TSI_OTBS_REG(port) (TSI_OTBS0_REG + ((port)<<5))
+#define TSI_OTAT_REG(port) (TSI_OTAT0_REG + ((port)<<5))
+# define TSI_OTAT_EN (1<<31)
+# define TSI_OTAT_MRPFD (1<<18)
+# define TSI_OTAT_PFS(x) (((x)&3)<<16)
+# define TSI_OTAT_2eSSTM(x) (((x)&7)<<11)
+# define TSI_OTAT_TM(x) (((x)&7)<<8)
+# define TSI_OTAT_DBW(x) (((x)&3)<<6)
+# define TSI_OTAT_SUP (1<<5)
+# define TSI_OTAT_PGM (1<<4)
+# define TSI_OTAT_ADMODE(x) (((x)&0xf))
+# define TSI_OTAT_ADMODE_A16 0
+# define TSI_OTAT_ADMODE_A24 1
+# define TSI_OTAT_ADMODE_A32 2
+# define TSI_OTAT_ADMODE_A64 4
+# define TSI_OTAT_ADMODE_USR1 8
+# define TSI_OTAT_ADMODE_USR2 9
+# define TSI_OTAT_ADMODE_USR3 0xa
+# define TSI_OTAT_ADMODE_USR4 0xb
+
+#define TSI_VIACK_1_REG 0x204
+
+#define TSI_VEAU_REG 0x260
+#define TSI_VEAL_REG 0x264
+#define TSI_VEAT_REG 0x268
+
+#define TSI_ITSAU_SPACING 0x020
+
+#define TSI_ITSAU0_REG 0x300
+#define TSI_ITSAL0_REG 0x304
+#define TSI_ITEAU0_REG 0x308
+#define TSI_ITEAL0_REG 0x30c
+#define TSI_ITOFU0_REG 0x310
+#define TSI_ITOFL0_REG 0x314
+#define TSI_ITAT0_REG 0x318
+#define TSI_ITSAU_REG(port) (TSI_ITSAU0_REG + ((port)<<5))
+#define TSI_ITSAL_REG(port) (TSI_ITSAL0_REG + ((port)<<5))
+#define TSI_ITEAU_REG(port) (TSI_ITEAU0_REG + ((port)<<5))
+#define TSI_ITEAL_REG(port) (TSI_ITEAL0_REG + ((port)<<5))
+#define TSI_ITOFU_REG(port) (TSI_ITOFU0_REG + ((port)<<5))
+#define TSI_ITOFL_REG(port) (TSI_ITOFL0_REG + ((port)<<5))
+#define TSI_ITAT_REG(port) (TSI_ITAT0_REG + ((port)<<5))
+
+# define TSI_ITAT_EN (1<<31)
+# define TSI_ITAT_TH (1<<18)
+# define TSI_ITAT_VFS(x) (((x)&3)<<16)
+# define TSI_ITAT_2eSSTM(x) (((x)&7)<<12)
+# define TSI_ITAT_2eSSTB (1<<11)
+# define TSI_ITAT_2eSST (1<<10)
+# define TSI_ITAT_2eVME (1<<9)
+# define TSI_ITAT_MBLT (1<<8)
+# define TSI_ITAT_BLT (1<<7)
+# define TSI_ITAT_AS(x) (((x)&7)<<4)
+# define TSI_ITAT_ADMODE_A16 (0<<4)
+# define TSI_ITAT_ADMODE_A24 (1<<4)
+# define TSI_ITAT_ADMODE_A32 (2<<4)
+# define TSI_ITAT_ADMODE_A64 (4<<4)
+# define TSI_ITAT_SUP (1<<3)
+# define TSI_ITAT_USR (1<<2)
+# define TSI_ITAT_PGM (1<<1)
+# define TSI_ITAT_DATA (1<<0)
+
+#define TSI_VICR_REG 0x440
+# define TSI_VICR_CNTS(v) (((v)&3)<<30)
+# define TSI_VICR_CNTS_DIS (0<<30)
+# define TSI_VICR_CNTS_IRQ1 (1<<30)
+# define TSI_VICR_CNTS_IRQ2 (2<<30)
+# define TSI_VICR_EDGIS(v) (((v)&3)<<28)
+# define TSI_VICR_EDGIS_DIS (0<<28)
+# define TSI_VICR_EDGIS_IRQ1 (1<<28)
+# define TSI_VICR_EDGIS_IRQ2 (2<<28)
+# define TSI_VICR_IRQ1F(v) (((v)&3)<<26)
+# define TSI_VICR_IRQ1F_NORML (0<<26)
+# define TSI_VICR_IRQ1F_PULSE (1<<26)
+# define TSI_VICR_IRQ1F_CLOCK (2<<26)
+# define TSI_VICR_IRQ1F_1MHZ (3<<26)
+# define TSI_VICR_IRQ2F(v) (((v)&3)<<24)
+# define TSI_VICR_IRQ2F_NORML (0<<24)
+# define TSI_VICR_IRQ2F_PULSE (1<<24)
+# define TSI_VICR_IRQ2F_CLOCK (2<<24)
+# define TSI_VICR_IRQ2F_1MHZ (3<<24)
+# define TSI_VICR_BIP (1<<23)
+# define TSI_VICR_BIPS (1<<22)
+# define TSI_VICR_IRQC (1<<15)
+# define TSI_VICR_IRQLS(v) (((v)&7)<<12)
+# define TSI_VICR_IRQS (1<<11)
+# define TSI_VICR_IRQL(v) (((v)&7)<<8)
+# define TSI_VICR_STID(v) ((v)&0xff)
+#define TSI_INTEN_REG 0x448
+#define TSI_INTEO_REG 0x44c
+#define TSI_INTS_REG 0x450
+# define TSI_INTS_IRQ1S (1<<1)
+# define TSI_INTS_IRQ2S (1<<2)
+# define TSI_INTS_IRQ3S (1<<3)
+# define TSI_INTS_IRQ4S (1<<4)
+# define TSI_INTS_IRQ5S (1<<5)
+# define TSI_INTS_IRQ6S (1<<6)
+# define TSI_INTS_IRQ7S (1<<7)
+# define TSI_INTS_ACFLS (1<<8)
+# define TSI_INTS_SYSFLS (1<<9)
+# define TSI_INTS_IACKS (1<<10)
+# define TSI_INTS_VIES (1<<11)
+# define TSI_INTS_VERRS (1<<12)
+# define TSI_INTS_PERRS (1<<13)
+# define TSI_INTS_MB0S (1<<16)
+# define TSI_INTS_MB1S (1<<17)
+# define TSI_INTS_MB2S (1<<18)
+# define TSI_INTS_MB3S (1<<19)
+# define TSI_INTS_LM0S (1<<20)
+# define TSI_INTS_LM1S (1<<21)
+# define TSI_INTS_LM2S (1<<22)
+# define TSI_INTS_LM3S (1<<23)
+# define TSI_INTS_DMA0S (1<<24)
+# define TSI_INTS_DMA1S (1<<25)
+#define TSI_INTC_REG 0x454
+# define TSI_INTC_ACFLC (1<<8)
+# define TSI_INTC_SYSFLC (1<<9)
+# define TSI_INTC_IACKC (1<<10)
+# define TSI_INTC_VIEC (1<<11)
+# define TSI_INTC_VERRC (1<<12)
+# define TSI_INTC_PERRC (1<<13)
+# define TSI_INTC_MB0C (1<<16)
+# define TSI_INTC_MB1C (1<<17)
+# define TSI_INTC_MB2C (1<<18)
+# define TSI_INTC_MB3C (1<<19)
+# define TSI_INTC_LM0C (1<<20)
+# define TSI_INTC_LM1C (1<<21)
+# define TSI_INTC_LM2C (1<<22)
+# define TSI_INTC_LM3C (1<<23)
+# define TSI_INTC_DMA0C (1<<24)
+# define TSI_INTC_DMA1C (1<<25)
+#define TSI_INTM1_REG 0x458
+#define TSI_INTM2_REG 0x45c
+
+
+#define TSI_RD(base, reg) in_be32(((base) + (reg)/sizeof(*base)))
+#define TSI_RD16(base, reg) in_be16((volatile unsigned short *)(base) + (reg)/sizeof(short))
+#define TSI_RD8(base, reg) *((volatile unsigned char *)(base) + (reg))
+#define TSI_WR(base, reg, val) out_be32(((base) + (reg)/sizeof(*base)), val)
+
+#define UNIV_SCTL_AM_MASK (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
+
+
+/* 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_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
+#ifndef BSP_PCI_CONFIG_IN_BYTE
+#define BSP_PCI_CONFIG_IN_BYTE pci_read_config_byte
+#endif
+
+typedef unsigned int pci_ulong;
+
+/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses.
+ * Should be defined by the BSP.
+ */
+#define PCI_TO_LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)
+
+typedef struct {
+ BERegister *base;
+ int irqLine;
+ int pic_pin[TSI_NUM_WIRES];
+} Tsi148Dev;
+
+static Tsi148Dev devs[NUM_TSI_DEVS] = {{0}};
+
+/* 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.
+ */
+
+/* private printing wrapper */
+static void
+uprintf(FILE *f, char *fmt, ...)
+{
+va_list ap;
+ va_start(ap, fmt);
+ if (!f || !_impure_ptr->__sdidinit) {
+ /* Might be called at an early stage when
+ * to a buffer.
+ */
+ vprintk(fmt,ap);
+ } else
+ {
+ vfprintf(f,fmt,ap);
+ }
+ va_end(ap);
+}
+
+#define CHECK_BASE(base,quiet,rval) \
+ do { \
+ if ( !base ) { \
+ if ( !quiet ) { \
+ uprintf(stderr,"Tsi148: Driver not initialized\n"); \
+ } \
+ return rval; \
+ } \
+ } while (0)
+
+int
+vmeTsi148FindPciBase(
+ int instance,
+ BERegister **pbase
+ )
+{
+int bus,dev,fun;
+pci_ulong busaddr;
+unsigned char irqline;
+unsigned short wrd;
+
+ if (BSP_PCI_FIND_DEVICE(
+ PCI_VENDOR_TUNDRA,
+ PCI_DEVICE_TSI148,
+ instance,
+ &bus,
+ &dev,
+ &fun))
+ return -1;
+ if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_BASE_ADDRESS_0,&busaddr))
+ return -1;
+ /* Assume upper BAR is zero */
+
+ *pbase=(BERegister*)(PCI_TO_LOCAL_ADDR(busaddr) & ~0xff);
+
+ 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;
+}
+
+int
+vmeTsi148InitInstance(unsigned instance)
+{
+int irq;
+BERegister *base;
+
+ if ( instance >= NUM_TSI_DEVS )
+ return -1;
+ if ( devs[instance].base )
+ return -1;
+
+ if ((irq=vmeTsi148FindPciBase(instance,&base)) < 0) {
+ uprintf(stderr,"unable to find a Tsi148 in pci config space\n");
+ } else {
+ uprintf(stderr,"Tundra Tsi148 PCI-VME bridge detected at 0x%08x, IRQ %d\n",
+ (unsigned int)base, irq);
+ }
+ devs[0].base = base;
+ devs[0].irqLine = irq;
+
+ return irq < 0 ? -1 : 0;
+}
+
+int
+vmeTsi148Init(void)
+{
+ return vmeTsi148InitInstance(0);
+}
+
+
+void
+vmeTsi148ResetXX(BERegister *base)
+{
+int port;
+
+ CHECK_BASE(base,0, );
+
+ vmeTsi148DisableAllOutboundPortsXX(base);
+ for ( port=0; port < TSI148_NUM_OPORTS; port++ )
+ TSI_WR(base, TSI_OTBS_REG(port), 0);
+ TSI_WR(base, TSI_INTEO_REG, 0);
+ TSI_WR(base, TSI_INTEN_REG, 0);
+ TSI_WR(base, TSI_INTC_REG, 0xffffffff);
+ TSI_WR(base, TSI_INTM1_REG, 0);
+ TSI_WR(base, TSI_INTM2_REG, 0);
+ TSI_WR(base, TSI_VICR_REG, 0);
+ TSI_WR(base, TSI_VEAT_REG, TSI_VEAT_VESCL);
+}
+
+void
+vmeTsi148Reset()
+{
+ vmeTsi148ResetXX(devs[0].base);
+}
+
+/* convert an address space selector to a corresponding
+ * Tsi148 control mode word
+ */
+
+STATIC int
+am2omode(unsigned long address_space, unsigned long *pmode)
+{
+unsigned long mode=0;
+unsigned long tm, mask;
+
+ if ( ! (VME_MODE_DBW32_DISABLE & address_space ) )
+ mode |= TSI_OTAT_DBW(1);
+ if ( ! (VME_MODE_PREFETCH_ENABLE & address_space) )
+ mode |= TSI_OTAT_MRPFD;
+ else {
+ mode |= TSI_OTAT_PFS(address_space>>12);
+ }
+ mode |= TSI_OTAT_2eSSTM(address_space>>16);
+
+ for ( tm = 1, mask = VME_MODE_BLT; ! (mask & address_space); tm++, mask<<=1 ) {
+ if ( VME_MODE_2eSST_BCST == mask ) {
+ tm = 0; /* select default: BLT enabled */
+ break;
+ }
+ }
+ mode |= TSI_OTAT_TM(tm);
+
+ switch (address_space & VME_AM_MASK) {
+ case VME_AM_STD_SUP_PGM:
+ case VME_AM_STD_USR_PGM:
+
+ mode |= TSI_OTAT_PGM;
+
+ /* fall thru */
+ case VME_AM_STD_SUP_DATA:
+ case VME_AM_STD_USR_DATA:
+
+ mode |= TSI_OTAT_ADMODE_A24;
+ break;
+
+ case VME_AM_EXT_SUP_PGM:
+ case VME_AM_EXT_USR_PGM:
+ mode |= TSI_OTAT_PGM;
+
+ /* fall thru */
+ case VME_AM_EXT_SUP_DATA:
+ case VME_AM_EXT_USR_DATA:
+ mode |= TSI_OTAT_ADMODE_A32;
+ break;
+
+ case VME_AM_SUP_SHORT_IO:
+ case VME_AM_USR_SHORT_IO:
+ mode |= TSI_OTAT_ADMODE_A16;
+ break;
+
+ case 0: /* disable the port alltogether */
+ break;
+
+ default:
+ return -1;
+ }
+ if ( VME_AM_IS_SUP(address_space) )
+ mode |= TSI_OTAT_SUP;
+ *pmode = mode;
+ return 0;
+}
+
+STATIC int
+am2imode(unsigned long address_space, unsigned long *pmode)
+{
+unsigned long mode=0;
+unsigned long tm, mask;
+
+ mode |= TSI_ITAT_VFS(address_space>>12);
+ mode |= TSI_ITAT_2eSSTM(address_space>>16);
+
+ mode |= TSI_ITAT_BLT;
+
+ mask = VME_MODE_BLT;
+ tm = TSI_ITAT_BLT;
+ do {
+ mask<<=1; tm<<=1;
+ if ( address_space & mask )
+ mode |= tm;
+ } while ( TSI_ITAT_2eSSTB != tm );
+
+ tm = 0;
+
+ switch (address_space & VME_AM_MASK) {
+ case VME_AM_STD_SUP_PGM:
+ case VME_AM_STD_USR_PGM:
+
+ tm = 1;
+
+ /* fall thru */
+ case VME_AM_STD_SUP_DATA:
+ case VME_AM_STD_USR_DATA:
+
+ mode |= TSI_ITAT_ADMODE_A24;
+ break;
+
+ case VME_AM_EXT_SUP_PGM:
+ case VME_AM_EXT_USR_PGM:
+ tm = 1;
+
+ /* fall thru */
+ case VME_AM_EXT_SUP_DATA:
+ case VME_AM_EXT_USR_DATA:
+ mode |= TSI_ITAT_ADMODE_A32;
+ break;
+
+ case VME_AM_SUP_SHORT_IO:
+ case VME_AM_USR_SHORT_IO:
+ mode |= TSI_ITAT_ADMODE_A16;
+ break;
+
+ case 0: /* disable the port alltogether */
+ *pmode = 0;
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ if ( VME_AM_IS_SUP(address_space) )
+ mode |= TSI_ITAT_SUP;
+ else
+ mode |= TSI_ITAT_USR;
+
+ if ( tm )
+ mode |= TSI_ITAT_PGM;
+ else
+ mode |= TSI_ITAT_DATA;
+
+ *pmode = mode;
+ return 0;
+}
+
+static void
+readTriple(
+ BERegister *base,
+ unsigned reg,
+ unsigned long long *ps,
+ unsigned long long *pl,
+ unsigned long long *po)
+{
+ *ps = TSI_RD(base, reg);
+ *ps = (*ps<<32) | (TSI_RD(base, (reg+4)) & 0xffff0000);
+ *pl = TSI_RD(base, (reg+8));
+ *pl = (*pl<<32) | (TSI_RD(base, (reg+0xc)) & 0xffff0000);
+ *po = TSI_RD(base, (reg+0x10));
+ *po = (*po<<32) | (TSI_RD(base, (reg+0x14)) & 0xffff0000);
+}
+
+
+static unsigned long
+inboundGranularity(unsigned long itat)
+{
+ switch ( itat & TSI_ITAT_AS(-1) ) {
+ case TSI_ITAT_ADMODE_A16: return 0xf;
+ case TSI_ITAT_ADMODE_A24: return 0xfff;
+ default:
+ break;
+ }
+ return 0xffff;
+}
+
+static int
+configTsiPort(
+ BERegister *base,
+ int isout,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length)
+{
+unsigned long long start, limit, offst;
+unsigned long mode, mask, tat_reg, tsau_reg;
+char *name = (isout ? "Outbound" : "Inbound");
+
+ CHECK_BASE(base,0,-1);
+
+ if ( port >= (isout ? TSI148_NUM_OPORTS : TSI148_NUM_IPORTS) ) {
+ uprintf(stderr,"Tsi148 %s Port Cfg: invalid port\n", name);
+ return -1;
+ }
+
+ if ( length && (isout ? am2omode(address_space, &mode) : am2imode(address_space, &mode)) ) {
+ uprintf(stderr,"Tsi148 %s Port Cfg: invalid address space / mode flags\n",name);
+ return -1;
+ }
+
+
+ if ( isout ) {
+ start = pci_address;
+ offst = (unsigned long long)vme_address - start;
+ mask = 0xffff;
+ tat_reg = TSI_OTAT_REG(port);
+ tsau_reg = TSI_OTSAU_REG(port);
+ mode |= TSI_OTAT_EN;
+ } else {
+ start = vme_address;
+ offst = (unsigned long long)pci_address - start;
+ mask = inboundGranularity(mode);
+ tat_reg = TSI_ITAT_REG(port);
+ tsau_reg = TSI_ITSAU_REG(port);
+ mode |= TSI_ITAT_EN;
+ }
+
+ /* If they pass 'length==0' just disable */
+ if ( 0 == length ) {
+ TSI_WR(base, tat_reg, TSI_RD(base, tat_reg) & ~(isout ? TSI_OTAT_EN : TSI_ITAT_EN));
+ return 0;
+ }
+
+
+ if ( (vme_address & mask)
+ || (pci_address & mask)
+ || (length & mask) ) {
+ uprintf(stderr,"Tsi148 %s Port Cfg: invalid address/length; must be multiple of 0x%x\n",
+ name,
+ mask+1);
+ return -1;
+ }
+
+ limit = start + length - 1;
+
+ if ( limit >= (unsigned long long)1<<32 ) {
+ uprintf(stderr,"Tsi148 %s Port Cfg: invalid address/length; must be < 1<<32\n", name);
+ return -1;
+ }
+
+ /* Disable port */
+ TSI_WR(base, tat_reg, 0);
+
+ /* Force to 32-bits */
+ TSI_WR(base, tsau_reg , 0);
+ TSI_WR(base, tsau_reg + 0x04, (unsigned32)start);
+ TSI_WR(base, tsau_reg + 0x08, 0);
+ TSI_WR(base, tsau_reg + 0x0c, (unsigned32)limit);
+ TSI_WR(base, tsau_reg + 0x10, (unsigned32)(offst>>32));
+ TSI_WR(base, tsau_reg + 0x14, (unsigned32)offst);
+
+ /* (outbound only:) leave 2eSST broadcast register alone for user to program */
+
+ /* Set mode and enable */
+ TSI_WR(base, tat_reg, mode);
+ return 0;
+}
+
+static int
+disableTsiPort(
+ BERegister *base,
+ int isout,
+ unsigned long port)
+{
+ return configTsiPort(base, isout, port, 0, 0, 0, 0);
+}
+
+int
+vmeTsi148InboundPortCfgXX(
+ BERegister *base,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length)
+{
+ return configTsiPort(base, 0, port, address_space, vme_address, pci_address, length);
+}
+
+int
+vmeTsi148InboundPortCfg(
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length)
+{
+ return configTsiPort(devs[0].base, 0, port, address_space, vme_address, pci_address, length);
+}
+
+
+int
+vmeTsi148OutboundPortCfgXX(
+ BERegister *base,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length)
+{
+ return configTsiPort(base, 1, port, address_space, vme_address, pci_address, length);
+}
+
+int
+vmeTsi148OutboundPortCfg(
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length)
+{
+ return configTsiPort(devs[0].base, 1, port, address_space, vme_address, pci_address, length);
+}
+
+
+int
+vmeTsi148XlateAddrXX(
+ BERegister *base, /* TSI 148 base address */
+ int outbound, /* look in the outbound windows */
+ int reverse, /* reverse mapping; for outbound ports: map local to VME */
+ unsigned long as, /* address space */
+ unsigned long aIn, /* address to look up */
+ unsigned long *paOut/* where to put result */
+ )
+{
+unsigned long mode, mode_msk;
+int port;
+unsigned long long start, limit, offst, a;
+unsigned long tsau_reg, tat_reg, gran, skip;
+
+ CHECK_BASE(base,0,-1);
+
+ if ( VME_MODE_EXACT_MATCH & as ) {
+ mode_msk = ~0;
+ } else {
+ if ( outbound )
+ mode_msk = TSI_OTAT_PGM | TSI_OTAT_SUP | TSI_OTAT_ADMODE(-1) | TSI_OTAT_EN;
+ else
+ mode_msk = TSI_ITAT_PGM | TSI_ITAT_DATA | TSI_ITAT_SUP | TSI_ITAT_USR | TSI_ITAT_AS(-1) | TSI_ITAT_EN;
+ }
+
+ as &= ~VME_MODE_EXACT_MATCH;
+
+ if ( outbound ? am2omode(as,&mode) : am2imode(as,&mode) ) {
+ uprintf(stderr, "vmeTsi148XlateAddr: invalid address space/mode argument");
+ return -2;
+ }
+
+ if (outbound ) {
+ tsau_reg = TSI_OTSAU_REG(0);
+ tat_reg = TSI_OTAT_REG(0);
+ skip = TSI_OTSAU_SPACING;
+ mode |= TSI_OTAT_EN;
+ gran = 0x10000;
+ } else {
+ tsau_reg = TSI_ITSAU_REG(0);
+ tat_reg = TSI_ITAT_REG(0);
+ skip = TSI_ITSAU_SPACING;
+ mode |= TSI_ITAT_EN;
+ gran = inboundGranularity(mode) + 1;
+ }
+
+ for ( port = 0; port < TSI148_NUM_OPORTS; port++, tsau_reg += skip, tat_reg += skip ) {
+
+ if ( (mode & mode_msk) == (TSI_RD(base, tat_reg) & mode_msk) ) {
+
+ /* found a window with of the right mode; now check the range */
+ readTriple(base, tsau_reg, &start, &limit, &offst);
+ limit += gran;
+
+ if ( !reverse ) {
+ start += offst;
+ limit += offst;
+ offst = -offst;
+ }
+ a = aIn;
+ if ( aIn >= start && aIn <= limit ) {
+ /* found it */
+ *paOut = (unsigned long)(a + offst);
+ return 0;
+ }
+ }
+ }
+
+ uprintf(stderr, "vmeTsi148XlateAddr: no matching mapping found\n");
+ return -1;
+}
+
+int
+vmeTsi148XlateAddr(
+ int outbound, /* look in the outbound windows */
+ int reverse, /* reverse mapping; for outbound ports: map local to VME */
+ unsigned long as, /* address space */
+ unsigned long aIn, /* address to look up */
+ unsigned long *paOut/* where to put result */
+ )
+{
+ return vmeTsi148XlateAddrXX(devs[0].base, outbound, reverse, as, aIn, paOut);
+}
+
+
+/* printk cannot format %llx */
+static void uprintfllx(FILE *f, unsigned long long v)
+{
+ if ( v >= ((unsigned long long)1)<<32 )
+ uprintf(f,"0x%lx%08lx ", (unsigned long)(v>>32), (unsigned long)(v & 0xffffffff));
+ else
+ uprintf(f,"0x%08lx ", (unsigned long)(v & 0xffffffff));
+}
+
+void
+vmeTsi148OutboundPortsShowXX(BERegister *base, FILE *f)
+{
+int port;
+unsigned long mode;
+char tit = 0;
+
+unsigned long long start, limit, offst;
+
+ CHECK_BASE(base,0, );
+
+ if (!f) f=stdout;
+ uprintf(f,"Tsi148 Outbound Ports:\n");
+
+ for ( port = 0; port < TSI148_NUM_OPORTS; port++ ) {
+ mode = TSI_RD(base, TSI_OTAT_REG(port));
+ if ( ! (TSI_OTAT_EN & mode) )
+ continue; /* skip disabled ports */
+
+ readTriple(base, TSI_OTSAU_REG(port), &start, &limit, &offst);
+
+ /* convert limit to size */
+ limit = limit-start+0x10000;
+ if ( !tit ) {
+ uprintf(f,"Port VME-Addr Size PCI-Adrs Mode:\n");
+ tit = 1;
+ }
+ uprintf(f,"%d: ", port);
+ uprintfllx(f,start+offst);
+ uprintfllx(f,limit);
+ uprintfllx(f,start);
+ switch( mode & TSI_OTAT_ADMODE(-1) ) {
+ case TSI_OTAT_ADMODE_A16: uprintf(f,"A16"); break;
+ case TSI_OTAT_ADMODE_A24: uprintf(f,"A24"); break;
+ case TSI_OTAT_ADMODE_A32: uprintf(f,"A32"); break;
+ case TSI_OTAT_ADMODE_A64: uprintf(f,"A64"); break;
+ default: uprintf(f,"A??"); break;
+ }
+
+ if ( mode & TSI_OTAT_PGM ) uprintf(f,", PGM");
+ if ( mode & TSI_OTAT_SUP ) uprintf(f,", SUP");
+ if ( ! (TSI_OTAT_MRPFD & mode) ) uprintf(f,", PREFETCH");
+
+ switch ( mode & TSI_OTAT_DBW(-1) ) {
+ case TSI_OTAT_DBW(0): uprintf(f,", D16"); break;
+ case TSI_OTAT_DBW(1): uprintf(f,", D32"); break;
+ default: uprintf(f,", D??"); break;
+ }
+
+ switch( mode & TSI_OTAT_TM(-1) ) {
+ case TSI_OTAT_TM(0): uprintf(f,", SCT"); break;
+ case TSI_OTAT_TM(1): uprintf(f,", BLT"); break;
+ case TSI_OTAT_TM(2): uprintf(f,", MBLT"); break;
+ case TSI_OTAT_TM(3): uprintf(f,", 2eVME"); break;
+ case TSI_OTAT_TM(4): uprintf(f,", 2eSST"); break;
+ case TSI_OTAT_TM(5): uprintf(f,", 2eSST_BCST"); break;
+ default: uprintf(f," TM??"); break;
+ }
+
+ uprintf(f,"\n");
+ }
+}
+
+void
+vmeTsi148OutboundPortsShow(FILE *f)
+{
+ vmeTsi148OutboundPortsShowXX(devs[0].base, f);
+}
+
+void
+vmeTsi148InboundPortsShowXX(BERegister *base, FILE *f)
+{
+int port;
+unsigned long mode;
+char tit = 0;
+
+unsigned long long start, limit, offst;
+
+ CHECK_BASE(base,0, );
+
+ if (!f) f=stdout;
+ uprintf(f,"Tsi148 Inbound Ports:\n");
+
+ for ( port = 0; port < TSI148_NUM_IPORTS; port++ ) {
+ mode = TSI_RD(base, TSI_ITAT_REG(port));
+ if ( ! (TSI_ITAT_EN & mode) )
+ continue; /* skip disabled ports */
+
+ readTriple(base, TSI_ITSAU_REG(port), &start, &limit, &offst);
+
+ /* convert limit to size */
+ limit = limit - start + inboundGranularity(mode) + 1;
+ if ( !tit ) {
+ uprintf(f,"Port VME-Addr Size PCI-Adrs Mode:\n");
+ tit = 1;
+ }
+ uprintf(f,"%d: ", port);
+ uprintfllx(f,start);
+ uprintfllx(f,limit);
+ uprintfllx(f,start+offst);
+ switch( mode & TSI_ITAT_AS(-1) ) {
+ case TSI_ITAT_ADMODE_A16: uprintf(f,"A16"); break;
+ case TSI_ITAT_ADMODE_A24: uprintf(f,"A24"); break;
+ case TSI_ITAT_ADMODE_A32: uprintf(f,"A32"); break;
+ case TSI_ITAT_ADMODE_A64: uprintf(f,"A64"); break;
+ default: uprintf(f,"A??"); break;
+ }
+
+ if ( mode & TSI_ITAT_PGM ) uprintf(f,", PGM");
+ if ( mode & TSI_ITAT_DATA ) uprintf(f,", DAT");
+ if ( mode & TSI_ITAT_SUP ) uprintf(f,", SUP");
+ if ( mode & TSI_ITAT_USR ) uprintf(f,", USR");
+
+ if ( mode & TSI_ITAT_2eSSTB ) uprintf(f,", 2eSSTB");
+ if ( mode & TSI_ITAT_2eSST ) uprintf(f,", 2eSST");
+ if ( mode & TSI_ITAT_2eVME ) uprintf(f,", 2eVME");
+ if ( mode & TSI_ITAT_MBLT ) uprintf(f,", MBLT");
+ if ( mode & TSI_ITAT_BLT ) uprintf(f,", BLT");
+
+ uprintf(f,"\n");
+ }
+}
+
+void
+vmeTsi148InboundPortsShow(FILE *f)
+{
+ vmeTsi148InboundPortsShowXX(devs[0].base, f);
+}
+
+
+void
+vmeTsi148DisableAllInboundPortsXX(BERegister *base)
+{
+int port;
+
+ for ( port = 0; port < TSI148_NUM_IPORTS; port++ )
+ if ( disableTsiPort(base, 0, port) )
+ break;
+}
+
+void
+vmeTsi148DisableAllInboundPorts(void)
+{
+ vmeTsi148DisableAllInboundPortsXX(devs[0].base);
+}
+
+void
+vmeTsi148DisableAllOutboundPortsXX(BERegister *base)
+{
+int port;
+
+ for ( port = 0; port < TSI148_NUM_IPORTS; port++ )
+ if ( disableTsiPort(base, 1, port) )
+ break;
+}
+
+void
+vmeTsi148DisableAllOutboundPorts(void)
+{
+ vmeTsi148DisableAllOutboundPortsXX(devs[0].base);
+}
+
+
+/* Interrupt Subsystem */
+
+typedef struct
+IRQEntryRec_ {
+ VmeTsi148ISR isr;
+ void *usrData;
+} IRQEntryRec, *IRQEntry;
+
+static IRQEntry irqHdlTbl[TSI_NUM_INT_VECS]={0};
+
+int vmeTsi148IrqMgrInstalled=0;
+
+static volatile unsigned long wire_mask[TSI_NUM_WIRES] = {0};
+/* wires are offset by 1 so we can initialize the wire table to all zeros */
+static int tsi_wire[TSI_NUM_WIRES] = {0};
+
+/* how should we iack a given level, 1,2,4 bytes? */
+static unsigned char tsi_iack_width[7] = {
+ 1,1,1,1,1,1,1
+};
+
+/* map universe compatible vector # to Tsi slot (which maps to bit layout in stat/enable/... regs) */
+static int uni2tsi_vec_map[TSI_NUM_INT_VECS-256] = {
+ /* 256 no VOWN interrupt */ -1,
+ /* TSI_DMA_INT_VEC 257 */ 256 + 24 - 8,
+ /* TSI_LERR_INT_VEC 258 */ 256 + 13 - 8,
+ /* TSI_VERR_INT_VEC 259 */ 256 + 12 - 8,
+ /* 260 is reserved */ -1,
+ /* TSI_VME_SW_IACK_INT_VEC 261 */ 256 + 10 - 8,
+ /* 262 no PCI SW IRQ */ -1,
+ /* TSI_SYSFAIL_INT_VEC 263 */ 256 + 9 - 8,
+ /* TSI_ACFAIL_INT_VEC 264 */ 256 + 8 - 8,
+ /* TSI_MBOX0_INT_VEC 265 */ 256 + 16 - 8,
+ /* TSI_MBOX1_INT_VEC 266 */ 256 + 17 - 8,
+ /* TSI_MBOX2_INT_VEC 267 */ 256 + 18 - 8,
+ /* TSI_MBOX3_INT_VEC 268 */ 256 + 19 - 8,
+ /* TSI_LM0_INT_VEC 269 */ 256 + 20 - 8,
+ /* TSI_LM1_INT_VEC 270 */ 256 + 21 - 8,
+ /* TSI_LM2_INT_VEC 271 */ 256 + 22 - 8,
+ /* TSI_LM3_INT_VEC 272 */ 256 + 23 - 8,
+/* New vectors; only on TSI148 */
+ /* TSI_VIES_INT_VEC 273 */ 256 + 11 - 8,
+ /* TSI_DMA1_INT_VEC 274 */ 256 + 25 - 8,
+};
+
+/* and the reverse; map tsi bit number to universe compatible 'special' vector number */
+static int tsi2uni_vec_map[TSI_NUM_INT_VECS - 256] = {
+ TSI_ACFAIL_INT_VEC,
+ TSI_SYSFAIL_INT_VEC,
+ TSI_VME_SW_IACK_INT_VEC,
+ TSI_VIES_INT_VEC,
+ TSI_VERR_INT_VEC,
+ TSI_LERR_INT_VEC,
+ -1,
+ -1,
+ TSI_MBOX0_INT_VEC,
+ TSI_MBOX1_INT_VEC,
+ TSI_MBOX2_INT_VEC,
+ TSI_MBOX3_INT_VEC,
+ TSI_LM0_INT_VEC,
+ TSI_LM1_INT_VEC,
+ TSI_LM2_INT_VEC,
+ TSI_LM3_INT_VEC,
+ TSI_DMA_INT_VEC,
+ TSI_DMA1_INT_VEC,
+ -1,
+};
+
+static inline int
+uni2tsivec(int v)
+{
+ if ( v < 0 || v >= TSI_NUM_INT_VECS )
+ return -1;
+ return v < 256 ? v : uni2tsi_vec_map[v-256];
+}
+
+static int
+lvl2bitno(unsigned int level)
+{
+ if ( level >= 256 )
+ return uni2tsivec(level) + 8 - 256;
+ else if ( level < 8 && level > 0 )
+ return level;
+ return -1;
+}
+
+int
+vmeTsi148IntRoute(unsigned int level, unsigned int pin)
+{
+int i;
+unsigned long mask, shift, mapreg, flags, wire;
+
+ if ( pin >= TSI_NUM_WIRES || ! tsi_wire[pin] || !vmeTsi148IrqMgrInstalled )
+ return -1;
+
+ if ( level >= 256 ) {
+ if ( (i = uni2tsivec(level)) < 0 )
+ return -1;
+ shift = 8 + (i-256);
+ } else if ( 1 <= level && level <=7 ) {
+ shift = level;
+ } else {
+ return -1; /* invalid level */
+ }
+
+ mask = 1<<shift;
+
+ /* calculate the mapping register and contents */
+ if ( shift < 16 ) {
+ mapreg = TSI_INTM2_REG;
+ } else if ( shift < 32 ) {
+ shift -= 16;
+ mapreg = TSI_INTM1_REG;
+ } else {
+ return -1;
+ }
+
+ shift <<=1;
+
+ /* wires are offset by 1 so we can initialize the wire table to all zeros */
+ wire = (tsi_wire[pin]-1) << shift;
+
+rtems_interrupt_disable(flags);
+
+ for ( i = 0; i<TSI_NUM_WIRES; i++ ) {
+ wire_mask[i] &= ~mask;
+ }
+ wire_mask[pin] |= mask;
+
+ mask = TSI_RD(devs[0].base, mapreg) & ~ (0x3<<shift);
+ mask |= wire;
+ TSI_WR( devs[0].base, mapreg, mask );
+
+rtems_interrupt_enable(flags);
+ return 0;
+}
+
+VmeTsi148ISR
+vmeTsi148ISRGet(unsigned long vector, void **parg)
+{
+VmeTsi148ISR rval = 0;
+unsigned long flags;
+volatile IRQEntry *p;
+int v = uni2tsivec(vector);
+
+
+ if ( v < 0 )
+ return rval;
+
+ p = irqHdlTbl + v;
+
+ rtems_interrupt_disable(flags);
+ if ( *p ) {
+ if ( parg )
+ *parg = (*p)->usrData;
+ rval = (*p)->isr;
+ }
+ rtems_interrupt_enable(flags);
+
+ return rval;
+}
+
+static void
+tsiVMEISR(rtems_irq_hdl_param arg)
+{
+int pin = (int)arg;
+BERegister *b = devs[0].base;
+IRQEntry ip;
+unsigned long msk,lintstat,vector, vecarg;
+int lvl;
+
+ /* only handle interrupts routed to this pin */
+ while ( (lintstat = (TSI_RD(b, TSI_INTS_REG) & wire_mask[pin])) ) {
+
+ /* bit 0 is never set since it is never set in wire_mask */
+
+ do {
+ /* simplicity is king; just handle them in MSB to LSB order; reserved bits read as 0 */
+#ifdef __PPC__
+ asm volatile("cntlzw %0, %1":"=r"(lvl):"r"(lintstat));
+ lvl = 31-lvl;
+ msk = 1<<lvl;
+#else
+ { static unsigned long m[] = {
+ 0xffff0000, 0xff00ff00, 0xf0f0f0f0, 0xcccccccc, 0xaaaaaaaa
+ };
+ int i;
+ unsigned tmp;
+
+ /* lintstat has already been checked...
+ if ( !lintstat ) {
+ lvl = -1; msk = 0;
+ } else
+ */
+ for ( i=0, lvl=0, msk = lintstat; i<5; i++ ) {
+ lvl <<= 1;
+ if ( (tmp = msk & m[i]) ) {
+ lvl++;
+ msk = tmp;
+ } else
+ msk = msk & ~m[i];
+ }
+ }
+#endif
+
+ if ( lvl > 7 ) {
+ /* clear this interrupt level */
+ TSI_WR(b, TSI_INTC_REG, msk);
+ vector = 256 + lvl - 8;
+ vecarg = tsi2uni_vec_map[lvl-8];
+ } else {
+ /* need to do get the vector for this level */
+ switch ( tsi_iack_width[lvl-1] ) {
+ default:
+ case 1:
+ vector = TSI_RD8(b, TSI_VIACK_1_REG - 4 + (lvl<<2) + 3);
+ break;
+
+ case 2:
+ vector = TSI_RD16(b, TSI_VIACK_1_REG - 4 + (lvl<<2) + 2);
+ break;
+
+ case 4:
+ vector = TSI_RD(b, TSI_VIACK_1_REG - 4 + (lvl<<2));
+ break;
+ }
+ vecarg = vector;
+ }
+
+ if ( !(ip=irqHdlTbl[vector])) {
+ /* TODO: log error message - RTEMS has no logger :-( */
+ printk("vmeTsi148 ISR: ERROR: no handler registered (level %i) IACK 0x%08x\n",
+ lvl, vector);
+ } else {
+ /* dispatch handler, it must clear the IRQ at the device */
+ ip->isr(ip->usrData, vecarg);
+ }
+ } while ( (lintstat &= ~msk) );
+ /* check if a new irq is pending already */
+ }
+}
+
+
+static void
+my_no_op(const rtems_irq_connect_data * arg)
+{}
+
+static int
+my_isOn(const rtems_irq_connect_data *arg)
+{
+ return (int)(TSI_RD(devs[0].base, TSI_INTEO_REG) & TSI_RD(devs[0].base, TSI_INTEN_REG));
+}
+
+static void
+connectIsr(int shared, void (*isr)(void), int pic_line, int slot)
+{
+rtems_irq_connect_data xx;
+ xx.on = my_no_op; /* at _least_ they could check for a 0 pointer */
+ xx.off = my_no_op;
+ xx.isOn = my_isOn;
+ xx.hdl = isr;
+ xx.handle = (rtems_irq_hdl_param)slot;
+ xx.name = pic_line;
+
+ if ( shared ) {
+#if BSP_SHARED_HANDLER_SUPPORT > 0
+ if (!BSP_install_rtems_shared_irq_handler(&xx))
+ BSP_panic("unable to install vmeTsi148 shared irq handler");
+#else
+ uprintf(stderr,"vmeTsi148: WARNING: your BSP doesn't support sharing interrupts\n");
+ if (!BSP_install_rtems_irq_handler(&xx))
+ BSP_panic("unable to install vmeTsi148 irq handler");
+#endif
+ } else {
+ if (!BSP_install_rtems_irq_handler(&xx))
+ BSP_panic("unable to install vmeTsi148 irq handler");
+ }
+}
+
+int
+vmeTsi148InstallIrqMgrAlt(int shared, int tsi_pin0, int pic_pin0, ...)
+{
+int rval;
+va_list ap;
+ va_start(ap, pic_pin0);
+ rval = vmeTsi148InstallIrqMgrVa(shared, tsi_pin0, pic_pin0, ap);
+ va_end(ap);
+ return rval;
+}
+
+int
+vmeTsi148InstallIrqMgrVa(int shared, int tsi_pin0, int pic_pin0, va_list ap)
+{
+int i,j, specialPin, tsi_pin[TSI_NUM_WIRES+1], pic_pin[TSI_NUM_WIRES];
+
+ if (vmeTsi148IrqMgrInstalled) return -4;
+
+ /* check parameters */
+
+ if ( tsi_pin0 < 0 || tsi_pin0 > 3 ) return -1;
+
+ tsi_pin[0] = tsi_pin0;
+ pic_pin[0] = pic_pin0 < 0 ? devs[0].irqLine : pic_pin0;
+ i = 1;
+ while ( (tsi_pin[i] = va_arg(ap, int)) >= 0 ) {
+
+ if ( i >= TSI_NUM_WIRES ) {
+ return -5;
+ }
+
+ pic_pin[i] = va_arg(ap,int);
+
+ if ( tsi_pin[i] > 3 ) return -2;
+ if ( pic_pin[i] < 0 ) return -3;
+ i++;
+ }
+
+ /* all routings must be different */
+ for ( i=0; tsi_pin[i] >= 0; i++ ) {
+ for ( j=i+1; tsi_pin[j] >= 0; j++ ) {
+ if ( tsi_pin[j] == tsi_pin[i] ) return -6;
+ if ( pic_pin[j] == pic_pin[i] ) return -7;
+ }
+ }
+
+ /* give them a chance to override buggy PCI info */
+ if ( pic_pin[0] >= 0 && devs[0].irqLine != pic_pin[0] ) {
+ uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
+ pic_pin[0]);
+ devs[0].irqLine = pic_pin[0];
+ }
+
+ for ( i = 0; tsi_pin[i] >= 0; i++ ) {
+ /* offset wire # by one so we can initialize to 0 == invalid */
+ tsi_wire[i] = tsi_pin[i] + 1;
+ connectIsr(shared, tsiVMEISR, pic_pin[i], i);
+ }
+
+ specialPin = tsi_pin[1] >= 0 ? 1 : 0;
+
+ /* setup routing */
+
+ /* IntRoute checks for mgr being installed */
+ vmeTsi148IrqMgrInstalled=1;
+
+ /* route 7 VME irqs to first / 'normal' pin */
+ for ( i=1; i<8; i++ )
+ vmeTsi148IntRoute( i, 0 );
+ for ( i=TSI_DMA_INT_VEC; i<TSI_NUM_INT_VECS; i++ )
+ vmeTsi148IntRoute( i, specialPin );
+
+ for ( i = 0; i<TSI_NUM_WIRES; i++ ) {
+ /* remember (for unloading the driver) */
+ devs[0].pic_pin[i] = ( ( tsi_pin[i] >=0 ) ? pic_pin[i] : -1 );
+ }
+
+ return 0;
+}
+
+int
+vmeTsi148InstallISR(unsigned long vector, VmeTsi148ISR hdl, void *arg)
+{
+IRQEntry ip;
+int v;
+unsigned long flags;
+volatile IRQEntry *p;
+
+ if ( !vmeTsi148IrqMgrInstalled || (v = uni2tsivec(vector)) < 0 )
+ return -1;
+
+ p = irqHdlTbl + v;
+
+ if (*p || !(ip=(IRQEntry)malloc(sizeof(IRQEntryRec))))
+ return -1;
+
+ ip->isr=hdl;
+ ip->usrData=arg;
+
+ rtems_interrupt_disable(flags);
+ if (*p) {
+ rtems_interrupt_enable(flags);
+ free(ip);
+ return -1;
+ }
+ *p = ip;
+ rtems_interrupt_enable(flags);
+ return 0;
+}
+
+int
+vmeTsi148RemoveISR(unsigned long vector, VmeTsi148ISR hdl, void *arg)
+{
+int v;
+IRQEntry ip;
+unsigned long flags;
+volatile IRQEntry *p;
+
+ if ( !vmeTsi148IrqMgrInstalled || (v = uni2tsivec(vector)) < 0 )
+ return -1;
+
+ p = irqHdlTbl + v;
+
+ rtems_interrupt_disable(flags);
+ ip = *p;
+ if ( !ip || ip->isr!=hdl || ip->usrData!=arg ) {
+ rtems_interrupt_enable(flags);
+ return -1;
+ }
+ *p = 0;
+ rtems_interrupt_enable(flags);
+
+ free(ip);
+ return 0;
+}
+
+static int
+intDoEnDis(unsigned int level, int dis)
+{
+BERegister *b = devs[0].base;
+unsigned long flags, v;
+int shift;
+
+ if ( ! vmeTsi148IrqMgrInstalled || (shift = lvl2bitno(level)) < 0 )
+ return -1;
+
+ v = 1<<shift;
+
+ if ( !dis )
+ return (int)(v & TSI_RD(b, TSI_INTEO_REG) & TSI_RD(b, TSI_INTEN_REG)) ? 1 : 0;
+
+ rtems_interrupt_disable(flags);
+ if ( dis<0 ) {
+ TSI_WR(b, TSI_INTEN_REG, TSI_RD(b, TSI_INTEN_REG) & ~v);
+ TSI_WR(b, TSI_INTEO_REG, TSI_RD(b, TSI_INTEO_REG) & ~v);
+ } else {
+ TSI_WR(b, TSI_INTEN_REG, TSI_RD(b, TSI_INTEN_REG) | v);
+ TSI_WR(b, TSI_INTEO_REG, TSI_RD(b, TSI_INTEO_REG) | v);
+ }
+ rtems_interrupt_enable(flags);
+ return 0;
+}
+
+int
+vmeTsi148IntEnable(unsigned int level)
+{
+ return intDoEnDis(level, 1);
+}
+
+int
+vmeTsi148IntDisable(unsigned int level)
+{
+ return intDoEnDis(level, -1);
+}
+
+int
+vmeTsi148IntIsEnabled(unsigned int level)
+{
+ return intDoEnDis(level, 0);
+}
+
+/* Set IACK width (1,2, or 4 bytes) for a given interrupt level.
+ *
+ * 'width' arg may be 0,1,2 or 4. If zero, the currently active
+ * value is returned but not modified.
+ *
+ * RETURNS: old width or -1 if invalid argument.
+ */
+
+int
+vmeTsi148SetIackWidth(int level, int width)
+{
+int rval;
+ if ( level < 1 || level > 7 || !vmeTsi148IrqMgrInstalled )
+ return -1;
+
+ switch ( width ) {
+ default: return -1;
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ break;
+ }
+
+ rval = tsi_iack_width[level-1];
+ if ( width )
+ tsi_iack_width[level-1] = width;
+ return rval;
+}
+
+int
+vmeTsi148IntRaiseXX(BERegister *base, int level, unsigned vector)
+{
+unsigned long v;
+
+ CHECK_BASE(base,0,-1);
+
+ if ( level < 1 || level > 7 || vector > 255 )
+ return -1; /* invalid argument */
+
+ /* Check if already asserted */
+ if ( (v = TSI_RD(base, TSI_VICR_REG)) & TSI_VICR_IRQS ) {
+ return -2; /* already asserted */
+ }
+
+ v &= ~255;
+
+ v |= TSI_VICR_IRQL(level) | TSI_VICR_STID(vector);
+
+ /* Write Vector */
+ TSI_WR(base, TSI_VICR_REG, v);
+
+ return 0;
+
+}
+
+int
+vmeTsi148IntRaise(int level, unsigned vector)
+{
+ return vmeTsi148IntRaiseXX(devs[0].base, level, vector);
+}
+
+/* Loopback test of VME/Tsi148 internal 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("vmeTsi148IntLoopbackTst: (ISR) message queue full / overrun ? disabling IRQ level %i\n", pa->l);
+ vmeTsi148IntDisable(pa->l);
+ }
+}
+
+int
+vmeTsi148IntLoopbackTst(int level, unsigned vector)
+{
+BERegister *b = devs[0].base;
+rtems_status_code sc;
+rtems_id q = 0;
+int installed = 0;
+int i, err = 0;
+int doDisable = 0;
+unsigned32 size;
+unsigned long msg;
+char * irqfmt = "VME IRQ @vector %3i %s";
+char * iackfmt = "VME IACK %s";
+LoopbackTstArgs a;
+
+ CHECK_BASE(b,0,-1);
+
+ /* arg check */
+ if ( level < 1 || level > 7 || vector > 255 )
+ 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, "vmeTsi148IntLoopbackTst: Unable to create message queue");
+ goto bail;
+ }
+
+ a.q = q;
+ a.l = level;
+
+ /* Install handlers */
+ if ( vmeTsi148InstallISR(vector, loopbackTstIsr, (void*)&a) ) {
+ uprintf(stderr,"Unable to install VME ISR to vector %i\n",vector);
+ goto bail;
+ }
+ installed++;
+ if ( vmeTsi148InstallISR(TSI_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a) ) {
+ uprintf(stderr,"Unable to install VME ISR to IACK special vector %i\n",TSI_VME_SW_IACK_INT_VEC);
+ goto bail;
+ }
+ installed++;
+
+ if ( !vmeTsi148IntIsEnabled(level) && 0==vmeTsi148IntEnable(level) )
+ doDisable = 1;
+
+ /* make sure there are no pending interrupts */
+ TSI_WR(b, TSI_INTC_REG, TSI_INTC_IACKC);
+
+ if ( vmeTsi148IntEnable( TSI_VME_SW_IACK_INT_VEC ) ) {
+ uprintf(stderr,"Unable to enable IACK interrupt\n");
+ goto bail;
+ }
+
+ printf("vmeTsi148 VME interrupt loopback test; STARTING...\n");
+ printf(" --> asserting VME IRQ level %i\n", level);
+ vmeTsi148IntRaise(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 == TSI_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 )
+ vmeTsi148IntDisable(level);
+ vmeTsi148IntDisable( TSI_VME_SW_IACK_INT_VEC );
+ if ( installed > 0 )
+ vmeTsi148RemoveISR(vector, loopbackTstIsr, (void*)&a);
+ if ( installed > 1 )
+ vmeTsi148RemoveISR(TSI_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a);
+ if ( q )
+ rtems_message_queue_delete(q);
+
+ return sc ? sc : err;
+}
+
+unsigned long
+vmeTsi148ClearVMEBusErrorsXX(BERegister *base, unsigned long long *paddr)
+{
+unsigned long rval;
+
+ CHECK_BASE(base,1,-1);
+
+ rval = TSI_RD(base, TSI_VEAT_REG);
+ if ( rval & TSI_VEAT_VES ) {
+ if ( paddr ) {
+ *paddr = ((unsigned long long)TSI_RD(base, TSI_VEAU_REG))<<32;
+ *paddr |= TSI_RD(base, TSI_VEAL_REG);
+ }
+ /* clear errors */
+ TSI_WR(base, TSI_VEAT_REG, TSI_VEAT_VESCL);
+ } else {
+ rval = 0;
+ }
+ return rval;
+}
+
+unsigned long
+vmeTsi148ClearVMEBusErrors(unsigned long long *paddr)
+{
+ return vmeTsi148ClearVMEBusErrorsXX(devs[0].base, paddr);
+}
+
+#ifdef DEBUG_MODULAR
+void
+_cexpModuleInitialize(void* unused)
+{
+ vmeTsi148Init();
+ vmeTsi148Reset();
+}
+
+int
+_cexpModuleFinalize(void *unused)
+{
+int i;
+int rval = 1;
+void (*isrs[TSI_NUM_WIRES])() = {
+ isr_pin0,
+ isr_pin1,
+ isr_pin2,
+ isr_pin3,
+};
+
+rtems_irq_connect_data xx;
+ xx.on = my_no_op; /* at _least_ they could check for a 0 pointer */
+ xx.off = my_no_op;
+ xx.isOn = my_isOn;
+
+ TSI_WR(devs[0].base, TSI_INTEO_REG, 0);
+
+ for ( i=0; i<TSI_NUM_INT_VECS; i++) {
+ /* Dont even bother to uninstall handlers */
+ }
+ if ( vmeTsi148IrqMgrInstalled ) {
+ for ( i=0; i<TSI_NUM_WIRES; i++ ) {
+ if ( (int)(xx.name = devs[0].pic_pin[i]) >=0 ) {
+ xx.hdl = isrs[i];
+ rval = rval && BSP_remove_rtems_irq_handler(&xx);
+ }
+ }
+ }
+ return !rval;
+}
+#endif
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.h b/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.h
new file mode 100644
index 0000000000..700206ae73
--- /dev/null
+++ b/c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.h
@@ -0,0 +1,533 @@
+/* $Id$ */
+#ifndef VME_TSI148_DRIVER_H
+#define VME_TSI148_DRIVER_H
+
+/* Routines to configure and use the Tundra Tsi148 VME bridge
+ * Author: Till Straumann <strauman@slac.stanford.edu>
+ * Sept. 2005.
+ */
+
+#include <bsp/vme_am_defs.h>
+
+/* NOTE: A64 currently not implemented */
+
+/* These can be ored with the AM */
+
+#define VME_MODE_PREFETCH_ENABLE (4<<12)
+#define VME_MODE_PREFETCH_SIZE(x) (((x)&3)<<12)
+#define VME_MODE_2eSSTM(x) (((x)&7)<<16)
+
+#define VME_MODE_DBW32_DISABLE (8<<12)
+
+/* Transfer modes:
+ *
+ * On a outbound window, only the least significant
+ * bit that is set is considered.
+ * On a inbound window, the bitwise OR of modes
+ * is accepted.
+ */
+#define VME_MODE_BLT (1<<20)
+#define VME_MODE_MBLT (1<<21)
+#define VME_MODE_2eVME (1<<22)
+#define VME_MODE_2eSST (1<<23)
+#define VME_MODE_2eSST_BCST (1<<24)
+#define VME_MODE_MASK (31<<20)
+
+#define VME_MODE_EXACT_MATCH (1<<31)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef volatile unsigned32 BERegister; /* emphasize contents are big endian */
+
+/*
+ * Scan the PCI busses for the Nth (N=='instance') Tsi148 VME bridge.
+ *
+ * RETURNS:
+ * contents of the IRQ_LINE PCI config register on Success,
+ * the base address of the Tsi148 register block is stored in
+ * *pbase.
+ * -1 on error (no Tsi found, error accessing PCI config space).
+ *
+ * SIDE_EFFECTS: PCI busmaster and response to memory addresses is enabled.
+ */
+int
+vmeTsi148FindPciBase(int instance, BERegister **pbase);
+
+/* Initialize driver for Nth Tsi148 device found.
+ * This routine does not change any registers but
+ * just scans the PCI bus for Tsi bridges and initializes
+ * a driver slot.
+ *
+ * RETURNS: 0 on success, nonzero on error (or if no Tsi148
+ * device is found).
+ */
+int
+vmeTsi148InitInstance(unsigned instance);
+
+/* Initialize driver with 1st Tsi148 bridge found
+ * RETURNS: (see vmeTsi148InitInstance()).
+ */
+int
+vmeTsi148Init(void);
+
+/* setup the tsi148 chip, i.e. disable most of its
+ * mappings, reset interrupts etc.
+ */
+void
+vmeTsi148ResetXX(BERegister *base);
+
+/* setup the tsi148 connected to the first driver slot */
+void
+vmeTsi148Reset();
+
+/* NOTE: all non-'XX' versions of driver entry points which
+ * have an associated 'XX' entry point operate on the
+ * device connected to the 1st driver slot.
+ */
+
+/* configure a outbound port
+ *
+ * port: port number 0..7
+ *
+ * address_space: vxWorks compliant addressing mode identifier
+ * (see vme.h). The most important are:
+ * 0x0d - A32, Sup, Data
+ * 0x3d - A24, Sup, Data
+ * 0x2d - A16, Sup, Data
+ * additionally, the value 0 is accepted; it will
+ * disable this port.
+ * vme_address: address on the vme_bus of this port.
+ * local_address: address on the pci_bus of this port.
+ * length: size of this port.
+ *
+ * NOTE: the addresses and length parameters must meet certain alignment
+ * requirements (see Tsi148 documentation).
+ *
+ * RETURNS: 0 on success, -1 on failure. Error messages printed to stderr.
+ */
+
+int
+vmeTsi148OutboundPortCfgXX(
+ BERegister *base,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length);
+
+int
+vmeTsi148OutboundPortCfg(
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length);
+
+
+/* configure a VME inbound (PCI master) port */
+int
+vmeTsi148InboundPortCfgXX(
+ BERegister *base,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length);
+
+int
+vmeTsi148InboundPortCfg(
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long pci_address,
+ unsigned long length);
+
+/* Translate an address through the bridge
+ *
+ * vmeTsi248XlateAddr(0,0,as,addr,&result)
+ * yields a VME a address that reflects
+ * a local memory location as seen from the VME bus through the
+ * tsi148 VME inbound port.
+ *
+ * Likewise does vmeTsi148XlateAddr(1,0,as,addr,&result)
+ * translate a VME bus addr (backwards, through the VME outbound
+ * port) to the PCI side of the bridge.
+ *
+ * A valid address space modifier must be specified.
+ * If VME_MODE_EXACT_MATCH is set, all the mode bits must
+ * match the requested mode. If VME_MODE_EXACT_MATCH is not
+ * set in the mode word, only the basic mode (address-space,
+ * sup/usr and pgm/data) is compared.
+ *
+ * The 'reverse' parameter may be used to find a reverse
+ * mapping, i.e. the pci address in a outbound window can be
+ * found if the respective vme address is known etc.
+ *
+ * RETURNS: translated address in *pbusAdrs / *plocalAdrs
+ *
+ * 0: success
+ * -1: address/modifier not found in any bridge port
+ * -2: invalid modifier
+ */
+
+int
+vmeTsi148XlateAddrXX(
+ BERegister *base, /* TSI 148 base address */
+ int outbound, /* look in the outbound windows */
+ int reverse, /* reverse mapping; for outbound ports: map local to VME */
+ unsigned long as, /* address space */
+ unsigned long aIn, /* address to look up */
+ unsigned long *paOut/* where to put result */
+ );
+
+int
+vmeTsi148XlateAddr(
+ int outbound, /* look in the outbound windows */
+ int reverse, /* reverse mapping; for outbound: map local to VME */
+ unsigned long as, /* address space */
+ unsigned long aIn, /* address to look up */
+ unsigned long *paOut/* where to put result */
+ );
+
+/* Print the current configuration of all outbound ports to
+ * f (stdout if NULL)
+ */
+
+void
+vmeTsi148OutboundPortsShowXX(BERegister *base, FILE *f);
+
+void
+vmeTsi148OutboundPortsShow(FILE *f);
+
+/* Print the current configuration of all inbound ports to
+ * f (stdout if NULL)
+ */
+
+void
+vmeTsi148InboundPortsShowXX(BERegister *base, FILE *f);
+
+void
+vmeTsi148InboundPortsShow(FILE *f);
+
+
+/* Disable all in- or out-bound ports, respectively */
+void
+vmeTsi148DisableAllInboundPortsXX(BERegister *base);
+
+void
+vmeTsi148DisableAllInboundPorts(void);
+
+void
+vmeTsi148DisableAllOutboundPortsXX(BERegister *base);
+
+void
+vmeTsi148DisableAllOutboundPorts(void);
+
+# define TSI_VEAT_VES (1<<31)
+# define TSI_VEAT_VEOF (1<<30)
+# define TSI_VEAT_VESCL (1<<29)
+# define TSI_VEAT_2eOT (1<<21)
+# define TSI_VEAT_2eST (1<<20)
+# define TSI_VEAT_BERR (1<<19)
+# define TSI_VEAT_LWORD (1<<18)
+# define TSI_VEAT_WRITE (1<<17)
+# define TSI_VEAT_IACK (1<<16)
+# define TSI_VEAT_DS1 (1<<15)
+# define TSI_VEAT_DS0 (1<<14)
+# define TSI_VEAT_AM(v) (((v)>>8)&63)
+# define TSI_VEAT_XAM(v) ((v)&255)
+
+/* Check and clear the error (AKA 'exception') register.
+ * Note that the Tsi148 does *not* propagate VME bus errors of any kind to
+ * the PCI status register and hence this routine (or registering an ISR
+ * to the TSI_VERR_INT_VEC) is the only means for detecting a bus error.
+ *
+ * RETURNS:
+ * 0 if no error has occurred since this routine was last called.
+ * Contents of the 'VEAT' register (bit definitions as above)
+ * otherwise.
+ * If a non-NULL 'paddr' argument is provided then the 64-bit error
+ * address is stored in *paddr (only if return value is non-zero).
+ *
+ * SIDE EFFECTS: this routine clears the error attribute register, allowing
+ * for future errors to be latched.
+ */
+unsigned long
+vmeTsi148ClearVMEBusErrorsXX(BERegister *base, unsigned long long *paddr);
+
+unsigned long
+vmeTsi148ClearVMEBusErrors(unsigned long long *paddr);
+
+/* VME Interrupt Handler functionality */
+
+/* we dont use the current RTEMS/BSP interrupt API for the
+ * following reasons:
+ *
+ * - RTEMS/BSP API does not pass an argument to the ISR :-( :-(
+ * - no separate vector space for VME vectors. Some vectors would
+ * have to overlap with existing PCI/ISA vectors.
+ * - RTEMS/BSP API allocates a structure for every possible vector
+ * - the irq_on(), irq_off() functions add more bloat than helping.
+ * They are (currently) only used by the framework to disable
+ * interrupts at the device level before removing a handler
+ * and to enable interrupts after installing a handler.
+ * These operations may as well be done by the driver itself.
+ *
+ * Hence, we maintain our own (VME) handler table and hook our PCI
+ * handler into the standard RTEMS/BSP environment. Our handler then
+ * dispatches VME interrupts.
+ */
+
+typedef void (*VmeTsi148ISR) (void *usrArg, unsigned long vector);
+
+/* install a handler for a VME vector
+ * RETURNS 0 on success, nonzero on failure.
+ */
+int
+vmeTsi148InstallISR(unsigned long vector, VmeTsi148ISR handler, void *usrArg);
+
+/* remove a handler for a VME vector. The vector and usrArg parameters
+ * must match the respective parameters used when installing the handler.
+ * RETURNS 0 on success, nonzero on failure.
+ */
+int
+vmeTsi148RemoveISR(unsigned long vector, VmeTsi148ISR handler, void *usrArg);
+
+/* query for the currently installed ISR and usr parameter at a given vector
+ * RETURNS: ISR or 0 (vector too big or no ISR installed)
+ */
+VmeTsi148ISR
+vmeTsi148ISRGet(unsigned long vector, void **parg);
+
+/* utility routines to enable/disable a VME IRQ level
+ *
+ * To enable/disable the internal interrupt sources (special vectors above)
+ * pass a vector argument > 255.
+ *
+ * RETURNS 0 on success, nonzero on failure
+ */
+int
+vmeTsi148IntEnable(unsigned int level);
+
+int
+vmeTsi148IntDisable(unsigned int level);
+
+/* Check if an interrupt level or internal source is enabled:
+ *
+ * 'level': VME level 1..7 or internal special vector > 255
+ *
+ * RETURNS: value > 0 if interrupt is currently enabled,
+ * zero if interrupt is currently disabled,
+ * -1 on error (invalid argument).
+ */
+
+int
+vmeTsi148IntIsEnabled(unsigned int level);
+
+/* Set IACK width (1,2, or 4 bytes) for a given interrupt level.
+ *
+ * 'width' arg may be 0,1,2 or 4. If zero, the currently active
+ * value is returned but not modified.
+ *
+ * RETURNS: old width or -1 if invalid argument.
+ */
+
+int
+vmeTsi148SetIackWidth(int level, int width);
+
+/* Change the routing of IRQ 'level' to 'pin'.
+ * If the BSP connects more than one of the four
+ * physical interrupt lines from the tsi148 to
+ * the board's PIC then you may change the physical
+ * line a given 'level' is using. By default,
+ * all 7 VME levels use the first wire (pin==0) and
+ * all internal sources use the (optional) second
+ * wire (pin==1) [The driver doesn't support more than
+ * four wires].
+ * This feature is useful if you want to make use of
+ * different hardware priorities of the PIC. Let's
+ * say you want to give IRQ level 7 the highest priority.
+ * You could then give 'pin 0' a higher priority (at the
+ * PIC) and 'pin 1' a lower priority and issue.
+ *
+ * for ( i=1; i<7; i++ ) vmeTsi148IntRoute(i, 1);
+ *
+ * PARAMETERS:
+ * 'level' : VME interrupt level '1..7' or one of
+ * the internal sources. Pass the internal
+ * source's vector number (>=256).
+ * 'pin' : a value of 0 routes the requested IRQ to
+ * the first line registered with the manager,
+ * a value of 1 routes it to the second wire
+ * etc.
+ *
+ * RETURNS: 0 on success, nonzero on error (invalid arguments)
+ *
+ * NOTES: - DONT change the tsi148 'map' registers
+ * directly. The driver caches routing internally.
+ * - support for the extra wires (beyond wire #0) is
+ * board dependent. If the board only provides
+ * a single physical wire from the tsi148 to
+ * the PIC then the feature might not be available.
+ */
+int
+vmeTsi148IntRoute(unsigned int level, unsigned int pin);
+
+/* Raise a VME Interrupt at 'level' and respond with 'vector' to a
+ * handler on the VME bus. (The handler could be a different board
+ * or the tsi148 itself.
+ *
+ * Note that you could install a interrupt handler at TSI_VME_SW_IACK_INT_VEC
+ * to be notified of an IACK cycle having completed.
+ *
+ * This routine is mainly FOR TESTING.
+ *
+ * NOTES:
+ * - the VICR register is modified.
+ * - NO MUTUAL EXCLUSION PROTECTION (reads VICR, modifies then writes back).
+ * If several users need access to VICR it is their responsibility to serialize access.
+ *
+ * Arguments:
+ * 'level': interrupt level, 1..7
+ * 'vector': vector number (0..255) that the tsi148 puts on the bus in response to
+ * an IACK cycle.
+ *
+ * RETURNS:
+ * 0: Success
+ * -1: Invalid argument (level not 1..7, vector >= 256)
+ * -2: Interrupt 'level' already asserted (maybe nobody handles it).
+ * You can manually clear it be setting the IRQC bit in
+ * VICR. Make sure really nobody responds to avoid spurious
+ * interrupts (consult tsi148 docs).
+ */
+
+int
+vmeTsi148IntRaiseXX(BERegister *base, int level, unsigned vector);
+
+int
+vmeTsi148IntRaise(int level, unsigned vector);
+
+/* Loopback test of the VME interrupt subsystem.
+ * - installs ISRs on 'vector' and on TSI_VME_SW_IACK_INT_VEC
+ * - asserts VME interrupt 'level'
+ * - waits for both interrupts: 'ordinary' VME interrupt of 'level' and
+ * IACK completion interrupt ('special' vector TSI_VME_SW_IACK_INT_VEC).
+ *
+ * NOTES:
+ * - make sure no other handler responds to 'level'.
+ * - make sure no ISR is installed on both vectors yet.
+ * - ISRs installed by this routine are removed after completion.
+ * - no concurrent access protection of all involved resources
+ * (levels, vectors and registers [see vmeTsi148IntRaise()])
+ * is implemented.
+ * - this routine is intended for TESTING (when implementing new BSPs etc.).
+ * - one RTEMS message queue is temporarily used (created/deleted).
+ *
+ * RETURNS:
+ * 0: Success.
+ * -1: Invalid arguments.
+ * 1: Test failed (outstanding interrupts).
+ * rtems_status_code: Failed RTEMS directive.
+ */
+
+int
+vmeTsi148IntLoopbackTst(int level, unsigned vector);
+
+/* use these special vectors to connect a handler to the
+ * tsi148 specific interrupts (such as "DMA done", SW or
+ * error irqs etc.)
+ * NOTE: The wrapper clears all status LINT bits (except
+ * for regular VME irqs). Also note that it is the user's
+ * responsibility to enable the necessary interrupts in
+ * LINT_EN
+ *
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * DO NOT CHANGE THE ORDER OF THESE VECTORS - THE DRIVER
+ * DEPENDS ON IT
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * Deliberately, these vectors match the universe driver's
+ */
+/* 256 no VOWN interrupt */
+#define TSI_DMA_INT_VEC 257
+#define TSI_LERR_INT_VEC 258
+#define TSI_VERR_INT_VEC 259
+/* 260 is reserved */
+#define TSI_VME_SW_IACK_INT_VEC 261
+/* 262 no PCI SW IRQ */
+#define TSI_SYSFAIL_INT_VEC 263
+#define TSI_ACFAIL_INT_VEC 264
+#define TSI_MBOX0_INT_VEC 265
+#define TSI_MBOX1_INT_VEC 266
+#define TSI_MBOX2_INT_VEC 267
+#define TSI_MBOX3_INT_VEC 268
+#define TSI_LM0_INT_VEC 269
+#define TSI_LM1_INT_VEC 270
+#define TSI_LM2_INT_VEC 271
+#define TSI_LM3_INT_VEC 272
+
+/* New vectors; only on TSI148 */
+#define TSI_VIES_INT_VEC 273
+#define TSI_DMA1_INT_VEC 274
+
+#define TSI_NUM_INT_VECS 275
+
+/* the tsi148 interrupt handler is capable of routing all sorts of
+ * (VME) interrupts to 4 different lines (some of) which may be hooked up
+ * in a (board specific) way to a PIC.
+ *
+ * This driver initially supports at most two lines (i.e., if the user
+ * doesn't re-route anything). By default, it routes the
+ * 7 VME interrupts to the main line and optionally, it routes the 'special'
+ * interrupts generated by the tsi148 itself (DMA done, SW irq etc.)
+ * to a second line. If no second line is available, all IRQs are routed
+ * to the main line.
+ *
+ * The routing of interrupts to the two lines can be modified (using
+ * the vmeTsi148IntRoute() call - see above - i.e., to make use of
+ * different hardware priorities and/or more physically available lines.
+ *
+ * Because the driver has no way to figure out which lines are actually
+ * wired to the PIC, this information has to be provided when installing
+ * the manager.
+ *
+ * Hence the manager sets up routing VME interrupts to 1 or 2 tsi148
+ * OUTPUTS. However, it must also be told to which PIC INPUTS they
+ * are wired.
+ * Optionally, the first PIC input line can be read from PCI config space
+ * but the second must be passed to this routine. Note that the info read
+ * from PCI config space is wrong for some boards!
+ *
+ * PARAMETERS:
+ * shared: use the BSP_install_rtems_shared_irq_handler() instead
+ * of BSP_install_rtems_irq_handler(). Use this if the PIC
+ * line is used by other devices, too.
+ * CAVEAT: shared interrupts need RTEMS workspace, i.e., the
+ * VME interrupt manager can only be installed
+ * *after workspace is initialized* if 'shared' is nonzero
+ * (i.e., *not* from bspstart()).
+ * tsi_pin_0: to which output pin (of the tsi148) should the 7
+ * VME irq levels be routed.
+ * pic_pin_0: specifies to which PIC input the 'main' output is
+ * wired on your board. If passed a value < 0, the driver
+ * reads this information from PCI config space ("IRQ line").
+ * ... : up to three additional tsi_pin/pic_pin pairs can be
+ * specified if your board provides more physical wires.
+ * In any case must the varargs list be terminated by '-1'.
+ *
+ * RETURNS: 0 on success, -1 on failure.
+ *
+ */
+int
+vmeTsi148InstallIrqMgrAlt(int shared, int tsi_pin0, int pic_pin0, ...);
+
+int
+vmeTsi148InstallIrqMgrVa(int shared, int tsi_pin0, int pic_pin0, va_list ap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c b/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c
index aa4dea6460..6612bcef11 100644
--- a/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c
+++ b/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c
@@ -5,6 +5,228 @@
* Nov 2000, Oct 2001, Jan 2002
*/
+#if 0
+/*
+ * $Log$
+ * Revision 1.55 2005/10/25 20:07:18 till
+ * - check #if BSP_SHARED_HANDLER_SUPPORT > 0 before using the shared irq API.
+ * Bail to use non-shared interrupts if the BSP doesn't support the shared API
+ * (giving a warning).
+ *
+ * Revision 1.54 2005/10/25 19:52:26 till
+ * - Loopback test: ISR check return status of message_queue_send and assumes
+ * there's a IRQ storm on error (queue full). The interrupt level is disabled
+ * if this occurs.
+ *
+ * Revision 1.53 2005/10/21 17:52:08 till
+ * - reverted default definition of PCI_MEM_BASE and introduced a short explanation
+ *
+ * Revision 1.52 2005/10/21 17:46:37 till
+ * - use 0 if PCI_MEM_BASE is undefined
+ *
+ * Revision 1.51 2005/10/14 19:04:51 till
+ * - removed shared irq handler kludge for mvme5500 bsp -- this *really* doesn't belong here...
+ *
+ * Revision 1.50 2005/10/10 21:57:59 till
+ * - xxIrqMgrInstalled must be set prior to calling xxIntRoute() because
+ * xxIntRoute() now checks the 'installed' flag.
+ *
+ * Revision 1.49 2005/10/10 19:45:08 till
+ * - enable PCI busmaster and memory access from the 'find' routine
+ *
+ * Revision 1.48 2005/10/10 19:35:47 till
+ * - added 'Va' version of IrqMgrInstall
+ *
+ * Revision 1.47 2005/10/10 18:04:50 till
+ * - maintain copy of vme_am_defs.h in driver headers (backwards compat, sigh...)
+ *
+ * Revision 1.46 2005/10/10 17:52:58 strauman
+ * - separated basic AM definitions into separate file
+ *
+ * Revision 1.45 2005/10/10 17:45:58 strauman
+ * - added check for initialized 'base' to tsi driver
+ *
+ * Revision 1.44 2005/10/10 06:24:15 till
+ * - if vector is unknown, the message number (and not the vector number) needs to
+ * be printed (IntLoopbackTest()).
+ *
+ * Revision 1.43 2005/10/09 00:28:20 till
+ * - added vmeUniverseIntRaise() and vmeUniverseIntLoopbackTest() routines
+ *
+ * Revision 1.42 2005/10/08 23:48:03 till
+ * - 'special' level numbers are now (almost - except for VOWN) aligned
+ * with bitmask; removed extra shift for levels >= SW_IACK.
+ * - use helper function to convert level -> bitmask. Also used by new feature
+ * to let vmeUniverseIntEnable/Disable handle 'special/internal' interrupts
+ * as well.
+ * - undid bad change. The level sensitive VME interrupts must really be cleared only
+ * *after* calling the user handler to prevent the chip to ACK another IRQ at the level
+ * currently processed.
+ * - added printk() statements in error-branches of ISR (no handler installed or bus error
+ * during IACK.
+ * - added kludgy weak alias that maps BSP_install_rtems_shared_irq_handler
+ * to BSP_install_rtems_irq_handler -- the mvme5500 bsp doesn't implement the shared handlers...
+ * - bugfix: vmeUniverseIntRoute 'pin' argument is actually the slot # in the wire table,
+ * so the 'special' pin has to be 1 not universe_wire[1].
+ * - added vmeUniverseIntIsEnabled() to check current setting.
+ *
+ * Revision 1.41 2005/10/06 21:41:57 till
+ * - FIX: protect handler table by switching interrupt off
+ * - FIX: skip vector # for nonexistent special IRQ
+ * - upgrade: new InstallMgr API allows for requesting shared irqs and more pin/line pairs
+ * - only do EOI if BSP defines macro
+ * - dont add PCI_LOWEST_OFFSET anymore to irq line info
+ *
+ * Revision 1.40 2005/06/04 05:47:00 till
+ * - added comment about STAT_ID pecularity
+ *
+ * Revision 1.39 2005/05/06 00:03:14 till
+ * - added interface to secondary devices (pass base addr of universe)
+ * - made the 'ResetBus' routine public
+ *
+ * Revision 1.38 2005/04/26 00:02:47 till
+ * - fix in inline assembly
+ *
+ * Revision 1.37 2004/11/08 22:43:09 till
+ * - fix: zero value of specialIrqUnivOut is legal.
+ *
+ * Revision 1.36 2003/03/21 20:54:18 till
+ * - modified 'printf()' formats to be compliant with the
+ * cpukit 'printk()' implementation and use the latter
+ * prior to stdio/libc being initialized.
+ *
+ * Revision 1.35 2003/03/06 20:52:44 till
+ * - fix lazy init bug reported by Kate Feng
+ * - rename misspelled dccp/DCCP to dcpp/DCPP ( :-( )
+ * - added 'packed' attribute (probably not really necessary)
+ *
+ * Revision 1.34 2003/02/10 23:20:05 till
+ * - added some macro magic to make porting easier (ppcn_60x BSP in mind)
+ * - made mgrInstalled public (vmeUniverseIrqMgrInstalled) so BSPs can
+ * supply their own versions of the mgrInstall() routine.
+ * - added READMEs to CVS
+ *
+ * Revision 1.33.2.1 2003/02/10 23:01:40 till
+ * - added some macro magic to make porting easier (ppcn_60x BSP in mind)
+ * - made mgrInstalled public (vmeUniverseIrqMgrInstalled) so BSPs can
+ * supply their own versions of the mgrInstall() routine.
+ *
+ * Revision 1.32 2002/09/05 02:50:41 till
+ * - use k_vsprintf(), not vsprintf() during early boot (RTEMS)
+ *
+ * Revision 1.31 2002/08/16 23:35:15 strauman
+ * - fixed typos
+ *
+ * Revision 1.30 2002/08/16 22:53:37 till
+ * - made address translation more generic; allow to look for reverse mappings
+ * also.
+ * - removed vmeUniverseLocalToBus() and vmeUniverseBusToLocal() and made
+ * vmeUniverseXlateAddr() public instead.
+ *
+ * Revision 1.29 2002/08/16 22:16:25 till
+ * - tweak U2SPEC (fix Joerger vtr10012_8 problem) the same
+ * way the synergy BSP does (rev 2 chips only)
+ *
+ * Revision 1.28 2002/08/15 23:16:01 till
+ * - bugfix: vmeUniverseISRGet() dereferenced a possibly NULL pointer
+ *
+ * Revision 1.25 2002/07/19 05:18:40 till
+ * - CVS log: again a problem. Cannot embed /_* *_/ comments in the log
+ * because the log has to be commented as a whole. I had tried
+ * #if 0 #endif - doesn't work because cpp expands char constants???
+ * WHAT A NUISANCE
+ *
+ * Revision 1.24 2002/07/19 02:44:14 till
+ * - added a new parameter to the IRQ manager install routine:
+ * the main interrupt's line can now also be specified (reading
+ * from PCI config does not always work - some boards don't
+ * have correct information there - PMC's will have a similar
+ * problem, though!)
+ *
+ * Revision 1.21 2002/04/11 06:54:48 till
+ * - silenced message about 'successfully configured a port'
+ *
+ * Revision 1.20 2002/03/27 21:14:50 till
+ * - fix: handler table holds pointers, so hdlrTbl[vector]->usrData etc.
+ * not hdlrTbl[vector].usrData...
+ *
+ * Revision 1.19 2002/03/09 00:14:36 till
+ * - added vmeUniverseISRGet() to retrieve the currently installed
+ * ISR for a given vector
+ * - swapped the argument order for ISRs to (usrarg, vector)
+ *
+ * Revision 1.18 2002/02/07 19:53:48 till
+ * - reverted back to publish base_addr/irq_line as variables rather than
+ * through functions: the irq_line is read by the interrupt dispatcher...
+ *
+ * Revision 1.17 2002/01/24 08:28:10 till
+ * - initialize driver when reading base address or irq line.
+ * however, this requires the pci driver to be working already.
+ *
+ * Revision 1.16 2002/01/24 08:21:48 till
+ * - replaced public global vars for base address/irq line by routines.
+ *
+ * Revision 1.15 2002/01/23 06:15:30 till
+ * - changed master port data width to 64 bit.
+ * 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.
+ * ????????
+ *
+ * Revision 1.14 2002/01/11 19:30:54 till
+ * - added more register defines to header
+ * - completed vmeUniverseReset
+ *
+ * Revision 1.13 2002/01/11 05:06:18 till
+ * - fixed VMEISR failing to check (lint_stat & msk) when determining
+ * the highes level...
+ * - tested interrupt handling & nesting. Seems to work.
+ *
+ * Revision 1.12 2002/01/11 02:25:55 till
+ * - added interrupt manager
+ *
+ * Revision 1.11 2002/01/08 03:59:52 till
+ * - vxworks always defines _LITTLE_ENDIAN, fixed the conditionals
+ * so it should work on __vxworks and on __rtems__ now.
+ * - rtems uprintf wrapper reverts to printk if stdio is not yet
+ * initialized (uses _impure_ptr->__sdidinit)
+ * - tested bus address translation utility routines
+ *
+ * Revision 1.9 2002/01/05 02:36:32 till
+ * - added vmeUniverseBusToLocalAdrs / vmeUniverseLocalToBusAdrs for address
+ * space translations.
+ * - include bsp.h under rtems to hack around the libcpu/powerpc/shared/io.h
+ * #define _IO_BASE & friends problem.
+ *
+ * Revision 1.8 2002/01/04 04:12:51 till
+ * - changed some rtems/pci related names
+ *
+ * Revision 1.7 2002/01/04 03:06:30 till
+ * - added further register definitions
+ *
+ * Revision 1.6 2001/12/20 04:42:44 till
+ * - fixed endianness stuff; theoretically, PPC could be LITTLE_ENDIAN...
+ *
+ * Revision 1.4 2001/12/19 01:59:02 till
+ * - started adding interrupt stuff
+ * - private implementation of PCI scanning if necessary
+ *
+ * Revision 1.3 2001/07/27 22:22:51 till
+ * - added more DMA support routines and defines to include file
+ * - xxxPortsShow can now print to a given file descriptor argument
+ *
+ * Revision 1.2 2001/07/26 18:06:13 till
+ * - ported to RTEMS
+ * - fixed a couple of wrong pointer calculations.
+ *
+ * Revision 1.1.1.1 2001/07/12 23:15:19 till
+ * - cvs import
+ */
+#endif
+
#include <stdio.h>
#include <stdarg.h>
#include "vmeUniverse.h"
@@ -47,30 +269,37 @@
#define UNIV_SCTL_AM_MASK (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
-/* we rely on a vxWorks definition here */
-#define VX_AM_SUP 4
-
#ifdef __rtems__
#include <stdlib.h>
#include <rtems/bspIo.h> /* printk */
+#include <rtems/error.h>
#include <bsp/pci.h>
#include <bsp.h>
/* allow the BSP to override the default routines */
#ifndef BSP_PCI_FIND_DEVICE
-#define BSP_PCI_FIND_DEVICE 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
+#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
+#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 unsigned int pci_ulong;
-#define PCI_TO_LOCAL_ADDR(memaddr) \
- ((pci_ulong)(memaddr) + PCI_MEM_BASE)
+#define PCI_TO_LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)
+
#elif defined(__vxworks)
typedef unsigned long pci_ulong;
@@ -89,6 +318,21 @@ typedef unsigned long pci_ulong;
volatile LERegister *vmeUniverse0BaseAddr=0;
int vmeUniverse0PciIrqLine=-1;
+#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 *
@@ -131,7 +375,7 @@ WRITE_LE(
#warning "SYNC instruction unknown for this architecture"
#endif
-/* registers should be mapped to guarded, non-cached memory; hence
+/* 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.
*/
@@ -174,10 +418,11 @@ return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off));
}
#define PORT_UNALIGNED(addr,port) \
- ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
+ ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
-#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
+#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
+
#if defined(__rtems__) && 0
static int
uprintk(char *fmt, va_list ap)
@@ -197,6 +442,7 @@ char buf[200];
}
#endif
+
/* private printing wrapper */
static void
uprintf(FILE *f, char *fmt, ...)
@@ -211,7 +457,7 @@ va_list ap;
* to a buffer.
*/
vprintk(fmt,ap);
- } else
+ } else
#endif
{
vfprintf(f,fmt,ap);
@@ -226,6 +472,7 @@ vmeUniverseFindPciBase(
)
{
int bus,dev,fun;
+unsigned short wrd;
pci_ulong busaddr;
unsigned char irqline;
@@ -249,10 +496,12 @@ unsigned char irqline;
if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline))
return -1;
- else
- vmeUniverse0PciIrqLine = irqline;
- return 0;
+ /* 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
@@ -305,7 +554,7 @@ unsigned long mode=0;
default:
return -1;
}
- if (address_space & VX_AM_SUP)
+ if ( VME_AM_IS_SUP(address_space) )
mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER);
*pmode = mode;
return 0;
@@ -324,6 +573,7 @@ unsigned long cntrl;
static int
cfgUniversePort(
+ volatile LERegister *base,
unsigned long ismaster,
unsigned long port,
unsigned long address_space,
@@ -331,11 +581,12 @@ cfgUniversePort(
unsigned long local_address,
unsigned long length)
{
-#define base vmeUniverse0BaseAddr
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");
@@ -438,7 +689,7 @@ unsigned long mode=0;
*/
if (ismaster)
mode |= UNIV_MCTL_EN | UNIV_MCTL_PWEN | UNIV_MCTL_VDW64 | UNIV_MCTL_VCT;
- else
+ else
mode |= UNIV_SCTL_EN | UNIV_SCTL_PWEN | UNIV_SCTL_PREN;
#ifdef TSILL
@@ -463,7 +714,6 @@ unsigned long mode=0;
#endif
}
return 0;
-#undef base
}
static int
@@ -518,7 +768,7 @@ showUniversePort(
cntrl&UNIV_MCTL_PGM ? "Pgm" : "Dat",
cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr");
} else {
- uprintf(f,"%s %s %s %s",
+ uprintf(f,"%s %s %s %s",
cntrl&UNIV_SCTL_PGM ? "Pgm," : " ",
cntrl&UNIV_SCTL_DAT ? "Dat," : " ",
cntrl&UNIV_SCTL_SUPER ? "Sup," : " ",
@@ -597,20 +847,17 @@ unsigned long cntrl, start, bound, offst, mask, x;
return 0;
}
+
static int
-mapOverAll(int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
+mapOverAll(volatile LERegister *base, int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
{
-#define base vmeUniverse0BaseAddr
volatile LERegister *rptr;
unsigned long port;
int rval;
- /* get the universe base address */
- if (!base && vmeUniverseInit()) {
- uprintf(stderr,"unable to find the universe in pci config space\n");
- return -1;
- }
- rptr = (base +
+ CHECK_DFLT_BASE(base);
+
+ rptr = (base +
(ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister));
#undef TSILL
#ifdef TSILL
@@ -625,27 +872,27 @@ int rval;
/* only rev. 2 has 8 ports */
if (UNIV_REV(base)<2) return -1;
- rptr = (base +
+ 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;
-#undef base
}
static void
-showUniversePorts(int ismaster, FILE *f)
+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(ismaster,showUniversePort,f);
+ mapOverAll(base,ismaster,showUniversePort,f);
}
int
-vmeUniverseXlateAddr(
+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 */
@@ -659,11 +906,25 @@ XlatRec l;
l.address = aIn;
l.reverse = reverse;
/* map result -1/0/1 to -2/-1/0 with 0 on success */
- rval = mapOverAll(master,xlatePort,(void*)&l) - 1;
+ rval = mapOverAll(base,master,xlatePort,(void*)&l) - 1;
*paOut = l.address;
return rval;
}
+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)
{
@@ -693,6 +954,7 @@ vmeUniverseReset(void)
/* disable VME bus image of VME CSR */
vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);
+
/* I had problems with a Joerger vtr10012_8 card who would
* only be accessible after tweaking the U2SPEC register
* (the t27 parameter helped).
@@ -719,7 +981,7 @@ vmeUniverseReset(void)
vmeUniverseDisableAllSlaves();
vmeUniverseDisableAllMasters();
-
+
vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR);
/* clear interrupt status bits */
@@ -746,9 +1008,11 @@ int
vmeUniverseInit(void)
{
int rval;
- if ((rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr))) {
+ 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);
}
@@ -756,15 +1020,41 @@ int rval;
}
void
+vmeUniverseMasterPortsShowXX(volatile LERegister *base, FILE *f)
+{
+ showUniversePorts(base,1,f);
+}
+
+void
vmeUniverseMasterPortsShow(FILE *f)
{
- showUniversePorts(1,f);
+ DFLT_BASE;
+ showUniversePorts(base,1,f);
+}
+
+void
+vmeUniverseSlavePortsShowXX(volatile LERegister *base, FILE *f)
+{
+ showUniversePorts(base,0,f);
}
void
vmeUniverseSlavePortsShow(FILE *f)
{
- showUniversePorts(0,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
@@ -775,7 +1065,20 @@ vmeUniverseMasterPortCfg(
unsigned long local_address,
unsigned long length)
{
- return cfgUniversePort(1,port,address_space,vme_address,local_address,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
@@ -786,29 +1089,44 @@ vmeUniverseSlavePortCfg(
unsigned long local_address,
unsigned long length)
{
- return cfgUniversePort(0,port,address_space,vme_address,local_address,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)
{
- mapOverAll(0,disableUniversePort,0);
+ DFLT_BASE;
+ mapOverAll(base,0,disableUniversePort,0);
+}
+
+void
+vmeUniverseDisableAllMastersXX(volatile LERegister *base)
+{
+ mapOverAll(base,1,disableUniversePort,0);
}
void
vmeUniverseDisableAllMasters(void)
{
- mapOverAll(1,disableUniversePort,0);
+ DFLT_BASE;
+ mapOverAll(base,1,disableUniversePort,0);
}
int
-vmeUniverseStartDMA(
+vmeUniverseStartDMAXX(
+ volatile LERegister *base,
unsigned long local_addr,
unsigned long vme_addr,
unsigned long count)
{
-
- if (!vmeUniverse0BaseAddr && vmeUniverseInit()) return -1;
if ((local_addr & 7) != (vme_addr & 7)) {
uprintf(stderr,"vmeUniverseStartDMA: misaligned addresses\n");
return -1;
@@ -816,7 +1134,7 @@ vmeUniverseStartDMA(
{
/* help the compiler allocate registers */
- register volatile LERegister *b=vmeUniverse0BaseAddr;
+ register volatile LERegister *b=base;;
register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs;
dgcs=READ_LE(b, dgcsoff);
@@ -840,6 +1158,25 @@ vmeUniverseStartDMA(
return 0;
}
+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 vmeUniverseStartDMAXX(base, local_addr, vme_addr, count);
+}
+
+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)
{
@@ -849,12 +1186,26 @@ unsigned long 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)
@@ -864,7 +1215,7 @@ register unsigned long *p=ptr+num;
__asm__ __volatile__(
"lwzu 0, -4(%0)\n"
"stwbrx 0, 0, %0\n"
- : "=r"(p) : "r"(p) : "r0"
+ : "=r"(p) : "0"(p) : "r0"
);
#elif defined(__rtems__)
p--; st_le32(p, *p);
@@ -875,6 +1226,56 @@ register unsigned long *p=ptr+num;
#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);
+}
+
+
/* RTEMS interrupt subsystem */
#ifdef __rtems__
@@ -889,30 +1290,112 @@ UniverseIRQEntryRec_ {
static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};
int vmeUniverseIrqMgrInstalled=0;
-static int vmeIrqUnivOut=-1;
-static int specialIrqUnivOut=-1;
+
+/* 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)
{
- if (vector>=UNIV_NUM_INT_VECS ||
- ! universeHdlTbl[vector])
+unsigned long flags;
+VmeUniverseISR rval = 0;
+volatile UniverseIRQEntry *pe = universeHdlTbl + vector;
+
+ if ( vector>=UNIV_NUM_INT_VECS || ! *pe )
return 0;
- if (parg)
- *parg=universeHdlTbl[vector]->usrData;
- return universeHdlTbl[vector]->isr;
+
+ 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(rtems_irq_hdl_param handle)
+universeSpecialISR(unsigned long status)
{
register UniverseIRQEntry ip;
register unsigned vec;
-register unsigned long status;
+register unsigned long s;
- status=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
+ /* handle all LINT bits except for the 'normal' VME interrupts */
- /* scan 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;
@@ -925,16 +1408,11 @@ register unsigned long status;
* The initial right shift brings the DMA bit into position 0;
* the loop is left early if there are no more bits set.
*/
- for (status>>=8; status; status>>=1) {
+ for ( s = status>>8; s; s >>= 1) {
vec++;
- if ((status&1) && (ip=universeHdlTbl[vec]))
+ if ( (s&1) && (ip=universeHdlTbl[vec]) )
ip->isr(ip->usrData,vec);
}
- /* clear all special interrupts */
- vmeUniverseWriteReg(
- ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1),
- UNIV_REGOFF_LINT_STAT
- );
/*
* clear our line in the VINT_STAT register
@@ -950,14 +1428,14 @@ register unsigned long status;
* like this:
*
*
- * VME IRQ ------
- * & ----- LINT_STAT ----
+ * VME IRQ ------
+ * & ----- LINT_STAT ----
* | & ---------- PCI LINE
* | |
- * | |
- * LINT_EN ---------------------------
+ * | |
+ * LINT_EN ---------------------------
*
- * I.e.
+ * 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.
@@ -978,34 +1456,60 @@ register unsigned long status;
*/
static void
-universeVMEISR(rtems_irq_hdl_param handle)
+universeVMEISR(rtems_irq_hdl_param arg)
{
-UniverseIRQEntry ip;
-unsigned long lvl,msk,lintstat,linten,status;
+int pin = (int)arg;
+UniverseIRQEntry ip;
+unsigned long msk,lintstat,status;
+int lvl;
+#ifdef BSP_PIC_DO_EOI
+unsigned long linten;
+#endif
/* determine the highest priority IRQ source */
- lintstat=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
+ 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;
}
- if (!lvl) {
+#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(NULL);
+ universeSpecialISR( lintstat & SPECIAL_IRQ_MSK );
- /*
+ /*
* let the pic end this cycle
*/
- BSP_PIC_DO_EOI;
+ if ( 0 == pin )
+ BSP_PIC_DO_EOI;
return;
}
linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
- /* mask this and all lower levels */
+ /* mask this and all lower levels that are routed to the same pin */
vmeUniverseWriteReg(
- linten & ~((msk<<1)-UNIV_LINT_STAT_VIRQ1),
+ linten & ~( ((msk<<1)-UNIV_LINT_STAT_VIRQ1) & wire_mask[pin]),
UNIV_REGOFF_LINT_EN
);
@@ -1013,7 +1517,9 @@ unsigned long lvl,msk,lintstat,linten,status;
* cycle on the PCI bus, so higher level interrupts can be
* caught from now on...
*/
- BSP_PIC_DO_EOI;
+ if ( 0 == pin )
+ BSP_PIC_DO_EOI;
+#endif
/* get vector and dispatch handler */
status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2));
@@ -1021,15 +1527,18 @@ unsigned long lvl,msk,lintstat,linten,status;
if (status & UNIV_VIRQ_ERR) {
/* TODO: log error message - RTEMS has no logger :-( */
+ printk("vmeUniverse ISR: error read from STATID register; (level: %i) STATID: 0x%08x\n", lvl, status);
} else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
/* TODO: log error message - RTEMS has no logger :-( */
+ printk("vmeUniverse ISR: no handler installed for this vector; (level: %i) STATID: 0x%08x\n", lvl, status);
} else {
/* dispatch handler, it must clear the IRQ at the device */
ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
}
- /* clear this interrupt level */
+ /* 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...
@@ -1038,10 +1547,14 @@ unsigned long lvl,msk,lintstat,linten,status;
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)
@@ -1053,129 +1566,379 @@ 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))
+ BSP_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))
+ BSP_panic("unable to install vmeUniverse irq handler");
+#endif
+ } else {
+ if (!BSP_install_rtems_irq_handler(&aarrggh))
+ BSP_panic("unable to install vmeUniverse irq handler");
+ }
+}
+
int
-vmeUniverseInstallIrqMgr(int vmeOut,
- int vmeIrqPicLine,
- int specialOut,
- int specialIrqPicLine)
+vmeUniverseInstallIrqMgrAlt(int shared, int uni_pin0, int pic_pin0, ...)
+{
+int rval;
+va_list ap;
+ va_start(ap, pic_pin0);
+ rval = vmeUniverseInstallIrqMgrVa(shared, uni_pin0, pic_pin0, ap);
+ va_end(ap);
+ return rval;
+}
+
+int
+vmeUniverseInstallIrqMgrVa(int shared, int uni_pin0, int pic_pin0, va_list ap)
{
-rtems_irq_connect_data aarrggh;
+int i,j, specialPin, uni_pin[UNIV_NUM_WIRES+1], pic_pin[UNIV_NUM_WIRES];
+
+ if (vmeUniverseIrqMgrInstalled) return -4;
/* check parameters */
- if ((vmeIrqUnivOut=vmeOut) < 0 || vmeIrqUnivOut > 7) return -1;
- if ((specialIrqUnivOut=specialOut) > 7) return -2;
- if (specialOut >=0 && specialIrqPicLine < 0) return -3;
+
+ 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;
+ }
+ }
+
/* give them a chance to override buggy PCI info */
- if (vmeIrqPicLine >= 0) {
+ if ( pic_pin[0] >= 0 && vmeUniverse0PciIrqLine != pic_pin[0] ) {
uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
- vmeIrqPicLine);
- vmeUniverse0PciIrqLine=vmeIrqPicLine;
+ pic_pin[0]);
+ vmeUniverse0PciIrqLine=pic_pin[0];
}
- if (vmeUniverseIrqMgrInstalled) return -4;
-
- 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=universeVMEISR;
- aarrggh.name=vmeUniverse0PciIrqLine + BSP_PCI_IRQ0;
- if (!BSP_install_rtems_irq_handler(&aarrggh))
- BSP_panic("unable to install vmeUniverse irq handler");
- if (specialIrqUnivOut > 0) {
- /* install the special handler to a separate irq */
- aarrggh.hdl=universeSpecialISR;
- aarrggh.name=specialIrqPicLine + BSP_PCI_IRQ0;
- if (!BSP_install_rtems_irq_handler(&aarrggh))
- BSP_panic("unable to install vmeUniverse secondary irq handler");
- } else {
- specialIrqUnivOut = vmeIrqUnivOut;
+ 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(shared, universeVMEISR, pic_pin[i], i);
}
+
+ specialPin = uni_pin[1] >= 0 ? 1 : 0;
+
/* setup routing */
- vmeUniverseWriteReg(
- (UNIV_LINT_MAP0_VIRQ7(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ6(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ5(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ4(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ3(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ2(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VIRQ1(vmeIrqUnivOut) |
- UNIV_LINT_MAP0_VOWN(specialIrqUnivOut)
- ),
- UNIV_REGOFF_LINT_MAP0);
- vmeUniverseWriteReg(
- (UNIV_LINT_MAP1_ACFAIL(specialIrqUnivOut) |
- UNIV_LINT_MAP1_SYSFAIL(specialIrqUnivOut) |
- UNIV_LINT_MAP1_SW_INT(specialIrqUnivOut) |
- UNIV_LINT_MAP1_SW_IACK(specialIrqUnivOut) |
- UNIV_LINT_MAP1_VERR(specialIrqUnivOut) |
- UNIV_LINT_MAP1_LERR(specialIrqUnivOut) |
- UNIV_LINT_MAP1_DMA(specialIrqUnivOut)
- ),
- UNIV_REGOFF_LINT_MAP1);
+ /* 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;
+UniverseIRQEntry ip;
+unsigned long flags;
+volatile UniverseIRQEntry *pe;
if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
return -1;
- ip=universeHdlTbl[vector];
+ pe = universeHdlTbl + vector;
- if (ip || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
+ if (*pe || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
return -1;
+
ip->isr=hdl;
ip->usrData=arg;
- universeHdlTbl[vector]=ip;
+
+ 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;
+UniverseIRQEntry ip;
+unsigned long flags;
+volatile UniverseIRQEntry *pe;
if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
return -1;
- ip=universeHdlTbl[vector];
+ pe = universeHdlTbl + vector;
- if (!ip || ip->isr!=hdl || ip->usrData!=arg)
- return -1;
- universeHdlTbl[vector]=0;
+ 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)
{
- if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
- return -1;
- vmeUniverseWriteReg(
- (vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) |
- (UNIV_LINT_EN_VIRQ1 << (level-1))
- ),
- UNIV_REGOFF_LINT_EN);
- return 0;
+ return intDoEnDis(level, 1);
}
int
vmeUniverseIntDisable(unsigned int level)
{
- if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
- return -1;
- vmeUniverseWriteReg(
- (vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) &
- ~ (UNIV_LINT_EN_VIRQ1 << (level-1))
- ),
- UNIV_REGOFF_LINT_EN);
- return 0;
+ 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;
+unsigned32 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 )
+ 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
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h b/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h
index fc996dee04..29ff1e62db 100644
--- a/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h
+++ b/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h
@@ -13,28 +13,71 @@
#ifdef __vxworks
#include <vme.h>
#else
+
/* vxworks compatible addressing modes */
+
+#ifndef VME_AM_STD_SUP_ASCENDING
#define VME_AM_STD_SUP_ASCENDING 0x3f
-#define VME_AM_STD_SUP_PGM 0x3e
+#endif
+#ifndef VME_AM_STD_SUP_PGM
+#define VME_AM_STD_SUP_PGM 0x3e
+#endif
+#ifndef VME_AM_STD_USR_ASCENDING
#define VME_AM_STD_USR_ASCENDING 0x3b
-#define VME_AM_STD_USR_PGM 0x3a
-#define VME_AM_STD_SUP_DATA 0x3d
-#define VME_AM_STD_USR_DATA 0x39
+#endif
+#ifndef VME_AM_STD_USR_PGM
+#define VME_AM_STD_USR_PGM 0x3a
+#endif
+#ifndef VME_AM_STD_SUP_DATA
+#define VME_AM_STD_SUP_DATA 0x3d
+#endif
+#ifndef VME_AM_STD_USR_DATA
+#define VME_AM_STD_USR_DATA 0x39
+#endif
+#ifndef VME_AM_EXT_SUP_ASCENDING
#define VME_AM_EXT_SUP_ASCENDING 0x0f
-#define VME_AM_EXT_SUP_PGM 0x0e
+#endif
+#ifndef VME_AM_EXT_SUP_PGM
+#define VME_AM_EXT_SUP_PGM 0x0e
+#endif
+#ifndef VME_AM_EXT_USR_ASCENDING
#define VME_AM_EXT_USR_ASCENDING 0x0b
-#define VME_AM_EXT_USR_PGM 0x0a
-#define VME_AM_EXT_SUP_DATA 0x0d
-#define VME_AM_EXT_USR_DATA 0x09
-#define VME_AM_SUP_SHORT_IO 0x2d
-#define VME_AM_USR_SHORT_IO 0x29
-
-#define VME_AM_IS_SHORT(a) (((a) & 0xf0) == 0x20)
-#define VME_AM_IS_STD(a) (((a) & 0xf0) == 0x30)
-#define VME_AM_IS_EXT(a) (((a) & 0xf0) == 0x00)
+#endif
+#ifndef VME_AM_EXT_USR_PGM
+#define VME_AM_EXT_USR_PGM 0x0a
+#endif
+#ifndef VME_AM_EXT_SUP_DATA
+#define VME_AM_EXT_SUP_DATA 0x0d
+#endif
+#ifndef VME_AM_EXT_USR_DATA
+#define VME_AM_EXT_USR_DATA 0x09
+#endif
+#ifndef VME_AM_SUP_SHORT_IO
+#define VME_AM_SUP_SHORT_IO 0x2d
+#endif
+#ifndef VME_AM_USR_SHORT_IO
+#define VME_AM_USR_SHORT_IO 0x29
+#endif
+#ifndef VME_AM_IS_SHORT
+#define VME_AM_IS_SHORT(a) (((a) & 0xf0) == 0x20)
+#endif
+#ifndef VME_AM_IS_STD
+#define VME_AM_IS_STD(a) (((a) & 0xf0) == 0x30)
+#endif
+#ifndef VME_AM_IS_EXT
+#define VME_AM_IS_EXT(a) (((a) & 0xf0) == 0x00)
+#endif
+#ifndef VME_AM_IS_SUP
+#define VME_AM_IS_SUP(a) ((a) & 4)
+#endif
+#ifndef VME_AM_MASK
+#define VME_AM_MASK 0xff
+#endif
#endif
+#include <stdarg.h>
+
typedef unsigned long LERegister; /* emphasize contents are little endian */
/* NOTE: DMA packet descriptors MUST be 32 byte aligned */
@@ -203,6 +246,7 @@ typedef struct VmeUniverseDMAPacketRec_ {
#define UNIV_REGOFF_D_LLUE 0x224
# define UNIV_D_LLUE_UPDATE (1<<31)
+
/* PCI (local) interrupt enable register */
#define UNIV_REGOFF_LINT_EN 0x300
# define UNIV_LINT_EN_LM3 (1<<23) /* location monitor 3 mask */
@@ -279,15 +323,28 @@ typedef struct VmeUniverseDMAPacketRec_ {
/* enabling of generation of VME bus IRQs, TODO */
#define UNIV_REGOFF_VINT_EN 0x310
# define UNIV_VINT_EN_DISABLE_ALL 0
+# define UNIV_VINT_EN_SWINT (1<<12)
+# define UNIV_VINT_EN_SWINT_LVL(l) (1<<(((l)&7)+24)) /* universe II only */
+
-/* status of generation of VME bus IRQs, TODO */
+/* status of generation of VME bus IRQs */
#define UNIV_REGOFF_VINT_STAT 0x314
# define UNIV_VINT_STAT_LINT(lint) (1<<((lint)&7))
# define UNIV_VINT_STAT_LINT_MASK (0xff)
# define UNIV_VINT_STAT_CLR (0xfe0f17ff)
+# define UNIV_VINT_STAT_SWINT(l) (1<<(((l)&7)+24))
+
#define UNIV_REGOFF_VINT_MAP0 0x318 /* VME destination of PCI IRQ source, TODO */
+
#define UNIV_REGOFF_VINT_MAP1 0x31c /* VME destination of PCI IRQ source, TODO */
+# define UNIV_VINT_MAP1_SWINT(level) (((level)&0x7)<<16)
+
+/* NOTE: The universe seems to always set LSB (which has a special purpose in
+ * the STATID register: enable raising a SW_INT on IACK) on the
+ * vector it puts out on the bus...
+ */
#define UNIV_REGOFF_VINT_STATID 0x320 /* our status/id response to IACK, TODO */
+# define UNIV_VINT_STATID(id) ((id)<<24)
#define UNIV_REGOFF_VIRQ1_STATID 0x324 /* status/id of VME IRQ level 1 */
#define UNIV_REGOFF_VIRQ2_STATID 0x328 /* status/id of VME IRQ level 2 */
@@ -360,7 +417,7 @@ typedef struct VmeUniverseDMAPacketRec_ {
/* Location Monitor control register */
#define UNIV_REGOFF_LM_CTL 0xf64
-# define UNIV_LM_CTL_EN (1<<31) /* image enable */
+# define UNIV_LM_CTL_EN (1<<31) /* image enable */
# define UNIV_LM_CTL_PGM (1<<23) /* program AM */
# define UNIV_LM_CTL_DATA (1<<22) /* data AM */
# define UNIV_LM_CTL_SUPER (1<<21) /* supervisor AM */
@@ -374,7 +431,7 @@ typedef struct VmeUniverseDMAPacketRec_ {
/* VMEbus register access image control register */
#define UNIV_REGOFF_VRAI_CTL 0xf70
-# define UNIV_VRAI_CTL_EN (1<<31) /* image enable */
+# define UNIV_VRAI_CTL_EN (1<<31) /* image enable */
# define UNIV_VRAI_CTL_PGM (1<<23) /* program AM */
# define UNIV_VRAI_CTL_DATA (1<<22) /* data AM */
# define UNIV_VRAI_CTL_SUPER (1<<21) /* supervisor AM */
@@ -433,6 +490,7 @@ extern "C" {
extern volatile LERegister *vmeUniverse0BaseAddr;
extern int vmeUniverse0PciIrqLine;
+
/* Initialize the driver */
int
vmeUniverseInit(void);
@@ -451,7 +509,7 @@ vmeUniverseReset(void);
* #include <vmeUniverse.h>
*/
#ifdef _VME_UNIVERSE_DECLARE_SHOW_ROUTINES
-/* print the current configuration of all master ports to
+/* print the current configuration of all master ports to
* f (stderr if NULL)
*/
void
@@ -508,12 +566,12 @@ vmeUniverseMasterPortCfg(
/* translate an address through the bridge
*
- * vmeUniverseXlateAddr(0,0,addr,as,&result)
+ * vmeUniverseXlateAddr(0,0,as,addr,&result)
* yields a VME a address that reflects
* a local memory location as seen from the VME bus through the universe
* VME slave.
*
- * likewise does vmeUniverseXlateAddr(1,0,addr,as,&result)
+ * likewise does vmeUniverseXlateAddr(1,0,as,addr,&result)
* translate a VME bus addr (through the VME master) to the
* PCI side of the bridge.
*
@@ -575,13 +633,119 @@ void
vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num);
/* reset the VME bus */
-static inline void
-vmeUniverseResetBus(void)
-{
- vmeUniverseWriteReg(
- vmeUniverseReadReg(UNIV_REGOFF_MISC_CTL) | UNIV_MISC_CTL_SW_SYSRST,
- UNIV_REGOFF_MISC_CTL);
-}
+void
+vmeUniverseResetBus(void);
+
+/* The ...XX routines take the universe base address as an additional
+ * argument - this allows for programming secondary devices.
+ */
+
+unsigned long
+vmeUniverseReadRegXX(volatile LERegister *ubase, unsigned long offset);
+
+void
+vmeUniverseWriteRegXX(volatile LERegister *ubase, unsigned long value, unsigned long offset);
+
+int
+vmeUniverseXlateAddrXX(
+ volatile LERegister *ubase,
+ int master,
+ int reverse,
+ unsigned long as,
+ unsigned long addr,
+ unsigned long *paOut
+ );
+
+int
+vmeUniverseMasterPortCfgXX(
+ volatile LERegister *ubase,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long local_address,
+ unsigned long length);
+
+int
+vmeUniverseSlavePortCfgXX(
+ volatile LERegister *ubase,
+ unsigned long port,
+ unsigned long address_space,
+ unsigned long vme_address,
+ unsigned long local_address,
+ unsigned long length);
+
+void
+vmeUniverseDisableAllMastersXX(volatile LERegister *ubase);
+
+void
+vmeUniverseDisableAllSlavesXX(volatile LERegister *ubase);
+
+#ifdef _VME_UNIVERSE_DECLARE_SHOW_ROUTINES
+/* print the current configuration of all master ports to
+ * f (stderr if NULL)
+ */
+void
+vmeUniverseMasterPortsShowXX(
+ volatile LERegister *ubase,FILE *f);
+
+/* print the current configuration of all slave ports to
+ * f (stderr if NULL)
+ */
+void
+vmeUniverseSlavePortsShowXX(
+ volatile LERegister *ubase,FILE *f);
+#else
+void
+vmeUniverseMasterPortsShowXX();
+void
+vmeUniverseSlavePortsShowXX();
+#endif
+
+int
+vmeUniverseStartDMAXX(
+ volatile LERegister *ubase,
+ unsigned long local_addr,
+ unsigned long vme_addr,
+ unsigned long count);
+
+/* Raise a VME Interrupt at 'level' and respond with 'vector' to a
+ * handler on the VME bus. (The handler could be a different board
+ * or the universe itself - [only works with universe II]).
+ *
+ * Note that you could install a interrupt handler at UNIV_VME_SW_IACK_INT_VEC
+ * to be notified of an IACK cycle having completed.
+ *
+ * This routine is mainly FOR TESTING.
+ *
+ * NOTES:
+ * - several registers are modified: the vector is written to VINT_STATID
+ * and (universe 1 chip only) the level is written to the SW_INT bits
+ * int VINT_MAP1
+ * - NO MUTUAL EXCLUSION PROTECTION (reads VINT_EN, modifies then writes back).
+ * If several users need access to VINT_EN and/or VINT_STATID (and VINT_MAP1
+ * on the universe 1) it is their responsibility to serialize access.
+ *
+ * Arguments:
+ * 'level': interrupt level, 1..7
+ * 'vector': vector number (0..254) that the universe puts on the bus in response to
+ * an IACK cycle. NOTE: the vector number *must be even* (hardware restriction
+ * of the universe -- it always clears the LSB when the interrupter is
+ * a software interrupt).
+ *
+ * RETURNS:
+ * 0: Success
+ * -1: Invalid argument (level not 1..7, vector odd or >= 256)
+ * -2: Interrupt 'level' already asserted (maybe nobody handles it).
+ * You can manually clear it be writing the respective bit in
+ * VINT_STAT. Make sure really nobody responds to avoid spurious
+ * interrupts (consult universe docs).
+ */
+
+int
+vmeUniverseIntRaiseXX(volatile LERegister *base, int level, unsigned vector);
+
+int
+vmeUniverseIntRaise(int level, unsigned vector);
#ifdef __rtems__
/* VME Interrupt Handler functionality */
@@ -625,7 +789,10 @@ vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR handler, void *usrArg)
VmeUniverseISR
vmeUniverseISRGet(unsigned long vector, void **parg);
-/* utility routines to enable/disable a VME IRQ level
+/* utility routines to enable/disable a VME IRQ level.
+ *
+ * To enable/disable the internal interrupt sources (special vectors above)
+ * pass a vector argument > 255.
*
* RETURNS 0 on success, nonzero on failure
*/
@@ -634,8 +801,58 @@ vmeUniverseIntEnable(unsigned int level);
int
vmeUniverseIntDisable(unsigned int level);
+/* Check if an interrupt level or internal source is enabled:
+ *
+ * 'level': VME level 1..7 or internal special vector > 255
+ *
+ * RETURNS: value > 0 if interrupt is currently enabled,
+ * zero if interrupt is currently disabled,
+ * -1 on error (invalid argument).
+ */
+int
+vmeUniverseIntIsEnabled(unsigned int level);
+
+
+/* Change the routing of IRQ 'level' to 'pin'.
+ * If the BSP connects more than one of the eight
+ * physical interrupt lines from the universe to
+ * the board's PIC then you may change the physical
+ * line a given 'level' is using. By default,
+ * all 7 VME levels use the first wire (pin==0) and
+ * all internal sources use the (optional) second
+ * wire (pin==1) [The driver doesn't support more than
+ * to wires].
+ * This feature is useful if you want to make use of
+ * different hardware priorities of the PIC. Let's
+ * say you want to give IRQ level 7 the highest priority.
+ * You could then give 'pin 0' a higher priority (at the
+ * PIC) and 'pin 1' a lower priority and issue.
+ *
+ * for ( i=1; i<7; i++ ) vmeUniverseIntRoute(i, 1);
+ *
+ * PARAMETERS:
+ * 'level' : VME interrupt level '1..7' or one of
+ * the internal sources. Pass the internal
+ * source's vector number (>=256).
+ * 'pin' : a value of 0 routes the requested IRQ to
+ * the first line registered with the manager
+ * (vmeIrqUnivOut parameter), a value of 1
+ * routes it to the alternate wire
+ * (specialIrqUnivOut)
+ * RETURNS: 0 on success, nonzero on error (invalid arguments)
+ *
+ * NOTES: - DONT change the universe 'map' registers
+ * directly. The driver caches routing internally.
+ * - support for the 'specialIrqUnivOut' wire is
+ * board dependent. If the board only provides
+ * a single physical wire from the universe to
+ * the PIC then the feature might not be available.
+ */
+int
+vmeUniverseIntRoute(unsigned int level, unsigned int pin);
+
/* use these special vectors to connect a handler to the
- * universe specific interrupts (such as "DMA done",
+ * universe specific interrupts (such as "DMA done",
* VOWN, error irqs etc.)
* NOTE: The wrapper clears all status LINT bits (except
* for regular VME irqs). Also note that it is the user's
@@ -646,37 +863,42 @@ vmeUniverseIntDisable(unsigned int level);
* DO NOT CHANGE THE ORDER OF THESE VECTORS - THE DRIVER
* DEPENDS ON IT
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- *
+ *
*/
#define UNIV_VOWN_INT_VEC 256
#define UNIV_DMA_INT_VEC 257
#define UNIV_LERR_INT_VEC 258
#define UNIV_VERR_INT_VEC 259
-#define UNIV_VME_SW_IACK_INT_VEC 260
-#define UNIV_PCI_SW_INT_VEC 261
-#define UNIV_SYSFAIL_INT_VEC 262
-#define UNIV_ACFAIL_INT_VEC 263
-#define UNIV_MBOX0_INT_VEC 264
-#define UNIV_MBOX1_INT_VEC 265
-#define UNIV_MBOX2_INT_VEC 266
-#define UNIV_MBOX3_INT_VEC 267
-#define UNIV_LM0_INT_VEC 268
-#define UNIV_LM1_INT_VEC 269
-#define UNIV_LM2_INT_VEC 270
-#define UNIV_LM3_INT_VEC 271
-
-#define UNIV_NUM_INT_VECS 272
+/* 260 is reserved */
+#define UNIV_VME_SW_IACK_INT_VEC 261
+#define UNIV_PCI_SW_INT_VEC 262
+#define UNIV_SYSFAIL_INT_VEC 263
+#define UNIV_ACFAIL_INT_VEC 264
+#define UNIV_MBOX0_INT_VEC 265
+#define UNIV_MBOX1_INT_VEC 266
+#define UNIV_MBOX2_INT_VEC 267
+#define UNIV_MBOX3_INT_VEC 268
+#define UNIV_LM0_INT_VEC 269
+#define UNIV_LM1_INT_VEC 270
+#define UNIV_LM2_INT_VEC 271
+#define UNIV_LM3_INT_VEC 272
+
+#define UNIV_NUM_INT_VECS 273
/* the universe interrupt handler is capable of routing all sorts of
* (VME) interrupts to 8 different lines (some of) which may be hooked up
* in a (board specific) way to a PIC.
*
- * This driver only supports at most two lines. It routes the 7 VME
- * interrupts to the main line and optionally, it routes the 'special'
+ * This driver only supports at most two lines. By default, it routes the
+ * 7 VME interrupts to the main line and optionally, it routes the 'special'
* interrupts generated by the universe itself (DMA done, VOWN etc.)
* to a second line. If no second line is available, all IRQs are routed
* to the main line.
*
+ * The routing of interrupts to the two lines can be modified (using
+ * the vmeUniverseIntRoute() call - see above - i.e., to make use of
+ * different hardware priorities of the two pins.
+ *
* Because the driver has no way to figure out which lines are actually
* wired to the PIC, this information has to be provided when installing
* the manager.
@@ -686,7 +908,7 @@ vmeUniverseIntDisable(unsigned int level);
* are wired.
* Optionally, the first PIC input line can be read from PCI config space
* but the second must be passed to this routine. Note that the info read
- * from PCI config space is wrong for many boards!
+ * from PCI config space is wrong for many boards!
*
* PARAMETERS:
* vmeIrqUnivOut: to which output pin (of the universe) should the 7
@@ -702,7 +924,7 @@ vmeUniverseIntDisable(unsigned int level);
* the PIC is determined by reading PCI config space.
*
* RETURNS: 0 on success, -1 on failure.
- *
+ *
*/
int
vmeUniverseInstallIrqMgr(int vmeIrqUnivOut,
@@ -710,6 +932,52 @@ vmeUniverseInstallIrqMgr(int vmeIrqUnivOut,
int specialIrqUnivOut,
int specialIrqPicLine);
+/* up to 4 universe outputs are now supported by this alternate
+ * entry point.
+ * Terminate the vararg list (uni_pin/pic_pin pairs) with a
+ * '-1' uni_pin.
+ * E.g., the old interface is now just a wrapper to
+ * vmeUniverseInstallIrqMgrAlt(0, vmeUnivOut, vmePicLint, specUnivOut, specPicLine, -1);
+ *
+ * The 'shared' argument uses the BSP_install_rtems_shared_irq_handler()
+ * API. CAVEAT: shared interrupts need RTEMS workspace, i.e., the
+ * VME interrupt manager can only be installed *after workspace is initialized*
+ * if 'shared' is nonzero (i.e., *not* from bspstart()).
+ */
+int
+vmeUniverseInstallIrqMgrAlt(int shared, int uni_pin0, int pic_pin0, ...);
+
+int
+vmeUniverseInstallIrqMgrVa(int shared, int uni_pin0, int pic_pin0, va_list ap);
+
+/* Loopback test of the VME interrupt subsystem.
+ * - installs ISRs on 'vector' and on UNIV_VME_SW_IACK_INT_VEC
+ * - asserts VME interrupt 'level'
+ * - waits for both interrupts: 'ordinary' VME interrupt of 'level' and
+ * IACK completion interrupt ('special' vector UNIV_VME_SW_IACK_INT_VEC).
+ *
+ * NOTES:
+ * - make sure no other handler responds to 'level'.
+ * - make sure no ISR is installed on both vectors yet.
+ * - ISRs installed by this routine are removed after completion.
+ * - no concurrent access protection of all involved resources
+ * (levels, vectors and registers [see vmeUniverseIntRaise()])
+ * is implemented.
+ * - this routine is intended for TESTING (when implementing new BSPs etc.).
+ * - one RTEMS message queue is temporarily used (created/deleted).
+ * - the universe 1 always yields a zero vector (VIRQx_STATID) in response
+ * to a self-generated VME interrupt. As a workaround, the routine
+ * only accepts a zero vector when running on a universe 1.
+ *
+ * RETURNS:
+ * 0: Success.
+ * -1: Invalid arguments.
+ * 1: Test failed (outstanding interrupts).
+ * rtems_status_code: Failed RTEMS directive.
+ */
+int
+vmeUniverseIntLoopbackTst(int level, unsigned vector);
+
#endif
#ifdef __cplusplus
diff --git a/c/src/lib/libbsp/shared/vmeUniverse/vme_am_defs.h b/c/src/lib/libbsp/shared/vmeUniverse/vme_am_defs.h
new file mode 100644
index 0000000000..0292c3060a
--- /dev/null
+++ b/c/src/lib/libbsp/shared/vmeUniverse/vme_am_defs.h
@@ -0,0 +1,67 @@
+#ifndef VME_AM_DEFINITIONS_H
+#define VME_AM_DEFINITIONS_H
+
+/* vxworks compatible addressing modes */
+
+#ifndef VME_AM_STD_SUP_ASCENDING
+#define VME_AM_STD_SUP_ASCENDING 0x3f
+#endif
+#ifndef VME_AM_STD_SUP_PGM
+#define VME_AM_STD_SUP_PGM 0x3e
+#endif
+#ifndef VME_AM_STD_USR_ASCENDING
+#define VME_AM_STD_USR_ASCENDING 0x3b
+#endif
+#ifndef VME_AM_STD_USR_PGM
+#define VME_AM_STD_USR_PGM 0x3a
+#endif
+#ifndef VME_AM_STD_SUP_DATA
+#define VME_AM_STD_SUP_DATA 0x3d
+#endif
+#ifndef VME_AM_STD_USR_DATA
+#define VME_AM_STD_USR_DATA 0x39
+#endif
+#ifndef VME_AM_EXT_SUP_ASCENDING
+#define VME_AM_EXT_SUP_ASCENDING 0x0f
+#endif
+#ifndef VME_AM_EXT_SUP_PGM
+#define VME_AM_EXT_SUP_PGM 0x0e
+#endif
+#ifndef VME_AM_EXT_USR_ASCENDING
+#define VME_AM_EXT_USR_ASCENDING 0x0b
+#endif
+#ifndef VME_AM_EXT_USR_PGM
+#define VME_AM_EXT_USR_PGM 0x0a
+#endif
+#ifndef VME_AM_EXT_SUP_DATA
+#define VME_AM_EXT_SUP_DATA 0x0d
+#endif
+#ifndef VME_AM_EXT_USR_DATA
+#define VME_AM_EXT_USR_DATA 0x09
+#endif
+#ifndef VME_AM_SUP_SHORT_IO
+#define VME_AM_SUP_SHORT_IO 0x2d
+#endif
+#ifndef VME_AM_USR_SHORT_IO
+#define VME_AM_USR_SHORT_IO 0x29
+#endif
+#ifndef VME_AM_IS_SHORT
+#define VME_AM_IS_SHORT(a) (((a) & 0xf0) == 0x20)
+#endif
+#ifndef VME_AM_IS_STD
+#define VME_AM_IS_STD(a) (((a) & 0xf0) == 0x30)
+#endif
+#ifndef VME_AM_IS_EXT
+#define VME_AM_IS_EXT(a) (((a) & 0xf0) == 0x00)
+#endif
+#ifndef VME_AM_IS_SUP
+#define VME_AM_IS_SUP(a) ((a) & 4)
+#endif
+
+/* Higher order bits are driver specific */
+
+#ifndef VME_AM_MASK
+#define VME_AM_MASK 0xff
+#endif
+
+#endif