aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/cell/interrupt.c
diff options
context:
space:
mode:
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 97936f547f1..9d5da789689 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}