source: rtems/c/src/lib/libbsp/shared/vmeUniverse/vmeUniverse.c @ 30062b7

Last change on this file since 30062b7 was 30062b7, checked in by Joel Sherrill <joel.sherrill@…>, on Nov 16, 2004 at 11:00:27 PM

2004-11-16 Richard Campbell <richard.campbell@…>

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