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

4.104.114.84.95
Last change on this file since 0943f48d was 9c25cad, checked in by Joel Sherrill <joel.sherrill@…>, on 03/04/05 at 21:49:05

2005-03-04 Joel Sherrill <joel@…>

  • vmeUniverse/README.porting, vmeUniverse/vmeUniverse.c: Make PCI initialize function part of the unified PCI API as pci_initialize().
  • Property mode set to 100644
File size: 29.5 KB
Line 
1/* $Id$ */
2
3/* Routines to configure the VME interface
4 * Author: Till Straumann <strauman@slac.stanford.edu>
5 *         Nov 2000, Oct 2001, Jan 2002
6 */
7
8#include <stdio.h>
9#include <stdarg.h>
10#include "vmeUniverse.h"
11
12#define UNIV_NUM_MPORTS         8 /* number of master ports */
13#define UNIV_NUM_SPORTS         8 /* number of slave ports */
14
15#define PCI_VENDOR_TUNDRA       0x10e3
16#define PCI_DEVICE_UNIVERSEII   0
17#define PCI_UNIVERSE_BASE0      0x10
18#define PCI_UNIVERSE_BASE1      0x14
19
20#define UNIV_REGOFF_PCITGT0_CTRL 0x100
21#define UNIV_REGOFF_PCITGT4_CTRL 0x1a0
22#define UNIV_REGOFF_VMESLV0_CTRL 0xf00
23#define UNIV_REGOFF_VMESLV4_CTRL 0xf90
24
25#define UNIV_CTL_VAS16          (0x00000000)
26#define UNIV_CTL_VAS24          (0x00010000)
27#define UNIV_CTL_VAS32          (0x00020000)
28#define UNIV_CTL_VAS            (0x00070000)
29
30#define UNIV_MCTL_EN            (0x80000000)
31#define UNIV_MCTL_PWEN          (0x40000000)
32#define UNIV_MCTL_PGM           (0x00004000)
33#define UNIV_MCTL_VCT           (0x00000100)
34#define UNIV_MCTL_SUPER         (0x00001000)
35#define UNIV_MCTL_VDW32         (0x00800000)
36#define UNIV_MCTL_VDW64         (0x00c00000)
37
38#define UNIV_MCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_MCTL_PGM | UNIV_MCTL_SUPER)
39
40#define UNIV_SCTL_EN            (0x80000000)
41#define UNIV_SCTL_PWEN          (0x40000000)
42#define UNIV_SCTL_PREN          (0x20000000)
43#define UNIV_SCTL_PGM           (0x00800000)
44#define UNIV_SCTL_DAT           (0x00400000)
45#define UNIV_SCTL_SUPER         (0x00200000)
46#define UNIV_SCTL_USER          (0x00100000)
47
48#define UNIV_SCTL_AM_MASK       (UNIV_CTL_VAS | UNIV_SCTL_PGM | UNIV_SCTL_DAT | UNIV_SCTL_USER | UNIV_SCTL_SUPER)
49
50/* we rely on a vxWorks definition here */
51#define VX_AM_SUP               4
52
53#ifdef __rtems__
54
55#include <stdlib.h>
56#include <rtems/bspIo.h>        /* printk */
57#include <bsp/pci.h>
58#include <bsp.h>
59
60/* allow the BSP to override the default routines */
61#ifndef BSP_PCI_FIND_DEVICE
62#define BSP_PCI_FIND_DEVICE             pci_find_by_devid
63#endif
64#ifndef BSP_PCI_CONFIG_IN_LONG
65#define BSP_PCI_CONFIG_IN_LONG  pci_read_config_dword
66#endif
67#ifndef BSP_PCI_CONFIG_IN_BYTE
68#define BSP_PCI_CONFIG_IN_BYTE  pci_read_config_byte
69#endif
70
71typedef unsigned int pci_ulong;
72#define PCI_TO_LOCAL_ADDR(memaddr) \
73    ((pci_ulong)(memaddr) + PCI_MEM_BASE_ADJUSTMENT)
74
75#elif defined(__vxworks)
76typedef unsigned long pci_ulong;
77#define PCI_TO_LOCAL_ADDR(memaddr) (memaddr)
78#define BSP_PCI_FIND_DEVICE             pciFindDevice
79#define BSP_PCI_CONFIG_IN_LONG  pciConfigInLong
80#define BSP_PCI_CONFIG_IN_BYTE  pciConfigInByte
81#else
82#error "vmeUniverse not ported to this architecture yet"
83#endif
84
85#ifndef PCI_INTERRUPT_LINE
86#define PCI_INTERRUPT_LINE              0x3c
87#endif
88
89volatile LERegister *vmeUniverse0BaseAddr=0;
90int vmeUniverse0PciIrqLine=-1;
91
92#if 0
93/* public access functions */
94volatile LERegister *
95vmeUniverseBaseAddr(void)
96{
97        if (!vmeUniverse0BaseAddr) vmeUniverseInit();
98        return vmeUniverse0BaseAddr;
99}
100
101int
102vmeUniversePciIrqLine(void)
103{
104        if (vmeUniverse0PciIrqLine<0) vmeUniverseInit();
105        return vmeUniverse0PciIrqLine;
106}
107#endif
108
109static inline void
110WRITE_LE(
111        unsigned long val,
112        volatile LERegister    *adrs,
113        unsigned long off)
114{
115#if (__LITTLE_ENDIAN__ == 1)
116        *(volatile unsigned long*)(((unsigned long)adrs)+off)=val;
117#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
118        /* offset is in bytes and MUST not end up in r0 */
119        __asm__ __volatile__("stwbrx %1, %0, %2" :: "b"(off),"r"(val),"r"(adrs));
120#elif defined(__rtems__)
121        st_le32((volatile unsigned long*)(((unsigned long)adrs)+off), val);
122#else
123#error "little endian register writing not implemented"
124#endif
125}
126
127#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)
128#define SYNC __asm__ __volatile__("sync")
129#else
130#define SYNC
131#warning "SYNC instruction unknown for this architecture"
132#endif
133
134/* registers should be mapped to guarded, non-cached memory; hence
135 * subsequent stores are ordered. eieio is only needed to enforce
136 * ordering of loads with respect to stores.
137 */
138#define EIEIO_REG
139
140static inline unsigned long
141READ_LE0(volatile LERegister *adrs)
142{
143#if (__LITTLE_ENDIAN__ == 1)
144        return *(volatile unsigned long *)adrs;
145#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
146register unsigned long rval;
147__asm__ __volatile__("lwbrx %0, 0, %1":"=r"(rval):"r"(adrs));
148        return rval;
149#elif defined(__rtems__)
150        return ld_le32((volatile unsigned long*)adrs);
151#else
152#error "little endian register reading not implemented"
153#endif
154}
155
156static inline unsigned long
157READ_LE(volatile LERegister *adrs, unsigned long off)
158{
159#if (__LITTLE_ENDIAN__ == 1)
160        return  *((volatile LERegister *)(((unsigned long)adrs)+off));
161#elif (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
162register unsigned long rval;
163        /* offset is in bytes and MUST not end up in r0 */
164__asm__ __volatile__("lwbrx %0, %2, %1"
165                                : "=r"(rval)
166                                : "r"(adrs), "b"(off));
167#if 0
168__asm__ __volatile__("eieio");
169#endif
170return rval;
171#else
172return READ_LE0((volatile LERegister *)(((unsigned long)adrs)+off));
173#endif
174}
175
176#define PORT_UNALIGNED(addr,port) \
177        ( (port)%4 ? ((addr) & 0xffff) : ((addr) & 4095) )
178
179#define UNIV_REV(base) (READ_LE(base,2*sizeof(LERegister)) & 0xff)
180
181#if defined(__rtems__) && 0
182static int
183uprintk(char *fmt, va_list ap)
184{
185int             rval;
186extern int k_vsprintf(char *, char *, va_list);
187/* during bsp init, there is no malloc and no stdio,
188 * hence we assemble the message on the stack and revert
189 * to printk
190 */
191char    buf[200];
192        rval = k_vsprintf(buf,fmt,ap);
193        if (rval > sizeof(buf))
194                        BSP_panic("vmeUniverse/uprintk: buffer overrun");
195        printk(buf);
196        return rval;
197}
198#endif
199
200/* private printing wrapper */
201static void
202uprintf(FILE *f, char *fmt, ...)
203{
204va_list ap;
205        va_start(ap, fmt);
206#ifdef __rtems__
207        if (!f || !_impure_ptr->__sdidinit) {
208                /* Might be called at an early stage when
209                 * stdio is not yet initialized.
210                 * There is no vprintk, hence we must assemble
211                 * to a buffer.
212                 */
213                vprintk(fmt,ap);
214        } else
215#endif
216        {
217                vfprintf(f,fmt,ap);
218        }
219        va_end(ap);
220}
221
222int
223vmeUniverseFindPciBase(
224        int instance,
225        volatile LERegister **pbase
226        )
227{
228int bus,dev,fun;
229pci_ulong busaddr;
230unsigned char irqline;
231
232        if (BSP_PCI_FIND_DEVICE(
233                        PCI_VENDOR_TUNDRA,
234                        PCI_DEVICE_UNIVERSEII,
235                        instance,
236                        &bus,
237                        &dev,
238                        &fun))
239                return -1;
240        if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE0,&busaddr))
241                return -1;
242        if ((unsigned long)(busaddr) & 1) {
243                /* it's IO space, try BASE1 */
244                if (BSP_PCI_CONFIG_IN_LONG(bus,dev,fun,PCI_UNIVERSE_BASE1,&busaddr)
245                   || ((unsigned long)(busaddr) & 1))
246                        return -1;
247        }
248        *pbase=(volatile LERegister*)PCI_TO_LOCAL_ADDR(busaddr);
249
250        if (BSP_PCI_CONFIG_IN_BYTE(bus,dev,fun,PCI_INTERRUPT_LINE,&irqline))
251                return -1;
252        else
253                vmeUniverse0PciIrqLine = irqline;
254
255        return 0;
256}
257
258/* convert an address space selector to a corresponding
259 * universe control mode word
260 */
261
262static int
263am2mode(int ismaster, unsigned long address_space, unsigned long *pmode)
264{
265unsigned long mode=0;
266        if (!ismaster) {
267                mode |= UNIV_SCTL_DAT | UNIV_SCTL_PGM;
268                mode |= UNIV_SCTL_USER;
269        }
270        switch (address_space) {
271                case VME_AM_STD_SUP_PGM:
272                case VME_AM_STD_USR_PGM:
273                        if (ismaster)
274                                mode |= UNIV_MCTL_PGM ;
275                        else {
276                                mode &= ~UNIV_SCTL_DAT;
277                        }
278                        /* fall thru */
279                case VME_AM_STD_SUP_DATA:
280                case VME_AM_STD_USR_DATA:
281                        mode |= UNIV_CTL_VAS24;
282                        break;
283
284                case VME_AM_EXT_SUP_PGM:
285                case VME_AM_EXT_USR_PGM:
286                        if (ismaster)
287                                mode |= UNIV_MCTL_PGM ;
288                        else {
289                                mode &= ~UNIV_SCTL_DAT;
290                        }
291                        /* fall thru */
292                case VME_AM_EXT_SUP_DATA:
293                case VME_AM_EXT_USR_DATA:
294                        mode |= UNIV_CTL_VAS32;
295                        break;
296
297                case VME_AM_SUP_SHORT_IO:
298                case VME_AM_USR_SHORT_IO:
299                        mode |= UNIV_CTL_VAS16;
300                        break;
301
302                case 0: /* disable the port alltogether */
303                        break;
304
305                default:
306                        return -1;
307        }
308        if (address_space & VX_AM_SUP)
309                mode |= (ismaster ? UNIV_MCTL_SUPER : UNIV_SCTL_SUPER);
310        *pmode = mode;
311        return 0;
312}
313
314static int
315disableUniversePort(int ismaster, int portno, volatile unsigned long *preg, void *param)
316{
317unsigned long cntrl;
318        cntrl=READ_LE0(preg);
319        cntrl &= ~(ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN);
320        WRITE_LE(cntrl,preg,0);
321        SYNC; /* make sure this command completed */
322        return 0;
323}
324
325static int
326cfgUniversePort(
327        unsigned long   ismaster,
328        unsigned long   port,
329        unsigned long   address_space,
330        unsigned long   vme_address,
331        unsigned long   local_address,
332        unsigned long   length)
333{
334#define base vmeUniverse0BaseAddr
335volatile LERegister *preg;
336unsigned long   p=port;
337unsigned long   mode=0;
338
339        /* check parameters */
340        if (port >= (ismaster ? UNIV_NUM_MPORTS : UNIV_NUM_SPORTS)) {
341                uprintf(stderr,"invalid port\n");
342                return -1;
343        }
344        /* port start, bound addresses and offset must lie on 64k boundary
345         * (4k for port 0 and 4)
346         */
347        if ( PORT_UNALIGNED(local_address,port) ) {
348                uprintf(stderr,"local address misaligned\n");
349                return -1;
350        }
351        if ( PORT_UNALIGNED(vme_address,port) ) {
352                uprintf(stderr,"vme address misaligned\n");
353                return -1;
354        }
355        if ( PORT_UNALIGNED(length,port) ) {
356                uprintf(stderr,"length misaligned\n");
357                return -1;
358        }
359
360        /* check address space validity */
361        if (am2mode(ismaster,address_space,&mode)) {
362                uprintf(stderr,"invalid address space\n");
363                return -1;
364        }
365
366        /* get the universe base address */
367        if (!base && vmeUniverseInit()) {
368                return -1;
369        }
370
371        preg=base;
372
373        /* find out if we have a rev. II chip */
374        if ( UNIV_REV(base) < 2 ) {
375                if (port>3) {
376                        uprintf(stderr,"Universe rev. < 2 has only 4 ports\n");
377                        return -1;
378                }
379        }
380
381        /* finally, configure the port */
382
383        /* find the register set for our port */
384        if (port<4) {
385                preg += (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister);
386        } else {
387                preg += (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister);
388                p-=4;
389        }
390        preg += 5 * p;
391
392        /* temporarily disable the port */
393        disableUniversePort(ismaster,port,preg,0);
394
395        /* address_space == 0 means disable */
396        if (address_space != 0) {
397                unsigned long start,offst;
398                /* set the port starting address;
399                 * this is the local address for the master
400                 * and the VME address for the slave
401                 */
402                if (ismaster) {
403                        start=local_address;
404                        /* let it overflow / wrap around 0 */
405                        offst=vme_address-local_address;
406                } else {
407                        start=vme_address;
408                        /* let it overflow / wrap around 0 */
409                        offst=local_address-vme_address;
410                }
411#undef TSILL
412#ifdef TSILL
413                uprintf(stderr,"writing 0x%08x to 0x%08x + 4\n",start,preg);
414#else
415                WRITE_LE(start,preg,4);
416#endif
417                /* set bound address */
418                length+=start;
419#ifdef TSILL
420                uprintf(stderr,"writing 0x%08x to 0x%08x + 8\n",length,preg);
421#else
422                WRITE_LE(length,preg,8);
423#endif
424                /* set offset */
425#ifdef TSILL
426                uprintf(stderr,"writing 0x%08x to 0x%08x + 12\n",offst,preg);
427#else
428                WRITE_LE(offst,preg,12);
429#endif
430                /* calculate configuration word and enable the port */
431                /* NOTE: reading the CY961 (Echotek ECDR814) with VDW32
432                 *       generated bus errors when reading 32-bit words
433                 *       - very weird, because the registers are 16-bit
434                 *         AFAIK.
435                 *       - 32-bit accesses worked fine on vxWorks which
436                 *         has the port set to 64-bit.
437                 *       ????????
438                 */
439                if (ismaster)
440                        mode |= UNIV_MCTL_EN | UNIV_MCTL_PWEN | UNIV_MCTL_VDW64 | UNIV_MCTL_VCT;
441                else
442                        mode |= UNIV_SCTL_EN | UNIV_SCTL_PWEN | UNIV_SCTL_PREN;
443
444#ifdef TSILL
445                uprintf(stderr,"writing 0x%08x to 0x%08x + 0\n",mode,preg);
446#else
447                EIEIO_REG;      /* make sure mode is written last */
448                WRITE_LE(mode,preg,0);
449                SYNC;           /* enforce completion */
450#endif
451
452#ifdef TSILL
453                uprintf(stderr,
454                        "universe %s port %lu successfully configured\n",
455                                ismaster ? "master" : "slave",
456                                port);
457#endif
458
459#ifdef __vxworks
460                if (ismaster)
461                        uprintf(stderr,
462                        "WARNING: on the synergy, sysMasterPortsShow() may show incorrect settings (it uses cached values)\n");
463#endif
464        }
465        return 0;
466#undef base
467}
468
469static int
470showUniversePort(
471                int             ismaster,
472                int             portno,
473                volatile LERegister *preg,
474                void            *parm)
475{
476        FILE *f=parm ? (FILE *)parm : stdout;
477        unsigned long cntrl, start, bound, offst, mask;
478
479        cntrl = READ_LE0(preg++);
480#undef TSILL
481#ifdef TSILL
482        uprintf(stderr,"showUniversePort: *(0x%08x): 0x%08x\n",preg-1,cntrl);
483#endif
484#undef TSILL
485
486        /* skip this port if disabled */
487        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
488                return 0;
489
490        /* for the master `start' is the PCI address,
491         * for the slave  `start' is the VME address
492         */
493        mask = ~PORT_UNALIGNED(0xffffffff,portno);
494
495        start = READ_LE0(preg++)&mask;
496        bound = READ_LE0(preg++)&mask;
497        offst = READ_LE0(preg++)&mask;
498
499        offst+=start; /* calc start on the other bus */
500
501        if (ismaster) {
502                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
503                        portno,offst,bound-start,start);
504        } else {
505                uprintf(f,"%d:    0x%08lx 0x%08lx 0x%08lx ",
506                        portno,start,bound-start,offst);
507        }
508
509        switch (cntrl & UNIV_CTL_VAS) {
510                case UNIV_CTL_VAS16: uprintf(f,"A16, "); break;
511                case UNIV_CTL_VAS24: uprintf(f,"A24, "); break;
512                case UNIV_CTL_VAS32: uprintf(f,"A32, "); break;
513                default: uprintf(f,"A??, "); break;
514        }
515
516        if (ismaster) {
517                uprintf(f,"%s, %s",
518                        cntrl&UNIV_MCTL_PGM ?   "Pgm" : "Dat",
519                        cntrl&UNIV_MCTL_SUPER ? "Sup" : "Usr");
520        } else {
521                uprintf(f,"%s %s %s %s",
522                        cntrl&UNIV_SCTL_PGM ?   "Pgm," : "    ",
523                        cntrl&UNIV_SCTL_DAT ?   "Dat," : "    ",
524                        cntrl&UNIV_SCTL_SUPER ? "Sup," : "    ",
525                        cntrl&UNIV_SCTL_USER  ? "Usr" :  "");
526        }
527        uprintf(f,"\n");
528        return 0;
529}
530
531typedef struct XlatRec_ {
532        unsigned long   address;
533        unsigned long   aspace;
534        unsigned                reverse; /* find reverse mapping of this port */
535} XlatRec, *Xlat;
536
537/* try to translate an address through the bridge
538 *
539 * IN:  l->address, l->aspace
540 * OUT: l->address (translated address)
541 *
542 * RETURNS: -1: invalid space
543 *           0: invalid address (not found in range)
544 *           1: success
545 */
546
547static int
548xlatePort(int ismaster, int port, volatile LERegister *preg, void *parm)
549{
550Xlat    l=(Xlat)parm;
551unsigned long cntrl, start, bound, offst, mask, x;
552
553        cntrl = READ_LE0(preg++);
554
555        /* skip this port if disabled */
556        if (!(cntrl & (ismaster ? UNIV_MCTL_EN : UNIV_SCTL_EN)))
557                return 0;
558
559        /* check for correct address space */
560        if ( am2mode(ismaster,l->aspace,&offst) ) {
561                uprintf(stderr,"vmeUniverse WARNING: invalid adressing mode 0x%x\n",
562                               l->aspace);
563                return -1;
564        }
565        if ( (cntrl & (ismaster ? UNIV_MCTL_AM_MASK : UNIV_SCTL_AM_MASK))
566            != offst )
567                return 0; /* mode doesn't match requested AM */
568
569        /* OK, we found a matching mode, now we must check the address range */
570        mask = ~PORT_UNALIGNED(0xffffffff,port);
571
572        /* for the master `start' is the PCI address,
573         * for the slave  `start' is the VME address
574         */
575        start = READ_LE0(preg++) & mask;
576        bound = READ_LE0(preg++) & mask;
577        offst = READ_LE0(preg++) & mask;
578
579        /* translate address to the other bus */
580        if (l->reverse) {
581                /* reverse mapping, i.e. for master ports we map from
582                 * VME to PCI, for slave ports we map from VME to PCI
583                 */
584                if (l->address >= start && l->address < bound) {
585                                l->address+=offst;
586                                return 1;
587                }
588        } else {
589                x = l->address - offst;
590
591                if (x >= start && x < bound) {
592                        /* valid address found */
593                        l->address = x;
594                        return 1;
595                }
596        }
597        return 0;
598}
599
600static int
601mapOverAll(int ismaster, int (*func)(int,int,volatile LERegister *,void*), void *arg)
602{
603#define base    vmeUniverse0BaseAddr
604volatile LERegister     *rptr;
605unsigned long   port;
606int     rval;
607
608        /* get the universe base address */
609        if (!base && vmeUniverseInit()) {
610                uprintf(stderr,"unable to find the universe in pci config space\n");
611                return -1;
612        }
613        rptr = (base +
614                (ismaster ? UNIV_REGOFF_PCITGT0_CTRL : UNIV_REGOFF_VMESLV0_CTRL)/sizeof(LERegister));
615#undef TSILL
616#ifdef TSILL
617        uprintf(stderr,"mapoverall: base is 0x%08x, rptr 0x%08x\n",base,rptr);
618#endif
619#undef TSILL
620        for (port=0; port<4; port++) {
621                if ((rval=func(ismaster,port,rptr,arg))) return rval;
622                rptr+=5; /* register block spacing */
623        }
624
625        /* only rev. 2 has 8 ports */
626        if (UNIV_REV(base)<2) return -1;
627
628        rptr = (base +
629                (ismaster ? UNIV_REGOFF_PCITGT4_CTRL : UNIV_REGOFF_VMESLV4_CTRL)/sizeof(LERegister));
630        for (port=4; port<UNIV_NUM_MPORTS; port++) {
631                if ((rval=func(ismaster,port,rptr,arg))) return rval;
632                rptr+=5; /* register block spacing */
633        }
634        return 0;
635#undef base
636}
637
638static void
639showUniversePorts(int ismaster, FILE *f)
640{
641        if (!f) f=stdout;
642        uprintf(f,"Universe %s Ports:\n",ismaster ? "Master" : "Slave");
643        uprintf(f,"Port  VME-Addr   Size       PCI-Adrs   Mode:\n");
644        mapOverAll(ismaster,showUniversePort,f);
645}
646
647int
648vmeUniverseXlateAddr(
649        int master,             /* look in the master windows */
650        int reverse,            /* reverse mapping; for masters: map local to VME */
651        unsigned long as,       /* address space */
652        unsigned long aIn,      /* address to look up */
653        unsigned long *paOut/* where to put result */
654        )
655{
656int     rval;
657XlatRec l;
658        l.aspace  = as;
659        l.address = aIn;
660        l.reverse = reverse;
661        /* map result -1/0/1 to -2/-1/0 with 0 on success */
662        rval = mapOverAll(master,xlatePort,(void*)&l) - 1;
663        *paOut = l.address;
664        return rval;
665}
666
667void
668vmeUniverseReset(void)
669{
670        /* disable/reset special cycles (ADOH, RMW) */
671        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_CTL);
672        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_ADDR);
673        vmeUniverseWriteReg(0, UNIV_REGOFF_SCYC_EN);
674
675        /* set coupled window timeout to 0 (release VME after each transaction)
676         * CRT (coupled request timeout) is unused by Universe II
677         */
678        vmeUniverseWriteReg(UNIV_LMISC_CRT_128_US, UNIV_REGOFF_LMISC);
679
680        /* disable/reset DMA engine */
681        vmeUniverseWriteReg(0, UNIV_REGOFF_DCTL);
682        vmeUniverseWriteReg(0, UNIV_REGOFF_DTBC);
683        vmeUniverseWriteReg(0, UNIV_REGOFF_DLA);
684        vmeUniverseWriteReg(0, UNIV_REGOFF_DVA);
685        vmeUniverseWriteReg(0, UNIV_REGOFF_DCPP);
686
687        /* disable location monitor */
688        vmeUniverseWriteReg(0, UNIV_REGOFF_LM_CTL);
689
690        /* disable universe register access from VME bus */
691        vmeUniverseWriteReg(0, UNIV_REGOFF_VRAI_CTL);
692
693        /* disable VME bus image of VME CSR */
694        vmeUniverseWriteReg(0, UNIV_REGOFF_VCSR_CTL);
695
696        /* I had problems with a Joerger vtr10012_8 card who would
697         * only be accessible after tweaking the U2SPEC register
698         * (the t27 parameter helped).
699         * I use the same settings here that are used by the
700         * Synergy VGM-powerpc BSP for vxWorks.
701         */
702        if (2==UNIV_REV(vmeUniverse0BaseAddr))
703                vmeUniverseWriteReg(UNIV_U2SPEC_DTKFLTR |
704                                                UNIV_U2SPEC_MASt11   |
705                                                UNIV_U2SPEC_READt27_NODELAY |
706                                                UNIV_U2SPEC_POSt28_FAST |
707                                                UNIV_U2SPEC_PREt28_FAST,
708                                                UNIV_REGOFF_U2SPEC);
709
710        /* disable interrupts, reset routing */
711        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_EN);
712        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP0);
713        vmeUniverseWriteReg(0, UNIV_REGOFF_LINT_MAP1);
714
715        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_EN);
716        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP0);
717        vmeUniverseWriteReg(0, UNIV_REGOFF_VINT_MAP1);
718
719        vmeUniverseDisableAllSlaves();
720
721        vmeUniverseDisableAllMasters();
722
723        vmeUniverseWriteReg(UNIV_VCSR_CLR_SYSFAIL, UNIV_REGOFF_VCSR_CLR);
724
725        /* clear interrupt status bits */
726        vmeUniverseWriteReg(UNIV_LINT_STAT_CLR, UNIV_REGOFF_LINT_STAT);
727        vmeUniverseWriteReg(UNIV_VINT_STAT_CLR, UNIV_REGOFF_VINT_STAT);
728
729        vmeUniverseWriteReg(UNIV_V_AMERR_V_STAT, UNIV_REGOFF_V_AMERR);
730
731        vmeUniverseWriteReg(
732                vmeUniverseReadReg(UNIV_REGOFF_PCI_CSR) |
733                UNIV_PCI_CSR_D_PE | UNIV_PCI_CSR_S_SERR | UNIV_PCI_CSR_R_MA |
734                UNIV_PCI_CSR_R_TA | UNIV_PCI_CSR_S_TA,
735                UNIV_REGOFF_PCI_CSR);
736
737        vmeUniverseWriteReg(UNIV_L_CMDERR_L_STAT, UNIV_REGOFF_L_CMDERR);
738
739        vmeUniverseWriteReg(
740                UNIV_DGCS_STOP | UNIV_DGCS_HALT | UNIV_DGCS_DONE |
741                UNIV_DGCS_LERR | UNIV_DGCS_VERR | UNIV_DGCS_P_ERR,
742                UNIV_REGOFF_DGCS);
743}
744
745int
746vmeUniverseInit(void)
747{
748int rval;
749        if ((rval=vmeUniverseFindPciBase(0,&vmeUniverse0BaseAddr))) {
750                uprintf(stderr,"unable to find the universe in pci config space\n");
751        } else {
752                uprintf(stderr,"Universe II PCI-VME bridge detected at 0x%08x, IRQ %d\n",
753                                (unsigned int)vmeUniverse0BaseAddr, vmeUniverse0PciIrqLine);
754        }
755        return rval;
756}
757
758void
759vmeUniverseMasterPortsShow(FILE *f)
760{
761        showUniversePorts(1,f);
762}
763
764void
765vmeUniverseSlavePortsShow(FILE *f)
766{
767        showUniversePorts(0,f);
768}
769
770int
771vmeUniverseMasterPortCfg(
772        unsigned long   port,
773        unsigned long   address_space,
774        unsigned long   vme_address,
775        unsigned long   local_address,
776        unsigned long   length)
777{
778        return cfgUniversePort(1,port,address_space,vme_address,local_address,length);
779}
780
781int
782vmeUniverseSlavePortCfg(
783        unsigned long   port,
784        unsigned long   address_space,
785        unsigned long   vme_address,
786        unsigned long   local_address,
787        unsigned long   length)
788{
789        return cfgUniversePort(0,port,address_space,vme_address,local_address,length);
790}
791
792void
793vmeUniverseDisableAllSlaves(void)
794{
795        mapOverAll(0,disableUniversePort,0);
796}
797
798void
799vmeUniverseDisableAllMasters(void)
800{
801        mapOverAll(1,disableUniversePort,0);
802}
803
804int
805vmeUniverseStartDMA(
806        unsigned long local_addr,
807        unsigned long vme_addr,
808        unsigned long count)
809{
810
811        if (!vmeUniverse0BaseAddr && vmeUniverseInit()) return -1;
812        if ((local_addr & 7) != (vme_addr & 7)) {
813                uprintf(stderr,"vmeUniverseStartDMA: misaligned addresses\n");
814                return -1;
815        }
816
817        {
818        /* help the compiler allocate registers */
819        register volatile LERegister *b=vmeUniverse0BaseAddr;
820        register unsigned long dgcsoff=UNIV_REGOFF_DGCS,dgcs;
821
822        dgcs=READ_LE(b, dgcsoff);
823
824        /* clear status and make sure CHAIN is clear */
825        dgcs &= ~UNIV_DGCS_CHAIN;
826        WRITE_LE(dgcs,
827                      b, dgcsoff);
828        WRITE_LE(local_addr,
829                      b, UNIV_REGOFF_DLA);
830        WRITE_LE(vme_addr,
831                      b, UNIV_REGOFF_DVA);
832        WRITE_LE(count,
833                      b, UNIV_REGOFF_DTBC);
834        dgcs |= UNIV_DGCS_GO;
835        EIEIO_REG; /* make sure GO is written after everything else */
836        WRITE_LE(dgcs,
837                      b, dgcsoff);
838        }
839        SYNC; /* enforce command completion */
840        return 0;
841}
842
843unsigned long
844vmeUniverseReadReg(unsigned long offset)
845{
846unsigned long rval;
847        rval = READ_LE(vmeUniverse0BaseAddr,offset);
848        return rval;
849}
850
851void
852vmeUniverseWriteReg(unsigned long value, unsigned long offset)
853{
854        WRITE_LE(value, vmeUniverse0BaseAddr, offset);
855}
856
857void
858vmeUniverseCvtToLE(unsigned long *ptr, unsigned long num)
859{
860#if !defined(__LITTLE_ENDIAN__) || (__LITTLE_ENDIAN__ != 1)
861register unsigned long *p=ptr+num;
862        while (p > ptr) {
863#if (defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC)) && (__BIG_ENDIAN__ == 1)
864                __asm__ __volatile__(
865                        "lwzu 0, -4(%0)\n"
866                        "stwbrx 0, 0, %0\n"
867                        : "=r"(p) : "r"(p) : "r0"
868                        );
869#elif defined(__rtems__)
870                p--; st_le32(p, *p);
871#else
872#error  "vmeUniverse: endian conversion not implemented for this architecture"
873#endif
874        }
875#endif
876}
877
878/* RTEMS interrupt subsystem */
879
880#ifdef __rtems__
881#include <bsp/irq.h>
882
883typedef struct
884UniverseIRQEntryRec_ {
885                VmeUniverseISR  isr;
886                void                    *usrData;
887} UniverseIRQEntryRec, *UniverseIRQEntry;
888
889static UniverseIRQEntry universeHdlTbl[UNIV_NUM_INT_VECS]={0};
890
891int        vmeUniverseIrqMgrInstalled=0;
892static int vmeIrqUnivOut=-1;
893static int specialIrqUnivOut=-1;
894
895VmeUniverseISR
896vmeUniverseISRGet(unsigned long vector, void **parg)
897{
898        if (vector>=UNIV_NUM_INT_VECS ||
899                ! universeHdlTbl[vector])
900                return 0;
901        if (parg)
902                *parg=universeHdlTbl[vector]->usrData;
903        return universeHdlTbl[vector]->isr;
904}
905
906static void
907universeSpecialISR(void)
908{
909register UniverseIRQEntry       ip;
910register unsigned                       vec;
911register unsigned long          status;
912
913        status=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
914
915        /* scan all LINT bits except for the 'normal' VME interrupts */
916
917        /* do VOWN first */
918        vec=UNIV_VOWN_INT_VEC;
919        if ( (status & UNIV_LINT_STAT_VOWN) && (ip=universeHdlTbl[vec]))
920                ip->isr(ip->usrData,vec);
921
922        /* now continue with DMA and scan through all bits;
923         * we assume the vectors are in the right order!
924         *
925         * The initial right shift brings the DMA bit into position 0;
926         * the loop is left early if there are no more bits set.
927         */
928        for (status>>=8; status; status>>=1) {
929                vec++;
930                if ((status&1) && (ip=universeHdlTbl[vec]))
931                        ip->isr(ip->usrData,vec);
932        }
933        /* clear all special interrupts */
934        vmeUniverseWriteReg(
935                                        ~((UNIV_LINT_STAT_VIRQ7<<1)-UNIV_LINT_STAT_VIRQ1),
936                                        UNIV_REGOFF_LINT_STAT
937                                        );
938
939/*
940 *      clear our line in the VINT_STAT register
941 *  seems to be not neccessary...
942        vmeUniverseWriteReg(
943                                        UNIV_VINT_STAT_LINT(specialIrqUnivOut),
944                                        UNIV_REGOFF_VINT_STAT);
945 */
946}
947
948/*
949 * interrupts from VME to PCI seem to be processed more or less
950 * like this:
951 *
952 *
953 *   VME IRQ ------
954 *                  & ----- LINT_STAT ----
955 *                  |                       &  ---------- PCI LINE
956 *                  |                       |
957 *                  |                       |
958 *       LINT_EN ---------------------------
959 *
960 *  I.e.
961 *   - if LINT_EN is disabled, a VME IRQ will not set LINT_STAT.
962 *   - while LINT_STAT is set, it will pull the PCI line unless
963 *     masked by LINT_EN.
964 *   - VINT_STAT(lint_bit) seems to have no effect beyond giving
965 *     status info.
966 *
967 *  Hence, it is possible to
968 *    - arm (set LINT_EN, routing etc.)
969 *    - receive an irq (sets. LINT_STAT)
970 *    - the ISR then:
971 *                * clears LINT_EN, results in masking LINT_STAT (which
972 *                  is still set to prevent another VME irq at the same
973 *                  level to be ACKEd by the universe.
974 *                * do PCI_EOI to allow nesting of higher VME irqs.
975 *                  (previous step also cleared LINT_EN of lower levels)
976 *                * when the handler returns, clear LINT_STAT
977 *                * re-enable setting LINT_EN.
978 */
979
980static void
981universeVMEISR(void)
982{
983UniverseIRQEntry ip;
984unsigned long lvl,msk,lintstat,linten,status;
985
986                /* determine the highest priority IRQ source */
987                lintstat=vmeUniverseReadReg(UNIV_REGOFF_LINT_STAT);
988                for (msk=UNIV_LINT_STAT_VIRQ7, lvl=7;
989                         lvl>0;
990                         lvl--, msk>>=1) {
991                        if (lintstat & msk) break;
992                }
993                if (!lvl) {
994                                /* try the special handler */
995                                universeSpecialISR();
996
997                                /*
998                                 * let the pic end this cycle
999                                 */
1000                                BSP_PIC_DO_EOI;
1001
1002                                return;
1003                }
1004                linten = vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
1005
1006                /* mask this and all lower levels */
1007                vmeUniverseWriteReg(
1008                                                linten & ~((msk<<1)-UNIV_LINT_STAT_VIRQ1),
1009                                                UNIV_REGOFF_LINT_EN
1010                                                );
1011
1012                /* end this interrupt
1013                 * cycle on the PCI bus, so higher level interrupts can be
1014                 * caught from now on...
1015                 */
1016                BSP_PIC_DO_EOI;
1017
1018                /* get vector and dispatch handler */
1019                status = vmeUniverseReadReg(UNIV_REGOFF_VIRQ1_STATID - 4 + (lvl<<2));
1020                /* determine the highest priority IRQ source */
1021
1022                if (status & UNIV_VIRQ_ERR) {
1023                                /* TODO: log error message - RTEMS has no logger :-( */
1024                } else if (!(ip=universeHdlTbl[status & UNIV_VIRQ_STATID_MASK])) {
1025                                /* TODO: log error message - RTEMS has no logger :-( */
1026                } else {
1027                                /* dispatch handler, it must clear the IRQ at the device */
1028                                ip->isr(ip->usrData, status&UNIV_VIRQ_STATID_MASK);
1029                }
1030
1031                /* clear this interrupt level */
1032                vmeUniverseWriteReg(msk, UNIV_REGOFF_LINT_STAT);
1033/*
1034 *  this seems not to be necessary; we just leave the
1035 *  bit set to save a couple of instructions...
1036                vmeUniverseWriteReg(
1037                                        UNIV_VINT_STAT_LINT(vmeIrqUnivOut),
1038                                        UNIV_REGOFF_VINT_STAT);
1039*/
1040
1041                /* re-enable the previous level */
1042                vmeUniverseWriteReg(linten, UNIV_REGOFF_LINT_EN);
1043}
1044
1045/* STUPID API */
1046static void
1047my_no_op(const rtems_irq_connect_data * arg)
1048{}
1049
1050static int
1051my_isOn(const rtems_irq_connect_data *arg)
1052{
1053                return (int)vmeUniverseReadReg(UNIV_REGOFF_LINT_EN);
1054}
1055
1056int
1057vmeUniverseInstallIrqMgr(int vmeOut,
1058                                                 int vmeIrqPicLine,
1059                                                 int specialOut,
1060                                                 int specialIrqPicLine)
1061{
1062rtems_irq_connect_data aarrggh;
1063
1064        /* check parameters */
1065        if ((vmeIrqUnivOut=vmeOut) < 0 || vmeIrqUnivOut > 7) return -1;
1066        if ((specialIrqUnivOut=specialOut) > 7) return -2;
1067        if (specialOut >=0 && specialIrqPicLine < 0) return -3;
1068        /* give them a chance to override buggy PCI info */
1069        if (vmeIrqPicLine >= 0) {
1070                uprintf(stderr,"Overriding main IRQ line PCI info with %d\n",
1071                                vmeIrqPicLine);
1072                vmeUniverse0PciIrqLine=vmeIrqPicLine;
1073        }
1074
1075        if (vmeUniverseIrqMgrInstalled) return -4;
1076
1077        aarrggh.on=my_no_op; /* at _least_ they could check for a 0 pointer */
1078        aarrggh.off=my_no_op;
1079        aarrggh.isOn=my_isOn;
1080        aarrggh.hdl=universeVMEISR;
1081        aarrggh.name=vmeUniverse0PciIrqLine + BSP_PCI_IRQ0;
1082        if (!BSP_install_rtems_irq_handler(&aarrggh))
1083                        BSP_panic("unable to install vmeUniverse irq handler");
1084        if (specialIrqUnivOut > 0) {
1085                        /* install the special handler to a separate irq */
1086                        aarrggh.hdl=universeSpecialISR;
1087                        aarrggh.name=specialIrqPicLine + BSP_PCI_IRQ0;
1088                        if (!BSP_install_rtems_irq_handler(&aarrggh))
1089                                BSP_panic("unable to install vmeUniverse secondary irq handler");
1090        } else {
1091                specialIrqUnivOut = vmeIrqUnivOut;
1092        }
1093        /* setup routing */
1094
1095        vmeUniverseWriteReg(
1096                (UNIV_LINT_MAP0_VIRQ7(vmeIrqUnivOut) |
1097                 UNIV_LINT_MAP0_VIRQ6(vmeIrqUnivOut) |
1098                 UNIV_LINT_MAP0_VIRQ5(vmeIrqUnivOut) |
1099                 UNIV_LINT_MAP0_VIRQ4(vmeIrqUnivOut) |
1100                 UNIV_LINT_MAP0_VIRQ3(vmeIrqUnivOut) |
1101                 UNIV_LINT_MAP0_VIRQ2(vmeIrqUnivOut) |
1102                 UNIV_LINT_MAP0_VIRQ1(vmeIrqUnivOut) |
1103                 UNIV_LINT_MAP0_VOWN(specialIrqUnivOut)
1104                ),
1105                UNIV_REGOFF_LINT_MAP0);
1106        vmeUniverseWriteReg(
1107                (UNIV_LINT_MAP1_ACFAIL(specialIrqUnivOut) |
1108                 UNIV_LINT_MAP1_SYSFAIL(specialIrqUnivOut) |
1109                 UNIV_LINT_MAP1_SW_INT(specialIrqUnivOut) |
1110                 UNIV_LINT_MAP1_SW_IACK(specialIrqUnivOut) |
1111                 UNIV_LINT_MAP1_VERR(specialIrqUnivOut) |
1112                 UNIV_LINT_MAP1_LERR(specialIrqUnivOut) |
1113                 UNIV_LINT_MAP1_DMA(specialIrqUnivOut)
1114                ),
1115                UNIV_REGOFF_LINT_MAP1);
1116        vmeUniverseIrqMgrInstalled=1;
1117        return 0;
1118}
1119
1120int
1121vmeUniverseInstallISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
1122{
1123UniverseIRQEntry ip;
1124
1125                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
1126                                return -1;
1127
1128                ip=universeHdlTbl[vector];
1129
1130                if (ip || !(ip=(UniverseIRQEntry)malloc(sizeof(UniverseIRQEntryRec))))
1131                                return -1;
1132                ip->isr=hdl;
1133                ip->usrData=arg;
1134                universeHdlTbl[vector]=ip;
1135                return 0;
1136}
1137
1138int
1139vmeUniverseRemoveISR(unsigned long vector, VmeUniverseISR hdl, void *arg)
1140{
1141UniverseIRQEntry ip;
1142
1143                if (vector>sizeof(universeHdlTbl)/sizeof(universeHdlTbl[0]) || !vmeUniverseIrqMgrInstalled)
1144                                return -1;
1145
1146                ip=universeHdlTbl[vector];
1147
1148                if (!ip || ip->isr!=hdl || ip->usrData!=arg)
1149                                return -1;
1150                universeHdlTbl[vector]=0;
1151                free(ip);
1152                return 0;
1153}
1154
1155int
1156vmeUniverseIntEnable(unsigned int level)
1157{
1158                if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
1159                                return -1;
1160                vmeUniverseWriteReg(
1161                                (vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) |
1162                                 (UNIV_LINT_EN_VIRQ1 << (level-1))
1163                                ),
1164                                UNIV_REGOFF_LINT_EN);
1165                return 0;
1166}
1167
1168int
1169vmeUniverseIntDisable(unsigned int level)
1170{
1171                if (!vmeUniverseIrqMgrInstalled || level<1 || level>7)
1172                                return -1;
1173                vmeUniverseWriteReg(
1174                                (vmeUniverseReadReg(UNIV_REGOFF_LINT_EN) &
1175                                 ~ (UNIV_LINT_EN_VIRQ1 << (level-1))
1176                                ),
1177                                UNIV_REGOFF_LINT_EN);
1178                return 0;
1179}
1180
1181#endif
Note: See TracBrowser for help on using the repository browser.