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

4.104.114.84.95
Last change on this file since 6f10ffe was 6f10ffe, checked in by Till Straumann <strauman@…>, on 01/27/06 at 00:35:27

2006-01-26 Till Straumann <strauman@…>

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