diff options
| -rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 124 |
1 files changed, 118 insertions, 6 deletions
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index 2b7a1f316a93..0c0895202970 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c | |||
| @@ -599,18 +599,128 @@ struct pci_iommu_ops pci_sun4v_iommu_ops = { | |||
| 599 | 599 | ||
| 600 | /* SUN4V PCI configuration space accessors. */ | 600 | /* SUN4V PCI configuration space accessors. */ |
| 601 | 601 | ||
| 602 | static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func) | 602 | struct pdev_entry { |
| 603 | struct pdev_entry *next; | ||
| 604 | u32 devhandle; | ||
| 605 | unsigned int bus; | ||
| 606 | unsigned int device; | ||
| 607 | unsigned int func; | ||
| 608 | }; | ||
| 609 | |||
| 610 | #define PDEV_HTAB_SIZE 16 | ||
| 611 | #define PDEV_HTAB_MASK (PDEV_HTAB_SIZE - 1) | ||
| 612 | static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE]; | ||
| 613 | |||
| 614 | static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) | ||
| 603 | { | 615 | { |
| 604 | if (bus == pbm->pci_first_busno) { | 616 | unsigned int val; |
| 605 | if (device == 0 && func == 0) | 617 | |
| 606 | return 0; | 618 | val = (devhandle ^ (devhandle >> 4)); |
| 607 | return 1; | 619 | val ^= bus; |
| 620 | val ^= device; | ||
| 621 | val ^= func; | ||
| 622 | |||
| 623 | return val & PDEV_HTAB_MASK; | ||
| 624 | } | ||
| 625 | |||
| 626 | static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) | ||
| 627 | { | ||
| 628 | struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
| 629 | struct pdev_entry **slot; | ||
| 630 | |||
| 631 | if (!p) | ||
| 632 | return -ENOMEM; | ||
| 633 | |||
| 634 | slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; | ||
| 635 | p->next = *slot; | ||
| 636 | *slot = p; | ||
| 637 | |||
| 638 | p->devhandle = devhandle; | ||
| 639 | p->bus = bus; | ||
| 640 | p->device = device; | ||
| 641 | p->func = func; | ||
| 642 | |||
| 643 | return 0; | ||
| 644 | } | ||
| 645 | |||
| 646 | /* Recursively descend into the OBP device tree, rooted at toplevel_node, | ||
| 647 | * looking for a PCI device matching bus and devfn. | ||
| 648 | */ | ||
| 649 | static int obp_find(struct linux_prom_pci_registers *pregs, int toplevel_node, unsigned int bus, unsigned int devfn) | ||
| 650 | { | ||
| 651 | toplevel_node = prom_getchild(toplevel_node); | ||
| 652 | |||
| 653 | while (toplevel_node != 0) { | ||
| 654 | int ret = obp_find(pregs, toplevel_node, bus, devfn); | ||
| 655 | |||
| 656 | if (ret != 0) | ||
| 657 | return ret; | ||
| 658 | |||
| 659 | ret = prom_getproperty(toplevel_node, "reg", (char *) pregs, | ||
| 660 | sizeof(*pregs) * PROMREG_MAX); | ||
| 661 | if (ret == 0 || ret == -1) | ||
| 662 | goto next_sibling; | ||
| 663 | |||
| 664 | if (((pregs[0].phys_hi >> 16) & 0xff) == bus && | ||
| 665 | ((pregs[0].phys_hi >> 8) & 0xff) == devfn) | ||
| 666 | break; | ||
| 667 | |||
| 668 | next_sibling: | ||
| 669 | toplevel_node = prom_getsibling(toplevel_node); | ||
| 670 | } | ||
| 671 | |||
| 672 | return toplevel_node; | ||
| 673 | } | ||
| 674 | |||
| 675 | static int pdev_htab_populate(struct pci_pbm_info *pbm) | ||
| 676 | { | ||
| 677 | struct linux_prom_pci_registers pr[PROMREG_MAX]; | ||
| 678 | u32 devhandle = pbm->devhandle; | ||
| 679 | unsigned int bus; | ||
| 680 | |||
| 681 | for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) { | ||
| 682 | unsigned int devfn; | ||
| 683 | |||
| 684 | for (devfn = 0; devfn < 256; devfn++) { | ||
| 685 | unsigned int device = PCI_SLOT(devfn); | ||
| 686 | unsigned int func = PCI_FUNC(devfn); | ||
| 687 | |||
| 688 | if (obp_find(pr, pbm->prom_node, bus, devfn)) { | ||
| 689 | int err = pdev_htab_add(devhandle, bus, | ||
| 690 | device, func); | ||
| 691 | if (err) | ||
| 692 | return err; | ||
| 693 | } | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | return 0; | ||
| 698 | } | ||
| 699 | |||
| 700 | static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) | ||
| 701 | { | ||
| 702 | struct pdev_entry *p; | ||
| 703 | |||
| 704 | p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; | ||
| 705 | while (p) { | ||
| 706 | if (p->devhandle == devhandle && | ||
| 707 | p->bus == bus && | ||
| 708 | p->device == device && | ||
| 709 | p->func == func) | ||
| 710 | break; | ||
| 711 | |||
| 712 | p = p->next; | ||
| 608 | } | 713 | } |
| 609 | 714 | ||
| 715 | return p; | ||
| 716 | } | ||
| 717 | |||
| 718 | static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func) | ||
| 719 | { | ||
| 610 | if (bus < pbm->pci_first_busno || | 720 | if (bus < pbm->pci_first_busno || |
| 611 | bus > pbm->pci_last_busno) | 721 | bus > pbm->pci_last_busno) |
| 612 | return 1; | 722 | return 1; |
| 613 | return 0; | 723 | return pdev_find(pbm->devhandle, bus, device, func) == NULL; |
| 614 | } | 724 | } |
| 615 | 725 | ||
| 616 | static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | 726 | static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, |
| @@ -1063,6 +1173,8 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 | |||
| 1063 | 1173 | ||
| 1064 | pci_sun4v_get_bus_range(pbm); | 1174 | pci_sun4v_get_bus_range(pbm); |
| 1065 | pci_sun4v_iommu_init(pbm); | 1175 | pci_sun4v_iommu_init(pbm); |
| 1176 | |||
| 1177 | pdev_htab_populate(pbm); | ||
| 1066 | } | 1178 | } |
| 1067 | 1179 | ||
| 1068 | void sun4v_pci_init(int node, char *model_name) | 1180 | void sun4v_pci_init(int node, char *model_name) |
