source: rtems/bsps/powerpc/shared/vme/vmeUniverse.c @ 8a640bf

Last change on this file since 8a640bf was 8a640bf, checked in by Sebastian Huber <sebastian.huber@…>, on 02/25/22 at 09:42:04

bsps/powerpc: Do no use Newlib internals

The struct _reent::sdidinit member was renamed in a recent Newlib.

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