diff options
Diffstat (limited to 'c')
-rw-r--r-- | c/src/lib/libbsp/ChangeLog | 23 | ||||
-rw-r--r-- | c/src/lib/libbsp/Makefile.am | 8 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/README.universe | 18 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.c | 1649 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/vmeTsi148.h | 533 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c | 1075 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.h | 364 | ||||
-rw-r--r-- | c/src/lib/libbsp/shared/vmeUniverse/vme_am_defs.h | 67 |
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 |