📄 vmeuniverse.c
字号:
/* $Id: vmeUniverse.c,v 1.1.4.4 2004/11/16 23:00:27 joel Exp $ *//* Routines to configure the VME interface * Author: Till Straumann <strauman@slac.stanford.edu> * Nov 2000, Oct 2001, Jan 2002 */#include <stdio.h>#include <stdarg.h>#include "vmeUniverse.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_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_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)/* we rely on a vxWorks definition here */#define VX_AM_SUP 4#ifdef __rtems__#include <stdlib.h>#include <rtems/bspIo.h> /* printk */#include <bsp/pci.h>#include <bsp.h>/* allow the BSP to override the default routines */#ifndef BSP_PCI_FIND_DEVICE#define BSP_PCI_FIND_DEVICE BSP_pciFindDevice#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#endiftypedef unsigned int pci_ulong;#define PCI_TO_LOCAL_ADDR(memaddr) \ ((pci_ulong)(memaddr) + PCI_MEM_BASE_ADJUSTMENT)#elif defined(__vxworks)typedef unsigned long pci_ulong;#define PCI_TO_LOCAL_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#endifvolatile LERegister *vmeUniverse0BaseAddr=0;int vmeUniverse0PciIrqLine=-1;#if 0/* public access functions */volatile LERegister *vmeUniverseBaseAddr(void){ if (!vmeUniverse0BaseAddr) vmeUniverseInit(); return vmeUniverse0BaseAddr;}intvmeUniversePciIrqLine(void){ if (vmeUniverse0PciIrqLine<0) vmeUniverseInit(); return vmeUniverse0PciIrqLine;}#endifstatic inline voidWRITE_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 unsigned long*)(((unsigned long)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_REGstatic inline unsigned longREAD_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 unsigned long*)adrs);#else#error "little endian register reading not implemented"#endif}static inline unsigned longREAD_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");#endifreturn rval;#elsereturn 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__) && 0static intuprintk(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)) BSP_panic("vmeUniverse/uprintk: buffer overrun"); printk(buf); return rval;}#endif/* private printing wrapper */static voiduprintf(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);}intvmeUniverseFindPciBase( int instance, volatile LERegister **pbase ){int bus,dev,fun;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*)PCI_TO_LOCAL_ADDR(busaddr); if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline)) return -1; else vmeUniverse0PciIrqLine = irqline; return 0;}/* convert an address space selector to a corresponding * universe control mode word */static intam2mode(int ismaster, unsigned long address_space, unsigned long *pmode){unsigned long mode=0; if (!ismaster) { mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM; mode |= UNIV_SCTL_USER; } switch (address_space) { 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: 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: mode |= UNIV_CTL_VAS32; break; case VME_AM_SUP_SHORT_IO: case VME_AM_USR_SHORT_IO: mode |= UNIV_CTL_VAS16; break; case 0: /* disable the port alltogether */ break; default: return -1; } if (address_space & VX_AM_SUP) mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER); *pmode = mode; return 0;}static intdisableUniversePort(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 intcfgUniversePort( unsigned long ismaster, unsigned long port, unsigned long address_space, unsigned long vme_address, unsigned long local_address, unsigned long length){#define base vmeUniverse0BaseAddrvolatile LERegister *preg;unsigned long p=port;unsigned long mode=0; /* 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 /* calculate configuration word and enable the port */ /* 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. * ???????? */ if (ismaster) mode |= UNIV_MCTL_EN | UNIV_MCTL_PWEN | UNIV_MCTL_VDW64 | UNIV_MCTL_VCT; else mode |= UNIV_SCTL_EN | UNIV_SCTL_PWEN | UNIV_SCTL_PREN;#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;#undef base}static intshowUniversePort( 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; default: uprintf(f,"A??, "); break; } if (ismaster) { uprintf(f,"%s, %s", cntrl&UNIV_MCTL_PGM ? "Pgm" : "Dat", cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr"); } 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" : ""); } 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) * 1: success */static intxlatePort(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; } if ( (cntrl & (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK)) != 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; } } else { x = l->address - offst; if (x >= start && x < bound) { /* valid address found */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -