ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc64 / kernel / open_pic.c
1 /*
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/init.h>
15 #include <linux/irq.h>
16 #include <linux/smp.h>
17 #include <linux/interrupt.h>
18 #include <asm/ptrace.h>
19 #include <asm/signal.h>
20 #include <asm/io.h>
21 #include <asm/pgtable.h>
22 #include <asm/irq.h>
23 #include <asm/prom.h>
24
25 #include <asm/machdep.h>
26
27 #include "open_pic.h"
28 #include "open_pic_defs.h"
29 #include "i8259.h"
30 #include <asm/ppcdebug.h>
31
32 void* OpenPIC_Addr;
33 static volatile struct OpenPIC *OpenPIC = NULL;
34 u_int OpenPIC_NumInitSenses __initdata = 0;
35 u_char *OpenPIC_InitSenses __initdata = NULL;
36
37 /*
38  *  Local (static) OpenPIC Operations
39  */
40
41
42 /* Global Operations */
43 static void openpic_reset(void);
44 static void openpic_enable_8259_pass_through(void);
45 static void openpic_disable_8259_pass_through(void);
46 static u_int openpic_irq(void);
47 static void openpic_eoi(void);
48 static u_int openpic_get_priority(void);
49 static void openpic_set_priority(u_int pri);
50 static u_int openpic_get_spurious(void);
51 static void openpic_set_spurious(u_int vector);
52
53 #ifdef CONFIG_SMP
54 /* Interprocessor Interrupts */
55 static void openpic_initipi(u_int ipi, u_int pri, u_int vector);
56 static irqreturn_t openpic_ipi_action(int cpl, void *dev_id,
57                                         struct pt_regs *regs);
58 #endif
59
60 /* Timer Interrupts */
61 static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
62 static void openpic_maptimer(u_int timer, u_int cpumask);
63
64 /* Interrupt Sources */
65 static void openpic_enable_irq(u_int irq);
66 static void openpic_disable_irq(u_int irq);
67 static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
68                             int is_level);
69 static void openpic_mapirq(u_int irq, u_int cpumask);
70
71 static void find_ISUs(void);
72
73 static u_int NumProcessors;
74 static u_int NumSources;
75 static int NumISUs;
76 static int open_pic_irq_offset;
77 static volatile unsigned char* chrp_int_ack_special;
78
79 OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU];
80
81 static void openpic_end_irq(unsigned int irq_nr);
82 static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask);
83
84 struct hw_interrupt_type open_pic = {
85         " OpenPIC  ",
86         NULL,
87         NULL,
88         openpic_enable_irq,
89         openpic_disable_irq,
90         NULL,
91         openpic_end_irq,
92         openpic_set_affinity
93 };
94
95 #ifdef CONFIG_SMP
96 static void openpic_end_ipi(unsigned int irq_nr);
97 static void openpic_enable_ipi(unsigned int irq_nr);
98 static void openpic_disable_ipi(unsigned int irq_nr);
99
100 struct hw_interrupt_type open_pic_ipi = {
101         " OpenPIC  ",
102         NULL,
103         NULL,
104         openpic_enable_ipi,
105         openpic_disable_ipi,
106         NULL,
107         openpic_end_ipi,
108         NULL
109 };
110 #endif /* CONFIG_SMP */
111
112 unsigned int openpic_vec_ipi;
113 unsigned int openpic_vec_timer;
114 unsigned int openpic_vec_spurious;
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 = hard_smp_processor_id()
122 #define CHECK_THIS_CPU          check_arg_cpu(cpu)
123 #else
124 #define THIS_CPU                Processor[hard_smp_processor_id()]
125 #define DECL_THIS_CPU
126 #define CHECK_THIS_CPU
127 #endif /* CONFIG_SMP */
128
129 #if 0
130 #define check_arg_ipi(ipi) \
131     if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
132         printk(KERN_ERR "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(KERN_ERR "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(KERN_ERR "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(KERN_ERR "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       printk(KERN_ERR "open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \
150       dump_stack(); }
151 #define check_arg_cpu(cpu) \
152     if (cpu < 0 || cpu >= OPENPIC_MAX_PROCESSORS){ \
153         printk(KERN_ERR "open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \
154         dump_stack(); }
155 #else
156 #define check_arg_ipi(ipi)      do {} while (0)
157 #define check_arg_timer(timer)  do {} while (0)
158 #define check_arg_vec(vec)      do {} while (0)
159 #define check_arg_pri(pri)      do {} while (0)
160 #define check_arg_irq(irq)      do {} while (0)
161 #define check_arg_cpu(cpu)      do {} while (0)
162 #endif
163
164 #define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf]
165
166 void __init pSeries_init_openpic(void)
167 {
168         struct device_node *np;
169         int i;
170         unsigned int *addrp;
171         unsigned char* chrp_int_ack_special = 0;
172         unsigned char init_senses[NR_IRQS - NUM_ISA_INTERRUPTS];
173         int nmi_irq = -1;
174 #if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON)
175         struct device_node *kbd;
176 #endif
177
178         if (!(np = of_find_node_by_name(NULL, "pci"))
179             || !(addrp = (unsigned int *)
180                  get_property(np, "8259-interrupt-acknowledge", NULL)))
181                 printk(KERN_ERR "Cannot find pci to get ack address\n");
182         else
183                 chrp_int_ack_special = (unsigned char *)
184                         __ioremap(addrp[prom_n_addr_cells(np)-1], 1, _PAGE_NO_CACHE);
185         /* hydra still sets OpenPIC_InitSenses to a static set of values */
186         if (OpenPIC_InitSenses == NULL) {
187                 prom_get_irq_senses(init_senses, NUM_ISA_INTERRUPTS, NR_IRQS);
188                 OpenPIC_InitSenses = init_senses;
189                 OpenPIC_NumInitSenses = NR_IRQS - NUM_ISA_INTERRUPTS;
190         }
191         openpic_init(1, NUM_ISA_INTERRUPTS, chrp_int_ack_special, nmi_irq);
192         for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
193                 irq_desc[i].handler = &i8259_pic;
194         of_node_put(np);
195 }
196
197 static inline u_int openpic_read(volatile u_int *addr)
198 {
199         u_int val;
200
201         val = in_le32(addr);
202         return val;
203 }
204
205 static inline void openpic_write(volatile u_int *addr, u_int val)
206 {
207         out_le32(addr, val);
208 }
209
210 static inline u_int openpic_readfield(volatile u_int *addr, u_int mask)
211 {
212         u_int val = openpic_read(addr);
213         return val & mask;
214 }
215
216 static inline void openpic_writefield(volatile u_int *addr, u_int mask,
217                                u_int field)
218 {
219         u_int val = openpic_read(addr);
220         openpic_write(addr, (val & ~mask) | (field & mask));
221 }
222
223 static inline void openpic_clearfield(volatile u_int *addr, u_int mask)
224 {
225         openpic_writefield(addr, mask, 0);
226 }
227
228 static inline void openpic_setfield(volatile u_int *addr, u_int mask)
229 {
230         openpic_writefield(addr, mask, mask);
231 }
232
233 static void openpic_safe_writefield(volatile u_int *addr, u_int mask,
234                                     u_int field)
235 {
236         unsigned int loops = 100000;
237
238         openpic_setfield(addr, OPENPIC_MASK);
239         while (openpic_read(addr) & OPENPIC_ACTIVITY) {
240                 if (!loops--) {
241                         printk(KERN_ERR "openpic_safe_writefield timeout\n");
242                         break;
243                 }
244         }
245         openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
246 }
247
248 #ifdef CONFIG_SMP
249
250 static int broken_ipi_registers;
251
252 static u_int openpic_read_IPI(volatile u_int* addr)
253 {
254         u_int val = 0;
255
256         if (broken_ipi_registers)
257                 /* yes this is right ... bug, feature, you decide! -- tgall */
258                 val = in_be32(addr);
259         else
260                 val = in_le32(addr);
261
262         return val;
263 }
264
265 static void openpic_test_broken_IPI(void)
266 {
267         u_int t;
268
269         openpic_write(&OpenPIC->Global.IPI_Vector_Priority(0), OPENPIC_MASK);
270         t = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(0));
271         if (t == le32_to_cpu(OPENPIC_MASK)) {
272                 printk(KERN_INFO "OpenPIC reversed IPI registers detected\n");
273                 broken_ipi_registers = 1;
274         }
275 }
276
277 /* because of the power3 be / le above, this is needed */
278 static inline void openpic_writefield_IPI(volatile u_int* addr, u_int mask, u_int field)
279 {
280         u_int  val = openpic_read_IPI(addr);
281         openpic_write(addr, (val & ~mask) | (field & mask));
282 }
283
284 static inline void openpic_clearfield_IPI(volatile u_int *addr, u_int mask)
285 {
286         openpic_writefield_IPI(addr, mask, 0);
287 }
288
289 static inline void openpic_setfield_IPI(volatile u_int *addr, u_int mask)
290 {
291         openpic_writefield_IPI(addr, mask, mask);
292 }
293
294 static void openpic_safe_writefield_IPI(volatile u_int *addr, u_int mask, u_int field)
295 {
296         unsigned int loops = 100000;
297
298         openpic_setfield_IPI(addr, OPENPIC_MASK);
299
300         /* wait until it's not in use */
301         /* BenH: Is this code really enough ? I would rather check the result
302          *       and eventually retry ...
303          */
304         while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY) {
305                 if (!loops--) {
306                         printk(KERN_ERR "openpic_safe_writefield timeout\n");
307                         break;
308                 }
309         }
310
311         openpic_writefield_IPI(addr, mask, field | OPENPIC_MASK);
312 }
313 #endif /* CONFIG_SMP */
314
315 void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack,
316                          int programmer_switch_irq)
317 {
318         u_int t, i;
319         u_int timerfreq;
320         const char *version;
321
322         if (!OpenPIC_Addr) {
323                 printk(KERN_INFO "No OpenPIC found !\n");
324                 return;
325         }
326         OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr;
327
328         ppc64_boot_msg(0x20, "OpenPic Init");
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         NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
348                       OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1;
349         printk(KERN_INFO "OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n",
350                version, NumProcessors, NumSources, OpenPIC);
351         timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
352         if (timerfreq)
353                 printk(KERN_INFO "OpenPIC timer frequency is %d.%06d MHz\n",
354                        timerfreq / 1000000, timerfreq % 1000000);
355
356         if (!main_pic)
357                 return;
358
359         open_pic_irq_offset = offset;
360         chrp_int_ack_special = (volatile unsigned char*)chrp_ack;
361
362         find_ISUs();
363
364         /* Initialize timer interrupts */
365         ppc64_boot_msg(0x21, "OpenPic Timer");
366         for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
367                 /* Disabled, Priority 0 */
368                 openpic_inittimer(i, 0, openpic_vec_timer+i);
369                 /* No processor */
370                 openpic_maptimer(i, 0);
371         }
372
373 #ifdef CONFIG_SMP
374         /* Initialize IPI interrupts */
375         ppc64_boot_msg(0x22, "OpenPic IPI");
376         openpic_test_broken_IPI();
377         for (i = 0; i < OPENPIC_NUM_IPI; i++) {
378                 /* Disabled, Priority 10..13 */
379                 openpic_initipi(i, 10+i, openpic_vec_ipi+i);
380                 /* IPIs are per-CPU */
381                 irq_desc[openpic_vec_ipi+i].status |= IRQ_PER_CPU;
382                 irq_desc[openpic_vec_ipi+i].handler = &open_pic_ipi;
383         }
384 #endif
385
386         /* Initialize external interrupts */
387         ppc64_boot_msg(0x23, "OpenPic Ext");
388
389         openpic_set_priority(0xf);
390
391         /* SIOint (8259 cascade) is special */
392         if (offset) {
393                 openpic_initirq(0, 8, offset, 1, 1);
394                 openpic_mapirq(0, 1 << get_hard_smp_processor_id(boot_cpuid));
395         }
396
397         /* Init all external sources */
398         for (i = 0; i < NumSources; i++) {
399                 int pri, sense;
400
401                 /* skip cascade if any */
402                 if (offset && i == 0)
403                         continue;
404                 /* the bootloader may have left it enabled (bad !) */
405                 openpic_disable_irq(i+offset);
406
407                 pri = (i == programmer_switch_irq)? 9: 8;
408                 sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: 1;
409                 if (sense)
410                         irq_desc[i+offset].status = IRQ_LEVEL;
411
412                 /* Enabled, Priority 8 or 9 */
413                 openpic_initirq(i, pri, i+offset, !sense, sense);
414                 /* Processor 0 */
415                 openpic_mapirq(i, 1 << get_hard_smp_processor_id(boot_cpuid));
416         }
417
418         /* Init descriptors */
419         for (i = offset; i < NumSources + offset; i++)
420                 irq_desc[i].handler = &open_pic;
421
422         /* Initialize the spurious interrupt */
423         ppc64_boot_msg(0x24, "OpenPic Spurious");
424         openpic_set_spurious(openpic_vec_spurious);
425
426         openpic_set_priority(0);
427         openpic_disable_8259_pass_through();
428
429         ppc64_boot_msg(0x25, "OpenPic Done");
430 }
431
432 /* 
433  * We cant do this in init_IRQ because we need the memory subsystem up for
434  * request_irq()
435  */
436 static int __init openpic_setup_i8259(void)
437 {
438         if (systemcfg->platform == PLATFORM_POWERMAC)
439                 return 0;
440
441         if (naca->interrupt_controller == IC_OPEN_PIC) {
442                 /* Initialize the cascade */
443                 if (request_irq(NUM_ISA_INTERRUPTS, no_action, SA_INTERRUPT,
444                                 "82c59 cascade", NULL))
445                         printk(KERN_ERR "Unable to get OpenPIC IRQ 0 for cascade\n");
446                 i8259_init();
447         }
448
449         return 0;
450 }
451 arch_initcall(openpic_setup_i8259);
452
453 void openpic_setup_ISU(int isu_num, unsigned long addr)
454 {
455         if (isu_num >= OPENPIC_MAX_ISU)
456                 return;
457         ISU[isu_num] = (OpenPIC_SourcePtr) __ioremap(addr, 0x400, _PAGE_NO_CACHE);
458         if (isu_num >= NumISUs)
459                 NumISUs = isu_num + 1;
460 }
461
462 void find_ISUs(void)
463 {
464         /* For PowerMac, setup ISUs on base openpic */
465         if (systemcfg->platform == PLATFORM_POWERMAC) {
466                 int i;
467                 for (i=0; i<128; i+=0x10) {
468                         ISU[i>>4] = &((struct OpenPIC *)OpenPIC_Addr)->Source[i];
469                         NumISUs++;
470                 }
471         }
472         /* Use /interrupt-controller/reg and
473          * /interrupt-controller/interrupt-ranges from OF device tree
474          * the ISU array is setup in chrp_pci.c in ibm_add_bridges
475          * as a result
476          * -- tgall
477          */
478
479         /* basically each ISU is a bus, and this assumes that
480          * open_pic_isu_count interrupts per bus are possible 
481          * ISU == Interrupt Source
482          *
483          * On G5, we keep the original NumSources provided by the controller,
484          * it's below 128, so we have room to stuff the IPIs and timers like darwin
485          * does. We put the spurrious vector up at 0xff though.
486          */
487         if (systemcfg->platform == PLATFORM_POWERMAC) {
488                 openpic_vec_ipi = NumSources;
489                 openpic_vec_timer = openpic_vec_ipi + 4; 
490                 openpic_vec_spurious = 0xff;
491         } else {
492                 NumSources = NumISUs * 0x10;
493
494                 openpic_vec_ipi = NumSources + open_pic_irq_offset;
495                 openpic_vec_timer = openpic_vec_ipi + OPENPIC_NUM_IPI; 
496                 openpic_vec_spurious = openpic_vec_timer + OPENPIC_NUM_TIMERS;
497         }
498 }
499
500 static inline void openpic_reset(void)
501 {
502         openpic_setfield(&OpenPIC->Global.Global_Configuration0,
503                          OPENPIC_CONFIG_RESET);
504 }
505
506 static inline void openpic_enable_8259_pass_through(void)
507 {
508         openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
509                            OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
510 }
511
512 static void openpic_disable_8259_pass_through(void)
513 {
514         openpic_setfield(&OpenPIC->Global.Global_Configuration0,
515                          OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
516 }
517
518 /*
519  *  Find out the current interrupt
520  */
521 static u_int openpic_irq(void)
522 {
523         u_int vec;
524         DECL_THIS_CPU;
525
526         CHECK_THIS_CPU;
527         vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge,
528                                 OPENPIC_VECTOR_MASK);
529         return vec;
530 }
531
532 static void openpic_eoi(void)
533 {
534         DECL_THIS_CPU;
535
536         CHECK_THIS_CPU;
537         openpic_write(&OpenPIC->THIS_CPU.EOI, 0);
538         /* Handle PCI write posting */
539         (void)openpic_read(&OpenPIC->THIS_CPU.EOI);
540 }
541
542
543 static inline u_int openpic_get_priority(void)
544 {
545         DECL_THIS_CPU;
546
547         CHECK_THIS_CPU;
548         return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
549                                  OPENPIC_CURRENT_TASK_PRIORITY_MASK);
550 }
551
552 static void openpic_set_priority(u_int pri)
553 {
554         DECL_THIS_CPU;
555
556         CHECK_THIS_CPU;
557         check_arg_pri(pri);
558         openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority,
559                            OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
560 }
561
562 /*
563  *  Get/set the spurious vector
564  */
565 static inline u_int openpic_get_spurious(void)
566 {
567         return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
568                                  OPENPIC_VECTOR_MASK);
569 }
570
571 static void openpic_set_spurious(u_int vec)
572 {
573         check_arg_vec(vec);
574         openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
575                            vec);
576 }
577
578 /*
579  * Convert a cpu mask from logical to physical cpu numbers.
580  */
581 static inline u32 physmask(u32 cpumask)
582 {
583         int i;
584         u32 mask = 0;
585
586         for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
587                 mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
588         return mask;
589 }
590
591 void openpic_init_processor(u_int cpumask)
592 {
593         openpic_write(&OpenPIC->Global.Processor_Initialization,
594                       physmask(cpumask & cpus_coerce(cpu_online_map)));
595 }
596
597 #ifdef CONFIG_SMP
598 /*
599  *  Initialize an interprocessor interrupt (and disable it)
600  *
601  *  ipi: OpenPIC interprocessor interrupt number
602  *  pri: interrupt source priority
603  *  vec: the vector it will produce
604  */
605 static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec)
606 {
607         check_arg_ipi(ipi);
608         check_arg_pri(pri);
609         check_arg_vec(vec);
610         openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi),
611                                 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
612                                 (pri << OPENPIC_PRIORITY_SHIFT) | vec);
613 }
614
615 /*
616  *  Send an IPI to one or more CPUs
617  *  
618  *  Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI)
619  *  and not a system-wide interrupt number
620  */
621 void openpic_cause_IPI(u_int ipi, u_int cpumask)
622 {
623         DECL_THIS_CPU;
624
625         CHECK_THIS_CPU;
626         check_arg_ipi(ipi);
627         openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi),
628                       physmask(cpumask & cpus_coerce(cpu_online_map)));
629 }
630
631 void openpic_request_IPIs(void)
632 {
633         int i;
634         
635         /*
636          * Make sure this matches what is defined in smp.c for 
637          * smp_message_{pass|recv}() or what shows up in 
638          * /proc/interrupts will be wrong!!! --Troy */
639         
640         if (OpenPIC == NULL)
641                 return;
642
643         /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
644         request_irq(openpic_vec_ipi, openpic_ipi_action, SA_INTERRUPT,
645                     "IPI0 (call function)", 0);
646         request_irq(openpic_vec_ipi+1, openpic_ipi_action, SA_INTERRUPT,
647                    "IPI1 (reschedule)", 0);
648         request_irq(openpic_vec_ipi+2, openpic_ipi_action, SA_INTERRUPT,
649                    "IPI2 (unused)", 0);
650         request_irq(openpic_vec_ipi+3, openpic_ipi_action, SA_INTERRUPT,
651                    "IPI3 (debugger break)", 0);
652
653         for ( i = 0; i < OPENPIC_NUM_IPI ; i++ )
654                 openpic_enable_ipi(openpic_vec_ipi+i);
655 }
656
657 /*
658  * Do per-cpu setup for SMP systems.
659  *
660  * Get IPI's working and start taking interrupts.
661  *   -- Cort
662  */
663 static spinlock_t openpic_setup_lock __devinitdata = SPIN_LOCK_UNLOCKED;
664
665 void __devinit do_openpic_setup_cpu(void)
666 {
667 #ifdef CONFIG_IRQ_ALL_CPUS
668         int i;
669         u32 msk = 1 << hard_smp_processor_id();
670 #endif
671
672         spin_lock(&openpic_setup_lock);
673
674 #ifdef CONFIG_IRQ_ALL_CPUS
675         /* let the openpic know we want intrs. default affinity
676          * is 0xffffffff until changed via /proc
677          * That's how it's done on x86. If we want it differently, then
678          * we should make sure we also change the default values of irq_affinity
679          * in irq.c.
680          */
681         for (i = 0; i < NumSources ; i++)
682                 openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk);
683 #endif /* CONFIG_IRQ_ALL_CPUS */
684         openpic_set_priority(0);
685
686         spin_unlock(&openpic_setup_lock);
687 }
688 #endif /* CONFIG_SMP */
689
690 /*
691  *  Initialize a timer interrupt (and disable it)
692  *
693  *  timer: OpenPIC timer number
694  *  pri: interrupt source priority
695  *  vec: the vector it will produce
696  */
697 static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec)
698 {
699         check_arg_timer(timer);
700         check_arg_pri(pri);
701         check_arg_vec(vec);
702         openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority,
703                                 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
704                                 (pri << OPENPIC_PRIORITY_SHIFT) | vec);
705 }
706
707 /*
708  *  Map a timer interrupt to one or more CPUs
709  */
710 static void __init openpic_maptimer(u_int timer, u_int cpumask)
711 {
712         check_arg_timer(timer);
713         openpic_write(&OpenPIC->Global.Timer[timer].Destination,
714                       physmask(cpumask & cpus_coerce(cpu_online_map)));
715 }
716
717
718 /*
719  *
720  * All functions below take an offset'ed irq argument
721  *
722  */
723
724
725 /*
726  *  Enable/disable an external interrupt source
727  *
728  *  Externally called, irq is an offseted system-wide interrupt number
729  */
730 static void openpic_enable_irq(u_int irq)
731 {
732         unsigned int loops = 100000;
733         check_arg_irq(irq);
734
735         openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK);
736         /* make sure mask gets to controller before we return to user */
737         do {
738                 if (!loops--) {
739                         printk(KERN_ERR "openpic_enable_irq timeout\n");
740                         break;
741                 }
742
743                 mb(); /* sync is probably useless here */
744         } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority,
745                         OPENPIC_MASK));
746 }
747
748 static void openpic_disable_irq(u_int irq)
749 {
750         u32 vp;
751         unsigned int loops = 100000;
752         
753         check_arg_irq(irq);
754
755         openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK);
756         /* make sure mask gets to controller before we return to user */
757         do {
758                 if (!loops--) {
759                         printk(KERN_ERR "openpic_disable_irq timeout\n");
760                         break;
761                 }
762
763                 mb();  /* sync is probably useless here */
764                 vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority,
765                         OPENPIC_MASK | OPENPIC_ACTIVITY);
766         } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
767 }
768
769 #ifdef CONFIG_SMP
770 /*
771  *  Enable/disable an IPI interrupt source
772  *  
773  *  Externally called, irq is an offseted system-wide interrupt number
774  */
775 void openpic_enable_ipi(u_int irq)
776 {
777         irq -= openpic_vec_ipi;
778         check_arg_ipi(irq);
779         openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
780
781 }
782 void openpic_disable_ipi(u_int irq)
783 {
784    /* NEVER disable an IPI... that's just plain wrong! */
785 }
786
787 #endif
788
789 /*
790  *  Initialize an interrupt source (and disable it!)
791  *
792  *  irq: OpenPIC interrupt number
793  *  pri: interrupt source priority
794  *  vec: the vector it will produce
795  *  pol: polarity (1 for positive, 0 for negative)
796  *  sense: 1 for level, 0 for edge
797  */
798 static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
799 {
800         openpic_safe_writefield(&GET_ISU(irq).Vector_Priority,
801                                 OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
802                                 OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
803                                 (pri << OPENPIC_PRIORITY_SHIFT) | vec |
804                                 (pol ? OPENPIC_POLARITY_POSITIVE :
805                                         OPENPIC_POLARITY_NEGATIVE) |
806                                 (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
807 }
808
809 /*
810  *  Map an interrupt source to one or more CPUs
811  */
812 static void openpic_mapirq(u_int irq, u_int physmask)
813 {
814         openpic_write(&GET_ISU(irq).Destination, physmask);
815 }
816
817 /*
818  *  Set the sense for an interrupt source (and disable it!)
819  *
820  *  sense: 1 for level, 0 for edge
821  */
822 #if 0   /* not used */
823 static void openpic_set_sense(u_int irq, int sense)
824 {
825         openpic_safe_writefield(&GET_ISU(irq).Vector_Priority,
826                                 OPENPIC_SENSE_LEVEL,
827                                 (sense ? OPENPIC_SENSE_LEVEL : 0));
828 }
829
830 static int openpic_get_sense(u_int irq)
831 {
832         return openpic_readfield(&GET_ISU(irq).Vector_Priority,
833                                  OPENPIC_SENSE_LEVEL) != 0;
834 }
835 #endif
836
837 static void openpic_end_irq(unsigned int irq_nr)
838 {
839         openpic_eoi();
840 }
841
842 static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask)
843 {
844         cpumask_t tmp;
845
846         cpus_and(tmp, cpumask, cpu_online_map);
847         openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpus_coerce(tmp)));
848 }
849
850 #ifdef CONFIG_SMP
851 static void openpic_end_ipi(unsigned int irq_nr)
852 {
853         /*
854          * IPIs are marked IRQ_PER_CPU. This has the side effect of
855          * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
856          * applying to them. We EOI them late to avoid re-entering.
857          * We mark IPI's with SA_INTERRUPT as they must run with
858          * irqs disabled.
859          */
860         openpic_eoi();
861 }
862
863 static irqreturn_t openpic_ipi_action(int cpl, void *dev_id,
864                                         struct pt_regs *regs)
865 {
866         smp_message_recv(cpl-openpic_vec_ipi, regs);
867         return IRQ_HANDLED;
868 }
869
870 #endif /* CONFIG_SMP */
871
872 int openpic_get_irq(struct pt_regs *regs)
873 {
874         extern int i8259_irq(int cpu);
875
876         int irq = openpic_irq();
877
878         if (open_pic_irq_offset && irq == open_pic_irq_offset) {
879                 /*
880                  * This magic address generates a PCI IACK cycle.
881                  */
882                 if ( chrp_int_ack_special )
883                         irq = *chrp_int_ack_special;
884                 else
885                         irq = i8259_irq( smp_processor_id() );
886                 openpic_eoi();
887         }
888         if (irq == openpic_vec_spurious)
889                 irq = -1;
890         return irq;
891 }