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 d139f72..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.
@@ -246,6 +375,46 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre,
        return NULL;
 }
 
+static struct pci_dn *pci_dn_next_one(struct pci_dn *root,
+                                     struct pci_dn *pdn)
+{
+       struct list_head *next = pdn->child_list.next;
+
+       if (next != &pdn->child_list)
+               return list_entry(next, struct pci_dn, list);
+
+       while (1) {
+               if (pdn == root)
+                       return NULL;
+
+               next = pdn->list.next;
+               if (next != &pdn->parent->child_list)
+                       break;
+
+               pdn = pdn->parent;
+       }
+
+       return list_entry(next, struct pci_dn, list);
+}
+
+void *traverse_pci_dn(struct pci_dn *root,
+                     void *(*fn)(struct pci_dn *, void *),
+                     void *data)
+{
+       struct pci_dn *pdn = root;
+       void *ret;
+
+       /* Only scan the child nodes */
+       for (pdn = pci_dn_next_one(root, pdn); pdn;
+            pdn = pci_dn_next_one(root, pdn)) {
+               ret = fn(pdn, data);
+               if (ret)
+                       return ret;
+       }
+
+       return NULL;
+}
+
 /** 
  * pci_devs_phb_init_dynamic - setup pci devices under this PHB
  * phb: pci-to-host bridge (top-level bridge connecting to cpu)