summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTill Straumann <strauman@slac.stanford.edu>2005-11-04 08:17:56 +0000
committerTill Straumann <strauman@slac.stanford.edu>2005-11-04 08:17:56 +0000
commit784e792a04c600b79d535ad81a5ba32b4f4de33e (patch)
tree47eae99ca61f0b46976ee5fbf8edbe81d8ed2ee3
parent2005-11-03 Till Straumann <strauman@slac.stanford.edu> (diff)
downloadrtems-784e792a04c600b79d535ad81a5ba32b4f4de33e.tar.bz2
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.
-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