diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-03 16:41:16 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-09 07:09:46 +0200 |
commit | 814eccb4cf0e5433bd884a48dfa8e219bb2d0dd0 (patch) | |
tree | 2e079ca82187e519b3be56eb25057330b8344648 /bsps | |
parent | bsp: Move umon support to bsps (diff) | |
download | rtems-814eccb4cf0e5433bd884a48dfa8e219bb2d0dd0.tar.bz2 |
bsps: Move VME support to bsps
The VME support is only used by powerpc BSPs.
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
18 files changed, 6406 insertions, 5 deletions
diff --git a/bsps/headers.am b/bsps/headers.am index ab7fa34c58..294d7869a9 100644 --- a/bsps/headers.am +++ b/bsps/headers.am @@ -23,11 +23,6 @@ include_bsp_HEADERS += ../../bsps/include/bsp/stackalloc.h include_bsp_HEADERS += ../../bsps/include/bsp/u-boot.h include_bsp_HEADERS += ../../bsps/include/bsp/uart-output-char.h include_bsp_HEADERS += ../../bsps/include/bsp/utility.h -include_bsp_HEADERS += ../../bsps/include/bsp/vmeTsi148.h -include_bsp_HEADERS += ../../bsps/include/bsp/vmeTsi148DMA.h -include_bsp_HEADERS += ../../bsps/include/bsp/vmeUniverse.h -include_bsp_HEADERS += ../../bsps/include/bsp/vmeUniverseDMA.h -include_bsp_HEADERS += ../../bsps/include/bsp/vme_am_defs.h include_libchipdir = $(includedir)/libchip include_libchip_HEADERS = diff --git a/bsps/powerpc/headers.am b/bsps/powerpc/headers.am index 17122404e3..ac73dce08c 100644 --- a/bsps/powerpc/headers.am +++ b/bsps/powerpc/headers.am @@ -25,6 +25,11 @@ include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/tsec.h include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/u-boot-board-info.h include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/uart.h include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vectors.h +include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vmeTsi148.h +include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vmeTsi148DMA.h +include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vmeUniverse.h +include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vmeUniverseDMA.h +include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vme_am_defs.h include_bsp_HEADERS += ../../../../../bsps/powerpc/include/bsp/vpd.h include_libcpudir = $(includedir)/libcpu diff --git a/bsps/include/bsp/vmeTsi148.h b/bsps/powerpc/include/bsp/vmeTsi148.h index 6183940a2d..6183940a2d 100644 --- a/bsps/include/bsp/vmeTsi148.h +++ b/bsps/powerpc/include/bsp/vmeTsi148.h diff --git a/bsps/include/bsp/vmeTsi148DMA.h b/bsps/powerpc/include/bsp/vmeTsi148DMA.h index da7c99302b..da7c99302b 100644 --- a/bsps/include/bsp/vmeTsi148DMA.h +++ b/bsps/powerpc/include/bsp/vmeTsi148DMA.h diff --git a/bsps/include/bsp/vmeUniverse.h b/bsps/powerpc/include/bsp/vmeUniverse.h index 7cb9f8d2de..7cb9f8d2de 100644 --- a/bsps/include/bsp/vmeUniverse.h +++ b/bsps/powerpc/include/bsp/vmeUniverse.h diff --git a/bsps/include/bsp/vmeUniverseDMA.h b/bsps/powerpc/include/bsp/vmeUniverseDMA.h index d0a3d6f0e7..d0a3d6f0e7 100644 --- a/bsps/include/bsp/vmeUniverseDMA.h +++ b/bsps/powerpc/include/bsp/vmeUniverseDMA.h diff --git a/bsps/include/bsp/vme_am_defs.h b/bsps/powerpc/include/bsp/vme_am_defs.h index efa28b3aa0..efa28b3aa0 100644 --- a/bsps/include/bsp/vme_am_defs.h +++ b/bsps/powerpc/include/bsp/vme_am_defs.h diff --git a/bsps/powerpc/shared/vme-sources.am b/bsps/powerpc/shared/vme-sources.am new file mode 100644 index 0000000000..62ce3fbcf6 --- /dev/null +++ b/bsps/powerpc/shared/vme-sources.am @@ -0,0 +1,5 @@ +libbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/vme/bspVmeDmaList.c +libbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/vme/vmeconfig.c +libbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/vme/vmeTsi148.c +libbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/vme/vme_universe.c +libbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/vme/vmeUniverse.c diff --git a/bsps/powerpc/shared/vme/README b/bsps/powerpc/shared/vme/README new file mode 100644 index 0000000000..e2e0020026 --- /dev/null +++ b/bsps/powerpc/shared/vme/README @@ -0,0 +1,10 @@ +libbsp/shared/vmeUniverse/VME.h: VME API; BSP and bridge-chip independent +VMEConfig.h: defines BSP specific constants for VME configuration +vmeconfig.c configures the VME bridge using the VME.h API calls + and BSP specific constants from VMEConfig.h. + Independent of the bridge chip, however. +vme_universe.c: implements VME.h for the vmeUniverse driver. +vme_universe_dma.c: implements VMEDMA.h for the vmeUniverse driver. + +o other universe BSP --> use its own VMEConfig.h; may reuse vmeconfig.c, vme_universe.c +o other non-universe BSP --> use its own VMEConfig.h and vme_xxx.c; may reuse vmeconfig.c diff --git a/bsps/powerpc/shared/vme/README.porting b/bsps/powerpc/shared/vme/README.porting new file mode 100644 index 0000000000..6873f5b1d6 --- /dev/null +++ b/bsps/powerpc/shared/vme/README.porting @@ -0,0 +1,96 @@ +# + +NOTE: (T.S, 2007/1) The information in this file is outdated + (but some portions may still be useful). Some more information + about how to use the Universe and Tsi148 drivers in new BSPs + can be found in + + README.universe, + libbsp/powerpc/shared/vme/VMEConfig.h, + + source files in this directory and libbsp/powerpc/shared/vme + +The vmeUniverse driver needs some support from the BSP for + +a) PCI configuration space access +b) PCI interrupt acknowledgement +c) PCI interrupt handler installation + +The driver was developed using the powerpc/shared/ BSP +(it also supports vxWorks) and by default uses that BSP's +a) PCI access API +b,c) irq handling API (AKA 'new' style BSP_install_rtems_irq_handler() + API). + +Some hooks exist in the driver to ease porting to other BSPs. +The following information has been assembled when answering a +question regarding a ppcn_60x BSP port: + +I looked through the ppcn_60x BSP. Here's what I found: + + - this BSP does NOT adhere to neither the 'old' nor the 'new' API + but provides its own (startup/setvec.c: set_vector()). + - the BSP has a 'driver' for vmeUniverse although mine is far more + complete (including support for VME interrupts, DMA etc.). + - Porting my driver to your BSP should not be too hard: + + 1) vmeUniverse needs PCI configuration space support from the + BSP: + a) a routine 'pciFindDevice' (need to be macro-aliased + to the proper routine/wrapper of your BSP) who scans + PCI config space for the universe bridge. + You could add 'libbsp/powerpc/shared/pci/pcifinddevice.c' + to your BSP substituting the pci_read_config_xxx calls + by the ones present on your BSP (see step 2)) + b) routines to read PCI config registers (byte and longword) + [on your BSP these are PCIConfigRead32/PCIConfigRead8; + hence you could replace the macros on top with + #define pciConfigInLong PCIConfigRead32 + 2) vmeUniverse needs to know how to acknowledge a PCI interrupt + In your case, nothing needs to be done + #define BSP_PIC_DO_EOI do {} while (0) + 3) Install the VME ISR dispatcher: replace the 'new' style + interrupt installer (BSP_install_rtems_irq_handler()) by + a proper call to 'set_vector()' + 4) I might have missed something... + +I attach the latest version of the vmeUniverse driver in case you want +to try to do the port (should be easy). + +For the sake of ease of maintenance, I just added a few hooks making it +possible to override some things without having to modify the driver code. + + 1,2) PCI config space access macros may be overriden via CFLAGS + when compiling vmeUniverse.c, hence: + CFLAGS += -DBSP_PIC_DO_EOI=do{}while(0) + CFLAGS += -DBSP_PCI_CONFIG_IN_LONG=PCIConfigRead32 + CFLAGS += -DBSP_PCI_CONFIG_IN_BYTE=PCIConfigRead8 + (you still need to supply pci_find_device) + 3) create your own version of vmeUniverseInstallIrqMgr(): + copy to a separate file and replace + BSP_rtems_install_irq_handler() by a proper call to set_vector. + + 4) Send me email :-) + +USAGE NOTE: To fully initialize the driver, the following steps can/must +be performed: + + vmeUniverseInit(); /* MANDATORY: Driver Initialization */ + vmeUniverseReset(); /* OPTIONAL: Reset most registers to a known state; + * if this step is omitted, firmware setup is + * preserved + */ + vmeUniverseMasterPortCfg(...); /* OPTIONAL: setup the master windows + * (current setup preserved if omitted) + */ + vmeUniverseSlavePortCfg(...); /* OPTIONAL: setup the slave windows + * (current setup preserved if omitted) + */ + vmeUniverseInstallIrqMgr(); /* NEEDED FOR VME INTERRUPT SUPPRORT + * initialize the interrupt manager. + * NOTE: you need to call your own + * version of this routine here + */ + +For an example of init/setup, consult libbsp/powerpc/shared/vme/vmeconfig.c + diff --git a/bsps/powerpc/shared/vme/README.universe b/bsps/powerpc/shared/vme/README.universe new file mode 100644 index 0000000000..1059d8c806 --- /dev/null +++ b/bsps/powerpc/shared/vme/README.universe @@ -0,0 +1,65 @@ +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, 2005, 2007 + +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'). + - <libcpu/io.h> for the I/O operations (out_le32,in_le32, ..., out_be32,...) + - <libcpu/byteorder.h> for byte-swapping (st_le32, ld_le32, ..., st_be32,...) + - glue code that implements the 'VME.h' and 'VMEDMA.h' APIs + using the vmeUniverse and/or vmeTsi148 driver code. + The 'glue' code initializes appropriate VME/PCI windows when booting + and installs the VME interrupt manager. + + The 'glue' may also use the 'bspVmeDmaList' code to implement generic + parts of linked-list DMA. + + Boards with a 'universe' chip may use a pretty generic version of + the glue code that is defined in libbsp/powerpc/shared/vmeconfig.c, + libbsp/powerpc/shared/vme_universe.c, and + libbsp/powerpc/shared/vme_universe_dma.c. The board-specific parameters + are defined in a single BSP-specific file 'VMEConfig.h'. That's where + the actual addresses of VME/PCI windows are configured and where + interrupt wires can be assigned etc. + + Read libbsp/powerpc/shared/VMEConfig.h for more information and use + it as a template. Note that BSP implementors should try *not* to + clone 'vmeconfig.c' but use the constants in VMEConfig.h + + - The BSP should export 'VME.h' and 'VMEDMA.h' to applications + and encourage them to only use the API defined there in order + to make application code driver-independent. This will ensure + seamless portability of applications between the universe and Tsi148 + drivers. + + +TESTING: A valuable tool for testing are the (substitute XXX for + 'Universe' or 'Tsi148') routines: + vmeXXXMapCRG() + maps the controller registers to VME space so you + can test if you successfully can read/write from VME. + You can read or DMA the PCI configuration registers + and compare to what you expect (beware of endianness). + + vmeXXXIntLoopbackTest() + this installs an ISR and then asserts an IRQ on the VME + backplane so you can verify that your interrupt routing + and handling really works. + +NOTES: The universe may always issue MBLTs if a data width of 64-bit + is enabled (default for non-BLT addressing modes -- the + VME_AM_STD_xx_BLT / VME_AM_EXT_xx_BLT enforce 32-bit transfers). + + Therefore, if you want to setup a outbound window that always + uses single cycles then you must explicitely request a data + width < 64, e.g., + + vmeUniverseMasterPortCfg(port, VME_AM_EXT_SUP_DATA | VME_MODE_DBW32, vme_addr, pci_addr, size); + diff --git a/bsps/powerpc/shared/vme/bspVmeDmaList.c b/bsps/powerpc/shared/vme/bspVmeDmaList.c new file mode 100644 index 0000000000..73b398dda1 --- /dev/null +++ b/bsps/powerpc/shared/vme/bspVmeDmaList.c @@ -0,0 +1,334 @@ +/* bspVmeDmaList.c: + * implementation of generic parts of the 'linked-list VME DMA' API. + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2006, 2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define DEBUG +#include <bsp/VMEDMA.h> +#include <bsp/bspVmeDmaList.h> +#include "bspVmeDmaListP.h" + + +typedef struct VMEDmaListNodeRec_ { + VMEDmaListNode p, n; /* linkage */ + DmaDescriptor d; /* real descriptor */ + void *usrData; + VMEDmaListClass class; /* pointer to 'class' record */ +} VMEDmaListNodeRec; + +#define LCHUNK 10 + +#ifdef DEBUG +static void +lprint(VMEDmaListNode d) +{ + printf("n 0x%08lx, p: 0x%08lx, n: 0x%08lx d: 0x%08lx\n", + (uint32_t)d, (uint32_t)d->p, (uint32_t)d->n, (uint32_t)d->d); +} +#endif + +static VMEDmaListNode +lalloc(VMEDmaListClass pc) +{ +VMEDmaListNode rval; +int i; + + if ( !pc->freeList ) { + /* alloc block of 10 descriptors */ + pc->freeList = calloc( (LCHUNK), sizeof(*pc->freeList)); + + if ( ! (pc->freeList) ) { + return 0; + } + + /* link together and set 'class' pointer */ + for (i=0; i<(LCHUNK)-1; i++) { + pc->freeList[i].n = &pc->freeList[i+1]; + pc->freeList[i].class = pc; + } + pc->freeList[i].n = 0; + pc->freeList[i].class = pc; + + /* Allocate 'real' descriptor memory */ + if ( pc->desc_alloc ) { + for (i=0; i<(LCHUNK); i++) { + if ( ! (pc->freeList[i].d = pc->desc_alloc()) ) { + int j; + if ( pc->desc_free ) { + for (j=0; j<i; j++) + pc->desc_free(pc->freeList[i].d); + } + free(pc->freeList); + pc->freeList = 0; + return 0; + } + } + } else { + int blksize; + uint32_t algnmsk = pc->desc_align - 1; + char *memptr; + + /* ignore their 'free' method */ + pc->desc_free = 0; + + blksize = (pc->desc_size + algnmsk) & ~algnmsk; + + if ( ! (memptr = malloc(blksize*(LCHUNK) + pc->desc_align - 1)) ) { + free(pc->freeList); + pc->freeList = 0; + return 0; + } + + /* align memory ptr; must not be freed() anymore */ + memptr = (char*)( ((uint32_t)memptr + algnmsk) & ~ algnmsk ); + + for ( i = 0; i<(LCHUNK); i++, memptr+=blksize ) { + memset(memptr, 0, blksize); + pc->freeList[i].d = (DmaDescriptor)memptr; + } + } + } + rval = pc->freeList; + pc->freeList = pc->freeList->n; + rval->n = rval->p = 0; + return rval; +} + +static int +lfree(VMEDmaListNode d) +{ + if ( d->p || d->n ) + return -1; + d->n = d->class->freeList; + d->class->freeList = d; + return 0; +} + +static int +lenq(VMEDmaListNode a, VMEDmaListNode d) +{ + if ( a ) { + /* enqueue */ + if ( d->n || d->p ) + return -1; + if ( (d->n = a->n) ) + a->n->p = d; + d->p = a; + a->n = d; + } else { + /* dequeue */ + if ( d->n ) + d->n->p = d->p; + if ( d->p ) + d->p->n = d->n; + d->n = d->p = 0; + } + return 0; +} + + +int +BSP_VMEDmaListDescriptorStartTool(volatile void *controller, int channel, VMEDmaListNode n) +{ + if ( !n ) + return -1; + return n->class->desc_start(controller, channel, n->d); +} + +VMEDmaListNode +BSP_VMEDmaListDescriptorSetupTool( + VMEDmaListNode n, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ + if ( !n ) + return 0; + + if ( n->class->desc_setup(n->d, attr_mask, xfer_mode, pci_addr, vme_addr, n_bytes) ) { + return 0; + } + + return n; +} + +VMEDmaListNode +BSP_VMEDmaListDescriptorNewTool( + VMEDmaListClass pc, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ +VMEDmaListNode n; + + if ( !(n=lalloc(pc)) ) + return 0; /* no memory */ + + if ( n->class->desc_init ) + n->class->desc_init(n->d); + + if ( n->class->desc_setup(n->d, attr_mask, xfer_mode, pci_addr, vme_addr, n_bytes) ) { + BSP_VMEDmaListDescriptorDestroy(n); + return 0; + } + return n; +} + +int +BSP_VMEDmaListDescriptorDestroy(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; + return lfree(d); +} + +int +BSP_VMEDmaListDestroy(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; +VMEDmaListNode n; + while (d) { + n = d->n; + if ( BSP_VMEDmaListDescriptorEnq(0, d) || + BSP_VMEDmaListDescriptorDestroy(d) ) + return -1; + d = n; + } + return 0; +} + +int +BSP_VMEDmaListDescriptorEnq(BSP_VMEDmaListDescriptor p, BSP_VMEDmaListDescriptor q) +{ +VMEDmaListNode anchor = p; +VMEDmaListNode d = q; +DmaDescriptorSetNxt setnxt = d->class->desc_setnxt; + + if ( !anchor ) { + /* dequeue can't fail - we can update dnlal first (need d->p) */ + if ( d->p ) + setnxt(d->p->d, d->n ? d->n->d : 0); + /* paranoia */ + setnxt(d->d, 0); + } else { + if ( d->class != anchor->class ) + return -1; + } + if ( lenq(anchor, d) ) + return -1; + /* update descriptor pointers */ + if ( anchor ) { + setnxt(d->d, d->n ? d->n->d : 0); + setnxt(anchor->d, d->d); + } + return 0; +} + +BSP_VMEDmaListDescriptor +BSP_VMEDmaListDescriptorNext(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; + return d->n; +} + +BSP_VMEDmaListDescriptor +BSP_VMEDmaListDescriptorPrev(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; + return d->p; +} + +void +BSP_VMEDmaListDescriptorSetUsr(BSP_VMEDmaListDescriptor p, void *usrData) +{ +VMEDmaListNode d = p; + d->usrData = usrData; +} + +void * +BSP_VMEDmaListDescriptorGetUsr(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; + return d->usrData; +} + +int +BSP_VMEDmaListRefresh(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode anchor = p; +DmaDescriptorRefr desc_refr; +VMEDmaListNode n; + if ( (desc_refr = anchor->class->desc_refr) ) { + for ( n = anchor; n; n = n->n ) { + desc_refr(n->d); + } + } + return 0; +} + +#ifdef DEBUG +void +BSP_VMEDmaListDump(BSP_VMEDmaListDescriptor p) +{ +VMEDmaListNode d = p; + while (d) { + printf("----------\n"); + lprint(d); + if (d->class->desc_dump) + d->class->desc_dump(d->d); + d = d->n; + } +} +#endif diff --git a/bsps/powerpc/shared/vme/bspVmeDmaListP.h b/bsps/powerpc/shared/vme/bspVmeDmaListP.h new file mode 100644 index 0000000000..1a1f97b9ad --- /dev/null +++ b/bsps/powerpc/shared/vme/bspVmeDmaListP.h @@ -0,0 +1,107 @@ +/** + * @file + * + * @ingroup shared_bspvmedmalistp + * + * @brief Private Interface to the bspVmeDmaList facility + */ + +#ifndef BSP_VME_DMA_LIST_P_H +#define BSP_VME_DMA_LIST_P_H + +#include <bsp/bspVmeDmaList.h> + +/* + * This is used by chip drivers to implement the + * 'class' members so that 'bspVmeDmaList' can access + * the device in an abstract manner. + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2006, 2007 + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *DmaDescriptor; + +/** + * @defgroup shared_bspvmedmalistp member functions + * + * @ingroup shared_vmeuniverse + * + * @brief Member functions to be implemented by chip drivers + */ + +typedef DmaDescriptor (*DmaDescriptorAlloc)(void); +typedef void (*DmaDescriptorFree) (DmaDescriptor d); +typedef void (*DmaDescriptorInit) (DmaDescriptor d); +/* Setup takes the parameters declared in VMEDMA.h */ +typedef int (*DmaDescriptorSetup)(DmaDescriptor d, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); +typedef void (*DmaDescriptorSetNxt)(DmaDescriptor d, DmaDescriptor next); +typedef int (*DmaDescriptorStart)(volatile void *controller_addr, int channel, DmaDescriptor); +typedef int (*DmaDescriptorRefr) (DmaDescriptor); +typedef void (*DmaDescriptorDump) (DmaDescriptor); + + +typedef struct VMEDmaListClassRec_ { + int desc_size; /* size of a descritor */ + int desc_align; /* alignment of a descriptor */ + VMEDmaListNode freeList; /* list of free descriptors of this class, MUST be initialized to NULL */ + DmaDescriptorAlloc desc_alloc; /* [optional, may be NULL] allocator for one descriptor */ + DmaDescriptorFree desc_free; /* [optional, may be NULL] destructor for one descriptor */ + DmaDescriptorInit desc_init; /* [optional, may be NULL] set stuff that don't change during lifetime */ + DmaDescriptorSetNxt desc_setnxt;/* set 'NEXT' pointer in descriptor; mark as LAST if next == 0 */ + DmaDescriptorSetup desc_setup; /* setup a descriptor */ + DmaDescriptorStart desc_start; /* start list at a descriptor */ + DmaDescriptorRefr desc_refr; /* refresh a descriptor */ + DmaDescriptorDump desc_dump; /* dump a descriptor (for debugging) */ +} VMEDmaListClassRec; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsps/powerpc/shared/vme/doxygen.h b/bsps/powerpc/shared/vme/doxygen.h new file mode 100644 index 0000000000..88ded941ac --- /dev/null +++ b/bsps/powerpc/shared/vme/doxygen.h @@ -0,0 +1,7 @@ +/** + * @defgroup shared_vmeuniverse SHARED VMEUNIVERSE Modules + * + * @ingroup bsp_shared + * + * @brief SHARED VMEUNIVERSE Modules + */
\ No newline at end of file diff --git a/bsps/powerpc/shared/vme/vmeTsi148.c b/bsps/powerpc/shared/vme/vmeTsi148.c new file mode 100644 index 0000000000..4e1893b593 --- /dev/null +++ b/bsps/powerpc/shared/vme/vmeTsi148.c @@ -0,0 +1,2695 @@ +/* Driver for the Tundra Tsi148 pci-vme bridge */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <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 <rtems/pci.h> +#include <bsp.h> +#include <libcpu/byteorder.h> + +#define __INSIDE_RTEMS_BSP__ +#define _VME_TSI148_DECLARE_SHOW_ROUTINES + +#include <bsp/vmeTsi148.h> +#include <bsp/VMEDMA.h> +#include <bsp/vmeTsi148DMA.h> +#include "bspVmeDmaListP.h" + + +#define DEBUG + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +/* 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_2eSSTM_160 TSI_OTAT_2eSSTM(0) +# define TSI_OTAT_2eSSTM_267 TSI_OTAT_2eSSTM(1) +# define TSI_OTAT_2eSSTM_320 TSI_OTAT_2eSSTM(2) +# define TSI_OTAT_TM(x) (((x)&7)<<8) +# define TSI_TM_SCT_IDX 0 +# define TSI_TM_BLT_IDX 1 +# define TSI_TM_MBLT_IDX 2 +# define TSI_TM_2eVME_IDX 3 +# define TSI_TM_2eSST_IDX 4 +# define TSI_TM_2eSSTB_IDX 5 +# 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_CSR 5 +# 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_VMCTRL_REG 0x234 +# define TSI_VMCTRL_VSA (1<<27) +# define TSI_VMCTRL_VS (1<<26) +# define TSI_VMCTRL_DHB (1<<25) +# define TSI_VMCTRL_DWB (1<<24) +# define TSI_VMCTRL_RMWEN (1<<20) +# define TSI_VMCTRL_A64DS (1<<16) +# define TSI_VMCTRL_VTOFF_MSK (7<<12) +# define TSI_VMCTRL_VTOFF_0us (0<<12) +# define TSI_VMCTRL_VTOFF_1us (1<<12) +# define TSI_VMCTRL_VTOFF_2us (2<<12) +# define TSI_VMCTRL_VTOFF_4us (3<<12) +# define TSI_VMCTRL_VTOFF_8us (4<<12) +# define TSI_VMCTRL_VTOFF_16us (5<<12) +# define TSI_VMCTRL_VTOFF_32us (6<<12) +# define TSI_VMCTRL_VTOFF_64us (7<<12) +# define TSI_VMCTRL_VTON_MSK (7<< 8) +# define TSI_VMCTRL_VTON_4us (0<< 8) +# define TSI_VMCTRL_VTON_8us (1<< 8) +# define TSI_VMCTRL_VTON_16us (2<< 8) +# define TSI_VMCTRL_VTON_32us (3<< 8) +# define TSI_VMCTRL_VTON_64us (4<< 8) +# define TSI_VMCTRL_VTON_128us (5<< 8) +# define TSI_VMCTRL_VTON_256us (6<< 8) +# define TSI_VMCTRL_VTON_512us (7<< 8) +# define TSI_VMCTRL_VREL_MSK (3<< 3) +# define TSI_VMCTRL_VREL_TON_or_DONE (0<< 3) +# define TSI_VMCTRL_VREL_TONandREQ_or_DONE (1<< 3) +# define TSI_VMCTRL_VREL_TONandBCLR_or_DONE (2<< 3) +# define TSI_VMCTRL_VREL_TONorDONE_and_REQ (3<< 3) +# define TSI_VMCTRL_VFAIR (1<< 2) +# define TSI_VMCTRL_VREQL_MSK (3<< 0) +# define TSI_VMCTRL_VREQL(x) ((x)&3) + +#define TSI_VCTRL_REG 0x238 +#define TSI_VCTRL_DLT_MSK (0xf<<24) +#define TSI_VCTRL_NELBB (1<<20) +#define TSI_VCTRL_SRESET (1<<17) +#define TSI_VCTRL_LRESET (1<<16) +#define TSI_VCTRL_SFAILAI (1<<15) +#define TSI_VCTRL_BID_MSK (0x1f<<8) +#define TSI_VCTRL_ATOEN (1<< 7) +#define TSI_VCTRL_ROBIN (1<< 6) +#define TSI_VCTRL_GTO_MSK (7<< 0) + + +#define TSI_VSTAT_REG 0x23c +# define TSI_VSTAT_CPURST (1<<15) /* clear power-up reset bit */ +# define TSI_VSTAT_BDFAIL (1<<14) +# define TSI_VSTAT_PURSTS (1<<12) +# define TSI_VSTAT_BDFAILS (1<<11) +# define TSI_VSTAT_SYSFLS (1<<10) +# define TSI_VSTAT_ACFAILS (1<< 9) +# define TSI_VSTAT_SCONS (1<< 8) +# define TSI_VSTAT_GAP (1<< 5) +# define TSI_VSTAT_GA_MSK (0x1f) + +#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_2eSSTM_160 TSI_ITAT_2eSSTM(0) +# define TSI_ITAT_2eSSTM_267 TSI_ITAT_2eSSTM(1) +# define TSI_ITAT_2eSSTM_320 TSI_ITAT_2eSSTM(2) +# 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_CBAU_REG 0x40c +#define TSI_CBAL_REG 0x410 +#define TSI_CRGAT_REG 0x414 +# define TSI_CRGAT_EN (1<<7) +# define TSI_CRGAT_AS_MSK (7<<4) +# define TSI_CRGAT_A16 (0<<4) +# define TSI_CRGAT_A24 (1<<4) +# define TSI_CRGAT_A32 (2<<4) +# define TSI_CRGAT_A64 (4<<4) +# define TSI_CRGAT_SUP (1<<3) +# define TSI_CRGAT_USR (1<<2) +# define TSI_CRGAT_PGM (1<<1) +# define TSI_CRGAT_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_CBAR_REG 0xffc + +#define TSI_CSR_OFFSET 0x7f000 + +#define TSI_CRG_SIZE (1<<12) /* 4k */ + + +#define TSI_RD(base, reg) in_be32((volatile uint32_t *)((base) + (reg)/sizeof(*base))) +#define TSI_RD16(base, reg) in_be16((volatile uint16_t *)(base) + (reg)/sizeof(uint16_t)) +#define TSI_LE_RD16(base, reg) in_le16((volatile uint16_t *)(base) + (reg)/sizeof(uint16_t)) +#define TSI_LE_RD32(base, reg) in_le32((volatile uint32_t *)(base) + (reg)/sizeof(*base)) +#define TSI_RD8(base, reg) in_8((volatile uint8_t *)(base) + (reg)) +#define TSI_WR(base, reg, val) out_be32((volatile uint32_t *)((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 uint32_t pci_ulong; + +#ifdef __BIG_ENDIAN__ + static inline void st_be32( uint32_t *a, uint32_t v) + { + *a = v; + } + static inline uint32_t ld_be32( uint32_t *a ) + { + return *a; + } +#elif defined(__LITTLE_ENDIAN__) +#error "You need to implement st_be32/ld_be32" +#else +#error "Undefined endianness??" +#endif + +#ifndef BSP_LOCAL2PCI_ADDR +/* try legacy PCI_DRAM_OFFSET */ +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif +#define BSP_LOCAL2PCI_ADDR(l) (((uint32_t)l)+PCI_DRAM_OFFSET) +#endif + +/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses. + * Should be defined by the BSP. + */ +#ifndef BSP_PCI2LOCAL_ADDR +#ifndef PCI_MEM_BASE +#define PCI_MEM_BASE 0 +#endif +#define BSP_PCI2LOCAL_ADDR(memaddr) ((unsigned long)(memaddr) + PCI_MEM_BASE) +#endif + +typedef uint32_t BEValue; + +typedef struct { + BERegister *base; + int irqLine; + int pic_pin[TSI_NUM_WIRES]; +} Tsi148Dev; + +static Tsi148Dev devs[NUM_TSI_DEVS] = {{0}}; + +#define THEBASE (devs[0].base) + +/* forward decl */ +extern int vmeTsi148RegPort; +extern int vmeTsi148RegCSR; + +/* 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_ulong)BSP_PCI2LOCAL_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; +uint32_t v; + + 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); + /* Clear BDFAIL / (--> SYSFAIL) */ +# define TSI_VSTAT_BDFAIL (1<<14) + TSI_WR(base, TSI_VSTAT_REG, TSI_RD(base, TSI_VSTAT_REG) & ~TSI_VSTAT_BDFAIL); + /* Set (long) bus master timeout; the timeout actually overrides + * the DMA block size so that the DMA settings would effectively + * not be used. + * Also, we enable 'release on request' mode so that we normally + * don't have to rearbitrate the bus for every transfer. + */ + v = TSI_RD(base, TSI_VMCTRL_REG); + v &= ~( TSI_VMCTRL_VTON_MSK | TSI_VMCTRL_VREL_MSK ); + v |= (TSI_VMCTRL_VTON_512us | TSI_VMCTRL_VREL_TONorDONE_and_REQ ); + TSI_WR(base, TSI_VMCTRL_REG, v); +} + +void +vmeTsi148Reset(void) +{ + vmeTsi148ResetXX(THEBASE); +} + +void +vmeTsi148ResetBusXX(BERegister *base) +{ +unsigned long flags; +uint32_t v; + + rtems_interrupt_disable(flags); + v = TSI_RD(base, TSI_VCTRL_REG); + TSI_WR(base, TSI_VCTRL_REG, v | TSI_VCTRL_SRESET); + rtems_interrupt_enable(flags); +} + +void +vmeTsi148ResetBus(void) +{ + vmeTsi148ResetBusXX(THEBASE); +} + + +/* convert an address space selector to a corresponding + * Tsi148 control mode word + */ + +static unsigned long ck2esst(unsigned long am) +{ + if ( VME_AM_IS_2eSST(am) ) { + /* make sure 2eVME is selected */ + am &= ~VME_AM_MASK; + am |= VME_AM_2eVME_6U; + } + return am; +} + +static int +am2omode(unsigned long address_space, unsigned long *pmode) +{ +unsigned long mode = 0; +unsigned long tm = TSI_TM_SCT_IDX; + + switch ( VME_MODE_DBW_MSK & address_space ) { + case VME_MODE_DBW8: + return -1; /* unsupported */ + + case VME_MODE_DBW16: + break; + + default: + case VME_MODE_DBW32: + mode |= TSI_OTAT_DBW(1); + break; + } + + if ( ! (VME_MODE_PREFETCH_ENABLE & address_space) ) + mode |= TSI_OTAT_MRPFD; + else { + mode |= TSI_OTAT_PFS(address_space>>_LD_VME_MODE_PREFETCHSZ); + } + + address_space = ck2esst(address_space); + + 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_BLT: + case VME_AM_STD_SUP_MBLT: + + case VME_AM_STD_USR_BLT: + case VME_AM_STD_USR_MBLT: + switch ( address_space & 3 ) { + case 0: tm = TSI_TM_MBLT_IDX; break; + case 3: tm = TSI_TM_BLT_IDX; break; + default: break; + } + + 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_BLT: + case VME_AM_EXT_SUP_MBLT: + + case VME_AM_EXT_USR_BLT: + case VME_AM_EXT_USR_MBLT: + switch ( address_space & 3 ) { + case 0: tm = TSI_TM_MBLT_IDX; break; + case 3: tm = TSI_TM_BLT_IDX; break; + default: break; + } + + 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 VME_AM_CSR: + mode |= TSI_OTAT_ADMODE_CSR; + break; + + case VME_AM_2eVME_6U: + case VME_AM_2eVME_3U: + mode |= TSI_OTAT_ADMODE_A32; + if ( VME_AM_IS_2eSST(address_space) ) { + tm = ( VME_AM_2eSST_BCST & address_space ) ? + TSI_TM_2eSSTB_IDX : TSI_TM_2eSST_IDX; + switch ( VME_AM_IS_2eSST(address_space) ) { + default: + case VME_AM_2eSST_LO: mode |= TSI_OTAT_2eSSTM_160; break; + case VME_AM_2eSST_MID: mode |= TSI_OTAT_2eSSTM_267; break; + case VME_AM_2eSST_HI: mode |= TSI_OTAT_2eSSTM_320; break; + } + } else { + tm = TSI_TM_2eVME_IDX; + } + break; + + case 0: /* disable the port alltogether */ + break; + + default: + return -1; + } + + mode |= TSI_OTAT_TM(tm); + + 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 pgm = 0; + + mode |= TSI_ITAT_VFS(address_space>>_LD_VME_MODE_PREFETCHSZ); + + if ( VME_AM_IS_2eSST(address_space) ) { + mode |= TSI_ITAT_2eSST; + if ( VME_AM_2eSST_BCST & address_space ) + mode |= TSI_ITAT_2eSSTB; + switch ( VME_AM_IS_2eSST(address_space) ) { + default: + case VME_AM_2eSST_LO: mode |= TSI_ITAT_2eSSTM_160; break; + case VME_AM_2eSST_MID: mode |= TSI_ITAT_2eSSTM_267; break; + case VME_AM_2eSST_HI: mode |= TSI_ITAT_2eSSTM_320; break; + } + address_space = ck2esst(address_space); + } + + mode |= TSI_ITAT_BLT; + mode |= TSI_ITAT_MBLT; + + mode |= TSI_ITAT_PGM; /* always allow PGM access */ + mode |= TSI_ITAT_USR; /* always allow USR access */ + + switch (address_space & VME_AM_MASK) { + case VME_AM_STD_SUP_PGM: + case VME_AM_STD_USR_PGM: + + pgm = 1; + + /* fall thru */ + case VME_AM_STD_SUP_BLT: + case VME_AM_STD_SUP_MBLT: + case VME_AM_STD_USR_BLT: + case VME_AM_STD_USR_MBLT: + 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: + pgm = 1; + + /* fall thru */ + case VME_AM_2eVME_6U: + case VME_AM_2eVME_3U: + case VME_AM_EXT_SUP_BLT: + case VME_AM_EXT_SUP_MBLT: + case VME_AM_EXT_USR_BLT: + case VME_AM_EXT_USR_MBLT: + 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; + + if ( !pgm ) + 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"); +int i,s,l; + + CHECK_BASE(base,0,-1); + + mode = 0; /* silence warning */ + + if ( port >= (isout ? TSI148_NUM_OPORTS : TSI148_NUM_IPORTS) ) { + uprintf(stderr,"Tsi148 %s Port Cfg: invalid port\n", name); + return -1; + } + + if ( base == THEBASE && isout && vmeTsi148RegPort == port ) { + uprintf(stderr,"Tsi148 %s Port Cfg: invalid port; reserved by the interrupt manager for CRG\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; + + /* check for overlap */ + for ( i = 0; i < TSI148_NUM_OPORTS; i++ ) { + /* ignore 'this' port */ + if ( i == port || ! (TSI_OTAT_EN & TSI_RD(base, TSI_OTAT_REG(i))) ) + continue; + + /* check requested PCI range against current port 'i' config */ + s = TSI_RD(base, TSI_OTSAU_REG(i) + 0x04); /* start */ + l = TSI_RD(base, TSI_OTSAU_REG(i) + 0x0c); /* limit */ + if ( ! ( start + length <= s || start > s + l ) ) { + uprintf(stderr,"Tsi148 Outbound Port Cfg: PCI address range overlaps with port %i (0x%08x..0x%08x)\n", i, s, l); + return -1; + } + } + } 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; + + /* check for overlap */ + for ( i = 0; i < TSI148_NUM_IPORTS; i++ ) { + /* ignore 'this' port */ + if ( i == port || ! (TSI_ITAT_EN & (s=TSI_RD(base, TSI_ITAT_REG(i)))) ) + continue; + + if ( (TSI_ITAT_AS(-1) & s) != (TSI_ITAT_AS(-1) & mode) ) { + /* different address space */ + continue; + } + + if ( ! (mode & s & (TSI_ITAT_SUP | TSI_ITAT_USR | TSI_ITAT_PGM | TSI_ITAT_DATA)) ) { + /* orthogonal privileges */ + continue; + } + + /* check requested VME range against current port 'i' config */ + s = TSI_RD(base, TSI_ITSAU_REG(i) + 0x04); /* start */ + l = TSI_RD(base, TSI_ITSAU_REG(i) + 0x0c); /* limit */ + if ( ! ( start + length <= s || start > s + l ) ) { + uprintf(stderr,"Tsi148 Inbound Port Cfg: VME address range overlaps with port %i (0x%08x..0x%08x)\n", i, s, l); + return -1; + } + } + } + + /* 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, (uint32_t)start); + TSI_WR(base, tsau_reg + 0x08, 0); + TSI_WR(base, tsau_reg + 0x0c, (uint32_t)limit); + TSI_WR(base, tsau_reg + 0x10, (uint32_t)(offst>>32)); + TSI_WR(base, tsau_reg + 0x14, (uint32_t)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(THEBASE, 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(THEBASE, 1, port, address_space, vme_address, pci_address, length); +} + + +static int +xlateFindPort( + 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); + + mode = 0; /* silence warning */ + + switch ( as & VME_MODE_MATCH_MASK ) { + case VME_MODE_EXACT_MATCH: + mode_msk = ~0; + break; + + case VME_MODE_AS_MATCH: + if ( outbound ) + mode_msk = TSI_OTAT_ADMODE(-1) | TSI_OTAT_EN; + else + mode_msk = TSI_ITAT_AS(-1) | TSI_ITAT_EN; + break; + + default: + 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; + break; + } + + as &= ~VME_MODE_MATCH_MASK; + + 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 port; + } + } + } + + uprintf(stderr, "vmeTsi148XlateAddr: no matching mapping found\n"); + return -1; +} + +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 port = xlateFindPort( base, outbound, reverse, as, aIn, paOut ); + return port < 0 ? -1 : 0; +} + +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(THEBASE, 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; + case TSI_OTAT_ADMODE_CSR: uprintf(f,"CSR"); 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(THEBASE, 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(THEBASE, 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(THEBASE); +} + +void +vmeTsi148DisableAllOutboundPortsXX(BERegister *base) +{ +int port; + + for ( port = 0; port < TSI148_NUM_IPORTS; port++ ) + if ( disableTsiPort(base, 1, port) ) + break; +} + +void +vmeTsi148DisableAllOutboundPorts(void) +{ + vmeTsi148DisableAllOutboundPortsXX(THEBASE); +} + + +/* Map internal register block to VME */ + +int +vmeTsi148MapCRGXX(BERegister *b, uint32_t vme_base, uint32_t as ) +{ +uint32_t mode; + + CHECK_BASE( b, 0, -1 ); + + if ( vmeTsi148RegPort > -1 && ! vmeTsi148RegCSR ) { + uprintf(stderr,"vmeTsi148: CRG already mapped and in use by interrupt manager\n"); + return -1; + } + + /* enable all, SUP/USR/PGM/DATA accesses */ + mode = TSI_CRGAT_EN | TSI_CRGAT_SUP | TSI_CRGAT_USR | TSI_CRGAT_PGM | TSI_CRGAT_DATA; + + if ( VME_AM_IS_SHORT(as) ) { + mode |= TSI_CRGAT_A16; + } else + if ( VME_AM_IS_STD(as) ) { + mode |= TSI_CRGAT_A24; + } else + if ( VME_AM_IS_EXT(as) ) { + mode |= TSI_CRGAT_A32; + } else { + return -2; + } + + /* map CRG to VME bus */ + TSI_WR( b, TSI_CBAL_REG, (vme_base & ~(TSI_CRG_SIZE-1))); + TSI_WR( b, TSI_CRGAT_REG, mode ); + + return 0; +} + +int +vmeTsi148MapCRG(uint32_t vme_base, uint32_t as ) +{ + return vmeTsi148MapCRGXX( THEBASE, vme_base, as ); +} + +/* Interrupt Subsystem */ + +typedef struct +IRQEntryRec_ { + VmeTsi148ISR isr; + void *usrData; +} IRQEntryRec, *IRQEntry; + +static IRQEntry irqHdlTbl[TSI_NUM_INT_VECS]={0}; + +int vmeTsi148IrqMgrInstalled = 0; +int vmeTsi148RegPort = -1; +int vmeTsi148RegCSR = 0; +BERegister *vmeTsi148RegBase = 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(THEBASE, mapreg) & ~ (0x3<<shift); + mask |= wire; + TSI_WR( THEBASE, 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 = THEBASE; +IRQEntry ip; +unsigned long msk,lintstat,vector, vecarg; +int lvl; + + /* only handle interrupts routed to this pin */ + + /* NOTE: we read the status register over VME, thus flushing the FIFO + * where the user ISR possibly left write commands to clear + * the interrupt condition at the device. + * This is very important - otherwise, the IRQ level may still be + * asserted and we would detect an interrupt here but the subsequent + * IACK would fail since the write operation was flushed to the + * device in the mean time. + */ + while ( (lintstat = (TSI_RD(vmeTsi148RegBase, 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 :-( */ + vmeTsi148IntDisable(lvl); + printk("vmeTsi148 ISR: ERROR: no handler registered (level %i) IACK 0x%08lx -- DISABLING level %i\n", + lvl, vector, lvl); + } else { + /* dispatch handler, it must clear the IRQ at the device */ + ip->isr(ip->usrData, vecarg); + /* convenience for disobedient users who don't use in_xxx/out_xxx; make + * sure we order the subsequent read from the status register after + * their business. + */ + iobarrier_rw(); + } + } 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(THEBASE, TSI_INTEO_REG) & TSI_RD(THEBASE, TSI_INTEN_REG)); +} + +static void +connectIsr(int shared, rtems_irq_hdl isr, 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)) + rtems_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)) + rtems_panic("unable to install vmeTsi148 irq handler"); +#endif + } else { + if (!BSP_install_rtems_irq_handler(&xx)) + rtems_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; +} + +#ifndef BSP_EARLY_PROBE_VME +#define BSP_EARLY_PROBE_VME(addr) \ + ( \ + vmeTsi148ClearVMEBusErrorsXX( THEBASE, 0 ), \ + ( ((PCI_DEVICE_TSI148 << 16) | PCI_VENDOR_TUNDRA ) == TSI_LE_RD32( ((BERegister*)(addr)), 0 ) \ + && 0 == vmeTsi148ClearVMEBusErrorsXX( THEBASE, 0 ) ) \ + ) +#endif + +/* Check if there is a vme address/as is mapped in any of the outbound windows + * and look for the PCI vendordevice ID there. + * RETURNS: -1 on error (no mapping or probe failure), outbound window # (0..7) + * on success. Address translated into CPU address is returned in *pcpu_addr. + */ +static int +mappedAndProbed(unsigned long vme_addr, unsigned as, unsigned long *pcpu_addr) +{ +int j; +char *regtype = (as & VME_AM_MASK) == VME_AM_CSR ? "CSR" : "CRG"; + + /* try to find mapping */ + if ( 0 > (j = xlateFindPort( + THEBASE, + 1, 0, + as | VME_MODE_AS_MATCH, + vme_addr, + pcpu_addr ) ) ) { + uprintf(stderr,"vmeTsi148 - Unable to find mapping for %s VME base (0x%08x)\n", regtype, vme_addr); + uprintf(stderr," in outbound windows.\n"); + } + else { + /* found a slot number; probe it */ + *pcpu_addr = BSP_PCI2LOCAL_ADDR( *pcpu_addr ); + if ( BSP_EARLY_PROBE_VME(*pcpu_addr) ) { + uprintf(stderr,"vmeTsi148 - IRQ manager using VME %s to flush FIFO\n", regtype); + return j; + } else { + uprintf(stderr,"vmeTsi148 - Found slot info but detection of tsi148 in VME %s space failed\n", regtype); + } + } + return -1; +} + +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]; +unsigned long cpu_base, vme_reg_base; + + 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; + } + } + + i = -1; + + /* first try VME CSR space; do we have a base address set ? */ + + uprintf(stderr,"vmeTsi148 IRQ manager: looking for registers on VME...\n"); + + if ( ( i = ((TSI_RD( THEBASE, TSI_CBAR_REG ) & 0xff) >> 3) ) > 0 ) { + uprintf(stderr,"Trying to find CSR on VME...\n"); + vme_reg_base = i*0x80000 + TSI_CSR_OFFSET; + i = mappedAndProbed( vme_reg_base, VME_AM_CSR , &cpu_base); + if ( i >= 0 ) + vmeTsi148RegCSR = 1; + } else { + i = -1; + } + + if ( -1 == i ) { + + uprintf(stderr,"Trying to find CRG on VME...\n"); + + /* Next we see if the CRG block is mapped to VME */ + + if ( (TSI_CRGAT_EN & (j = TSI_RD( THEBASE, TSI_CRGAT_REG ))) ) { + switch ( j & TSI_CRGAT_AS_MSK ) { + case TSI_CRGAT_A16 : i = VME_AM_SUP_SHORT_IO; break; + case TSI_CRGAT_A24 : i = VME_AM_STD_SUP_DATA; break; + case TSI_CRGAT_A32 : i = VME_AM_EXT_SUP_DATA; break; + default: + break; + } + vme_reg_base = TSI_RD( THEBASE, TSI_CBAL_REG ) & ~ (TSI_CRG_SIZE - 1); + } + + if ( -1 == i ) { + } else { + i = mappedAndProbed( vme_reg_base, (i & VME_AM_MASK), &cpu_base ); + } + } + + if ( i < 0 ) { + uprintf(stderr,"vmeTsi148 IRQ manager - BSP configuration error: registers not found on VME\n"); + uprintf(stderr,"(should open outbound window to CSR space or map CRG [vmeTsi148MapCRG()])\n"); + uprintf(stderr,"Falling back to PCI but you might experience spurious VME interrupts; read a register\n"); + uprintf(stderr,"back from user ISR to flush posted-write FIFO as a work-around\n"); + cpu_base = (unsigned long)THEBASE; + i = -1; + } + + vmeTsi148RegBase = (BERegister*)cpu_base; + vmeTsi148RegPort = i; + + /* 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 = THEBASE; +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(THEBASE, 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 = THEBASE; +rtems_status_code sc; +rtems_id q = 0; +int installed = 0; +int i, err = 0; +int doDisable = 0; +size_t size; +unsigned long msg; +char * irqfmt = "VME IRQ @vector %3i %s"; +char * iackfmt = "VME IACK %s"; +LoopbackTstArgs a; + + CHECK_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, uint32_t *paddr) +{ +unsigned long rval; + + CHECK_BASE(base,1,-1); + + rval = TSI_RD(base, TSI_VEAT_REG); + if ( rval & TSI_VEAT_VES ) { + if ( paddr ) { +#if 0 /* no 64-bit support yet */ + *paddr = ((unsigned long long)TSI_RD(base, TSI_VEAU_REG))<<32; + *paddr |= TSI_RD(base, TSI_VEAL_REG); +#else + *paddr = TSI_RD(base, TSI_VEAL_REG); +#endif + } + /* clear errors */ + TSI_WR(base, TSI_VEAT_REG, TSI_VEAT_VESCL); + } else { + rval = 0; + } + return rval; +} + +unsigned long +vmeTsi148ClearVMEBusErrors(uint32_t *paddr) +{ + return vmeTsi148ClearVMEBusErrorsXX(THEBASE, paddr); +} + +/** DMA Support **/ + +/* descriptor must be 8-byte aligned */ +typedef struct VmeTsi148DmaListDescriptorRec_ { + BEValue dsau, dsal; + BEValue ddau, ddal; + BEValue dsat, ddat; + BEValue dnlau, dnlal; + BEValue dcnt, ddbs; +} VmeTsi148DmaListDescriptorRec; + +static void tsi_desc_init (DmaDescriptor); +static int tsi_desc_setup (DmaDescriptor, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); +static void tsi_desc_setnxt(DmaDescriptor, DmaDescriptor); +static void tsi_desc_dump (DmaDescriptor); +static int tsi_desc_start (volatile void *controller_addr, int channel, DmaDescriptor p); + +VMEDmaListClassRec vmeTsi148DmaListClass = { + desc_size: sizeof(VmeTsi148DmaListDescriptorRec), + desc_align: 8, + freeList: 0, + desc_alloc: 0, + desc_free: 0, + desc_init: tsi_desc_init, + desc_setnxt:tsi_desc_setnxt, + desc_setup: tsi_desc_setup, + desc_start: tsi_desc_start, + desc_refr: 0, + desc_dump: tsi_desc_dump, +}; + +/* DMA Control */ +#define TSI_DMA_REG(off,i) ((off)+(((i)&1)<<7)) + +#define TSI_DCTL_REG(i) TSI_DMA_REG(0x500,i) +#define TSI_DCTL0_REG 0x500 +#define TSI_DCTL1_REG 0x580 +# define TSI_DCTL_ABT (1<<27) /* abort */ +# define TSI_DCTL_PAU (1<<26) /* pause */ +# define TSI_DCTL_DGO (1<<25) /* GO */ +# define TSI_DCTL_MOD (1<<23) /* linked list: 0, direct: 1 */ +# define TSI_DCTL_VFAR (1<<17) /* flush FIFO on VME error: 1 (discard: 0) */ +# define TSI_DCTL_PFAR (1<<16) /* flush FIFO on PCI error: 1 (discard: 0) */ + +# define TSI_DCTL_VBKS(i) (((i)&7)<<12) /* VME block size */ +# define TSI_DCTL_VBKS_32 TSI_DCTL_VBKS(0) +# define TSI_DCTL_VBKS_64 TSI_DCTL_VBKS(1) +# define TSI_DCTL_VBKS_128 TSI_DCTL_VBKS(2) +# define TSI_DCTL_VBKS_256 TSI_DCTL_VBKS(3) +# define TSI_DCTL_VBKS_512 TSI_DCTL_VBKS(4) +# define TSI_DCTL_VBKS_1024 TSI_DCTL_VBKS(5) +# define TSI_DCTL_VBKS_2048 TSI_DCTL_VBKS(6) +# define TSI_DCTL_VBKS_4096 TSI_DCTL_VBKS(7) + +# define TSI_DCTL_VBOT(i) (((i)&7)<< 8) /* VME back-off time */ +# define TSI_DCTL_VBOT_0us TSI_DCTL_VBOT(0) +# define TSI_DCTL_VBOT_1us TSI_DCTL_VBOT(1) +# define TSI_DCTL_VBOT_2us TSI_DCTL_VBOT(2) +# define TSI_DCTL_VBOT_4us TSI_DCTL_VBOT(3) +# define TSI_DCTL_VBOT_8us TSI_DCTL_VBOT(4) +# define TSI_DCTL_VBOT_16us TSI_DCTL_VBOT(5) +# define TSI_DCTL_VBOT_32us TSI_DCTL_VBOT(6) +# define TSI_DCTL_VBOT_64us TSI_DCTL_VBOT(7) + +# define TSI_DCTL_PBKS(i) (((i)&7)<< 4) /* PCI block size */ +# define TSI_DCTL_PBKS_32 TSI_DCTL_PBKS(0) +# define TSI_DCTL_PBKS_64 TSI_DCTL_PBKS(1) +# define TSI_DCTL_PBKS_128 TSI_DCTL_PBKS(2) +# define TSI_DCTL_PBKS_256 TSI_DCTL_PBKS(3) +# define TSI_DCTL_PBKS_512 TSI_DCTL_PBKS(4) +# define TSI_DCTL_PBKS_1024 TSI_DCTL_PBKS(5) +# define TSI_DCTL_PBKS_2048 TSI_DCTL_PBKS(6) +# define TSI_DCTL_PBKS_4096 TSI_DCTL_PBKS(7) + +# define TSI_DCTL_PBOT(i) (((i)&7)<< 0) /* PCI back-off time */ +# define TSI_DCTL_PBOT_0us TSI_DCTL_PBOT(0) +# define TSI_DCTL_PBOT_1us TSI_DCTL_PBOT(1) +# define TSI_DCTL_PBOT_2us TSI_DCTL_PBOT(2) +# define TSI_DCTL_PBOT_4us TSI_DCTL_PBOT(3) +# define TSI_DCTL_PBOT_8us TSI_DCTL_PBOT(4) +# define TSI_DCTL_PBOT_16us TSI_DCTL_PBOT(5) +# define TSI_DCTL_PBOT_32us TSI_DCTL_PBOT(6) +# define TSI_DCTL_PBOT_64us TSI_DCTL_PBOT(7) + +/* DMA Status */ +#define TSI_DSTA_REG(i) TSI_DMA_REG(0x504,i) +#define TSI_DSTA0_REG 0x504 +#define TSI_DSTA1_REG 0x584 +# define TSI_DSTA_ERR (1<<28) +# define TSI_DSTA_ABT (1<<27) +# define TSI_DSTA_PAU (1<<26) +# define TSI_DSTA_DON (1<<25) +# define TSI_DSTA_BSY (1<<24) +# define TSI_DSTA_ERRS (1<<20) /* Error source; PCI:1, VME:0 */ +# define TSI_DSTA_ERT_MSK (3<<16) /* Error type */ +# define TSI_DSTA_ERT_BERR_E (0<<16) /* 2eVME even or other bus error */ +# define TSI_DSTA_ERT_BERR_O (1<<16) /* 2eVME odd bus error */ +# define TSI_DSTA_ERT_SLVE_E (2<<16) /* 2eVME even or other slave termination */ +# define TSI_DSTA_ERT_SLVE_O (3<<16) /* 2eVME odd slave termination; 2eSST read last word invalid */ + +/* DMA Current source address upper */ +#define TSI_DCSAU_REG(i) TSI_DMA_REG(0x508,i) +#define TSI_DCSAU0_REG 0x508 +#define TSI_DCSAU1_REG 0x588 + +/* DMA Current source address lower */ +#define TSI_DCSAL_REG(i) TSI_DMA_REG(0x50c,i) +#define TSI_DCSAL0_REG 0x50c +#define TSI_DCSAL1_REG 0x58c + +/* DMA Current destination address upper */ +#define TSI_DCDAU_REG(i) TSI_DMA_REG(0x510,i) +#define TSI_DCDAU0_REG 0x510 +#define TSI_DCDAU1_REG 0x590 + +/* DMA Current destination address lower */ +#define TSI_DCDAL_REG(i) TSI_DMA_REG(0x514,i) +#define TSI_DCDAL0_REG 0x514 +#define TSI_DCDAL1_REG 0x594 + +/* DMA Current link address upper */ +#define TSI_DCLAU_REG(i) TSI_DMA_REG(0x518,i) +#define TSI_DCLAU0_REG 0x518 +#define TSI_DCLAU1_REG 0x598 + +/* DMA Current link address lower */ +#define TSI_DCLAL_REG(i) TSI_DMA_REG(0x51c,i) +#define TSI_DCLAL0_REG 0x51c +#define TSI_DCLAL1_REG 0x59c + +/* DMA Source address upper */ +#define TSI_DSAU_REG(i) TSI_DMA_REG(0x520,i) +#define TSI_DSAU0_REG 0x520 +#define TSI_DSAU1_REG 0x5a0 + +/* DMA Source address lower */ +#define TSI_DSAL_REG(i) TSI_DMA_REG(0x524,i) +#define TSI_DSAL0_REG 0x524 +#define TSI_DSAL1_REG 0x5a4 + +/* DMA Destination address upper */ +#define TSI_DDAU_REG(i) TSI_DMA_REG(0x528,i) +#define TSI_DDAU0_REG 0x528 +#define TSI_DDAU1_REG 0x5a8 + +/* DMA Destination address lower */ +#define TSI_DDAL_REG(i) TSI_DMA_REG(0x52c,i) +#define TSI_DDAL0_REG 0x52c +#define TSI_DDAL1_REG 0x5ac + +/* DMA Source Attribute */ +#define TSI_DSAT_REG(i) TSI_DMA_REG(0x530,i) +#define TSI_DSAT0_REG 0x530 +#define TSI_DSAT1_REG 0x5b0 + +/* DMA Destination Attribute */ +#define TSI_DDAT_REG(i) TSI_DMA_REG(0x534,i) +#define TSI_DDAT0_REG 0x534 +#define TSI_DDAT1_REG 0x5b4 + +# define TSI_DXAT_TYP(i) (((i)&3)<<28) /* Xfer type */ +# define TSI_DXAT_TYP_PCI TSI_DXAT_TYP(0) +# define TSI_DXAT_TYP_VME TSI_DXAT_TYP(1) +# define TSI_DSAT_TYP_PAT TSI_DXAT_TYP(2) /* pattern */ + +# define TSI_DSAT_PSZ (1<<25) /* pattern size 32-bit: 0, 8-bit: 1 */ +# define TSI_DSAT_NIN (1<<24) /* no-increment */ + +# define TSI_DXAT_OTAT_MSK ((1<<13)-1) /* get bits compatible with OTAT */ + +# define TSI_DXAT_SSTM(i) (((i)&3)<<11) /* 2eSST Xfer rate (MB/s) */ +# define TSI_DXAT_SSTM_116 TSI_DXAT_SSTM(0) +# define TSI_DXAT_SSTM_267 TSI_DXAT_SSTM(1) +# define TSI_DXAT_SSTM_320 TSI_DXAT_SSTM(2) + +# define TSI_DXAT_TM(i) (((i)&7)<< 8) /* VME Xfer mode */ +# define TSI_DXAT_TM_SCT TSI_DXAT_TM(0) +# define TSI_DXAT_TM_BLT TSI_DXAT_TM(1) +# define TSI_DXAT_TM_MBLT TSI_DXAT_TM(2) +# define TSI_DXAT_TM_2eVME TSI_DXAT_TM(3) +# define TSI_DXAT_TM_2eSST TSI_DXAT_TM(4) +# define TSI_DSAT_TM_2eSST_B TSI_DXAT_TM(5) /* 2eSST broadcast */ + +# define TSI_DXAT_DBW(i) (((i)&3)<< 6) /* VME Data width */ +# define TSI_DXAT_DBW_16 TSI_DXAT_DBW(0) +# define TSI_DXAT_DBW_32 TSI_DXAT_DBW(1) + +# define TSI_DXAT_SUP (1<<5) /* supervisor access */ +# define TSI_DXAT_PGM (1<<4) /* program access */ + +# define TSI_DXAT_AM(i) (((i)&15)<<0) /* VME Address mode */ +# define TSI_DXAT_AM_A16 TSI_DXAT_AM(0) +# define TSI_DXAT_AM_A24 TSI_DXAT_AM(1) +# define TSI_DXAT_AM_A32 TSI_DXAT_AM(2) +# define TSI_DXAT_AM_A64 TSI_DXAT_AM(4) +# define TSI_DXAT_AM_CSR TSI_DXAT_AM(5) + +/* DMA Next link address upper */ +#define TSI_DNLAU_REG(i) TSI_DMA_REG(0x538,i) +#define TSI_DNLAU0_REG 0x538 +#define TSI_DNLAU1_REG 0x5b8 + +/* DMA Next link address lower */ +#define TSI_DNLAL_REG(i) TSI_DMA_REG(0x53c,i) +#define TSI_DNLAL0_REG 0x53c +#define TSI_DNLAL1_REG 0x5bc + +# define TSI_DNLAL_LLA 1 /* last element in chain */ + +/* DMA Byte Count */ +#define TSI_DCNT_REG(i) TSI_DMA_REG(0x540,i) +#define TSI_DCNT0_REG 0x540 +#define TSI_DCNT1_REG 0x54c + +/* DMA 2eSST destination broadcast select */ +#define TSI_DDBS_REG(i) TSI_DMA_REG(0x544,i) +#define TSI_DDBS0_REG 0x544 +#define TSI_DDBS1_REG 0x5c4 + +/* Convert canonical xfer_mode into Tsi148 bits; return -1 if invalid */ +static uint32_t +vme_attr(uint32_t xfer_mode) +{ +uint32_t vme_mode; + if ( am2omode(xfer_mode, &vme_mode) ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* am2omode may set prefetch and other bits */ + vme_mode &= TSI_DXAT_OTAT_MSK; + vme_mode |= TSI_DXAT_TYP_VME; + + if ( BSP_VMEDMA_MODE_NOINC_VME & xfer_mode ) { + /* no-incr. only supported on source address */ + if ( (BSP_VMEDMA_MODE_PCI2VME & xfer_mode) ) + return BSP_VMEDMA_STATUS_UNSUP; + vme_mode |= TSI_DSAT_NIN; + } + + return vme_mode; +} + +static uint32_t +pci_attr(uint32_t xfer_mode) +{ +uint32_t pci_mode = 0; + if ( BSP_VMEDMA_MODE_NOINC_PCI & xfer_mode ) { + /* no-incr. only supported on source address */ + if ( ! (BSP_VMEDMA_MODE_PCI2VME & xfer_mode) ) + return BSP_VMEDMA_STATUS_UNSUP; + pci_mode |= TSI_DSAT_NIN; + } + return pci_mode; +} + +static void tsi_desc_init(DmaDescriptor p) +{ +VmeTsi148DmaListDescriptor d = p; + st_be32( &d->dnlau, 0 ); + st_be32( &d->dnlal, TSI_DNLAL_LLA ); + st_be32( &d->ddbs, (1<<22)-1 ); /* SSTB broadcast not yet fully supported */ +} + +static void +tsi_desc_setnxt(DmaDescriptor p, DmaDescriptor n) +{ +VmeTsi148DmaListDescriptor d = p; + if ( 0 == n ) { + st_be32( &d->dnlal, TSI_DNLAL_LLA ); + } else { + st_be32( &d->dnlal, BSP_LOCAL2PCI_ADDR((uint32_t)n) ); + } +} + +static void +tsi_desc_dump(DmaDescriptor p) +{ +VmeTsi148DmaListDescriptor d = p; + printf(" DSA: 0x%08lx%08lx\n", ld_be32(&d->dsau), ld_be32(&d->dsal)); + printf(" DDA: 0x%08lx%08lx\n", ld_be32(&d->ddau), ld_be32(&d->ddal)); + printf(" NLA: 0x%08lx%08lx\n", ld_be32(&d->dnlau), ld_be32(&d->dnlal)); + printf(" SAT: 0x%08lx DAT: 0x%08lx\n", ld_be32(&d->dsat), ld_be32(&d->ddat)); + printf(" CNT: 0x%08lx\n", ld_be32(&d->dcnt)); +} + + +int +vmeTsi148DmaSetupXX(BERegister *base, int channel, uint32_t mode, uint32_t xfer_mode, void *custom) +{ +uint32_t ctl = 0; +uint32_t vmeatt, pciatt, sat, dat; + + if ( channel < 0 || channel > 1 ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* Check bus mode */ + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == (vmeatt = vme_attr(xfer_mode)) ) + return -2; + + /* Check PCI bus mode */ + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == (pciatt = pci_attr(xfer_mode)) ) + return -3; + + /* Compute control word; bottleneck is VME; */ + ctl |= TSI_DCTL_PBKS_32; + ctl |= (BSP_VMEDMA_OPT_THROUGHPUT == mode ? TSI_DCTL_PBOT_0us : TSI_DCTL_PBOT_1us); + + switch ( mode ) { + case BSP_VMEDMA_OPT_THROUGHPUT: + ctl |= TSI_DCTL_VBKS_1024; + ctl |= TSI_DCTL_VBOT_0us; + break; + + case BSP_VMEDMA_OPT_LOWLATENCY: + ctl |= TSI_DCTL_VBKS_32; + ctl |= TSI_DCTL_VBOT_0us; + break; + + case BSP_VMEDMA_OPT_SHAREDBUS: + ctl |= TSI_DCTL_VBKS_128; + ctl |= TSI_DCTL_VBOT_64us; + break; + + case BSP_VMEDMA_OPT_CUSTOM: + ctl = *(uint32_t*)custom; + break; + + default: + case BSP_VMEDMA_OPT_DEFAULT: + ctl = 0; + break; + } + TSI_WR(base, TSI_DCTL_REG(channel), ctl); + if ( BSP_VMEDMA_MODE_PCI2VME & xfer_mode ) { + dat = vmeatt; sat = pciatt; + } else { + sat = vmeatt; dat = pciatt; + } + TSI_WR(base, TSI_DSAT_REG(channel), sat); + TSI_WR(base, TSI_DDAT_REG(channel), dat); + return 0; +} + +int +vmeTsi148DmaSetup(int channel, uint32_t mode, uint32_t xfer_mode, void *custom) +{ +BERegister *base = THEBASE; + return vmeTsi148DmaSetupXX(base, channel, mode, xfer_mode, custom); +} + + +int +vmeTsi148DmaListStartXX(BERegister *base, int channel, VmeTsi148DmaListDescriptor d) +{ +uint32_t ctl; + + if ( d ) { + /* Set list pointer and start */ + if ( channel < 0 || channel > 1 ) + return BSP_VMEDMA_STATUS_UNSUP; + + if ( TSI_DSTA_BSY & TSI_RD(base, TSI_DSTA_REG(channel)) ) + return BSP_VMEDMA_STATUS_BUSY; /* channel busy */ + + TSI_WR(base, TSI_DNLAL_REG(channel), (uint32_t)BSP_LOCAL2PCI_ADDR(d)); + + asm volatile("":::"memory"); + + /* Start transfer */ + ctl = TSI_RD(base, TSI_DCTL_REG(channel)) | TSI_DCTL_DGO; + ctl &= ~TSI_DCTL_MOD; + TSI_WR(base, TSI_DCTL_REG(channel), ctl); + } + /* else: list vs. direct mode is set by the respective start commands */ + return 0; +} + +int +vmeTsi148DmaListStart(int channel, VmeTsi148DmaListDescriptor d) +{ +BERegister *base = THEBASE; + return vmeTsi148DmaListStartXX(base, channel, d); +} + +int +vmeTsi148DmaStartXX(BERegister *base, int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ +uint32_t src, dst, ctl; + + if ( channel < 0 || channel > 1 ) + return BSP_VMEDMA_STATUS_UNSUP; + + if ( TSI_DSTA_BSY & TSI_RD(base, TSI_DSTA_REG(channel)) ) + return BSP_VMEDMA_STATUS_BUSY; /* channel busy */ + + /* retrieve direction from dst attribute */ + if ( TSI_DXAT_TYP_VME & TSI_RD(base, TSI_DDAT_REG(channel)) ) { + dst = vme_addr; + src = pci_addr; + } else { + src = vme_addr; + dst = pci_addr; + } + /* FIXME: we leave the 'upper' registers (topmost 32bits) alone. + * Probably, we should reset them at init... + */ + TSI_WR(base, TSI_DSAL_REG(channel), src); + TSI_WR(base, TSI_DDAL_REG(channel), dst); + TSI_WR(base, TSI_DCNT_REG(channel), n_bytes); + + asm volatile("":::"memory"); + + /* Start transfer */ + ctl = TSI_RD(base, TSI_DCTL_REG(channel)) | TSI_DCTL_DGO | TSI_DCTL_MOD; + TSI_WR(base, TSI_DCTL_REG(channel), ctl); + + return 0; +} + +int +vmeTsi148DmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ +BERegister *base = THEBASE; + return vmeTsi148DmaStartXX(base, channel, pci_addr, vme_addr, n_bytes); +} + +uint32_t +vmeTsi148DmaStatusXX(BERegister *base, int channel) +{ +uint32_t st = TSI_RD(base, TSI_DSTA_REG(channel)); + + if ( channel < 0 || channel > 1 ) + return BSP_VMEDMA_STATUS_UNSUP; + + st = TSI_RD(base, TSI_DSTA_REG(channel)); + + /* Status can be zero if an empty list (all counts == 0) is executed */ + if ( (TSI_DSTA_DON & st) || 0 == st ) + return BSP_VMEDMA_STATUS_OK; + + if ( TSI_DSTA_BSY & st ) + return BSP_VMEDMA_STATUS_BUSY; /* channel busy */ + + if ( TSI_DSTA_ERR & st ) { + if ( TSI_DSTA_ERRS & st ) + return BSP_VMEDMA_STATUS_BERR_PCI; + if ( ! (TSI_DSTA_ERT_SLVE_E & st) ) + return BSP_VMEDMA_STATUS_BERR_VME; + } + + return BSP_VMEDMA_STATUS_OERR; +} + +uint32_t +vmeTsi148DmaStatus(int channel) +{ +BERegister *base = THEBASE; + return vmeTsi148DmaStatusXX(base, channel); +} + +#define ALL_BITS_NEEDED (BSP_VMEDMA_MSK_ATTR | BSP_VMEDMA_MSK_PCIA | BSP_VMEDMA_MSK_VMEA) + +static int +tsi_desc_setup ( + DmaDescriptor p, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ +VmeTsi148DmaListDescriptor d = p; +uint32_t vmeatt = 0, pciatt = 0, tmp, src, dst, dat, sat; + + /* argument check */ + + /* since we must vme/pci into src/dst we need the direction + * bit. Reject requests that have only part of the mask + * bits set. It would be possible to be more sophisticated + * by caching more information but we try to be simple here... + */ + tmp = attr_mask & ALL_BITS_NEEDED; + if ( tmp != 0 && tmp != ALL_BITS_NEEDED ) + return -1; + + if ( BSP_VMEDMA_MSK_ATTR & attr_mask ) { + /* Check VME bus mode */ + vmeatt = vme_attr(xfer_mode); + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == vmeatt ) + return -1; + + /* Check PCI bus mode */ + pciatt = pci_attr(xfer_mode); + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == pciatt ) + return -1; + } + + if ( BSP_VMEDMA_MSK_ATTR & attr_mask ) { + if ( BSP_VMEDMA_MODE_PCI2VME & xfer_mode ) { + dat = vmeatt; sat = pciatt; + dst = vme_addr; src = pci_addr; + } else { + sat = vmeatt; dat = pciatt; + src = vme_addr; dst = pci_addr; + } + st_be32( &d->dsau, 0 ); st_be32( &d->dsal, src ); + st_be32( &d->ddau, 0 ); st_be32( &d->ddal, dst ); + st_be32( &d->dsat, sat ); st_be32( &d->ddat, dat ); + } + + if ( BSP_VMEDMA_MSK_BCNT & attr_mask ) + st_be32( &d->dcnt, n_bytes); + + return 0; +} + +static int +tsi_desc_start (volatile void *controller_addr, int channel, DmaDescriptor p) +{ +VmeTsi148DmaListDescriptor d = p; + if ( !controller_addr ) + controller_addr = THEBASE; + return vmeTsi148DmaListStartXX((BERegister*)controller_addr, channel, d); +} + +#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(THEBASE, 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/bsps/powerpc/shared/vme/vmeUniverse.c b/bsps/powerpc/shared/vme/vmeUniverse.c new file mode 100644 index 0000000000..c7373b4e51 --- /dev/null +++ b/bsps/powerpc/shared/vme/vmeUniverse.c @@ -0,0 +1,2504 @@ +/* Driver for the Tundra Universe II pci-vme bridge */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2000-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <stdio.h> +#include <inttypes.h> + +#if defined(__rtems__) +#ifndef __INSIDE_RTEMS_BSP__ +#define __INSIDE_RTEMS_BSP__ +#endif +#endif + +#include <bsp/vmeUniverse.h> +#include <bsp/vmeUniverseDMA.h> + +#define UNIV_NUM_MPORTS 8 /* number of master ports */ +#define UNIV_NUM_SPORTS 8 /* number of slave ports */ + +#define PCI_VENDOR_TUNDRA 0x10e3 +#define PCI_DEVICE_UNIVERSEII 0 +#define PCI_UNIVERSE_BASE0 0x10 +#define PCI_UNIVERSE_BASE1 0x14 + +#define UNIV_REGOFF_PCITGT0_CTRL 0x100 +#define UNIV_REGOFF_PCITGT4_CTRL 0x1a0 +#define UNIV_REGOFF_VMESLV0_CTRL 0xf00 +#define UNIV_REGOFF_VMESLV4_CTRL 0xf90 + +#define UNIV_CTL_VAS16 (0x00000000) +#define UNIV_CTL_VAS24 (0x00010000) +#define UNIV_CTL_VAS32 (0x00020000) +#define UNIV_MCTL_VASCSR (0x00050000) +#define UNIV_CTL_VAS (0x00070000) + +#define UNIV_MCTL_EN (0x80000000) +#define UNIV_MCTL_PWEN (0x40000000) +#define UNIV_MCTL_PGM (0x00004000) +#define UNIV_MCTL_VCT (0x00000100) +#define UNIV_MCTL_SUPER (0x00001000) +#define UNIV_MCTL_VDW16 (0x00400000) +#define UNIV_MCTL_VDW32 (0x00800000) +#define UNIV_MCTL_VDW64 (0x00c00000) + +#define UNIV_MCTL_AM_MASK (UNIV_CTL_VAS | UNIV_MCTL_PGM | UNIV_MCTL_SUPER) + +#define UNIV_SCTL_EN (0x80000000) +#define UNIV_SCTL_PWEN (0x40000000) +#define UNIV_SCTL_PREN (0x20000000) +#define UNIV_SCTL_PGM (0x00800000) +#define UNIV_SCTL_DAT (0x00400000) +#define UNIV_SCTL_SUPER (0x00200000) +#define UNIV_SCTL_USER (0x00100000) + +#define UNIV_SCTL_AM_MASK (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER) + +#ifdef __rtems__ + +#include <stdlib.h> +#include <rtems/bspIo.h> /* printk */ +#include <rtems/error.h> +#include <rtems/pci.h> +#include <bsp.h> +#include <libcpu/byteorder.h> + +/* allow the BSP to override the default routines */ +#ifndef BSP_PCI_FIND_DEVICE +#define BSP_PCI_FIND_DEVICE pci_find_device +#endif +#ifndef BSP_PCI_CONFIG_IN_LONG +#define BSP_PCI_CONFIG_IN_LONG pci_read_config_dword +#endif +#ifndef BSP_PCI_CONFIG_IN_BYTE +#define BSP_PCI_CONFIG_IN_BYTE pci_read_config_byte +#endif +#ifndef BSP_PCI_CONFIG_IN_SHORT +#define BSP_PCI_CONFIG_IN_SHORT pci_read_config_word +#endif +#ifndef BSP_PCI_CONFIG_OUT_SHORT +#define BSP_PCI_CONFIG_OUT_SHORT pci_write_config_word +#endif + +/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses. + * Should be defined by the BSP. + */ +typedef uint32_t pci_ulong; + +#ifndef BSP_PCI2LOCAL_ADDR +#ifndef PCI_MEM_BASE +#define PCI_MEM_BASE 0 +#endif +#define BSP_PCI2LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE) +#endif + +#ifndef BSP_LOCAL2PCI_ADDR +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif +#define BSP_LOCAL2PCI_ADDR(pciaddr) ((uint32_t)(pciaddr) + PCI_DRAM_OFFSET) +#endif + + +#elif defined(__vxworks) +typedef unsigned long pci_ulong; +#define BSP_PCI2LOCAL_ADDR(memaddr) (memaddr) +#define BSP_PCI_FIND_DEVICE pciFindDevice +#define BSP_PCI_CONFIG_IN_LONG pciConfigInLong +#define BSP_PCI_CONFIG_IN_BYTE pciConfigInByte +#else +#error "vmeUniverse not ported to this architecture yet" +#endif + +#ifndef PCI_INTERRUPT_LINE +#define PCI_INTERRUPT_LINE 0x3c +#endif + +volatile LERegister *vmeUniverse0BaseAddr=0; +int vmeUniverse0PciIrqLine=-1; + +#ifdef __rtems__ +int vmeUniverseRegPort = -1; +int vmeUniverseRegCSR = 0; +#endif + +#define DFLT_BASE volatile LERegister *base = vmeUniverse0BaseAddr + +#define CHECK_DFLT_BASE(base) \ + do { \ + /* get the universe base address */ \ + if (!base) { \ + if (vmeUniverseInit()) { \ + uprintf(stderr,"unable to find the universe in pci config space\n"); \ + return -1; \ + } else { \ + base = vmeUniverse0BaseAddr; \ + } \ + } \ + } while (0) + +#if 0 +/* public access functions */ +volatile LERegister * +vmeUniverseBaseAddr(void) +{ + if (!vmeUniverse0BaseAddr) vmeUniverseInit(); + return vmeUniverse0BaseAddr; +} + +int +vmeUniversePciIrqLine(void) +{ + if (vmeUniverse0PciIrqLine<0) vmeUniverseInit(); + return vmeUniverse0PciIrqLine; +} +#endif + +static inline void +WRITE_LE( + unsigned long val, + volatile LERegister *adrs, + unsigned long off) +{ +#if (__LITTLE_ENDIAN__ == 1) + *(volatile unsigned long*)(((unsigned long)adrs)+off)=val; +#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1) + /* offset is in bytes and MUST not end up in r0 */ + __asm__ __volatile__("stwbrx %1, %0, %2" :: "b"(off),"r"(val),"r"(adrs)); +#elif defined(__rtems__) + st_le32((volatile uint32_t *)(((uint32_t)adrs)+off), val); +#else +#error "little endian register writing not implemented" +#endif +} + +#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) +#define SYNC __asm__ __volatile__("sync") +#else +#define SYNC +#warning "SYNC instruction unknown for this architecture" +#endif + +/* registers should be mapped to guarded, non-cached memory; hence + * subsequent stores are ordered. eieio is only needed to enforce + * ordering of loads with respect to stores. + */ +#define EIEIO_REG + +static inline unsigned long +READ_LE0(volatile LERegister *adrs) +{ +#if (__LITTLE_ENDIAN__ == 1) + return *(volatile unsigned long *)adrs; +#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1) +register unsigned long rval; +__asm__ __volatile__("lwbrx %0, 0, %1":"=r"(rval):"r"(adrs)); + return rval; +#elif defined(__rtems__) + return ld_le32((volatile uint32_t*)adrs); +#else +#error "little endian register reading not implemented" +#endif +} + +static inline unsigned long +READ_LE(volatile LERegister *adrs, unsigned long off) +{ +#if (__LITTLE_ENDIAN__ == 1) + return *((volatile LERegister *)(((unsigned long)adrs)+off)); +#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1) +register unsigned long rval; + /* offset is in bytes and MUST not end up in r0 */ +__asm__ __volatile__("lwbrx %0, %2, %1" + : "=r"(rval) + : "r"(adrs), "b"(off)); +#if 0 +__asm__ __volatile__("eieio"); +#endif +return rval; +#else +return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off)); +#endif +} + +#define PORT_UNALIGNED(addr,port) \ + ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) ) + + +#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff) + +#if defined(__rtems__) && 0 +static int +uprintk(char *fmt, va_list ap) +{ +int rval; +extern int k_vsprintf(char *, char *, va_list); +/* during bsp init, there is no malloc and no stdio, + * hence we assemble the message on the stack and revert + * to printk + */ +char buf[200]; + rval = k_vsprintf(buf,fmt,ap); + if (rval > sizeof(buf)) + rtems_panic("vmeUniverse/uprintk: buffer overrun"); + printk(buf); + return rval; +} +#endif + + +/* private printing wrapper */ +static void +uprintf(FILE *f, char *fmt, ...) +{ +va_list ap; + va_start(ap, fmt); +#ifdef __rtems__ + if (!f || !_impure_ptr->__sdidinit) { + /* Might be called at an early stage when + * stdio is not yet initialized. + * There is no vprintk, hence we must assemble + * to a buffer. + */ + vprintk(fmt,ap); + } else +#endif + { + vfprintf(f,fmt,ap); + } + va_end(ap); +} + +static int +vmeUniverseFindPciBase( + int instance, + volatile LERegister **pbase + ) +{ +int bus,dev,fun; +unsigned short wrd; +pci_ulong busaddr; +unsigned char irqline; + + if (BSP_PCI_FIND_DEVICE( + PCI_VENDOR_TUNDRA, + PCI_DEVICE_UNIVERSEII, + instance, + &bus, + &dev, + &fun)) + return -1; + if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE0,&busaddr)) + return -1; + if ((unsigned long)(busaddr) & 1) { + /* it's IO space, try BASE1 */ + if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE1,&busaddr) + || ((unsigned long)(busaddr) & 1)) + return -1; + } + *pbase=(volatile LERegister*)BSP_PCI2LOCAL_ADDR(busaddr); + + if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline)) + return -1; + + /* Enable PCI master and memory access */ + BSP_PCI_CONFIG_IN_SHORT(bus, dev, fun, PCI_COMMAND, &wrd); + BSP_PCI_CONFIG_OUT_SHORT(bus, dev, fun, PCI_COMMAND, wrd | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + return irqline; +} + +/* convert an address space selector to a corresponding + * universe control mode word + */ + +static int +am2mode(int ismaster, unsigned long address_space, unsigned long *pmode) +{ +unsigned long mode=0; +unsigned long vdw =0; + + /* NOTE: reading the CY961 (Echotek ECDR814) with VDW32 + * generated bus errors when reading 32-bit words + * - very weird, because the registers are 16-bit + * AFAIK. + * - 32-bit accesses worked fine on vxWorks which + * has the port set to 64-bit. + * ???????? + */ + + address_space &= ~VME_MODE_MATCH_MASK; + + if (!ismaster) { + mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM; + mode |= UNIV_SCTL_USER; + if ( VME_AM_IS_MEMORY & address_space ) + mode |= UNIV_SCTL_PWEN | UNIV_SCTL_PREN; + mode |= UNIV_SCTL_EN; + } else { + switch ( VME_MODE_DBW_MSK & address_space ) { + default: + vdw = UNIV_MCTL_VDW64; + break; + + case VME_MODE_DBW8: + break; + + case VME_MODE_DBW16: + vdw = UNIV_MCTL_VDW16; + break; + + case VME_MODE_DBW32: + vdw = UNIV_MCTL_VDW32; + break; + } + if ( VME_AM_IS_MEMORY & address_space ) + mode |= UNIV_MCTL_PWEN; + mode |= UNIV_MCTL_EN; + } + + address_space &= ~VME_AM_IS_MEMORY; + + switch (address_space & VME_AM_MASK) { + case VME_AM_STD_SUP_PGM: + case VME_AM_STD_USR_PGM: + if (ismaster) + mode |= UNIV_MCTL_PGM ; + else { + mode &= ~UNIV_SCTL_DAT; + } + + /* fall thru */ + + case VME_AM_STD_SUP_DATA: + case VME_AM_STD_USR_DATA: + case VME_AM_STD_SUP_BLT: + case VME_AM_STD_SUP_MBLT: + case VME_AM_STD_USR_BLT: + case VME_AM_STD_USR_MBLT: + + if ( ismaster ) { + switch ( address_space & 3 ) { + case 0: /* mblt */ + if ( UNIV_MCTL_VDW64 != vdw ) + return -1; + break; + + case 3: /* blt */ + mode |= UNIV_MCTL_VCT; + /* universe may do mblt anyways so go back to + * 32-bit width + */ + vdw = UNIV_MCTL_VDW32; + } + } + + mode |= UNIV_CTL_VAS24; + break; + + + case VME_AM_EXT_SUP_PGM: + case VME_AM_EXT_USR_PGM: + if (ismaster) + mode |= UNIV_MCTL_PGM ; + else { + mode &= ~UNIV_SCTL_DAT; + } + /* fall thru */ + + case VME_AM_EXT_SUP_DATA: + case VME_AM_EXT_USR_DATA: + case VME_AM_EXT_SUP_BLT: + case VME_AM_EXT_SUP_MBLT: + case VME_AM_EXT_USR_BLT: + case VME_AM_EXT_USR_MBLT: + + if ( ismaster ) { + switch ( address_space & 3 ) { + case 0: /* mblt */ + if ( UNIV_MCTL_VDW64 != vdw ) + return -1; + break; + + case 3: /* blt */ + mode |= UNIV_MCTL_VCT; + /* universe may do mblt anyways so go back to + * 32-bit width + */ + vdw = UNIV_MCTL_VDW32; + } + } + + mode |= UNIV_CTL_VAS32; + + break; + + case VME_AM_SUP_SHORT_IO: + case VME_AM_USR_SHORT_IO: + mode |= UNIV_CTL_VAS16; + break; + + case VME_AM_CSR: + if ( !ismaster ) + return -1; + mode |= UNIV_MCTL_VASCSR; + break; + + case 0: /* disable the port alltogether */ + break; + + default: + return -1; + } + if ( VME_AM_IS_SUP(address_space) ) + mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER); + + mode |= vdw; /* vdw still 0 in slave mode */ + *pmode = mode; + return 0; +} + +static int +disableUniversePort(int ismaster, int portno, volatile unsigned long *preg, void *param) +{ +unsigned long cntrl; + cntrl=READ_LE0(preg); + cntrl &= ~(ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN); + WRITE_LE(cntrl,preg,0); + SYNC; /* make sure this command completed */ + return 0; +} + +static int +cfgUniversePort( + volatile LERegister *base, + unsigned long ismaster, + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long local_address, + unsigned long length) +{ +volatile LERegister *preg; +unsigned long p=port; +unsigned long mode=0; + + CHECK_DFLT_BASE(base); + + /* check parameters */ + if (port >= (ismaster ? UNIV_NUM_MPORTS : UNIV_NUM_SPORTS)) { + uprintf(stderr,"invalid port\n"); + return -1; + } + /* port start, bound addresses and offset must lie on 64k boundary + * (4k for port 0 and 4) + */ + if ( PORT_UNALIGNED(local_address,port) ) { + uprintf(stderr,"local address misaligned\n"); + return -1; + } + if ( PORT_UNALIGNED(vme_address,port) ) { + uprintf(stderr,"vme address misaligned\n"); + return -1; + } + if ( PORT_UNALIGNED(length,port) ) { + uprintf(stderr,"length misaligned\n"); + return -1; + } + + /* check address space validity */ + if (am2mode(ismaster,address_space,&mode)) { + uprintf(stderr,"invalid address space\n"); + return -1; + } + + /* get the universe base address */ + if (!base && vmeUniverseInit()) { + return -1; + } + + preg=base; + + /* find out if we have a rev. II chip */ + if ( UNIV_REV(base) < 2 ) { + if (port>3) { + uprintf(stderr,"Universe rev. < 2 has only 4 ports\n"); + return -1; + } + } + + /* finally, configure the port */ + + /* find the register set for our port */ + if (port<4) { + preg += (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister); + } else { + preg += (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister); + p-=4; + } + preg += 5 * p; + + /* temporarily disable the port */ + disableUniversePort(ismaster,port,preg,0); + + /* address_space == 0 means disable */ + if (address_space != 0) { + unsigned long start,offst; + /* set the port starting address; + * this is the local address for the master + * and the VME address for the slave + */ + if (ismaster) { + start=local_address; + /* let it overflow / wrap around 0 */ + offst=vme_address-local_address; + } else { + start=vme_address; + /* let it overflow / wrap around 0 */ + offst=local_address-vme_address; + } +#undef TSILL +#ifdef TSILL + uprintf(stderr,"writing 0x%08x to 0x%08x + 4\n",start,preg); +#else + WRITE_LE(start,preg,4); +#endif + /* set bound address */ + length+=start; +#ifdef TSILL + uprintf(stderr,"writing 0x%08x to 0x%08x + 8\n",length,preg); +#else + WRITE_LE(length,preg,8); +#endif + /* set offset */ +#ifdef TSILL + uprintf(stderr,"writing 0x%08x to 0x%08x + 12\n",offst,preg); +#else + WRITE_LE(offst,preg,12); +#endif + +#ifdef TSILL + uprintf(stderr,"writing 0x%08x to 0x%08x + 0\n",mode,preg); +#else + EIEIO_REG; /* make sure mode is written last */ + WRITE_LE(mode,preg,0); + SYNC; /* enforce completion */ +#endif + +#ifdef TSILL + uprintf(stderr, + "universe %s port %lu successfully configured\n", + ismaster ? "master" : "slave", + port); +#endif + +#ifdef __vxworks + if (ismaster) + uprintf(stderr, + "WARNING: on the synergy, sysMasterPortsShow() may show incorrect settings (it uses cached values)\n"); +#endif + } + return 0; +} + +static int +showUniversePort( + int ismaster, + int portno, + volatile LERegister *preg, + void *parm) +{ + FILE *f=parm ? (FILE *)parm : stdout; + unsigned long cntrl, start, bound, offst, mask; + + cntrl = READ_LE0(preg++); +#undef TSILL +#ifdef TSILL + uprintf(stderr,"showUniversePort: *(0x%08x): 0x%08x\n",preg-1,cntrl); +#endif +#undef TSILL + + /* skip this port if disabled */ + if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN))) + return 0; + + /* for the master `start' is the PCI address, + * for the slave `start' is the VME address + */ + mask = ~PORT_UNALIGNED(0xffffffff,portno); + + start = READ_LE0(preg++)&mask; + bound = READ_LE0(preg++)&mask; + offst = READ_LE0(preg++)&mask; + + offst+=start; /* calc start on the other bus */ + + if (ismaster) { + uprintf(f,"%d: 0x%08lx 0x%08lx 0x%08lx ", + portno,offst,bound-start,start); + } else { + uprintf(f,"%d: 0x%08lx 0x%08lx 0x%08lx ", + portno,start,bound-start,offst); + } + + switch (cntrl & UNIV_CTL_VAS) { + case UNIV_CTL_VAS16: uprintf(f,"A16, "); break; + case UNIV_CTL_VAS24: uprintf(f,"A24, "); break; + case UNIV_CTL_VAS32: uprintf(f,"A32, "); break; + case UNIV_MCTL_VASCSR: if ( ismaster ) { uprintf(f,"CSR, "); break; } + /* else fallthru */ + default: uprintf(f,"A??, "); break; + } + + if (ismaster) { + unsigned vdw; + switch ( cntrl & UNIV_MCTL_VDW64 ) { + case UNIV_MCTL_VDW64: + vdw = 64; + break; + + case UNIV_MCTL_VDW32: + vdw = 32; + break; + + case UNIV_MCTL_VDW16: + vdw = 16; + break; + + default: + vdw = 8; + break; + } + + if ( 64 == vdw ) { + switch ( UNIV_CTL_VAS & cntrl ) { + case UNIV_CTL_VAS24: + case UNIV_CTL_VAS32: + uprintf(f,"D64 [MBLT], "); + break; + + default: + uprintf(f,"D64, "); + break; + } + } else { + uprintf(f, "D%u%s, ", vdw, (cntrl & UNIV_MCTL_VCT) ? " [BLT]" : ""); + } + + uprintf(f,"%s, %s", + cntrl&UNIV_MCTL_PGM ? "Pgm" : "Dat", + cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr"); + if ( cntrl & UNIV_MCTL_PWEN ) + uprintf(f,", PWEN"); + } else { + uprintf(f,"%s %s %s %s", + cntrl&UNIV_SCTL_PGM ? "Pgm," : " ", + cntrl&UNIV_SCTL_DAT ? "Dat," : " ", + cntrl&UNIV_SCTL_SUPER ? "Sup," : " ", + cntrl&UNIV_SCTL_USER ? "Usr" : ""); + if ( cntrl & UNIV_SCTL_PWEN ) + uprintf(f,", PWEN"); + if ( cntrl & UNIV_SCTL_PREN ) + uprintf(f,", PREN"); + } + uprintf(f,"\n"); + return 0; +} + +typedef struct XlatRec_ { + unsigned long address; + unsigned long aspace; + unsigned reverse; /* find reverse mapping of this port */ +} XlatRec, *Xlat; + +/* try to translate an address through the bridge + * + * IN: l->address, l->aspace + * OUT: l->address (translated address) + * + * RETURNS: -1: invalid space + * 0: invalid address (not found in range) + * port+1: success + */ + +static int +xlatePort(int ismaster, int port, volatile LERegister *preg, void *parm) +{ +Xlat l=(Xlat)parm; +unsigned long cntrl, start, bound, offst, mask, x; + + cntrl = READ_LE0(preg++); + + /* skip this port if disabled */ + if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN))) + return 0; + + /* check for correct address space */ + if ( am2mode(ismaster,l->aspace,&offst) ) { + uprintf(stderr,"vmeUniverse WARNING: invalid adressing mode 0x%x\n", + l->aspace); + return -1; + } + + + switch (VME_MODE_MATCH_MASK & l->aspace) { + case VME_MODE_EXACT_MATCH: + mask = -1 & ~VME_MODE_MATCH_MASK; + break; + + case VME_MODE_AS_MATCH: + mask = UNIV_CTL_VAS; + break; + + default: + mask = (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK); + break; + } + + cntrl &= mask; + offst &= mask; + + if ( cntrl != offst ) + return 0; /* mode doesn't match requested AM */ + + /* OK, we found a matching mode, now we must check the address range */ + mask = ~PORT_UNALIGNED(0xffffffff,port); + + /* for the master `start' is the PCI address, + * for the slave `start' is the VME address + */ + start = READ_LE0(preg++) & mask; + bound = READ_LE0(preg++) & mask; + offst = READ_LE0(preg++) & mask; + + /* translate address to the other bus */ + if (l->reverse) { + /* reverse mapping, i.e. for master ports we map from + * VME to PCI, for slave ports we map from VME to PCI + */ + if (l->address >= start && l->address < bound) { + l->address+=offst; + return 1 + port; + } + } else { + x = l->address - offst; + + if (x >= start && x < bound) { + /* valid address found */ + l->address = x; + return 1 + port; + } + } + return 0; +} + +/* check if there is any active window with write posting enabled */ +static int +hasPWENWindow( + int ismaster, + int portno, + volatile LERegister *preg, + void *parm) +{ +unsigned long cntrl = READ_LE0(preg); +unsigned long mask = ismaster ? (UNIV_MCTL_EN|UNIV_MCTL_PWEN) : (UNIV_SCTL_EN|UNIV_SCTL_PWEN); + return (cntrl & mask) == mask ? -1 : 0; +} + +static int +mapOverAll(volatile LERegister *base, int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg) +{ +volatile LERegister *rptr; +unsigned long port; +int rval; + + CHECK_DFLT_BASE(base); + + rptr = (base + + (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister)); +#undef TSILL +#ifdef TSILL + uprintf(stderr,"mapoverall: base is 0x%08x, rptr 0x%08x\n",base,rptr); +#endif +#undef TSILL + for (port=0; port<4; port++) { + if ((rval=func(ismaster,port,rptr,arg))) return rval; + rptr+=5; /* register block spacing */ + } + + /* only rev. 2 has 8 ports */ + if (UNIV_REV(base)<2) return -1; + + rptr = (base + + (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister)); + for (port=4; port<UNIV_NUM_MPORTS; port++) { + if ((rval=func(ismaster,port,rptr,arg))) return rval; + rptr+=5; /* register block spacing */ + } + return 0; +} + +static void +showUniversePorts(volatile LERegister *base, int ismaster, FILE *f) +{ + if (!f) f=stdout; + uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave"); + uprintf(f,"Port VME-Addr Size PCI-Adrs Mode:\n"); + mapOverAll(base,ismaster,showUniversePort,f); +} + +static int +xlateFindPort( + volatile LERegister *base, /* Universe base address */ + int master, /* look in the master windows */ + int reverse, /* reverse mapping; for masters: map local to VME */ + unsigned long as, /* address space */ + unsigned long aIn, /* address to look up */ + unsigned long *paOut/* where to put result */ + ) +{ +int rval; +XlatRec l; + l.aspace = as; + l.address = aIn; + l.reverse = reverse; + /* map result -1/0/1 to -2/-1/0 with 0 on success */ + rval = mapOverAll(base,master,xlatePort,(void*)&l) - 1; + *paOut = l.address; + return rval; +} + +int +vmeUniverseXlateAddrXX( + volatile LERegister *base, /* Universe base address */ + int master, /* look in the master windows */ + int reverse, /* reverse mapping; for masters: map local to VME */ + unsigned long as, /* address space */ + unsigned long aIn, /* address to look up */ + unsigned long *paOut/* where to put result */ + ) +{ + return xlateFindPort(base, master, reverse, as, aIn, paOut) >= 0 ? 0 : -1; +} + +int +vmeUniverseXlateAddr( + int master, /* look in the master windows */ + int reverse, /* reverse mapping; for masters: map local to VME */ + unsigned long as, /* address space */ + unsigned long aIn, /* address to look up */ + unsigned long *paOut/* where to put result */ + ) +{ + DFLT_BASE; + return vmeUniverseXlateAddrXX(base, master, reverse, as, aIn, paOut); +} + + +void +vmeUniverseReset(void) +{ + /* disable/reset special cycles (ADOH, RMW) */ + vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_CTL); + vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_ADDR); + vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_EN); + + /* set coupled window timeout to 0 (release VME after each transaction) + * CRT (coupled request timeout) is unused by Universe II + */ + vmeUniverseWriteReg(UNIV_LMISC_CRT_128_US, UNIV_REGOFF_LMISC); + + /* disable/reset DMA engine */ + vmeUniverseWriteReg(0, UNIV_REGOFF_DCTL); + vmeUniverseWriteReg(0, UNIV_REGOFF_DTBC); + vmeUniverseWriteReg(0, UNIV_REGOFF_DLA); + vmeUniverseWriteReg(0, UNIV_REGOFF_DVA); + vmeUniverseWriteReg(0, UNIV_REGOFF_DCPP); + + /* disable location monitor */ + vmeUniverseWriteReg(0, UNIV_REGOFF_LM_CTL); + + /* disable universe register access from VME bus */ + vmeUniverseWriteReg(0, UNIV_REGOFF_VRAI_CTL); + +#if 0 /* leave CSR bus image alone; IRQ manager can use it */ + /* disable VME bus image of VME CSR */ + vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL); +#endif + + + /* I had problems with a Joerger vtr10012_8 card who would + * only be accessible after tweaking the U2SPEC register + * (the t27 parameter helped). + * I use the same settings here that are used by the + * Synergy VGM-powerpc BSP for vxWorks. + */ + if (2==UNIV_REV(vmeUniverse0BaseAddr)) + vmeUniverseWriteReg(UNIV_U2SPEC_DTKFLTR | + UNIV_U2SPEC_MASt11 | + UNIV_U2SPEC_READt27_NODELAY | + UNIV_U2SPEC_POSt28_FAST | + UNIV_U2SPEC_PREt28_FAST, + UNIV_REGOFF_U2SPEC); + + /* disable interrupts, reset routing */ + vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_EN); + vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP0); + vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP1); + + vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_EN); + vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP0); + vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP1); + + vmeUniverseDisableAllSlaves(); + + vmeUniverseDisableAllMasters(); + + vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR); + + /* clear interrupt status bits */ + vmeUniverseWriteReg(UNIV_LINT_STAT_CLR, UNIV_REGOFF_LINT_STAT); + vmeUniverseWriteReg(UNIV_VINT_STAT_CLR, UNIV_REGOFF_VINT_STAT); + + vmeUniverseWriteReg(UNIV_V_AMERR_V_STAT, UNIV_REGOFF_V_AMERR); + + vmeUniverseWriteReg( + vmeUniverseReadReg(UNIV_REGOFF_PCI_CSR) | + UNIV_PCI_CSR_D_PE | UNIV_PCI_CSR_S_SERR | UNIV_PCI_CSR_R_MA | + UNIV_PCI_CSR_R_TA | UNIV_PCI_CSR_S_TA, + UNIV_REGOFF_PCI_CSR); + + vmeUniverseWriteReg(UNIV_L_CMDERR_L_STAT, UNIV_REGOFF_L_CMDERR); + + vmeUniverseWriteReg( + UNIV_DGCS_STOP | UNIV_DGCS_HALT | UNIV_DGCS_DONE | + UNIV_DGCS_LERR | UNIV_DGCS_VERR | UNIV_DGCS_P_ERR, + UNIV_REGOFF_DGCS); +} + +int +vmeUniverseInit(void) +{ +int rval; + if ( (rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr)) < 0 ) { + uprintf(stderr,"unable to find the universe in pci config space\n"); + } else { + vmeUniverse0PciIrqLine = rval; + rval = 0; + uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %d\n", + (unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine); + } + return rval; +} + +void +vmeUniverseMasterPortsShowXX(volatile LERegister *base, FILE *f) +{ + showUniversePorts(base,1,f); +} + +void +vmeUniverseMasterPortsShow(FILE *f) +{ + DFLT_BASE; + showUniversePorts(base,1,f); +} + +void +vmeUniverseSlavePortsShowXX(volatile LERegister *base, FILE *f) +{ + showUniversePorts(base,0,f); +} + +void +vmeUniverseSlavePortsShow(FILE *f) +{ + DFLT_BASE; + showUniversePorts(base,0,f); +} + +int +vmeUniverseMasterPortCfgXX( + volatile LERegister *base, + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long local_address, + unsigned long length) +{ + return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length); +} + +int +vmeUniverseMasterPortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long local_address, + unsigned long length) +{ + DFLT_BASE; + return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length); +} + +int +vmeUniverseSlavePortCfgXX( + volatile LERegister *base, + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long local_address, + unsigned long length) +{ + return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length); +} + +int +vmeUniverseSlavePortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long local_address, + unsigned long length) +{ + DFLT_BASE; + return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length); +} + + +void +vmeUniverseDisableAllSlavesXX(volatile LERegister *base) +{ + mapOverAll(base,0,disableUniversePort,0); +} + +void +vmeUniverseDisableAllSlaves(void) +{ + DFLT_BASE; + mapOverAll(base,0,disableUniversePort,0); +} + +void +vmeUniverseDisableAllMastersXX(volatile LERegister *base) +{ + mapOverAll(base,1,disableUniversePort,0); +} + +void +vmeUniverseDisableAllMasters(void) +{ + DFLT_BASE; + mapOverAll(base,1,disableUniversePort,0); +} + +unsigned long +vmeUniverseReadRegXX(volatile LERegister *base, unsigned long offset) +{ +unsigned long rval; + rval = READ_LE(base,offset); + return rval; +} + + +unsigned long +vmeUniverseReadReg(unsigned long offset) +{ +unsigned long rval; + rval = READ_LE(vmeUniverse0BaseAddr,offset); + return rval; +} + +void +vmeUniverseWriteRegXX(volatile LERegister *base, unsigned long value, unsigned long offset) +{ + WRITE_LE(value, base, offset); +} + +void +vmeUniverseWriteReg(unsigned long value, unsigned long offset) +{ + WRITE_LE(value, vmeUniverse0BaseAddr, offset); +} + +void +vmeUniverseResetBus(void) +{ + vmeUniverseWriteReg( + vmeUniverseReadReg(UNIV_REGOFF_MISC_CTL) | UNIV_MISC_CTL_SW_SYSRST, + UNIV_REGOFF_MISC_CTL); +} + +void +vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num) +{ +#if !defined(__LITTLE_ENDIAN__) || (__LITTLE_ENDIAN__ != 1) +register unsigned long *p=ptr+num; + while (p > ptr) { +#if (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1) + __asm__ __volatile__( + "lwzu 0, -4(%0)\n" + "stwbrx 0, 0, %0\n" + : "=r"(p) : "0"(p) : "r0" + ); +#elif defined(__rtems__) + p--; st_le32(p, *p); +#else +#error "vmeUniverse: endian conversion not implemented for this architecture" +#endif + } +#endif +} + +int +vmeUniverseIntRaiseXX(volatile LERegister *base, int level, unsigned vector) +{ +unsigned long v; +unsigned long b; + + CHECK_DFLT_BASE(base); + + if ( level < 1 || level > 7 || vector > 255 ) + return -1; /* invalid argument */ + + if ( vector & 1 ) /* SW interrupts always ACK an even vector (pp 2-67) */ + return -1; + + + /* Check if already asserted */ + if ( vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_STAT ) & UNIV_VINT_STAT_SWINT(level) ) { + return -2; /* already asserted */ + } + + /* Write Vector */ + vmeUniverseWriteRegXX(base, UNIV_VINT_STATID(vector), UNIV_REGOFF_VINT_STATID ); + + if ( UNIV_REV(base) >= 2 ) { + /* universe II has individual bits for individual levels */ + b = UNIV_VINT_STAT_SWINT(level); + } else { + /* version that is compatible with universe I */ + v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_MAP1); + v &= ~UNIV_VINT_MAP1_SWINT(0x7); + v |= UNIV_VINT_MAP1_SWINT(level); + vmeUniverseWriteRegXX(base, v, UNIV_REGOFF_VINT_MAP1); + b = UNIV_VINT_EN_SWINT; + } + v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_EN); + /* make sure it is clear, then assert */ + vmeUniverseWriteRegXX(base, v & ~b, UNIV_REGOFF_VINT_EN ); + vmeUniverseWriteRegXX(base, v | b, UNIV_REGOFF_VINT_EN ); + + return 0; + +} + +int +vmeUniverseIntRaise(int level, unsigned vector) +{ + return vmeUniverseIntRaiseXX(vmeUniverse0BaseAddr, level, vector); +} + + +/* Map internal register block to VME */ +#define UNIV_CRG_SIZE (1<<12) + +int +vmeUniverseMapCRGXX(volatile LERegister *base, unsigned long vme_base, unsigned long as ) +{ +uint32_t mode; + + CHECK_DFLT_BASE(base); + +#ifdef __rtems__ + if ( vmeUniverseRegPort > -1 && ! vmeUniverseRegCSR ) { + uprintf(stderr,"vmeUniverse: CRG already mapped and in use by interrupt manager\n"); + return -1; + } +#endif + + /* enable all, SUP/USR/PGM/DATA accesses */ + mode = UNIV_VRAI_CTL_EN | UNIV_VRAI_CTL_PGM | UNIV_VRAI_CTL_DATA | UNIV_VRAI_CTL_SUPER | UNIV_VRAI_CTL_USER; + + if ( VME_AM_IS_SHORT(as) ) { + mode |= UNIV_VRAI_CTL_VAS_A16; + } else + if ( VME_AM_IS_STD(as) ) { + mode |= UNIV_VRAI_CTL_VAS_A24; + } else + if ( VME_AM_IS_EXT(as) ) { + mode |= UNIV_VRAI_CTL_VAS_A32; + } else { + return -2; + } + + /* map CRG to VME bus */ + WRITE_LE( (vme_base & ~(UNIV_CRG_SIZE-1)), base, UNIV_REGOFF_VRAI_BS ); + WRITE_LE( mode, base, UNIV_REGOFF_VRAI_CTL ); + + return 0; +} + +int +vmeUniverseMapCRG(unsigned long vme_base, unsigned long as ) +{ + return vmeUniverseMapCRGXX( vmeUniverse0BaseAddr, vme_base, as ); +} + +#ifdef __rtems__ +/* DMA Support -- including linked-list implementation */ +#include "bspVmeDmaListP.h" +#include <bsp/vmeUniverseDMA.h> + +/* Filter valid bits of DCTL */ +#define DCTL_MODE_MASK \ + ( UNIV_DCTL_VDW_MSK | UNIV_DCTL_VAS_MSK | UNIV_DCTL_PGM | UNIV_DCTL_SUPER | UNIV_DCTL_VCT ) + +static uint32_t +xfer_mode2dctl(uint32_t xfer_mode) +{ +uint32_t dctl; + + /* Check requested bus mode */ + + /* Universe does not support 'non-incrementing' DMA */ + + /* NOTE: Universe IIb/d *does* support NOINC_VME but states + * that the VME address needs to be reprogrammed + * when re-issuing a transfer + */ + if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_PCI ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* ignore memory hint */ + xfer_mode &= ~VME_AM_IS_MEMORY; + + if ( VME_AM_IS_2eSST(xfer_mode) ) + return BSP_VMEDMA_STATUS_UNSUP; + + if ( ! VME_AM_IS_SHORT(xfer_mode) && ! VME_AM_IS_STD(xfer_mode) && ! VME_AM_IS_EXT(xfer_mode) ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* Luckily DCTL bits match MCTL bits so we can use am2mode */ + if ( am2mode( 1, xfer_mode, &dctl ) ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* However, the book says that for DMA VAS==5 [which would + * be a CSR access] is reserved. Tests indicate that + * CSR access works on the IIb/d but not really (odd 32-bit + * addresses read 0) on the II. + * Nevertheless, we disallow DMA CSR access at this point + * in order to play it safe... + */ + switch ( UNIV_DCTL_VAS_MSK & dctl ) { + case UNIV_DCTL_VAS_A24: + case UNIV_DCTL_VAS_A32: + /* fixup the data width; universe may always use MBLT + * if data width is 64-bit so we go back to 32-bit + * if they didn't explicitely ask for MBLT cycles + */ + if ( (xfer_mode & 0xb) != 8 /* MBLT */ + && ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) ) { + dctl &= ~UNIV_DCTL_VDW_MSK; + dctl |= UNIV_DCTL_VDW_32; + } + break; + + case UNIV_DCTL_VAS_A16: + break; + + default: + return BSP_VMEDMA_STATUS_UNSUP; + } + + /* Make sure other MCTL bits are masked */ + dctl &= DCTL_MODE_MASK; + + if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) { + /* If they want NOINC_VME then we have to do some + * fixup :-( ('errata' [in this case: feature addition] doc. pp. 11+) + */ + dctl &= ~UNIV_DCTL_VCT; /* clear block xfer flag */ + dctl |= UNIV_DCTL_NO_VINC; + /* cannot do 64 bit transfers; go back to 32 */ + if ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) { + dctl &= ~UNIV_DCTL_VDW_MSK; + dctl |= UNIV_DCTL_VDW_32; + } + } + + /* Set direction flag */ + + if ( BSP_VMEDMA_MODE_PCI2VME & xfer_mode ) + dctl |= UNIV_DCTL_L2V; + + return dctl; +} + +/* Convert canonical xfer_mode into Universe setup bits; return -1 if request + * cannot be satisfied (unsupported features) + */ +int +vmeUniverseDmaSetupXX(volatile LERegister *base, int channel, uint32_t mode, uint32_t xfer_mode, void *custom) +{ +uint32_t dctl, dgcs; + + if ( channel != 0 ) + return BSP_VMEDMA_STATUS_UNSUP; + + dctl = xfer_mode2dctl(xfer_mode); + + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl ) + return BSP_VMEDMA_STATUS_UNSUP; + + /* Enable all interrupts at the controller */ + dgcs = UNIV_DGCS_INT_MSK; + + switch ( mode ) { + case BSP_VMEDMA_OPT_THROUGHPUT: + dgcs |= UNIV_DGCS_VON_1024 | UNIV_DGCS_VOFF_0_US; + /* VON counts are different in NO_VINC mode :-( */ + dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ? + UNIV_DGCS_VON_2048 : UNIV_DGCS_VON_1024; + break; + + case BSP_VMEDMA_OPT_LOWLATENCY: + dgcs |= UNIV_DGCS_VOFF_0_US; + /* VON counts are different in NO_VINC mode :-( */ + dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ? + UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256; + break; + + case BSP_VMEDMA_OPT_SHAREDBUS: + dgcs |= UNIV_DGCS_VOFF_512_US; + /* VON counts are different in NO_VINC mode :-( */ + dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ? + UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256; + break; + + case BSP_VMEDMA_OPT_CUSTOM: + dctl = ((uint32_t*)custom)[0]; + dgcs = ((uint32_t*)custom)[1]; + break; + + default: + case BSP_VMEDMA_OPT_DEFAULT: + break; + } + + /* clear status bits */ + dgcs |= UNIV_DGCS_STATUS_CLEAR; + + vmeUniverseWriteRegXX(base, dctl, UNIV_REGOFF_DCTL); + vmeUniverseWriteRegXX(base, dgcs, UNIV_REGOFF_DGCS); + + return BSP_VMEDMA_STATUS_OK; +} + +int +vmeUniverseDmaSetup(int channel, uint32_t mode, uint32_t xfer_mode, void *custom) +{ +DFLT_BASE; + return vmeUniverseDmaSetupXX(base, channel, mode, xfer_mode, custom); +} + +int +vmeUniverseDmaStartXX(volatile LERegister *base, int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ + if ( channel != 0 ) + return BSP_VMEDMA_STATUS_UNSUP; + + if ((pci_addr & 7) != (vme_addr & 7)) { + uprintf(stderr,"vmeUniverseDmaStartXX: misaligned addresses\n"); + return -1; + } + + { + /* help the compiler allocate registers */ + register volatile LERegister *b=base; + register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs; + + dgcs=READ_LE(b, dgcsoff); + + /* clear status and make sure CHAIN is clear */ + dgcs &= ~UNIV_DGCS_CHAIN; + WRITE_LE(dgcs, + b, dgcsoff); + WRITE_LE(pci_addr, + b, UNIV_REGOFF_DLA); + WRITE_LE(vme_addr, + b, UNIV_REGOFF_DVA); + WRITE_LE(n_bytes, + b, UNIV_REGOFF_DTBC); + dgcs |= UNIV_DGCS_GO; + EIEIO_REG; /* make sure GO is written after everything else */ + WRITE_LE(dgcs, + b, dgcsoff); + } + SYNC; /* enforce command completion */ + return 0; +} + +/* This entry point is deprecated */ +int +vmeUniverseStartDMAXX( + volatile LERegister *base, + unsigned long local_addr, + unsigned long vme_addr, + unsigned long count) +{ + return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count); +} + +int +vmeUniverseDmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ + DFLT_BASE; /* vmeUniverseDmaStartXX doesn't check for a valid base address for efficiency reasons */ + return vmeUniverseDmaStartXX(base, channel, pci_addr, vme_addr, n_bytes); +} + +/* This entry point is deprecated */ +int +vmeUniverseStartDMA( + unsigned long local_addr, + unsigned long vme_addr, + unsigned long count) +{ + DFLT_BASE; /* vmeUniverseStartDMAXX doesn't check for a valid base address for efficiency reasons */ + return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count); +} + +uint32_t +vmeUniverseDmaStatusXX(volatile LERegister *base, int channel) +{ +uint32_t dgcs; + if ( channel != 0 ) + return BSP_VMEDMA_STATUS_UNSUP; + + dgcs = vmeUniverseReadRegXX(base, UNIV_REGOFF_DGCS); + + dgcs &= UNIV_DGCS_STATUS_CLEAR; + + if ( 0 == dgcs || UNIV_DGCS_DONE == dgcs ) + return BSP_VMEDMA_STATUS_OK; + + if ( UNIV_DGCS_ACT & dgcs ) + return BSP_VMEDMA_STATUS_BUSY; + + if ( UNIV_DGCS_LERR & dgcs ) + return BSP_VMEDMA_STATUS_BERR_PCI; + + if ( UNIV_DGCS_VERR & dgcs ) + return BSP_VMEDMA_STATUS_BERR_VME; + + return BSP_VMEDMA_STATUS_OERR; +} + +uint32_t +vmeUniverseDmaStatus(int channel) +{ +DFLT_BASE; + return vmeUniverseDmaStatusXX(base, channel); +} + +/* bspVmeDmaList driver interface implementation */ + +/* Cannot use VmeUniverseDMAPacketRec because st_le32 expects unsigned * + * and we get 'alias' warnings when we submit uint32_t * + */ + +typedef volatile uint32_t LERegister1; + +typedef struct VmeUniverseDmaListDescRec_ { + LERegister1 dctl; + LERegister1 dtbc; + LERegister1 dla; + LERegister1 dummy1; + LERegister1 dva; + LERegister1 dummy2; + LERegister1 dcpp; + LERegister1 dummy3; +} __attribute__((aligned(32), __may_alias__)) +VmeUniverseDmaListDescRec; + +typedef VmeUniverseDmaListDescRec *VmeUniverseDmaListDesc; + +static void uni_desc_init (DmaDescriptor); +static int uni_desc_setup (DmaDescriptor, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); +static void uni_desc_setnxt(DmaDescriptor, DmaDescriptor); +static void uni_desc_dump (DmaDescriptor); +static int uni_desc_start (volatile void *controller_addr, int channel, DmaDescriptor p); + +VMEDmaListClassRec vmeUniverseDmaListClass = { + desc_size: sizeof(VmeUniverseDMAPacketRec), + desc_align: 32, + freeList: 0, + desc_alloc: 0, + desc_free: 0, + desc_init: uni_desc_init, + desc_setnxt:uni_desc_setnxt, + desc_setup: uni_desc_setup, + desc_start: uni_desc_start, + desc_refr: 0, + desc_dump: uni_desc_dump, +}; + +static void uni_desc_init (DmaDescriptor p) +{ +VmeUniverseDmaListDesc d = p; + st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL ); +} + +static void uni_desc_setnxt(DmaDescriptor p, DmaDescriptor n) +{ +VmeUniverseDmaListDesc d = p; + if ( 0 == n ) { + st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL ); + } else { + st_le32( &d->dcpp, BSP_LOCAL2PCI_ADDR( (uint32_t)n)); + } +} + +static int +uni_desc_setup ( + DmaDescriptor p, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ +VmeUniverseDmaListDesc d = p; +LERegister1 dctl; + + if ( BSP_VMEDMA_MSK_ATTR & attr_mask ) { + dctl = xfer_mode2dctl(xfer_mode); + + if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl ) + return -1; + + st_le32( &d->dctl, dctl ); + } + + /* Last 3 bits of src & destination addresses must be the same. + * For sake of simplicity we enforce (stricter) 8-byte alignment + */ + + if ( BSP_VMEDMA_MSK_PCIA & attr_mask ) { + if ( pci_addr & 0x7 ) + return -1; + + st_le32( &d->dla, pci_addr ); + } + + if ( BSP_VMEDMA_MSK_VMEA & attr_mask ) { + if ( vme_addr & 0x7 ) + return -1; + + st_le32( &d->dva, vme_addr ); + } + + if ( BSP_VMEDMA_MSK_BCNT & attr_mask ) { + st_le32( &d->dtbc, n_bytes ); + } + + return 0; +} + +static int uni_desc_start +(volatile void *controller_addr, int channel, DmaDescriptor p) +{ +volatile LERegister *base = controller_addr; +uint32_t dgcs; + + if ( !base ) + base = vmeUniverse0BaseAddr; + + dgcs = vmeUniverseReadRegXX( base, UNIV_REGOFF_DGCS ); + + if ( UNIV_DGCS_ACT & dgcs ) + return BSP_VMEDMA_STATUS_BUSY; + + if ( !p ) { + /* Chain bit is cleared by non-linked-list start command + * but do this anyways... + */ + dgcs &= ~UNIV_DGCS_CHAIN; + vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs); + return 0; + } + + /* clear status and set CHAIN bit */ + dgcs |= UNIV_DGCS_CHAIN; + + vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs); + + /* make sure count is 0 for linked list DMA */ + vmeUniverseWriteRegXX( base, 0x0, UNIV_REGOFF_DTBC); + + /* set the address of the descriptor chain */ + vmeUniverseWriteRegXX( base, BSP_LOCAL2PCI_ADDR((uint32_t)p), UNIV_REGOFF_DCPP); + + /* and GO */ + dgcs |= UNIV_DGCS_GO; + vmeUniverseWriteReg(dgcs, UNIV_REGOFF_DGCS); + + return 0; +} + +static void +uni_desc_dump(DmaDescriptor p) +{ +VmeUniverseDmaListDesc d = p; +LERegister1 dcpp = ld_le32(&d->dcpp); + + printf(" DLA: 0x%08x\n", ld_le32(&d->dla)); + printf(" DVA: 0x%08x\n", ld_le32(&d->dva)); + printf(" DCPP: 0x%08"PRIx32"%s\n", dcpp, (dcpp & UNIV_DCPP_IMG_NULL) ? " (LAST)" : ""); + printf(" CTL: 0x%08x\n", ld_le32(&d->dctl)); + printf(" TBC: 0x%08x\n", ld_le32(&d->dtbc)); +} + +/* RTEMS interrupt subsystem */ + +#include <bsp/irq.h> + +typedef struct +UniverseIRQEntryRec_ { + VmeUniverseISR isr; + void *usrData; +} UniverseIRQEntryRec, *UniverseIRQEntry; + +static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0}; + +int vmeUniverseIrqMgrInstalled = 0; + +volatile LERegister *vmeUniverseRegBase = 0; + +/* We support 4 wires between universe + PIC */ + +#define UNIV_NUM_WIRES 4 + +static volatile unsigned long wire_mask[UNIV_NUM_WIRES] = {0}; +/* wires are offset by 1 so we can initialize the wire table to all zeros */ +static int universe_wire[UNIV_NUM_WIRES] = {0}; + +static int +lvl2bit(unsigned int level) +{ +int shift = -1; + if ( level >= UNIV_DMA_INT_VEC && level <= UNIV_LM3_INT_VEC ) { + shift = 8 + (level-UNIV_DMA_INT_VEC); + } else if ( UNIV_VOWN_INT_VEC == level ) { + shift = 0; + } else if ( 1 <= level && level <=7 ) { + shift = level; + } else { + /* invalid level */ + } + return shift; +} + +int +vmeUniverseIntRoute(unsigned int level, unsigned int pin) +{ +int i, shift; +unsigned long mask, mapreg, flags, wire; + + if ( pin >= UNIV_NUM_WIRES || ! universe_wire[pin] || !vmeUniverseIrqMgrInstalled ) + return -1; + + if ( (shift = lvl2bit(level)) < 0 ) { + return -1; /* invalid level */ + } + + mask = 1<<shift; + + /* calculate the mapping register and contents */ + if ( shift < 8 ) { + mapreg = UNIV_REGOFF_LINT_MAP0; + } else if ( shift < 16 ) { + shift -= 8; + mapreg = UNIV_REGOFF_LINT_MAP1; + } else if ( shift < 24 ) { + shift -= 16; + mapreg = UNIV_REGOFF_LINT_MAP2; + } else { + return -1; + } + + shift <<=2; + + /* wires are offset by 1 so we can initialize the wire table to all zeros */ + wire = (universe_wire[pin]-1) << shift; + +rtems_interrupt_disable(flags); + + for ( i = 0; i<UNIV_NUM_WIRES; i++ ) { + wire_mask[i] &= ~mask; + } + wire_mask[pin] |= mask; + + mask = vmeUniverseReadReg(mapreg) & ~ (0xf<<shift); + mask |= wire; + vmeUniverseWriteReg( mask, mapreg ); + +rtems_interrupt_enable(flags); + return 0; +} + +VmeUniverseISR +vmeUniverseISRGet(unsigned long vector, void **parg) +{ +unsigned long flags; +VmeUniverseISR rval = 0; +volatile UniverseIRQEntry *pe = universeHdlTbl + vector; + + if ( vector>=UNIV_NUM_INT_VECS || ! *pe ) + return 0; + + rtems_interrupt_disable(flags); + if ( *pe ) { + if (parg) + *parg=(*pe)->usrData; + rval = (*pe)->isr; + } + rtems_interrupt_enable(flags); + return rval; +} + +#define SPECIAL_IRQ_MSK ( ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1) ) + +static void +universeSpecialISR(unsigned long status) +{ +register UniverseIRQEntry ip; +register unsigned vec; +register unsigned long s; + + /* handle all LINT bits except for the 'normal' VME interrupts */ + + /* clear all detected special interrupts */ + vmeUniverseWriteReg( (status & SPECIAL_IRQ_MSK), UNIV_REGOFF_LINT_STAT ); + + /* do VOWN first */ + vec=UNIV_VOWN_INT_VEC; + if ( (status & UNIV_LINT_STAT_VOWN) && (ip=universeHdlTbl[vec])) + ip->isr(ip->usrData,vec); + + /* now continue with DMA and scan through all bits; + * we assume the vectors are in the right order! + * + * The initial right shift brings the DMA bit into position 0; + * the loop is left early if there are no more bits set. + */ + for ( s = status>>8; s; s >>= 1) { + vec++; + if ( (s&1) && (ip=universeHdlTbl[vec]) ) + ip->isr(ip->usrData,vec); + } + +/* + * clear our line in the VINT_STAT register + * seems to be not neccessary... + vmeUniverseWriteReg( + UNIV_VINT_STAT_LINT(specialIrqUnivOut), + UNIV_REGOFF_VINT_STAT); + */ +} + +/* + * interrupts from VME to PCI seem to be processed more or less + * like this: + * + * + * VME IRQ ------ + * & ----- LINT_STAT ---- + * | & ---------- PCI LINE + * | | + * | | + * LINT_EN --------------------------- + * + * I.e. + * - if LINT_EN is disabled, a VME IRQ will not set LINT_STAT. + * - while LINT_STAT is set, it will pull the PCI line unless + * masked by LINT_EN. + * - VINT_STAT(lint_bit) seems to have no effect beyond giving + * status info. + * + * Hence, it is possible to + * - arm (set LINT_EN, routing etc.) + * - receive an irq (sets. LINT_STAT) + * - the ISR then: + * * clears LINT_EN, results in masking LINT_STAT (which + * is still set to prevent another VME irq at the same + * level to be ACKEd by the universe. + * * do PCI_EOI to allow nesting of higher VME irqs. + * (previous step also cleared LINT_EN of lower levels) + * * when the handler returns, clear LINT_STAT + * * re-enable setting LINT_EN. + */ + +static void +universeVMEISR(rtems_irq_hdl_param arg) +{ +int pin = (int)arg; +UniverseIRQEntry ip; +unsigned long msk,lintstat,status; +int lvl; +#if defined(BSP_PIC_DO_EOI) +unsigned long linten; +#endif + + /* determine the highest priority IRQ source */ + lintstat = vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT); + + /* only handle interrupts routed to this pin */ + lintstat &= wire_mask[pin]; + +#ifdef __PPC__ + asm volatile("cntlzw %0, %1":"=r"(lvl):"r"(lintstat & ~SPECIAL_IRQ_MSK)); + lvl = 31-lvl; + msk = 1<<lvl; +#else + for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7; + lvl>0; + lvl--, msk>>=1) { + if (lintstat & msk) break; + } +#endif + +#ifndef BSP_PIC_DO_EOI /* Software priorities not supported */ + + if ( (status = (lintstat & SPECIAL_IRQ_MSK)) ) + universeSpecialISR( status ); + + if ( lvl <= 0) + return; + +#else + if ( lvl <= 0 ) { + /* try the special handler */ + universeSpecialISR( lintstat & SPECIAL_IRQ_MSK ); + + /* + * let the pic end this cycle + */ + if ( 0 == pin ) + BSP_PIC_DO_EOI; + + return; + } + linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN); + + /* mask this and all lower levels that are routed to the same pin */ + vmeUniverseWriteReg( + linten & ~( ((msk<<1)-UNIV_LINT_STAT_VIRQ1) & wire_mask[pin]), + UNIV_REGOFF_LINT_EN + ); + + /* end this interrupt + * cycle on the PCI bus, so higher level interrupts can be + * caught from now on... + */ + if ( 0 == pin ) + BSP_PIC_DO_EOI; +#endif + + /* get vector and dispatch handler */ + status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2)); + /* determine the highest priority IRQ source */ + + if (status & UNIV_VIRQ_ERR) { + /* TODO: log error message - RTEMS has no logger :-( */ +#ifdef BSP_PIC_DO_EOI + linten &= ~msk; +#else + vmeUniverseIntDisable(lvl); +#endif + printk("vmeUniverse ISR: error read from STATID register; (level: %i) STATID: 0x%08" PRIx32 " -- DISABLING\n", lvl, status); + } else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) { +#ifdef BSP_PIC_DO_EOI + linten &= ~msk; +#else + vmeUniverseIntDisable(lvl); +#endif + /* TODO: log error message - RTEMS has no logger :-( */ + printk("vmeUniverse ISR: no handler installed for this vector; (level: %i) STATID: 0x%08" PRIx32 " -- DISABLING\n", lvl, status); + } else { + /* dispatch handler, it must clear the IRQ at the device */ + ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK); + + /* insert a VME read operation to flush fifo, making sure all user write-ops complete */ +#ifdef __PPC__ + /* courtesy to disobedient users who don't use I/O ops */ + asm volatile("eieio"); +#endif + READ_LE0(vmeUniverseRegBase); +#ifdef __PPC__ + /* make sure this is ordered before re-enabling */ + asm volatile("eieio"); +#endif + } + + /* clear this interrupt level; allow the universe to handler further interrupts */ + vmeUniverseWriteReg(msk, UNIV_REGOFF_LINT_STAT); + +/* + * this seems not to be necessary; we just leave the + * bit set to save a couple of instructions... + vmeUniverseWriteReg( + UNIV_VINT_STAT_LINT(vmeIrqUnivOut), + UNIV_REGOFF_VINT_STAT); +*/ + +#ifdef BSP_PIC_DO_EOI + + /* re-enable the previous level */ + vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN); +#endif +} + + +/* STUPID API */ +static void +my_no_op(const rtems_irq_connect_data * arg) +{} + +static int +my_isOn(const rtems_irq_connect_data *arg) +{ + return (int)vmeUniverseReadReg(UNIV_REGOFF_LINT_EN); +} + +typedef struct { + int uni_pin, pic_pin; +} IntRoute; + +static void +connectIsr(int shared, rtems_irq_hdl isr, int pic_line, int pic_pin) +{ +rtems_irq_connect_data aarrggh; + aarrggh.on = my_no_op; /* at _least_ they could check for a 0 pointer */ + aarrggh.off = my_no_op; + aarrggh.isOn = my_isOn; + aarrggh.hdl = isr; + aarrggh.handle = (rtems_irq_hdl_param)pic_pin; + aarrggh.name = pic_line; + + if ( shared ) { +#if BSP_SHARED_HANDLER_SUPPORT > 0 + if (!BSP_install_rtems_shared_irq_handler(&aarrggh)) + rtems_panic("unable to install vmeUniverse shared irq handler"); +#else + uprintf(stderr,"vmeUniverse: WARNING: your BSP doesn't support sharing interrupts\n"); + if (!BSP_install_rtems_irq_handler(&aarrggh)) + rtems_panic("unable to install vmeUniverse irq handler"); +#endif + } else { + if (!BSP_install_rtems_irq_handler(&aarrggh)) + rtems_panic("unable to install vmeUniverse irq handler"); + } +} + +#ifndef BSP_EARLY_PROBE_VME +#define BSP_EARLY_PROBE_VME(addr) \ + ( \ + ((PCI_DEVICE_UNIVERSEII << 16) | PCI_VENDOR_TUNDRA ) == READ_LE( ((volatile LERegister*)(addr)), 0 ) \ + ) +#endif + +/* Check if there is a vme address/as is mapped in any of the outbound windows + * and look for the PCI vendordevice ID there. + * RETURNS: -1 on error (no mapping or probe failure), outbound window # (0..7) + * on success. Address translated into CPU address is returned in *pcpu_addr. + */ +static int +mappedAndProbed(unsigned long vme_addr, unsigned as, unsigned long *pcpu_addr) +{ +int j; +char *regtype = (as & VME_AM_MASK) == VME_AM_CSR ? "CSR" : "CRG"; + + /* try to find mapping */ + if ( 0 > (j = xlateFindPort( + vmeUniverse0BaseAddr, + 1, 0, + as | VME_MODE_AS_MATCH, + vme_addr, + pcpu_addr ) ) ) { + uprintf(stderr,"vmeUniverse - Unable to find mapping for %s VME base (0x%08x)\n", regtype, vme_addr); + uprintf(stderr," in outbound windows.\n"); + } else { + /* found a slot number; probe it */ + *pcpu_addr = BSP_PCI2LOCAL_ADDR( *pcpu_addr ); + if ( BSP_EARLY_PROBE_VME(*pcpu_addr) ) { + uprintf(stderr,"vmeUniverse - IRQ manager using VME %s to flush FIFO\n", regtype); + return j; + } else { + uprintf(stderr,"vmeUniverse - Found slot info but detection of universe in VME %s space failed\n", regtype); + } + } + return -1; +} + + +int +vmeUniverseInstallIrqMgrAlt(int flags, int uni_pin0, int pic_pin0, ...) +{ +int rval; +va_list ap; + va_start(ap, pic_pin0); + rval = vmeUniverseInstallIrqMgrVa(flags, uni_pin0, pic_pin0, ap); + va_end(ap); + return rval; +} + +int +vmeUniverseInstallIrqMgrVa(int flags, int uni_pin0, int pic_pin0, va_list ap) +{ +int i,j, specialPin, uni_pin[UNIV_NUM_WIRES+1], pic_pin[UNIV_NUM_WIRES]; +unsigned long cpu_base, vme_reg_base; + + if (vmeUniverseIrqMgrInstalled) return -4; + + /* check parameters */ + + if ( uni_pin0 < 0 || uni_pin0 > 7 ) return -1; + + uni_pin[0] = uni_pin0; + pic_pin[0] = pic_pin0 < 0 ? vmeUniverse0PciIrqLine : pic_pin0; + i = 1; + while ( (uni_pin[i] = va_arg(ap, int)) >= 0 ) { + + if ( i >= UNIV_NUM_WIRES ) { + return -5; + } + + pic_pin[i] = va_arg(ap,int); + + if ( uni_pin[i] > 7 ) { + return -2; + } + if ( pic_pin[i] < 0 ) { + return -3; + } + i++; + } + + /* all routings must be different */ + for ( i=0; uni_pin[i] >= 0; i++ ) { + for ( j=i+1; uni_pin[j] >= 0; j++ ) { + if ( uni_pin[j] == uni_pin[i] ) return -6; + if ( pic_pin[j] == pic_pin[i] ) return -7; + } + } + + if ( flags & VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND ) { + + /* Find registers on VME so the ISR can issue a read to flush the FIFO */ + uprintf(stderr,"vmeUniverse IRQ manager: looking for registers on VME...\n"); + + /* NOTE: The universe [unlike the Tsi148] doesn't know about geographical + * addressing but the MotLoad firmware [mvme5500] is kind enough to + * program VCSR_BS based on the board's geographical address for us :-) + */ + if ( ( i = ((READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VCSR_BS ) >> 27) & 0x1f ) ) > 0 ) { + uprintf(stderr,"Trying to find CSR on VME...\n"); + vme_reg_base = i*0x80000 + UNIV_CSR_OFFSET; + i = mappedAndProbed( vme_reg_base, VME_AM_CSR , &cpu_base); + if ( i >= 0 ) + vmeUniverseRegCSR = 1; + } else { + i = -1; + } + + if ( -1 == i ) { + + uprintf(stderr,"Trying to find CRG on VME...\n"); + + /* Next we see if the CRG block is mapped to VME */ + + if ( UNIV_VRAI_CTL_EN & (j = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_CTL )) ) { + switch ( j & UNIV_VRAI_CTL_VAS_MSK ) { + case UNIV_VRAI_CTL_VAS_A16 : i = VME_AM_SUP_SHORT_IO; break; + case UNIV_VRAI_CTL_VAS_A24 : i = VME_AM_STD_SUP_DATA; break; + case UNIV_VRAI_CTL_VAS_A32 : i = VME_AM_EXT_SUP_DATA; break; + default: + break; + } + vme_reg_base = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_BS ) & ~(UNIV_CRG_SIZE - 1); + } + + if ( -1 == i ) { + } else { + i = mappedAndProbed( vme_reg_base, (i & VME_AM_MASK), &cpu_base ); + } + } + + if ( i < 0 ) { + if ( mapOverAll( vmeUniverse0BaseAddr, 1, hasPWENWindow, 0 ) ) { + uprintf(stderr,"vmeUniverse IRQ manager - BSP configuration error: registers not found on VME\n"); + uprintf(stderr,"(should open outbound window to CSR space or map CRG [vmeUniverseMapCRG()])\n"); + uprintf(stderr,"Falling back to PCI but you might experience spurious VME interrupts; read a register\n"); + uprintf(stderr,"back from user ISR to flush universe FIFO as a work-around or\n"); + uprintf(stderr,"make sure ISR accesses device using a window with posted-writes disabled\n"); + } else { + uprintf(stderr,"vmeUniverse IRQ manager - registers not found on VME; falling back to PCI\n"); + } + vmeUniverseRegBase = vmeUniverse0BaseAddr; + vmeUniverseRegPort = -1; + } else { + vmeUniverseRegBase = (volatile LERegister*)cpu_base; + vmeUniverseRegPort = i; + } + } else { + vmeUniverseRegBase = vmeUniverse0BaseAddr; + vmeUniverseRegPort = -1; + } + + /* give them a chance to override buggy PCI info */ + if ( pic_pin[0] >= 0 && vmeUniverse0PciIrqLine != pic_pin[0] ) { + uprintf(stderr,"Overriding main IRQ line PCI info with %d\n", + pic_pin[0]); + vmeUniverse0PciIrqLine=pic_pin[0]; + } + + for ( i = 0; uni_pin[i] >= 0; i++ ) { + /* offset wire # by one so we can initialize to 0 == invalid */ + universe_wire[i] = uni_pin[i] + 1; + connectIsr((flags & VMEUNIVERSE_IRQ_MGR_FLAG_SHARED), universeVMEISR, pic_pin[i], i); + } + + specialPin = uni_pin[1] >= 0 ? 1 : 0; + + /* setup routing */ + + /* IntRoute checks for mgr being installed */ + vmeUniverseIrqMgrInstalled=1; + + /* route 7 VME irqs to first / 'normal' pin */ + for ( i=1; i<8; i++ ) + vmeUniverseIntRoute( i, 0 ); + for ( i=UNIV_VOWN_INT_VEC; i<=UNIV_LM3_INT_VEC; i++ ) { + if ( vmeUniverseIntRoute( i, specialPin ) ) + printk("Routing lvl %i -> wire # %i failed\n", i, specialPin); + } + + return 0; +} + +int +vmeUniverseInstallIrqMgr(int vmeIrqUnivOut, + int vmeIrqPicLine, + int specialIrqUnivOut, + int specialIrqPicLine) +{ + return vmeUniverseInstallIrqMgrAlt( + 0, /* bwds compat. */ + vmeIrqUnivOut, vmeIrqPicLine, + specialIrqUnivOut, specialIrqPicLine, + -1); +} + +int +vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg) +{ +UniverseIRQEntry ip; +unsigned long flags; +volatile UniverseIRQEntry *pe; + + if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled) + return -1; + + pe = universeHdlTbl + vector; + + if (*pe || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec)))) + return -1; + + ip->isr=hdl; + ip->usrData=arg; + + rtems_interrupt_disable(flags); + if ( *pe ) { + /* oops; someone intervened */ + rtems_interrupt_enable(flags); + free(ip); + return -1; + } + *pe = ip; + rtems_interrupt_enable(flags); + return 0; +} + +int +vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg) +{ +UniverseIRQEntry ip; +unsigned long flags; +volatile UniverseIRQEntry *pe; + + if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled) + return -1; + + pe = universeHdlTbl + vector; + + rtems_interrupt_disable(flags); + ip = *pe; + if (!ip || ip->isr!=hdl || ip->usrData!=arg) { + rtems_interrupt_enable(flags); + return -1; + } + *pe = 0; + rtems_interrupt_enable(flags); + free(ip); + return 0; +} + +static int +intDoEnDis(unsigned int level, int dis) +{ +unsigned long flags, v; +int shift; + + if ( ! vmeUniverseIrqMgrInstalled || (shift = lvl2bit(level)) < 0 ) + return -1; + + v = 1<<shift; + + if ( !dis ) + return vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & v ? 1 : 0; + + rtems_interrupt_disable(flags); + if ( dis<0 ) + vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & ~v, UNIV_REGOFF_LINT_EN ); + else { + vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) | v, UNIV_REGOFF_LINT_EN ); + } + rtems_interrupt_enable(flags); + return 0; +} + +int +vmeUniverseIntEnable(unsigned int level) +{ + return intDoEnDis(level, 1); +} + +int +vmeUniverseIntDisable(unsigned int level) +{ + return intDoEnDis(level, -1); +} + +int +vmeUniverseIntIsEnabled(unsigned int level) +{ + return intDoEnDis(level, 0); +} + +/* Loopback test of VME/universe interrupts */ + +typedef struct { + rtems_id q; + int l; +} LoopbackTstArgs; + +static void +loopbackTstIsr(void *arg, unsigned long vector) +{ +LoopbackTstArgs *pa = arg; + if ( RTEMS_SUCCESSFUL != rtems_message_queue_send(pa->q, (void*)&vector, sizeof(vector)) ) { + /* Overrun ? */ + printk("vmeUniverseIntLoopbackTst: (ISR) message queue full / overrun ? disabling IRQ level %i\n", pa->l); + vmeUniverseIntDisable(pa->l); + } +} + +int +vmeUniverseIntLoopbackTst(int level, unsigned vector) +{ +DFLT_BASE; +rtems_status_code sc; +rtems_id q = 0; +int installed = 0; +int i, err = 0; +int doDisable = 0; +size_t size; +unsigned long msg; +char * irqfmt = "VME IRQ @vector %3i %s"; +char * iackfmt = "VME IACK %s"; +LoopbackTstArgs a; + + CHECK_DFLT_BASE(base); + + /* arg check */ + if ( level < 1 || level > 7 || vector > 255 ) { + fprintf(stderr,"Invalid level or vector argument\n"); + return -1; + } + + if ( (vector & 1) ) { + fprintf(stderr,"Software interrupts can only use even-numbered vectors, sorry.\n"); + return -1; + } + + if ( UNIV_REV(base) < 2 && vector != 0 ) { + fprintf(stderr, + "vmeUniverseIntLoopbackTst(): Universe 1 has a bug. IACK in response to\n"); + fprintf(stderr, + "self-generated VME interrupt yields always a zero vector. As a workaround,\n"); + fprintf(stderr, + "use vector 0, please.\n"); + return -1; + } + + /* Create message queue */ + if ( RTEMS_SUCCESSFUL != (sc=rtems_message_queue_create( + rtems_build_name('t' ,'U','I','I'), + 4, + sizeof(unsigned long), + 0, /* default attributes: fifo, local */ + &q)) ) { + rtems_error(sc, "vmeUniverseIntLoopbackTst: Unable to create message queue"); + goto bail; + } + + a.q = q; + a.l = level; + + /* Install handlers */ + if ( vmeUniverseInstallISR(vector, loopbackTstIsr, (void*)&a) ) { + fprintf(stderr,"Unable to install VME ISR to vector %i\n",vector); + goto bail; + } + installed++; + if ( vmeUniverseInstallISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a) ) { + fprintf(stderr,"Unable to install VME ISR to IACK special vector %i\n",UNIV_VME_SW_IACK_INT_VEC); + goto bail; + } + installed++; + + if ( !vmeUniverseIntIsEnabled(level) && 0==vmeUniverseIntEnable(level) ) + doDisable = 1; + + /* make sure there are no pending interrupts */ + vmeUniverseWriteReg( UNIV_LINT_STAT_SW_IACK, UNIV_REGOFF_LINT_STAT ); + + if ( vmeUniverseIntEnable( UNIV_VME_SW_IACK_INT_VEC ) ) { + fprintf(stderr,"Unable to enable IACK interrupt\n"); + goto bail; + } + + printf("vmeUniverse VME interrupt loopback test; STARTING...\n"); + printf(" --> asserting VME IRQ level %i\n", level); + vmeUniverseIntRaise(level, vector); + + for ( i = 0; i< 3; i++ ) { + sc = rtems_message_queue_receive( + q, + &msg, + &size, + RTEMS_WAIT, + 20); + if ( sc ) { + if ( RTEMS_TIMEOUT == sc && i>1 ) { + /* OK; we dont' expect more to happen */ + sc = 0; + } else { + rtems_error(sc,"Error waiting for interrupts"); + } + break; + } + if ( msg == vector ) { + if ( !irqfmt ) { + printf("Excess VME IRQ received ?? -- BAD\n"); + err = 1; + } else { + printf(irqfmt, vector, "received -- PASSED\n"); + irqfmt = 0; + } + } else if ( msg == UNIV_VME_SW_IACK_INT_VEC ) { + if ( !iackfmt ) { + printf("Excess VME IACK received ?? -- BAD\n"); + err = 1; + } else { + printf(iackfmt, "received -- PASSED\n"); + iackfmt = 0; + } + } else { + printf("Unknown IRQ (vector %lu) received -- BAD\n", msg); + err = 1; + } + } + + + /* Missing anything ? */ + if ( irqfmt ) { + printf(irqfmt,vector, "MISSED -- BAD\n"); + err = 1; + } + if ( iackfmt ) { + printf(iackfmt, "MISSED -- BAD\n"); + err = 1; + } + + printf("FINISHED.\n"); + +bail: + if ( doDisable ) + vmeUniverseIntDisable(level); + vmeUniverseIntDisable( UNIV_VME_SW_IACK_INT_VEC ); + if ( installed > 0 ) + vmeUniverseRemoveISR(vector, loopbackTstIsr, (void*)&a); + if ( installed > 1 ) + vmeUniverseRemoveISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a); + if ( q ) + rtems_message_queue_delete(q); + + return sc ? sc : err; +} + +#endif diff --git a/bsps/powerpc/shared/vme/vme_universe.c b/bsps/powerpc/shared/vme/vme_universe.c new file mode 100644 index 0000000000..158a899b2a --- /dev/null +++ b/bsps/powerpc/shared/vme/vme_universe.c @@ -0,0 +1,436 @@ +/* Implementation of the VME.h and VMEDMA.h APIs for the BSP using the + * vmeUniverse/vmeTsi148 drivers + * + * This file is named vme_universe.c for historic reasons. + */ + + +#include <rtems.h> +#include <bsp.h> +#include <bsp/VME.h> +#include <bsp/VMEDMA.h> +#include <bsp/VMEConfig.h> +#include <bsp/irq.h> +#include <stdio.h> + +#define __INSIDE_RTEMS_BSP__ + +#if !defined(_VME_DRIVER_TSI148) && !defined(_VME_DRIVER_UNIVERSE) +#define _VME_DRIVER_UNIVERSE +#endif + +#if defined(_VME_DRIVER_TSI148) +#define _VME_TSI148_DECLARE_SHOW_ROUTINES +#include <bsp/vmeTsi148.h> +#include <bsp/vmeTsi148DMA.h> +#endif + +#if defined(_VME_DRIVER_UNIVERSE) +#define _VME_UNIVERSE_DECLARE_SHOW_ROUTINES +#include <bsp/vmeUniverse.h> +#include <bsp/vmeUniverseDMA.h> +#if !defined(BSP_VME_INSTALL_IRQ_MGR) && defined(BSP_VME_UNIVERSE_INSTALL_IRQ_MGR) +#define BSP_VME_INSTALL_IRQ_MGR BSP_VME_UNIVERSE_INSTALL_IRQ_MGR +#endif +#endif + +#include <bsp/bspVmeDmaList.h> + +/* Wrap BSP VME calls around driver calls - we do this so EPICS doesn't have to + * include bridge-specific headers. This file provides the necessary glue + * to make VME.h and vmeconfig.c independent of the bridge chip. + * + * This file is named 'vme_universe.c' for historical reasons. + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 9/2005, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +typedef struct { + int (*xlate_adrs)(int, int, unsigned long, unsigned long, unsigned long *); + int (*install_isr)(unsigned long, BSP_VME_ISR_t, void *); + int (*remove_isr)(unsigned long, BSP_VME_ISR_t, void *); + BSP_VME_ISR_t (*get_isr)(unsigned long vector, void **); + int (*enable_int_lvl)(unsigned int); + int (*disable_int_lvl)(unsigned int); + int (*outbound_p_cfg)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); + int (*inbound_p_cfg) (unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); + void (*outbound_p_show)(FILE*); + void (*inbound_p_show) (FILE*); + void (*reset_bus)(void); + int (*install_irq_mgr)(int, int, int, ...); + int irq_mgr_flags; +} VMEOpsRec, *VMEOps; + +/* two separate 'ops' structs for historic reasons */ +typedef struct DmaOpsRec_ { + int (*setup)(int, uint32_t, uint32_t, void *); + int (*start)(int, uint32_t, uint32_t, uint32_t); + uint32_t (*status)(int); + VMEDmaListClass listClass; + int nChannels; + int *vectors; +} DmaOpsRec, *DmaOps; + +#ifdef _VME_DRIVER_UNIVERSE +static VMEOpsRec uniOpsRec = { + xlate_adrs: vmeUniverseXlateAddr, + install_isr: vmeUniverseInstallISR, + remove_isr: vmeUniverseRemoveISR, + get_isr: vmeUniverseISRGet, + enable_int_lvl: vmeUniverseIntEnable, + disable_int_lvl: vmeUniverseIntDisable, + outbound_p_cfg: vmeUniverseMasterPortCfg, + inbound_p_cfg: vmeUniverseSlavePortCfg, + outbound_p_show: vmeUniverseMasterPortsShow, + inbound_p_show: vmeUniverseSlavePortsShow, + reset_bus: vmeUniverseResetBus, + install_irq_mgr: vmeUniverseInstallIrqMgrAlt, + irq_mgr_flags: VMEUNIVERSE_IRQ_MGR_FLAG_SHARED | + VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND, +}; + +static int uniVecs[] = { UNIV_DMA_INT_VEC }; + +static DmaOpsRec uniDmaOpsRec = { + setup: vmeUniverseDmaSetup, + start: vmeUniverseDmaStart, + status: vmeUniverseDmaStatus, + listClass: &vmeUniverseDmaListClass, + nChannels: 1, + vectors: uniVecs, +}; +#endif + +#ifdef _VME_DRIVER_TSI148 +static VMEOpsRec tsiOpsRec = { + xlate_adrs: vmeTsi148XlateAddr, + install_isr: vmeTsi148InstallISR, + remove_isr: vmeTsi148RemoveISR, + get_isr: vmeTsi148ISRGet, + enable_int_lvl: vmeTsi148IntEnable, + disable_int_lvl: vmeTsi148IntDisable, + outbound_p_cfg: vmeTsi148OutboundPortCfg, + inbound_p_cfg: vmeTsi148InboundPortCfg, + outbound_p_show: vmeTsi148OutboundPortsShow, + inbound_p_show: vmeTsi148InboundPortsShow, + reset_bus: vmeTsi148ResetBus, + install_irq_mgr: vmeTsi148InstallIrqMgrAlt, + irq_mgr_flags: VMETSI148_IRQ_MGR_FLAG_SHARED, +}; + +static int tsiVecs[] = { + TSI_DMA_INT_VEC, + TSI_DMA1_INT_VEC, +}; + +static DmaOpsRec tsiDmaOpsRec = { + setup: vmeTsi148DmaSetup, + start: vmeTsi148DmaStart, + status: vmeTsi148DmaStatus, + listClass: &vmeTsi148DmaListClass, + nChannels: 2, + vectors: tsiVecs, +}; +#endif + +static VMEOps theOps = 0; +static DmaOps theDmaOps = 0; + +int +BSP_vme2local_adrs(unsigned long am, unsigned long vmeaddr, unsigned long *plocaladdr) +{ +int rval=theOps->xlate_adrs(1,0,am,vmeaddr,plocaladdr); + *plocaladdr+=PCI_MEM_BASE; + return rval; +} + +int +BSP_local2vme_adrs(unsigned long am, unsigned long localaddr, unsigned long *pvmeaddr) +{ + return theOps->xlate_adrs(0, 0, am,localaddr+PCI_DRAM_OFFSET,pvmeaddr); +} + +int +BSP_installVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *arg) +{ + return theOps->install_isr(vector, handler, arg); +} + +int +BSP_removeVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *arg) +{ + return theOps->remove_isr(vector, handler, arg); +} + +/* retrieve the currently installed ISR for a given vector */ +BSP_VME_ISR_t +BSP_getVME_isr(unsigned long vector, void **parg) +{ + return theOps->get_isr(vector, parg); +} + +int +BSP_enableVME_int_lvl(unsigned int level) +{ + return theOps->enable_int_lvl(level); +} + +int +BSP_disableVME_int_lvl(unsigned int level) +{ + return theOps->disable_int_lvl(level); +} + +int +BSP_VMEOutboundPortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long pci_address, + unsigned long size) +{ + return theOps->outbound_p_cfg(port, address_space, vme_address, pci_address, size); +} + +int +BSP_VMEInboundPortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long pci_address, + unsigned long size) +{ + return theOps->inbound_p_cfg(port, address_space, vme_address, pci_address, size); +} + +void +BSP_VMEOutboundPortsShow(FILE *f) +{ + theOps->outbound_p_show(f); +} + +void +BSP_VMEInboundPortsShow(FILE *f) +{ + theOps->inbound_p_show(f); +} + +void +BSP_VMEResetBus(void) +{ + theOps->reset_bus(); +} + +int +BSP_VMEDmaSetup(int channel, uint32_t bus_mode, uint32_t xfer_mode, void *custom_setup) +{ + return theDmaOps->setup(channel, bus_mode, xfer_mode, custom_setup); +} + +int +BSP_VMEDmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ + return theDmaOps->start(channel, pci_addr, vme_addr, n_bytes); +} + +uint32_t +BSP_VMEDmaStatus(int channel) +{ + return theDmaOps->status(channel); +} + +BSP_VMEDmaListDescriptor +BSP_VMEDmaListDescriptorSetup( + BSP_VMEDmaListDescriptor d, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ +VMEDmaListClass pc; + + if ( !d ) { + + pc = theDmaOps->listClass; + + return BSP_VMEDmaListDescriptorNewTool( + pc, + attr_mask, + xfer_mode, + pci_addr, + vme_addr, + n_bytes); + + } + + return BSP_VMEDmaListDescriptorSetupTool(d, attr_mask, xfer_mode, pci_addr, vme_addr, n_bytes); +} + +int +BSP_VMEDmaListStart(int channel, BSP_VMEDmaListDescriptor list) +{ + return BSP_VMEDmaListDescriptorStartTool(0, channel, list); +} + +/* NOT thread safe! */ +int +BSP_VMEDmaInstallISR(int channel, BSP_VMEDmaIRQCallback cb, void *usr_arg) +{ +int vec; +BSP_VME_ISR_t curr; +void *carg; + + if ( channel < 0 || channel >= theDmaOps->nChannels ) + return -1; + + vec = theDmaOps->vectors[channel]; + + curr = BSP_getVME_isr(vec, &carg); + + if ( cb && curr ) { + /* IRQ currently in use */ + return -1; + } + + if ( !cb && !curr ) { + /* Allow uninstall if no handler is currently installed; + * just make sure IRQ is disabled + */ + BSP_disableVME_int_lvl(vec); + return 0; + } + + if ( cb ) { + if ( BSP_installVME_isr(vec, (BSP_VME_ISR_t)cb, usr_arg) ) + return -4; + BSP_enableVME_int_lvl(vec); + } else { + BSP_disableVME_int_lvl(vec); + if ( BSP_removeVME_isr(vec, curr, carg) ) + return -4; + } + return 0; +} + +#if defined(_VME_DRIVER_TSI148) && !defined(VME_CLEAR_BRIDGE_ERRORS) +static unsigned short +tsi_clear_errors(int quiet) +{ +unsigned long v; +unsigned short rval; + v = vmeTsi148ClearVMEBusErrors(0); + + /* return bits 8..23 of VEAT; set bit 15 to make sure rval is nonzero on error */ + rval = v ? ((v>>8) & 0xffff) | (1<<15) : 0; + return rval; +} + +#define VME_CLEAR_BRIDGE_ERRORS tsi_clear_errors +#endif + +extern unsigned short (*_BSP_clear_vmebridge_errors)(int); + +int BSP_VMEInit(void) +{ +#if defined(_VME_DRIVER_UNIVERSE) + if ( 0 == vmeUniverseInit() ) { + theOps = &uniOpsRec; + theDmaOps = &uniDmaOpsRec; + vmeUniverseReset(); + } +#endif +#if defined(_VME_DRIVER_UNIVERSE) && defined(_VME_DRIVER_TSI148) + else +#endif +#if defined(_VME_DRIVER_TSI148) + if ( 0 == vmeTsi148Init() ) { + theOps = &tsiOpsRec; + theDmaOps = &tsiDmaOpsRec; + vmeTsi148Reset(); +#ifdef VME_CLEAR_BRIDGE_ERRORS + { + + _BSP_clear_vmebridge_errors = VME_CLEAR_BRIDGE_ERRORS; + + } +#endif + } +#endif + else + /* maybe no VME at all - or no universe/tsi148 ... */ + return -1; + + return 0; +} + +int BSP_VMEIrqMgrInstall(void) +{ +int err; +#ifndef BSP_VME_INSTALL_IRQ_MGR + /* No map; use first line only and obtain PIC wire from PCI config */ + err = theOps->install_irq_mgr( + theOps->irq_mgr_flags, /* use shared IRQs */ + 0, -1, /* Universe/Tsi148 pin0 -> PIC line from config space */ + -1 /* terminate list */ + ); +#else + BSP_VME_INSTALL_IRQ_MGR(err); +#endif + + if ( err ) + return err; + +/* This feature is only supported by the Universe driver (not Tsi148) */ +#if defined(BSP_PCI_VME_DRIVER_DOES_EOI) && defined(BSP_PIC_DO_EOI) +#ifdef _VME_DRIVER_TSI148 +#error "BSP_PCI_VME_DRIVER_DOES_EOI/BSP_PIC_DO_EOI feature can only be used with vmeUniverse" +#endif + if ( vmeUniverse0PciIrqLine < 0 ) + rtems_panic("Unable to get universe interrupt line info from PCI config"); + _BSP_vme_bridge_irq = vmeUniverse0PciIrqLine; +#endif + return 0; +} diff --git a/bsps/powerpc/shared/vme/vmeconfig.c b/bsps/powerpc/shared/vme/vmeconfig.c new file mode 100644 index 0000000000..c128d75f3c --- /dev/null +++ b/bsps/powerpc/shared/vme/vmeconfig.c @@ -0,0 +1,142 @@ +/* Default VME bridge configuration - note that this file + * is independent of the bridge driver/chip + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 3/2002, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <bsp.h> +#include <bsp/VME.h> +#include <bsp/VMEConfig.h> +#ifdef BSP_VME_BAT_IDX +#include <libcpu/bat.h> +#endif +#include <rtems/bspIo.h> + +extern int BSP_VMEInit(void); +extern int BSP_VMEIrqMgrInstall(void); + +/* Use a weak alias for the VME configuration. + * This permits individual applications to override + * this routine. + * They may even create an 'empty' + * + * void BSP_vme_config(void) {} + * + * which will avoid linking in the Universe driver + * at all :-). + */ + +void __BSP_default_vme_config(void); +void BSP_vme_config(void) + __attribute__ (( weak, alias("__BSP_default_vme_config") )); + +void +__BSP_default_vme_config(void) +{ + + if ( BSP_VMEInit() ) { + printk("Skipping VME initialization...\n"); + return; + } + +#ifdef BSP_VME_BAT_IDX + /* setup a PCI area to map the VME bus */ + setdbat(BSP_VME_BAT_IDX, + PCI_MEM_BASE + _VME_A32_WIN0_ON_PCI, + PCI_MEM_BASE + _VME_A32_WIN0_ON_PCI, + 0x10000000, + IO_PAGE); +#endif + + /* map VME address ranges */ + BSP_VMEOutboundPortCfg( + 0, + VME_AM_EXT_SUP_DATA, + _VME_A32_WIN0_ON_VME, + _VME_A32_WIN0_ON_PCI, + 0x0e000000); + BSP_VMEOutboundPortCfg( + 1, + VME_AM_STD_SUP_DATA, + 0x00000000, + _VME_A24_ON_PCI, + 0x00ff0000); + BSP_VMEOutboundPortCfg( + 2, + VME_AM_SUP_SHORT_IO, + 0x00000000, + _VME_A16_ON_PCI, + 0x00010000); + +#ifdef _VME_CSR_ON_PCI + /* Map VME64 CSR */ + BSP_VMEOutboundPortCfg( + 7, + VME_AM_CSR, + 0, + _VME_CSR_ON_PCI, + 0x01000000); +#endif + + +#ifdef _VME_DRAM_OFFSET + /* map our memory to VME giving the driver a hint that it's ordinary memory + * so they can enable decoupled cycles which should give better performance... + */ + BSP_VMEInboundPortCfg( + 0, + VME_AM_EXT_SUP_DATA | VME_AM_IS_MEMORY, + _VME_DRAM_OFFSET, + PCI_DRAM_OFFSET, + BSP_mem_size); +#endif + + /* stdio is not yet initialized; the driver will revert to printk */ + BSP_VMEOutboundPortsShow(0); + BSP_VMEInboundPortsShow(0); + + BSP_VMEIrqMgrInstall(); +} |