aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/pci_common.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2006-02-17 02:01:10 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-03-20 04:13:20 -0500
commit6154f94f0e1b3984ad2d0bcda586bc8946398b8a (patch)
tree8abc4d83ed8ddef986de2c0593f55d3e879c7c78 /arch/sparc64/kernel/pci_common.c
parent14f6689cbb3ec2c194bd770fbe0d6e2d90eb6760 (diff)
[SPARC64]: Rewrite pci_intmap_match().
The whole algorithm was wrong. What we need to do is: 1) Walk each PCI bus above this device on the path to the PCI controller nexus, and for each: a) If interrupt-map exists, apply it, record IRQ controller node b) Else, swivel interrupt number using PCI_SLOT(), use PCI bus parent OBP node as controller node c) Walk up to "controller node" until we hit the first PCI bus in this domain, or "controller node" is the PCI controller OBP node 2) If we walked to PCI controller OBP node, we're done. 3) Else, apply PCI controller interrupt-map to interrupt. There is some stuff that needs to be checked out for ebus and isa, but the PCI part is good to go. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r--arch/sparc64/kernel/pci_common.c269
1 files changed, 155 insertions, 114 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index ab6a2e1b76fb..f9101966a746 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -541,142 +541,183 @@ void __init pci_assign_unassigned(struct pci_pbm_info *pbm,
541 pci_assign_unassigned(pbm, bus); 541 pci_assign_unassigned(pbm, bus);
542} 542}
543 543
544static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) 544static inline unsigned int pci_slot_swivel(struct pci_pbm_info *pbm,
545 struct pci_dev *toplevel_pdev,
546 struct pci_dev *pdev,
547 unsigned int interrupt)
545{ 548{
546 struct linux_prom_pci_intmap bridge_local_intmap[PROM_PCIIMAP_MAX], *intmap; 549 unsigned int ret;
547 struct linux_prom_pci_intmask bridge_local_intmask, *intmask;
548 struct pcidev_cookie *dev_pcp = pdev->sysdata;
549 struct pci_pbm_info *pbm = dev_pcp->pbm;
550 struct linux_prom_pci_registers *pregs = dev_pcp->prom_regs;
551 unsigned int hi, mid, lo, irq;
552 int i, num_intmap, map_slot;
553 550
554 intmap = &pbm->pbm_intmap[0]; 551 if (unlikely(interrupt < 1 || interrupt > 4)) {
555 intmask = &pbm->pbm_intmask; 552 printk("%s: Device %s interrupt value of %u is strange.\n",
556 num_intmap = pbm->num_pbm_intmap; 553 pbm->name, pci_name(pdev), interrupt);
557 map_slot = 0; 554 return interrupt;
555 }
558 556
559 /* If we are underneath a PCI bridge, use PROM register 557 ret = ((interrupt - 1 + (PCI_SLOT(pdev->devfn) & 3)) & 3) + 1;
560 * property of the parent bridge which is closest to 558
561 * the PBM. 559 printk("%s: %s IRQ Swivel %s [%x:%x] -> [%x]\n",
562 * 560 pbm->name, pci_name(toplevel_pdev), pci_name(pdev),
563 * However if that parent bridge has interrupt map/mask 561 interrupt, PCI_SLOT(pdev->devfn), ret);
564 * properties of its own we use the PROM register property
565 * of the next child device on the path to PDEV.
566 *
567 * In detail the two cases are (note that the 'X' below is the
568 * 'next child on the path to PDEV' mentioned above):
569 *
570 * 1) PBM --> PCI bus lacking int{map,mask} --> X ... PDEV
571 *
572 * Here we use regs of 'PCI bus' device.
573 *
574 * 2) PBM --> PCI bus with int{map,mask} --> X ... PDEV
575 *
576 * Here we use regs of 'X'. Note that X can be PDEV.
577 */
578 if (pdev->bus->number != pbm->pci_first_busno) {
579 struct pcidev_cookie *bus_pcp, *regs_pcp;
580 struct pci_dev *bus_dev, *regs_dev;
581 int plen;
582 562
583 bus_dev = pdev->bus->self; 563 return ret;
584 bus_pcp = bus_dev->sysdata; 564}
585 regs_dev = pdev;
586 regs_pcp = regs_dev->sysdata;
587 565
588 while (bus_dev->bus && 566static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm,
589 bus_dev->bus->number != pbm->pci_first_busno && 567 struct pci_dev *toplevel_pdev,
590 prom_getproplen(bus_pcp->prom_node, 568 struct pci_dev *pbus,
591 "interrupt-map") <= 0) { 569 struct pci_dev *pdev,
592 regs_dev = bus_dev; 570 unsigned int interrupt,
593 regs_pcp = regs_dev->sysdata; 571 unsigned int *cnode)
572{
573 struct linux_prom_pci_intmap imap[PROM_PCIIMAP_MAX];
574 struct linux_prom_pci_intmask imask;
575 struct pcidev_cookie *pbus_pcp = pbus->sysdata;
576 struct pcidev_cookie *pdev_pcp = pdev->sysdata;
577 struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs;
578 int plen, num_imap, i;
579 unsigned int hi, mid, lo, irq, orig_interrupt;
580
581 *cnode = pbus_pcp->prom_node;
582
583 plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map",
584 (char *) &imap[0], sizeof(imap));
585 if (plen <= 0 ||
586 (plen % sizeof(struct linux_prom_pci_intmap)) != 0) {
587 printk("%s: Device %s interrupt-map has bad len %d\n",
588 pbm->name, pci_name(pbus), plen);
589 goto no_intmap;
590 }
591 num_imap = plen / sizeof(struct linux_prom_pci_intmap);
592
593 plen = prom_getproperty(pbus_pcp->prom_node, "interrupt-map-mask",
594 (char *) &imask, sizeof(imask));
595 if (plen <= 0 ||
596 (plen % sizeof(struct linux_prom_pci_intmask)) != 0) {
597 printk("%s: Device %s interrupt-map-mask has bad len %d\n",
598 pbm->name, pci_name(pbus), plen);
599 goto no_intmap;
600 }
594 601
595 bus_dev = bus_dev->bus->self; 602 orig_interrupt = interrupt;
596 bus_pcp = bus_dev->sysdata; 603
604 hi = pregs->phys_hi & imask.phys_hi;
605 mid = pregs->phys_mid & imask.phys_mid;
606 lo = pregs->phys_lo & imask.phys_lo;
607 irq = interrupt & imask.interrupt;
608
609 for (i = 0; i < num_imap; i++) {
610 if (imap[i].phys_hi == hi &&
611 imap[i].phys_mid == mid &&
612 imap[i].phys_lo == lo &&
613 imap[i].interrupt == irq) {
614 *cnode = imap[i].cnode;
615 interrupt = imap[i].cinterrupt;
597 } 616 }
617 }
598 618
599 pregs = regs_pcp->prom_regs; 619 printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n",
620 pbm->name, pci_name(toplevel_pdev),
621 pci_name(pbus), pci_name(pdev),
622 orig_interrupt, interrupt);
600 623
624no_intmap:
625 return interrupt;
626}
601 627
602 /* But if the PCI bridge has it's own interrupt map 628/* For each PCI bus on the way to the root:
603 * and mask properties, use that and the regs of the 629 * 1) If it has an interrupt-map property, apply it.
604 * PCI entity at the next level down on the path to the 630 * 2) Else, swivel the interrupt number based upon the PCI device number.
605 * device. 631 *
606 */ 632 * Return the "IRQ controller" node. If this is the PBM's device node,
607 plen = prom_getproperty(bus_pcp->prom_node, "interrupt-map", 633 * all interrupt translations are complete, else we should use that node's
608 (char *) &bridge_local_intmap[0], 634 * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt.
609 sizeof(bridge_local_intmap)); 635 */
610 if (plen != -1) { 636static unsigned int __init pci_intmap_match_to_root(struct pci_pbm_info *pbm,
611 intmap = &bridge_local_intmap[0]; 637 struct pci_dev *pdev,
612 num_intmap = plen / sizeof(struct linux_prom_pci_intmap); 638 unsigned int *interrupt)
613 plen = prom_getproperty(bus_pcp->prom_node, 639{
614 "interrupt-map-mask", 640 struct pci_dev *toplevel_pdev = pdev;
615 (char *) &bridge_local_intmask, 641 struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata;
616 sizeof(bridge_local_intmask)); 642 unsigned int cnode = toplevel_pcp->prom_node;
617 if (plen == -1) {
618 printk("pci_intmap_match: Warning! Bridge has intmap "
619 "but no intmask.\n");
620 printk("pci_intmap_match: Trying to recover.\n");
621 return 0;
622 }
623 643
624 intmask = &bridge_local_intmask; 644 while (pdev->bus->number != pbm->pci_first_busno) {
645 struct pci_dev *pbus = pdev->bus->self;
646 struct pcidev_cookie *pcp = pbus->sysdata;
647 int plen;
625 648
626 if (pdev->bus->self != bus_dev) 649 plen = prom_getproplen(pcp->prom_node, "interrupt-map");
627 map_slot = 1; 650 if (plen <= 0) {
651 *interrupt = pci_slot_swivel(pbm, toplevel_pdev,
652 pdev, *interrupt);
653 cnode = pcp->prom_node;
628 } else { 654 } else {
629 pregs = bus_pcp->prom_regs; 655 *interrupt = pci_apply_intmap(pbm, toplevel_pdev,
630 map_slot = 1; 656 pbus, pdev,
657 *interrupt, &cnode);
658
659 while (pcp->prom_node != cnode &&
660 pbus->bus->number != pbm->pci_first_busno) {
661 pbus = pbus->bus->self;
662 pcp = pbus->sysdata;
663 }
631 } 664 }
632 } 665 pdev = pbus;
633 666
634 if (map_slot) { 667 if (cnode == pbm->prom_node)
635 *interrupt = ((*interrupt 668 break;
636 - 1
637 + PCI_SLOT(pdev->devfn)) & 0x3) + 1;
638 } 669 }
639 670
640 hi = pregs->phys_hi & intmask->phys_hi; 671 return cnode;
641 mid = pregs->phys_mid & intmask->phys_mid; 672}
642 lo = pregs->phys_lo & intmask->phys_lo; 673
643 irq = *interrupt & intmask->interrupt; 674static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
644 675{
645 for (i = 0; i < num_intmap; i++) { 676 struct pcidev_cookie *dev_pcp = pdev->sysdata;
646 if (intmap[i].phys_hi == hi && 677 struct pci_pbm_info *pbm = dev_pcp->pbm;
647 intmap[i].phys_mid == mid && 678 struct linux_prom_pci_registers reg;
648 intmap[i].phys_lo == lo && 679 unsigned int hi, mid, lo, irq;
649 intmap[i].interrupt == irq) { 680 int i, cnode, plen;
650 *interrupt = intmap[i].cinterrupt; 681
651 printk("PCI-IRQ: Routing bus[%2x] slot[%2x] map[%d] to INO[%02x]\n", 682 cnode = pci_intmap_match_to_root(pbm, pdev, interrupt);
652 pdev->bus->number, PCI_SLOT(pdev->devfn), 683 if (cnode == pbm->prom_node)
653 map_slot, *interrupt); 684 goto success;
654 return 1; 685
655 } 686 plen = prom_getproperty(cnode, "reg", (char *) &reg, sizeof(reg));
687 if (plen <= 0 ||
688 (plen % sizeof(struct linux_prom_pci_registers)) != 0) {
689 printk("%s: OBP node %x reg property has bad len %d\n",
690 pbm->name, cnode, plen);
691 goto fail;
656 } 692 }
657 693
658 /* We will run this code even if pbm->num_pbm_intmap is zero, just so 694 hi = reg.phys_hi & pbm->pbm_intmask.phys_hi;
659 * we can apply the slot mapping to the PROM interrupt property value. 695 mid = reg.phys_mid & pbm->pbm_intmask.phys_mid;
660 * So do not spit out these warnings in that case. 696 lo = reg.phys_lo & pbm->pbm_intmask.phys_lo;
661 */ 697 irq = *interrupt & pbm->pbm_intmask.interrupt;
662 if (num_intmap != 0) { 698
663 /* Print it both to OBP console and kernel one so that if bootup 699 for (i = 0; i < pbm->num_pbm_intmap; i++) {
664 * hangs here the user has the information to report. 700 struct linux_prom_pci_intmap *intmap;
665 */ 701
666 prom_printf("pci_intmap_match: bus %02x, devfn %02x: ", 702 intmap = &pbm->pbm_intmap[i];
667 pdev->bus->number, pdev->devfn); 703
668 prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n", 704 if (intmap->phys_hi == hi &&
669 pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt); 705 intmap->phys_mid == mid &&
670 prom_printf("Please email this information to davem@redhat.com\n"); 706 intmap->phys_lo == lo &&
671 707 intmap->interrupt == irq) {
672 printk("pci_intmap_match: bus %02x, devfn %02x: ", 708 *interrupt = intmap->cinterrupt;
673 pdev->bus->number, pdev->devfn); 709 goto success;
674 printk("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n", 710 }
675 pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
676 printk("Please email this information to davem@redhat.com\n");
677 } 711 }
678 712
713fail:
679 return 0; 714 return 0;
715
716success:
717 printk("PCI-IRQ: Routing bus[%2x] slot[%2x] to INO[%02x]\n",
718 pdev->bus->number, PCI_SLOT(pdev->devfn),
719 *interrupt);
720 return 1;
680} 721}
681 722
682static void __init pdev_fixup_irq(struct pci_dev *pdev) 723static void __init pdev_fixup_irq(struct pci_dev *pdev)