summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c
blob: 0762f9ad01971c1daded9182ef95029fbf079190 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                      
  
        










































                                                                               
























                                                                            






                                                              














                                                                      
                                                          
































                                                                             
   














































                                                                                                                 
                





                                            









                                                         







                                                                        


                                                     



                                                        


                                            































                                                                                         
                        



























                                                                                         
                        































                                                                                         
                



                              
                                               




                                                        
                                      














                                                                         
                

























                                                              
                                






                                              
                                                                            


                                              
                                                                                   




                                                                
                                                                            

























































































































































































































































































                                                                                                                               
                                                                                












































                                                                                    









                                                                     
 




                                                 



















































                                                                                                     







                                                                                    


             
                       
                        
                            
                                                            
                                                          



                           




























                                                                               














                                                                             






























































































































































                                                                                            
                        









                                                                              
                







                                                 
                                                              
 
                                        





                                                    


                                         







                                                      






















                                                                        





























































































































                                                                                           



                                                                       





                                                                       






                                                                             
 
                                                  







































                                                                                                 
                                     








                                                                          
                                                                                                           
















                                                                                      
                                                                                                           













                                                            
                                                                      











                                                                           
                                                                      










                                                                           
/* $Id$ */

/* Routines to configure the VME interface
 * Author: Till Straumann <strauman@slac.stanford.edu>
 *         Nov 2000, Oct 2001, Jan 2002
 */

#if 0
/*
 * $Log$
 * Revision 1.34  2003/02/10 23:20:05  till
 *  - added some macro magic to make porting easier (ppcn_60x BSP in mind)
 *  - made mgrInstalled public (vmeUniverseIrqMgrInstalled) so BSPs can
 *    supply their own versions of the mgrInstall() routine.
 *  - added READMEs to CVS
 *
 * Revision 1.33.2.1  2003/02/10 23:01:40  till
 *  - added some macro magic to make porting easier (ppcn_60x BSP in mind)
 *  - made mgrInstalled public (vmeUniverseIrqMgrInstalled) so BSPs can
 *    supply their own versions of the mgrInstall() routine.
 *
 * Revision 1.32  2002/09/05 02:50:41  till
 *  - use k_vsprintf(), not vsprintf() during early boot (RTEMS)
 *
 * Revision 1.31  2002/08/16 23:35:15  strauman
 *  - fixed typos
 *
 * Revision 1.30  2002/08/16 22:53:37  till
 *  - made address translation more generic; allow to look for reverse mappings
 *    also.
 *  - removed vmeUniverseLocalToBus() and vmeUniverseBusToLocal() and made
 *    vmeUniverseXlateAddr() public instead.
 *
 * Revision 1.29  2002/08/16 22:16:25  till
 *  - tweak U2SPEC (fix Joerger vtr10012_8 problem) the same
 *    way the synergy BSP does (rev 2 chips only)
 *
 * Revision 1.28  2002/08/15 23:16:01  till
 *  - bugfix: vmeUniverseISRGet() dereferenced a possibly NULL pointer
 *
 * Revision 1.25  2002/07/19 05:18:40  till
 *  - CVS log: again a problem. Cannot embed /_* *_/ comments in the log
 *    because the log has to be commented as a whole. I had tried
 *    #if 0 #endif - doesn't work because cpp expands char constants???
 *    WHAT A NUISANCE
 *
 * Revision 1.24  2002/07/19 02:44:14  till
 *  - added a new parameter to the IRQ manager install routine:
 *    the main interrupt's line can now also be specified (reading
 *    from PCI config does not always work - some boards don't
 *    have correct information there - PMC's will have a similar
 *    problem, though!)
 *
 * Revision 1.21  2002/04/11 06:54:48  till
 *  - silenced message about 'successfully configured a port'
 *
 * Revision 1.20  2002/03/27 21:14:50  till
 *  - fix: handler table holds pointers, so hdlrTbl[vector]->usrData etc.
 *    not hdlrTbl[vector].usrData...
 *
 * Revision 1.19  2002/03/09 00:14:36  till
 *  - added vmeUniverseISRGet() to retrieve the currently installed
 *    ISR for a given vector
 *  - swapped the argument order for ISRs to (usrarg, vector)
 *
 * Revision 1.18  2002/02/07 19:53:48  till
 *  - reverted back to publish base_addr/irq_line as variables rather than
 *    through functions: the irq_line is read by the interrupt dispatcher...
 *
 * Revision 1.17  2002/01/24 08:28:10  till
 *  - initialize driver when reading base address or irq line.
 *    however, this requires the pci driver to be working already.
 *
 * Revision 1.16  2002/01/24 08:21:48  till
 *  - replaced public global vars for base address/irq line by routines.
 *
 * Revision 1.15  2002/01/23 06:15:30  till
 *   - changed master port data width to 64 bit.
 *        NOTE: reading the CY961 (Echotek ECDR814) with VDW32
 *              generated bus errors when reading 32-bit words
 *              - very weird, because the registers are 16-bit
 *                AFAIK.
 *              - 32-bit accesses worked fine on vxWorks which
 *                has the port set to 64-bit.
 *              ????????
 *
 * Revision 1.14  2002/01/11 19:30:54  till
 *  - added more register defines to header
 *  - completed vmeUniverseReset
 *
 * Revision 1.13  2002/01/11 05:06:18  till
 *  - fixed VMEISR failing to check (lint_stat & msk) when determining
 *    the highes level...
 *  - tested interrupt handling & nesting. Seems to work.
 *
 * Revision 1.12  2002/01/11 02:25:55  till
 *  - added interrupt manager
 *
 * Revision 1.11  2002/01/08 03:59:52  till
 *  - vxworks always defines _LITTLE_ENDIAN, fixed the conditionals
 *    so it should work on __vxworks and on __rtems__ now.
 *  - rtems uprintf wrapper reverts to printk if stdio is not yet
 *    initialized (uses _impure_ptr->__sdidinit)
 *  - tested bus address translation utility routines
 *
 * Revision 1.9  2002/01/05 02:36:32  till
 *  - added vmeUniverseBusToLocalAdrs / vmeUniverseLocalToBusAdrs for address
 *    space translations.
 *  - include bsp.h under rtems to hack around the libcpu/powerpc/shared/io.h
 *    #define _IO_BASE & friends problem.
 *
 * Revision 1.8  2002/01/04 04:12:51  till
 *  - changed some rtems/pci related names
 *
 * Revision 1.7  2002/01/04 03:06:30  till
 *  - added further register definitions
 *
 * Revision 1.6  2001/12/20 04:42:44  till
 *  - fixed endianness stuff; theoretically, PPC could be LITTLE_ENDIAN...
 *
 * Revision 1.4  2001/12/19 01:59:02  till
 *  - started adding interrupt stuff
 *  - private implementation of PCI scanning if necessary
 *
 * Revision 1.3  2001/07/27 22:22:51  till
 *  - added more DMA support routines and defines to include file
 *  - xxxPortsShow can now print to a given file descriptor argument
 *
 * Revision 1.2  2001/07/26 18:06:13  till
 *  - ported to RTEMS
 *  - fixed a couple of wrong pointer calculations.
 *
 * Revision 1.1.1.1  2001/07/12 23:15:19  till
 *  - cvs import
 */
