diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_common.c')
-rw-r--r-- | arch/sparc64/kernel/pci_common.c | 301 |
1 files changed, 174 insertions, 127 deletions
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 58310aacea28..33dedb1aacd4 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c | |||
@@ -39,6 +39,8 @@ static int __init find_device_prom_node(struct pci_pbm_info *pbm, | |||
39 | { | 39 | { |
40 | int node; | 40 | int node; |
41 | 41 | ||
42 | *nregs = 0; | ||
43 | |||
42 | /* | 44 | /* |
43 | * Return the PBM's PROM node in case we are it's PCI device, | 45 | * Return the PBM's PROM node in case we are it's PCI device, |
44 | * as the PBM's reg property is different to standard PCI reg | 46 | * as the PBM's reg property is different to standard PCI reg |
@@ -51,10 +53,8 @@ static int __init find_device_prom_node(struct pci_pbm_info *pbm, | |||
51 | pdev->device == PCI_DEVICE_ID_SUN_SCHIZO || | 53 | pdev->device == PCI_DEVICE_ID_SUN_SCHIZO || |
52 | pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO || | 54 | pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO || |
53 | pdev->device == PCI_DEVICE_ID_SUN_SABRE || | 55 | pdev->device == PCI_DEVICE_ID_SUN_SABRE || |
54 | pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) { | 56 | pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) |
55 | *nregs = 0; | ||
56 | return bus_prom_node; | 57 | return bus_prom_node; |
57 | } | ||
58 | 58 | ||
59 | node = prom_getchild(bus_prom_node); | 59 | node = prom_getchild(bus_prom_node); |
60 | while (node != 0) { | 60 | while (node != 0) { |
@@ -541,135 +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 | ||
544 | static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) | 544 | static 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 | 562 | |
565 | * of the next child device on the path to PDEV. | 563 | return ret; |
566 | * | 564 | } |
567 | * In detail the two cases are (note that the 'X' below is the | 565 | |
568 | * 'next child on the path to PDEV' mentioned above): | 566 | static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm, |
569 | * | 567 | struct pci_dev *toplevel_pdev, |
570 | * 1) PBM --> PCI bus lacking int{map,mask} --> X ... PDEV | 568 | struct pci_dev *pbus, |
571 | * | 569 | struct pci_dev *pdev, |
572 | * Here we use regs of 'PCI bus' device. | 570 | unsigned int interrupt, |
573 | * | 571 | unsigned int *cnode) |
574 | * 2) PBM --> PCI bus with int{map,mask} --> X ... PDEV | 572 | { |
575 | * | 573 | struct linux_prom_pci_intmap imap[PROM_PCIIMAP_MAX]; |
576 | * Here we use regs of 'X'. Note that X can be PDEV. | 574 | struct linux_prom_pci_intmask imask; |
577 | */ | 575 | struct pcidev_cookie *pbus_pcp = pbus->sysdata; |
578 | if (pdev->bus->number != pbm->pci_first_busno) { | 576 | struct pcidev_cookie *pdev_pcp = pdev->sysdata; |
579 | struct pcidev_cookie *bus_pcp, *regs_pcp; | 577 | struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs; |
580 | struct pci_dev *bus_dev, *regs_dev; | 578 | int plen, num_imap, i; |
581 | int plen; | 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 | } | ||
601 | |||
602 | orig_interrupt = interrupt; | ||
582 | 603 | ||
583 | bus_dev = pdev->bus->self; | 604 | hi = pregs->phys_hi & imask.phys_hi; |
584 | regs_dev = pdev; | 605 | mid = pregs->phys_mid & imask.phys_mid; |
606 | lo = pregs->phys_lo & imask.phys_lo; | ||
607 | irq = interrupt & imask.interrupt; | ||
585 | 608 | ||
586 | while (bus_dev->bus && | 609 | for (i = 0; i < num_imap; i++) { |
587 | bus_dev->bus->number != pbm->pci_first_busno) { | 610 | if (imap[i].phys_hi == hi && |
588 | regs_dev = bus_dev; | 611 | imap[i].phys_mid == mid && |
589 | bus_dev = bus_dev->bus->self; | 612 | imap[i].phys_lo == lo && |
613 | imap[i].interrupt == irq) { | ||
614 | *cnode = imap[i].cnode; | ||
615 | interrupt = imap[i].cinterrupt; | ||
590 | } | 616 | } |
617 | } | ||
591 | 618 | ||
592 | regs_pcp = regs_dev->sysdata; | 619 | printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n", |
593 | pregs = regs_pcp->prom_regs; | 620 | pbm->name, pci_name(toplevel_pdev), |
621 | pci_name(pbus), pci_name(pdev), | ||
622 | orig_interrupt, interrupt); | ||
594 | 623 | ||
595 | bus_pcp = bus_dev->sysdata; | 624 | no_intmap: |
625 | return interrupt; | ||
626 | } | ||
596 | 627 | ||
597 | /* But if the PCI bridge has it's own interrupt map | 628 | /* For each PCI bus on the way to the root: |
598 | * and mask properties, use that and the regs of the | 629 | * 1) If it has an interrupt-map property, apply it. |
599 | * 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. |
600 | * device. | 631 | * |
601 | */ | 632 | * Return the "IRQ controller" node. If this is the PBM's device node, |
602 | plen = prom_getproperty(bus_pcp->prom_node, "interrupt-map", | 633 | * all interrupt translations are complete, else we should use that node's |
603 | (char *) &bridge_local_intmap[0], | 634 | * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt. |
604 | sizeof(bridge_local_intmap)); | 635 | */ |
605 | if (plen != -1) { | 636 | static unsigned int __init pci_intmap_match_to_root(struct pci_pbm_info *pbm, |
606 | intmap = &bridge_local_intmap[0]; | 637 | struct pci_dev *pdev, |
607 | num_intmap = plen / sizeof(struct linux_prom_pci_intmap); | 638 | unsigned int *interrupt) |
608 | plen = prom_getproperty(bus_pcp->prom_node, | 639 | { |
609 | "interrupt-map-mask", | 640 | struct pci_dev *toplevel_pdev = pdev; |
610 | (char *) &bridge_local_intmask, | 641 | struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata; |
611 | sizeof(bridge_local_intmask)); | 642 | unsigned int cnode = toplevel_pcp->prom_node; |
612 | if (plen == -1) { | 643 | |
613 | printk("pci_intmap_match: Warning! Bridge has intmap " | 644 | while (pdev->bus->number != pbm->pci_first_busno) { |
614 | "but no intmask.\n"); | 645 | struct pci_dev *pbus = pdev->bus->self; |
615 | printk("pci_intmap_match: Trying to recover.\n"); | 646 | struct pcidev_cookie *pcp = pbus->sysdata; |
616 | return 0; | 647 | int plen; |
617 | } | ||
618 | 648 | ||
619 | if (pdev->bus->self != bus_dev) | 649 | plen = prom_getproplen(pcp->prom_node, "interrupt-map"); |
620 | map_slot = 1; | 650 | if (plen <= 0) { |
651 | *interrupt = pci_slot_swivel(pbm, toplevel_pdev, | ||
652 | pdev, *interrupt); | ||
653 | cnode = pcp->prom_node; | ||
621 | } else { | 654 | } else { |
622 | pregs = bus_pcp->prom_regs; | 655 | *interrupt = pci_apply_intmap(pbm, toplevel_pdev, |
623 | 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 | } | ||
624 | } | 664 | } |
625 | } | 665 | pdev = pbus; |
626 | 666 | ||
627 | if (map_slot) { | 667 | if (cnode == pbm->prom_node) |
628 | *interrupt = ((*interrupt | 668 | break; |
629 | - 1 | ||
630 | + PCI_SLOT(pdev->devfn)) & 0x3) + 1; | ||
631 | } | 669 | } |
632 | 670 | ||
633 | hi = pregs->phys_hi & intmask->phys_hi; | 671 | return cnode; |
634 | mid = pregs->phys_mid & intmask->phys_mid; | 672 | } |
635 | lo = pregs->phys_lo & intmask->phys_lo; | 673 | |
636 | irq = *interrupt & intmask->interrupt; | 674 | static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) |
637 | 675 | { | |
638 | for (i = 0; i < num_intmap; i++) { | 676 | struct pcidev_cookie *dev_pcp = pdev->sysdata; |
639 | if (intmap[i].phys_hi == hi && | 677 | struct pci_pbm_info *pbm = dev_pcp->pbm; |
640 | intmap[i].phys_mid == mid && | 678 | struct linux_prom_pci_registers reg[PROMREG_MAX]; |
641 | intmap[i].phys_lo == lo && | 679 | unsigned int hi, mid, lo, irq; |
642 | intmap[i].interrupt == irq) { | 680 | int i, cnode, plen; |
643 | *interrupt = intmap[i].cinterrupt; | 681 | |
644 | printk("PCI-IRQ: Routing bus[%2x] slot[%2x] map[%d] to INO[%02x]\n", | 682 | cnode = pci_intmap_match_to_root(pbm, pdev, interrupt); |
645 | pdev->bus->number, PCI_SLOT(pdev->devfn), | 683 | if (cnode == pbm->prom_node) |
646 | map_slot, *interrupt); | 684 | goto success; |
647 | return 1; | 685 | |
648 | } | 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; | ||
649 | } | 692 | } |
650 | 693 | ||
651 | /* We will run this code even if pbm->num_pbm_intmap is zero, just so | 694 | hi = reg[0].phys_hi & pbm->pbm_intmask.phys_hi; |
652 | * we can apply the slot mapping to the PROM interrupt property value. | 695 | mid = reg[0].phys_mid & pbm->pbm_intmask.phys_mid; |
653 | * So do not spit out these warnings in that case. | 696 | lo = reg[0].phys_lo & pbm->pbm_intmask.phys_lo; |
654 | */ | 697 | irq = *interrupt & pbm->pbm_intmask.interrupt; |
655 | if (num_intmap != 0) { | 698 | |
656 | /* Print it both to OBP console and kernel one so that if bootup | 699 | for (i = 0; i < pbm->num_pbm_intmap; i++) { |
657 | * hangs here the user has the information to report. | 700 | struct linux_prom_pci_intmap *intmap; |
658 | */ | 701 | |
659 | prom_printf("pci_intmap_match: bus %02x, devfn %02x: ", | 702 | intmap = &pbm->pbm_intmap[i]; |
660 | pdev->bus->number, pdev->devfn); | 703 | |
661 | prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n", | 704 | if (intmap->phys_hi == hi && |
662 | pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt); | 705 | intmap->phys_mid == mid && |
663 | prom_printf("Please email this information to davem@redhat.com\n"); | 706 | intmap->phys_lo == lo && |
664 | 707 | intmap->interrupt == irq) { | |
665 | printk("pci_intmap_match: bus %02x, devfn %02x: ", | 708 | *interrupt = intmap->cinterrupt; |
666 | pdev->bus->number, pdev->devfn); | 709 | goto success; |
667 | printk("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n", | 710 | } |
668 | pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt); | ||
669 | printk("Please email this information to davem@redhat.com\n"); | ||
670 | } | 711 | } |
671 | 712 | ||
713 | fail: | ||
672 | return 0; | 714 | return 0; |
715 | |||
716 | success: | ||
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; | ||
673 | } | 721 | } |
674 | 722 | ||
675 | static void __init pdev_fixup_irq(struct pci_dev *pdev) | 723 | static void __init pdev_fixup_irq(struct pci_dev *pdev) |
@@ -703,16 +751,18 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev) | |||
703 | return; | 751 | return; |
704 | } | 752 | } |
705 | 753 | ||
706 | /* Fully specified already? */ | 754 | if (tlb_type != hypervisor) { |
707 | if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) { | 755 | /* Fully specified already? */ |
708 | pdev->irq = p->irq_build(pbm, pdev, prom_irq); | 756 | if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) { |
709 | goto have_irq; | 757 | pdev->irq = p->irq_build(pbm, pdev, prom_irq); |
710 | } | 758 | goto have_irq; |
759 | } | ||
711 | 760 | ||
712 | /* An onboard device? (bit 5 set) */ | 761 | /* An onboard device? (bit 5 set) */ |
713 | if ((prom_irq & PCI_IRQ_INO) & 0x20) { | 762 | if ((prom_irq & PCI_IRQ_INO) & 0x20) { |
714 | pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq)); | 763 | pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq)); |
715 | goto have_irq; | 764 | goto have_irq; |
765 | } | ||
716 | } | 766 | } |
717 | 767 | ||
718 | /* Can we find a matching entry in the interrupt-map? */ | 768 | /* Can we find a matching entry in the interrupt-map? */ |
@@ -927,33 +977,30 @@ void pci_register_legacy_regions(struct resource *io_res, | |||
927 | struct resource *p; | 977 | struct resource *p; |
928 | 978 | ||
929 | /* VGA Video RAM. */ | 979 | /* VGA Video RAM. */ |
930 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 980 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
931 | if (!p) | 981 | if (!p) |
932 | return; | 982 | return; |
933 | 983 | ||
934 | memset(p, 0, sizeof(*p)); | ||
935 | p->name = "Video RAM area"; | 984 | p->name = "Video RAM area"; |
936 | p->start = mem_res->start + 0xa0000UL; | 985 | p->start = mem_res->start + 0xa0000UL; |
937 | p->end = p->start + 0x1ffffUL; | 986 | p->end = p->start + 0x1ffffUL; |
938 | p->flags = IORESOURCE_BUSY; | 987 | p->flags = IORESOURCE_BUSY; |
939 | request_resource(mem_res, p); | 988 | request_resource(mem_res, p); |
940 | 989 | ||
941 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 990 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
942 | if (!p) | 991 | if (!p) |
943 | return; | 992 | return; |
944 | 993 | ||
945 | memset(p, 0, sizeof(*p)); | ||
946 | p->name = "System ROM"; | 994 | p->name = "System ROM"; |
947 | p->start = mem_res->start + 0xf0000UL; | 995 | p->start = mem_res->start + 0xf0000UL; |
948 | p->end = p->start + 0xffffUL; | 996 | p->end = p->start + 0xffffUL; |
949 | p->flags = IORESOURCE_BUSY; | 997 | p->flags = IORESOURCE_BUSY; |
950 | request_resource(mem_res, p); | 998 | request_resource(mem_res, p); |
951 | 999 | ||
952 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 1000 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
953 | if (!p) | 1001 | if (!p) |
954 | return; | 1002 | return; |
955 | 1003 | ||
956 | memset(p, 0, sizeof(*p)); | ||
957 | p->name = "Video ROM"; | 1004 | p->name = "Video ROM"; |
958 | p->start = mem_res->start + 0xc0000UL; | 1005 | p->start = mem_res->start + 0xc0000UL; |
959 | p->end = p->start + 0x7fffUL; | 1006 | p->end = p->start + 0x7fffUL; |