Merge tag 'dm-4.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
[firefly-linux-kernel-4.4.55.git] / drivers / pci / iov.c
index a8752c2c2b53f845336bf0ef7c688318250a07ff..ee0ebff103a412bc3db69c05f0a30ecc1872be48 100644 (file)
 
 #define VIRTFN_ID_LEN  16
 
-static inline u8 virtfn_bus(struct pci_dev *dev, int id)
+int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)
 {
+       if (!dev->is_physfn)
+               return -EINVAL;
        return dev->bus->number + ((dev->devfn + dev->sriov->offset +
-                                   dev->sriov->stride * id) >> 8);
+                                   dev->sriov->stride * vf_id) >> 8);
 }
 
-static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
+int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)
 {
+       if (!dev->is_physfn)
+               return -EINVAL;
        return (dev->devfn + dev->sriov->offset +
-               dev->sriov->stride * id) & 0xff;
+               dev->sriov->stride * vf_id) & 0xff;
 }
 
 /*
@@ -46,6 +50,30 @@ static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
        pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride);
 }
 
+/*
+ * The PF consumes one bus number.  NumVFs, First VF Offset, and VF Stride
+ * determine how many additional bus numbers will be consumed by VFs.
+ *
+ * Iterate over all valid NumVFs and calculate the maximum number of bus
+ * numbers that could ever be required.
+ */
+static inline u8 virtfn_max_buses(struct pci_dev *dev)
+{
+       struct pci_sriov *iov = dev->sriov;
+       int nr_virtfn;
+       u8 max = 0;
+       int busnr;
+
+       for (nr_virtfn = 1; nr_virtfn <= iov->total_VFs; nr_virtfn++) {
+               pci_iov_set_numvfs(dev, nr_virtfn);
+               busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
+               if (busnr > max)
+                       max = busnr;
+       }
+
+       return max;
+}
+
 static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
 {
        struct pci_bus *child;
@@ -92,7 +120,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
        struct pci_bus *bus;
 
        mutex_lock(&iov->dev->sriov->lock);
-       bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
+       bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
        if (!bus)
                goto failed;
 
@@ -100,7 +128,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
        if (!virtfn)
                goto failed0;
 
-       virtfn->devfn = virtfn_devfn(dev, id);
+       virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
        virtfn->vendor = dev->vendor;
        pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
        pci_setup_device(virtfn);
@@ -162,8 +190,8 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
        struct pci_sriov *iov = dev->sriov;
 
        virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
-                                            virtfn_bus(dev, id),
-                                            virtfn_devfn(dev, id));
+                                            pci_iov_virtfn_bus(dev, id),
+                                            pci_iov_virtfn_devfn(dev, id));
        if (!virtfn)
                return;
 
@@ -192,6 +220,11 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
        pci_dev_put(dev);
 }
 
+int __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
+{
+       return 0;
+}
+
 static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
 {
        int rc;
@@ -202,7 +235,8 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
        struct pci_dev *pdev;
        struct pci_sriov *iov = dev->sriov;
        int bars = 0;
-       u8 bus;
+       int bus;
+       int retval;
 
        if (!nr_virtfn)
                return 0;
@@ -239,7 +273,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
        iov->offset = offset;
        iov->stride = stride;
 
-       bus = virtfn_bus(dev, nr_virtfn - 1);
+       bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
        if (bus > dev->bus->busn_res.end) {
                dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
                        nr_virtfn, bus, &dev->bus->busn_res);
@@ -279,6 +313,12 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
        if (nr_virtfn < initial)
                initial = nr_virtfn;
 
+       if ((retval = pcibios_sriov_enable(dev, initial))) {
+               dev_err(&dev->dev, "failure %d from pcibios_sriov_enable()\n",
+                       retval);
+               return retval;
+       }
+
        for (i = 0; i < initial; i++) {
                rc = virtfn_add(dev, i, 0);
                if (rc)
@@ -307,6 +347,11 @@ failed:
        return rc;
 }
 
+int __weak pcibios_sriov_disable(struct pci_dev *pdev)
+{
+       return 0;
+}
+
 static void sriov_disable(struct pci_dev *dev)
 {
        int i;
@@ -318,6 +363,8 @@ static void sriov_disable(struct pci_dev *dev)
        for (i = 0; i < iov->num_VFs; i++)
                virtfn_remove(dev, i, 0);
 
+       pcibios_sriov_disable(dev);
+
        iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
        pci_cfg_access_lock(dev);
        pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
@@ -427,6 +474,7 @@ found:
 
        dev->sriov = iov;
        dev->is_physfn = 1;
+       iov->max_VF_buses = virtfn_max_buses(dev);
 
        return 0;
 
@@ -521,6 +569,12 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno)
                4 * (resno - PCI_IOV_RESOURCES);
 }
 
+resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
+                                                     int resno)
+{
+       return pci_iov_resource_size(dev, resno);
+}
+
 /**
  * pci_sriov_resource_alignment - get resource alignment for VF BAR
  * @dev: the PCI device
@@ -533,7 +587,7 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno)
  */
 resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
 {
-       return pci_iov_resource_size(dev, resno);
+       return pcibios_iov_resource_alignment(dev, resno);
 }
 
 /**
@@ -556,15 +610,13 @@ void pci_restore_iov_state(struct pci_dev *dev)
 int pci_iov_bus_range(struct pci_bus *bus)
 {
        int max = 0;
-       u8 busnr;
        struct pci_dev *dev;
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                if (!dev->is_physfn)
                        continue;
-               busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
-               if (busnr > max)
-                       max = busnr;
+               if (dev->sriov->max_VF_buses > max)
+                       max = dev->sriov->max_VF_buses;
        }
 
        return max ? max - bus->number : 0;