aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/cell/interrupt.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2006-07-03 07:36:01 -0400
committerPaul Mackerras <paulus@samba.org>2006-07-03 07:36:01 -0400
commit0ebfff1491ef85d41ddf9c633834838be144f69f (patch)
tree5b469a6d61a9fcfbf94e7b6d411e544dbdec8dec /arch/powerpc/platforms/cell/interrupt.c
parentf63e115fb50db39706b955b81e3375ef6bab2268 (diff)
[POWERPC] Add new interrupt mapping core and change platforms to use it
This adds the new irq remapper core and removes the old one. Because there are some fundamental conflicts with the old code, like the value of NO_IRQ which I'm now setting to 0 (as per discussions with Linus), etc..., this commit also changes the relevant platform and driver code over to use the new remapper (so as not to cause difficulties later in bisecting). This patch removes the old pre-parsing of the open firmware interrupt tree along with all the bogus assumptions it made to try to renumber interrupts according to the platform. This is all to be handled by the new code now. For the pSeries XICS interrupt controller, a single remapper host is created for the whole machine regardless of how many interrupt presentation and source controllers are found, and it's set to match any device node that isn't a 8259. That works fine on pSeries and avoids having to deal with some of the complexities of split source controllers vs. presentation controllers in the pSeries device trees. The powerpc i8259 PIC driver now always requests the legacy interrupt range. It also has the feature of being able to match any device node (including NULL) if passed no device node as an input. That will help porting over platforms with broken device-trees like Pegasos who don't have a proper interrupt tree. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/cell/interrupt.c')
-rw-r--r--arch/powerpc/platforms/cell/interrupt.c396
1 files changed, 174 insertions, 222 deletions
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 97936f547f19..9d5da7896892 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -1,6 +1,9 @@
1/* 1/*
2 * Cell Internal Interrupt Controller 2 * Cell Internal Interrupt Controller
3 * 3 *
4 * Copyright (C) 2006 Benjamin Herrenschmidt (benh@kernel.crashing.org)
5 * IBM, Corp.
6 *
4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 7 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
5 * 8 *
6 * Author: Arnd Bergmann <arndb@de.ibm.com> 9 * Author: Arnd Bergmann <arndb@de.ibm.com>
@@ -25,11 +28,13 @@
25#include <linux/module.h> 28#include <linux/module.h>
26#include <linux/percpu.h> 29#include <linux/percpu.h>
27#include <linux/types.h> 30#include <linux/types.h>
31#include <linux/ioport.h>
28 32
29#include <asm/io.h> 33#include <asm/io.h>
30#include <asm/pgtable.h> 34#include <asm/pgtable.h>
31#include <asm/prom.h> 35#include <asm/prom.h>
32#include <asm/ptrace.h> 36#include <asm/ptrace.h>
37#include <asm/machdep.h>
33 38
34#include "interrupt.h" 39#include "interrupt.h"
35#include "cbe_regs.h" 40#include "cbe_regs.h"
@@ -39,9 +44,25 @@ struct iic {
39 u8 target_id; 44 u8 target_id;
40 u8 eoi_stack[16]; 45 u8 eoi_stack[16];
41 int eoi_ptr; 46 int eoi_ptr;
47 struct irq_host *host;
42}; 48};
43 49
44static DEFINE_PER_CPU(struct iic, iic); 50static DEFINE_PER_CPU(struct iic, iic);
51#define IIC_NODE_COUNT 2
52static struct irq_host *iic_hosts[IIC_NODE_COUNT];
53
54/* Convert between "pending" bits and hw irq number */
55static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)
56{
57 unsigned char unit = bits.source & 0xf;
58
59 if (bits.flags & CBE_IIC_IRQ_IPI)
60 return IIC_IRQ_IPI0 | (bits.prio >> 4);
61 else if (bits.class <= 3)
62 return (bits.class << 4) | unit;
63 else
64 return IIC_IRQ_INVALID;
65}
45 66
46static void iic_mask(unsigned int irq) 67static void iic_mask(unsigned int irq)
47{ 68{
@@ -65,197 +86,21 @@ static struct irq_chip iic_chip = {
65 .eoi = iic_eoi, 86 .eoi = iic_eoi,
66}; 87};
67 88
68/* XXX All of this has to be reworked completely. We need to assign a real
69 * interrupt numbers to the external interrupts and remove all the hard coded
70 * interrupt maps (rely on the device-tree whenever possible).
71 *
72 * Basically, my scheme is to define the "pendings" bits to be the HW interrupt
73 * number (ignoring the data and flags here). That means we can sort-of split
74 * external sources based on priority, and we can use request_irq() on pretty
75 * much anything.
76 *
77 * For spider or axon, they have their own interrupt space. spider will just have
78 * local "hardward" interrupts 0...xx * node stride. The node stride is not
79 * necessary (separate interrupt chips will have separate HW number space), but
80 * will allow to be compatible with existing device-trees.
81 *
82 * All of thise little world will get a standard remapping scheme to map those HW
83 * numbers into the linux flat irq number space.
84*/
85static int iic_external_get_irq(struct cbe_iic_pending_bits pending)
86{
87 int irq;
88 unsigned char node, unit;
89
90 node = pending.source >> 4;
91 unit = pending.source & 0xf;
92 irq = -1;
93
94 /*
95 * This mapping is specific to the Cell Broadband
96 * Engine. We might need to get the numbers
97 * from the device tree to support future CPUs.
98 */
99 switch (unit) {
100 case 0x00:
101 case 0x0b:
102 /*
103 * One of these units can be connected
104 * to an external interrupt controller.
105 */
106 if (pending.class != 2)
107 break;
108 /* TODO: We might want to silently ignore cascade interrupts
109 * when no cascade handler exist yet
110 */
111 irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE;
112 break;
113 case 0x01 ... 0x04:
114 case 0x07 ... 0x0a:
115 /*
116 * These units are connected to the SPEs
117 */
118 if (pending.class > 2)
119 break;
120 irq = IIC_SPE_OFFSET
121 + pending.class * IIC_CLASS_STRIDE
122 + node * IIC_NODE_STRIDE
123 + unit;
124 break;
125 }
126 if (irq == -1)
127 printk(KERN_WARNING "Unexpected interrupt class %02x, "
128 "source %02x, prio %02x, cpu %02x\n", pending.class,
129 pending.source, pending.prio, smp_processor_id());
130 return irq;
131}
132
133/* Get an IRQ number from the pending state register of the IIC */ 89/* Get an IRQ number from the pending state register of the IIC */
134int iic_get_irq(struct pt_regs *regs) 90static unsigned int iic_get_irq(struct pt_regs *regs)
135{
136 struct iic *iic;
137 int irq;
138 struct cbe_iic_pending_bits pending;
139
140 iic = &__get_cpu_var(iic);
141 *(unsigned long *) &pending =
142 in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
143 iic->eoi_stack[++iic->eoi_ptr] = pending.prio;
144 BUG_ON(iic->eoi_ptr > 15);
145
146 irq = -1;
147 if (pending.flags & CBE_IIC_IRQ_VALID) {
148 if (pending.flags & CBE_IIC_IRQ_IPI) {
149 irq = IIC_IPI_OFFSET + (pending.prio >> 4);
150/*
151 if (irq > 0x80)
152 printk(KERN_WARNING "Unexpected IPI prio %02x"
153 "on CPU %02x\n", pending.prio,
154 smp_processor_id());
155*/
156 } else {
157 irq = iic_external_get_irq(pending);
158 }
159 }
160 return irq;
161}
162
163/* hardcoded part to be compatible with older firmware */
164
165static int __init setup_iic_hardcoded(void)
166{
167 struct device_node *np;
168 int nodeid, cpu;
169 unsigned long regs;
170 struct iic *iic;
171
172 for_each_possible_cpu(cpu) {
173 iic = &per_cpu(iic, cpu);
174 nodeid = cpu/2;
175
176 for (np = of_find_node_by_type(NULL, "cpu");
177 np;
178 np = of_find_node_by_type(np, "cpu")) {
179 if (nodeid == *(int *)get_property(np, "node-id", NULL))
180 break;
181 }
182
183 if (!np) {
184 printk(KERN_WARNING "IIC: CPU %d not found\n", cpu);
185 iic->regs = NULL;
186 iic->target_id = 0xff;
187 return -ENODEV;
188 }
189
190 regs = *(long *)get_property(np, "iic", NULL);
191
192 /* hack until we have decided on the devtree info */
193 regs += 0x400;
194 if (cpu & 1)
195 regs += 0x20;
196
197 printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs);
198 iic->regs = ioremap(regs, sizeof(struct cbe_iic_thread_regs));
199 iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
200 iic->eoi_stack[0] = 0xff;
201 }
202
203 return 0;
204}
205
206static int __init setup_iic(void)
207{ 91{
208 struct device_node *dn; 92 struct cbe_iic_pending_bits pending;
209 unsigned long *regs; 93 struct iic *iic;
210 char *compatible; 94
211 unsigned *np, found = 0; 95 iic = &__get_cpu_var(iic);
212 struct iic *iic = NULL; 96 *(unsigned long *) &pending =
213 97 in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
214 for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { 98 iic->eoi_stack[++iic->eoi_ptr] = pending.prio;
215 compatible = (char *)get_property(dn, "compatible", NULL); 99 BUG_ON(iic->eoi_ptr > 15);
216 100 if (pending.flags & CBE_IIC_IRQ_VALID)
217 if (!compatible) { 101 return irq_linear_revmap(iic->host,
218 printk(KERN_WARNING "no compatible property found !\n"); 102 iic_pending_to_hwnum(pending));
219 continue; 103 return NO_IRQ;
220 }
221
222 if (strstr(compatible, "IBM,CBEA-Internal-Interrupt-Controller"))
223 regs = (unsigned long *)get_property(dn,"reg", NULL);
224 else
225 continue;
226
227 if (!regs)
228 printk(KERN_WARNING "IIC: no reg property\n");
229
230 np = (unsigned int *)get_property(dn, "ibm,interrupt-server-ranges", NULL);
231
232 if (!np) {
233 printk(KERN_WARNING "IIC: CPU association not found\n");
234 iic->regs = NULL;
235 iic->target_id = 0xff;
236 return -ENODEV;
237 }
238
239 iic = &per_cpu(iic, np[0]);
240 iic->regs = ioremap(regs[0], sizeof(struct cbe_iic_thread_regs));
241 iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe);
242 iic->eoi_stack[0] = 0xff;
243 printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs);
244
245 iic = &per_cpu(iic, np[1]);
246 iic->regs = ioremap(regs[2], sizeof(struct cbe_iic_thread_regs));
247 iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe);
248 iic->eoi_stack[0] = 0xff;
249
250 printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs);
251
252 found++;
253 }
254
255 if (found)
256 return 0;
257 else
258 return -ENODEV;
259} 104}
260 105
261#ifdef CONFIG_SMP 106#ifdef CONFIG_SMP
@@ -263,12 +108,12 @@ static int __init setup_iic(void)
263/* Use the highest interrupt priorities for IPI */ 108/* Use the highest interrupt priorities for IPI */
264static inline int iic_ipi_to_irq(int ipi) 109static inline int iic_ipi_to_irq(int ipi)
265{ 110{
266 return IIC_IPI_OFFSET + IIC_NUM_IPIS - 1 - ipi; 111 return IIC_IRQ_IPI0 + IIC_NUM_IPIS - 1 - ipi;
267} 112}
268 113
269static inline int iic_irq_to_ipi(int irq) 114static inline int iic_irq_to_ipi(int irq)
270{ 115{
271 return IIC_NUM_IPIS - 1 - (irq - IIC_IPI_OFFSET); 116 return IIC_NUM_IPIS - 1 - (irq - IIC_IRQ_IPI0);
272} 117}
273 118
274void iic_setup_cpu(void) 119void iic_setup_cpu(void)
@@ -287,22 +132,51 @@ u8 iic_get_target_id(int cpu)
287} 132}
288EXPORT_SYMBOL_GPL(iic_get_target_id); 133EXPORT_SYMBOL_GPL(iic_get_target_id);
289 134
135struct irq_host *iic_get_irq_host(int node)
136{
137 if (node < 0 || node >= IIC_NODE_COUNT)
138 return NULL;
139 return iic_hosts[node];
140}
141EXPORT_SYMBOL_GPL(iic_get_irq_host);
142
143
290static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) 144static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
291{ 145{
292 smp_message_recv(iic_irq_to_ipi(irq), regs); 146 int ipi = (int)(long)dev_id;
147
148 smp_message_recv(ipi, regs);
149
293 return IRQ_HANDLED; 150 return IRQ_HANDLED;
294} 151}
295 152
296static void iic_request_ipi(int ipi, const char *name) 153static void iic_request_ipi(int ipi, const char *name)
297{ 154{
298 int irq; 155 int node, virq;
299 156
300 irq = iic_ipi_to_irq(ipi); 157 for (node = 0; node < IIC_NODE_COUNT; node++) {
301 158 char *rname;
302 /* IPIs are marked IRQF_DISABLED as they must run with irqs 159 if (iic_hosts[node] == NULL)
303 * disabled */ 160 continue;
304 set_irq_chip_and_handler(irq, &iic_chip, handle_percpu_irq); 161 virq = irq_create_mapping(iic_hosts[node],
305 request_irq(irq, iic_ipi_action, IRQF_DISABLED, name, NULL); 162 iic_ipi_to_irq(ipi), 0);
163 if (virq == NO_IRQ) {
164 printk(KERN_ERR
165 "iic: failed to map IPI %s on node %d\n",
166 name, node);
167 continue;
168 }
169 rname = kzalloc(strlen(name) + 16, GFP_KERNEL);
170 if (rname)
171 sprintf(rname, "%s node %d", name, node);
172 else
173 rname = (char *)name;
174 if (request_irq(virq, iic_ipi_action, IRQF_DISABLED,
175 rname, (void *)(long)ipi))
176 printk(KERN_ERR
177 "iic: failed to request IPI %s on node %d\n",
178 name, node);
179 }
306} 180}
307 181
308void iic_request_IPIs(void) 182void iic_request_IPIs(void)
@@ -313,41 +187,119 @@ void iic_request_IPIs(void)
313 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); 187 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
314#endif /* CONFIG_DEBUGGER */ 188#endif /* CONFIG_DEBUGGER */
315} 189}
190
316#endif /* CONFIG_SMP */ 191#endif /* CONFIG_SMP */
317 192
318static void __init iic_setup_builtin_handlers(void) 193
194static int iic_host_match(struct irq_host *h, struct device_node *node)
195{
196 return h->host_data != NULL && node == h->host_data;
197}
198
199static int iic_host_map(struct irq_host *h, unsigned int virq,
200 irq_hw_number_t hw, unsigned int flags)
201{
202 if (hw < IIC_IRQ_IPI0)
203 set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq);
204 else
205 set_irq_chip_and_handler(virq, &iic_chip, handle_percpu_irq);
206 return 0;
207}
208
209static int iic_host_xlate(struct irq_host *h, struct device_node *ct,
210 u32 *intspec, unsigned int intsize,
211 irq_hw_number_t *out_hwirq, unsigned int *out_flags)
212
319{ 213{
320 int be, isrc; 214 /* Currently, we don't translate anything. That needs to be fixed as
215 * we get better defined device-trees. iic interrupts have to be
216 * explicitely mapped by whoever needs them
217 */
218 return -ENODEV;
219}
220
221static struct irq_host_ops iic_host_ops = {
222 .match = iic_host_match,
223 .map = iic_host_map,
224 .xlate = iic_host_xlate,
225};
226
227static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr,
228 struct irq_host *host)
229{
230 /* XXX FIXME: should locate the linux CPU number from the HW cpu
231 * number properly. We are lucky for now
232 */
233 struct iic *iic = &per_cpu(iic, hw_cpu);
321 234
322 /* XXX FIXME: Assume two threads per BE are present */ 235 iic->regs = ioremap(addr, sizeof(struct cbe_iic_thread_regs));
323 for (be=0; be < num_present_cpus() / 2; be++) { 236 BUG_ON(iic->regs == NULL);
324 int irq;
325 237
326 /* setup SPE chip and handlers */ 238 iic->target_id = ((hw_cpu & 2) << 3) | ((hw_cpu & 1) ? 0xf : 0xe);
327 for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { 239 iic->eoi_stack[0] = 0xff;
328 irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; 240 iic->host = host;
329 set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq); 241 out_be64(&iic->regs->prio, 0);
242
243 printk(KERN_INFO "IIC for CPU %d at %lx mapped to %p, target id 0x%x\n",
244 hw_cpu, addr, iic->regs, iic->target_id);
245}
246
247static int __init setup_iic(void)
248{
249 struct device_node *dn;
250 struct resource r0, r1;
251 struct irq_host *host;
252 int found = 0;
253 u32 *np;
254
255 for (dn = NULL;
256 (dn = of_find_node_by_name(dn,"interrupt-controller")) != NULL;) {
257 if (!device_is_compatible(dn,
258 "IBM,CBEA-Internal-Interrupt-Controller"))
259 continue;
260 np = (u32 *)get_property(dn, "ibm,interrupt-server-ranges",
261 NULL);
262 if (np == NULL) {
263 printk(KERN_WARNING "IIC: CPU association not found\n");
264 of_node_put(dn);
265 return -ENODEV;
330 } 266 }
331 /* setup cascade chip */ 267 if (of_address_to_resource(dn, 0, &r0) ||
332 irq = IIC_EXT_CASCADE + be * IIC_NODE_STRIDE; 268 of_address_to_resource(dn, 1, &r1)) {
333 set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq); 269 printk(KERN_WARNING "IIC: Can't resolve addresses\n");
270 of_node_put(dn);
271 return -ENODEV;
272 }
273 host = NULL;
274 if (found < IIC_NODE_COUNT) {
275 host = irq_alloc_host(IRQ_HOST_MAP_LINEAR,
276 IIC_SOURCE_COUNT,
277 &iic_host_ops,
278 IIC_IRQ_INVALID);
279 iic_hosts[found] = host;
280 BUG_ON(iic_hosts[found] == NULL);
281 iic_hosts[found]->host_data = of_node_get(dn);
282 found++;
283 }
284 init_one_iic(np[0], r0.start, host);
285 init_one_iic(np[1], r1.start, host);
334 } 286 }
287
288 if (found)
289 return 0;
290 else
291 return -ENODEV;
335} 292}
336 293
337void __init iic_init_IRQ(void) 294void __init iic_init_IRQ(void)
338{ 295{
339 int cpu, irq_offset; 296 /* Discover and initialize iics */
340 struct iic *iic;
341
342 if (setup_iic() < 0) 297 if (setup_iic() < 0)
343 setup_iic_hardcoded(); 298 panic("IIC: Failed to initialize !\n");
344 299
345 irq_offset = 0; 300 /* Set master interrupt handling function */
346 for_each_possible_cpu(cpu) { 301 ppc_md.get_irq = iic_get_irq;
347 iic = &per_cpu(iic, cpu);
348 if (iic->regs)
349 out_be64(&iic->regs->prio, 0xff);
350 }
351 iic_setup_builtin_handlers();
352 302
303 /* Enable on current CPU */
304 iic_setup_cpu();
353} 305}