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

4.104.114.84.95
Last change on this file since 6fae458 was 6fae458, checked in by Joel Sherrill <joel.sherrill@…>, on 05/14/02 at 17:04:40

2001-05-14 Till Straumann <strauman@…>

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