blob: df0f76dc0ce36f23a13347fc9fce21a1403f5b1b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
3 *
4 * Copyright (C) 1997 Geert Uytterhoeven
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/config.h>
12#include <linux/types.h>
13#include <linux/kernel.h>
14#include <linux/sched.h>
15#include <linux/init.h>
16#include <linux/irq.h>
17#include <linux/interrupt.h>
18#include <linux/sysdev.h>
19#include <linux/errno.h>
20#include <asm/ptrace.h>
21#include <asm/signal.h>
22#include <asm/io.h>
23#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/sections.h>
25#include <asm/open_pic.h>
26#include <asm/i8259.h>
Paul Mackerrasfd582ec2005-10-11 22:08:12 +100027#include <asm/machdep.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include "open_pic_defs.h"
30
31#if defined(CONFIG_PRPMC800) || defined(CONFIG_85xx)
32#define OPENPIC_BIG_ENDIAN
33#endif
34
35void __iomem *OpenPIC_Addr;
36static volatile struct OpenPIC __iomem *OpenPIC = NULL;
37
38/*
39 * We define OpenPIC_InitSenses table thusly:
40 * bit 0x1: sense, 0 for edge and 1 for level.
41 * bit 0x2: polarity, 0 for negative, 1 for positive.
42 */
43u_int OpenPIC_NumInitSenses __initdata = 0;
44u_char *OpenPIC_InitSenses __initdata = NULL;
45extern int use_of_interrupt_tree;
46
47static u_int NumProcessors;
48static u_int NumSources;
49static int open_pic_irq_offset;
50static volatile OpenPIC_Source __iomem *ISR[NR_IRQS];
51static int openpic_cascade_irq = -1;
52static int (*openpic_cascade_fn)(struct pt_regs *);
53
54/* Global Operations */
55static void openpic_disable_8259_pass_through(void);
56static void openpic_set_spurious(u_int vector);
57
58#ifdef CONFIG_SMP
59/* Interprocessor Interrupts */
60static void openpic_initipi(u_int ipi, u_int pri, u_int vector);
61static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *);
62#endif
63
64/* Timer Interrupts */
65static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
66static void openpic_maptimer(u_int timer, cpumask_t cpumask);
67
68/* Interrupt Sources */
69static void openpic_enable_irq(u_int irq);
70static void openpic_disable_irq(u_int irq);
71static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
72 int is_level);
73static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask);
74
75/*
76 * These functions are not used but the code is kept here
77 * for completeness and future reference.
78 */
79#ifdef notused
80static void openpic_enable_8259_pass_through(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081static u_int openpic_get_spurious(void);
82static void openpic_set_sense(u_int irq, int sense);
83#endif /* notused */
84
85/*
86 * Description of the openpic for the higher-level irq code
87 */
88static void openpic_end_irq(unsigned int irq_nr);
89static void openpic_ack_irq(unsigned int irq_nr);
90static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask);
91
92struct hw_interrupt_type open_pic = {
93 .typename = " OpenPIC ",
94 .enable = openpic_enable_irq,
95 .disable = openpic_disable_irq,
96 .ack = openpic_ack_irq,
97 .end = openpic_end_irq,
98 .set_affinity = openpic_set_affinity,
99};
100
101#ifdef CONFIG_SMP
102static void openpic_end_ipi(unsigned int irq_nr);
103static void openpic_ack_ipi(unsigned int irq_nr);
104static void openpic_enable_ipi(unsigned int irq_nr);
105static void openpic_disable_ipi(unsigned int irq_nr);
106
107struct hw_interrupt_type open_pic_ipi = {
108 .typename = " OpenPIC ",
109 .enable = openpic_enable_ipi,
110 .disable = openpic_disable_ipi,
111 .ack = openpic_ack_ipi,
112 .end = openpic_end_ipi,
113};
114#endif /* CONFIG_SMP */
115
116/*
117 * Accesses to the current processor's openpic registers
118 */
119#ifdef CONFIG_SMP
120#define THIS_CPU Processor[cpu]
121#define DECL_THIS_CPU int cpu = smp_hw_index[smp_processor_id()]
122#define CHECK_THIS_CPU check_arg_cpu(cpu)
123#else
124#define THIS_CPU Processor[0]
125#define DECL_THIS_CPU
126#define CHECK_THIS_CPU
127#endif /* CONFIG_SMP */
128
129#if 1
130#define check_arg_ipi(ipi) \
131 if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
132 printk("open_pic.c:%d: invalid ipi %d\n", __LINE__, ipi);
133#define check_arg_timer(timer) \
134 if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
135 printk("open_pic.c:%d: invalid timer %d\n", __LINE__, timer);
136#define check_arg_vec(vec) \
137 if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
138 printk("open_pic.c:%d: invalid vector %d\n", __LINE__, vec);
139#define check_arg_pri(pri) \
140 if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
141 printk("open_pic.c:%d: invalid priority %d\n", __LINE__, pri);
142/*
143 * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's
144 * data has probably been corrupted and we're going to panic or deadlock later
145 * anyway --Troy
146 */
147#define check_arg_irq(irq) \
148 if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \
149 || ISR[irq - open_pic_irq_offset] == 0) { \
150 printk("open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \
151 dump_stack(); }
152#define check_arg_cpu(cpu) \
153 if (cpu < 0 || cpu >= NumProcessors){ \
154 printk("open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \
155 dump_stack(); }
156#else
157#define check_arg_ipi(ipi) do {} while (0)
158#define check_arg_timer(timer) do {} while (0)
159#define check_arg_vec(vec) do {} while (0)
160#define check_arg_pri(pri) do {} while (0)
161#define check_arg_irq(irq) do {} while (0)
162#define check_arg_cpu(cpu) do {} while (0)
163#endif
164
165u_int openpic_read(volatile u_int __iomem *addr)
166{
167 u_int val;
168
169#ifdef OPENPIC_BIG_ENDIAN
170 val = in_be32(addr);
171#else
172 val = in_le32(addr);
173#endif
174 return val;
175}
176
177static inline void openpic_write(volatile u_int __iomem *addr, u_int val)
178{
179#ifdef OPENPIC_BIG_ENDIAN
180 out_be32(addr, val);
181#else
182 out_le32(addr, val);
183#endif
184}
185
186static inline u_int openpic_readfield(volatile u_int __iomem *addr, u_int mask)
187{
188 u_int val = openpic_read(addr);
189 return val & mask;
190}
191
192inline void openpic_writefield(volatile u_int __iomem *addr, u_int mask,
193 u_int field)
194{
195 u_int val = openpic_read(addr);
196 openpic_write(addr, (val & ~mask) | (field & mask));
197}
198
199static inline void openpic_clearfield(volatile u_int __iomem *addr, u_int mask)
200{
201 openpic_writefield(addr, mask, 0);
202}
203
204static inline void openpic_setfield(volatile u_int __iomem *addr, u_int mask)
205{
206 openpic_writefield(addr, mask, mask);
207}
208
209static void openpic_safe_writefield(volatile u_int __iomem *addr, u_int mask,
210 u_int field)
211{
212 openpic_setfield(addr, OPENPIC_MASK);
213 while (openpic_read(addr) & OPENPIC_ACTIVITY);
214 openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
215}
216
217#ifdef CONFIG_SMP
218/* yes this is right ... bug, feature, you decide! -- tgall */
219u_int openpic_read_IPI(volatile u_int __iomem * addr)
220{
221 u_int val = 0;
222#if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3)
223 val = in_be32(addr);
224#else
225 val = in_le32(addr);
226#endif
227 return val;
228}
229
230/* because of the power3 be / le above, this is needed */
231inline void openpic_writefield_IPI(volatile u_int __iomem * addr, u_int mask, u_int field)
232{
233 u_int val = openpic_read_IPI(addr);
234 openpic_write(addr, (val & ~mask) | (field & mask));
235}
236
237static inline void openpic_clearfield_IPI(volatile u_int __iomem *addr, u_int mask)
238{
239 openpic_writefield_IPI(addr, mask, 0);
240}
241
242static inline void openpic_setfield_IPI(volatile u_int __iomem *addr, u_int mask)
243{
244 openpic_writefield_IPI(addr, mask, mask);
245}
246
247static void openpic_safe_writefield_IPI(volatile u_int __iomem *addr, u_int mask, u_int field)
248{
249 openpic_setfield_IPI(addr, OPENPIC_MASK);
250
251 /* wait until it's not in use */
252 /* BenH: Is this code really enough ? I would rather check the result
253 * and eventually retry ...
254 */
255 while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY);
256
257 openpic_writefield_IPI(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
258}
259#endif /* CONFIG_SMP */
260
261#ifdef CONFIG_EPIC_SERIAL_MODE
262/* On platforms that may use EPIC serial mode, the default is enabled. */
263int epic_serial_mode = 1;
264
265static void __init openpic_eicr_set_clk(u_int clkval)
266{
267 openpic_writefield(&OpenPIC->Global.Global_Configuration1,
268 OPENPIC_EICR_S_CLK_MASK, (clkval << 28));
269}
270
271static void __init openpic_enable_sie(void)
272{
273 openpic_setfield(&OpenPIC->Global.Global_Configuration1,
274 OPENPIC_EICR_SIE);
275}
276#endif
277
Benjamin Herrenschmidtb16eeb42005-05-27 12:53:02 -0700278#if defined(CONFIG_EPIC_SERIAL_MODE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279static void openpic_reset(void)
280{
281 openpic_setfield(&OpenPIC->Global.Global_Configuration0,
282 OPENPIC_CONFIG_RESET);
283 while (openpic_readfield(&OpenPIC->Global.Global_Configuration0,
284 OPENPIC_CONFIG_RESET))
285 mb();
286}
287#endif
288
289void __init openpic_set_sources(int first_irq, int num_irqs, void __iomem *first_ISR)
290{
291 volatile OpenPIC_Source __iomem *src = first_ISR;
292 int i, last_irq;
293
294 last_irq = first_irq + num_irqs;
295 if (last_irq > NumSources)
296 NumSources = last_irq;
297 if (src == 0)
298 src = &((struct OpenPIC __iomem *)OpenPIC_Addr)->Source[first_irq];
299 for (i = first_irq; i < last_irq; ++i, ++src)
300 ISR[i] = src;
301}
302
303/*
304 * The `offset' parameter defines where the interrupts handled by the
305 * OpenPIC start in the space of interrupt numbers that the kernel knows
306 * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the
307 * kernel's interrupt numbering scheme.
308 * We assume there is only one OpenPIC.
309 */
310void __init openpic_init(int offset)
311{
312 u_int t, i;
313 u_int timerfreq;
314 const char *version;
315
316 if (!OpenPIC_Addr) {
317 printk("No OpenPIC found !\n");
318 return;
319 }
320 OpenPIC = (volatile struct OpenPIC __iomem *)OpenPIC_Addr;
321
322#ifdef CONFIG_EPIC_SERIAL_MODE
323 /* Have to start from ground zero.
324 */
325 openpic_reset();
326#endif
327
328 if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122);
329
330 t = openpic_read(&OpenPIC->Global.Feature_Reporting0);
331 switch (t & OPENPIC_FEATURE_VERSION_MASK) {
332 case 1:
333 version = "1.0";
334 break;
335 case 2:
336 version = "1.2";
337 break;
338 case 3:
339 version = "1.3";
340 break;
341 default:
342 version = "?";
343 break;
344 }
345 NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
346 OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
347 if (NumSources == 0)
348 openpic_set_sources(0,
349 ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
350 OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1,
351 NULL);
352 printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n",
353 version, NumProcessors, NumSources, OpenPIC);
354 timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
355 if (timerfreq)
356 printk("OpenPIC timer frequency is %d.%06d MHz\n",
357 timerfreq / 1000000, timerfreq % 1000000);
358
359 open_pic_irq_offset = offset;
360
361 /* Initialize timer interrupts */
362 if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba);
363 for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
364 /* Disabled, Priority 0 */
365 openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset);
366 /* No processor */
367 openpic_maptimer(i, CPU_MASK_NONE);
368 }
369
370#ifdef CONFIG_SMP
371 /* Initialize IPI interrupts */
372 if ( ppc_md.progress ) ppc_md.progress("openpic: ipi",0x3bb);
373 for (i = 0; i < OPENPIC_NUM_IPI; i++) {
Benjamin Herrenschmidte4ee69c2005-06-27 14:36:32 -0700374 /* Disabled, increased priorities 10..13 */
375 openpic_initipi(i, OPENPIC_PRIORITY_IPI_BASE+i,
376 OPENPIC_VEC_IPI+i+offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 /* IPIs are per-CPU */
378 irq_desc[OPENPIC_VEC_IPI+i+offset].status |= IRQ_PER_CPU;
379 irq_desc[OPENPIC_VEC_IPI+i+offset].handler = &open_pic_ipi;
380 }
381#endif
382
383 /* Initialize external interrupts */
384 if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc);
385
386 openpic_set_priority(0xf);
387
388 /* Init all external sources, including possibly the cascade. */
389 for (i = 0; i < NumSources; i++) {
390 int sense;
391
392 if (ISR[i] == 0)
393 continue;
394
395 /* the bootloader may have left it enabled (bad !) */
396 openpic_disable_irq(i+offset);
397
398 sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \
399 (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE);
400
401 if (sense & IRQ_SENSE_MASK)
402 irq_desc[i+offset].status = IRQ_LEVEL;
403
Benjamin Herrenschmidte4ee69c2005-06-27 14:36:32 -0700404 /* Enabled, Default priority */
405 openpic_initirq(i, OPENPIC_PRIORITY_DEFAULT, i+offset,
406 (sense & IRQ_POLARITY_MASK),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 (sense & IRQ_SENSE_MASK));
408 /* Processor 0 */
409 openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE);
410 }
411
412 /* Init descriptors */
413 for (i = offset; i < NumSources + offset; i++)
414 irq_desc[i].handler = &open_pic;
415
416 /* Initialize the spurious interrupt */
417 if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd);
418 openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
419 openpic_disable_8259_pass_through();
420#ifdef CONFIG_EPIC_SERIAL_MODE
421 if (epic_serial_mode) {
422 openpic_eicr_set_clk(7); /* Slowest value until we know better */
423 openpic_enable_sie();
424 }
425#endif
426 openpic_set_priority(0);
427
428 if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222);
429}
430
431#ifdef notused
432static void openpic_enable_8259_pass_through(void)
433{
434 openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
435 OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
436}
437#endif /* notused */
438
439static void openpic_disable_8259_pass_through(void)
440{
441 openpic_setfield(&OpenPIC->Global.Global_Configuration0,
442 OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
443}
444
445/*
446 * Find out the current interrupt
447 */
448u_int openpic_irq(void)
449{
450 u_int vec;
451 DECL_THIS_CPU;
452
453 CHECK_THIS_CPU;
454 vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge,
455 OPENPIC_VECTOR_MASK);
456 return vec;
457}
458
459void openpic_eoi(void)
460{
461 DECL_THIS_CPU;
462
463 CHECK_THIS_CPU;
464 openpic_write(&OpenPIC->THIS_CPU.EOI, 0);
465 /* Handle PCI write posting */
466 (void)openpic_read(&OpenPIC->THIS_CPU.EOI);
467}
468
Benjamin Herrenschmidt7a648b92005-04-16 15:24:18 -0700469u_int openpic_get_priority(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
471 DECL_THIS_CPU;
472
473 CHECK_THIS_CPU;
474 return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
475 OPENPIC_CURRENT_TASK_PRIORITY_MASK);
476}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478void openpic_set_priority(u_int pri)
479{
480 DECL_THIS_CPU;
481
482 CHECK_THIS_CPU;
483 check_arg_pri(pri);
484 openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority,
485 OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
486}
487
488/*
489 * Get/set the spurious vector
490 */
491#ifdef notused
492static u_int openpic_get_spurious(void)
493{
494 return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
495 OPENPIC_VECTOR_MASK);
496}
497#endif /* notused */
498
499static void openpic_set_spurious(u_int vec)
500{
501 check_arg_vec(vec);
502 openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
503 vec);
504}
505
506#ifdef CONFIG_SMP
507/*
508 * Convert a cpu mask from logical to physical cpu numbers.
509 */
510static inline cpumask_t physmask(cpumask_t cpumask)
511{
512 int i;
513 cpumask_t mask = CPU_MASK_NONE;
514
515 cpus_and(cpumask, cpu_online_map, cpumask);
516
517 for (i = 0; i < NR_CPUS; i++)
518 if (cpu_isset(i, cpumask))
519 cpu_set(smp_hw_index[i], mask);
520
521 return mask;
522}
523#else
524#define physmask(cpumask) (cpumask)
525#endif
526
527void openpic_reset_processor_phys(u_int mask)
528{
529 openpic_write(&OpenPIC->Global.Processor_Initialization, mask);
530}
531
532#if defined(CONFIG_SMP) || defined(CONFIG_PM)
533static DEFINE_SPINLOCK(openpic_setup_lock);
534#endif
535
536#ifdef CONFIG_SMP
537/*
538 * Initialize an interprocessor interrupt (and disable it)
539 *
540 * ipi: OpenPIC interprocessor interrupt number
541 * pri: interrupt source priority
542 * vec: the vector it will produce
543 */
544static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec)
545{
546 check_arg_ipi(ipi);
547 check_arg_pri(pri);
548 check_arg_vec(vec);
549 openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi),
550 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
551 (pri << OPENPIC_PRIORITY_SHIFT) | vec);
552}
553
554/*
555 * Send an IPI to one or more CPUs
556 *
557 * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI)
558 * and not a system-wide interrupt number
559 */
560void openpic_cause_IPI(u_int ipi, cpumask_t cpumask)
561{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 DECL_THIS_CPU;
563
564 CHECK_THIS_CPU;
565 check_arg_ipi(ipi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi),
567 cpus_addr(physmask(cpumask))[0]);
568}
569
570void openpic_request_IPIs(void)
571{
572 int i;
573
574 /*
575 * Make sure this matches what is defined in smp.c for
576 * smp_message_{pass|recv}() or what shows up in
577 * /proc/interrupts will be wrong!!! --Troy */
578
579 if (OpenPIC == NULL)
580 return;
581
582 /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
583 request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset,
584 openpic_ipi_action, SA_INTERRUPT,
585 "IPI0 (call function)", NULL);
586 request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+1,
587 openpic_ipi_action, SA_INTERRUPT,
588 "IPI1 (reschedule)", NULL);
589 request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+2,
590 openpic_ipi_action, SA_INTERRUPT,
591 "IPI2 (invalidate tlb)", NULL);
592 request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+3,
593 openpic_ipi_action, SA_INTERRUPT,
594 "IPI3 (xmon break)", NULL);
595
596 for ( i = 0; i < OPENPIC_NUM_IPI ; i++ )
597 openpic_enable_ipi(OPENPIC_VEC_IPI+open_pic_irq_offset+i);
598}
599
600/*
601 * Do per-cpu setup for SMP systems.
602 *
603 * Get IPI's working and start taking interrupts.
604 * -- Cort
605 */
606
607void __devinit do_openpic_setup_cpu(void)
608{
609#ifdef CONFIG_IRQ_ALL_CPUS
610 int i;
611 cpumask_t msk = CPU_MASK_NONE;
612#endif
613 spin_lock(&openpic_setup_lock);
614
615#ifdef CONFIG_IRQ_ALL_CPUS
616 cpu_set(smp_hw_index[smp_processor_id()], msk);
617
618 /* let the openpic know we want intrs. default affinity
619 * is 0xffffffff until changed via /proc
620 * That's how it's done on x86. If we want it differently, then
621 * we should make sure we also change the default values of irq_affinity
622 * in irq.c.
623 */
624 for (i = 0; i < NumSources; i++)
625 openpic_mapirq(i, msk, CPU_MASK_ALL);
626#endif /* CONFIG_IRQ_ALL_CPUS */
627 openpic_set_priority(0);
628
629 spin_unlock(&openpic_setup_lock);
630}
631#endif /* CONFIG_SMP */
632
633/*
634 * Initialize a timer interrupt (and disable it)
635 *
636 * timer: OpenPIC timer number
637 * pri: interrupt source priority
638 * vec: the vector it will produce
639 */
640static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec)
641{
642 check_arg_timer(timer);
643 check_arg_pri(pri);
644 check_arg_vec(vec);
645 openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority,
646 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
647 (pri << OPENPIC_PRIORITY_SHIFT) | vec);
648}
649
650/*
651 * Map a timer interrupt to one or more CPUs
652 */
653static void __init openpic_maptimer(u_int timer, cpumask_t cpumask)
654{
655 cpumask_t phys = physmask(cpumask);
656 check_arg_timer(timer);
657 openpic_write(&OpenPIC->Global.Timer[timer].Destination,
658 cpus_addr(phys)[0]);
659}
660
661/*
Benjamin Herrenschmidte4ee69c2005-06-27 14:36:32 -0700662 * Change the priority of an interrupt
663 */
664void __init
665openpic_set_irq_priority(u_int irq, u_int pri)
666{
667 check_arg_irq(irq);
668 openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority,
669 OPENPIC_PRIORITY_MASK,
670 pri << OPENPIC_PRIORITY_SHIFT);
671}
672
673/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 * Initalize the interrupt source which will generate an NMI.
675 * This raises the interrupt's priority from 8 to 9.
676 *
677 * irq: The logical IRQ which generates an NMI.
678 */
679void __init
680openpic_init_nmi_irq(u_int irq)
681{
682 check_arg_irq(irq);
Benjamin Herrenschmidte4ee69c2005-06-27 14:36:32 -0700683 openpic_set_irq_priority(irq, OPENPIC_PRIORITY_NMI);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
685
686/*
687 *
688 * All functions below take an offset'ed irq argument
689 *
690 */
691
692/*
693 * Hookup a cascade to the OpenPIC.
694 */
695
696static struct irqaction openpic_cascade_irqaction = {
697 .handler = no_action,
698 .flags = SA_INTERRUPT,
699 .mask = CPU_MASK_NONE,
700};
701
702void __init
703openpic_hookup_cascade(u_int irq, char *name,
704 int (*cascade_fn)(struct pt_regs *))
705{
706 openpic_cascade_irq = irq;
707 openpic_cascade_fn = cascade_fn;
708
709 if (setup_irq(irq, &openpic_cascade_irqaction))
710 printk("Unable to get OpenPIC IRQ %d for cascade\n",
711 irq - open_pic_irq_offset);
712}
713
714/*
715 * Enable/disable an external interrupt source
716 *
717 * Externally called, irq is an offseted system-wide interrupt number
718 */
719static void openpic_enable_irq(u_int irq)
720{
721 volatile u_int __iomem *vpp;
722
723 check_arg_irq(irq);
724 vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority;
725 openpic_clearfield(vpp, OPENPIC_MASK);
726 /* make sure mask gets to controller before we return to user */
727 do {
728 mb(); /* sync is probably useless here */
729 } while (openpic_readfield(vpp, OPENPIC_MASK));
730}
731
732static void openpic_disable_irq(u_int irq)
733{
734 volatile u_int __iomem *vpp;
735 u32 vp;
736
737 check_arg_irq(irq);
738 vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority;
739 openpic_setfield(vpp, OPENPIC_MASK);
740 /* make sure mask gets to controller before we return to user */
741 do {
742 mb(); /* sync is probably useless here */
743 vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY);
744 } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
745}
746
747#ifdef CONFIG_SMP
748/*
749 * Enable/disable an IPI interrupt source
750 *
751 * Externally called, irq is an offseted system-wide interrupt number
752 */
753void openpic_enable_ipi(u_int irq)
754{
755 irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset);
756 check_arg_ipi(irq);
757 openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
758
759}
760
761void openpic_disable_ipi(u_int irq)
762{
763 irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset);
764 check_arg_ipi(irq);
765 openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
766}
767#endif
768
769/*
770 * Initialize an interrupt source (and disable it!)
771 *
772 * irq: OpenPIC interrupt number
773 * pri: interrupt source priority
774 * vec: the vector it will produce
775 * pol: polarity (1 for positive, 0 for negative)
776 * sense: 1 for level, 0 for edge
777 */
778static void __init
779openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
780{
781 openpic_safe_writefield(&ISR[irq]->Vector_Priority,
782 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
783 OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
784 (pri << OPENPIC_PRIORITY_SHIFT) | vec |
785 (pol ? OPENPIC_POLARITY_POSITIVE :
786 OPENPIC_POLARITY_NEGATIVE) |
787 (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
788}
789
790/*
791 * Map an interrupt source to one or more CPUs
792 */
793static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask)
794{
795 if (ISR[irq] == 0)
796 return;
797 if (!cpus_empty(keepmask)) {
798 cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) };
799 cpus_and(irqdest, irqdest, keepmask);
800 cpus_or(physmask, physmask, irqdest);
801 }
802 openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]);
803}
804
805#ifdef notused
806/*
807 * Set the sense for an interrupt source (and disable it!)
808 *
809 * sense: 1 for level, 0 for edge
810 */
811static void openpic_set_sense(u_int irq, int sense)
812{
813 if (ISR[irq] != 0)
814 openpic_safe_writefield(&ISR[irq]->Vector_Priority,
815 OPENPIC_SENSE_LEVEL,
816 (sense ? OPENPIC_SENSE_LEVEL : 0));
817}
818#endif /* notused */
819
820/* No spinlocks, should not be necessary with the OpenPIC
821 * (1 register = 1 interrupt and we have the desc lock).
822 */
823static void openpic_ack_irq(unsigned int irq_nr)
824{
825#ifdef __SLOW_VERSION__
826 openpic_disable_irq(irq_nr);
827 openpic_eoi();
828#else
829 if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
830 openpic_eoi();
831#endif
832}
833
834static void openpic_end_irq(unsigned int irq_nr)
835{
836#ifdef __SLOW_VERSION__
837 if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
838 && irq_desc[irq_nr].action)
839 openpic_enable_irq(irq_nr);
840#else
841 if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
842 openpic_eoi();
843#endif
844}
845
846static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask)
847{
848 openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE);
849}
850
851#ifdef CONFIG_SMP
852static void openpic_ack_ipi(unsigned int irq_nr)
853{
854 openpic_eoi();
855}
856
857static void openpic_end_ipi(unsigned int irq_nr)
858{
859}
860
861static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs)
862{
863 smp_message_recv(cpl-OPENPIC_VEC_IPI-open_pic_irq_offset, regs);
864 return IRQ_HANDLED;
865}
866
867#endif /* CONFIG_SMP */
868
869int
870openpic_get_irq(struct pt_regs *regs)
871{
872 int irq = openpic_irq();
873
874 /*
875 * Check for the cascade interrupt and call the cascaded
876 * interrupt controller function (usually i8259_irq) if so.
877 * This should move to irq.c eventually. -- paulus
878 */
879 if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) {
880 int cirq = openpic_cascade_fn(regs);
881
882 /* Allow for the cascade being shared with other devices */
883 if (cirq != -1) {
884 irq = cirq;
885 openpic_eoi();
886 }
887 } else if (irq == OPENPIC_VEC_SPURIOUS)
888 irq = -1;
889 return irq;
890}
891
892#ifdef CONFIG_SMP
893void
894smp_openpic_message_pass(int target, int msg, unsigned long data, int wait)
895{
896 cpumask_t mask = CPU_MASK_ALL;
897 /* make sure we're sending something that translates to an IPI */
898 if (msg > 0x3) {
899 printk("SMP %d: smp_message_pass: unknown msg %d\n",
900 smp_processor_id(), msg);
901 return;
902 }
903 switch (target) {
904 case MSG_ALL:
905 openpic_cause_IPI(msg, mask);
906 break;
907 case MSG_ALL_BUT_SELF:
908 cpu_clear(smp_processor_id(), mask);
909 openpic_cause_IPI(msg, mask);
910 break;
911 default:
912 openpic_cause_IPI(msg, cpumask_of_cpu(target));
913 break;
914 }
915}
916#endif /* CONFIG_SMP */
917
918#ifdef CONFIG_PM
919
920/*
921 * We implement the IRQ controller as a sysdev and put it
922 * to sleep at powerdown stage (the callback is named suspend,
923 * but it's old semantics, for the Device Model, it's really
924 * powerdown). The possible problem is that another sysdev that
925 * happens to be suspend after this one will have interrupts off,
926 * that may be an issue... For now, this isn't an issue on pmac
927 * though...
928 */
929
930static u32 save_ipi_vp[OPENPIC_NUM_IPI];
931static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES];
932static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES];
933static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS];
934static int openpic_suspend_count;
935
936static void openpic_cached_enable_irq(u_int irq)
937{
938 check_arg_irq(irq);
939 save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK;
940}
941
942static void openpic_cached_disable_irq(u_int irq)
943{
944 check_arg_irq(irq);
945 save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK;
946}
947
948/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
949 * we need something better to deal with that... Maybe switch to S1 for
950 * cpufreq changes
951 */
Pavel Machek829ca9a2005-09-03 15:56:56 -0700952int openpic_suspend(struct sys_device *sysdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
954 int i;
955 unsigned long flags;
956
957 spin_lock_irqsave(&openpic_setup_lock, flags);
958
959 if (openpic_suspend_count++ > 0) {
960 spin_unlock_irqrestore(&openpic_setup_lock, flags);
961 return 0;
962 }
963
964 openpic_set_priority(0xf);
965
966 open_pic.enable = openpic_cached_enable_irq;
967 open_pic.disable = openpic_cached_disable_irq;
968
969 for (i=0; i<NumProcessors; i++) {
970 save_cpu_task_pri[i] = openpic_read(&OpenPIC->Processor[i].Current_Task_Priority);
971 openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority,
972 OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
973 }
974
975 for (i=0; i<OPENPIC_NUM_IPI; i++)
976 save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i));
977 for (i=0; i<NumSources; i++) {
978 if (ISR[i] == 0)
979 continue;
980 save_irq_src_vp[i] = openpic_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY;
981 save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination);
982 }
983
984 spin_unlock_irqrestore(&openpic_setup_lock, flags);
985
986 return 0;
987}
988
989/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
990 * we need something better to deal with that... Maybe switch to S1 for
991 * cpufreq changes
992 */
993int openpic_resume(struct sys_device *sysdev)
994{
995 int i;
996 unsigned long flags;
997 u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
998 OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK |
999 OPENPIC_MASK;
1000
1001 spin_lock_irqsave(&openpic_setup_lock, flags);
1002
1003 if ((--openpic_suspend_count) > 0) {
1004 spin_unlock_irqrestore(&openpic_setup_lock, flags);
1005 return 0;
1006 }
1007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 /* OpenPIC sometimes seem to need some time to be fully back up... */
1009 do {
1010 openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
1011 } while(openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK)
1012 != OPENPIC_VEC_SPURIOUS);
1013
1014 openpic_disable_8259_pass_through();
1015
1016 for (i=0; i<OPENPIC_NUM_IPI; i++)
1017 openpic_write(&OpenPIC->Global.IPI_Vector_Priority(i),
1018 save_ipi_vp[i]);
1019 for (i=0; i<NumSources; i++) {
1020 if (ISR[i] == 0)
1021 continue;
1022 openpic_write(&ISR[i]->Destination, save_irq_src_dest[i]);
1023 openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
1024 /* make sure mask gets to controller before we return to user */
1025 do {
1026 openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
1027 } while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask)
1028 != (save_irq_src_vp[i] & vppmask));
1029 }
1030 for (i=0; i<NumProcessors; i++)
1031 openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
1032 save_cpu_task_pri[i]);
1033
1034 open_pic.enable = openpic_enable_irq;
1035 open_pic.disable = openpic_disable_irq;
1036
1037 openpic_set_priority(0);
1038
1039 spin_unlock_irqrestore(&openpic_setup_lock, flags);
1040
1041 return 0;
1042}
1043
1044#endif /* CONFIG_PM */
1045
1046static struct sysdev_class openpic_sysclass = {
1047 set_kset_name("openpic"),
1048};
1049
1050static struct sys_device device_openpic = {
1051 .id = 0,
1052 .cls = &openpic_sysclass,
1053};
1054
1055static struct sysdev_driver driver_openpic = {
1056#ifdef CONFIG_PM
1057 .suspend = &openpic_suspend,
1058 .resume = &openpic_resume,
1059#endif /* CONFIG_PM */
1060};
1061
1062static int __init init_openpic_sysfs(void)
1063{
1064 int rc;
1065
1066 if (!OpenPIC_Addr)
1067 return -ENODEV;
1068 printk(KERN_DEBUG "Registering openpic with sysfs...\n");
1069 rc = sysdev_class_register(&openpic_sysclass);
1070 if (rc) {
1071 printk(KERN_ERR "Failed registering openpic sys class\n");
1072 return -ENODEV;
1073 }
1074 rc = sysdev_register(&device_openpic);
1075 if (rc) {
1076 printk(KERN_ERR "Failed registering openpic sys device\n");
1077 return -ENODEV;
1078 }
1079 rc = sysdev_driver_register(&openpic_sysclass, &driver_openpic);
1080 if (rc) {
1081 printk(KERN_ERR "Failed registering openpic sys driver\n");
1082 return -ENODEV;
1083 }
1084 return 0;
1085}
1086
1087subsys_initcall(init_openpic_sysfs);
1088