aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/pci_common.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2006-06-29 18:07:37 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-06-29 19:37:38 -0400
commit2b1e59787198e75fb2ffb3bb4fb247da1c55ac12 (patch)
tree96d74048849b310135e0c79f663b16c52186caa5 /arch/sparc64/kernel/pci_common.c
parentc3a8b85f5ac2c21f4ef75e87bfe55ee7a753ffcf (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/pci_common.c')
-rw-r--r--arch/sparc64/kernel/pci_common.c291
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. */
17int 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
552static 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
575static 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
635no_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 */
647static struct device_node * __init
648pci_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
686static 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
728fail:
729 return 0;
730
731success:
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
740static void __init pdev_fixup_irq(struct pci_dev *pdev) 551static 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
841have_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}