OSDN Git Service

powerpc/pci: Refactor pci_dn
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Tue, 17 Mar 2015 05:15:02 +0000 (16:15 +1100)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 24 Mar 2015 02:15:49 +0000 (13:15 +1100)
Currently, the PCI config accessors are implemented based on device node.
Unfortunately, SRIOV VFs won't have the corresponding device nodes. pci_dn
will be used in replacement with device node for SRIOV VFs. So we have to
use pci_dn in PCI config accessors.

The patch refactors pci_dn in following aspects to make it ready to be used
in PCI config accessors as we do in subsequent patch:

   * pci_dn is organized as a hierarchy tree.  PCI device's pci_dn is
     put to the child list of pci_dn of its upstream bridge or PHB. VF's
     pci_dn will be put to the child list of pci_dn of PF's bridge.

   * For one particular PCI device (VF or not), its pci_dn can be
     found from pdev->dev.archdata.pci_data, PCI_DN(devnode), or
     parent's list.  The fast path (fetching pci_dn through PCI device
     instance) is populated during early fixup time.

[bhelgaas: changelog]
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/device.h
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/kernel/pci_dn.c

index 38faede..9f1371b 100644 (file)
@@ -8,6 +8,9 @@
 
 struct dma_map_ops;
 struct device_node;
+#ifdef CONFIG_PPC64
+struct pci_dn;
+#endif
 
 /*
  * Arch extensions to struct device.
@@ -34,6 +37,9 @@ struct dev_archdata {
 #ifdef CONFIG_SWIOTLB
        dma_addr_t              max_direct_dma_addr;
 #endif
+#ifdef CONFIG_PPC64
+       struct pci_dn           *pci_data;
+#endif
 #ifdef CONFIG_EEH
        struct eeh_dev          *edev;
 #endif
index 546d036..706710b 100644 (file)
@@ -89,6 +89,7 @@ struct pci_controller {
 
 #ifdef CONFIG_PPC64
        unsigned long buid;
+       struct pci_dn *pci_data;
 #endif /* CONFIG_PPC64 */
 
        void *private_data;
@@ -154,9 +155,12 @@ static inline int isa_vaddr_is_ioport(void __iomem *address)
 struct iommu_table;
 
 struct pci_dn {
+       int     flags;
+
        int     busno;                  /* pci bus number */
        int     devfn;                  /* pci device and function number */
 
+       struct  pci_dn *parent;
        struct  pci_controller *phb;    /* for pci devices */
        struct  iommu_table *iommu_table;       /* for phb's or bridges */
        struct  device_node *node;      /* back-pointer to the device_node */
@@ -171,14 +175,17 @@ struct pci_dn {
 #ifdef CONFIG_PPC_POWERNV
        int     pe_number;
 #endif
+       struct list_head child_list;
+       struct list_head list;
 };
 
 /* Get the pointer to a device_node's pci_dn */
 #define PCI_DN(dn)     ((struct pci_dn *) (dn)->data)
 
+extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
+                                          int devfn);
 extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev);
-
-extern void * update_dn_pci_info(struct device_node *dn, void *data);
+extern void *update_dn_pci_info(struct device_node *dn, void *data);
 
 static inline int pci_device_from_OF_node(struct device_node *np,
                                          u8 *bus, u8 *devfn)
index 83df307..0ab2dad 100644 (file)
 #include <asm/ppc-pci.h>
 #include <asm/firmware.h>
 
