diff options
Diffstat (limited to 'arch/powerpc/platforms/cell/interrupt.c')
-rw-r--r-- | arch/powerpc/platforms/cell/interrupt.c | 396 |
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 | ||
44 | static DEFINE_PER_CPU(struct iic, iic); | 50 | static DEFINE_PER_CPU(struct iic, iic); |
51 | #define IIC_NODE_COUNT 2 | ||
52 | static struct irq_host *iic_hosts[IIC_NODE_COUNT]; | ||
53 | |||
54 | /* Convert between "pending" bits and hw irq number */ | ||
55 | static 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 | ||
46 | static void iic_mask(unsigned int irq) | 67 | static 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 | */ | ||
85 | static 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 */ |
134 | int iic_get_irq(struct pt_regs *regs) | 90 | static 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 | |||
165 | static 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 | |||
206 | static 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 */ |
264 | static inline int iic_ipi_to_irq(int ipi) | 109 | static 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 | ||
269 | static inline int iic_irq_to_ipi(int irq) | 114 | static 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 | ||
274 | void iic_setup_cpu(void) | 119 | void iic_setup_cpu(void) |
@@ -287,22 +132,51 @@ u8 iic_get_target_id(int cpu) | |||
287 | } | 132 | } |
288 | EXPORT_SYMBOL_GPL(iic_get_target_id); | 133 | EXPORT_SYMBOL_GPL(iic_get_target_id); |
289 | 134 | ||
135 | struct 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 | } | ||
141 | EXPORT_SYMBOL_GPL(iic_get_irq_host); | ||
142 | |||
143 | |||
290 | static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) | 144 | static 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 | ||
296 | static void iic_request_ipi(int ipi, const char *name) | 153 | static 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 | ||
308 | void iic_request_IPIs(void) | 182 | void 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 | ||
318 | static void __init iic_setup_builtin_handlers(void) | 193 | |
194 | static 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 | |||
199 | static 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 | |||
209 | static 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 | |||
221 | static struct irq_host_ops iic_host_ops = { | ||
222 | .match = iic_host_match, | ||
223 | .map = iic_host_map, | ||
224 | .xlate = iic_host_xlate, | ||
225 | }; | ||
226 | |||
227 | static 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 | |||
247 | static 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 | ||
337 | void __init iic_init_IRQ(void) | 294 | void __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 | } |