diff options
Diffstat (limited to 'arch/powerpc/platforms/cell/interrupt.c')
-rw-r--r-- | arch/powerpc/platforms/cell/interrupt.c | 133 |
1 files changed, 106 insertions, 27 deletions
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 63aa52acf441..978be1c30c1b 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c | |||
@@ -63,7 +63,24 @@ static DEFINE_PER_CPU(struct iic, iic); | |||
63 | 63 | ||
64 | void iic_local_enable(void) | 64 | void iic_local_enable(void) |
65 | { | 65 | { |
66 | out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | 66 | struct iic *iic = &__get_cpu_var(iic); |
67 | u64 tmp; | ||
68 | |||
69 | /* | ||
70 | * There seems to be a bug that is present in DD2.x CPUs | ||
71 | * and still only partially fixed in DD3.1. | ||
72 | * This bug causes a value written to the priority register | ||
73 | * not to make it there, resulting in a system hang unless we | ||
74 | * write it again. | ||
75 | * Masking with 0xf0 is done because the Cell BE does not | ||
76 | * implement the lower four bits of the interrupt priority, | ||
77 | * they always read back as zeroes, although future CPUs | ||
78 | * might implement different bits. | ||
79 | */ | ||
80 | do { | ||
81 | out_be64(&iic->regs->prio, 0xff); | ||
82 | tmp = in_be64(&iic->regs->prio); | ||
83 | } while ((tmp & 0xf0) != 0xf0); | ||
67 | } | 84 | } |
68 | 85 | ||
69 | void iic_local_disable(void) | 86 | void iic_local_disable(void) |
@@ -123,7 +140,7 @@ static int iic_external_get_irq(struct iic_pending_bits pending) | |||
123 | pending.class != 2) | 140 | pending.class != 2) |
124 | break; | 141 | break; |
125 | irq = IIC_EXT_OFFSET | 142 | irq = IIC_EXT_OFFSET |
126 | + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) | 143 | + spider_get_irq(node) |
127 | + node * IIC_NODE_STRIDE; | 144 | + node * IIC_NODE_STRIDE; |
128 | break; | 145 | break; |
129 | case 0x01 ... 0x04: | 146 | case 0x01 ... 0x04: |
@@ -174,38 +191,98 @@ int iic_get_irq(struct pt_regs *regs) | |||
174 | return irq; | 191 | return irq; |
175 | } | 192 | } |
176 | 193 | ||
177 | static int setup_iic(int cpu, struct iic *iic) | 194 | /* hardcoded part to be compatible with older firmware */ |
195 | |||
196 | static int setup_iic_hardcoded(void) | ||
178 | { | 197 | { |
179 | struct device_node *np; | 198 | struct device_node *np; |
180 | int nodeid = cpu / 2; | 199 | int nodeid, cpu; |
181 | unsigned long regs; | 200 | unsigned long regs; |
201 | struct iic *iic; | ||
182 | 202 | ||
183 | for (np = of_find_node_by_type(NULL, "cpu"); | 203 | for_each_cpu(cpu) { |
184 | np; | 204 | iic = &per_cpu(iic, cpu); |
185 | np = of_find_node_by_type(np, "cpu")) { | 205 | nodeid = cpu/2; |
186 | if (nodeid == *(int *)get_property(np, "node-id", NULL)) | 206 | |
187 | break; | 207 | for (np = of_find_node_by_type(NULL, "cpu"); |
208 | np; | ||
209 | np = of_find_node_by_type(np, "cpu")) { | ||
210 | if (nodeid == *(int *)get_property(np, "node-id", NULL)) | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | if (!np) { | ||
215 | printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); | ||
216 | iic->regs = NULL; | ||
217 | iic->target_id = 0xff; | ||
218 | return -ENODEV; | ||
219 | } | ||
220 | |||
221 | regs = *(long *)get_property(np, "iic", NULL); | ||
222 | |||
223 | /* hack until we have decided on the devtree info */ | ||
224 | regs += 0x400; | ||
225 | if (cpu & 1) | ||
226 | regs += 0x20; | ||
227 | |||
228 | printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs); | ||
229 | iic->regs = ioremap(regs, sizeof(struct iic_regs)); | ||
230 | iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe); | ||
188 | } | 231 | } |
189 | 232 | ||
190 | if (!np) { | 233 | return 0; |
191 | printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); | 234 | } |
192 | iic->regs = NULL; | ||
193 | iic->target_id = 0xff; | ||
194 | return -ENODEV; | ||
195 | } | ||
196 | 235 | ||
197 | regs = *(long *)get_property(np, "iic", NULL); | 236 | static int setup_iic(void) |
237 | { | ||
238 | struct device_node *dn; | ||
239 | unsigned long *regs; | ||
240 | char *compatible; | ||
241 | unsigned *np, found = 0; | ||
242 | struct iic *iic = NULL; | ||
243 | |||
244 | for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { | ||
245 | compatible = (char *)get_property(dn, "compatible", NULL); | ||
246 | |||
247 | if (!compatible) { | ||
248 | printk(KERN_WARNING "no compatible property found !\n"); | ||
249 | continue; | ||
250 | } | ||
198 | 251 | ||
199 | /* hack until we have decided on the devtree info */ | 252 | if (strstr(compatible, "IBM,CBEA-Internal-Interrupt-Controller")) |
200 | regs += 0x400; | 253 | regs = (unsigned long *)get_property(dn,"reg", NULL); |
201 | if (cpu & 1) | 254 | else |
202 | regs += 0x20; | 255 | continue; |
203 | 256 | ||
204 | printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); | 257 | if (!regs) |
205 | iic->regs = __ioremap(regs, sizeof(struct iic_regs), | 258 | printk(KERN_WARNING "IIC: no reg property\n"); |
206 | _PAGE_NO_CACHE); | 259 | |
207 | iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe); | 260 | np = (unsigned int *)get_property(dn, "ibm,interrupt-server-ranges", NULL); |
208 | return 0; | 261 | |
262 | if (!np) { | ||
263 | printk(KERN_WARNING "IIC: CPU association not found\n"); | ||
264 | iic->regs = NULL; | ||
265 | iic->target_id = 0xff; | ||
266 | return -ENODEV; | ||
267 | } | ||
268 | |||
269 | iic = &per_cpu(iic, np[0]); | ||
270 | iic->regs = ioremap(regs[0], sizeof(struct iic_regs)); | ||
271 | iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe); | ||
272 | printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs); | ||
273 | |||
274 | iic = &per_cpu(iic, np[1]); | ||
275 | iic->regs = ioremap(regs[2], sizeof(struct iic_regs)); | ||
276 | iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe); | ||
277 | printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs); | ||
278 | |||
279 | found++; | ||
280 | } | ||
281 | |||
282 | if (found) | ||
283 | return 0; | ||
284 | else | ||
285 | return -ENODEV; | ||
209 | } | 286 | } |
210 | 287 | ||
211 | #ifdef CONFIG_SMP | 288 | #ifdef CONFIG_SMP |
@@ -283,10 +360,12 @@ void iic_init_IRQ(void) | |||
283 | int cpu, irq_offset; | 360 | int cpu, irq_offset; |
284 | struct iic *iic; | 361 | struct iic *iic; |
285 | 362 | ||
363 | if (setup_iic() < 0) | ||
364 | setup_iic_hardcoded(); | ||
365 | |||
286 | irq_offset = 0; | 366 | irq_offset = 0; |
287 | for_each_cpu(cpu) { | 367 | for_each_possible_cpu(cpu) { |
288 | iic = &per_cpu(iic, cpu); | 368 | iic = &per_cpu(iic, cpu); |
289 | setup_iic(cpu, iic); | ||
290 | if (iic->regs) | 369 | if (iic->regs) |
291 | out_be64(&iic->regs->prio, 0xff); | 370 | out_be64(&iic->regs->prio, 0xff); |
292 | } | 371 | } |