diff options
author | David S. Miller <davem@davemloft.net> | 2006-06-29 18:07:37 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-29 19:37:38 -0400 |
commit | 2b1e59787198e75fb2ffb3bb4fb247da1c55ac12 (patch) | |
tree | 96d74048849b310135e0c79f663b16c52186caa5 /arch/sparc64/kernel/ebus.c | |
parent | c3a8b85f5ac2c21f4ef75e87bfe55ee7a753ffcf (diff) |
[SPARC64]: of_device layer IRQ resolution
Do IRQ determination generically by parsing the PROM properties,
and using IRQ controller drivers for final resolution.
One immediate positive effect is that all of the IRQ frobbing
in the EBUS, ISA, and PCI controller layers has been eliminated.
We just look up the of_device and use the properly computed
value.
The PCI controller irq_build() routines are gone and no longer
used. Unfortunately sbus_build_irq() has to remain as there is
a direct reference to this in the sunzilog driver. That can be
killed off once the sparc32 side of this is written and the
sunzilog driver is transformed into an "of" bus driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/ebus.c')
-rw-r--r-- | arch/sparc64/kernel/ebus.c | 150 |
1 files changed, 33 insertions, 117 deletions
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 98e0a8cbeecd..aac014d15ad3 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <asm/pbm.h> | 20 | #include <asm/pbm.h> |
21 | #include <asm/ebus.h> | 21 | #include <asm/ebus.h> |
22 | #include <asm/oplib.h> | 22 | #include <asm/oplib.h> |
23 | #include <asm/prom.h> | ||
24 | #include <asm/of_device.h> | ||
23 | #include <asm/bpp.h> | 25 | #include <asm/bpp.h> |
24 | #include <asm/irq.h> | 26 | #include <asm/irq.h> |
25 | 27 | ||
@@ -279,45 +281,12 @@ static inline void *ebus_alloc(size_t size) | |||
279 | return mem; | 281 | return mem; |
280 | } | 282 | } |
281 | 283 | ||
282 | int __init ebus_intmap_match(struct linux_ebus *ebus, | 284 | static void __init fill_ebus_child(struct device_node *dp, |
283 | struct linux_prom_registers *reg, | 285 | struct linux_ebus_child *dev, |
284 | int *interrupt) | 286 | int non_standard_regs) |
285 | { | ||
286 | struct linux_prom_ebus_intmap *imap; | ||
287 | struct linux_prom_ebus_intmask *imask; | ||
288 | unsigned int hi, lo, irq; | ||
289 | int i, len, n_imap; | ||
290 | |||
291 | imap = of_get_property(ebus->prom_node, "interrupt-map", &len); | ||
292 | if (!imap) | ||
293 | return 0; | ||
294 | n_imap = len / sizeof(imap[0]); | ||
295 | |||
296 | imask = of_get_property(ebus->prom_node, "interrupt-map-mask", NULL); | ||
297 | if (!imask) | ||
298 | return 0; | ||
299 | |||
300 | hi = reg->which_io & imask->phys_hi; | ||
301 | lo = reg->phys_addr & imask->phys_lo; | ||
302 | irq = *interrupt & imask->interrupt; | ||
303 | for (i = 0; i < n_imap; i++) { | ||
304 | if ((imap[i].phys_hi == hi) && | ||
305 | (imap[i].phys_lo == lo) && | ||
306 | (imap[i].interrupt == irq)) { | ||
307 | *interrupt = imap[i].cinterrupt; | ||
308 | return 0; | ||
309 | } | ||
310 | } | ||
311 | return -1; | ||
312 | } | ||
313 | |||
314 | void __init fill_ebus_child(struct device_node *dp, | ||
315 | struct linux_prom_registers *preg, | ||
316 | struct linux_ebus_child *dev, | ||
317 | int non_standard_regs) | ||
318 | { | 287 | { |
288 | struct of_device *op; | ||
319 | int *regs; | 289 | int *regs; |
320 | int *irqs; | ||
321 | int i, len; | 290 | int i, len; |
322 | 291 | ||
323 | dev->prom_node = dp; | 292 | dev->prom_node = dp; |
@@ -354,12 +323,16 @@ void __init fill_ebus_child(struct device_node *dp, | |||
354 | } | 323 | } |
355 | } | 324 | } |
356 | 325 | ||
357 | for (i = 0; i < PROMINTR_MAX; i++) | 326 | op = of_find_device_by_node(dp); |
358 | dev->irqs[i] = PCI_IRQ_NONE; | 327 | if (!op) { |
359 | |||
360 | irqs = of_get_property(dp, "interrupts", &len); | ||
361 | if (!irqs) { | ||
362 | dev->num_irqs = 0; | 328 | dev->num_irqs = 0; |
329 | } else { | ||
330 | dev->num_irqs = op->num_irqs; | ||
331 | for (i = 0; i < dev->num_irqs; i++) | ||
332 | dev->irqs[i] = op->irqs[i]; | ||
333 | } | ||
334 | |||
335 | if (!dev->num_irqs) { | ||
363 | /* | 336 | /* |
364 | * Oh, well, some PROMs don't export interrupts | 337 | * Oh, well, some PROMs don't export interrupts |
365 | * property to children of EBus devices... | 338 | * property to children of EBus devices... |
@@ -375,23 +348,6 @@ void __init fill_ebus_child(struct device_node *dp, | |||
375 | dev->irqs[0] = dev->parent->irqs[1]; | 348 | dev->irqs[0] = dev->parent->irqs[1]; |
376 | } | 349 | } |
377 | } | 350 | } |
378 | } else { | ||
379 | dev->num_irqs = len / sizeof(irqs[0]); | ||
380 | for (i = 0; i < dev->num_irqs; i++) { | ||
381 | struct pci_pbm_info *pbm = dev->bus->parent; | ||
382 | struct pci_controller_info *p = pbm->parent; | ||
383 | |||
384 | if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) { | ||
385 | dev->irqs[i] = p->irq_build(pbm, | ||
386 | dev->bus->self, | ||
387 | irqs[i]); | ||
388 | } else { | ||
389 | /* If we get a bogus interrupt property, just | ||
390 | * record the raw value instead of punting. | ||
391 | */ | ||
392 | dev->irqs[i] = irqs[i]; | ||
393 | } | ||
394 | } | ||
395 | } | 351 | } |
396 | } | 352 | } |
397 | 353 | ||
@@ -403,72 +359,32 @@ static int __init child_regs_nonstandard(struct linux_ebus_device *dev) | |||
403 | return 0; | 359 | return 0; |
404 | } | 360 | } |
405 | 361 | ||
406 | void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) | 362 | static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) |
407 | { | 363 | { |
408 | struct linux_prom_registers *regs; | ||
409 | struct linux_ebus_child *child; | 364 | struct linux_ebus_child *child; |
410 | int *irqs; | 365 | struct of_device *op; |
411 | int i, n, len; | 366 | int i, len; |
412 | 367 | ||
413 | dev->prom_node = dp; | 368 | dev->prom_node = dp; |
414 | 369 | ||
415 | printk(" [%s", dp->name); | 370 | printk(" [%s", dp->name); |
416 | 371 | ||
417 | regs = of_get_property(dp, "reg", &len); | 372 | op = of_find_device_by_node(dp); |
418 | if (!regs) { | 373 | if (!op) { |
419 | dev->num_addrs = 0; | 374 | dev->num_addrs = 0; |
420 | goto probe_interrupts; | ||
421 | } | ||
422 | |||
423 | if (len % sizeof(struct linux_prom_registers)) { | ||
424 | prom_printf("UGH: proplen for %s was %d, need multiple of %d\n", | ||
425 | dev->prom_node->name, len, | ||
426 | (int)sizeof(struct linux_prom_registers)); | ||
427 | prom_halt(); | ||
428 | } | ||
429 | dev->num_addrs = len / sizeof(struct linux_prom_registers); | ||
430 | |||
431 | for (i = 0; i < dev->num_addrs; i++) { | ||
432 | /* XXX Learn how to interpret ebus ranges... -DaveM */ | ||
433 | if (regs[i].which_io >= 0x10) | ||
434 | n = (regs[i].which_io - 0x10) >> 2; | ||
435 | else | ||
436 | n = regs[i].which_io; | ||
437 | |||
438 | dev->resource[i].start = dev->bus->self->resource[n].start; | ||
439 | dev->resource[i].start += (unsigned long)regs[i].phys_addr; | ||
440 | dev->resource[i].end = | ||
441 | (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL); | ||
442 | dev->resource[i].flags = IORESOURCE_MEM; | ||
443 | dev->resource[i].name = dev->prom_node->name; | ||
444 | request_resource(&dev->bus->self->resource[n], | ||
445 | &dev->resource[i]); | ||
446 | } | ||
447 | |||
448 | probe_interrupts: | ||
449 | for (i = 0; i < PROMINTR_MAX; i++) | ||
450 | dev->irqs[i] = PCI_IRQ_NONE; | ||
451 | |||
452 | irqs = of_get_property(dp, "interrupts", &len); | ||
453 | if (!irqs) { | ||
454 | dev->num_irqs = 0; | 375 | dev->num_irqs = 0; |
455 | } else { | 376 | } else { |
456 | dev->num_irqs = len / sizeof(irqs[0]); | 377 | (void) of_get_property(dp, "reg", &len); |
457 | for (i = 0; i < dev->num_irqs; i++) { | 378 | dev->num_addrs = len / sizeof(struct linux_prom_registers); |
458 | struct pci_pbm_info *pbm = dev->bus->parent; | 379 | |
459 | struct pci_controller_info *p = pbm->parent; | 380 | for (i = 0; i < dev->num_addrs; i++) |
460 | 381 | memcpy(&dev->resource[i], | |
461 | if (ebus_intmap_match(dev->bus, ®s[0], &irqs[i]) != -1) { | 382 | &op->resource[i], |
462 | dev->irqs[i] = p->irq_build(pbm, | 383 | sizeof(struct resource)); |
463 | dev->bus->self, | 384 | |
464 | irqs[i]); | 385 | dev->num_irqs = op->num_irqs; |
465 | } else { | 386 | for (i = 0; i < dev->num_irqs; i++) |
466 | /* If we get a bogus interrupt property, just | 387 | dev->irqs[i] = op->irqs[i]; |
467 | * record the raw value instead of punting. | ||
468 | */ | ||
469 | dev->irqs[i] = irqs[i]; | ||
470 | } | ||
471 | } | ||
472 | } | 388 | } |
473 | 389 | ||
474 | dev->ofdev.node = dp; | 390 | dev->ofdev.node = dp; |
@@ -490,7 +406,7 @@ probe_interrupts: | |||
490 | child->next = NULL; | 406 | child->next = NULL; |
491 | child->parent = dev; | 407 | child->parent = dev; |
492 | child->bus = dev->bus; | 408 | child->bus = dev->bus; |
493 | fill_ebus_child(dp, regs, child, | 409 | fill_ebus_child(dp, child, |
494 | child_regs_nonstandard(dev)); | 410 | child_regs_nonstandard(dev)); |
495 | 411 | ||
496 | while ((dp = dp->sibling) != NULL) { | 412 | while ((dp = dp->sibling) != NULL) { |
@@ -500,7 +416,7 @@ probe_interrupts: | |||
500 | child->next = NULL; | 416 | child->next = NULL; |
501 | child->parent = dev; | 417 | child->parent = dev; |
502 | child->bus = dev->bus; | 418 | child->bus = dev->bus; |
503 | fill_ebus_child(dp, regs, child, | 419 | fill_ebus_child(dp, child, |
504 | child_regs_nonstandard(dev)); | 420 | child_regs_nonstandard(dev)); |
505 | } | 421 | } |
506 | } | 422 | } |