123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*
- * 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;
- }
- static void sriov_restore_state(struct pci_dev *dev)
- {
- int i;
- u16 ctrl;
- struct pci_sriov *iov = dev->sriov;
- pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
- if (ctrl & PCI_SRIOV_CTRL_VFE)
- return;
- for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
- pci_update_resource(dev, i);
- pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
- if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
- msleep(100);
- }
- /**
- * 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);
- }
- /**
- * pci_restore_iov_state - restore the state of the IOV capability
- * @dev: the PCI device
- */
- void pci_restore_iov_state(struct pci_dev *dev)
- {
- if (dev->is_physfn)
- sriov_restore_state(dev);
- }
|