source: rtems/c/src/lib/libbsp/powerpc/shared/openpic/openpic.c @ 561dec5

Last change on this file since 561dec5 was 561dec5, checked in by Joel Sherrill <joel@…>, on Mar 21, 2016 at 3:53:32 PM

powerpc/shared/openpic/openpic.c: Ignore warnings for builtin_frame_address() usage

  • Property mode set to 100644
File size: 17.3 KB
Line 
1/*
2 *  openpic.c -- OpenPIC Interrupt Handling
3 *
4 *  Copyright (C) 1997 Geert Uytterhoeven
5 *
6 *  Modified to compile in RTEMS development environment
7 *  by Eric Valette
8 *
9 *  Copyright (C) 1999 Eric Valette. valette@crf.canon.fr
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.org/license/LICENSE.
14 */
15
16/*
17 *  Note: Interprocessor Interrupt (IPI) and Timer support is incomplete
18 */
19
20#include <rtems.h>
21#include <bsp.h>
22#include <rtems/bspIo.h>
23#include <bsp/openpic.h>
24#include <rtems/pci.h>
25#include <libcpu/io.h>
26#include <libcpu/byteorder.h>
27#include <rtems/bspIo.h>
28
29#ifndef NULL
30#define NULL 0
31#endif
32#define REGISTER_DEBUG
33#undef REGISTER_DEBUG
34
35volatile struct OpenPIC *OpenPIC = NULL;
36
37static unsigned int NumProcessors;
38static unsigned int NumSources;
39
40static unsigned int openpic_eoi_delay = 0;
41static          int openpic_src_offst = 0;
42#define SOURCE(irq)     Source[ (irq) + openpic_src_offst ]
43
44    /*
45     *  Accesses to the current processor's registers
46     */
47
48#define THIS_CPU                Processor[cpu]
49#define CHECK_THIS_CPU          check_arg_cpu(cpu)
50
51    /*
52     *  Sanity checks
53     */
54
55#if 1
56/* This software deliberately uses non-zero values to the method
57 * __builtin_return_address() and we want to avoid the GCC warning.
58 */
59#pragma GCC diagnostic push
60#pragma GCC diagnostic ignored "-Wframe-address"
61#define check_arg_ipi(ipi) \
62    if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
63        printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi);
64#define check_arg_timer(timer) \
65    if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
66        printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer);
67#define check_arg_vec(vec) \
68    if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
69        printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec);
70#define check_arg_pri(pri) \
71    if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
72        printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri);
73#define check_arg_irq(irq) \
74    if (irq < 0 || irq >= NumSources) \
75        printk("openpic.c:%d: illegal irq %d from 0x%08x,[0x%08x],[[0x%08x]]\n", \
76               __LINE__, irq, __builtin_return_address(0), \
77               __builtin_return_address(1), __builtin_return_address(2) \
78               );
79#define check_arg_cpu(cpu) \
80    if (cpu < 0 || cpu >= NumProcessors) \
81        printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu);
82#else
83#define check_arg_ipi(ipi)      do {} while (0)
84#define check_arg_timer(timer)  do {} while (0)
85#define check_arg_vec(vec)      do {} while (0)
86#define check_arg_pri(pri)      do {} while (0)
87#define check_arg_irq(irq)      do {} while (0)
88#define check_arg_cpu(cpu)      do {} while (0)
89#endif
90
91    /*
92     *  I/O functions
93     */
94
95static inline unsigned int openpic_read(volatile unsigned int *addr)
96{
97    unsigned int val;
98
99#ifdef BSP_OPEN_PIC_BIG_ENDIAN
100        val = in_be32((volatile uint32_t *)addr);
101#else
102    val = in_le32((volatile uint32_t *)addr);
103#endif
104#ifdef REGISTER_DEBUG
105    printk("openpic_read(0x%08x) = 0x%08x\n", (unsigned int)addr, val);
106#endif
107    return val;
108}
109
110static inline void openpic_write(volatile unsigned int *addr, unsigned int val)
111{
112#ifdef REGISTER_DEBUG
113    printk("openpic_write(0x%08x, 0x%08x)\n", (unsigned int)addr, val);
114#endif
115#ifdef BSP_OPEN_PIC_BIG_ENDIAN
116    out_be32((volatile uint32_t *)addr, val);
117#else
118        out_le32((volatile uint32_t *)addr, val);
119#endif
120}
121
122static inline unsigned int openpic_readfield(volatile unsigned int *addr, unsigned int mask)
123{
124    unsigned int val = openpic_read(addr);
125    return val & mask;
126}
127
128static inline void openpic_writefield(volatile unsigned int *addr, unsigned int mask,
129                                      unsigned int field)
130{
131    unsigned int val = openpic_read(addr);
132    openpic_write(addr, (val & ~mask) | (field & mask));
133}
134
135static inline void openpic_clearfield(volatile unsigned int *addr, unsigned int mask)
136{
137    openpic_writefield(addr, mask, 0);
138}
139
140static inline void openpic_setfield(volatile unsigned int *addr, unsigned int mask)
141{
142    openpic_writefield(addr, mask, mask);
143}
144
145    /*
146     *  Update a Vector/Priority register in a safe manner. The interrupt will
147     *  be disabled.
148     */
149
150static void openpic_safe_writefield(volatile unsigned int *addr, unsigned int mask,
151                                    unsigned int field)
152{
153    openpic_setfield(addr, OPENPIC_MASK);
154    /* wait until it's not in use */
155    while (openpic_read(addr) & OPENPIC_ACTIVITY);
156    openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
157}
158
159/* -------- Global Operations ---------------------------------------------- */
160
161    /*
162     *  Initialize the OpenPIC
163     *
164     * Add some kludge to use the Motorola Raven OpenPIC which does not
165     * report vendor and device id, and gets the wrong number of interrupts.
166     * (Motorola did a great job on that one!)
167     *
168     * T. Straumann, 12/20/2001: polarities and senses are now passed as
169     *                           parameters, eliminated global vars.
170     *                           IRQ0 is no longer treated specially.
171     */
172
173void openpic_init(int main_pic, unsigned char *polarities, unsigned char *senses, int num_sources, int source_offset, unsigned long epic_freq)
174{
175    unsigned int t, i;
176    unsigned int vendorid, devid, stepping, timerfreq;
177    const char *version, *vendor, *device;
178
179    if (!OpenPIC)
180        BSP_panic("No OpenPIC found");
181
182    t = openpic_read(&OpenPIC->Global.Feature_Reporting0);
183    switch (t & OPENPIC_FEATURE_VERSION_MASK) {
184        case 1:
185            version = "1.0";
186            break;
187        case 2:
188            version = "1.2";
189            break;
190        default:
191            version = "?";
192            break;
193    }
194    NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
195                     OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
196    NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
197                  OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1;
198    t = openpic_read(&OpenPIC->Global.Vendor_Identification);
199
200    vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK;
201    devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >>
202            OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT;
203    stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >>
204               OPENPIC_VENDOR_ID_STEPPING_SHIFT;
205
206    /* Kludge for the Raven */
207/*
208    pci_read_config_dword(0, 0, 0, 0, &t);
209*/
210    if (t == PCI_VENDOR_ID_MOTOROLA + (PCI_DEVICE_ID_MOTOROLA_RAVEN<<16)) {
211        vendor = "Motorola";
212        device = "Raven";
213        NumSources += 1;
214    }
215    else if (t == PCI_VENDOR_ID_MOTOROLA + (PCI_DEVICE_ID_MOTOROLA_HAWK<<16)) {
216        vendor = "Motorola";
217        device = "Hawk";
218        NumSources += 1;
219    } else {
220        switch (vendorid) {
221            case OPENPIC_VENDOR_ID_APPLE:
222                vendor = "Apple";
223                break;
224            default:
225                vendor = "Unknown";
226            break;
227        }
228        switch (devid) {
229            case OPENPIC_DEVICE_ID_APPLE_HYDRA:
230                device = "Hydra";
231                break;
232            default:
233                device = "Unknown";
234                break;
235        }
236    }
237    printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at 0x%08x\n", version,
238           NumProcessors, NumSources, OpenPIC);
239
240    printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid,
241           vendor, devid, device, stepping);
242
243        /* Override if they desire */
244        if ( num_sources ) {
245                if ( NumSources != num_sources )
246                        printk("Overriding NumSources (%i) from configuration with %i\n",
247                                NumSources, num_sources);
248                NumSources = num_sources;
249        }
250
251        openpic_src_offst = source_offset;
252
253    timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
254    printk("OpenPIC timer frequency is ");
255    if (timerfreq)
256        printk("%d Hz\n", timerfreq);
257    else
258        printk("not set\n");
259
260    if ( main_pic )
261    {
262            /* Initialize timer interrupts */
263            for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
264                    /* Disabled, Priority 0 */
265                    openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i);
266                    /* No processor */
267                    openpic_maptimer(i, 0);
268            }
269
270            /* Initialize IPI interrupts */
271            for (i = 0; i < OPENPIC_NUM_IPI; i++) {
272                    /* Disabled, Priority 0 */
273                    openpic_initipi(i, 0, OPENPIC_VEC_IPI+i);
274            }
275
276            /* Initialize external interrupts */
277            for (i = 0; i < NumSources; i++) {
278                    /* Enabled, Priority 8 */
279                    openpic_initirq(i, 8, OPENPIC_VEC_SOURCE+i,
280                                        polarities ? polarities[i] : 0,
281                                        senses     ? senses[i]     : 1);
282                    /* Processor 0 */
283                    openpic_mapirq(i, 1<<0);
284            }
285
286            /* Initialize the spurious interrupt */
287            openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
288#if 0
289            if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT,
290                            "82c59 cascade", NULL))
291              printk("Unable to get OpenPIC IRQ 0 for cascade\n");
292#endif
293            openpic_set_priority(0, 0);
294            openpic_disable_8259_pass_through();
295    }
296        if ( epic_freq ) {
297                /* Speed up the serial interface; if it is too slow then we might get spurious
298                 * interrupts:
299                 * After an ISR clears the interrupt condition at the source/device, the wire
300                 * remains asserted during the propagation delay introduced by the serial interface
301                 * (something really stupid). If the ISR returns while the wire is not released
302                 * yet, then a spurious interrupt happens.
303                 * The book says we should be careful if the serial clock is > 33MHz.
304                 * Empirically, it seems that running it at 33MHz is fast enough. Otherwise,
305                 * we should introduce a delay in openpic_eoi().
306                 * The maximal delay are 16 (serial) clock cycles. If the divisor is 8
307                 * [power-up default] then the lag is 2us [66MHz SDRAM clock; I assume this
308                 * is equal to the bus frequency].
309                 * FIXME: This should probably be a EPIC-specific piece in 'openpic.c'
310                 *        Unfortunately, there is no easy way of figuring out if the
311                 *        device is an EPIC or not.
312                 */
313                uint32_t eicr_val, ratio;
314                /* On the 8240 this is the EICR register */
315                eicr_val = in_le32( (volatile uint32_t *)&OpenPIC->Global.Global_Configuration1 ) & ~(7<<28);
316                if ( (1<<27) & eicr_val ) {
317                        /* serial interface mode enabled */
318
319                        /* round to nearest integer:
320                         *   round(Bus_freq/33000000) = floor( 2*(Bus_freq/33e6) + 1 ) / 2
321                         */
322                        ratio   = epic_freq / 16500000 + 1;
323                        ratio >>= 2; /* EICR value is half actual divisor */
324                        if ( 0==ratio )
325                                ratio = 1;
326                        out_le32((volatile uint32_t *)&OpenPIC->Global.Global_Configuration1, eicr_val | ((ratio &7) << 28));
327                        /*  Delay in TB cycles (assuming TB runs at 1/4 of the bus frequency) */
328                        openpic_set_eoi_delay( 16 * (2*ratio) / 4 );
329                }
330        }
331}
332
333    /*
334     *  Reset the OpenPIC
335     */
336
337void openpic_reset(void)
338{
339    openpic_setfield(&OpenPIC->Global.Global_Configuration0,
340                       OPENPIC_CONFIG_RESET);
341}
342
343    /*
344     *  Enable/disable 8259 Pass Through Mode
345     */
346
347void openpic_enable_8259_pass_through(void)
348{
349    openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
350                       OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
351}
352
353void openpic_disable_8259_pass_through(void)
354{
355    openpic_setfield(&OpenPIC->Global.Global_Configuration0,
356                     OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
357}
358
359    /*
360     *  Find out the current interrupt
361     */
362
363unsigned int openpic_irq(unsigned int cpu)
364{
365    unsigned int vec;
366
367    check_arg_cpu(cpu);
368    vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge,
369                            OPENPIC_VECTOR_MASK);
370    return vec;
371}
372
373    /*
374     *  Signal end of interrupt (EOI) processing
375     */
376
377void openpic_eoi(unsigned int cpu)
378{
379    check_arg_cpu(cpu);
380    if ( openpic_eoi_delay )
381        rtems_bsp_delay_in_bus_cycles(openpic_eoi_delay);
382    openpic_write(&OpenPIC->THIS_CPU.EOI, 0);
383}
384
385unsigned openpic_set_eoi_delay(unsigned tb_cycles)
386{
387unsigned rval = openpic_eoi_delay;
388    openpic_eoi_delay = tb_cycles;
389        return rval;
390}
391
392    /*
393     *  Get/set the current task priority
394     */
395
396unsigned int openpic_get_priority(unsigned int cpu)
397{
398    CHECK_THIS_CPU;
399    return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
400                             OPENPIC_CURRENT_TASK_PRIORITY_MASK);
401}
402
403void openpic_set_priority(unsigned int cpu, unsigned int pri)
404{
405    CHECK_THIS_CPU;
406    check_arg_pri(pri);
407    openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority,
408                       OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
409}
410
411    /*
412     *  Get/set the spurious vector
413     */
414
415unsigned int openpic_get_spurious(void)
416{
417    return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
418                             OPENPIC_VECTOR_MASK);
419}
420
421void openpic_set_spurious(unsigned int vec)
422{
423    check_arg_vec(vec);
424    openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
425                       vec);
426}
427
428    /*
429     *  Initialize one or more CPUs
430     */
431
432void openpic_init_processor(unsigned int cpumask)
433{
434    openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask);
435}
436
437/* -------- Interprocessor Interrupts -------------------------------------- */
438
439    /*
440     *  Initialize an interprocessor interrupt (and disable it)
441     *
442     *  ipi: OpenPIC interprocessor interrupt number
443     *  pri: interrupt source priority
444     *  vec: the vector it will produce
445     */
446
447void openpic_initipi(unsigned int ipi, unsigned int pri, unsigned int vec)
448{
449    check_arg_timer(ipi);
450    check_arg_pri(pri);
451    check_arg_vec(vec);
452    openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi),
453                            OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
454                            (pri << OPENPIC_PRIORITY_SHIFT) | vec);
455}
456
457    /*
458     *  Send an IPI to one or more CPUs
459     */
460
461void openpic_cause_IPI(unsigned int cpu, unsigned int ipi, unsigned int cpumask)
462{
463    CHECK_THIS_CPU;
464    check_arg_ipi(ipi);
465    openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask);
466}
467
468/* -------- Timer Interrupts ----------------------------------------------- */
469
470    /*
471     *  Initialize a timer interrupt (and disable it)
472     *
473     *  timer: OpenPIC timer number
474     *  pri: interrupt source priority
475     *  vec: the vector it will produce
476     */
477
478void openpic_inittimer(unsigned int timer, unsigned int pri, unsigned int vec)
479{
480    check_arg_timer(timer);
481    check_arg_pri(pri);
482    check_arg_vec(vec);
483    openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority,
484                            OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
485                            (pri << OPENPIC_PRIORITY_SHIFT) | vec);
486}
487
488    /*
489     *  Map a timer interrupt to one or more CPUs
490     */
491
492void openpic_maptimer(unsigned int timer, unsigned int cpumask)
493{
494    check_arg_timer(timer);
495    openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask);
496}
497
498    /*
499     *  Set base count and / or enable / disable interrupt.
500     */
501
502
503void openpic_settimer(unsigned int timer, unsigned int base_count, int irq_enable)
504{
505        check_arg_timer(timer);
506        if ( base_count )
507                openpic_write(&OpenPIC->Global.Timer[timer].Base_Count, base_count);
508        if ( irq_enable )
509                openpic_clearfield(&OpenPIC->Global.Timer[timer].Vector_Priority, OPENPIC_MASK);
510        else
511                openpic_setfield(&OpenPIC->Global.Timer[timer].Vector_Priority, OPENPIC_MASK);
512}
513
514unsigned int openpic_gettimer(unsigned int timer)
515{
516        check_arg_timer(timer);
517        return (openpic_read(&OpenPIC->Global.Timer[timer].Current_Count) & ~OPENPIC_MASK);
518}
519
520/* -------- Interrupt Sources ---------------------------------------------- */
521
522    /*
523     *  Enable/disable an interrupt source
524     */
525
526void openpic_enable_irq(unsigned int irq)
527{
528unsigned long flags;
529    check_arg_irq(irq);
530        rtems_interrupt_disable(flags);
531    openpic_clearfield(&OpenPIC->SOURCE(irq).Vector_Priority, OPENPIC_MASK);
532        rtems_interrupt_enable(flags);
533}
534
535int openpic_disable_irq(unsigned int irq)
536{
537int           rval;
538unsigned long flags;
539    check_arg_irq(irq);
540        if ( irq < 0 || irq >=NumSources )
541                return -1;
542        rtems_interrupt_disable(flags);
543        rval = openpic_readfield(&OpenPIC->SOURCE(irq).Vector_Priority, OPENPIC_MASK) ? 0 : 1;
544    openpic_setfield(&OpenPIC->SOURCE(irq).Vector_Priority, OPENPIC_MASK);
545        rtems_interrupt_enable(flags);
546        return rval;
547}
548
549    /*
550     *  Initialize an interrupt source (and disable it!)
551     *
552     *  irq: OpenPIC interrupt number
553     *  pri: interrupt source priority
554     *  vec: the vector it will produce
555     *  pol: polarity (1 for positive, 0 for negative)
556     *  sense: 1 for level, 0 for edge
557     */
558
559void openpic_initirq(unsigned int irq, unsigned int pri, unsigned int vec, int pol, int sense)
560{
561#if 0
562  printk("openpic_initirq: irq=%d pri=%d vec=%d pol=%d sense=%d\n",
563    irq, pri, vec, pol, sense);
564#endif
565
566    check_arg_irq(irq);
567    check_arg_pri(pri);
568    check_arg_vec(vec);
569    openpic_safe_writefield(&OpenPIC->SOURCE(irq).Vector_Priority,
570                            OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
571                            OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL,
572                            (pri << OPENPIC_PRIORITY_SHIFT) | vec |
573                            (pol ? OPENPIC_SENSE_POLARITY : 0) |
574                            (sense ? OPENPIC_SENSE_LEVEL : 0));
575}
576
577    /*
578     *  Map an interrupt source to one or more CPUs
579     */
580
581void openpic_mapirq(unsigned int irq, unsigned int cpumask)
582{
583    check_arg_irq(irq);
584    openpic_write(&OpenPIC->SOURCE(irq).Destination, cpumask);
585}
586
587        /*
588         * Get the current priority of an external interrupt
589         */
590unsigned int openpic_get_source_priority(unsigned int irq)
591{
592    check_arg_irq(irq);
593        return openpic_readfield(&OpenPIC->SOURCE(irq).Vector_Priority,
594                                                         OPENPIC_PRIORITY_MASK) >> OPENPIC_PRIORITY_SHIFT;
595}
596
597void openpic_set_source_priority(unsigned int irq, unsigned int pri)
598{
599unsigned long flags;
600    check_arg_irq(irq);
601    check_arg_pri(pri);
602        rtems_interrupt_disable(flags);
603        openpic_writefield(
604                                        &OpenPIC->SOURCE(irq).Vector_Priority,
605                                        OPENPIC_PRIORITY_MASK,
606                                        pri << OPENPIC_PRIORITY_SHIFT);
607        rtems_interrupt_enable(flags);
608}
609    /*
610     *  Set the sense for an interrupt source (and disable it!)
611     *
612     *  sense: 1 for level, 0 for edge
613     */
614
615void openpic_set_sense(unsigned int irq, int sense)
616{
617    check_arg_irq(irq);
618    openpic_safe_writefield(&OpenPIC->SOURCE(irq).Vector_Priority,
619                            OPENPIC_SENSE_LEVEL,
620                            (sense ? OPENPIC_SENSE_LEVEL : 0));
621}
Note: See TracBrowser for help on using the repository browser.