Merge tag 'dm-4.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
[linux-2.6-microblaze.git] / arch / powerpc / kernel / pci_dn.c
index 65b9836..b3b4df9 100644 (file)
@@ -136,6 +136,135 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
        return NULL;
 }
 
+#ifdef CONFIG_PCI_IOV
+static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent,
+                                          struct pci_dev *pdev,
+                                          int busno, int devfn)
+{
+       struct pci_dn *pdn;
+
+       /* Except PHB, we always have the parent */
+       if (!parent)
+               return NULL;
+
+       pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
+       if (!pdn) {
+               dev_warn(&pdev->dev, "%s: Out of memory!\n", __func__);
+               return NULL;
+       }
+
+       pdn->phb = parent->phb;
+       pdn->parent = parent;
+       pdn->busno = busno;
+       pdn->devfn = devfn;
+#ifdef CONFIG_PPC_POWERNV
+       pdn->pe_number = IODA_INVALID_PE;
+#endif
+       INIT_LIST_HEAD(&pdn->child_list);
+       INIT_LIST_HEAD(&pdn->list);
+       list_add_tail(&pdn->list, &parent->child_list);
+
+       /*
+        * If we already have PCI device instance, lets
+        * bind them.
+        */
+       if (pdev)
+               pdev->dev.archdata.pci_data = pdn;
+
+       return pdn;
+}
+#endif
+
+struct pci_dn *add_dev_pci_data(struct pci_dev *pdev)
+{
+#ifdef CONFIG_PCI_IOV
+       struct pci_dn *parent, *pdn;
+       int i;
+
+       /* Only support IOV for now */
+       if (!pdev->is_physfn)
+               return pci_get_pdn(pdev);
+
+       /* Check if VFs have been populated */
+       pdn = pci_get_pdn(pdev);
+       if (!pdn || (pdn->flags & PCI_DN_FLAG_IOV_VF))
+               return NULL;
+
+       pdn->flags |= PCI_DN_FLAG_IOV_VF;
+       parent = pci_bus_to_pdn(pdev->bus);
+       if (!parent)
+               return NULL;
+
+       for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) {
+               pdn = add_one_dev_pci_data(parent, NULL,
+                                          pci_iov_virtfn_bus(pdev, i),
+                                          pci_iov_virtfn_devfn(pdev, i));
+               if (!pdn) {
+                       dev_warn(&pdev->dev, "%s: Cannot create firmware data for VF#%d\n",
+                                __func__, i);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_PCI_IOV */
+
+       return pci_get_pdn(pdev);
+}
+
+void remove_dev_pci_data(struct pci_dev *pdev)
+{
+#ifdef CONFIG_PCI_IOV
+       struct pci_dn *parent;
+       struct pci_dn *pdn, *tmp;
+       int i;
+
+       /*
+        * VF and VF PE are created/released dynamically, so we need to
+        * bind/unbind them.  Otherwise the VF and VF PE would be mismatched
+        * when re-enabling SR-IOV.
+        */
+       if (pdev->is_virtfn) {
+               pdn = pci_get_pdn(pdev);
+#ifdef CONFIG_PPC_POWERNV
+               pdn->pe_number = IODA_INVALID_PE;
+#endif
+               return;
+       }
+
+       /* Only support IOV PF for now */
+       if (!pdev->is_physfn)
+               return;
+
+       /* Check if VFs have been populated */
+       pdn = pci_get_pdn(pdev);
+       if (!pdn || !(pdn->flags & PCI_DN_FLAG_IOV_VF))
+               return;
+
+       pdn->flags &= ~PCI_DN_FLAG_IOV_VF;
+       parent = pci_bus_to_pdn(pdev->bus);
+       if (!parent)
+               return;
+
+       /*
+        * We might introduce flag to pci_dn in future
+        * so that we can release VF's firmware data in
+        * a batch mode.
+        */
+       for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) {
+               list_for_each_entry_safe(pdn, tmp,
+                       &parent->child_list, list) {
+                       if (pdn->busno != pci_iov_virtfn_bus(pdev, i) ||
+                           pdn->devfn != pci_iov_virtfn_devfn(pdev, i))
+                               continue;
+
+                       if (!list_empty(&pdn->list))
+                               list_del(&pdn->list);
+
+                       kfree(pdn);
+               }
+       }
+#endif /* CONFIG_PCI_IOV */
+}
+
 /*
  * Traverse_func that inits the PCI fields of the device node.
  * NOTE: this *must* be done before read/write config to the device.