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

4.104.114.84.95
Last change on this file since 08f9722 was 08f9722, checked in by Till Straumann <strauman@…>, on 05/11/07 at 00:19:20
  • vmeUniverse/vmeUniverse.c: Changed types to silence compiler warnings.
  • Property mode set to 100644
File size: 62.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#include "vmeUniverseDMA.h"
58
59#define UNIV_NUM_MPORTS         8 /* number of master ports */
60#define UNIV_NUM_SPORTS         8 /* number of slave ports */
61
62#define PCI_VENDOR_TUNDRA       0x10e3
63#define PCI_DEVICE_UNIVERSEII   0
64#define PCI_UNIVERSE_BASE0      0x10
65#define PCI_UNIVERSE_BASE1      0x14
66
67#define UNIV_REGOFF_PCITGT0_CTRL 0x100
68#define UNIV_REGOFF_PCITGT4_CTRL 0x1a0
69#define UNIV_REGOFF_VMESLV0_CTRL 0xf00
70#define UNIV_REGOFF_VMESLV4_CTRL 0xf90
71
72#define UNIV_CTL_VAS16          (0x00000000)
73#define UNIV_CTL_VAS24          (0x00010000)
74#define UNIV_CTL_VAS32          (0x00020000)
75#define UNIV_MCTL_VASCSR        (0x00050000)
76#define UNIV_CTL_VAS            (0x00070000)
77
78#define UNIV_MCTL_EN            (0x80000000)
79#define UNIV_MCTL_PWEN          (0x40000000)
80#define UNIV_MCTL_PGM           (0x00004000)
81#define UNIV_MCTL_VCT           (0x00000100)
82#define UNIV_MCTL_SUPER         (0x00001000)
83#define UNIV_MCTL_VDW16         (0x00400000)
84#define UNIV_MCTL_VDW32         (0x00800000)
85#define UNIV_MCTL_VDW64         (0x00c00000)
86
87#define UNIV_MCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_MCTL_PGM | UNIV_MCTL_SUPER)
88
89#define UNIV_SCTL_EN            (0x80000000)
90#define UNIV_SCTL_PWEN          (0x40000000)
91#define UNIV_SCTL_PREN          (0x20000000)
92#define UNIV_SCTL_PGM           (0x00800000)
93#define UNIV_SCTL_DAT           (0x00400000)
94#define UNIV_SCTL_SUPER         (0x00200000)
95#define UNIV_SCTL_USER          (0x00100000)
96
97#define UNIV_SCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
98
99#ifdef __rtems__
100
101#include <stdlib.h>
102#include <rtems/bspIo.h>        /* printk */
103#include <rtems/error.h>
104#include <bsp/pci.h>
105#include <bsp.h>
106#include <libcpu/byteorder.h>
107
108/* allow the BSP to override the default routines */
109#ifndef BSP_PCI_FIND_DEVICE
110#define BSP_PCI_FIND_DEVICE                     pci_find_device
111#endif
112#ifndef BSP_PCI_CONFIG_IN_LONG
113#define BSP_PCI_CONFIG_IN_LONG          pci_read_config_dword
114#endif
115#ifndef BSP_PCI_CONFIG_IN_BYTE
116#define BSP_PCI_CONFIG_IN_BYTE          pci_read_config_byte
117#endif
118#ifndef BSP_PCI_CONFIG_IN_SHORT
119#define BSP_PCI_CONFIG_IN_SHORT         pci_read_config_word
120#endif
121#ifndef BSP_PCI_CONFIG_OUT_SHORT
122#define BSP_PCI_CONFIG_OUT_SHORT        pci_write_config_word
123#endif
124
125/* PCI_MEM_BASE is a possible offset between CPU- and PCI addresses.
126 * Should be defined by the BSP.
127 */
128typedef unsigned int pci_ulong;
129
130#ifndef BSP_PCI2LOCAL_ADDR
131#ifndef PCI_MEM_BASE
132#define PCI_MEM_BASE 0
133#endif
134#define BSP_PCI2LOCAL_ADDR(memaddr) ((pci_ulong)(memaddr) + PCI_MEM_BASE)
135#endif
136
137#ifndef BSP_LOCAL2PCI_ADDR
138#ifndef PCI_DRAM_OFFSET
139#define PCI_DRAM_OFFSET 0
140#endif
141#define BSP_LOCAL2PCI_ADDR(pciaddr) ((uint32_t)(pciaddr) + PCI_DRAM_OFFSET)
142#endif
143
144
145#elif defined(__vxworks)
146typedef unsigned long pci_ulong;
147#define BSP_PCI2LOCAL_ADDR(memaddr) (memaddr)
148#define BSP_PCI_FIND_DEVICE             pciFindDevice
149#define BSP_PCI_CONFIG_IN_LONG  pciConfigInLong
150#define BSP_PCI_CONFIG_IN_BYTE  pciConfigInByte
151#else
152#error "vmeUniverse not ported to this architecture yet"
153#endif
154
155#ifndef PCI_INTERRUPT_LINE
156#define PCI_INTERRUPT_LINE              0x3c
157#endif
158
159volatile LERegister *vmeUniverse0BaseAddr=0;
160int vmeUniverse0PciIrqLine=-1;
161
162#ifdef __rtems__
163int vmeUniverseRegPort = -1;
164int vmeUniverseRegCSR  = 0;
165#endif
166
167#define DFLT_BASE       volatile LERegister *base = vmeUniverse0BaseAddr
168
169#define CHECK_DFLT_BASE(base) \
170        do { \
171                /* get the universe base address */ \
172                if (!base) { \
173                        if (vmeUniverseInit()) { \
174                                uprintf(stderr,"unable to find the universe in pci config space\n"); \
175                                return -1; \
176                        } else { \
177                                base = vmeUniverse0BaseAddr; \
178                        } \
179                } \
180        } while (0)
181
182#if 0
183/* public access functions */
184volatile LERegister *
185vmeUniverseBaseAddr(void)
186{
187        if (!vmeUniverse0BaseAddr) vmeUniverseInit();
188        return vmeUniverse0BaseAddr;
189}
190
191int
192vmeUniversePciIrqLine(void)
193{
194        if (vmeUniverse0PciIrqLine<0) vmeUniverseInit();
195        return vmeUniverse0PciIrqLine;
196}
197#endif
198
199static inline void
200WRITE_LE(
201        unsigned long val,
202        volatile LERegister    *adrs,
203        unsigned long off)
204{
205#if (__LITTLE_ENDIAN__ == 1)
206        *(volatile unsigned long*)(((unsigned long)adrs)+off)=val;
207#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
208        /* offset is in bytes and MUST not end up in r0 */
209        __asm__ __volatile__("stwbrx %1, %0, %2" :: "b"(off),"r"(val),"r"(adrs));
210#elif defined(__rtems__)
211        st_le32((volatile unsigned long*)(((unsigned long)adrs)+off), val);
212#else
213#error "little endian register writing not implemented"
214#endif
215}
216
217#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)
218#define SYNC __asm__ __volatile__("sync")
219#else
220#define SYNC
221#warning "SYNC instruction unknown for this architecture"
222#endif
223
224/* registers should be mapped to guarded, non-cached memory; hence
225 * subsequent stores are ordered. eieio is only needed to enforce
226 * ordering of loads with respect to stores.
227 */
228#define EIEIO_REG
229
230static inline unsigned long
231READ_LE0(volatile LERegister *adrs)
232{
233#if (__LITTLE_ENDIAN__ == 1)
234        return *(volatile unsigned long *)adrs;
235#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
236register unsigned long rval;
237__asm__ __volatile__("lwbrx %0, 0, %1":"=r"(rval):"r"(adrs));
238        return rval;
239#elif defined(__rtems__)
240        return ld_le32((volatile unsigned long*)adrs);
241#else
242#error "little endian register reading not implemented"
243#endif
244}
245
246static inline unsigned long
247READ_LE(volatile LERegister *adrs, unsigned long off)
248{
249#if (__LITTLE_ENDIAN__ == 1)
250        return  *((volatile LERegister *)(((unsigned long)adrs)+off));
251#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
252register unsigned long rval;
253        /* offset is in bytes and MUST not end up in r0 */
254__asm__ __volatile__("lwbrx %0, %2, %1"
255                                : "=r"(rval)
256                                : "r"(adrs), "b"(off));
257#if 0
258__asm__ __volatile__("eieio");
259#endif
260return rval;
261#else
262return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off));
263#endif
264}
265
266#define PORT_UNALIGNED(addr,port) \
267        ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
268
269
270#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
271       
272#if defined(__rtems__) && 0
273static int
274uprintk(char *fmt, va_list ap)
275{
276int             rval;
277extern int k_vsprintf(char *, char *, va_list);
278/* during bsp init, there is no malloc and no stdio,
279 * hence we assemble the message on the stack and revert
280 * to printk
281 */
282char    buf[200];
283        rval = k_vsprintf(buf,fmt,ap);
284        if (rval > sizeof(buf))
285                        BSP_panic("vmeUniverse/uprintk: buffer overrun");
286        printk(buf);
287        return rval;
288}
289#endif
290
291
292/* private printing wrapper */
293static void
294uprintf(FILE *f, char *fmt, ...)
295{
296va_list ap;
297        va_start(ap, fmt);
298#ifdef __rtems__
299        if (!f || !_impure_ptr->__sdidinit) {
300                /* Might be called at an early stage when
301                 * stdio is not yet initialized.
302                 * There is no vprintk, hence we must assemble
303                 * to a buffer.
304                 */
305                vprintk(fmt,ap);
306        } else
307#endif
308        {
309                vfprintf(f,fmt,ap);
310        }
311        va_end(ap);
312}
313
314int
315vmeUniverseFindPciBase(
316        int instance,
317        volatile LERegister **pbase
318        )
319{
320int bus,dev,fun;
321unsigned short wrd;
322pci_ulong busaddr;
323unsigned char irqline;
324
325        if (BSP_PCI_FIND_DEVICE(
326                        PCI_VENDOR_TUNDRA,
327                        PCI_DEVICE_UNIVERSEII,
328                        instance,
329                        &bus,
330                        &dev,
331                        &fun))
332                return -1;
333        if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE0,&busaddr))
334                return -1;
335        if ((unsigned long)(busaddr) & 1) {
336                /* it's IO space, try BASE1 */
337                if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE1,&busaddr)
338                   || ((unsigned long)(busaddr) & 1))
339                        return -1;
340        }
341        *pbase=(volatile LERegister*)BSP_PCI2LOCAL_ADDR(busaddr);
342
343        if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline))
344                return -1;
345
346        /* Enable PCI master and memory access */
347        BSP_PCI_CONFIG_IN_SHORT(bus, dev, fun, PCI_COMMAND, &wrd);
348        BSP_PCI_CONFIG_OUT_SHORT(bus, dev, fun, PCI_COMMAND, wrd | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
349
350        return irqline;
351}
352
353/* convert an address space selector to a corresponding
354 * universe control mode word
355 */
356
357static int
358am2mode(int ismaster, unsigned long address_space, unsigned long *pmode)
359{
360unsigned long mode=0;
361unsigned long vdw =0;
362
363    /* NOTE: reading the CY961 (Echotek ECDR814) with VDW32
364     *       generated bus errors when reading 32-bit words
365     *       - very weird, because the registers are 16-bit
366     *         AFAIK.
367     *       - 32-bit accesses worked fine on vxWorks which
368     *         has the port set to 64-bit.
369     *       ????????
370     */
371
372        address_space &= ~VME_MODE_MATCH_MASK;
373
374        if (!ismaster) {
375                mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM;
376                mode |= UNIV_SCTL_USER;
377                if ( VME_AM_IS_MEMORY & address_space )
378                        mode |= UNIV_SCTL_PWEN | UNIV_SCTL_PREN;
379                mode |= UNIV_SCTL_EN;
380        } else {
381                switch ( VME_MODE_DBW_MSK & address_space ) {
382                        default:
383                                vdw = UNIV_MCTL_VDW64;
384                        break;
385
386                        case VME_MODE_DBW8:
387                                break;
388
389                        case VME_MODE_DBW16:
390                                vdw = UNIV_MCTL_VDW16;
391                                break;
392
393                        case VME_MODE_DBW32:
394                                vdw = UNIV_MCTL_VDW32;
395                                break;
396                }
397                if ( VME_AM_IS_MEMORY & address_space )
398                        mode |= UNIV_MCTL_PWEN;
399                mode |= UNIV_MCTL_EN;
400        }
401
402        address_space &= ~VME_AM_IS_MEMORY;
403
404        switch (address_space & VME_AM_MASK) {
405                case VME_AM_STD_SUP_PGM:
406                case VME_AM_STD_USR_PGM:
407                        if (ismaster)
408                                mode |= UNIV_MCTL_PGM ;
409                        else {
410                                mode &= ~UNIV_SCTL_DAT;
411                        }
412
413                        /* fall thru */
414
415                case VME_AM_STD_SUP_DATA:
416                case VME_AM_STD_USR_DATA:
417                case VME_AM_STD_SUP_BLT:
418                case VME_AM_STD_SUP_MBLT:
419                case VME_AM_STD_USR_BLT:
420                case VME_AM_STD_USR_MBLT:
421
422                        if ( ismaster ) {
423                                switch ( address_space & 3 ) {
424                                        case 0: /* mblt */
425                                                if ( UNIV_MCTL_VDW64 != vdw )
426                                                        return -1;
427                                                break;
428
429                                        case 3: /* blt  */
430                                                mode |= UNIV_MCTL_VCT;
431                                                /* universe may do mblt anyways so go back to
432                                                 * 32-bit width
433                                                 */
434                                                vdw   = UNIV_MCTL_VDW32;
435                                }
436                        }
437
438                        mode |= UNIV_CTL_VAS24;
439                        break;
440       
441
442                case VME_AM_EXT_SUP_PGM:
443                case VME_AM_EXT_USR_PGM:
444                        if (ismaster)
445                                mode |= UNIV_MCTL_PGM ;
446                        else {
447                                mode &= ~UNIV_SCTL_DAT;
448                        }
449                        /* fall thru */
450
451                case VME_AM_EXT_SUP_DATA:
452                case VME_AM_EXT_USR_DATA:
453                case VME_AM_EXT_SUP_BLT:
454                case VME_AM_EXT_SUP_MBLT:
455                case VME_AM_EXT_USR_BLT:
456                case VME_AM_EXT_USR_MBLT:
457
458                        if ( ismaster ) {
459                                switch ( address_space & 3 ) {
460                                        case 0: /* mblt */
461                                                if ( UNIV_MCTL_VDW64 != vdw )
462                                                        return -1;
463                                                break;
464
465                                        case 3: /* blt  */
466                                                mode |= UNIV_MCTL_VCT;
467                                                /* universe may do mblt anyways so go back to
468                                                 * 32-bit width
469                                                 */
470                                                vdw   = UNIV_MCTL_VDW32;
471                                }
472                        }
473
474                        mode |= UNIV_CTL_VAS32;
475
476                        break;
477
478                case VME_AM_SUP_SHORT_IO:
479                case VME_AM_USR_SHORT_IO:
480                        mode |= UNIV_CTL_VAS16;
481                        break;
482
483                case VME_AM_CSR:
484                        if ( !ismaster )
485                                return -1;
486                        mode |= UNIV_MCTL_VASCSR;
487                        break;
488
489                case 0: /* disable the port alltogether */
490                        break;
491
492                default:
493                        return -1;
494        }
495        if ( VME_AM_IS_SUP(address_space) )
496                mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER);
497
498        mode |= vdw; /* vdw still 0 in slave mode */
499        *pmode = mode;
500        return 0;
501}
502
503static int
504disableUniversePort(int ismaster, int portno, volatile unsigned long *preg, void *param)
505{
506unsigned long cntrl;
507        cntrl=READ_LE0(preg);
508        cntrl &= ~(ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN);
509        WRITE_LE(cntrl,preg,0);
510        SYNC; /* make sure this command completed */
511        return 0;
512}
513
514static int
515cfgUniversePort(
516        volatile LERegister *base,
517        unsigned long   ismaster,
518        unsigned long   port,
519        unsigned long   address_space,
520        unsigned long   vme_address,
521        unsigned long   local_address,
522        unsigned long   length)
523{
524volatile LERegister *preg;
525unsigned long   p=port;
526unsigned long   mode=0;
527
528        CHECK_DFLT_BASE(base);
529
530        /* check parameters */
531        if (port >= (ismaster ? UNIV_NUM_MPORTS : UNIV_NUM_SPORTS)) {
532                uprintf(stderr,"invalid port\n");
533                return -1;
534        }
535        /* port start, bound addresses and offset must lie on 64k boundary
536         * (4k for port 0 and 4)
537         */
538        if ( PORT_UNALIGNED(local_address,port) ) {
539                uprintf(stderr,"local address misaligned\n");
540                return -1;
541        }
542        if ( PORT_UNALIGNED(vme_address,port) ) {
543                uprintf(stderr,"vme address misaligned\n");
544                return -1;
545        }
546        if ( PORT_UNALIGNED(length,port) ) {
547                uprintf(stderr,"length misaligned\n");
548                return -1;
549        }
550
551        /* check address space validity */
552        if (am2mode(ismaster,address_space,&mode)) {
553                uprintf(stderr,"invalid address space\n");
554                return -1;
555        }
556
557        /* get the universe base address */
558        if (!base && vmeUniverseInit()) {
559                return -1;
560        }
561
562        preg=base;
563
564        /* find out if we have a rev. II chip */
565        if ( UNIV_REV(base) < 2 ) {
566                if (port>3) {
567                        uprintf(stderr,"Universe rev. < 2 has only 4 ports\n");
568                        return -1;
569                }
570        }
571
572        /* finally, configure the port */
573
574        /* find the register set for our port */
575        if (port<4) {
576                preg += (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister);
577        } else {
578                preg += (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister);
579                p-=4;
580        }
581        preg += 5 * p;
582
583        /* temporarily disable the port */
584        disableUniversePort(ismaster,port,preg,0);
585
586        /* address_space == 0 means disable */
587        if (address_space != 0) {
588                unsigned long start,offst;
589                /* set the port starting address;
590                 * this is the local address for the master
591                 * and the VME address for the slave
592                 */
593                if (ismaster) {
594                        start=local_address;
595                        /* let it overflow / wrap around 0 */
596                        offst=vme_address-local_address;
597                } else {
598                        start=vme_address;
599                        /* let it overflow / wrap around 0 */
600                        offst=local_address-vme_address;
601                }
602#undef TSILL
603#ifdef TSILL
604                uprintf(stderr,"writing 0x%08x to 0x%08x + 4\n",start,preg);
605#else
606                WRITE_LE(start,preg,4);
607#endif
608                /* set bound address */
609                length+=start;
610#ifdef TSILL
611                uprintf(stderr,"writing 0x%08x to 0x%08x + 8\n",length,preg);
612#else
613                WRITE_LE(length,preg,8);
614#endif
615                /* set offset */
616#ifdef TSILL
617                uprintf(stderr,"writing 0x%08x to 0x%08x + 12\n",offst,preg);
618#else
619                WRITE_LE(offst,preg,12);
620#endif
621
622#ifdef TSILL
623                uprintf(stderr,"writing 0x%08x to 0x%08x + 0\n",mode,preg);
624#else
625                EIEIO_REG;      /* make sure mode is written last */
626                WRITE_LE(mode,preg,0);
627                SYNC;           /* enforce completion */
628#endif
629
630#ifdef TSILL
631                uprintf(stderr,
632                        "universe %s port %lu successfully configured\n",
633                                ismaster ? "master" : "slave",
634                                port);
635#endif
636
637#ifdef __vxworks
638                if (ismaster)
639                        uprintf(stderr,
640                        "WARNING: on the synergy, sysMasterPortsShow() may show incorrect settings (it uses cached values)\n");
641#endif
642        }
643        return 0;
644}
645
646static int
647showUniversePort(
648                int             ismaster,
649                int             portno,
650                volatile LERegister *preg,
651                void            *parm)
652{
653        FILE *f=parm ? (FILE *)parm : stdout;
654        unsigned long cntrl, start, bound, offst, mask;
655
656        cntrl = READ_LE0(preg++);
657#undef TSILL
658#ifdef TSILL
659        uprintf(stderr,"showUniversePort: *(0x%08x): 0x%08x\n",preg-1,cntrl);
660#endif
661#undef TSILL
662
663        /* skip this port if disabled */
664        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
665                return 0;
666
667        /* for the master `start' is the PCI address,
668         * for the slave  `start' is the VME address
669         */
670        mask = ~PORT_UNALIGNED(0xffffffff,portno);
671
672        start = READ_LE0(preg++)&mask;
673        bound = READ_LE0(preg++)&mask;
674        offst = READ_LE0(preg++)&mask;
675
676        offst+=start; /* calc start on the other bus */
677
678        if (ismaster) {
679                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
680                        portno,offst,bound-start,start);
681        } else {
682                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
683                        portno,start,bound-start,offst);
684        }
685
686        switch (cntrl & UNIV_CTL_VAS) {
687                case UNIV_CTL_VAS16:   uprintf(f,"A16, "); break;
688                case UNIV_CTL_VAS24:   uprintf(f,"A24, "); break;
689                case UNIV_CTL_VAS32:   uprintf(f,"A32, "); break;
690                case UNIV_MCTL_VASCSR: if ( ismaster ) { uprintf(f,"CSR, "); break; }
691                                       /* else fallthru */
692                default: uprintf(f,"A??, "); break;
693        }
694
695        if (ismaster) {
696                unsigned vdw;
697                switch ( cntrl & UNIV_MCTL_VDW64 ) {
698                        case UNIV_MCTL_VDW64:
699                                vdw = 64;
700                        break;
701
702                        case UNIV_MCTL_VDW32:
703                                vdw = 32;
704                        break;
705
706                        case UNIV_MCTL_VDW16:
707                                vdw = 16;
708                        break;
709
710                        default:
711                                vdw = 8;
712                        break;
713                }
714
715                if ( 64 == vdw ) {
716                        switch ( UNIV_CTL_VAS & cntrl ) {
717                                case UNIV_CTL_VAS24:
718                                case UNIV_CTL_VAS32:
719                                        uprintf(f,"D64 [MBLT], ");
720                                        break;
721
722                                default:
723                                        uprintf(f,"D64, ");
724                                        break;
725                        }
726                } else {
727                        uprintf(f, "D%u%s, ", vdw, (cntrl & UNIV_MCTL_VCT) ? " [BLT]" : "");
728                }
729
730                uprintf(f,"%s, %s",
731                        cntrl&UNIV_MCTL_PGM ?   "Pgm" : "Dat",
732                        cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr");
733                if ( cntrl & UNIV_MCTL_PWEN )
734                        uprintf(f,", PWEN");
735        } else {
736                uprintf(f,"%s %s %s %s",
737                        cntrl&UNIV_SCTL_PGM ?   "Pgm," : "    ",
738                        cntrl&UNIV_SCTL_DAT ?   "Dat," : "    ",
739                        cntrl&UNIV_SCTL_SUPER ? "Sup," : "    ",
740                        cntrl&UNIV_SCTL_USER  ? "Usr" :  "");
741                if ( cntrl & UNIV_SCTL_PWEN )
742                        uprintf(f,", PWEN");
743                if ( cntrl & UNIV_SCTL_PREN )
744                        uprintf(f,", PREN");
745        }
746        uprintf(f,"\n");
747        return 0;
748}
749
750typedef struct XlatRec_ {
751        unsigned long   address;
752        unsigned long   aspace;
753        unsigned                reverse; /* find reverse mapping of this port */
754} XlatRec, *Xlat;
755
756/* try to translate an address through the bridge
757 *
758 * IN:  l->address, l->aspace
759 * OUT: l->address (translated address)
760 *
761 * RETURNS: -1: invalid space
762 *           0: invalid address (not found in range)
763 *      port+1: success
764 */
765
766static int
767xlatePort(int ismaster, int port, volatile LERegister *preg, void *parm)
768{
769Xlat    l=(Xlat)parm;
770unsigned long cntrl, start, bound, offst, mask, x;
771
772        cntrl = READ_LE0(preg++);
773
774        /* skip this port if disabled */
775        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
776                return 0;
777
778        /* check for correct address space */
779        if ( am2mode(ismaster,l->aspace,&offst) ) {
780                uprintf(stderr,"vmeUniverse WARNING: invalid adressing mode 0x%x\n",
781                               l->aspace);
782                return -1;
783        }
784
785
786        switch (VME_MODE_MATCH_MASK & l->aspace) {
787                case VME_MODE_EXACT_MATCH:
788                        mask = -1 & ~VME_MODE_MATCH_MASK;
789                        break;
790
791                case VME_MODE_AS_MATCH:
792                        mask = UNIV_CTL_VAS;
793                        break;
794
795                default:
796                        mask = (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK);
797                        break;
798        }
799
800        cntrl &= mask;
801        offst &= mask;
802
803        if ( cntrl != offst )
804                return 0; /* mode doesn't match requested AM */
805
806        /* OK, we found a matching mode, now we must check the address range */
807        mask = ~PORT_UNALIGNED(0xffffffff,port);
808
809        /* for the master `start' is the PCI address,
810         * for the slave  `start' is the VME address
811         */
812        start = READ_LE0(preg++) & mask;
813        bound = READ_LE0(preg++) & mask;
814        offst = READ_LE0(preg++) & mask;
815
816        /* translate address to the other bus */
817        if (l->reverse) {
818                /* reverse mapping, i.e. for master ports we map from
819                 * VME to PCI, for slave ports we map from VME to PCI
820                 */
821                if (l->address >= start && l->address < bound) {
822                                l->address+=offst;
823                                return 1 + port;
824                }
825        } else {
826                x = l->address - offst;
827
828                if (x >= start && x < bound) {
829                        /* valid address found */
830                        l->address = x;
831                        return 1 + port;
832                }
833        }
834        return 0;
835}
836
837/* check if there is any active window with write posting enabled */
838static int
839hasPWENWindow(
840                int             ismaster,
841                int             portno,
842                volatile LERegister *preg,
843                void            *parm)
844{
845unsigned long cntrl = READ_LE0(preg);
846unsigned long mask  = ismaster ? (UNIV_MCTL_EN|UNIV_MCTL_PWEN) : (UNIV_SCTL_EN|UNIV_SCTL_PWEN);
847        return (cntrl & mask) == mask ? -1 : 0;
848}
849
850static int
851mapOverAll(volatile LERegister *base, int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
852{
853volatile LERegister     *rptr;
854unsigned long   port;
855int     rval;
856
857        CHECK_DFLT_BASE(base);
858
859        rptr = (base +
860                (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister));
861#undef TSILL
862#ifdef TSILL
863        uprintf(stderr,"mapoverall: base is 0x%08x, rptr 0x%08x\n",base,rptr);
864#endif
865#undef TSILL
866        for (port=0; port<4; port++) {
867                if ((rval=func(ismaster,port,rptr,arg))) return rval;
868                rptr+=5; /* register block spacing */
869        }
870
871        /* only rev. 2 has 8 ports */
872        if (UNIV_REV(base)<2) return -1;
873
874        rptr = (base +
875                (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister));
876        for (port=4; port<UNIV_NUM_MPORTS; port++) {
877                if ((rval=func(ismaster,port,rptr,arg))) return rval;
878                rptr+=5; /* register block spacing */
879        }
880        return 0;
881}
882
883static void
884showUniversePorts(volatile LERegister *base, int ismaster, FILE *f)
885{
886        if (!f) f=stdout;
887        uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave");
888        uprintf(f,"Port  VME-Addr   Size       PCI-Adrs   Mode:\n");
889        mapOverAll(base,ismaster,showUniversePort,f);
890}
891
892static int
893xlateFindPort(
894        volatile LERegister *base,      /* Universe base address */
895        int master,             /* look in the master windows */
896        int reverse,            /* reverse mapping; for masters: map local to VME */
897        unsigned long as,       /* address space */
898        unsigned long aIn,      /* address to look up */
899        unsigned long *paOut/* where to put result */
900        )
901{
902int     rval;
903XlatRec l;
904        l.aspace  = as;
905        l.address = aIn;
906        l.reverse = reverse;
907        /* map result -1/0/1 to -2/-1/0 with 0 on success */
908        rval = mapOverAll(base,master,xlatePort,(void*)&l) - 1;
909        *paOut = l.address;
910        return rval;
911}
912
913int
914vmeUniverseXlateAddrXX(
915        volatile LERegister *base,      /* Universe base address */
916        int master,             /* look in the master windows */
917        int reverse,            /* reverse mapping; for masters: map local to VME */
918        unsigned long as,       /* address space */
919        unsigned long aIn,      /* address to look up */
920        unsigned long *paOut/* where to put result */
921        )
922{
923        return xlateFindPort(base, master, reverse, as, aIn, paOut) >= 0 ? 0 : -1;
924}
925
926int
927vmeUniverseXlateAddr(
928        int master,             /* look in the master windows */
929        int reverse,            /* reverse mapping; for masters: map local to VME */
930        unsigned long as,       /* address space */
931        unsigned long aIn,      /* address to look up */
932        unsigned long *paOut/* where to put result */
933        )
934{
935        DFLT_BASE;
936        return vmeUniverseXlateAddrXX(base, master, reverse, as, aIn, paOut);
937}
938
939
940void
941vmeUniverseReset(void)
942{
943        /* disable/reset special cycles (ADOH, RMW) */
944        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_CTL);
945        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_ADDR);
946        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_EN);
947
948        /* set coupled window timeout to 0 (release VME after each transaction)
949         * CRT (coupled request timeout) is unused by Universe II
950         */
951        vmeUniverseWriteReg(UNIV_LMISC_CRT_128_US, UNIV_REGOFF_LMISC);
952
953        /* disable/reset DMA engine */
954        vmeUniverseWriteReg(0, UNIV_REGOFF_DCTL);
955        vmeUniverseWriteReg(0, UNIV_REGOFF_DTBC);
956        vmeUniverseWriteReg(0, UNIV_REGOFF_DLA);
957        vmeUniverseWriteReg(0, UNIV_REGOFF_DVA);
958        vmeUniverseWriteReg(0, UNIV_REGOFF_DCPP);
959
960        /* disable location monitor */
961        vmeUniverseWriteReg(0, UNIV_REGOFF_LM_CTL);
962
963        /* disable universe register access from VME bus */
964        vmeUniverseWriteReg(0, UNIV_REGOFF_VRAI_CTL);
965
966#if 0   /* leave CSR bus image alone; IRQ manager can use it */
967        /* disable VME bus image of VME CSR */
968        vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);
969#endif
970
971
972        /* I had problems with a Joerger vtr10012_8 card who would
973         * only be accessible after tweaking the U2SPEC register
974         * (the t27 parameter helped).
975         * I use the same settings here that are used by the
976         * Synergy VGM-powerpc BSP for vxWorks.
977         */
978        if (2==UNIV_REV(vmeUniverse0BaseAddr))
979                vmeUniverseWriteReg(UNIV_U2SPEC_DTKFLTR |
980                                                UNIV_U2SPEC_MASt11   |
981                                                UNIV_U2SPEC_READt27_NODELAY |
982                                                UNIV_U2SPEC_POSt28_FAST |
983                                                UNIV_U2SPEC_PREt28_FAST,
984                                                UNIV_REGOFF_U2SPEC);
985
986        /* disable interrupts, reset routing */
987        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_EN);
988        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP0);
989        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP1);
990
991        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_EN);
992        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP0);
993        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP1);
994
995        vmeUniverseDisableAllSlaves();
996
997        vmeUniverseDisableAllMasters();
998       
999        vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR);
1000
1001        /* clear interrupt status bits */
1002        vmeUniverseWriteReg(UNIV_LINT_STAT_CLR, UNIV_REGOFF_LINT_STAT);
1003        vmeUniverseWriteReg(UNIV_VINT_STAT_CLR, UNIV_REGOFF_VINT_STAT);
1004
1005        vmeUniverseWriteReg(UNIV_V_AMERR_V_STAT, UNIV_REGOFF_V_AMERR);
1006
1007        vmeUniverseWriteReg(
1008                vmeUniverseReadReg(UNIV_REGOFF_PCI_CSR) |
1009                UNIV_PCI_CSR_D_PE | UNIV_PCI_CSR_S_SERR | UNIV_PCI_CSR_R_MA |
1010                UNIV_PCI_CSR_R_TA | UNIV_PCI_CSR_S_TA,
1011                UNIV_REGOFF_PCI_CSR);
1012
1013        vmeUniverseWriteReg(UNIV_L_CMDERR_L_STAT, UNIV_REGOFF_L_CMDERR);
1014
1015        vmeUniverseWriteReg(
1016                UNIV_DGCS_STOP | UNIV_DGCS_HALT | UNIV_DGCS_DONE |
1017                UNIV_DGCS_LERR | UNIV_DGCS_VERR | UNIV_DGCS_P_ERR,
1018                UNIV_REGOFF_DGCS);
1019}
1020
1021int
1022vmeUniverseInit(void)
1023{
1024int rval;
1025        if ( (rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr)) < 0 ) {
1026                uprintf(stderr,"unable to find the universe in pci config space\n");
1027        } else {
1028                vmeUniverse0PciIrqLine = rval;
1029                rval                   = 0;
1030                uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %d\n",
1031                                (unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine);
1032        }
1033        return rval;
1034}
1035
1036void
1037vmeUniverseMasterPortsShowXX(volatile LERegister *base, FILE *f)
1038{
1039        showUniversePorts(base,1,f);
1040}
1041
1042void
1043vmeUniverseMasterPortsShow(FILE *f)
1044{
1045        DFLT_BASE;
1046        showUniversePorts(base,1,f);
1047}
1048
1049void
1050vmeUniverseSlavePortsShowXX(volatile LERegister *base, FILE *f)
1051{
1052        showUniversePorts(base,0,f);
1053}
1054
1055void
1056vmeUniverseSlavePortsShow(FILE *f)
1057{
1058        DFLT_BASE;
1059        showUniversePorts(base,0,f);
1060}
1061
1062int
1063vmeUniverseMasterPortCfgXX(
1064        volatile LERegister *base,
1065        unsigned long   port,
1066        unsigned long   address_space,
1067        unsigned long   vme_address,
1068        unsigned long   local_address,
1069        unsigned long   length)
1070{
1071        return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
1072}
1073
1074int
1075vmeUniverseMasterPortCfg(
1076        unsigned long   port,
1077        unsigned long   address_space,
1078        unsigned long   vme_address,
1079        unsigned long   local_address,
1080        unsigned long   length)
1081{
1082        DFLT_BASE;
1083        return cfgUniversePort(base,1,port,address_space,vme_address,local_address,length);
1084}
1085
1086int
1087vmeUniverseSlavePortCfgXX(
1088        volatile LERegister *base,
1089        unsigned long   port,
1090        unsigned long   address_space,
1091        unsigned long   vme_address,
1092        unsigned long   local_address,
1093        unsigned long   length)
1094{
1095        return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
1096}
1097
1098int
1099vmeUniverseSlavePortCfg(
1100        unsigned long   port,
1101        unsigned long   address_space,
1102        unsigned long   vme_address,
1103        unsigned long   local_address,
1104        unsigned long   length)
1105{
1106        DFLT_BASE;
1107        return cfgUniversePort(base,0,port,address_space,vme_address,local_address,length);
1108}
1109
1110
1111void
1112vmeUniverseDisableAllSlavesXX(volatile LERegister *base)
1113{
1114        mapOverAll(base,0,disableUniversePort,0);
1115}
1116
1117void
1118vmeUniverseDisableAllSlaves(void)
1119{
1120        DFLT_BASE;
1121        mapOverAll(base,0,disableUniversePort,0);
1122}
1123
1124void
1125vmeUniverseDisableAllMastersXX(volatile LERegister *base)
1126{
1127        mapOverAll(base,1,disableUniversePort,0);
1128}
1129
1130void
1131vmeUniverseDisableAllMasters(void)
1132{
1133        DFLT_BASE;
1134        mapOverAll(base,1,disableUniversePort,0);
1135}
1136
1137unsigned long
1138vmeUniverseReadRegXX(volatile LERegister *base, unsigned long offset)
1139{
1140unsigned long rval;
1141        rval = READ_LE(base,offset);
1142        return rval;
1143}
1144
1145
1146unsigned long
1147vmeUniverseReadReg(unsigned long offset)
1148{
1149unsigned long rval;
1150        rval = READ_LE(vmeUniverse0BaseAddr,offset);
1151        return rval;
1152}
1153
1154void
1155vmeUniverseWriteRegXX(volatile LERegister *base, unsigned long value, unsigned long offset)
1156{
1157        WRITE_LE(value, base, offset);
1158}
1159
1160void
1161vmeUniverseWriteReg(unsigned long value, unsigned long offset)
1162{
1163        WRITE_LE(value, vmeUniverse0BaseAddr, offset);
1164}
1165
1166void
1167vmeUniverseResetBus(void)
1168{
1169        vmeUniverseWriteReg(
1170                vmeUniverseReadReg(UNIV_REGOFF_MISC_CTL) | UNIV_MISC_CTL_SW_SYSRST,
1171                UNIV_REGOFF_MISC_CTL);
1172}
1173
1174void
1175vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num)
1176{
1177#if !defined(__LITTLE_ENDIAN__) || (__LITTLE_ENDIAN__ != 1)
1178register unsigned long *p=ptr+num;
1179        while (p > ptr) {
1180#if (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
1181                __asm__ __volatile__(
1182                        "lwzu 0, -4(%0)\n"
1183                        "stwbrx 0, 0, %0\n"
1184                        : "=r"(p) : "0"(p) : "r0"
1185                        );
1186#elif defined(__rtems__)
1187                p--; st_le32(p, *p);
1188#else
1189#error  "vmeUniverse: endian conversion not implemented for this architecture"
1190#endif
1191        }
1192#endif
1193}
1194
1195int
1196vmeUniverseIntRaiseXX(volatile LERegister *base, int level, unsigned vector)
1197{
1198unsigned long v;
1199unsigned long b;
1200
1201        CHECK_DFLT_BASE(base);
1202
1203        if ( level < 1 || level > 7 || vector > 255 )
1204                return -1;      /* invalid argument */
1205
1206        if ( vector & 1 ) /* SW interrupts always ACK an even vector (pp 2-67) */
1207                return -1;
1208
1209
1210        /* Check if already asserted */
1211        if ( vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_STAT ) & UNIV_VINT_STAT_SWINT(level) ) {
1212                return -2;  /* already asserted */
1213        }
1214
1215        /* Write Vector */
1216        vmeUniverseWriteRegXX(base, UNIV_VINT_STATID(vector), UNIV_REGOFF_VINT_STATID );
1217
1218        if ( UNIV_REV(base) >= 2 ) {
1219                /* universe II has individual bits for individual levels */
1220                b = UNIV_VINT_STAT_SWINT(level);
1221        } else {
1222                /* version that is compatible with universe I */
1223                v  = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_MAP1);
1224                v &= ~UNIV_VINT_MAP1_SWINT(0x7);
1225                v |=  UNIV_VINT_MAP1_SWINT(level);
1226                vmeUniverseWriteRegXX(base, v, UNIV_REGOFF_VINT_MAP1);
1227                b  = UNIV_VINT_EN_SWINT;
1228        }
1229        v = vmeUniverseReadRegXX(base, UNIV_REGOFF_VINT_EN);
1230        /* make sure it is clear, then assert */
1231        vmeUniverseWriteRegXX(base, v & ~b, UNIV_REGOFF_VINT_EN );
1232        vmeUniverseWriteRegXX(base, v |  b, UNIV_REGOFF_VINT_EN );
1233
1234        return 0;
1235       
1236}
1237
1238int
1239vmeUniverseIntRaise(int level, unsigned vector)
1240{
1241        return vmeUniverseIntRaiseXX(vmeUniverse0BaseAddr, level, vector);
1242}
1243
1244
1245/* Map internal register block to VME */
1246#define UNIV_CRG_SIZE (1<<12)
1247
1248int
1249vmeUniverseMapCRGXX(volatile LERegister *base, unsigned long vme_base, unsigned long as )
1250{
1251uint32_t mode;
1252
1253        CHECK_DFLT_BASE(base);
1254
1255#ifdef __rtems__
1256        if ( vmeUniverseRegPort > -1 && ! vmeUniverseRegCSR ) {
1257        uprintf(stderr,"vmeUniverse: CRG already mapped and in use by interrupt manager\n");
1258                return -1;
1259        }
1260#endif
1261
1262        /* enable all, SUP/USR/PGM/DATA accesses */
1263        mode = UNIV_VRAI_CTL_EN | UNIV_VRAI_CTL_PGM | UNIV_VRAI_CTL_DATA | UNIV_VRAI_CTL_SUPER | UNIV_VRAI_CTL_USER;
1264
1265        if ( VME_AM_IS_SHORT(as) ) {
1266                mode |= UNIV_VRAI_CTL_VAS_A16;
1267        } else
1268        if ( VME_AM_IS_STD(as) ) {
1269                mode |= UNIV_VRAI_CTL_VAS_A24;
1270        } else
1271        if ( VME_AM_IS_EXT(as) ) {
1272                mode |= UNIV_VRAI_CTL_VAS_A32;
1273        } else {
1274                return -2;
1275        }
1276
1277        /* map CRG to VME bus */
1278        WRITE_LE( (vme_base & ~(UNIV_CRG_SIZE-1)), base, UNIV_REGOFF_VRAI_BS );
1279        WRITE_LE( mode, base, UNIV_REGOFF_VRAI_CTL );
1280
1281        return 0;
1282}
1283
1284int
1285vmeUniverseMapCRG(unsigned long vme_base, unsigned long as )
1286{
1287        return vmeUniverseMapCRGXX( vmeUniverse0BaseAddr, vme_base, as );
1288}
1289
1290#ifdef __rtems__
1291/* DMA Support -- including linked-list implementation */
1292#include "bspVmeDmaListP.h"
1293#include <bsp/vmeUniverseDMA.h>
1294
1295/* Filter valid bits of DCTL */
1296#define DCTL_MODE_MASK \
1297        ( UNIV_DCTL_VDW_MSK | UNIV_DCTL_VAS_MSK | UNIV_DCTL_PGM | UNIV_DCTL_SUPER | UNIV_DCTL_VCT )
1298
1299static uint32_t
1300xfer_mode2dctl(uint32_t xfer_mode)
1301{
1302uint32_t dctl;
1303
1304        /* Check requested bus mode */
1305
1306        /* Universe does not support 'non-incrementing' DMA */
1307
1308        /* NOTE: Universe IIb/d *does* support NOINC_VME but states
1309         *       that the VME address needs to be reprogrammed
1310         *       when re-issuing a transfer
1311         */
1312        if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_PCI )
1313                return BSP_VMEDMA_STATUS_UNSUP;
1314
1315        /* ignore memory hint */
1316        xfer_mode &= ~VME_AM_IS_MEMORY;
1317
1318        if ( VME_AM_IS_2eSST(xfer_mode) )
1319                return BSP_VMEDMA_STATUS_UNSUP;
1320
1321        if ( ! VME_AM_IS_SHORT(xfer_mode) && ! VME_AM_IS_STD(xfer_mode) && ! VME_AM_IS_EXT(xfer_mode) )
1322                return BSP_VMEDMA_STATUS_UNSUP;
1323
1324        /* Luckily DCTL bits match MCTL bits so we can use am2mode */
1325        if ( am2mode( 1, xfer_mode, &dctl ) )
1326                return BSP_VMEDMA_STATUS_UNSUP;
1327       
1328        /* However, the book says that for DMA VAS==5 [which would
1329         * be a CSR access] is reserved. Tests indicate that
1330         * CSR access works on the IIb/d but not really (odd 32-bit
1331         * addresses read 0) on the II.
1332         * Nevertheless, we disallow DMA CSR access at this point
1333         * in order to play it safe...
1334         */
1335        switch ( UNIV_DCTL_VAS_MSK & dctl ) {
1336                case UNIV_DCTL_VAS_A24:
1337                case UNIV_DCTL_VAS_A32:
1338                        /* fixup the data width; universe may always use MBLT
1339                         * if data width is 64-bit so we go back to 32-bit
1340                         * if they didn't explicitely ask for MBLT cycles
1341                         */
1342                        if (   (xfer_mode & 0xb) != 8 /* MBLT */
1343                            && ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) ) {
1344                                dctl &= ~UNIV_DCTL_VDW_MSK;
1345                                dctl |= UNIV_DCTL_VDW_32;
1346                        }
1347                        break;
1348
1349                case UNIV_DCTL_VAS_A16:
1350                        break;
1351
1352                default:
1353                        return BSP_VMEDMA_STATUS_UNSUP;
1354        }
1355
1356        /* Make sure other MCTL bits are masked */
1357        dctl &= DCTL_MODE_MASK;
1358
1359        if ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) {
1360                /* If they want NOINC_VME then we have to do some
1361                 * fixup :-( ('errata' [in this case: feature addition] doc. pp. 11+)
1362                 */
1363                dctl &= ~UNIV_DCTL_VCT; /* clear block xfer flag */
1364                dctl |= UNIV_DCTL_NO_VINC;
1365                /* cannot do 64 bit transfers; go back to 32 */
1366                if ( UNIV_DCTL_VDW_64 == (dctl & UNIV_DCTL_VDW_MSK) ) {
1367                        dctl &= ~UNIV_DCTL_VDW_MSK;
1368                        dctl |= UNIV_DCTL_VDW_32;
1369                }
1370        }
1371       
1372        /* Set direction flag */
1373
1374        if ( BSP_VMEDMA_MODE_PCI2VME & xfer_mode )
1375                dctl |= UNIV_DCTL_L2V;
1376
1377        return dctl;
1378}
1379
1380/* Convert canonical xfer_mode into Universe setup bits; return -1 if request
1381 * cannot be satisfied (unsupported features)
1382 */
1383int
1384vmeUniverseDmaSetupXX(volatile LERegister *base, int channel, uint32_t mode, uint32_t xfer_mode, void *custom)
1385{
1386uint32_t dctl, dgcs;
1387
1388        if ( channel != 0 )
1389                return BSP_VMEDMA_STATUS_UNSUP;
1390
1391        dctl = xfer_mode2dctl(xfer_mode);
1392
1393        if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl )
1394                return BSP_VMEDMA_STATUS_UNSUP;
1395
1396        /* Enable all interrupts at the controller */
1397        dgcs = UNIV_DGCS_INT_MSK;
1398
1399        switch ( mode ) {
1400                case BSP_VMEDMA_OPT_THROUGHPUT:
1401                        dgcs |= UNIV_DGCS_VON_1024 | UNIV_DGCS_VOFF_0_US;
1402                        /* VON counts are different in NO_VINC mode :-( */
1403                        dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
1404                                        UNIV_DGCS_VON_2048 : UNIV_DGCS_VON_1024;
1405                break;
1406               
1407                case BSP_VMEDMA_OPT_LOWLATENCY:
1408                        dgcs |= UNIV_DGCS_VOFF_0_US;
1409                        /* VON counts are different in NO_VINC mode :-( */
1410                        dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
1411                                        UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256;
1412                break;
1413
1414                case BSP_VMEDMA_OPT_SHAREDBUS:
1415                        dgcs |= UNIV_DGCS_VOFF_512_US;
1416                        /* VON counts are different in NO_VINC mode :-( */
1417                        dgcs |= ( xfer_mode & BSP_VMEDMA_MODE_NOINC_VME ) ?
1418                                        UNIV_DGCS_VON_512 : UNIV_DGCS_VON_256;
1419                break;
1420
1421                case BSP_VMEDMA_OPT_CUSTOM:
1422                        dctl = ((uint32_t*)custom)[0];
1423                        dgcs = ((uint32_t*)custom)[1];
1424                break;
1425
1426                default:
1427                case BSP_VMEDMA_OPT_DEFAULT:
1428                break;
1429        }
1430
1431        /* clear status bits */
1432        dgcs |= UNIV_DGCS_STATUS_CLEAR;
1433
1434        vmeUniverseWriteRegXX(base, dctl, UNIV_REGOFF_DCTL);
1435        vmeUniverseWriteRegXX(base, dgcs, UNIV_REGOFF_DGCS);
1436
1437        return BSP_VMEDMA_STATUS_OK;
1438}
1439
1440int
1441vmeUniverseDmaSetup(int channel, uint32_t mode, uint32_t xfer_mode, void *custom)
1442{
1443DFLT_BASE;
1444        return vmeUniverseDmaSetupXX(base, channel, mode, xfer_mode, custom);
1445}
1446
1447int
1448vmeUniverseDmaStartXX(volatile LERegister *base, int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes)
1449{
1450        if ( channel != 0 )
1451                return BSP_VMEDMA_STATUS_UNSUP;
1452
1453        if ((pci_addr & 7) != (vme_addr & 7)) {
1454                uprintf(stderr,"vmeUniverseDmaStartXX: misaligned addresses\n");
1455                return -1;
1456        }
1457
1458        {
1459        /* help the compiler allocate registers */
1460        register volatile LERegister *b=base;;
1461        register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs;
1462
1463        dgcs=READ_LE(b, dgcsoff);
1464
1465        /* clear status and make sure CHAIN is clear */
1466        dgcs &= ~UNIV_DGCS_CHAIN;
1467        WRITE_LE(dgcs,
1468                      b, dgcsoff);
1469        WRITE_LE(pci_addr,
1470                      b, UNIV_REGOFF_DLA);
1471        WRITE_LE(vme_addr,
1472                      b, UNIV_REGOFF_DVA);
1473        WRITE_LE(n_bytes,
1474                      b, UNIV_REGOFF_DTBC);
1475        dgcs |= UNIV_DGCS_GO;
1476        EIEIO_REG; /* make sure GO is written after everything else */
1477        WRITE_LE(dgcs,
1478                      b, dgcsoff);
1479        }
1480        SYNC; /* enforce command completion */
1481        return 0;
1482}
1483
1484/* This entry point is deprecated */
1485int
1486vmeUniverseStartDMAXX(
1487        volatile LERegister *base,
1488        unsigned long local_addr,
1489        unsigned long vme_addr,
1490        unsigned long count)
1491{
1492        return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count);
1493}
1494
1495int
1496vmeUniverseDmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes)
1497{
1498        DFLT_BASE; /* vmeUniverseDmaStartXX doesn't check for a valid base address for efficiency reasons */
1499        return vmeUniverseDmaStartXX(base, channel, pci_addr, vme_addr, n_bytes);
1500}
1501
1502/* This entry point is deprecated */
1503int
1504vmeUniverseStartDMA(
1505        unsigned long local_addr,
1506        unsigned long vme_addr,
1507        unsigned long count)
1508{
1509        DFLT_BASE; /* vmeUniverseStartDMAXX doesn't check for a valid base address for efficiency reasons */
1510        return vmeUniverseDmaStartXX(base, 0, local_addr, vme_addr, count);
1511}
1512
1513uint32_t
1514vmeUniverseDmaStatusXX(volatile LERegister *base, int channel)
1515{
1516uint32_t dgcs;
1517        if ( channel != 0 )
1518                return BSP_VMEDMA_STATUS_UNSUP;
1519
1520        dgcs = vmeUniverseReadRegXX(base, UNIV_REGOFF_DGCS);
1521
1522        dgcs &= UNIV_DGCS_STATUS_CLEAR;
1523
1524        if ( 0 == dgcs || UNIV_DGCS_DONE == dgcs )
1525                return BSP_VMEDMA_STATUS_OK;
1526
1527        if ( UNIV_DGCS_ACT & dgcs )
1528                return BSP_VMEDMA_STATUS_BUSY;
1529
1530        if ( UNIV_DGCS_LERR & dgcs )
1531                return BSP_VMEDMA_STATUS_BERR_PCI;
1532
1533        if ( UNIV_DGCS_VERR & dgcs )
1534                return BSP_VMEDMA_STATUS_BERR_VME;
1535
1536        return BSP_VMEDMA_STATUS_OERR;
1537}
1538
1539uint32_t
1540vmeUniverseDmaStatus(int channel)
1541{
1542DFLT_BASE;
1543        return vmeUniverseDmaStatusXX(base, channel);
1544}
1545
1546/* bspVmeDmaList driver interface implementation */
1547
1548/* Cannot use VmeUniverseDMAPacketRec because st_le32 expects unsigned *
1549 * and we get 'alias' warnings when we submit uint32_t *
1550 */
1551
1552typedef volatile unsigned LERegister1;
1553
1554typedef struct VmeUniverseDmaListDescRec_ {
1555        LERegister1     dctl;
1556        LERegister1     dtbc;
1557        LERegister1     dla;
1558        LERegister1     dummy1;
1559        LERegister1     dva;
1560        LERegister1     dummy2;
1561        LERegister1     dcpp;
1562        LERegister1     dummy3;
1563} VmeUniverseDmaListDescRec
1564__attribute__((aligned(32), __may_alias__));
1565typedef VmeUniverseDmaListDescRec *VmeUniverseDmaListDesc;
1566
1567static void     uni_desc_init  (DmaDescriptor);
1568static int      uni_desc_setup (DmaDescriptor, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
1569static void     uni_desc_setnxt(DmaDescriptor, DmaDescriptor);
1570static void     uni_desc_dump  (DmaDescriptor);
1571static int      uni_desc_start (volatile void *controller_addr, int channel, DmaDescriptor p);
1572
1573VMEDmaListClassRec      vmeUniverseDmaListClass = {
1574        desc_size:  sizeof(VmeUniverseDMAPacketRec),
1575        desc_align: 32,
1576        freeList:   0,
1577        desc_alloc: 0,
1578        desc_free:  0,
1579        desc_init:  uni_desc_init,
1580        desc_setnxt:uni_desc_setnxt,
1581        desc_setup: uni_desc_setup,
1582        desc_start: uni_desc_start,
1583        desc_refr:  0,
1584        desc_dump:      uni_desc_dump,
1585};
1586
1587/* gcc complains even if unsigned and uint32 are the same size :-( */
1588
1589static inline void ST_LE32(volatile uint32_t *a, uint32_t v)
1590{
1591        st_le32( (volatile unsigned *)a, v);
1592}
1593
1594static void     uni_desc_init  (DmaDescriptor p)
1595{
1596VmeUniverseDmaListDesc d = p;
1597        st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL );
1598}
1599
1600static void     uni_desc_setnxt(DmaDescriptor p, DmaDescriptor n)
1601{
1602VmeUniverseDmaListDesc d = p;
1603        if ( 0 == n ) {
1604                st_le32( &d->dcpp, UNIV_DCPP_IMG_NULL );
1605        } else {
1606                st_le32( &d->dcpp, BSP_LOCAL2PCI_ADDR( (uint32_t)n));
1607        }
1608}
1609
1610static int
1611uni_desc_setup (
1612        DmaDescriptor p,
1613        uint32_t        attr_mask,
1614        uint32_t        xfer_mode,
1615        uint32_t        pci_addr,
1616        uint32_t        vme_addr,
1617        uint32_t        n_bytes)
1618{
1619VmeUniverseDmaListDesc d = p;
1620LERegister1            dctl;
1621
1622        if ( BSP_VMEDMA_MSK_ATTR & attr_mask ) {
1623                dctl = xfer_mode2dctl(xfer_mode);
1624
1625                if ( (uint32_t)BSP_VMEDMA_STATUS_UNSUP == dctl )
1626                        return -1;
1627
1628                st_le32( &d->dctl, dctl );
1629        }
1630
1631        /* Last 3 bits of src & destination addresses must be the same.
1632         * For sake of simplicity we enforce (stricter) 8-byte alignment
1633         */
1634
1635        if ( BSP_VMEDMA_MSK_PCIA & attr_mask ) {
1636                if ( pci_addr & 0x7 )
1637                        return -1;
1638
1639                st_le32( &d->dla, pci_addr );
1640        }
1641
1642        if ( BSP_VMEDMA_MSK_VMEA & attr_mask ) {
1643                if ( vme_addr & 0x7 )
1644                        return -1;
1645
1646                st_le32( &d->dva, vme_addr );
1647        }
1648
1649        if ( BSP_VMEDMA_MSK_BCNT & attr_mask ) {
1650                st_le32( &d->dtbc, n_bytes );
1651        }
1652
1653        return 0;
1654}
1655
1656static int      uni_desc_start
1657(volatile void *controller_addr, int channel, DmaDescriptor p)
1658{
1659volatile LERegister *base = controller_addr;
1660uint32_t                        dgcs;
1661
1662        if ( !base )
1663                base = vmeUniverse0BaseAddr;
1664
1665        dgcs  = vmeUniverseReadRegXX( base, UNIV_REGOFF_DGCS );
1666
1667        if ( UNIV_DGCS_ACT & dgcs )
1668                return BSP_VMEDMA_STATUS_BUSY;
1669
1670        if ( !p ) {
1671                /* Chain bit is cleared by non-linked-list start command
1672                 * but do this anyways...
1673                 */
1674                dgcs &= ~UNIV_DGCS_CHAIN;
1675                vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs);
1676                return 0;
1677        }
1678
1679        /* clear status and set CHAIN bit */
1680        dgcs |= UNIV_DGCS_CHAIN;
1681
1682        vmeUniverseWriteRegXX( base, UNIV_REGOFF_DGCS, dgcs);
1683
1684    /* make sure count is 0 for linked list DMA */
1685    vmeUniverseWriteRegXX( base, 0x0, UNIV_REGOFF_DTBC);
1686
1687    /* set the address of the descriptor chain */
1688    vmeUniverseWriteRegXX( base, BSP_LOCAL2PCI_ADDR((uint32_t)p), UNIV_REGOFF_DCPP);
1689
1690        /* and GO */
1691        dgcs |= UNIV_DGCS_GO;
1692        vmeUniverseWriteReg(dgcs, UNIV_REGOFF_DGCS);
1693
1694        return 0;
1695}
1696
1697static void
1698uni_desc_dump(DmaDescriptor p)
1699{
1700VmeUniverseDmaListDesc d = p;
1701LERegister1            dcpp = ld_le32(&d->dcpp);
1702
1703        printf("   DLA: 0x%08x\n", ld_le32(&d->dla));
1704        printf("   DVA: 0x%08x\n", ld_le32(&d->dva));
1705        printf("  DCPP: 0x%08x%s\n", dcpp, (dcpp & UNIV_DCPP_IMG_NULL) ? " (LAST)" : "");
1706        printf("   CTL: 0x%08x\n", ld_le32(&d->dctl));
1707        printf("   TBC: 0x%08x\n", ld_le32(&d->dtbc));
1708}
1709
1710/* RTEMS interrupt subsystem */
1711
1712#include <bsp/irq.h>
1713
1714typedef struct
1715UniverseIRQEntryRec_ {
1716                VmeUniverseISR  isr;
1717                void                    *usrData;
1718} UniverseIRQEntryRec, *UniverseIRQEntry;
1719
1720static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};
1721
1722int          vmeUniverseIrqMgrInstalled = 0;
1723
1724volatile LERegister *vmeUniverseRegBase = 0;
1725
1726/* We support 4 wires between universe + PIC */
1727
1728#define UNIV_NUM_WIRES 4
1729
1730static volatile unsigned long   wire_mask[UNIV_NUM_WIRES]     = {0};
1731/* wires are offset by 1 so we can initialize the wire table to all zeros */
1732static int                                              universe_wire[UNIV_NUM_WIRES] = {0};
1733
1734static int
1735lvl2bit(unsigned int level)
1736{
1737int shift = -1;
1738        if ( level >= UNIV_DMA_INT_VEC && level <= UNIV_LM3_INT_VEC ) {
1739                shift = 8 + (level-UNIV_DMA_INT_VEC);
1740        } else if ( UNIV_VOWN_INT_VEC == level ) {
1741                shift = 0;
1742        } else if ( 1 <= level && level <=7 ) {
1743                shift = level;
1744        } else {
1745                /* invalid level */
1746        }
1747        return shift;
1748}
1749
1750int
1751vmeUniverseIntRoute(unsigned int level, unsigned int pin)
1752{
1753int                             i, shift;
1754unsigned long   mask, mapreg, flags, wire;
1755
1756        if ( pin >= UNIV_NUM_WIRES || ! universe_wire[pin] || !vmeUniverseIrqMgrInstalled )
1757                return -1;
1758
1759        if ( (shift = lvl2bit(level)) < 0 ) {
1760                return -1; /* invalid level */
1761        }
1762
1763        mask = 1<<shift;
1764
1765        /* calculate the mapping register and contents */
1766        if ( shift < 8 ) {
1767                mapreg = UNIV_REGOFF_LINT_MAP0;
1768        } else if ( shift < 16 ) {
1769                shift -= 8;
1770                mapreg = UNIV_REGOFF_LINT_MAP1;
1771        } else if ( shift < 24 ) {
1772                shift -= 16;
1773                mapreg = UNIV_REGOFF_LINT_MAP2;
1774        } else {
1775                return -1;
1776        }
1777
1778        shift <<=2;
1779
1780        /* wires are offset by 1 so we can initialize the wire table to all zeros */
1781        wire = (universe_wire[pin]-1) << shift;
1782
1783rtems_interrupt_disable(flags);
1784
1785        for ( i = 0; i<UNIV_NUM_WIRES; i++ ) {
1786                wire_mask[i] &= ~mask;
1787        }
1788        wire_mask[pin] |= mask;
1789
1790        mask = vmeUniverseReadReg(mapreg) & ~ (0xf<<shift);
1791        mask |= wire;
1792        vmeUniverseWriteReg( mask, mapreg );
1793
1794rtems_interrupt_enable(flags);
1795        return 0;
1796}
1797
1798VmeUniverseISR
1799vmeUniverseISRGet(unsigned long vector, void **parg)
1800{
1801unsigned long             flags;
1802VmeUniverseISR                    rval = 0;
1803volatile UniverseIRQEntry *pe  = universeHdlTbl + vector;
1804
1805        if ( vector>=UNIV_NUM_INT_VECS || ! *pe )
1806                return 0;
1807
1808        rtems_interrupt_disable(flags);
1809                if ( *pe ) {
1810                        if (parg)
1811                                *parg=(*pe)->usrData;
1812                        rval = (*pe)->isr;
1813                }
1814        rtems_interrupt_enable(flags);
1815        return rval;
1816}
1817
1818#define SPECIAL_IRQ_MSK  ( ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1) )
1819
1820static void
1821universeSpecialISR(unsigned long status)
1822{
1823register UniverseIRQEntry       ip;
1824register unsigned                       vec;
1825register unsigned long          s;
1826
1827        /* handle all LINT bits except for the 'normal' VME interrupts */
1828
1829        /* clear all detected special interrupts */
1830        vmeUniverseWriteReg( (status & SPECIAL_IRQ_MSK), UNIV_REGOFF_LINT_STAT );
1831
1832        /* do VOWN first */
1833        vec=UNIV_VOWN_INT_VEC;
1834        if ( (status & UNIV_LINT_STAT_VOWN) && (ip=universeHdlTbl[vec]))
1835                ip->isr(ip->usrData,vec);
1836
1837        /* now continue with DMA and scan through all bits;
1838         * we assume the vectors are in the right order!
1839         *
1840         * The initial right shift brings the DMA bit into position 0;
1841         * the loop is left early if there are no more bits set.
1842         */
1843        for ( s = status>>8; s; s >>= 1) {
1844                vec++;
1845                if ( (s&1) && (ip=universeHdlTbl[vec]) )
1846                        ip->isr(ip->usrData,vec);
1847        }
1848
1849/*
1850 *      clear our line in the VINT_STAT register
1851 *  seems to be not neccessary...
1852        vmeUniverseWriteReg(
1853                                        UNIV_VINT_STAT_LINT(specialIrqUnivOut),
1854                                        UNIV_REGOFF_VINT_STAT);
1855 */
1856}
1857
1858/*
1859 * interrupts from VME to PCI seem to be processed more or less
1860 * like this:
1861 *
1862 *
1863 *   VME IRQ ------
1864 *                  & ----- LINT_STAT ----
1865 *                  |                       &  ---------- PCI LINE
1866 *                  |                       |
1867 *                  |                       |
1868 *       LINT_EN ---------------------------
1869 *
1870 *  I.e.
1871 *   - if LINT_EN is disabled, a VME IRQ will not set LINT_STAT.
1872 *   - while LINT_STAT is set, it will pull the PCI line unless
1873 *     masked by LINT_EN.
1874 *   - VINT_STAT(lint_bit) seems to have no effect beyond giving
1875 *     status info.
1876 *
1877 *  Hence, it is possible to
1878 *    - arm (set LINT_EN, routing etc.)
1879 *    - receive an irq (sets. LINT_STAT)
1880 *    - the ISR then:
1881 *                * clears LINT_EN, results in masking LINT_STAT (which
1882 *                  is still set to prevent another VME irq at the same
1883 *                  level to be ACKEd by the universe.
1884 *                * do PCI_EOI to allow nesting of higher VME irqs.
1885 *                  (previous step also cleared LINT_EN of lower levels)
1886 *                * when the handler returns, clear LINT_STAT
1887 *                * re-enable setting LINT_EN.
1888 */
1889
1890static void
1891universeVMEISR(rtems_irq_hdl_param arg)
1892{
1893int                                     pin = (int)arg;
1894UniverseIRQEntry        ip;
1895unsigned long           msk,lintstat,status;
1896int                                     lvl;
1897#if defined(BSP_PIC_DO_EOI)
1898unsigned long           linten;
1899#endif
1900
1901                /* determine the highest priority IRQ source */
1902                lintstat  = vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
1903
1904                /* only handle interrupts routed to this pin */
1905                lintstat &= wire_mask[pin];
1906
1907#ifdef __PPC__
1908                asm volatile("cntlzw %0, %1":"=r"(lvl):"r"(lintstat & ~SPECIAL_IRQ_MSK));
1909                lvl = 31-lvl;
1910                msk = 1<<lvl;
1911#else
1912                for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7;
1913                         lvl>0;
1914                         lvl--, msk>>=1) {
1915                        if (lintstat & msk) break;
1916                }
1917#endif
1918
1919#ifndef BSP_PIC_DO_EOI  /* Software priorities not supported */
1920
1921                if ( (status = (lintstat & SPECIAL_IRQ_MSK)) )
1922                        universeSpecialISR( status );
1923
1924                if ( lvl <= 0)
1925                        return;
1926
1927#else
1928                if ( lvl <= 0 ) {
1929                                /* try the special handler */
1930                                universeSpecialISR( lintstat & SPECIAL_IRQ_MSK );
1931
1932                                /*
1933                                 * let the pic end this cycle
1934                                 */
1935                                if ( 0 == pin )
1936                                        BSP_PIC_DO_EOI;
1937
1938                                return;
1939                }
1940                linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
1941
1942                /* mask this and all lower levels that are routed to the same pin */
1943                vmeUniverseWriteReg(
1944                                                linten & ~( ((msk<<1)-UNIV_LINT_STAT_VIRQ1) & wire_mask[pin]),
1945                                                UNIV_REGOFF_LINT_EN
1946                                                );
1947
1948                /* end this interrupt
1949                 * cycle on the PCI bus, so higher level interrupts can be
1950                 * caught from now on...
1951                 */
1952                if ( 0 == pin )
1953                        BSP_PIC_DO_EOI;
1954#endif
1955
1956                /* get vector and dispatch handler */
1957                status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2));
1958                /* determine the highest priority IRQ source */
1959
1960                if (status & UNIV_VIRQ_ERR) {
1961                                /* TODO: log error message - RTEMS has no logger :-( */
1962#ifdef BSP_PIC_DO_EOI
1963                        linten &= ~msk;
1964#else
1965                        vmeUniverseIntDisable(lvl);
1966#endif
1967                        printk("vmeUniverse ISR: error read from STATID register; (level: %i) STATID: 0x%08x -- DISABLING\n", lvl, status);
1968                } else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
1969#ifdef BSP_PIC_DO_EOI
1970                        linten &= ~msk;
1971#else
1972                        vmeUniverseIntDisable(lvl);
1973#endif
1974                                /* TODO: log error message - RTEMS has no logger :-( */
1975                        printk("vmeUniverse ISR: no handler installed for this vector; (level: %i) STATID: 0x%08x -- DISABLING\n", lvl, status);
1976                } else {
1977                                /* dispatch handler, it must clear the IRQ at the device */
1978                                ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
1979
1980                                /* insert a VME read operation to flush fifo, making sure all user write-ops complete */
1981#ifdef __PPC__
1982                                /* courtesy to disobedient users who don't use I/O ops */
1983                                asm volatile("eieio");
1984#endif
1985                                READ_LE0(vmeUniverseRegBase);
1986#ifdef __PPC__
1987                                /* make sure this is ordered before re-enabling */
1988                                asm volatile("eieio");
1989#endif
1990                }
1991
1992                /* clear this interrupt level; allow the universe to handler further interrupts */
1993                vmeUniverseWriteReg(msk, UNIV_REGOFF_LINT_STAT);
1994
1995/*
1996 *  this seems not to be necessary; we just leave the
1997 *  bit set to save a couple of instructions...
1998                vmeUniverseWriteReg(
1999                                        UNIV_VINT_STAT_LINT(vmeIrqUnivOut),
2000                                        UNIV_REGOFF_VINT_STAT);
2001*/
2002
2003#ifdef BSP_PIC_DO_EOI
2004
2005                /* re-enable the previous level */
2006                vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN);
2007#endif
2008}
2009
2010
2011/* STUPID API */
2012static void
2013my_no_op(const rtems_irq_connect_data * arg)
2014{}
2015
2016static int
2017my_isOn(const rtems_irq_connect_data *arg)
2018{
2019                return (int)vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
2020}
2021
2022typedef struct {
2023        int uni_pin, pic_pin;
2024} IntRoute;
2025
2026static void
2027connectIsr(int shared, rtems_irq_hdl isr, int pic_line, int pic_pin)
2028{
2029rtems_irq_connect_data  aarrggh;
2030        aarrggh.on     = my_no_op; /* at _least_ they could check for a 0 pointer */
2031        aarrggh.off    = my_no_op;
2032        aarrggh.isOn   = my_isOn;
2033        aarrggh.hdl    = isr;
2034        aarrggh.handle = (rtems_irq_hdl_param)pic_pin;
2035        aarrggh.name   = pic_line;
2036
2037        if ( shared ) {
2038#if BSP_SHARED_HANDLER_SUPPORT > 0
2039                if (!BSP_install_rtems_shared_irq_handler(&aarrggh))
2040                        BSP_panic("unable to install vmeUniverse shared irq handler");
2041#else
2042                uprintf(stderr,"vmeUniverse: WARNING: your BSP doesn't support sharing interrupts\n");
2043                if (!BSP_install_rtems_irq_handler(&aarrggh))
2044                        BSP_panic("unable to install vmeUniverse irq handler");
2045#endif
2046        } else {
2047                if (!BSP_install_rtems_irq_handler(&aarrggh))
2048                        BSP_panic("unable to install vmeUniverse irq handler");
2049        }
2050}
2051
2052#ifndef BSP_EARLY_PROBE_VME
2053#define BSP_EARLY_PROBE_VME(addr)       \
2054        (                                                                                                                                                                                                                       \
2055                ((PCI_DEVICE_UNIVERSEII << 16) | PCI_VENDOR_TUNDRA ) == READ_LE( ((volatile LERegister*)(addr)), 0 )    \
2056        )
2057#endif
2058
2059/* Check if there is a vme address/as is mapped in any of the outbound windows
2060 * and look for the PCI vendordevice ID there.
2061 * RETURNS: -1 on error (no mapping or probe failure), outbound window # (0..7)
2062 *          on success. Address translated into CPU address is returned in *pcpu_addr.
2063 */
2064static int
2065mappedAndProbed(unsigned long vme_addr, unsigned as, unsigned long *pcpu_addr)
2066{
2067int j;
2068char *regtype = (as & VME_AM_MASK) == VME_AM_CSR ? "CSR" : "CRG";
2069
2070        /* try to find mapping */
2071        if ( 0 > (j = xlateFindPort(
2072                                vmeUniverse0BaseAddr,
2073                                1, 0,
2074                                as | VME_MODE_AS_MATCH,
2075                                vme_addr,
2076                                pcpu_addr ) ) ) {
2077                        uprintf(stderr,"vmeUniverse - Unable to find mapping for %s VME base (0x%08x)\n", regtype, vme_addr);
2078                        uprintf(stderr,"              in outbound windows.\n");
2079        } else {
2080                        /* found a slot number; probe it */
2081                        *pcpu_addr = BSP_PCI2LOCAL_ADDR( *pcpu_addr );
2082                        if ( BSP_EARLY_PROBE_VME(*pcpu_addr) ) {
2083                                uprintf(stderr,"vmeUniverse - IRQ manager using VME %s to flush FIFO\n", regtype);
2084                                return j;
2085                        } else {
2086                                uprintf(stderr,"vmeUniverse - Found slot info but detection of universe in VME %s space failed\n", regtype);
2087                        }
2088        }
2089        return -1;
2090}
2091
2092
2093int
2094vmeUniverseInstallIrqMgrAlt(int flags, int uni_pin0, int pic_pin0, ...)
2095{
2096int             rval;
2097va_list ap;
2098        va_start(ap, pic_pin0);
2099        rval = vmeUniverseInstallIrqMgrVa(flags, uni_pin0, pic_pin0, ap);
2100        va_end(ap);
2101        return rval;
2102}
2103
2104int
2105vmeUniverseInstallIrqMgrVa(int flags, int uni_pin0, int pic_pin0, va_list ap)
2106{
2107int     i,j, specialPin, uni_pin[UNIV_NUM_WIRES+1], pic_pin[UNIV_NUM_WIRES];
2108unsigned long cpu_base, vme_reg_base;
2109
2110        if (vmeUniverseIrqMgrInstalled)                return -4;
2111
2112        /* check parameters */
2113
2114        if ( uni_pin0 < 0 || uni_pin0 > 7 )            return -1;
2115
2116        uni_pin[0] = uni_pin0;
2117        pic_pin[0] = pic_pin0 < 0 ? vmeUniverse0PciIrqLine : pic_pin0;
2118        i = 1;
2119        while ( (uni_pin[i] = va_arg(ap, int)) >= 0 ) {
2120               
2121                if ( i >= UNIV_NUM_WIRES ) {
2122                                                               return -5;
2123                }
2124
2125                pic_pin[i] = va_arg(ap,int);
2126
2127                if ( uni_pin[i] > 7 ) {
2128                                                   return -2;
2129                }
2130                if ( pic_pin[i] < 0 ) {
2131                                                   return -3;
2132                }
2133                i++;
2134        }
2135
2136        /* all routings must be different */
2137        for ( i=0; uni_pin[i] >= 0; i++ ) {
2138                for ( j=i+1; uni_pin[j] >= 0; j++ ) {
2139                        if ( uni_pin[j] == uni_pin[i] )        return -6;
2140                        if ( pic_pin[j] == pic_pin[i] )        return -7;
2141                }
2142        }
2143
2144        if ( flags & VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND ) {
2145
2146                /* Find registers on VME so the ISR can issue a read to flush the FIFO */
2147                uprintf(stderr,"vmeUniverse IRQ manager: looking for registers on VME...\n");
2148
2149                /* NOTE: The universe [unlike the Tsi148] doesn't know about geographical
2150                 *       addressing but the MotLoad firmware [mvme5500] is kind enough to
2151                 *       program VCSR_BS based on the board's geographical address for us :-)
2152                 */
2153                if ( ( i = ((READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VCSR_BS ) >> 27) & 0x1f ) ) > 0 ) {
2154                        uprintf(stderr,"Trying to find CSR on VME...\n");
2155                        vme_reg_base = i*0x80000 + UNIV_CSR_OFFSET;
2156                        i = mappedAndProbed( vme_reg_base, VME_AM_CSR , &cpu_base);
2157                        if ( i >= 0 )
2158                                vmeUniverseRegCSR = 1;
2159                } else {
2160                        i = -1;
2161                }
2162
2163                if ( -1 == i ) {
2164
2165                        uprintf(stderr,"Trying to find CRG on VME...\n");
2166
2167                        /* Next we see if the CRG block is mapped to VME */
2168
2169                        if ( UNIV_VRAI_CTL_EN & (j = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_CTL )) ) {
2170                                switch ( j & UNIV_VRAI_CTL_VAS_MSK ) {
2171                                        case UNIV_VRAI_CTL_VAS_A16 : i = VME_AM_SUP_SHORT_IO; break;
2172                                        case UNIV_VRAI_CTL_VAS_A24 : i = VME_AM_STD_SUP_DATA; break;
2173                                        case UNIV_VRAI_CTL_VAS_A32 : i = VME_AM_EXT_SUP_DATA; break;
2174                                        default:
2175                                                                                                 break;
2176                                }
2177                                vme_reg_base = READ_LE( vmeUniverse0BaseAddr, UNIV_REGOFF_VRAI_BS ) & ~(UNIV_CRG_SIZE - 1);
2178                        }
2179
2180                        if ( -1 == i ) {
2181                        } else {
2182                                i = mappedAndProbed( vme_reg_base, (i & VME_AM_MASK), &cpu_base );
2183                        }
2184                }
2185
2186                if ( i < 0 ) {
2187                        if ( mapOverAll( vmeUniverse0BaseAddr, 1, hasPWENWindow, 0 ) ) {
2188                                uprintf(stderr,"vmeUniverse IRQ manager - BSP configuration error: registers not found on VME\n");
2189                                uprintf(stderr,"(should open outbound window to CSR space or map CRG [vmeUniverseMapCRG()])\n");
2190                                uprintf(stderr,"Falling back to PCI but you might experience spurious VME interrupts; read a register\n");
2191                                uprintf(stderr,"back from user ISR to flush universe FIFO as a work-around or\n");
2192                                uprintf(stderr,"make sure ISR accesses device using a window with posted-writes disabled\n");
2193                        } else {
2194                                uprintf(stderr,"vmeUniverse IRQ manager - registers not found on VME; falling back to PCI\n");
2195                        }
2196                        vmeUniverseRegBase = vmeUniverse0BaseAddr;
2197                        vmeUniverseRegPort = -1;
2198                } else {
2199                        vmeUniverseRegBase = (volatile LERegister*)cpu_base;
2200                        vmeUniverseRegPort = i;
2201                }
2202        } else {
2203                vmeUniverseRegBase = vmeUniverse0BaseAddr;
2204                vmeUniverseRegPort = -1;
2205        }
2206
2207        /* give them a chance to override buggy PCI info */
2208        if ( pic_pin[0] >= 0 && vmeUniverse0PciIrqLine != pic_pin[0] ) {
2209                uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
2210                                pic_pin[0]);
2211                vmeUniverse0PciIrqLine=pic_pin[0];
2212        }
2213
2214        for ( i = 0; uni_pin[i] >= 0; i++ ) {
2215                /* offset wire # by one so we can initialize to 0 == invalid */
2216                universe_wire[i] = uni_pin[i] + 1;
2217                connectIsr((flags & VMEUNIVERSE_IRQ_MGR_FLAG_SHARED), universeVMEISR, pic_pin[i], i);
2218        }
2219
2220        specialPin = uni_pin[1] >= 0 ? 1 : 0;
2221
2222        /* setup routing */
2223
2224        /* IntRoute checks for mgr being installed */
2225        vmeUniverseIrqMgrInstalled=1;
2226
2227        /* route 7 VME irqs to first / 'normal' pin */
2228        for ( i=1; i<8; i++ )
2229                vmeUniverseIntRoute( i, 0 );
2230        for ( i=UNIV_VOWN_INT_VEC; i<=UNIV_LM3_INT_VEC; i++ ) {
2231                if ( vmeUniverseIntRoute( i, specialPin ) )
2232                        printk("Routing lvl %i -> wire # %i failed\n", i, specialPin);
2233        }
2234
2235        return 0;
2236}
2237
2238int
2239vmeUniverseInstallIrqMgr(int vmeIrqUnivOut,
2240                                                 int vmeIrqPicLine,
2241                                                 int specialIrqUnivOut,
2242                                                 int specialIrqPicLine)
2243{
2244        return vmeUniverseInstallIrqMgrAlt(
2245                                0,      /* bwds compat. */
2246                                vmeIrqUnivOut, vmeIrqPicLine,
2247                                specialIrqUnivOut, specialIrqPicLine,
2248                                -1);
2249}
2250
2251int
2252vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
2253{
2254UniverseIRQEntry          ip;
2255unsigned long             flags;
2256volatile UniverseIRQEntry *pe;
2257
2258                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
2259                                return -1;
2260
2261                pe = universeHdlTbl + vector;
2262
2263                if (*pe || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
2264                                return -1;
2265
2266                ip->isr=hdl;
2267                ip->usrData=arg;
2268
2269        rtems_interrupt_disable(flags);
2270                if ( *pe ) {
2271                        /* oops; someone intervened */
2272                        rtems_interrupt_enable(flags);
2273                        free(ip);
2274                        return -1;
2275                }
2276                *pe = ip;
2277        rtems_interrupt_enable(flags);
2278                return 0;
2279}
2280
2281int
2282vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
2283{
2284UniverseIRQEntry          ip;
2285unsigned long             flags;
2286volatile UniverseIRQEntry *pe;
2287
2288                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
2289                                return -1;
2290
2291                pe = universeHdlTbl + vector;
2292
2293        rtems_interrupt_disable(flags);
2294                ip = *pe;
2295                if (!ip || ip->isr!=hdl || ip->usrData!=arg) {
2296                        rtems_interrupt_enable(flags);
2297                        return -1;
2298                }
2299                *pe = 0;
2300        rtems_interrupt_enable(flags);
2301                free(ip);
2302                return 0;
2303}
2304
2305static int
2306intDoEnDis(unsigned int level, int dis)
2307{
2308unsigned long   flags, v;
2309int                             shift;
2310
2311        if (  ! vmeUniverseIrqMgrInstalled || (shift = lvl2bit(level)) < 0 )
2312                return -1;
2313
2314        v = 1<<shift;
2315
2316        if ( !dis )
2317                return vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & v ? 1 : 0;
2318
2319        rtems_interrupt_disable(flags);
2320        if ( dis<0 )
2321                vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) & ~v, UNIV_REGOFF_LINT_EN );
2322        else {
2323                vmeUniverseWriteReg( vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) |  v, UNIV_REGOFF_LINT_EN  );
2324        }
2325        rtems_interrupt_enable(flags);
2326                return 0;
2327}
2328
2329int
2330vmeUniverseIntEnable(unsigned int level)
2331{
2332        return intDoEnDis(level, 1);
2333}
2334
2335int
2336vmeUniverseIntDisable(unsigned int level)
2337{
2338        return intDoEnDis(level, -1);
2339}
2340
2341int
2342vmeUniverseIntIsEnabled(unsigned int level)
2343{
2344        return intDoEnDis(level, 0);
2345}
2346
2347/* Loopback test of VME/universe interrupts */
2348
2349typedef struct {
2350        rtems_id        q;
2351        int                     l;
2352} LoopbackTstArgs;
2353
2354static void
2355loopbackTstIsr(void *arg, unsigned long vector)
2356{
2357LoopbackTstArgs *pa = arg;
2358        if ( RTEMS_SUCCESSFUL != rtems_message_queue_send(pa->q, (void*)&vector, sizeof(vector)) ) {
2359                /* Overrun ? */
2360                printk("vmeUniverseIntLoopbackTst: (ISR) message queue full / overrun ? disabling IRQ level %i\n", pa->l);
2361                vmeUniverseIntDisable(pa->l);
2362        }
2363}
2364
2365int
2366vmeUniverseIntLoopbackTst(int level, unsigned vector)
2367{
2368DFLT_BASE;
2369rtems_status_code       sc;
2370rtems_id                        q = 0;
2371int                                     installed = 0;
2372int                                     i, err = 0;
2373int                                     doDisable = 0;
2374uint32_t                        size;
2375unsigned long           msg;
2376char *                          irqfmt  = "VME IRQ @vector %3i %s";
2377char *                          iackfmt = "VME IACK            %s";
2378LoopbackTstArgs         a;
2379
2380        CHECK_DFLT_BASE(base);
2381
2382        /* arg check */
2383        if ( level < 1 || level > 7 || vector > 255 )
2384                return -1;
2385
2386        if ( UNIV_REV(base) < 2 && vector != 0 ) {
2387                fprintf(stderr,
2388                        "vmeUniverseIntLoopbackTst(): Universe 1 has a bug. IACK in response to\n");
2389                fprintf(stderr,
2390                        "self-generated VME interrupt yields always a zero vector. As a workaround,\n");
2391                fprintf(stderr,
2392                        "use vector 0, please.\n");
2393                return -1;
2394        }
2395
2396        /* Create message queue */
2397        if ( RTEMS_SUCCESSFUL != (sc=rtems_message_queue_create(
2398                                                                        rtems_build_name('t' ,'U','I','I'),
2399                                                                        4,
2400                                                                        sizeof(unsigned long),
2401                                                                        0,  /* default attributes: fifo, local */
2402                                                                        &q)) ) {
2403                rtems_error(sc, "vmeUniverseIntLoopbackTst: Unable to create message queue");
2404                goto bail;
2405        }
2406
2407        a.q = q;
2408        a.l = level;
2409
2410        /* Install handlers */
2411        if ( vmeUniverseInstallISR(vector, loopbackTstIsr, (void*)&a) ) {
2412                fprintf(stderr,"Unable to install VME ISR to vector %i\n",vector);
2413                goto bail;
2414        }
2415        installed++;
2416        if ( vmeUniverseInstallISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a) ) {
2417                fprintf(stderr,"Unable to install VME ISR to IACK special vector %i\n",UNIV_VME_SW_IACK_INT_VEC);
2418                goto bail;
2419        }
2420        installed++;
2421
2422        if ( !vmeUniverseIntIsEnabled(level) && 0==vmeUniverseIntEnable(level) )
2423                doDisable = 1;
2424       
2425        /* make sure there are no pending interrupts */
2426        vmeUniverseWriteReg( UNIV_LINT_STAT_SW_IACK,  UNIV_REGOFF_LINT_STAT );
2427
2428        if ( vmeUniverseIntEnable( UNIV_VME_SW_IACK_INT_VEC ) ) {
2429                fprintf(stderr,"Unable to enable IACK interrupt\n");
2430                goto bail;
2431        }       
2432
2433        printf("vmeUniverse VME interrupt loopback test; STARTING...\n");
2434        printf(" --> asserting VME IRQ level %i\n", level);
2435        vmeUniverseIntRaise(level, vector);
2436
2437        for ( i = 0; i< 3; i++ ) {
2438        sc = rtems_message_queue_receive(
2439                            q,
2440                            &msg,
2441                            &size,
2442                            RTEMS_WAIT,
2443                            20);
2444                if ( sc ) {
2445                        if ( RTEMS_TIMEOUT == sc && i>1 ) {
2446                                /* OK; we dont' expect more to happen */
2447                                sc = 0;
2448                        } else {
2449                                rtems_error(sc,"Error waiting for interrupts");
2450                        }
2451                        break;
2452                }
2453                if ( msg == vector ) {
2454                        if ( !irqfmt ) {
2455                                printf("Excess VME IRQ received ?? -- BAD\n");
2456                                err = 1;
2457                        } else {
2458                                printf(irqfmt, vector, "received -- PASSED\n");
2459                                irqfmt = 0;
2460                        }
2461                } else if ( msg == UNIV_VME_SW_IACK_INT_VEC ) {
2462                        if ( !iackfmt ) {
2463                                printf("Excess VME IACK received ?? -- BAD\n");
2464                                err = 1;
2465                        } else {
2466                                printf(iackfmt, "received -- PASSED\n");
2467                                iackfmt = 0;
2468                        }
2469                } else {
2470                        printf("Unknown IRQ (vector %lu) received -- BAD\n", msg);
2471                        err = 1;
2472                }
2473        }
2474
2475
2476        /* Missing anything ? */
2477        if ( irqfmt ) {
2478                printf(irqfmt,vector, "MISSED -- BAD\n");
2479                err = 1;
2480        }
2481        if ( iackfmt ) {
2482                printf(iackfmt, "MISSED -- BAD\n");
2483                err = 1;
2484        }
2485
2486        printf("FINISHED.\n");
2487
2488bail:
2489        if ( doDisable )
2490                vmeUniverseIntDisable(level);
2491        vmeUniverseIntDisable( UNIV_VME_SW_IACK_INT_VEC );
2492        if ( installed > 0 )
2493                vmeUniverseRemoveISR(vector, loopbackTstIsr, (void*)&a);
2494        if ( installed > 1 )
2495                vmeUniverseRemoveISR(UNIV_VME_SW_IACK_INT_VEC, loopbackTstIsr, (void*)&a);
2496        if ( q )
2497                rtems_message_queue_delete(q);
2498
2499        return sc ? sc : err;
2500}
2501
2502#endif
Note: See TracBrowser for help on using the repository browser.