source: rtems/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c @ adac8ab

4.104.114.84.95
Last change on this file since adac8ab was adac8ab, checked in by Till Straumann <strauman@…>, on 01/17/07 at 06:33:29

2007-01-16 Till Straumann <strauman@…>

  • vmeUniverse/vmeTsi148.c, vmeUniverse/vmeTsi148.h,
  • vmeUniverse/vmeUniverse.c, vmeUniverse/vmeUniverse.h,
  • vmeUniverse/vme_am_defs.h: Added SLAC/Stanford Authorship Note / Copyright + Liability Disclaimer.
  • Property mode set to 100644
File size: 51.3 KB
Line 
1/* $Id$ */
2
3/* Driver for the Tundra Universe II pci-vme bridge */
4
5/*
6 * Authorship
7 * ----------
8 * This software was created by
9 *     Till Straumann <strauman@slac.stanford.edu>, 2000-2007,
10 *         Stanford Linear Accelerator Center, Stanford University.
11 *
12 * Acknowledgement of sponsorship
13 * ------------------------------
14 * This software was produced by
15 *     the Stanford Linear Accelerator Center, Stanford University,
16 *         under Contract DE-AC03-76SFO0515 with the Department of Energy.
17 *
18 * Government disclaimer of liability
19 * ----------------------------------
20 * Neither the United States nor the United States Department of Energy,
21 * nor any of their employees, makes any warranty, express or implied, or
22 * assumes any legal liability or responsibility for the accuracy,
23 * completeness, or usefulness of any data, apparatus, product, or process
24 * disclosed, or represents that its use would not infringe privately owned
25 * rights.
26 *
27 * Stanford disclaimer of liability
28 * --------------------------------
29 * Stanford University makes no representations or warranties, express or
30 * implied, nor assumes any liability for the use of this software.
31 *
32 * Stanford disclaimer of copyright
33 * --------------------------------
34 * Stanford University, owner of the copyright, hereby disclaims its
35 * copyright and all other rights in this software.  Hence, anyone may
36 * freely use it for any purpose without restriction. 
37 *
38 * Maintenance of notices
39 * ----------------------
40 * In the interest of clarity regarding the origin and status of this
41 * SLAC software, this and all the preceding Stanford University notices
42 * are to remain affixed to any copy or derivative of this software made
43 * or distributed by the recipient and are to be affixed to any copy of
44 * software made or distributed by the recipient that contains a copy or
45 * derivative of this software.
46 *
47 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
48 */
49
50#include <stdio.h>
51
52#if defined(__rtems__)
53#define __INSIDE_RTEMS_BSP__
54#endif
55
56#include "vmeUniverse.h"
57
58#define UNIV_NUM_MPORTS         8 /* number of master ports */
59#define UNIV_NUM_SPORTS         8 /* number of slave ports */
60
61#define PCI_VENDOR_TUNDRA       0x10e3
62#define PCI_DEVICE_UNIVERSEII   0
63#define PCI_UNIVERSE_BASE0      0x10
64#define PCI_UNIVERSE_BASE1      0x14
65
66#define UNIV_REGOFF_PCITGT0_CTRL 0x100
67#define UNIV_REGOFF_PCITGT4_CTRL 0x1a0
68#define UNIV_REGOFF_VMESLV0_CTRL 0xf00
69#define UNIV_REGOFF_VMESLV4_CTRL 0xf90
70
71#define UNIV_CTL_VAS16          (0x00000000)
72#define UNIV_CTL_VAS24          (0x00010000)
73#define UNIV_CTL_VAS32          (0x00020000)
74#define UNIV_MCTL_VASCSR        (0x00050000)
75#define UNIV_CTL_VAS            (0x00070000)
76
77#define UNIV_MCTL_EN            (0x80000000)
78#define UNIV_MCTL_PWEN          (0x40000000)
79#define UNIV_MCTL_PGM           (0x00004000)
80#define UNIV_MCTL_VCT           (0x00000100)
81#define UNIV_MCTL_SUPER         (0x00001000)
82#define UNIV_MCTL_VDW16         (0x00400000)
83#define UNIV_MCTL_VDW32         (0x00800000)
84#define UNIV_MCTL_VDW64         (0x00c00000)
85
86#define UNIV_MCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_MCTL_PGM | UNIV_MCTL_SUPER)
87
88#define UNIV_SCTL_EN            (0x80000000)
89#define UNIV_SCTL_PWEN          (0x40000000)
90#define UNIV_SCTL_PREN          (0x20000000)
91#define UNIV_SCTL_PGM           (0x00800000)
92#define UNIV_SCTL_DAT           (0x00400000)
93#define UNIV_SCTL_SUPER         (0x00200000)
94#define UNIV_SCTL_USER          (0x00100000)
95
96#define UNIV_SCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
97
98#ifdef __rtems__
99
100#include <stdlib.h>
101#include <rtems/bspIo.h>        /* printk */
102#include <rtems/error.h>
103#include <bsp/pci.h>
104#include <bsp.h>
105
106/* allow the BSP to override the default routines */
107#ifndef BSP_PCI_FIND_DEVICE
108#define BSP_PCI_FIND_DEVICE                     pci_find_device
109#endif
110#ifndef BSP_PCI_CONFIG_IN_LONG
111#define BSP_PCI_CONFIG_IN_LONG          pci_read_config_dword
112#endif
113#ifndef BSP_PCI_CONFIG_IN_BYTE
114#define BSP_PCI_CONFIG_IN_BYTE          pci_read_config_byte
115#endif
116#ifndef BSP_PCI_CONFIG_IN_SHORT
117#define BSP_PCI_CONFIG_IN_SHORT         pci_read_config_word
118#endif
119#ifndef BSP_PCI_CONFIG_OUT_SHORT
120#define BSP_PCI_CONFIG_OUT_SHORT        pci_write_config_word
121#endif
122
123/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses.
124 * Should be defined by the BSP.
125 */
126typedef unsigned int pci_ulong;
127
128#ifndef BSP_PCI2LOCAL_ADDR
129#ifndef PCI_MEM_BASE
130#define PCI_MEM_BASE 0
131#endif
132#define BSP_PCI2LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)
133#endif
134
135
136#elif defined(__vxworks)
137typedef unsigned long pci_ulong;
138#define BSP_PCI2LOCAL_ADDR(memaddr) (memaddr)
139#define BSP_PCI_FIND_DEVICE             pciFindDevice
140#define BSP_PCI_CONFIG_IN_LONG  pciConfigInLong
141#define BSP_PCI_CONFIG_IN_BYTE  pciConfigInByte
142#else
143#error "vmeUniverse not ported to this architecture yet"
144#endif
145
146#ifndef PCI_INTERRUPT_LINE
147#define PCI_INTERRUPT_LINE              0x3c
148#endif
149
150volatile LERegister *vmeUniverse0BaseAddr=0;
151int vmeUniverse0PciIrqLine=-1;
152
153#ifdef __rtems__
154int vmeUniverseRegPort = -1;
155int vmeUniverseRegCSR  = 0;
156#endif
157
158#define DFLT_BASE       volatile LERegister *base = vmeUniverse0BaseAddr
159
160#define CHECK_DFLT_BASE(base) \
161        do { \
162                /* get the universe base address */ \
163                if (!base) { \
164                        if (vmeUniverseInit()) { \
165                                uprintf(stderr,"unable to find the universe in pci config space\n"); \
166                                return -1; \
167                        } else { \
168                                base = vmeUniverse0BaseAddr; \
169                        } \
170                } \
171        } while (0)
172
173#if 0
174/* public access functions */
175volatile LERegister *
176vmeUniverseBaseAddr(void)
177{
178        if (!vmeUniverse0BaseAddr) vmeUniverseInit();
179        return vmeUniverse0BaseAddr;
180}
181
182int
183vmeUniversePciIrqLine(void)
184{
185        if (vmeUniverse0PciIrqLine<0) vmeUniverseInit();
186        return vmeUniverse0PciIrqLine;
187}
188#endif
189
190static inline void
191WRITE_LE(
192        unsigned long val,
193        volatile LERegister    *adrs,
194        unsigned long off)
195{
196#if (__LITTLE_ENDIAN__ == 1)
197        *(volatile unsigned long*)(((unsigned long)adrs)+off)=val;
198#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
199        /* offset is in bytes and MUST not end up in r0 */
200        __asm__ __volatile__("stwbrx %1, %0, %2" :: "b"(off),"r"(val),"r"(adrs));
201#elif defined(__rtems__)
202        st_le32((volatile unsigned long*)(((unsigned long)adrs)+off), val);
203#else
204#error "little endian register writing not implemented"
205#endif
206}
207
208#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)
209#define SYNC __asm__ __volatile__("sync")
210#else
211#define SYNC
212#warning "SYNC instruction unknown for this architecture"
213#endif
214
215/* registers should be mapped to guarded, non-cached memory; hence
216 * subsequent stores are ordered. eieio is only needed to enforce
217 * ordering of loads with respect to stores.
218 */
219#define EIEIO_REG
220
221static inline unsigned long
222READ_LE0(volatile LERegister *adrs)
223{
224#if (__LITTLE_ENDIAN__ == 1)
225        return *(volatile unsigned long *)adrs;
226#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
227register unsigned long rval;
228__asm__ __volatile__("lwbrx %0, 0, %1":"=r"(rval):"r"(adrs));
229        return rval;
230#elif defined(__rtems__)
231        return ld_le32((volatile unsigned long*)adrs);
232#else
233#error "little endian register reading not implemented"
234#endif
235}
236
237static inline unsigned long
238READ_LE(volatile LERegister *adrs, unsigned long off)
239{
240#if (__LITTLE_ENDIAN__ == 1)
241        return  *((volatile LERegister *)(((unsigned long)adrs)+off));
242#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
243register unsigned long rval;
244        /* offset is in bytes and MUST not end up in r0 */
245__asm__ __volatile__("lwbrx %0, %2, %1"
246                                : "=r"(rval)
247                                : "r"(adrs), "b"(off));
248#if 0
249__asm__ __volatile__("eieio");
250#endif
251return rval;
252#else
253return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off));
254#endif
255}
256
257#define PORT_UNALIGNED(addr,port) \
258        ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
259
260
261#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
262       
263#if defined(__rtems__) && 0
264static int
265uprintk(char *fmt, va_list ap)
266{
267int             rval;
268extern int k_vsprintf(char *, char *, va_list);
269/* during bsp init, there is no malloc and no stdio,
270 * hence we assemble the message on the stack and revert
271 * to printk
272 */
273char    buf[200];
274        rval = k_vsprintf(buf,fmt,ap);
275        if (rval > sizeof(buf))
276                        BSP_panic("vmeUniverse/uprintk: buffer overrun");
277        printk(buf);
278        return rval;
279}
280#endif
281
282
283/* private printing wrapper */
284static void
285uprintf(FILE *f, char *fmt, ...)
286{
287va_list ap;
288        va_start(ap, fmt);
289#ifdef __rtems__
290        if (!f || !_impure_ptr->__sdidinit) {
291                /* Might be called at an early stage when
292                 * stdio is not yet initialized.
293                 * There is no vprintk, hence we must assemble
294                 * to a buffer.
295                 */
296                vprintk(fmt,ap);
297        } else
298#endif
299        {
300                vfprintf(f,fmt,ap);
301        }
302        va_end(ap);
303}
304
305int
306vmeUniverseFindPciBase(
307        int instance,
308        volatile LERegister **pbase
309        )
310{
311int bus,dev,fun;
312unsigned short wrd;
313pci_ulong busaddr;
314unsigned char irqline;
315
316        if (BSP_PCI_FIND_DEVICE(
317                        PCI_VENDOR_TUNDRA,
318                        PCI_DEVICE_UNIVERSEII,
319                        instance,
320                        &bus,
321                        &dev,
322                        &fun))
323                return -1;
324        if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE0,&busaddr))
325                return -1;
326        if ((unsigned long)(busaddr) & 1) {
327                /* it's IO space, try BASE1 */
328                if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE1,&busaddr)
329                   || ((unsigned long)(busaddr) & 1))
330                        return -1;
331        }
332        *pbase=(volatile LERegister*)BSP_PCI2LOCAL_ADDR(busaddr);
333
334        if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline))
335                return -1;
336
337        /* Enable PCI master and memory access */
338        BSP_PCI_CONFIG_IN_SHORT(bus, dev, fun, PCI_COMMAND, &wrd);
339        BSP_PCI_CONFIG_OUT_SHORT(bus, dev, fun, PCI_COMMAND, wrd | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
340
341        return irqline;
342}
343
344/* convert an address space selector to a corresponding
345 * universe control mode word
346 */
347
348static int
349am2mode(int ismaster, unsigned long address_space, unsigned long *pmode)
350{
351unsigned long mode=0;
352
353    /* NOTE: reading the CY961 (Echotek ECDR814) with VDW32
354     *       generated bus errors when reading 32-bit words
355     *       - very weird, because the registers are 16-bit
356     *         AFAIK.
357     *       - 32-bit accesses worked fine on vxWorks which
358     *         has the port set to 64-bit.
359     *       ????????
360     */
361
362        address_space &= ~VME_MODE_MATCH_MASK;
363
364        if (!ismaster) {
365                mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM;
366                mode |= UNIV_SCTL_USER;
367                if ( VME_AM_IS_MEMORY & address_space )
368                        mode |= UNIV_SCTL_PWEN | UNIV_SCTL_PREN;
369                mode |= UNIV_SCTL_EN;
370        } else {
371                if ( VME_MODE_DBW16 & address_space )
372                        mode |= UNIV_MCTL_VDW16;
373                else
374                        mode |= UNIV_MCTL_VDW64;
375                mode |= UNIV_MCTL_VCT /* enable block transfers */;
376                if ( VME_AM_IS_MEMORY & address_space )
377                        mode |= UNIV_MCTL_PWEN;
378                mode |= UNIV_MCTL_EN;
379        }
380
381        address_space &= ~VME_AM_IS_MEMORY;
382
383        switch (address_space & VME_AM_MASK) {
384                case VME_AM_STD_SUP_PGM:
385                case VME_AM_STD_USR_PGM:
386                        if (ismaster)
387                                mode |= UNIV_MCTL_PGM ;
388                        else {
389                                mode &= ~UNIV_SCTL_DAT;
390                        }
391                        /* fall thru */
392                case VME_AM_STD_SUP_DATA:
393                case VME_AM_STD_USR_DATA:
394                        mode |= UNIV_CTL_VAS24;
395                        break;
396
397                case VME_AM_EXT_SUP_PGM:
398                case VME_AM_EXT_USR_PGM:
399                        if (ismaster)
400                                mode |= UNIV_MCTL_PGM ;
401                        else {
402                                mode &= ~UNIV_SCTL_DAT;
403                        }
404                        /* fall thru */
405                case VME_AM_EXT_SUP_DATA:
406                case VME_AM_EXT_USR_DATA:
407                        mode |= UNIV_CTL_VAS32;
408                        break;
409
410                case VME_AM_SUP_SHORT_IO:
411                case VME_AM_USR_SHORT_IO:
412                        mode |= UNIV_CTL_VAS16;
413                        break;
414
415                case VME_AM_CSR:
416                        if ( !ismaster )
417                                return -1;
418                        mode |= UNIV_MCTL_VASCSR;
419                        break;
420
421                case 0: /* disable the port alltogether */
422                        break;
423
424                default:
425                        return -1;
426        }
427        if ( VME_AM_IS_SUP(address_space) )
428                mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER);
429        *pmode = mode;
430        return 0;
431}
432
433static int
434disableUniversePort(int ismaster, int portno, volatile unsigned long *preg, void *param)
435{
436unsigned long cntrl;
437        cntrl=READ_LE0(preg);
438        cntrl &= ~(ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN);
439        WRITE_LE(cntrl,preg,0);
440        SYNC; /* make sure this command completed */
441        return 0;
442}
443
444static int
445cfgUniversePort(
446        volatile LERegister *base,
447        unsigned long   ismaster,
448        unsigned long   port,
449        unsigned long   address_space,
450        unsigned long   vme_address,
451        unsigned long   local_address,
452        unsigned long   length)
453{
454volatile LERegister *preg;
455unsigned long   p=port;
456unsigned long   mode=0;
457
458        CHECK_DFLT_BASE(base);
459
460        /* check parameters */
461        if (port >= (ismaster ? UNIV_NUM_MPORTS : UNIV_NUM_SPORTS)) {
462                uprintf(stderr,"invalid port\n");
463                return -1;
464        }
465        /* port start, bound addresses and offset must lie on 64k boundary
466         * (4k for port 0 and 4)
467         */
468        if ( PORT_UNALIGNED(local_address,port) ) {
469                uprintf(stderr,"local address misaligned\n");
470                return -1;
471        }
472        if ( PORT_UNALIGNED(vme_address,port) ) {
473                uprintf(stderr,"vme address misaligned\n");
474                return -1;
475        }
476        if ( PORT_UNALIGNED(length,port) ) {
477                uprintf(stderr,"length misaligned\n");
478                return -1;
479        }
480
481        /* check address space validity */
482        if (am2mode(ismaster,address_space,&mode)) {
483                uprintf(stderr,"invalid address space\n");
484                return -1;
485        }
486
487        /* get the universe base address */
488        if (!base && vmeUniverseInit()) {
489                return -1;
490        }
491
492        preg=base;
493
494        /* find out if we have a rev. II chip */
495        if ( UNIV_REV(base) < 2 ) {
496                if (port>3) {
497                        uprintf(stderr,"Universe rev. < 2 has only 4 ports\n");
498                        return -1;
499                }
500        }
501
502        /* finally, configure the port */
503
504        /* find the register set for our port */
505        if (port<4) {
506                preg += (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister);
507        } else {
508                preg += (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister);
509                p-=4;
510        }
511        preg += 5 * p;
512
513        /* temporarily disable the port */
514        disableUniversePort(ismaster,port,preg,0);
515
516        /* address_space == 0 means disable */
517        if (address_space != 0) {
518                unsigned long start,offst;
519                /* set the port starting address;
520                 * this is the local address for the master
521                 * and the VME address for the slave
522                 */
523                if (ismaster) {
524                        start=local_address;
525                        /* let it overflow / wrap around 0 */
526                        offst=vme_address-local_address;
527                } else {
528                        start=vme_address;
529                        /* let it overflow / wrap around 0 */
530                        offst=local_address-vme_address;
531                }
532#undef TSILL
533#ifdef TSILL
534                uprintf(stderr,"writing 0x%08x to 0x%08x + 4\n",start,preg);
535#else
536                WRITE_LE(start,preg,4);
537#endif
538                /* set bound address */
539                length+=start;
540#ifdef TSILL
541                uprintf(stderr,"writing 0x%08x to 0x%08x + 8\n",length,preg);
542#else
543                WRITE_LE(length,preg,8);
544#endif
545                /* set offset */
546#ifdef TSILL
547                uprintf(stderr,"writing 0x%08x to 0x%08x + 12\n",offst,preg);
548#else
549                WRITE_LE(offst,preg,12);
550#endif
551
552#ifdef TSILL
553                uprintf(stderr,"writing 0x%08x to 0x%08x + 0\n",mode,preg);
554#else
555                EIEIO_REG;      /* make sure mode is written last */
556                WRITE_LE(mode,preg,0);
557                SYNC;           /* enforce completion */
558#endif
559
560#ifdef TSILL
561                uprintf(stderr,
562                        "universe %s port %lu successfully configured\n",
563                                ismaster ? "master" : "slave",
564                                port);
565#endif
566
567#ifdef __vxworks
568                if (ismaster)
569                        uprintf(stderr,
570                        "WARNING: on the synergy, sysMasterPortsShow() may show incorrect settings (it uses cached values)\n");
571#endif
572        }
573        return 0;
574}
575
576static int
577showUniversePort(
578                int             ismaster,
579                int             portno,
580                volatile LERegister *preg,
581                void            *parm)
582{
583        FILE *f=parm ? (FILE *)parm : stdout;
584        unsigned long cntrl, start, bound, offst, mask;
585
586        cntrl = READ_LE0(preg++);
587#undef TSILL
588#ifdef TSILL
589        uprintf(stderr,"showUniversePort: *(0x%08x): 0x%08x\n",preg-1,cntrl);
590#endif
591#undef TSILL
592
593        /* skip this port if disabled */
594        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
595                return 0;
596
597        /* for the master `start' is the PCI address,
598         * for the slave  `start' is the VME address
599         */
600        mask = ~PORT_UNALIGNED(0xffffffff,portno);
601
602        start = READ_LE0(preg++)&mask;
603        bound = READ_LE0(preg++)&mask;
604        offst = READ_LE0(preg++)&mask;
605
606        offst+=start; /* calc start on the other bus */
607
608        if (ismaster) {
609                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
610                        portno,offst,bound-start,start);
611        } else {
612                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
613                        portno,start,bound-start,offst);
614        }
615
616        switch (cntrl & UNIV_CTL_VAS) {
617                case UNIV_CTL_VAS16:   uprintf(f,"A16, "); break;
618                case UNIV_CTL_VAS24:   uprintf(f,"A24, "); break;
619                case UNIV_CTL_VAS32:   uprintf(f,"A32, "); break;
620                case UNIV_MCTL_VASCSR: if ( ismaster ) { uprintf(f,"CSR, "); break; }
621                                       /* else fallthru */
622                default: uprintf(f,"A??, "); break;
623        }
624
625        if (ismaster) {
626                uprintf(f,"%s, %s",
627                        cntrl&UNIV_MCTL_PGM ?   "Pgm" : "Dat",
628                        cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr");
629                if ( cntrl & UNIV_MCTL_PWEN )
630                        uprintf(f,", PWEN");
631        } else {
632                uprintf(f,"%s %s %s %s",
633                        cntrl&UNIV_SCTL_PGM ?   "Pgm," : "    ",
634                        cntrl&UNIV_SCTL_DAT ?   "Dat," : "    ",
635                        cntrl&UNIV_SCTL_SUPER ? "Sup," : "    ",
636                        cntrl&UNIV_SCTL_USER  ? "Usr" :  "");
637                if ( cntrl & UNIV_SCTL_PWEN )
638                        uprintf(f,", PWEN");
639                if ( cntrl & UNIV_SCTL_PREN )
640                        uprintf(f,", PREN");
641        }
642        uprintf(f,"\n");
643        return 0;
644}
645
646typedef struct XlatRec_ {
647        unsigned long   address;
648        unsigned long   aspace;
649        unsigned                reverse; /* find reverse mapping of this port */
650} XlatRec, *Xlat;
651
652/* try to translate an address through the bridge
653 *
654 * IN:  l->address, l->aspace
655 * OUT: l->address (translated address)
656 *
657 * RETURNS: -1: invalid space
658 *           0: invalid address (not found in range)
659 *      port+1: success
660 */
661
662static int
663xlatePort(int ismaster, int port, volatile LERegister *preg, void *parm)
664{
665Xlat    l=(Xlat)parm;
666unsigned long cntrl, start, bound, offst, mask, x;
667
668        cntrl = READ_LE0(preg++);
669
670        /* skip this port if disabled */
671        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
672                return 0;
673
674        /* check for correct address space */
675        if ( am2mode(ismaster,l->aspace,&offst) ) {
676                uprintf(stderr,"vmeUniverse WARNING: invalid adressing mode 0x%x\n",
677                               l->aspace);
678                return -1;
679        }
680
681
682        switch (VME_MODE_MATCH_MASK & l->aspace) {
683                case VME_MODE_EXACT_MATCH:
684                        mask = -1 & ~VME_MODE_MATCH_MASK;
685                        break;
686
687                case VME_MODE_AS_MATCH:
688                        mask = UNIV_CTL_VAS;
689                        break;
690
691                default:
692                        mask = (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK);
693                        break;
694        }
695
696        cntrl &= mask;
697        offst &= mask;
698
699        if ( cntrl != offst )
700                return 0; /* mode doesn't match requested AM */
701
702        /* OK, we found a matching mode, now we must check the address range */
703        mask = ~PORT_UNALIGNED(0xffffffff,port);
704
705        /* for the master `start' is the PCI address,
706         * for the slave  `start' is the VME address
707         */
708        start = READ_LE0(preg++) & mask;
709        bound = READ_LE0(preg++) & mask;
710        offst = READ_LE0(preg++) & mask;
711
712        /* translate address to the other bus */
713        if (l->reverse) {
714                /* reverse mapping, i.e. for master ports we map from
715                 * VME to PCI, for slave ports we map from VME to PCI
716                 */
717                if (l->address >= start && l->address < bound) {
718                                l->address+=offst;
719                                return 1 + port;
720                }
721        } else {
722                x = l->address - offst;
723
724                if (x >= start && x < bound) {
725                        /* valid address found */
726                        l->address = x;
727                        return 1 + port;
728                }
729        }
730        return 0;
731}
732
733/* check if there is any active window with write posting enabled */
734static int
735hasPWENWindow(
736                int             ismaster,
737                int             portno,
738                volatile LERegister *preg,
739                void            *parm)
740{
741unsigned long cntrl = READ_LE0(preg);
742unsigned long mask  = ismaster ? (UNIV_MCTL_EN|UNIV_MCTL_PWEN) : (UNIV_SCTL_EN|UNIV_SCTL_PWEN);
743        return (cntrl & mask) == mask ? -1 : 0;
744}
745
746static int
747mapOverAll(volatile LERegister *base, int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
748{
749volatile LERegister     *rptr;
750unsigned long   port;
751int     rval;
752
753        CHECK_DFLT_BASE(base);
754
755        rptr = (base +
756                (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister));
757#undef TSILL
758#ifdef TSILL
759        uprintf(stderr,"mapoverall: base is 0x%08x, rptr 0x%08x\n",base,rptr);
760#endif
761#undef TSILL
762        for (port=0; port<4; port++) {
763                if ((rval=func(ismaster,port,rptr,arg))) return rval;
764                rptr+=5; /* register block spacing */
765        }
766
767        /* only rev. 2 has 8 ports */
768        if (UNIV_REV(base)<2) return -1;
769
770        rptr = (base +
771                (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister));
772        for (port=4; port<UNIV_NUM_MPORTS; port++) {
773                if ((rval=func(ismaster,port,rptr,arg))) return rval;
774                rptr+=5; /* register block spacing */
775        }
776        return 0;
777}
778
779static void
780showUniversePorts(volatile LERegister *base, int ismaster, FILE *f)
781{
782        if (!f) f=stdout;
783        uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave");
784        uprintf(f,"Port  VME-Addr   Size       PCI-Adrs   Mode:\n");
785        mapOverAll(base,ismaster,showUniversePort,f);
786}
787
788static int
789xlateFindPort(
790        volatile LERegister *base,      /* Universe base address */
791        int master,             /* look in the master windows */
792        int reverse,            /* reverse mapping; for masters: map local to VME */
793        unsigned long as,       /* address space */
794        unsigned long aIn,      /* address to look up */
795        unsigned long *paOut/* where to put result */
796        )
797{
798int     rval;
799XlatRec l;
800        l.aspace  = as;
801        l.address = aIn;
802        l.reverse = reverse;
803        /* map result -1/0/1 to -2/-1/0 with 0 on success */
804        rval = mapOverAll(base,master,xlatePort,(void*)&l) - 1;
805        *paOut = l.address;
806        return rval;
807}
808
809int
810vmeUniverseXlateAddrXX(
811        volatile LERegister *base,      /* Universe base address */
812        int master,             /* look in the master windows */
813        int reverse,            /* reverse mapping; for masters: map local to VME */
814        unsigned long as,       /* address space */
815        unsigned long aIn,      /* address to look up */
816        unsigned long *paOut/* where to put result */
817        )
818{
819        return xlateFindPort(base, master, reverse, as, aIn, paOut) >= 0 ? 0 : -1;
820}
821
822int
823vmeUniverseXlateAddr(
824        int master,             /* look in the master windows */
825        int reverse,            /* reverse mapping; for masters: map local to VME */
826        unsigned long as,       /* address space */
827        unsigned long aIn,      /* address to look up */
828        unsigned long *paOut/* where to put result */
829        )
830{
831        DFLT_BASE;
832        return vmeUniverseXlateAddrXX(base, master, reverse, as, aIn, paOut);
833}
834
835
836void
837vmeUniverseReset(void)
838{
839        /* disable/reset special cycles (ADOH, RMW) */
840        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_CTL);
841        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_ADDR);
842        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_EN);
843
844        /* set coupled window timeout to 0 (release VME after each transaction)
845         * CRT (coupled request timeout) is unused by Universe II
846         */
847        vmeUniverseWriteReg(UNIV_LMISC_CRT_128_US, UNIV_REGOFF_LMISC);
848
849        /* disable/reset DMA engine */
850        vmeUniverseWriteReg(0, UNIV_REGOFF_DCTL);
851        vmeUniverseWriteReg(0, UNIV_REGOFF_DTBC);
852        vmeUniverseWriteReg(0, UNIV_REGOFF_DLA);
853        vmeUniverseWriteReg(0, UNIV_REGOFF_DVA);
854        vmeUniverseWriteReg(0, UNIV_REGOFF_DCPP);
855
856        /* disable location monitor */
857        vmeUniverseWriteReg(0, UNIV_REGOFF_LM_CTL);
858
859        /* disable universe register access from VME bus */
860        vmeUniverseWriteReg(0, UNIV_REGOFF_VRAI_CTL);
861
862#if 0   /* leave CSR bus image alone; IRQ manager can use it */
863        /* disable VME bus image of VME CSR */
864        vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);
865#endif
866
867
868        /* I had problems with a Joerger vtr10012_8 card who would
869         * only be accessible after tweaking the U2SPEC register
870         * (the t27 parameter helped).
871         * I use the same settings here that are used by the
872         * Synergy VGM-powerpc BSP for vxWorks.
873         */
874        if (2==UNIV_REV(vmeUniverse0BaseAddr))
875                vmeUniverseWriteReg(UNIV_U2SPEC_DTKFLTR |
876                                                UNIV_U2SPEC_MASt11   |
877                                                UNIV_U2SPEC_READt27_NODELAY |
878                                                UNIV_U2SPEC_POSt28_FAST |
879                                                UNIV_U2SPEC_PREt28_FAST,
880                                                UNIV_REGOFF_U2SPEC);
881
882        /* disable interrupts, reset routing */
883        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_EN);
884        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP0);
885        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP1);
886
887        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_EN);
888        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP0);
889        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP1);
890
891        vmeUniverseDisableAllSlaves();
892
893        vmeUniverseDisableAllMasters();
894       
895        vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR);
896
897        /* clear interrupt status bits */
898        vmeUniverseWriteReg(UNIV_LINT_STAT_CLR, UNIV_REGOFF_LINT_STAT);
899        vmeUniverseWriteReg(UNIV_VINT_STAT_CLR, UNIV_REGOFF_VINT_STAT);
900
901        vmeUniverseWriteReg(UNIV_V_AMERR_V_STAT, UNIV_REGOFF_V_AMERR);
902
903        vmeUniverseWriteReg(
904                vmeUniverseReadReg(UNIV_REGOFF_PCI_CSR) |
905                UNIV_PCI_CSR_D_PE | UNIV_PCI_CSR_S_SERR | UNIV_PCI_CSR_R_MA |
906                UNIV_PCI_CSR_R_TA | UNIV_PCI_CSR_S_TA,
907                UNIV_REGOFF_PCI_CSR);
908
909        vmeUniverseWriteReg(UNIV_L_CMDERR_L_STAT, UNIV_REGOFF_L_CMDERR);
910
911        vmeUniverseWriteReg(
912                UNIV_DGCS_STOP | UNIV_DGCS_HALT | UNIV_DGCS_DONE |
913                UNIV_DGCS_LERR | UNIV_DGCS_VERR | UNIV_DGCS_P_ERR,
914                UNIV_REGOFF_DGCS);
915}
916
917int
918vmeUniverseInit(void)
919{
920int rval;
921        if ( (rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr)) < 0 ) {
922                uprintf(stderr,"unable to find the universe in pci config space\n");
923        } else {
924                vmeUniverse0PciIrqLine = rval;
925                rval                   = 0;
926                uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %d\n",
927                                (unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine);
928        }
929        return rval;
930}
931
932void
933vmeUniverseMasterPortsShowXX(volatile LERegister *base, FILE *f)
934{
935        showUniversePorts(base,1,f);
936}
937
938void
939vmeUniverseMasterPortsShow(FILE *f)
940{
941        DFLT_BASE;
942        showUniversePorts(base,1,f);
943}
944
945void
946vmeUniverseSlavePortsShowXX(volatile LERegister *base, FILE *f)
947{
948        showUniversePorts(base,0,f);
949}
950
951void
952vmeUniverseSlavePortsShow(FILE *f)
953{
954        DFLT_BASE;
955        showUniversePorts(base,0,f);
956}
957
958int
959vmeUniverseMasterPortCfgXX(
960        volatile LERegister *base,
961        unsigned long   port,
962        unsigned long   address_space,
963        unsigned long   vme_address,
964        unsigned long   local_address,
965        unsigned long   length)
966{
967        return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
968}
969
970int
971vmeUniverseMasterPortCfg(
972        unsigned long   port,
973        unsigned long   address_space,
974        unsigned long   vme_address,
975        unsigned long   local_address,
976        unsigned long   length)
977{
978        DFLT_BASE;
979        return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
980}
981
982int
983vmeUniverseSlavePortCfgXX(
984        volatile LERegister *base,
985        unsigned long   port,
986        unsigned long   address_space,
987        unsigned long   vme_address,
988        unsigned long   local_address,
989        unsigned long   length)
990{
991        return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
992}
993
994int
995vmeUniverseSlavePortCfg(
996        unsigned long   port,
997        unsigned long   address_space,
998        unsigned long   vme_address,
999        unsigned long   local_address,
1000        unsigned long   length)
1001{
1002        DFLT_BASE;
1003        return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
1004}
1005
1006
1007void
1008vmeUniverseDisableAllSlavesXX(volatile LERegister *base)
1009{
1010        mapOverAll(base,0,disableUniversePort,0);
1011}
1012
1013void
1014vmeUniverseDisableAllSlaves(void)
1015{
1016        DFLT_BASE;
1017        mapOverAll(base,0,disableUniversePort,0);
1018}
1019
1020void
1021vmeUniverseDisableAllMastersXX(volatile LERegister *base)
1022{
1023        mapOverAll(base,1,disableUniversePort,0);
1024}
1025
1026void
1027vmeUniverseDisableAllMasters(void)
1028{
1029        DFLT_BASE;
1030        mapOverAll(base,1,disableUniversePort,0);
1031}
1032
1033int
1034vmeUniverseStartDMAXX(
1035        volatile LERegister *base,
1036        unsigned long local_addr,
1037        unsigned long vme_addr,
1038        unsigned long count)
1039{
1040        if ((local_addr & 7) != (vme_addr & 7)) {
1041                uprintf(stderr,"vmeUniverseStartDMA: misaligned addresses\n");
1042                return -1;
1043        }
1044
1045        {
1046        /* help the compiler allocate registers */
1047        register volatile LERegister *b=base;;
1048        register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs;
1049
1050        dgcs=READ_LE(b, dgcsoff);
1051
1052        if ( UNIV_DGCS_ACT & dgcs )
1053#if 0
1054                return BSP_VMEDMA_STATUS_BUSY;
1055#else
1056                return -1;
1057#warning "BSP_VMEDMA_STATUS_BUSY commented"
1058#endif
1059
1060        /* clear status and make sure CHAIN is clear */
1061        dgcs &= ~UNIV_DGCS_CHAIN;
1062        WRITE_LE(dgcs,
1063                      b, dgcsoff);
1064        WRITE_LE(local_addr,
1065                      b, UNIV_REGOFF_DLA);
1066        WRITE_LE(vme_addr,
1067                      b, UNIV_REGOFF_DVA);
1068        WRITE_LE(count,
1069                      b, UNIV_REGOFF_DTBC);
1070        dgcs |= UNIV_DGCS_GO;
1071        EIEIO_REG; /* make sure GO is written after everything else */
1072        WRITE_LE(dgcs,
1073                      b, dgcsoff);
1074        }
1075        SYNC; /* enforce command completion */
1076        return 0;
1077}
1078
1079int
1080vmeUniverseStartDMA(
1081        unsigned long local_addr,
1082        unsigned long vme_addr,
1083        unsigned long count)
1084{
1085        DFLT_BASE; /* vmeUniverseStartDMAXX doesn't check for a valid base address for efficiency reasons */
1086        return vmeUniverseStartDMAXX(base, local_addr, vme_addr, count);
1087}
1088
1089unsigned long
1090vmeUniverseReadRegXX(volatile LERegister *base, unsigned long offset)
1091{
1092unsigned long rval;
1093        rval = READ_LE(base,offset);
1094        return rval;
1095}
1096
1097
1098unsigned long
1099vmeUniverseReadReg(unsigned long offset)
1100{
1101unsigned long rval;
1102        rval = READ_LE(vmeUniverse0BaseAddr,offset);
1103        return rval;
1104}
1105
1106void
1107vmeUniverseWriteRegXX(volatile LERegister *base, unsigned long value, unsigned long offset)
1108{
1109        WRITE_LE(value, base, offset);
1110}
1111
1112void
1113vmeUniverseWriteReg(unsigned long value, unsigned long offset)
1114{
1115        WRITE_LE(value, vmeUniverse0BaseAddr, offset);
1116}
1117
1118void
1119vmeUniverseResetBus(void)
1120{
1121        vmeUniverseWriteReg(
1122                vmeUniverseReadReg(UNIV_REGOFF_MISC_CTL) | UNIV_MISC_CTL_SW_SYSRST,
1123                UNIV_REGOFF_MISC_CTL);
1124}
1125
1126void
1127vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num)
1128{
1129#if !defined(__LITTLE_ENDIAN__) || (__LITTLE_ENDIAN__ != 1)
1130register unsigned long *p=ptr+num;
1131        while (p > ptr) {
1132#if (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
1133                __asm__ __volatile__(
1134                        "lwzu 0, -4(%0)\n"
1135                        "stwbrx 0, 0, %0\n"
1136                        : "=r"(p) : "0"(p) : "r0"
1137                        );
1138#elif defined(__rtems__)
1139                p--; st_le32(p, *p);
1140#else
1141#error  "vmeUniverse: endian conversion not implemented for this architecture"
1142#endif
1143        }
1144#endif
1145}
1146
1147int
1148vmeUniverseIntRaiseXX(volatile LERegister *base, int level, unsigned vector)
1149{
1150unsigned long v;
1151unsigned long b;
1152
1153        CHECK_DFLT_BASE(base);
1154
1155        if ( level < 1 || level > 7 || vector > 255 )
1156                return -1;      /* invalid argument */
1157
1158        if ( vector & 1 ) /* SW interrupts always ACK an even vector (pp 2-67) */
1159                return -1;
1160
1161
1162        /* Check if already asserted */
1163        if ( vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_STAT ) & UNIV_VINT_STAT_SWINT(level) ) {
1164                return -2;  /* already asserted */
1165        }
1166
1167        /* Write Vector */
1168        vmeUniverseWriteRegXX(base, UNIV_VINT_STATID(vector), UNIV_REGOFF_VINT_STATID );
1169
1170        if ( UNIV_REV(base) >= 2 ) {
1171                /* universe II has individual bits for individual levels */
1172                b = UNIV_VINT_STAT_SWINT(level);
1173        } else {
1174                /* version that is compatible with universe I */
1175                v  = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_MAP1);
1176                v &= ~UNIV_VINT_MAP1_SWINT(0x7);
1177                v |=  UNIV_VINT_MAP1_SWINT(level);
1178                vmeUniverseWriteRegXX(base, v, UNIV_REGOFF_VINT_MAP1);
1179                b  = UNIV_VINT_EN_SWINT;
1180        }
1181        v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_EN);
1182        /* make sure it is clear, then assert */
1183        vmeUniverseWriteRegXX(base, v & ~b, UNIV_REGOFF_VINT_EN );
1184        vmeUniverseWriteRegXX(base, v |  b, UNIV_REGOFF_VINT_EN );
1185
1186        return 0;
1187       
1188}
1189
1190int
1191vmeUniverseIntRaise(int level, unsigned vector)
1192{
1193        return vmeUniverseIntRaiseXX(vmeUniverse0BaseAddr, level, vector);
1194}
1195
1196
1197/* Map internal register block to VME */
1198#define UNIV_CRG_SIZE (1<<12)
1199
1200int
1201vmeUniverseMapCRGXX(volatile LERegister *base, unsigned long vme_base, unsigned long as )
1202{
1203uint32_t mode;
1204
1205        CHECK_DFLT_BASE(base);
1206
1207#ifdef __rtems__
1208        if ( vmeUniverseRegPort > -1 && ! vmeUniverseRegCSR ) {
1209        uprintf(stderr,"vmeUniverse: CRG already mapped and in use by interrupt manager\n");
1210                return -1;
1211        }
1212#endif
1213
1214        /* enable all, SUP/USR/PGM/DATA accesses */
1215        mode = UNIV_VRAI_CTL_EN | UNIV_VRAI_CTL_PGM | UNIV_VRAI_CTL_DATA | UNIV_VRAI_CTL_SUPER | UNIV_VRAI_CTL_USER;
1216
1217        if ( VME_AM_IS_SHORT(as) ) {
1218                mode |= UNIV_VRAI_CTL_VAS_A16;
1219        } else
1220        if ( VME_AM_IS_STD(as) ) {
1221                mode |= UNIV_VRAI_CTL_VAS_A24;
1222        } else
1223        if ( VME_AM_IS_EXT(as) ) {
1224                mode |= UNIV_VRAI_CTL_VAS_A32;
1225        } else {
1226                return -2;
1227        }
1228
1229        /* map CRG to VME bus */
1230        WRITE_LE( (vme_base & ~(UNIV_CRG_SIZE-1)), base, UNIV_REGOFF_VRAI_BS );
1231        WRITE_LE( mode, base, UNIV_REGOFF_VRAI_CTL );
1232
1233        return 0;
1234}
1235
1236int
1237vmeUniverseMapCRG(unsigned long vme_base, unsigned long as )
1238{
1239        return vmeUniverseMapCRGXX( vmeUniverse0BaseAddr, vme_base, as );
1240}
1241
1242
1243/* RTEMS interrupt subsystem */
1244
1245#ifdef __rtems__
1246
1247#include <bsp/irq.h>
1248
1249typedef struct
1250UniverseIRQEntryRec_ {
1251                VmeUniverseISR  isr;
1252                void                    *usrData;
1253} UniverseIRQEntryRec, *UniverseIRQEntry;
1254
1255static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};
1256
1257int          vmeUniverseIrqMgrInstalled = 0;
1258
1259volatile LERegister *vmeUniverseRegBase = 0;
1260
1261/* We support 4 wires between universe + PIC */
1262
1263#define UNIV_NUM_WIRES 4
1264
1265static volatile unsigned long   wire_mask[UNIV_NUM_WIRES]     = {0};
1266/* wires are offset by 1 so we can initialize the wire table to all zeros */
1267static int                                              universe_wire[UNIV_NUM_WIRES] = {0};
1268
1269static int
1270lvl2bit(unsigned int level)
1271{
1272int shift = -1;
1273        if ( level >= UNIV_DMA_INT_VEC && level <= UNIV_LM3_INT_VEC ) {
1274                shift = 8 + (level-UNIV_DMA_INT_VEC);
1275        } else if ( UNIV_VOWN_INT_VEC == level ) {
1276                shift = 0;
1277        } else if ( 1 <= level && level <=7 ) {
1278                shift = level;
1279        } else {
1280                /* invalid level */
1281        }
1282        return shift;
1283}
1284
1285int
1286vmeUniverseIntRoute(unsigned int level, unsigned int pin)
1287{
1288int                             i, shift;
1289unsigned long   mask, mapreg, flags, wire;
1290
1291        if ( pin >= UNIV_NUM_WIRES || ! universe_wire[pin] || !vmeUniverseIrqMgrInstalled )
1292                return -1;
1293
1294        if ( (shift = lvl2bit(level)) < 0 ) {
1295                return -1; /* invalid level */
1296        }
1297
1298        mask = 1<<shift;
1299
1300        /* calculate the mapping register and contents */
1301        if ( shift < 8 ) {
1302                mapreg = UNIV_REGOFF_LINT_MAP0;
1303        } else if ( shift < 16 ) {
1304                shift -= 8;
1305                mapreg = UNIV_REGOFF_LINT_MAP1;
1306        } else if ( shift < 24 ) {
1307                shift -= 16;
1308                mapreg = UNIV_REGOFF_LINT_MAP2;
1309        } else {
1310                return -1;
1311        }
1312
1313        shift <<=2;
1314
1315        /* wires are offset by 1 so we can initialize the wire table to all zeros */
1316        wire = (universe_wire[pin]-1) << shift;
1317
1318rtems_interrupt_disable(flags);
1319
1320        for ( i = 0; i<UNIV_NUM_WIRES; i++ ) {
1321                wire_mask[i] &= ~mask;
1322        }
1323        wire_mask[pin] |= mask;
1324
1325        mask = vmeUniverseReadReg(mapreg) & ~ (0xf<<shift);
1326        mask |= wire;
1327        vmeUniverseWriteReg( mask, mapreg );
1328
1329rtems_interrupt_enable(flags);
1330        return 0;
1331}
1332
1333VmeUniverseISR
1334vmeUniverseISRGet(unsigned long vector, void **parg)
1335{
1336unsigned long             flags;
1337VmeUniverseISR                    rval = 0;
1338volatile UniverseIRQEntry *pe  = universeHdlTbl + vector;
1339
1340        if ( vector>=UNIV_NUM_INT_VECS || ! *pe )
1341                return 0;
1342
1343        rtems_interrupt_disable(flags);
1344                if ( *pe ) {
1345                        if (parg)
1346                                *parg=(*pe)->usrData;
1347                        rval = (*pe)->isr;
1348                }
1349        rtems_interrupt_enable(flags);
1350        return rval;
1351}
1352
1353#define SPECIAL_IRQ_MSK  ( ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1) )
1354
1355static void
1356universeSpecialISR(unsigned long status)
1357{
1358register UniverseIRQEntry       ip;
1359register unsigned                       vec;
1360register unsigned long          s;
1361
1362        /* handle all LINT bits except for the 'normal' VME interrupts */
1363
1364        /* clear all detected special interrupts */
1365        vmeUniverseWriteReg( (status & SPECIAL_IRQ_MSK), UNIV_REGOFF_LINT_STAT );
1366
1367        /* do VOWN first */
1368        vec=UNIV_VOWN_INT_VEC;
1369        if ( (status & UNIV_LINT_STAT_VOWN) && (ip=universeHdlTbl[vec]))
1370                ip->isr(ip->usrData,vec);
1371
1372        /* now continue with DMA and scan through all bits;
1373         * we assume the vectors are in the right order!
1374         *
1375         * The initial right shift brings the DMA bit into position 0;
1376         * the loop is left early if there are no more bits set.
1377         */
1378        for ( s = status>>8; s; s >>= 1) {
1379                vec++;
1380                if ( (s&1) && (ip=universeHdlTbl[vec]) )
1381                        ip->isr(ip->usrData,vec);
1382        }
1383
1384/*
1385 *      clear our line in the VINT_STAT register
1386 *  seems to be not neccessary...
1387        vmeUniverseWriteReg(
1388                                        UNIV_VINT_STAT_LINT(specialIrqUnivOut),
1389                                        UNIV_REGOFF_VINT_STAT);
1390 */
1391}
1392
1393/*
1394 * interrupts from VME to PCI seem to be processed more or less
1395 * like this:
1396 *
1397 *
1398 *   VME IRQ ------
1399 *                  & ----- LINT_STAT ----
1400 *                  |                       &  ---------- PCI LINE
1401 *                  |                       |
1402 *                  |                       |
1403 *       LINT_EN ---------------------------
1404 *
1405 *  I.e.
1406 *   - if LINT_EN is disabled, a VME IRQ will not set LINT_STAT.
1407 *   - while LINT_STAT is set, it will pull the PCI line unless
1408 *     masked by LINT_EN.
1409 *   - VINT_STAT(lint_bit) seems to have no effect beyond giving
1410 *     status info.
1411 *
1412 *  Hence, it is possible to
1413 *    - arm (set LINT_EN, routing etc.)
1414 *    - receive an irq (sets. LINT_STAT)
1415 *    - the ISR then:
1416 *                * clears LINT_EN, results in masking LINT_STAT (which
1417 *                  is still set to prevent another VME irq at the same
1418 *                  level to be ACKEd by the universe.
1419 *                * do PCI_EOI to allow nesting of higher VME irqs.
1420 *                  (previous step also cleared LINT_EN of lower levels)
1421 *                * when the handler returns, clear LINT_STAT
1422 *                * re-enable setting LINT_EN.
1423 */
1424
1425static void
1426universeVMEISR(rtems_irq_hdl_param arg)
1427{
1428int                                     pin = (int)arg;
1429UniverseIRQEntry        ip;
1430unsigned long           msk,lintstat,status;
1431int                                     lvl;
1432#ifdef BSP_PIC_DO_EOI
1433unsigned long           linten;
1434#endif
1435
1436                /* determine the highest priority IRQ source */
1437                lintstat  = vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
1438
1439                /* only handle interrupts routed to this pin */
1440                lintstat &= wire_mask[pin];
1441
1442#ifdef __PPC__
1443                asm volatile("cntlzw %0, %1":"=r"(lvl):"r"(lintstat & ~SPECIAL_IRQ_MSK));
1444                lvl = 31-lvl;
1445                msk = 1<<lvl;
1446#else
1447                for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7;
1448                         lvl>0;
1449                         lvl--, msk>>=1) {
1450                        if (lintstat & msk) break;
1451                }
1452#endif
1453
1454#ifndef BSP_PIC_DO_EOI /* Software priorities not supported */
1455
1456                if ( (status = (lintstat & SPECIAL_IRQ_MSK)) )
1457                        universeSpecialISR( status );
1458
1459                if ( lvl <= 0)
1460                        return;
1461
1462#else
1463                if ( lvl <= 0 ) {
1464                                /* try the special handler */
1465                                universeSpecialISR( lintstat & SPECIAL_IRQ_MSK );
1466
1467                                /*
1468                                 * let the pic end this cycle
1469                                 */
1470                                if ( 0 == pin )
1471                                        BSP_PIC_DO_EOI;
1472
1473                                return;
1474                }
1475                linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
1476
1477                /* mask this and all lower levels that are routed to the same pin */
1478                vmeUniverseWriteReg(
1479                                                linten & ~( ((msk<<1)-UNIV_LINT_STAT_VIRQ1) & wire_mask[pin]),
1480                                                UNIV_REGOFF_LINT_EN
1481                                                );
1482
1483                /* end this interrupt
1484                 * cycle on the PCI bus, so higher level interrupts can be
1485                 * caught from now on...
1486                 */
1487                if ( 0 == pin )
1488                        BSP_PIC_DO_EOI;
1489#endif
1490
1491                /* get vector and dispatch handler */
1492                status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2));
1493                /* determine the highest priority IRQ source */
1494
1495                if (status & UNIV_VIRQ_ERR) {
1496                                /* TODO: log error message - RTEMS has no logger :-( */
1497#ifdef BSP_PIC_DO_EOI
1498                        linten &= ~msk;
1499#else
1500                        vmeUniverseIntDisable(lvl);
1501#endif
1502                        printk("vmeUniverse ISR: error read from STATID register; (level: %i) STATID: 0x%08x -- DISABLING\n", lvl, status);
1503                } else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
1504#ifdef BSP_PIC_DO_EOI
1505                        linten &= ~msk;
1506#else
1507                        vmeUniverseIntDisable(lvl);
1508#endif
1509                                /* TODO: log error message - RTEMS has no logger :-( */
1510                        printk("vmeUniverse ISR: no handler installed for this vector; (level: %i) STATID: 0x%08x -- DISABLING\n", lvl, status);
1511                } else {
1512                                /* dispatch handler, it must clear the IRQ at the device */
1513                                ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
1514
1515                                /* insert a VME read operation to flush fifo, making sure all user write-ops complete */
1516#ifdef __PPC__
1517                                /* courtesy to disobedient users who don't use I/O ops */
1518                                asm volatile("eieio");
1519#endif
1520                                READ_LE0(vmeUniverseRegBase);
1521#ifdef __PPC__
1522                                /* make sure this is ordered before re-enabling */
1523                                asm volatile("eieio");
1524#endif
1525                }
1526
1527                /* clear this interrupt level; allow the universe to handler further interrupts */
1528                vmeUniverseWriteReg(msk, UNIV_REGOFF_LINT_STAT);
1529
1530/*
1531 *  this seems not to be necessary; we just leave the
1532 *  bit set to save a couple of instructions...
1533                vmeUniverseWriteReg(
1534                                        UNIV_VINT_STAT_LINT(vmeIrqUnivOut),
1535                                        UNIV_REGOFF_VINT_STAT);
1536*/
1537
1538#ifdef BSP_PIC_DO_EOI
1539
1540                /* re-enable the previous level */
1541                vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN);
1542#endif
1543}
1544
1545
1546/* STUPID API */
1547static void
1548my_no_op(const rtems_irq_connect_data * arg)
1549{}
1550
1551static int
1552my_isOn(const rtems_irq_connect_data *arg)
1553{
1554                return (int)vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
1555}
1556
1557typedef struct {
1558        int uni_pin, pic_pin;
1559} IntRoute;
1560
1561static void
1562connectIsr(int shared, rtems_irq_hdl isr, int pic_line, int pic_pin)
1563{
1564rtems_irq_connect_data  aarrggh;
1565        aarrggh.on     = my_no_op; /* at _least_ they could check for a 0 pointer */
1566        aarrggh.off    = my_no_op;
1567        aarrggh.isOn   = my_isOn;
1568        aarrggh.hdl    = isr;
1569        aarrggh.handle = (rtems_irq_hdl_param)pic_pin;
1570        aarrggh.name   = pic_line;
1571
1572        if ( shared ) {
1573#if BSP_SHARED_HANDLER_SUPPORT > 0
1574                if (!BSP_install_rtems_shared_irq_handler(&aarrggh))
1575                        BSP_panic("unable to install vmeUniverse shared irq handler");
1576#else
1577                uprintf(stderr,"vmeUniverse: WARNING: your BSP doesn't support sharing interrupts\n");
1578                if (!BSP_install_rtems_irq_handler(&aarrggh))
1579                        BSP_panic("unable to install vmeUniverse irq handler");
1580#endif
1581        } else {
1582                if (!BSP_install_rtems_irq_handler(&aarrggh))
1583                        BSP_panic("unable to install vmeUniverse irq handler");
1584        }
1585}
1586
1587#ifndef BSP_EARLY_PROBE_VME
1588#define BSP_EARLY_PROBE_VME(addr)       \
1589        (                                                                                                                                                                                                                       \
1590                ((PCI_DEVICE_UNIVERSEII << 16) | PCI_VENDOR_TUNDRA ) == READ_LE( ((volatile LERegister*)(addr)), 0 )    \
1591        )
1592#endif
1593
1594/* Check if there is a vme address/as is mapped in any of the outbound windows
1595 * and look for the PCI vendordevice ID there.
1596 * RETURNS: -1 on error (no mapping or probe failure), outbound window # (0..7)
1597 *          on success. Address translated into CPU address is returned in *pcpu_addr.
1598 */
1599static int
1600mappedAndProbed(unsigned long vme_addr, unsigned as, unsigned long *pcpu_addr)
1601{
1602int j;
1603char *regtype = (as & VME_AM_MASK) == VME_AM_CSR ? "CSR" : "CRG";
1604
1605        /* try to find mapping */
1606        if ( 0 > (j = xlateFindPort(
1607                                vmeUniverse0BaseAddr,
1608                                1, 0,
1609                                as | VME_MODE_AS_MATCH,
1610                                vme_addr,
1611                                pcpu_addr ) ) ) {
1612                        uprintf(stderr,"vmeUniverse - Unable to find mapping for %s VME base (0x%08x)\n", regtype, vme_addr);
1613                        uprintf(stderr,"              in outbound windows.\n");
1614        } else {
1615                        /* found a slot number; probe it */
1616                        *pcpu_addr = BSP_PCI2LOCAL_ADDR( *pcpu_addr );
1617                        if ( BSP_EARLY_PROBE_VME(*pcpu_addr) ) {
1618                                uprintf(stderr,"vmeUniverse - IRQ manager using VME %s to flush FIFO\n", regtype);
1619                                return j;
1620                        } else {
1621                                uprintf(stderr,"vmeUniverse - Found slot info but detection of universe in VME %s space failed\n", regtype);
1622                        }
1623        }
1624        return -1;
1625}
1626
1627
1628int
1629vmeUniverseInstallIrqMgrAlt(int flags, int uni_pin0, int pic_pin0, ...)
1630{
1631int             rval;
1632va_list ap;
1633        va_start(ap, pic_pin0);
1634        rval = vmeUniverseInstallIrqMgrVa(flags, uni_pin0, pic_pin0, ap);
1635        va_end(ap);
1636        return rval;
1637}
1638
1639int
1640vmeUniverseInstallIrqMgrVa(int flags, int uni_pin0, int pic_pin0, va_list ap)
1641{
1642int     i,j, specialPin, uni_pin[UNIV_NUM_WIRES+1], pic_pin[UNIV_NUM_WIRES];
1643unsigned long cpu_base, vme_reg_base;
1644
1645        if (vmeUniverseIrqMgrInstalled)                return -4;
1646
1647        /* check parameters */
1648
1649        if ( uni_pin0 < 0 || uni_pin0 > 7 )            return -1;
1650
1651        uni_pin[0] = uni_pin0;
1652        pic_pin[0] = pic_pin0 < 0 ? vmeUniverse0PciIrqLine : pic_pin0;
1653        i = 1;
1654        while ( (uni_pin[i] = va_arg(ap, int)) >= 0 ) {
1655               
1656                if ( i >= UNIV_NUM_WIRES ) {
1657                                                               return -5;
1658                }
1659
1660                pic_pin[i] = va_arg(ap,int);
1661
1662                if ( uni_pin[i] > 7 ) {
1663                                                   return -2;
1664                }
1665                if ( pic_pin[i] < 0 ) {
1666                                                   return -3;
1667                }
1668                i++;
1669        }
1670
1671        /* all routings must be different */
1672        for ( i=0; uni_pin[i] >= 0; i++ ) {
1673                for ( j=i+1; uni_pin[j] >= 0; j++ ) {
1674                        if ( uni_pin[j] == uni_pin[i] )        return -6;
1675                        if ( pic_pin[j] == pic_pin[i] )        return -7;
1676                }
1677        }
1678
1679        if ( flags & VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND ) {
1680
1681                /* Find registers on VME so the ISR can issue a read to flush the FIFO */
1682                uprintf(stderr,"vmeUniverse IRQ manager: looking for registers on VME...\n");
1683
1684                /* NOTE: The universe [unlike the Tsi148] doesn't know about geographical
1685                 *       addressing but the MotLoad firmware [mvme5500] is kind enough to
1686                 *       program VCSR_BS based on the board's geographical address for us :-)
1687                 */
1688                if ( ( i = ((READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VCSR_BS ) >> 27) & 0x1f ) ) > 0 ) {
1689                        uprintf(stderr,"Trying to find CSR on VME...\n");
1690                        vme_reg_base = i*0x80000 + UNIV_CSR_OFFSET;
1691                        i = mappedAndProbed( vme_reg_base, VME_AM_CSR , &cpu_base);
1692                        if ( i >= 0 )
1693                                vmeUniverseRegCSR = 1;
1694                } else {
1695                        i = -1;
1696                }
1697
1698                if ( -1 == i ) {
1699
1700                        uprintf(stderr,"Trying to find CRG on VME...\n");
1701
1702                        /* Next we see if the CRG block is mapped to VME */
1703
1704                        if ( UNIV_VRAI_CTL_EN & (j = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_CTL )) ) {
1705                                switch ( j & UNIV_VRAI_CTL_VAS_MSK ) {
1706                                        case UNIV_VRAI_CTL_VAS_A16 : i = VME_AM_SUP_SHORT_IO; break;
1707                                        case UNIV_VRAI_CTL_VAS_A24 : i = VME_AM_STD_SUP_DATA; break;
1708                                        case UNIV_VRAI_CTL_VAS_A32 : i = VME_AM_EXT_SUP_DATA; break;
1709                                        default:
1710                                                                                                 break;
1711                                }
1712                                vme_reg_base = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_BS ) & ~(UNIV_CRG_SIZE - 1);
1713                        }
1714
1715                        if ( -1 == i ) {
1716                        } else {
1717                                i = mappedAndProbed( vme_reg_base, (i & VME_AM_MASK), &cpu_base );
1718                        }
1719                }
1720
1721                if ( i < 0 ) {
1722                        if ( mapOverAll( vmeUniverse0BaseAddr, 1, hasPWENWindow, 0 ) ) {
1723                                uprintf(stderr,"vmeUniverse IRQ manager - BSP configuration error: registers not found on VME\n");
1724                                uprintf(stderr,"(should open outbound window to CSR space or map CRG [vmeUniverseMapCRG()])\n");
1725                                uprintf(stderr,"Falling back to PCI but you might experience spurious VME interrupts; read a register\n");
1726                                uprintf(stderr,"back from user ISR to flush universe FIFO as a work-around or\n");
1727                                uprintf(stderr,"make sure ISR accesses device using a window with posted-writes disabled\n");
1728                        } else {
1729                                uprintf(stderr,"vmeUniverse IRQ manager - registers not found on VME; falling back to PCI\n");
1730                        }
1731                        vmeUniverseRegBase = vmeUniverse0BaseAddr;
1732                        vmeUniverseRegPort = -1;
1733                } else {
1734                        vmeUniverseRegBase = (volatile LERegister*)cpu_base;
1735                        vmeUniverseRegPort = i;
1736                }
1737        } else {
1738                vmeUniverseRegBase = vmeUniverse0BaseAddr;
1739                vmeUniverseRegPort = -1;
1740        }
1741
1742        /* give them a chance to override buggy PCI info */
1743        if ( pic_pin[0] >= 0 && vmeUniverse0PciIrqLine != pic_pin[0] ) {
1744                uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
1745                                pic_pin[0]);
1746                vmeUniverse0PciIrqLine=pic_pin[0];
1747        }
1748
1749        for ( i = 0; uni_pin[i] >= 0; i++ ) {
1750                /* offset wire # by one so we can initialize to 0 == invalid */
1751                universe_wire[i] = uni_pin[i] + 1;
1752                connectIsr((flags & VMEUNIVERSE_IRQ_MGR_FLAG_SHARED), universeVMEISR, pic_pin[i], i);
1753        }
1754
1755        specialPin = uni_pin[1] >= 0 ? 1 : 0;
1756
1757        /* setup routing */
1758
1759        /* IntRoute checks for mgr being installed */
1760        vmeUniverseIrqMgrInstalled=1;
1761
1762        /* route 7 VME irqs to first / 'normal' pin */
1763        for ( i=1; i<8; i++ )
1764                vmeUniverseIntRoute( i, 0 );
1765        for ( i=UNIV_VOWN_INT_VEC; i<=UNIV_LM3_INT_VEC; i++ ) {
1766                if ( vmeUniverseIntRoute( i, specialPin ) )
1767                        printk("Routing lvl %i -> wire # %i failed\n", i, specialPin);
1768        }
1769
1770        return 0;
1771}
1772
1773int
1774vmeUniverseInstallIrqMgr(int vmeIrqUnivOut,
1775                                                 int vmeIrqPicLine,
1776                                                 int specialIrqUnivOut,
1777                                                 int specialIrqPicLine)
1778{
1779        return vmeUniverseInstallIrqMgrAlt(
1780                                0,      /* bwds compat. */
1781                                vmeIrqUnivOut, vmeIrqPicLine,
1782                                specialIrqUnivOut, specialIrqPicLine,
1783                                -1);
1784}
1785
1786int
1787vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
1788{
1789UniverseIRQEntry          ip;
1790unsigned long             flags;
1791volatile UniverseIRQEntry *pe;
1792
1793                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
1794                                return -1;
1795
1796                pe = universeHdlTbl + vector;
1797
1798                if (*pe || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
1799                                return -1;
1800
1801                ip->isr=hdl;
1802                ip->usrData=arg;
1803
1804        rtems_interrupt_disable(flags);
1805                if ( *pe ) {
1806                        /* oops; someone intervened */
1807                        rtems_interrupt_enable(flags);
1808                        free(ip);
1809                        return -1;
1810                }
1811                *pe = ip;
1812        rtems_interrupt_enable(flags);
1813                return 0;
1814}
1815
1816int
1817vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
1818{
1819UniverseIRQEntry          ip;
1820unsigned long             flags;
1821volatile UniverseIRQEntry *pe;
1822
1823                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
1824                                return -1;
1825
1826                pe = universeHdlTbl + vector;
1827
1828        rtems_interrupt_disable(flags);
1829                ip = *pe;
1830                if (!ip || ip->isr!=hdl || ip->usrData!=arg) {
1831                        rtems_interrupt_enable(flags);
1832                        return -1;
1833                }
1834                *pe = 0;
1835        rtems_interrupt_enable(flags);
1836                free(ip);
1837                return 0;
1838}
1839
1840static int
1841intDoEnDis(unsigned int level, int dis)
1842{
1843unsigned long   flags, v;
1844int                             shift;
1845
1846        if (  ! vmeUniverseIrqMgrInstalled || (shift = lvl2bit(level)) < 0 )
1847                return -1;
1848
1849        v = 1<<shift;
1850
1851        if ( !dis )
1852                return vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & v ? 1 : 0;
1853
1854        rtems_interrupt_disable(flags);
1855        if ( dis<0 )
1856                vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & ~v, UNIV_REGOFF_LINT_EN );
1857        else {
1858                vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) |  v, UNIV_REGOFF_LINT_EN  );
1859        }
1860        rtems_interrupt_enable(flags);
1861                return 0;
1862}
1863
1864int
1865vmeUniverseIntEnable(unsigned int level)
1866{
1867        return intDoEnDis(level, 1);
1868}
1869
1870int
1871vmeUniverseIntDisable(unsigned int level)
1872{
1873        return intDoEnDis(level, -1);
1874}
1875
1876int
1877vmeUniverseIntIsEnabled(unsigned int level)
1878{
1879        return intDoEnDis(level, 0);
1880}
1881
1882/* Loopback test of VME/universe interrupts */
1883
1884typedef struct {
1885        rtems_id        q;
1886        int                     l;
1887} LoopbackTstArgs;
1888
1889static void
1890loopbackTstIsr(void *arg, unsigned long vector)
1891{
1892LoopbackTstArgs *pa = arg;
1893        if ( RTEMS_SUCCESSFUL != rtems_message_queue_send(pa->q, (void*)&vector, sizeof(vector)) ) {
1894                /* Overrun ? */
1895                printk("vmeUniverseIntLoopbackTst: (ISR) message queue full / overrun ? disabling IRQ level %i\n", pa->l);
1896                vmeUniverseIntDisable(pa->l);
1897        }
1898}
1899
1900int
1901vmeUniverseIntLoopbackTst(int level, unsigned vector)
1902{
1903DFLT_BASE;
1904rtems_status_code       sc;
1905rtems_id                        q = 0;
1906int                                     installed = 0;
1907int                                     i, err = 0;
1908int                                     doDisable = 0;
1909uint32_t                size;
1910unsigned long           msg;
1911char *                          irqfmt  = "VME IRQ @vector %3i %s";
1912char *                          iackfmt = "VME IACK            %s";
1913LoopbackTstArgs         a;
1914
1915        CHECK_DFLT_BASE(base);
1916
1917        /* arg check */
1918        if ( level < 1 || level > 7 || vector > 255 )
1919                return -1;
1920
1921        if ( UNIV_REV(base) < 2 && vector != 0 ) {
1922                fprintf(stderr,
1923                        "vmeUniverseIntLoopbackTst(): Universe 1 has a bug. IACK in response to\n");
1924                fprintf(stderr,
1925                        "self-generated VME interrupt yields always a zero vector. As a workaround,\n");
1926                fprintf(stderr,
1927                        "use vector 0, please.\n");
1928                return -1;
1929        }
1930
1931        /* Create message queue */
1932        if ( RTEMS_SUCCESSFUL != (sc=rtems_message_queue_create(
1933                                                                        rtems_build_name('t' ,'U','I','I'),
1934                                                                        4,
1935                                                                        sizeof(unsigned long),
1936                                                                        0,  /* default attributes: fifo, local */
1937                                                                        &q)) ) {
1938                rtems_error(sc, "vmeUniverseIntLoopbackTst: Unable to create message queue");
1939                goto bail;
1940        }
1941
1942        a.q = q;
1943        a.l = level;
1944
1945        /* Install handlers */
1946        if ( vmeUniverseInstallISR(vector, loopbackTstIsr, (void*)&a) ) {
1947                fprintf(stderr,"Unable to install VME ISR to vector %i\n",vector);
1948                goto bail;
1949        }
1950        installed++;
1951        if ( vmeUniverseInstallISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a) ) {
1952                fprintf(stderr,"Unable to install VME ISR to IACK special vector %i\n",UNIV_VME_SW_IACK_INT_VEC);
1953                goto bail;
1954        }
1955        installed++;
1956
1957        if ( !vmeUniverseIntIsEnabled(level) && 0==vmeUniverseIntEnable(level) )
1958                doDisable = 1;
1959       
1960        /* make sure there are no pending interrupts */
1961        vmeUniverseWriteReg( UNIV_LINT_STAT_SW_IACK,  UNIV_REGOFF_LINT_STAT );
1962
1963        if ( vmeUniverseIntEnable( UNIV_VME_SW_IACK_INT_VEC ) ) {
1964                fprintf(stderr,"Unable to enable IACK interrupt\n");
1965                goto bail;
1966        }       
1967
1968        printf("vmeUniverse VME interrupt loopback test; STARTING...\n");
1969        printf(" --> asserting VME IRQ level %i\n", level);
1970        vmeUniverseIntRaise(level, vector);
1971
1972        for ( i = 0; i< 3; i++ ) {
1973        sc = rtems_message_queue_receive(
1974                            q,
1975                            &msg,
1976                            &size,
1977                            RTEMS_WAIT,
1978                            20);
1979                if ( sc ) {
1980                        if ( RTEMS_TIMEOUT == sc && i>1 ) {
1981                                /* OK; we dont' expect more to happen */
1982                                sc = 0;
1983                        } else {
1984                                rtems_error(sc,"Error waiting for interrupts");
1985                        }
1986                        break;
1987                }
1988                if ( msg == vector ) {
1989                        if ( !irqfmt ) {
1990                                printf("Excess VME IRQ received ?? -- BAD\n");
1991                                err = 1;
1992                        } else {
1993                                printf(irqfmt, vector, "received -- PASSED\n");
1994                                irqfmt = 0;
1995                        }
1996                } else if ( msg == UNIV_VME_SW_IACK_INT_VEC ) {
1997                        if ( !iackfmt ) {
1998                                printf("Excess VME IACK received ?? -- BAD\n");
1999                                err = 1;
2000                        } else {
2001                                printf(iackfmt, "received -- PASSED\n");
2002                                iackfmt = 0;
2003                        }
2004                } else {
2005                        printf("Unknown IRQ (vector %lu) received -- BAD\n", msg);
2006                        err = 1;
2007                }
2008        }
2009
2010
2011        /* Missing anything ? */
2012        if ( irqfmt ) {
2013                printf(irqfmt,vector, "MISSED -- BAD\n");
2014                err = 1;
2015        }
2016        if ( iackfmt ) {
2017                printf(iackfmt, "MISSED -- BAD\n");
2018                err = 1;
2019        }
2020
2021        printf("FINISHED.\n");
2022
2023bail:
2024        if ( doDisable )
2025                vmeUniverseIntDisable(level);
2026        vmeUniverseIntDisable( UNIV_VME_SW_IACK_INT_VEC );
2027        if ( installed > 0 )
2028                vmeUniverseRemoveISR(vector, loopbackTstIsr, (void*)&a);
2029        if ( installed > 1 )
2030                vmeUniverseRemoveISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a);
2031        if ( q )
2032                rtems_message_queue_delete(q);
2033
2034        return sc ? sc : err;
2035}
2036
2037#endif
Note: See TracBrowser for help on using the repository browser.