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.c419
1 files changed, 186 insertions, 233 deletions
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 22da1335445a..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"
@@ -37,231 +42,65 @@
37struct iic { 42struct iic {
38 struct cbe_iic_thread_regs __iomem *regs; 43 struct cbe_iic_thread_regs __iomem *regs;
39 u8 target_id; 44 u8 target_id;
45 u8 eoi_stack[16];
46 int eoi_ptr;
47 struct irq_host *host;
40}; 48};
41 49
42static 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];
43 53
44void iic_local_enable(void) 54/* Convert between "pending" bits and hw irq number */
55static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)
45{ 56{
46 struct iic *iic = &__get_cpu_var(iic); 57 unsigned char unit = bits.source & 0xf;
47 u64 tmp;
48
49 /*
50 * There seems to be a bug that is present in DD2.x CPUs
51 * and still only partially fixed in DD3.1.
52 * This bug causes a value written to the priority register
53 * not to make it there, resulting in a system hang unless we
54 * write it again.
55 * Masking with 0xf0 is done because the Cell BE does not
56 * implement the lower four bits of the interrupt priority,
57 * they always read back as zeroes, although future CPUs
58 * might implement different bits.
59 */
60 do {
61 out_be64(&iic->regs->prio, 0xff);
62 tmp = in_be64(&iic->regs->prio);
63 } while ((tmp & 0xf0) != 0xf0);
64}
65
66void iic_local_disable(void)
67{
68 out_be64(&__get_cpu_var(iic).regs->prio, 0x0);
69}
70 58
71static unsigned int iic_startup(unsigned int irq) 59 if (bits.flags & CBE_IIC_IRQ_IPI)
72{ 60 return IIC_IRQ_IPI0 | (bits.prio >> 4);
73 return 0; 61 else if (bits.class <= 3)
62 return (bits.class << 4) | unit;
63 else
64 return IIC_IRQ_INVALID;
74} 65}
75 66
76static void iic_enable(unsigned int irq) 67static void iic_mask(unsigned int irq)
77{ 68{
78 iic_local_enable();
79} 69}
80 70
81static void iic_disable(unsigned int irq) 71static void iic_unmask(unsigned int irq)
82{ 72{
83} 73}
84 74
85static void iic_end(unsigned int irq) 75static void iic_eoi(unsigned int irq)
86{ 76{
87 iic_local_enable(); 77 struct iic *iic = &__get_cpu_var(iic);
78 out_be64(&iic->regs->prio, iic->eoi_stack[--iic->eoi_ptr]);
79 BUG_ON(iic->eoi_ptr < 0);
88} 80}
89 81
90static struct hw_interrupt_type iic_pic = { 82static struct irq_chip iic_chip = {
91 .typename = " CELL-IIC ", 83 .typename = " CELL-IIC ",
92 .startup = iic_startup, 84 .mask = iic_mask,
93 .enable = iic_enable, 85 .unmask = iic_unmask,
94 .disable = iic_disable, 86 .eoi = iic_eoi,
95 .end = iic_end,
96}; 87};
97 88
98static int iic_external_get_irq(struct cbe_iic_pending_bits pending)
99{
100 int irq;
101 unsigned char node, unit;
102
103 node = pending.source >> 4;
104 unit = pending.source & 0xf;
105 irq = -1;
106
107 /*
108 * This mapping is specific to the Cell Broadband
109 * Engine. We might need to get the numbers
110 * from the device tree to support future CPUs.
111 */
112 switch (unit) {
113 case 0x00:
114 case 0x0b:
115 /*
116 * One of these units can be connected
117 * to an external interrupt controller.
118 */
119 if (pending.class != 2)
120 break;
121 irq = IIC_EXT_OFFSET
122 + spider_get_irq(node)
123 + node * IIC_NODE_STRIDE;
124 break;
125 case 0x01 ... 0x04:
126 case 0x07 ... 0x0a:
127 /*
128 * These units are connected to the SPEs
129 */
130 if (pending.class > 2)
131 break;
132 irq = IIC_SPE_OFFSET
133 + pending.class * IIC_CLASS_STRIDE
134 + node * IIC_NODE_STRIDE
135 + unit;
136 break;
137 }
138 if (irq == -1)
139 printk(KERN_WARNING "Unexpected interrupt class %02x, "
140 "source %02x, prio %02x, cpu %02x\n", pending.class,
141 pending.source, pending.prio, smp_processor_id());
142 return irq;
143}
144
145/* 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 */
146int iic_get_irq(struct pt_regs *regs) 90static unsigned int iic_get_irq(struct pt_regs *regs)
147{ 91{
148 struct iic *iic; 92 struct cbe_iic_pending_bits pending;
149 int irq; 93 struct iic *iic;
150 struct cbe_iic_pending_bits pending; 94
151 95 iic = &__get_cpu_var(iic);
152 iic = &__get_cpu_var(iic); 96 *(unsigned long *) &pending =
153 *(unsigned long *) &pending = 97 in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
154 in_be64((unsigned long __iomem *) &iic->regs->pending_destr); 98 iic->eoi_stack[++iic->eoi_ptr] = pending.prio;
155 99 BUG_ON(iic->eoi_ptr > 15);
156 irq = -1; 100 if (pending.flags & CBE_IIC_IRQ_VALID)
157 if (pending.flags & CBE_IIC_IRQ_VALID) { 101 return irq_linear_revmap(iic->host,
158 if (pending.flags & CBE_IIC_IRQ_IPI) { 102 iic_pending_to_hwnum(pending));
159 irq = IIC_IPI_OFFSET + (pending.prio >> 4); 103 return NO_IRQ;
160/*
161 if (irq > 0x80)
162 printk(KERN_WARNING "Unexpected IPI prio %02x"
163 "on CPU %02x\n", pending.prio,
164 smp_processor_id());
165*/
166 } else {
167 irq = iic_external_get_irq(pending);
168 }
169 }
170 return irq;
171}
172
173/* hardcoded part to be compatible with older firmware */
174
175static int setup_iic_hardcoded(void)
176{
177 struct device_node *np;
178 int nodeid, cpu;
179 unsigned long regs;
180 struct iic *iic;
181
182 for_each_possible_cpu(cpu) {
183 iic = &per_cpu(iic, cpu);
184 nodeid = cpu/2;
185
186 for (np = of_find_node_by_type(NULL, "cpu");
187 np;
188 np = of_find_node_by_type(np, "cpu")) {
189 if (nodeid == *(int *)get_property(np, "node-id", NULL))
190 break;
191 }
192
193 if (!np) {
194 printk(KERN_WARNING "IIC: CPU %d not found\n", cpu);
195 iic->regs = NULL;
196 iic->target_id = 0xff;
197 return -ENODEV;
198 }
199
200 regs = *(long *)get_property(np, "iic", NULL);
201
202 /* hack until we have decided on the devtree info */
203 regs += 0x400;
204 if (cpu & 1)
205 regs += 0x20;
206
207 printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs);
208 iic->regs = ioremap(regs, sizeof(struct cbe_iic_thread_regs));
209 iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
210 }
211
212 return 0;
213}
214
215static int setup_iic(void)
216{
217 struct device_node *dn;
218 unsigned long *regs;
219 char *compatible;
220 unsigned *np, found = 0;
221 struct iic *iic = NULL;
222
223 for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) {
224 compatible = (char *)get_property(dn, "compatible", NULL);
225
226 if (!compatible) {
227 printk(KERN_WARNING "no compatible property found !\n");
228 continue;
229 }
230
231 if (strstr(compatible, "IBM,CBEA-Internal-Interrupt-Controller"))
232 regs = (unsigned long *)get_property(dn,"reg", NULL);
233 else
234 continue;
235
236 if (!regs)
237 printk(KERN_WARNING "IIC: no reg property\n");
238
239 np = (unsigned int *)get_property(dn, "ibm,interrupt-server-ranges", NULL);
240
241 if (!np) {
242 printk(KERN_WARNING "IIC: CPU association not found\n");
243 iic->regs = NULL;
244 iic->target_id = 0xff;
245 return -ENODEV;
246 }
247
248 iic = &per_cpu(iic, np[0]);
249 iic->regs = ioremap(regs[0], sizeof(struct cbe_iic_thread_regs));
250 iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe);
251 printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs);
252
253 iic = &per_cpu(iic, np[1]);
254 iic->regs = ioremap(regs[2], sizeof(struct cbe_iic_thread_regs));
255 iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe);
256 printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs);
257
258 found++;
259 }
260
261 if (found)
262 return 0;
263 else
264 return -ENODEV;
265} 104}
266 105
267#ifdef CONFIG_SMP 106#ifdef CONFIG_SMP
@@ -269,12 +108,12 @@ static int setup_iic(void)
269/* Use the highest interrupt priorities for IPI */ 108/* Use the highest interrupt priorities for IPI */
270static inline int iic_ipi_to_irq(int ipi) 109static inline int iic_ipi_to_irq(int ipi)
271{ 110{
272 return IIC_IPI_OFFSET + IIC_NUM_IPIS - 1 - ipi; 111 return IIC_IRQ_IPI0 + IIC_NUM_IPIS - 1 - ipi;
273} 112}
274 113
275static inline int iic_irq_to_ipi(int irq) 114static inline int iic_irq_to_ipi(int irq)
276{ 115{
277 return IIC_NUM_IPIS - 1 - (irq - IIC_IPI_OFFSET); 116 return IIC_NUM_IPIS - 1 - (irq - IIC_IRQ_IPI0);
278} 117}
279 118
280void iic_setup_cpu(void) 119void iic_setup_cpu(void)
@@ -293,22 +132,51 @@ u8 iic_get_target_id(int cpu)
293} 132}
294EXPORT_SYMBOL_GPL(iic_get_target_id); 133EXPORT_SYMBOL_GPL(iic_get_target_id);
295 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
296static 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)
297{ 145{
298 smp_message_recv(iic_irq_to_ipi(irq), regs); 146 int ipi = (int)(long)dev_id;
147
148 smp_message_recv(ipi, regs);
149
299 return IRQ_HANDLED; 150 return IRQ_HANDLED;
300} 151}
301 152
302static void iic_request_ipi(int ipi, const char *name) 153static void iic_request_ipi(int ipi, const char *name)
303{ 154{
304 int irq; 155 int node, virq;
305 156
306 irq = iic_ipi_to_irq(ipi); 157 for (node = 0; node < IIC_NODE_COUNT; node++) {
307 /* IPIs are marked IRQF_DISABLED as they must run with irqs 158 char *rname;
308 * disabled */ 159 if (iic_hosts[node] == NULL)
309 get_irq_desc(irq)->chip = &iic_pic; 160 continue;
310 get_irq_desc(irq)->status |= IRQ_PER_CPU; 161 virq = irq_create_mapping(iic_hosts[node],
311 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 }
312} 180}
313 181
314void iic_request_IPIs(void) 182void iic_request_IPIs(void)
@@ -319,34 +187,119 @@ void iic_request_IPIs(void)
319 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); 187 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
320#endif /* CONFIG_DEBUGGER */ 188#endif /* CONFIG_DEBUGGER */
321} 189}
190
322#endif /* CONFIG_SMP */ 191#endif /* CONFIG_SMP */
323 192
324static void iic_setup_spe_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
213{
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)
325{ 229{
326 int be, isrc; 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);
234
235 iic->regs = ioremap(addr, sizeof(struct cbe_iic_thread_regs));
236 BUG_ON(iic->regs == NULL);
327 237
328 /* Assume two threads per BE are present */ 238 iic->target_id = ((hw_cpu & 2) << 3) | ((hw_cpu & 1) ? 0xf : 0xe);
329 for (be=0; be < num_present_cpus() / 2; be++) { 239 iic->eoi_stack[0] = 0xff;
330 for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { 240 iic->host = host;
331 int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; 241 out_be64(&iic->regs->prio, 0);
332 get_irq_desc(irq)->chip = &iic_pic; 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;
266 }
267 if (of_address_to_resource(dn, 0, &r0) ||
268 of_address_to_resource(dn, 1, &r1)) {
269 printk(KERN_WARNING "IIC: Can't resolve addresses\n");
270 of_node_put(dn);
271 return -ENODEV;
333 } 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 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); 302
348 if (iic->regs) 303 /* Enable on current CPU */
349 out_be64(&iic->regs->prio, 0xff); 304 iic_setup_cpu();
350 }
351 iic_setup_spe_handlers();
352} 305}