#endif

#include <stdio.h>
#include <stdarg.h>
#include "vmeUniverse.h"

#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
#endif

typedef unsigned int pci_ulong;
#define PCI_TO_LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)


#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
#endif

volatile LERegister *vmeUniverse0BaseAddr=0;
int vmeUniverse0PciIrqLine=-1;

#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 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_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 unsigned long*)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)
	
#ifdef __rtems__
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))
			BSP_panic("vmeUniverse/uprintk: buffer overrun");
	printk(buf);
	return rval;
}
#endif


/* private printing wrapper */
static int
uprintf(FILE *f, char *fmt, ...)
{
va_list	ap;
int	rval;
	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.
		 */
		rval=uprintk(fmt,ap);
	} else 
#endif
	{
		rval=vfprintf(f,fmt,ap);
	}
	va_end(ap);
	return rval;
}

int
vmeUniverseFindPciBase(
	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 int
am2mode(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 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(
	unsigned long	ismaster,
	unsigned long	port,
	unsigned long	address_space,
	unsigned long	vme_address,
	unsigned long	local_address,
	unsigned long	length)
{
#define base vmeUniverse0BaseAddr
volatile LERegister *preg=base;
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;
	}

	/* 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 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,"%i:    0x%08lx 0x%08lx 0x%08lx ",
			portno,offst,bound-start,start);
	} else {
		uprintf(f,"%i:    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 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;
	}
	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 */
			l->address = x;
			return 1;
		}
	}
	return 0;
}


