blob: c7f04420066d16a532ad61bf576f06e896d9b9fa [file] [log] [blame]
David Gibson007e8f52005-10-28 15:35:50 +10001/*
2 * arch/powerpc/platforms/pseries/xics.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Copyright 2000 IBM Corporation.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/types.h>
12#include <linux/threads.h>
13#include <linux/kernel.h>
14#include <linux/irq.h>
15#include <linux/smp.h>
16#include <linux/interrupt.h>
17#include <linux/signal.h>
18#include <linux/init.h>
19#include <linux/gfp.h>
20#include <linux/radix-tree.h>
21#include <linux/cpu.h>
Michael Ellerman57cfb812006-03-21 20:45:59 +110022#include <asm/firmware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <asm/prom.h>
24#include <asm/io.h>
25#include <asm/pgtable.h>
26#include <asm/smp.h>
27#include <asm/rtas.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <asm/hvcall.h>
29#include <asm/machdep.h>
Paul Mackerras22277182005-10-28 11:47:17 +100030#include <asm/i8259.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
David Gibson007e8f52005-10-28 15:35:50 +100032#include "xics.h"
33
Linus Torvalds1da177e2005-04-16 15:20:36 -070034/* This is used to map real irq numbers to virtual */
35static struct radix_tree_root irq_map = RADIX_TREE_INIT(GFP_ATOMIC);
36
37#define XICS_IPI 2
38#define XICS_IRQ_SPURIOUS 0
39
40/* Want a priority other than 0. Various HW issues require this. */
41#define DEFAULT_PRIORITY 5
42
David Gibson007e8f52005-10-28 15:35:50 +100043/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 * Mark IPIs as higher priority so we can take them inside interrupts that
Thomas Gleixner67144652006-07-01 19:29:22 -070045 * arent marked IRQF_DISABLED
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 */
47#define IPI_PRIORITY 4
48
49struct xics_ipl {
50 union {
51 u32 word;
52 u8 bytes[4];
53 } xirr_poll;
54 union {
55 u32 word;
56 u8 bytes[4];
57 } xirr;
58 u32 dummy;
59 union {
60 u32 word;
61 u8 bytes[4];
62 } qirr;
63};
64
65static struct xics_ipl __iomem *xics_per_cpu[NR_CPUS];
66
67static int xics_irq_8259_cascade = 0;
68static int xics_irq_8259_cascade_real = 0;
69static unsigned int default_server = 0xFF;
Anton Blanchard26370322005-09-12 13:12:11 +100070static unsigned int default_distrib_server = 0;
71static unsigned int interrupt_server_size = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73/*
74 * XICS only has a single IPI, so encode the messages per CPU
75 */
76struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;
77
78/* RTAS service tokens */
Anton Blanchard26370322005-09-12 13:12:11 +100079static int ibm_get_xive;
80static int ibm_set_xive;
81static int ibm_int_on;
82static int ibm_int_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +100084
85/* Direct HW low level accessors */
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +100088static inline int direct_xirr_info_get(int n_cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
90 return in_be32(&xics_per_cpu[n_cpu]->xirr.word);
91}
92
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +100093static inline void direct_xirr_info_set(int n_cpu, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
95 out_be32(&xics_per_cpu[n_cpu]->xirr.word, value);
96}
97
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +100098static inline void direct_cppr_info(int n_cpu, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099{
100 out_8(&xics_per_cpu[n_cpu]->xirr.bytes[0], value);
101}
102
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000103static inline void direct_qirr_info(int n_cpu, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
105 out_8(&xics_per_cpu[n_cpu]->qirr.bytes[0], value);
106}
107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000109/* LPAR low level accessors */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static inline long plpar_eoi(unsigned long xirr)
113{
114 return plpar_hcall_norets(H_EOI, xirr);
115}
116
117static inline long plpar_cppr(unsigned long cppr)
118{
119 return plpar_hcall_norets(H_CPPR, cppr);
120}
121
122static inline long plpar_ipi(unsigned long servernum, unsigned long mfrr)
123{
124 return plpar_hcall_norets(H_IPI, servernum, mfrr);
125}
126
127static inline long plpar_xirr(unsigned long *xirr_ret)
128{
129 unsigned long dummy;
130 return plpar_hcall(H_XIRR, 0, 0, 0, 0, xirr_ret, &dummy, &dummy);
131}
132
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000133static inline int lpar_xirr_info_get(int n_cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
135 unsigned long lpar_rc;
David Gibson007e8f52005-10-28 15:35:50 +1000136 unsigned long return_value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
138 lpar_rc = plpar_xirr(&return_value);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200139 if (lpar_rc != H_SUCCESS)
David Gibson007e8f52005-10-28 15:35:50 +1000140 panic(" bad return code xirr - rc = %lx \n", lpar_rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 return (int)return_value;
142}
143
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000144static inline void lpar_xirr_info_set(int n_cpu, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
146 unsigned long lpar_rc;
147 unsigned long val64 = value & 0xffffffff;
148
149 lpar_rc = plpar_eoi(val64);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200150 if (lpar_rc != H_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 panic("bad return code EOI - rc = %ld, value=%lx\n", lpar_rc,
David Gibson007e8f52005-10-28 15:35:50 +1000152 val64);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153}
154
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000155static inline void lpar_cppr_info(int n_cpu, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
157 unsigned long lpar_rc;
158
159 lpar_rc = plpar_cppr(value);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200160 if (lpar_rc != H_SUCCESS)
David Gibson007e8f52005-10-28 15:35:50 +1000161 panic("bad return code cppr - rc = %lx\n", lpar_rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000164static inline void lpar_qirr_info(int n_cpu , u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
166 unsigned long lpar_rc;
167
168 lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu), value);
Segher Boessenkool706c8c92006-03-30 14:49:40 +0200169 if (lpar_rc != H_SUCCESS)
David Gibson007e8f52005-10-28 15:35:50 +1000170 panic("bad return code qirr - rc = %lx\n", lpar_rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000174/* High level handlers and init code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
177#ifdef CONFIG_SMP
178static int get_irq_server(unsigned int irq)
179{
180 unsigned int server;
181 /* For the moment only implement delivery to all cpus or one cpu */
Ingo Molnara53da522006-06-29 02:24:38 -0700182 cpumask_t cpumask = irq_desc[irq].affinity;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 cpumask_t tmp = CPU_MASK_NONE;
184
185 if (!distribute_irqs)
186 return default_server;
187
188 if (cpus_equal(cpumask, CPU_MASK_ALL)) {
189 server = default_distrib_server;
190 } else {
191 cpus_and(tmp, cpu_online_map, cpumask);
192
193 if (cpus_empty(tmp))
194 server = default_distrib_server;
195 else
196 server = get_hard_smp_processor_id(first_cpu(tmp));
197 }
198
199 return server;
200
201}
202#else
203static int get_irq_server(unsigned int irq)
204{
205 return default_server;
206}
207#endif
208
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000209
210static void xics_unmask_irq(unsigned int virq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 unsigned int irq;
213 int call_status;
214 unsigned int server;
215
216 irq = virt_irq_to_real(irq_offset_down(virq));
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000217 WARN_ON(irq == NO_IRQ);
218 if (irq == XICS_IPI || irq == NO_IRQ)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 return;
220
221 server = get_irq_server(virq);
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server,
224 DEFAULT_PRIORITY);
225 if (call_status != 0) {
Anton Blanchard26370322005-09-12 13:12:11 +1000226 printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_set_xive "
227 "returned %d\n", irq, call_status);
228 printk("set_xive %x, server %x\n", ibm_set_xive, server);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 return;
230 }
231
232 /* Now unmask the interrupt (often a no-op) */
233 call_status = rtas_call(ibm_int_on, 1, 1, NULL, irq);
234 if (call_status != 0) {
Anton Blanchard26370322005-09-12 13:12:11 +1000235 printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_int_on "
236 "returned %d\n", irq, call_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return;
238 }
239}
240
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000241static void xics_mask_real_irq(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 int call_status;
244 unsigned int server;
245
246 if (irq == XICS_IPI)
247 return;
248
249 call_status = rtas_call(ibm_int_off, 1, 1, NULL, irq);
250 if (call_status != 0) {
Anton Blanchard26370322005-09-12 13:12:11 +1000251 printk(KERN_ERR "xics_disable_real_irq: irq=%u: "
252 "ibm_int_off returned %d\n", irq, call_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 return;
254 }
255
256 server = get_irq_server(irq);
257 /* Have to set XIVE to 0xff to be able to remove a slot */
258 call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server, 0xff);
259 if (call_status != 0) {
Anton Blanchard26370322005-09-12 13:12:11 +1000260 printk(KERN_ERR "xics_disable_irq: irq=%u: ibm_set_xive(0xff)"
261 " returned %d\n", irq, call_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return;
263 }
264}
265
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000266static void xics_mask_irq(unsigned int virq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 unsigned int irq;
269
270 irq = virt_irq_to_real(irq_offset_down(virq));
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000271 WARN_ON(irq == NO_IRQ);
272 if (irq != NO_IRQ)
273 xics_mask_real_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000276static void xics_set_irq_revmap(unsigned int virq)
277{
278 unsigned int irq;
279
280 irq = irq_offset_down(virq);
281 if (radix_tree_insert(&irq_map, virt_irq_to_real(irq),
282 &virt_irq_to_real_map[irq]) == -ENOMEM)
283 printk(KERN_CRIT "Out of memory creating real -> virtual"
284 " IRQ mapping for irq %u (real 0x%x)\n",
285 virq, virt_irq_to_real(irq));
286}
287
288static unsigned int xics_startup(unsigned int virq)
289{
290 xics_set_irq_revmap(virq);
291 xics_unmask_irq(virq);
292 return 0;
293}
294
295static unsigned int real_irq_to_virt(unsigned int real_irq)
296{
297 unsigned int *ptr;
298
299 ptr = radix_tree_lookup(&irq_map, real_irq);
300 if (ptr == NULL)
301 return NO_IRQ;
302 return ptr - virt_irq_to_real_map;
303}
304
305static void xics_eoi_direct(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{
307 int cpu = smp_processor_id();
308
309 iosync();
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000310 direct_xirr_info_set(cpu, ((0xff << 24) |
311 (virt_irq_to_real(irq_offset_down(irq)))));
312}
313
314
315static void xics_eoi_lpar(unsigned int irq)
316{
317 int cpu = smp_processor_id();
318
319 iosync();
320 lpar_xirr_info_set(cpu, ((0xff << 24) |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 (virt_irq_to_real(irq_offset_down(irq)))));
322
323}
324
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000325static inline int xics_remap_irq(int vec)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 int irq;
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 vec &= 0x00ffffff;
330
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000331 if (vec == XICS_IRQ_SPURIOUS)
332 return NO_IRQ;
333
334 irq = real_irq_to_virt(vec);
335 if (irq == NO_IRQ)
336 irq = real_irq_to_virt_slowpath(vec);
337 if (likely(irq != NO_IRQ))
338 return irq_offset_up(irq);
339
340 printk(KERN_ERR "Interrupt %u (real) is invalid,"
341 " disabling it.\n", vec);
342 xics_mask_real_irq(vec);
343 return NO_IRQ;
344}
345
346static int xics_get_irq_direct(struct pt_regs *regs)
347{
348 unsigned int cpu = smp_processor_id();
349
350 return xics_remap_irq(direct_xirr_info_get(cpu));
351}
352
353static int xics_get_irq_lpar(struct pt_regs *regs)
354{
355 unsigned int cpu = smp_processor_id();
356
357 return xics_remap_irq(lpar_xirr_info_get(cpu));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358}
359
360#ifdef CONFIG_SMP
361
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000362static irqreturn_t xics_ipi_dispatch(int cpu, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 WARN_ON(cpu_is_offline(cpu));
365
366 while (xics_ipi_message[cpu].value) {
367 if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION,
368 &xics_ipi_message[cpu].value)) {
369 mb();
370 smp_message_recv(PPC_MSG_CALL_FUNCTION, regs);
371 }
372 if (test_and_clear_bit(PPC_MSG_RESCHEDULE,
373 &xics_ipi_message[cpu].value)) {
374 mb();
375 smp_message_recv(PPC_MSG_RESCHEDULE, regs);
376 }
377#if 0
378 if (test_and_clear_bit(PPC_MSG_MIGRATE_TASK,
379 &xics_ipi_message[cpu].value)) {
380 mb();
381 smp_message_recv(PPC_MSG_MIGRATE_TASK, regs);
382 }
383#endif
Michael Ellermancc532912005-12-04 18:39:43 +1100384#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK,
386 &xics_ipi_message[cpu].value)) {
387 mb();
388 smp_message_recv(PPC_MSG_DEBUGGER_BREAK, regs);
389 }
390#endif
391 }
392 return IRQ_HANDLED;
393}
394
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000395static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id, struct pt_regs *regs)
396{
397 int cpu = smp_processor_id();
398
399 direct_qirr_info(cpu, 0xff);
400
401 return xics_ipi_dispatch(cpu, regs);
402}
403
404static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id, struct pt_regs *regs)
405{
406 int cpu = smp_processor_id();
407
408 lpar_qirr_info(cpu, 0xff);
409
410 return xics_ipi_dispatch(cpu, regs);
411}
412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413void xics_cause_IPI(int cpu)
414{
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000415 if (firmware_has_feature(FW_FEATURE_LPAR))
416 lpar_qirr_info(cpu, IPI_PRIORITY);
417 else
418 direct_qirr_info(cpu, IPI_PRIORITY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419}
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000420
Paul Mackerras6c80a212005-05-06 16:28:56 +1000421#endif /* CONFIG_SMP */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000423static void xics_set_cpu_priority(int cpu, unsigned char cppr)
424{
425 if (firmware_has_feature(FW_FEATURE_LPAR))
426 lpar_cppr_info(cpu, cppr);
427 else
428 direct_cppr_info(cpu, cppr);
429 iosync();
430}
431
432static void xics_set_affinity(unsigned int virq, cpumask_t cpumask)
433{
434 unsigned int irq;
435 int status;
436 int xics_status[2];
437 unsigned long newmask;
438 cpumask_t tmp = CPU_MASK_NONE;
439
440 irq = virt_irq_to_real(irq_offset_down(virq));
441 if (irq == XICS_IPI || irq == NO_IRQ)
442 return;
443
444 status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
445
446 if (status) {
447 printk(KERN_ERR "xics_set_affinity: irq=%u ibm,get-xive "
448 "returns %d\n", irq, status);
449 return;
450 }
451
452 /* For the moment only implement delivery to all cpus or one cpu */
453 if (cpus_equal(cpumask, CPU_MASK_ALL)) {
454 newmask = default_distrib_server;
455 } else {
456 cpus_and(tmp, cpu_online_map, cpumask);
457 if (cpus_empty(tmp))
458 return;
459 newmask = get_hard_smp_processor_id(first_cpu(tmp));
460 }
461
462 status = rtas_call(ibm_set_xive, 3, 1, NULL,
463 irq, newmask, xics_status[1]);
464
465 if (status) {
466 printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive "
467 "returns %d\n", irq, status);
468 return;
469 }
470}
471
472static struct irq_chip xics_pic_direct = {
473 .typename = " XICS ",
474 .startup = xics_startup,
475 .mask = xics_mask_irq,
476 .unmask = xics_unmask_irq,
477 .eoi = xics_eoi_direct,
478 .set_affinity = xics_set_affinity
479};
480
481
482static struct irq_chip xics_pic_lpar = {
483 .typename = " XICS ",
484 .startup = xics_startup,
485 .mask = xics_mask_irq,
486 .unmask = xics_unmask_irq,
487 .eoi = xics_eoi_lpar,
488 .set_affinity = xics_set_affinity
489};
490
491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492void xics_setup_cpu(void)
493{
494 int cpu = smp_processor_id();
495
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000496 xics_set_cpu_priority(cpu, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Paul Mackerras6c80a212005-05-06 16:28:56 +1000498 /*
499 * Put the calling processor into the GIQ. This is really only
500 * necessary from a secondary thread as the OF start-cpu interface
501 * performs this function for us on primary threads.
502 *
503 * XXX: undo of teardown on kexec needs this too, as may hotplug
504 */
505 rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE,
506 (1UL << interrupt_server_size) - 1 - default_distrib_server, 1);
507}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509void xics_init_IRQ(void)
510{
511 int i;
512 unsigned long intr_size = 0;
513 struct device_node *np;
514 uint *ireg, ilen, indx = 0;
515 unsigned long intr_base = 0;
516 struct xics_interrupt_node {
517 unsigned long addr;
518 unsigned long size;
David Gibson007e8f52005-10-28 15:35:50 +1000519 } intnodes[NR_CPUS];
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000520 struct irq_chip *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 ppc64_boot_msg(0x20, "XICS Init");
523
524 ibm_get_xive = rtas_token("ibm,get-xive");
525 ibm_set_xive = rtas_token("ibm,set-xive");
526 ibm_int_on = rtas_token("ibm,int-on");
527 ibm_int_off = rtas_token("ibm,int-off");
528
529 np = of_find_node_by_type(NULL, "PowerPC-External-Interrupt-Presentation");
530 if (!np)
531 panic("xics_init_IRQ: can't find interrupt presentation");
532
533nextnode:
534 ireg = (uint *)get_property(np, "ibm,interrupt-server-ranges", NULL);
535 if (ireg) {
536 /*
537 * set node starting index for this node
538 */
539 indx = *ireg;
540 }
541
542 ireg = (uint *)get_property(np, "reg", &ilen);
543 if (!ireg)
544 panic("xics_init_IRQ: can't find interrupt reg property");
David Gibson007e8f52005-10-28 15:35:50 +1000545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 while (ilen) {
547 intnodes[indx].addr = (unsigned long)*ireg++ << 32;
548 ilen -= sizeof(uint);
549 intnodes[indx].addr |= *ireg++;
550 ilen -= sizeof(uint);
551 intnodes[indx].size = (unsigned long)*ireg++ << 32;
552 ilen -= sizeof(uint);
553 intnodes[indx].size |= *ireg++;
554 ilen -= sizeof(uint);
555 indx++;
556 if (indx >= NR_CPUS) break;
557 }
558
559 np = of_find_node_by_type(np, "PowerPC-External-Interrupt-Presentation");
560 if ((indx < NR_CPUS) && np) goto nextnode;
561
562 /* Find the server numbers for the boot cpu. */
563 for (np = of_find_node_by_type(NULL, "cpu");
564 np;
565 np = of_find_node_by_type(np, "cpu")) {
566 ireg = (uint *)get_property(np, "reg", &ilen);
Anton Blanchard4df20462006-03-25 17:25:17 +1100567 if (ireg && ireg[0] == get_hard_smp_processor_id(boot_cpuid)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 ireg = (uint *)get_property(np, "ibm,ppc-interrupt-gserver#s",
569 &ilen);
570 i = ilen / sizeof(int);
571 if (ireg && i > 0) {
572 default_server = ireg[0];
573 default_distrib_server = ireg[i-1]; /* take last element */
574 }
575 ireg = (uint *)get_property(np,
576 "ibm,interrupt-server#-size", NULL);
577 if (ireg)
578 interrupt_server_size = *ireg;
579 break;
580 }
581 }
582 of_node_put(np);
583
584 intr_base = intnodes[0].addr;
585 intr_size = intnodes[0].size;
586
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000587 if (firmware_has_feature(FW_FEATURE_LPAR)) {
588 ppc_md.get_irq = xics_get_irq_lpar;
589 chip = &xics_pic_lpar;
590 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591#ifdef CONFIG_SMP
KAMEZAWA Hiroyuki0e551952006-03-28 14:50:51 -0800592 for_each_possible_cpu(i) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 int hard_id;
594
595 /* FIXME: Do this dynamically! --RR */
596 if (!cpu_present(i))
597 continue;
598
599 hard_id = get_hard_smp_processor_id(i);
David Gibson007e8f52005-10-28 15:35:50 +1000600 xics_per_cpu[i] = ioremap(intnodes[hard_id].addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 intnodes[hard_id].size);
602 }
603#else
604 xics_per_cpu[0] = ioremap(intr_base, intr_size);
605#endif /* CONFIG_SMP */
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000606 ppc_md.get_irq = xics_get_irq_direct;
607 chip = &xics_pic_direct;
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 }
610
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000611 for (i = irq_offset_value(); i < NR_IRQS; ++i) {
612 /* All IRQs on XICS are level for now. MSI code may want to modify
613 * that for reporting purposes
614 */
615 get_irq_desc(i)->status |= IRQ_LEVEL;
616 set_irq_chip_and_handler(i, chip, handle_fasteoi_irq);
617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Paul Mackerras6c80a212005-05-06 16:28:56 +1000619 xics_setup_cpu();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 ppc64_boot_msg(0x21, "XICS Done");
622}
623
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000624static int xics_setup_8259_cascade(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000626 struct device_node *np;
627 uint *ireg;
628
629 np = of_find_node_by_type(NULL, "interrupt-controller");
630 if (np == NULL) {
631 printk(KERN_WARNING "xics: no ISA interrupt controller\n");
632 xics_irq_8259_cascade_real = -1;
633 xics_irq_8259_cascade = -1;
634 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000636
637 ireg = (uint *) get_property(np, "interrupts", NULL);
638 if (!ireg)
639 panic("xics_init_IRQ: can't find ISA interrupts property");
640
641 xics_irq_8259_cascade_real = *ireg;
642 xics_irq_8259_cascade = irq_offset_up
643 (virt_irq_create_mapping(xics_irq_8259_cascade_real));
644 i8259_init(0, 0);
645 of_node_put(np);
646
647 xics_set_irq_revmap(xics_irq_8259_cascade);
648 set_irq_chained_handler(xics_irq_8259_cascade, pSeries_8259_cascade);
649
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 return 0;
651}
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000652arch_initcall(xics_setup_8259_cascade);
653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
655#ifdef CONFIG_SMP
656void xics_request_IPIs(void)
657{
658 virt_irq_to_real_map[XICS_IPI] = XICS_IPI;
659
Thomas Gleixner67144652006-07-01 19:29:22 -0700660 /*
661 * IPIs are marked IRQF_DISABLED as they must run with irqs
662 * disabled
663 */
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000664 set_irq_handler(irq_offset_up(XICS_IPI), handle_percpu_irq);
665 if (firmware_has_feature(FW_FEATURE_LPAR))
666 request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_lpar,
667 SA_INTERRUPT, "IPI", NULL);
668 else
669 request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_direct,
670 SA_INTERRUPT, "IPI", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671}
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000672#endif /* CONFIG_SMP */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Paul Mackerras6d22d852005-08-04 12:53:37 -0700674void xics_teardown_cpu(int secondary)
R Sharadafce0d572005-06-25 14:58:10 -0700675{
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000676 struct irq_desc *desc = get_irq_desc(irq_offset_up(XICS_IPI));
R Sharadafce0d572005-06-25 14:58:10 -0700677 int cpu = smp_processor_id();
R Sharadafce0d572005-06-25 14:58:10 -0700678
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000679 xics_set_cpu_priority(cpu, 0);
Haren Myneni81bbbe92006-04-05 21:10:18 -0600680
681 /*
682 * we need to EOI the IPI if we got here from kexec down IPI
683 *
684 * probably need to check all the other interrupts too
685 * should we be flagging idle loop instead?
686 * or creating some task to be scheduled?
687 */
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000688 if (desc->chip && desc->chip->eoi)
689 desc->chip->eoi(XICS_IPI);
Haren Myneni81bbbe92006-04-05 21:10:18 -0600690
R Sharadafce0d572005-06-25 14:58:10 -0700691 /*
Paul Mackerras6d22d852005-08-04 12:53:37 -0700692 * Some machines need to have at least one cpu in the GIQ,
693 * so leave the master cpu in the group.
R Sharadafce0d572005-06-25 14:58:10 -0700694 */
Haren Myneni81bbbe92006-04-05 21:10:18 -0600695 if (secondary)
Paul Mackerras6d22d852005-08-04 12:53:37 -0700696 rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE,
697 (1UL << interrupt_server_size) - 1 -
698 default_distrib_server, 0);
R Sharadafce0d572005-06-25 14:58:10 -0700699}
700
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701#ifdef CONFIG_HOTPLUG_CPU
702
703/* Interrupts are disabled. */
704void xics_migrate_irqs_away(void)
705{
706 int status;
707 unsigned int irq, virq, cpu = smp_processor_id();
708
709 /* Reject any interrupt that was queued to us... */
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000710 xics_set_cpu_priority(cpu, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 /* remove ourselves from the global interrupt queue */
713 status = rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE,
714 (1UL << interrupt_server_size) - 1 - default_distrib_server, 0);
715 WARN_ON(status < 0);
716
717 /* Allow IPIs again... */
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000718 xics_set_cpu_priority(cpu, DEFAULT_PRIORITY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
720 for_each_irq(virq) {
Benjamin Herrenschmidtb9e5b4e2006-07-03 19:32:51 +1000721 struct irq_desc *desc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 int xics_status[2];
723 unsigned long flags;
724
725 /* We cant set affinity on ISA interrupts */
726 if (virq < irq_offset_value())
727 continue;
728
729 desc = get_irq_desc(virq);
730 irq = virt_irq_to_real(irq_offset_down(virq));
731
732 /* We need to get IPIs still. */
733 if (irq == XICS_IPI || irq == NO_IRQ)
734 continue;
735
736 /* We only need to migrate enabled IRQS */
Ingo Molnard1bef4e2006-06-29 02:24:36 -0700737 if (desc == NULL || desc->chip == NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 || desc->action == NULL
Ingo Molnard1bef4e2006-06-29 02:24:36 -0700739 || desc->chip->set_affinity == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 continue;
741
742 spin_lock_irqsave(&desc->lock, flags);
743
744 status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
745 if (status) {
Anton Blanchard26370322005-09-12 13:12:11 +1000746 printk(KERN_ERR "migrate_irqs_away: irq=%u "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 "ibm,get-xive returns %d\n",
748 virq, status);
749 goto unlock;
750 }
751
752 /*
753 * We only support delivery to all cpus or to one cpu.
754 * The irq has to be migrated only in the single cpu
755 * case.
756 */
757 if (xics_status[0] != get_hard_smp_processor_id(cpu))
758 goto unlock;
759
Anton Blanchard26370322005-09-12 13:12:11 +1000760 printk(KERN_WARNING "IRQ %u affinity broken off cpu %u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 virq, cpu);
762
763 /* Reset affinity to all cpus */
Ingo Molnard1bef4e2006-06-29 02:24:36 -0700764 desc->chip->set_affinity(virq, CPU_MASK_ALL);
Ingo Molnara53da522006-06-29 02:24:38 -0700765 irq_desc[irq].affinity = CPU_MASK_ALL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766unlock:
767 spin_unlock_irqrestore(&desc->lock, flags);
768 }
769}
770#endif