+/*
+ * The function is used to find the firmware data of one
+ * specific PCI device, which is attached to the indicated
+ * PCI bus. For VFs, their firmware data is linked to that
+ * one of PF's bridge. For other devices, their firmware
+ * data is linked to that of their bridge.
+ */
+static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus)
+{
+       struct pci_bus *pbus;
+       struct device_node *dn;
+       struct pci_dn *pdn;
+
+       /*
+        * We probably have virtual bus which doesn't
+        * have associated bridge.
+        */
+       pbus = bus;
+       while (pbus) {
+               if (pci_is_root_bus(pbus) || pbus->self)
+                       break;
+
+               pbus = pbus->parent;
+       }
+
+       /*
+        * Except virtual bus, all PCI buses should
+        * have device nodes.
+        */
+       dn = pci_bus_to_OF_node(pbus);
+       pdn = dn ? PCI_DN(dn) : NULL;
+
+       return pdn;
+}
+
+struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
+                                   int devfn)
+{
+       struct device_node *dn = NULL;
+       struct pci_dn *parent, *pdn;
+       struct pci_dev *pdev = NULL;
+
+       /* Fast path: fetch from PCI device */
+       list_for_each_entry(pdev, &bus->devices, bus_list) {
+               if (pdev->devfn == devfn) {
+                       if (pdev->dev.archdata.pci_data)
+                               return pdev->dev.archdata.pci_data;
+
+                       dn = pci_device_to_OF_node(pdev);
+                       break;
+               }
+       }
+
+       /* Fast path: fetch from device node */
+       pdn = dn ? PCI_DN(dn) : NULL;
+       if (pdn)
+               return pdn;
+
+       /* Slow path: fetch from firmware data hierarchy */
+       parent = pci_bus_to_pdn(bus);
+       if (!parent)
+               return NULL;
+
+       list_for_each_entry(pdn, &parent->child_list, list) {
+               if (pdn->busno == bus->number &&
+                    pdn->devfn == devfn)
+                        return pdn;
+        }
+
+       return NULL;
+}
+
 struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
 {
-       struct device_node *dn = pci_device_to_OF_node(pdev);
-       if (!dn)
+       struct device_node *dn;
+       struct pci_dn *parent, *pdn;
+
+       /* Search device directly */
+       if (pdev->dev.archdata.pci_data)
+               return pdev->dev.archdata.pci_data;
+
+       /* Check device node */
+       dn = pci_device_to_OF_node(pdev);
+       pdn = dn ? PCI_DN(dn) : NULL;
+       if (pdn)
+               return pdn;
+
+       /*
+        * VFs don't have device nodes. We hook their
+        * firmware data to PF's bridge.
+        */
+       parent = pci_bus_to_pdn(pdev->bus);
+       if (!parent)
                return NULL;
-       return PCI_DN(dn);
+
+       list_for_each_entry(pdn, &parent->child_list, list) {
+               if (pdn->busno == pdev->bus->number &&
+                   pdn->devfn == pdev->devfn)
+                       return pdn;
+       }
+
+       return NULL;
 }
 
 /*
@@ -49,6 +145,7 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
        struct pci_controller *phb = data;
        const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
        const __be32 *regs;
+       struct device_node *parent;
        struct pci_dn *pdn;
 
        pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL);
@@ -70,6 +167,15 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
        }
 
        pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
+
+       /* Attach to parent node */
+       INIT_LIST_HEAD(&pdn->child_list);
+       INIT_LIST_HEAD(&pdn->list);
+       parent = of_get_parent(dn);
+       pdn->parent = parent ? PCI_DN(parent) : NULL;
+       if (pdn->parent)
+               list_add_tail(&pdn->list, &pdn->parent->child_list);
+
        return NULL;
 }
 
@@ -147,8 +253,11 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)
        /* PHB nodes themselves must not match */
        update_dn_pci_info(dn, phb);
        pdn = dn->data;
-       if (pdn)
+       if (pdn) {
                pdn->devfn = pdn->busno = -1;
+               pdn->phb = phb;
+               phb->pci_data = pdn;
+       }
 
        /* Update dn->phb ptrs for new phb and children devices */
        traverse_pci_devices(dn, update_dn_pci_info, phb);
@@ -171,3 +280,16 @@ void __init pci_devs_phb_init(void)
        list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
                pci_devs_phb_init_dynamic(phb);
 }
+
+static void pci_dev_pdn_setup(struct pci_dev *pdev)
+{
+       struct pci_dn *pdn;
+
+       if (pdev->dev.archdata.pci_data)
+               return;
+
+       /* Setup the fast path */
+       pdn = pci_get_pdn(pdev);
+       pdev->dev.archdata.pci_data = pdn;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup);