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

4.104.114.84.95
Last change on this file since f418d6e was 6600424d, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/11/03 at 11:00:20

Merger from rtems-4-6-branch.

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