summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c
blob: 9cb12e6e8f1dbe0556ac3770b05cb2494e4b6ca0 (plain) (tree)
1
2
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710

          













































                                                                           
 
                  




                            
                        
                           
















                                                              
                                            






                                            
                                            














                                                                                                                 
                


                                            
                        

                    
                             
 

                                                    
                                                               

                              
                                                             

                              






                                                             
      
 


                                                                    
                               






                                                                         
 






                                                                           
 

                                
                                             


                                                     



                                                        


                                            



                                            




                            














                                                                                                      



























                                                                                         
                        












                                                                           
                                                                   













                                                                                         
                        


























                                                                                         
                                                           
 
 

                                                                  
                           



                              
                                               




                                                        
                                      






                                                                         
 
                              
           


                                
                          
                





                                                              
                                
               

         
                                   

                   








                                   
                   


                      
                                






                                              
                                                                            


                                              
                                                                                   


                                                     
                                                                 
 
                                                                            
                          
 




                                                                                                            









                                                                        
                     









                                                           

                                              


                                                      



                                                                















                                                             


                                                       
         


                                           
                                              






                                                       
 
                                       
 

                                         




















                                                                                             

                                               
        








                                                       
 

                                         




















                                                                                             
                                               
 






                                               





                                                 





                                                          
                                           
                                                                       

                                                    
















                                                                                        
                                  






                                      
                          


                       

                              































                                                                          

                  
























































                                                                                                            






















                                                                                                                               

 
































                                                                             
                                                            

                                                        
                                                            



                                                        




                                                                                     



                                                   

































                                                                                            


                                                               

                                             
                
                                         



                                                                



                                             







                                
                                                                                








                                                    
                       



















                                                                                    
 












                                                                                  

         


                      
                             












                                                                               





                                                                     
                                                


                                       
 


                                                 
                                        
                 



                 











                                                                                               
 
          
                                                                                                                
 



                              


                              













                                                                                                     
                       





                                                                                                     


           
                                                                   



                                                                        
                                                     

 

              
                                                                   





                                                                                    


             
                       
                        
                            
                                                            
                                                               



                           
   












                                                                                    












                                                                                    

























                                                                               
                                                               

                                                     
      
 
 













                                                                             











                                                      
        

























                                                                             
                                                                           

                                                                                    

                                              
                                                                                         





                                                                                            





                                                                

                                   







                                                               




                                  













                                                                                           









                                      













                                                                                           









                                      








                                                                                           




                                 







                                                         




                                  

                                                 

 








                                                                     








                                                    





                                                                                           





                                                              







                                                                                   








                                                                                       
                                                 
                          
                        







                                                                              

















































                                                                                                 












































                                                                                                                    



                                                         
 


                                                                                                   
 



























































































































































































































































































































































































































                                                                                                                     
 







                                                 
                                                              
 


                                            







































































                                                                                            



                                                    




                                                         
                         








                                                     

 

                                                                              
           
                                        
 

                                            
                                  
 
                                                                         
 

                                                                                 











                                                                        
                                          
                      
                                                        
                                                 
         














                                                                               

                                           

                                                                  

                                              
  
         



















                                                                        
                                       
 



                                                       
                           

                               

                                                               









                                                                                         




                                                     

      
                                                               








                                                              
                                                             
                                                                                 
 
                                   

                                                             

                                                       




                                                                 
                                                                                    
                                    
                                                                                                              






                                                                          


                                       






                                                                                       





                                                                                                                                           
                                                                                  




                                                   
                                                                                       
                                                                                                                                                


                                                                                           










                                                                                                                        

                 
                                                                                                  
                                                                
 







                                                                           

                     

                                                                 
      

 
 










                                                                    





























                                                                                                      








































                                                                                                                                                                                                                                 
   
                                                                       



                               
                                                                         




                    
                                                                             
 
                                                                            
                                     

                                                                 

                              






























                                                                         






























































                                                                                                                                          
                                                           
                                                                        
                                                                             

                                                  
         
 


                                                                               
                                                                                                     
         


                                             

                           
                                                     
                                     








                                                                                      


                 
   












                                                                       

                                                                          


                                
 
                                                                                                           

                                          
                                             
 
                                                                                       
                                          
 

                                









                                                      





                                                                         


                                
 
                                                                                                           

                                          
                                             
 







                                                              



                         























                                                                                                           


                                        
                                    




                                         



































                                                                                                                          
                                     




























































































































                                                                                                                 

 
      
/* $Id$ */

/* 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>

#if defined(__rtems__)
#define __INSIDE_RTEMS_BSP__
#endif

#include "vmeUniverse.h"
#include "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 <bsp/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 unsigned int 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 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)
	
#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))
			BSP_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);
}

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 unsigned LERegister1;

typedef struct VmeUniverseDmaListDescRec_ {
	LERegister1	dctl;
	LERegister1	dtbc;
	LERegister1	dla;
	LERegister1	dummy1;
	LERegister1	dva;
	LERegister1	dummy2;
	LERegister1	dcpp;
	LERegister1	dummy3;
} VmeUniverseDmaListDescRec
__attribute__((aligned(32), __may_alias__));
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,
};

/* gcc complains even if unsigned and uint32 are the same size :-( */

static inline void ST_LE32(volatile uint32_t *a, uint32_t v)
{
	st_le32( (volatile unsigned *)a, v);
}

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%08x%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%08x -- 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%08x -- 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))
			BSP_panic("unable to install vmeUniverse shared irq handler");
#else
		uprintf(stderr,"vmeUniverse: WARNING: your BSP doesn't support sharing interrupts\n");
		if (!BSP_install_rtems_irq_handler(&aarrggh))
			BSP_panic("unable to install vmeUniverse irq handler");
#endif
	} else {
		if (!BSP_install_rtems_irq_handler(&aarrggh))
			BSP_panic("unable to install vmeUniverse irq handler");
	}
}

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