|
@@ -0,0 +1,182 @@
|
|
|
+/*
|
|
|
+ * drivers/pci/iov.c
|
|
|
+ *
|
|
|
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
|
|
|
+ *
|
|
|
+ * PCI Express I/O Virtualization (IOV) support.
|
|
|
+ * Single Root IOV 1.0
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/pci.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
+#include <linux/string.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include "pci.h"
|
|
|
+
|
|
|
+
|
|
|
+static int sriov_init(struct pci_dev *dev, int pos)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int rc;
|
|
|
+ int nres;
|
|
|
+ u32 pgsz;
|
|
|
+ u16 ctrl, total, offset, stride;
|
|
|
+ struct pci_sriov *iov;
|
|
|
+ struct resource *res;
|
|
|
+ struct pci_dev *pdev;
|
|
|
+
|
|
|
+ if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
|
|
|
+ dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
|
|
|
+ if (ctrl & PCI_SRIOV_CTRL_VFE) {
|
|
|
+ pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
|
|
|
+ ssleep(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
|
|
|
+ if (!total)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ctrl = 0;
|
|
|
+ list_for_each_entry(pdev, &dev->bus->devices, bus_list)
|
|
|
+ if (pdev->is_physfn)
|
|
|
+ goto found;
|
|
|
+
|
|
|
+ pdev = NULL;
|
|
|
+ if (pci_ari_enabled(dev->bus))
|
|
|
+ ctrl |= PCI_SRIOV_CTRL_ARI;
|
|
|
+
|
|
|
+found:
|
|
|
+ pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
|
|
|
+ pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
|
|
|
+ pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
|
|
|
+ pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
|
|
|
+ if (!offset || (total > 1 && !stride))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
|
|
|
+ i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
|
|
|
+ pgsz &= ~((1 << i) - 1);
|
|
|
+ if (!pgsz)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ pgsz &= ~(pgsz - 1);
|
|
|
+ pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
|
|
|
+
|
|
|
+ nres = 0;
|
|
|
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
|
|
|
+ res = dev->resource + PCI_IOV_RESOURCES + i;
|
|
|
+ i += __pci_read_base(dev, pci_bar_unknown, res,
|
|
|
+ pos + PCI_SRIOV_BAR + i * 4);
|
|
|
+ if (!res->flags)
|
|
|
+ continue;
|
|
|
+ if (resource_size(res) & (PAGE_SIZE - 1)) {
|
|
|
+ rc = -EIO;
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ res->end = res->start + resource_size(res) * total - 1;
|
|
|
+ nres++;
|
|
|
+ }
|
|
|
+
|
|
|
+ iov = kzalloc(sizeof(*iov), GFP_KERNEL);
|
|
|
+ if (!iov) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ iov->pos = pos;
|
|
|
+ iov->nres = nres;
|
|
|
+ iov->ctrl = ctrl;
|
|
|
+ iov->total = total;
|
|
|
+ iov->offset = offset;
|
|
|
+ iov->stride = stride;
|
|
|
+ iov->pgsz = pgsz;
|
|
|
+ iov->self = dev;
|
|
|
+ pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
|
|
|
+ pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
|
|
|
+
|
|
|
+ if (pdev)
|
|
|
+ iov->dev = pci_dev_get(pdev);
|
|
|
+ else {
|
|
|
+ iov->dev = dev;
|
|
|
+ mutex_init(&iov->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ dev->sriov = iov;
|
|
|
+ dev->is_physfn = 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+failed:
|
|
|
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
|
|
|
+ res = dev->resource + PCI_IOV_RESOURCES + i;
|
|
|
+ res->flags = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static void sriov_release(struct pci_dev *dev)
|
|
|
+{
|
|
|
+ if (dev == dev->sriov->dev)
|
|
|
+ mutex_destroy(&dev->sriov->lock);
|
|
|
+ else
|
|
|
+ pci_dev_put(dev->sriov->dev);
|
|
|
+
|
|
|
+ kfree(dev->sriov);
|
|
|
+ dev->sriov = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_iov_init - initialize the IOV capability
|
|
|
+ * @dev: the PCI device
|
|
|
+ *
|
|
|
+ * Returns 0 on success, or negative on failure.
|
|
|
+ */
|
|
|
+int pci_iov_init(struct pci_dev *dev)
|
|
|
+{
|
|
|
+ int pos;
|
|
|
+
|
|
|
+ if (!dev->is_pcie)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
|
|
|
+ if (pos)
|
|
|
+ return sriov_init(dev, pos);
|
|
|
+
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_iov_release - release resources used by the IOV capability
|
|
|
+ * @dev: the PCI device
|
|
|
+ */
|
|
|
+void pci_iov_release(struct pci_dev *dev)
|
|
|
+{
|
|
|
+ if (dev->is_physfn)
|
|
|
+ sriov_release(dev);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_iov_resource_bar - get position of the SR-IOV BAR
|
|
|
+ * @dev: the PCI device
|
|
|
+ * @resno: the resource number
|
|
|
+ * @type: the BAR type to be filled in
|
|
|
+ *
|
|
|
+ * Returns position of the BAR encapsulated in the SR-IOV capability.
|
|
|
+ */
|
|
|
+int pci_iov_resource_bar(struct pci_dev *dev, int resno,
|
|
|
+ enum pci_bar_type *type)
|
|
|
+{
|
|
|
+ if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ BUG_ON(!dev->is_physfn);
|
|
|
+
|
|
|
+ *type = pci_bar_unknown;
|
|
|
+
|
|
|
+ return dev->sriov->pos + PCI_SRIOV_BAR +
|
|
|
+ 4 * (resno - PCI_IOV_RESOURCES);
|
|
|
+}
|