static int
mapOverAll(int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
{
#define base	vmeUniverse0BaseAddr
volatile LERegister	*rptr;
unsigned long	port;
int	rval;

	/* get the universe base address */
	if (!base && vmeUniverseInit()) {
		uprintf(stderr,"unable to find the universe in pci config space\n");
		return -1;
	}
	rptr = (base + 
		(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;
#undef base
}

static void
showUniversePorts(int ismaster, FILE *f)
{
	if (!f) f=stdout;
	uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave");
	uprintf(f,"Port  VME-Addr   Size       PCI-Adrs   Mode:\n");
	mapOverAll(ismaster,showUniversePort,f);
}

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 */
	)
{
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(master,xlatePort,(void*)&l) - 1;
	*paOut = l.address;
	return rval;
}

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);

	/* disable VME bus image of VME CSR */
	vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);


	/* I had problems with a Joerger vtr10012_8 card who would
	 * only be accessible after tweaking the U2SPEC register
	 * (the t27 parameter helped).
	 * 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))) {
		uprintf(stderr,"unable to find the universe in pci config space\n");
	} else {
		uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %i\n",
				(unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine);
	}
	return rval;
}

void
vmeUniverseMasterPortsShow(FILE *f)
{
	showUniversePorts(1,f);
}

void
vmeUniverseSlavePortsShow(FILE *f)
{
	showUniversePorts(0,f);
}

int
vmeUniverseMasterPortCfg(
	unsigned long	port,
	unsigned long	address_space,
	unsigned long	vme_address,
	unsigned long	local_address,
	unsigned long	length)
{
	return cfgUniversePort(1,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)
{
	return cfgUniversePort(0,port,address_space,vme_address,local_address,length);
}

void
vmeUniverseDisableAllSlaves(void)
{
	mapOverAll(0,disableUniversePort,0);
}

void
vmeUniverseDisableAllMasters(void)
{
	mapOverAll(1,disableUniversePort,0);
}

int
vmeUniverseStartDMA(
	unsigned long local_addr,
	unsigned long vme_addr,
	unsigned long count)
{

	if (!vmeUniverse0BaseAddr && vmeUniverseInit()) return -1;
	if ((local_addr & 7) != (vme_addr & 7)) {
		uprintf(stderr,"vmeUniverseStartDMA: misaligned addresses\n");
		return -1;
	}

	{
	/* help the compiler allocate registers */
	register volatile LERegister *b=vmeUniverse0BaseAddr;
	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(local_addr,
		      b, UNIV_REGOFF_DLA);
	WRITE_LE(vme_addr,
		      b, UNIV_REGOFF_DVA);
	WRITE_LE(count,
		      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;
}

unsigned long
vmeUniverseReadReg(unsigned long offset)
{
unsigned long rval;
	rval = READ_LE(vmeUniverse0BaseAddr,offset);
	return rval;
}

void
vmeUniverseWriteReg(unsigned long value, unsigned long offset)
{
	WRITE_LE(value, vmeUniverse0BaseAddr, offset);
}

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) : "r"(p) : "r0"
			);
#elif defined(__rtems__)
		p--; st_le32(p, *p);
#else
#error	"vmeUniverse: endian conversion not implemented for this architecture"
#endif
	}
#endif
}

/* RTEMS interrupt subsystem */

#ifdef __rtems__
#include <bsp/irq.h>

typedef struct
UniverseIRQEntryRec_ {
		VmeUniverseISR	isr;
		void			*usrData;
} UniverseIRQEntryRec, *UniverseIRQEntry;

static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};

int        vmeUniverseIrqMgrInstalled=0;
static int vmeIrqUnivOut=-1;
static int specialIrqUnivOut=-1;

VmeUniverseISR
vmeUniverseISRGet(unsigned long vector, void **parg)
{
	if (vector>=UNIV_NUM_INT_VECS ||
		! universeHdlTbl[vector])
		return 0;
	if (parg)
		*parg=universeHdlTbl[vector]->usrData;
	return universeHdlTbl[vector]->isr;
}

static void
universeSpecialISR(void)
{
register UniverseIRQEntry	ip;
register unsigned			vec;
register unsigned long		status;

	status=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);

	/* scan all LINT bits except for the 'normal' VME interrupts */

	/* 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 (status>>=8; status; status>>=1) {
		vec++;
		if ((status&1) && (ip=universeHdlTbl[vec]))
			ip->isr(ip->usrData,vec);
	}
	/* clear all special interrupts */
	vmeUniverseWriteReg(
					~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1),
					UNIV_REGOFF_LINT_STAT
					);

