diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r-- | arch/sparc64/kernel/pci_common.c | 291 |
1 files changed, 6 insertions, 285 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index b06a2955bf5f..7a59cc72c844 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c | |||
@@ -10,12 +10,10 @@ | |||
10 | 10 | ||
11 | #include <asm/pbm.h> | 11 | #include <asm/pbm.h> |
12 | #include <asm/prom.h> | 12 | #include <asm/prom.h> |
13 | #include <asm/of_device.h> | ||
13 | 14 | ||
14 | #include "pci_impl.h" | 15 | #include "pci_impl.h" |
15 | 16 | ||
16 | /* Pass "pci=irq_verbose" on the kernel command line to enable this. */ | ||
17 | int pci_irq_verbose; | ||
18 | |||
19 | /* Fix self device of BUS and hook it into BUS->self. | 17 | /* Fix self device of BUS and hook it into BUS->self. |
20 | * The pci_scan_bus does not do this for the host bridge. | 18 | * The pci_scan_bus does not do this for the host bridge. |
21 | */ | 19 | */ |
@@ -169,6 +167,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, | |||
169 | } | 167 | } |
170 | pcp->pbm = pbm; | 168 | pcp->pbm = pbm; |
171 | pcp->prom_node = dp; | 169 | pcp->prom_node = dp; |
170 | pcp->op = of_find_device_by_node(dp); | ||
172 | memcpy(pcp->prom_regs, pregs, | 171 | memcpy(pcp->prom_regs, pregs, |
173 | nregs * sizeof(struct linux_prom_pci_registers)); | 172 | nregs * sizeof(struct linux_prom_pci_registers)); |
174 | pcp->num_prom_regs = nregs; | 173 | pcp->num_prom_regs = nregs; |
@@ -549,296 +548,18 @@ void __init pci_assign_unassigned(struct pci_pbm_info *pbm, | |||
549 | pci_assign_unassigned(pbm, bus); | 548 | pci_assign_unassigned(pbm, bus); |
550 | } | 549 | } |
551 | 550 | ||
552 | static inline unsigned int pci_slot_swivel(struct pci_pbm_info *pbm, | ||
553 | struct pci_dev *toplevel_pdev, | ||
554 | struct pci_dev *pdev, | ||
555 | unsigned int interrupt) | ||
556 | { | ||
557 | unsigned int ret; | ||
558 | |||
559 | if (unlikely(interrupt < 1 || interrupt > 4)) { | ||
560 | printk("%s: Device %s interrupt value of %u is strange.\n", | ||
561 | pbm->name, pci_name(pdev), interrupt); | ||
562 | return interrupt; | ||
563 | } | ||
564 | |||
565 | ret = ((interrupt - 1 + (PCI_SLOT(pdev->devfn) & 3)) & 3) + 1; | ||
566 | |||
567 | if (pci_irq_verbose) | ||
568 | printk("%s: %s IRQ Swivel %s [%x:%x] -> [%x]\n", | ||
569 | pbm->name, pci_name(toplevel_pdev), pci_name(pdev), | ||
570 | interrupt, PCI_SLOT(pdev->devfn), ret); | ||
571 | |||
572 | return ret; | ||
573 | } | ||
574 | |||
575 | static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm, | ||
576 | struct pci_dev *toplevel_pdev, | ||
577 | struct pci_dev *pbus, | ||
578 | struct pci_dev *pdev, | ||
579 | unsigned int interrupt, | ||
580 | struct device_node **cnode) | ||
581 | { | ||
582 | struct linux_prom_pci_intmap *imap; | ||
583 | struct linux_prom_pci_intmask *imask; | ||
584 | struct pcidev_cookie *pbus_pcp = pbus->sysdata; | ||
585 | struct pcidev_cookie *pdev_pcp = pdev->sysdata; | ||
586 | struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs; | ||
587 | struct property *prop; | ||
588 | int plen, num_imap, i; | ||
589 | unsigned int hi, mid, lo, irq, orig_interrupt; | ||
590 | |||
591 | *cnode = pbus_pcp->prom_node; | ||
592 | |||
593 | prop = of_find_property(pbus_pcp->prom_node, "interrupt-map", &plen); | ||
594 | if (!prop || | ||
595 | (plen % sizeof(struct linux_prom_pci_intmap)) != 0) { | ||
596 | printk("%s: Device %s interrupt-map has bad len %d\n", | ||
597 | pbm->name, pci_name(pbus), plen); | ||
598 | goto no_intmap; | ||
599 | } | ||
600 | imap = prop->value; | ||
601 | num_imap = plen / sizeof(struct linux_prom_pci_intmap); | ||
602 | |||
603 | prop = of_find_property(pbus_pcp->prom_node, "interrupt-map-mask", &plen); | ||
604 | if (!prop || | ||
605 | (plen % sizeof(struct linux_prom_pci_intmask)) != 0) { | ||
606 | printk("%s: Device %s interrupt-map-mask has bad len %d\n", | ||
607 | pbm->name, pci_name(pbus), plen); | ||
608 | goto no_intmap; | ||
609 | } | ||
610 | imask = prop->value; | ||
611 | |||
612 | orig_interrupt = interrupt; | ||
613 | |||
614 | hi = pregs->phys_hi & imask->phys_hi; | ||
615 | mid = pregs->phys_mid & imask->phys_mid; | ||
616 | lo = pregs->phys_lo & imask->phys_lo; | ||
617 | irq = interrupt & imask->interrupt; | ||
618 | |||
619 | for (i = 0; i < num_imap; i++) { | ||
620 | if (imap[i].phys_hi == hi && | ||
621 | imap[i].phys_mid == mid && | ||
622 | imap[i].phys_lo == lo && | ||
623 | imap[i].interrupt == irq) { | ||
624 | *cnode = of_find_node_by_phandle(imap[i].cnode); | ||
625 | interrupt = imap[i].cinterrupt; | ||
626 | } | ||
627 | } | ||
628 | |||
629 | if (pci_irq_verbose) | ||
630 | printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n", | ||
631 | pbm->name, pci_name(toplevel_pdev), | ||
632 | pci_name(pbus), pci_name(pdev), | ||
633 | orig_interrupt, interrupt); | ||
634 | |||
635 | no_intmap: | ||
636 | return interrupt; | ||
637 | } | ||
638 | |||
639 | /* For each PCI bus on the way to the root: | ||
640 | * 1) If it has an interrupt-map property, apply it. | ||
641 | * 2) Else, swivel the interrupt number based upon the PCI device number. | ||
642 | * | ||
643 | * Return the "IRQ controller" node. If this is the PBM's device node, | ||
644 | * all interrupt translations are complete, else we should use that node's | ||
645 | * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt. | ||
646 | */ | ||
647 | static struct device_node * __init | ||
648 | pci_intmap_match_to_root(struct pci_pbm_info *pbm, | ||
649 | struct pci_dev *pdev, | ||
650 | unsigned int *interrupt) | ||
651 | { | ||
652 | struct pci_dev *toplevel_pdev = pdev; | ||
653 | struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata; | ||
654 | struct device_node *cnode = toplevel_pcp->prom_node; | ||
655 | |||
656 | while (pdev->bus->number != pbm->pci_first_busno) { | ||
657 | struct pci_dev *pbus = pdev->bus->self; | ||
658 | struct pcidev_cookie *pcp = pbus->sysdata; | ||
659 | struct property *prop; | ||
660 | |||
661 | prop = of_find_property(pcp->prom_node, "interrupt-map", NULL); | ||
662 | if (!prop) { | ||
663 | *interrupt = pci_slot_swivel(pbm, toplevel_pdev, | ||
664 | pdev, *interrupt); | ||
665 | cnode = pcp->prom_node; | ||
666 | } else { | ||
667 | *interrupt = pci_apply_intmap(pbm, toplevel_pdev, | ||
668 | pbus, pdev, | ||
669 | *interrupt, &cnode); | ||
670 | |||
671 | while (pcp->prom_node != cnode && | ||
672 | pbus->bus->number != pbm->pci_first_busno) { | ||
673 | pbus = pbus->bus->self; | ||
674 | pcp = pbus->sysdata; | ||
675 | } | ||
676 | } | ||
677 | pdev = pbus; | ||
678 | |||
679 | if (cnode == pbm->prom_node) | ||
680 | break; | ||
681 | } | ||
682 | |||
683 | return cnode; | ||
684 | } | ||
685 | |||
686 | static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) | ||
687 | { | ||
688 | struct pcidev_cookie *dev_pcp = pdev->sysdata; | ||
689 | struct pci_pbm_info *pbm = dev_pcp->pbm; | ||
690 | struct linux_prom_pci_registers *reg; | ||
691 | struct device_node *cnode; | ||
692 | struct property *prop; | ||
693 | unsigned int hi, mid, lo, irq; | ||
694 | int i, plen; | ||
695 | |||
696 | cnode = pci_intmap_match_to_root(pbm, pdev, interrupt); | ||
697 | if (cnode == pbm->prom_node) | ||
698 | goto success; | ||
699 | |||
700 | prop = of_find_property(cnode, "reg", &plen); | ||
701 | if (!prop || | ||
702 | (plen % sizeof(struct linux_prom_pci_registers)) != 0) { | ||
703 | printk("%s: OBP node %s reg property has bad len %d\n", | ||
704 | pbm->name, cnode->full_name, plen); | ||
705 | goto fail; | ||
706 | } | ||
707 | reg = prop->value; | ||
708 | |||
709 | hi = reg[0].phys_hi & pbm->pbm_intmask->phys_hi; | ||
710 | mid = reg[0].phys_mid & pbm->pbm_intmask->phys_mid; | ||
711 | lo = reg[0].phys_lo & pbm->pbm_intmask->phys_lo; | ||
712 | irq = *interrupt & pbm->pbm_intmask->interrupt; | ||
713 | |||
714 | for (i = 0; i < pbm->num_pbm_intmap; i++) { | ||
715 | struct linux_prom_pci_intmap *intmap; | ||
716 | |||
717 | intmap = &pbm->pbm_intmap[i]; | ||
718 | |||
719 | if (intmap->phys_hi == hi && | ||
720 | intmap->phys_mid == mid && | ||
721 | intmap->phys_lo == lo && | ||
722 | intmap->interrupt == irq) { | ||
723 | *interrupt = intmap->cinterrupt; | ||
724 | goto success; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | fail: | ||
729 | return 0; | ||
730 | |||
731 | success: | ||
732 | if (pci_irq_verbose) | ||
733 | printk("%s: Routing bus[%2x] slot[%2x] to INO[%02x]\n", | ||
734 | pbm->name, | ||
735 | pdev->bus->number, PCI_SLOT(pdev->devfn), | ||
736 | *interrupt); | ||
737 | return 1; | ||
738 | } | ||
739 | |||
740 | static void __init pdev_fixup_irq(struct pci_dev *pdev) | 551 | static void __init pdev_fixup_irq(struct pci_dev *pdev) |
741 | { | 552 | { |
742 | struct pcidev_cookie *pcp = pdev->sysdata; | 553 | struct pcidev_cookie *pcp = pdev->sysdata; |
743 | struct pci_pbm_info *pbm = pcp->pbm; | 554 | struct of_device *op = pcp->op; |
744 | struct pci_controller_info *p = pbm->parent; | ||
745 | unsigned int portid = pbm->portid; | ||
746 | unsigned int prom_irq; | ||
747 | struct device_node *dp = pcp->prom_node; | ||
748 | struct property *prop; | ||
749 | |||
750 | /* If this is an empty EBUS device, sometimes OBP fails to | ||
751 | * give it a valid fully specified interrupts property. | ||
752 | * The EBUS hooked up to SunHME on PCI I/O boards of | ||
753 | * Ex000 systems is one such case. | ||
754 | * | ||
755 | * The interrupt is not important so just ignore it. | ||
756 | */ | ||
757 | if (pdev->vendor == PCI_VENDOR_ID_SUN && | ||
758 | pdev->device == PCI_DEVICE_ID_SUN_EBUS && | ||
759 | !dp->child) { | ||
760 | pdev->irq = 0; | ||
761 | return; | ||
762 | } | ||
763 | 555 | ||
764 | prop = of_find_property(dp, "interrupts", NULL); | 556 | if (op->irqs[0] == 0xffffffff) { |
765 | if (!prop) { | 557 | pdev->irq = PCI_IRQ_NONE; |
766 | pdev->irq = 0; | ||
767 | return; | 558 | return; |
768 | } | 559 | } |
769 | prom_irq = *(unsigned int *) prop->value; | ||
770 | |||
771 | if (tlb_type != hypervisor) { | ||
772 | /* Fully specified already? */ | ||
773 | if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) { | ||
774 | pdev->irq = p->irq_build(pbm, pdev, prom_irq); | ||
775 | goto have_irq; | ||
776 | } | ||
777 | |||
778 | /* An onboard device? (bit 5 set) */ | ||
779 | if ((prom_irq & PCI_IRQ_INO) & 0x20) { | ||
780 | pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq)); | ||
781 | goto have_irq; | ||
782 | } | ||
783 | } | ||
784 | |||
785 | /* Can we find a matching entry in the interrupt-map? */ | ||
786 | if (pci_intmap_match(pdev, &prom_irq)) { | ||
787 | pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq); | ||
788 | goto have_irq; | ||
789 | } | ||
790 | |||
791 | /* Ok, we have to do it the hard way. */ | ||
792 | { | ||
793 | unsigned int bus, slot, line; | ||
794 | |||
795 | bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0; | ||
796 | |||
797 | /* If we have a legal interrupt property, use it as | ||
798 | * the IRQ line. | ||
799 | */ | ||
800 | if (prom_irq > 0 && prom_irq < 5) { | ||
801 | line = ((prom_irq - 1) & 3); | ||
802 | } else { | ||
803 | u8 pci_irq_line; | ||
804 | 560 | ||
805 | /* Else just directly consult PCI config space. */ | 561 | pdev->irq = op->irqs[0]; |
806 | pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line); | ||
807 | line = ((pci_irq_line - 1) & 3); | ||
808 | } | ||
809 | |||
810 | /* Now figure out the slot. | ||
811 | * | ||
812 | * Basically, device number zero on the top-level bus is | ||
813 | * always the PCI host controller. Slot 0 is then device 1. | ||
814 | * PBM A supports two external slots (0 and 1), and PBM B | ||
815 | * supports 4 external slots (0, 1, 2, and 3). On-board PCI | ||
816 | * devices are wired to device numbers outside of these | ||
817 | * ranges. -DaveM | ||
818 | */ | ||
819 | if (pdev->bus->number == pbm->pci_first_busno) { | ||
820 | slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot; | ||
821 | } else { | ||
822 | struct pci_dev *bus_dev; | ||
823 | |||
824 | /* Underneath a bridge, use slot number of parent | ||
825 | * bridge which is closest to the PBM. | ||
826 | */ | ||
827 | bus_dev = pdev->bus->self; | ||
828 | while (bus_dev->bus && | ||
829 | bus_dev->bus->number != pbm->pci_first_busno) | ||
830 | bus_dev = bus_dev->bus->self; | ||
831 | |||
832 | slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot; | ||
833 | } | ||
834 | slot = slot << 2; | ||
835 | |||
836 | pdev->irq = p->irq_build(pbm, pdev, | ||
837 | ((portid << 6) & PCI_IRQ_IGN) | | ||
838 | (bus | slot | line)); | ||
839 | } | ||
840 | 562 | ||
841 | have_irq: | ||
842 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, | 563 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, |
843 | pdev->irq & PCI_IRQ_INO); | 564 | pdev->irq & PCI_IRQ_INO); |
844 | } | 565 | } |