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

5
Last change on this file since 1c193a2 was 1c193a2, checked in by Sebastian Huber <sebastian.huber@…>, on 11/21/17 at 10:43:13

powerpc: Replace BSP_panic() with rtems_panic()

Due to a new rtems_panic() implementation, it is possible to replace the
PowerPC-specific BSP_panic() with rtems_panic(). Remove BSP_panic()
implementations.

Close #3245.

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