/*
 *	clear our line in the VINT_STAT register
 *  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(void)
{
UniverseIRQEntry ip;
unsigned long lvl,msk,lintstat,linten,status;

		/* determine the highest priority IRQ source */
		lintstat=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
		for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7;
			 lvl>0;
			 lvl--, msk>>=1) {
			if (lintstat & msk) break;
		}
		if (!lvl) {
				/* try the special handler */
				universeSpecialISR();

				/* 
				 * let the pic end this cycle
				 */
				BSP_PIC_DO_EOI;

				return;
		}
		linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);

		/* mask this and all lower levels */
		vmeUniverseWriteReg(
						linten & ~((msk<<1)-UNIV_LINT_STAT_VIRQ1),
						UNIV_REGOFF_LINT_EN
						);

		/* end this interrupt
		 * cycle on the PCI bus, so higher level interrupts can be
		 * caught from now on...
		 */
		BSP_PIC_DO_EOI;

		/* 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 :-( */
		} else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
				/* TODO: log error message - RTEMS has no logger :-( */
		} else {
				/* dispatch handler, it must clear the IRQ at the device */
				ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
		}

		/* clear this interrupt level */
		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);
*/


		/* re-enable the previous level */
		vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN);
}

/* 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);
}

int
vmeUniverseInstallIrqMgr(int vmeOut,
						 int vmeIrqPicLine,
						 int specialOut,
						 int specialIrqPicLine)
{
rtems_irq_connect_data aarrggh;

	/* check parameters */
	if ((vmeIrqUnivOut=vmeOut) < 0 || vmeIrqUnivOut > 7) return -1;
	if ((specialIrqUnivOut=specialOut) > 7) return -2;
	if (specialOut >=0 && specialIrqPicLine < 0) return -3;
	/* give them a chance to override buggy PCI info */
	if (vmeIrqPicLine >= 0) {
		uprintf(stderr,"Overriding main IRQ line PCI info with %i\n",
				vmeIrqPicLine);
		vmeUniverse0PciIrqLine=vmeIrqPicLine;
	}

	if (vmeUniverseIrqMgrInstalled) return -4;

	aarrggh.on=my_no_op; /* at _least_ they could check for a 0 pointer */
	aarrggh.off=my_no_op;
	aarrggh.isOn=my_isOn;
	aarrggh.hdl=universeVMEISR;
	aarrggh.name=vmeUniverse0PciIrqLine + BSP_PCI_IRQ0;
	if (!BSP_install_rtems_irq_handler(&aarrggh))
			BSP_panic("unable to install vmeUniverse irq handler");
	if (specialIrqUnivOut > 0) {
			/* install the special handler to a separate irq */
			aarrggh.hdl=universeSpecialISR;
			aarrggh.name=specialIrqPicLine + BSP_PCI_IRQ0;
			if (!BSP_install_rtems_irq_handler(&aarrggh))
				BSP_panic("unable to install vmeUniverse secondary irq handler");
	} else {
		specialIrqUnivOut = vmeIrqUnivOut;
	}
	/* setup routing */

	vmeUniverseWriteReg(
		(UNIV_LINT_MAP0_VIRQ7(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ6(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ5(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ4(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ3(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ2(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VIRQ1(vmeIrqUnivOut) |
		 UNIV_LINT_MAP0_VOWN(specialIrqUnivOut)
		),
		UNIV_REGOFF_LINT_MAP0);
	vmeUniverseWriteReg(
		(UNIV_LINT_MAP1_ACFAIL(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_SYSFAIL(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_SW_INT(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_SW_IACK(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_VERR(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_LERR(specialIrqUnivOut) |
		 UNIV_LINT_MAP1_DMA(specialIrqUnivOut)
		),
		UNIV_REGOFF_LINT_MAP1);
	vmeUniverseIrqMgrInstalled=1;
	return 0;
}


int
vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
{
UniverseIRQEntry ip;

		if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
				return -1;

		ip=universeHdlTbl[vector];

		if (ip || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
				return -1;
		ip->isr=hdl;
		ip->usrData=arg;
		universeHdlTbl[vector]=ip;
		return 0;
}

int
vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
{
UniverseIRQEntry ip;

		if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
				return -1;

		ip=universeHdlTbl[vector];

		if (!ip || ip->isr!=hdl || ip->usrData!=arg)
				return -1;
		universeHdlTbl[vector]=0;
		free(ip);
		return 0;
}

int
vmeUniverseIntEnable(unsigned int level)
{
		if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
				return -1;
		vmeUniverseWriteReg(
				(vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) | 
				 (UNIV_LINT_EN_VIRQ1 << (level-1))
				),
				UNIV_REGOFF_LINT_EN);
		return 0;
}

int
vmeUniverseIntDisable(unsigned int level)
{
		if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
				return -1;
		vmeUniverseWriteReg(
				(vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & 
				 ~ (UNIV_LINT_EN_VIRQ1 << (level-1))
				),
				UNIV_REGOFF_LINT_EN);
		return 0;
}


#endif