aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/pci_common.c
diff options
context:
space:
mode:
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)