162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCI Express I/O Virtualization (IOV) support
462306a36Sopenharmony_ci *   Single Root IOV 1.0
562306a36Sopenharmony_ci *   Address Translation Service 1.0
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/export.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include "pci.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define VIRTFN_ID_LEN	17	/* "virtfn%u\0" for 2^32 - 1 */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciint pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	if (!dev->is_physfn)
2262306a36Sopenharmony_ci		return -EINVAL;
2362306a36Sopenharmony_ci	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
2462306a36Sopenharmony_ci				    dev->sriov->stride * vf_id) >> 8);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciint pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	if (!dev->is_physfn)
3062306a36Sopenharmony_ci		return -EINVAL;
3162306a36Sopenharmony_ci	return (dev->devfn + dev->sriov->offset +
3262306a36Sopenharmony_ci		dev->sriov->stride * vf_id) & 0xff;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_virtfn_devfn);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciint pci_iov_vf_id(struct pci_dev *dev)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct pci_dev *pf;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (!dev->is_virtfn)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	pf = pci_physfn(dev);
4462306a36Sopenharmony_ci	return (pci_dev_id(dev) - (pci_dev_id(pf) + pf->sriov->offset)) /
4562306a36Sopenharmony_ci	       pf->sriov->stride;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_vf_id);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/**
5062306a36Sopenharmony_ci * pci_iov_get_pf_drvdata - Return the drvdata of a PF
5162306a36Sopenharmony_ci * @dev: VF pci_dev
5262306a36Sopenharmony_ci * @pf_driver: Device driver required to own the PF
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * This must be called from a context that ensures that a VF driver is attached.
5562306a36Sopenharmony_ci * The value returned is invalid once the VF driver completes its remove()
5662306a36Sopenharmony_ci * callback.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Locking is achieved by the driver core. A VF driver cannot be probed until
5962306a36Sopenharmony_ci * pci_enable_sriov() is called and pci_disable_sriov() does not return until
6062306a36Sopenharmony_ci * all VF drivers have completed their remove().
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * The PF driver must call pci_disable_sriov() before it begins to destroy the
6362306a36Sopenharmony_ci * drvdata.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_civoid *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct pci_dev *pf_dev;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!dev->is_virtfn)
7062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7162306a36Sopenharmony_ci	pf_dev = dev->physfn;
7262306a36Sopenharmony_ci	if (pf_dev->driver != pf_driver)
7362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
7462306a36Sopenharmony_ci	return pci_get_drvdata(pf_dev);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_get_pf_drvdata);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
8062306a36Sopenharmony_ci * change when NumVFs changes.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Update iov->offset and iov->stride when NumVFs is written.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
8962306a36Sopenharmony_ci	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset);
9062306a36Sopenharmony_ci	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * The PF consumes one bus number.  NumVFs, First VF Offset, and VF Stride
9562306a36Sopenharmony_ci * determine how many additional bus numbers will be consumed by VFs.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Iterate over all valid NumVFs, validate offset and stride, and calculate
9862306a36Sopenharmony_ci * the maximum number of bus numbers that could ever be required.
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_cistatic int compute_max_vf_buses(struct pci_dev *dev)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
10362306a36Sopenharmony_ci	int nr_virtfn, busnr, rc = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) {
10662306a36Sopenharmony_ci		pci_iov_set_numvfs(dev, nr_virtfn);
10762306a36Sopenharmony_ci		if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) {
10862306a36Sopenharmony_ci			rc = -EIO;
10962306a36Sopenharmony_ci			goto out;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
11362306a36Sopenharmony_ci		if (busnr > iov->max_VF_buses)
11462306a36Sopenharmony_ci			iov->max_VF_buses = busnr;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciout:
11862306a36Sopenharmony_ci	pci_iov_set_numvfs(dev, 0);
11962306a36Sopenharmony_ci	return rc;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct pci_bus *child;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (bus->number == busnr)
12762306a36Sopenharmony_ci		return bus;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	child = pci_find_bus(pci_domain_nr(bus), busnr);
13062306a36Sopenharmony_ci	if (child)
13162306a36Sopenharmony_ci		return child;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	child = pci_add_new_bus(bus, NULL, busnr);
13462306a36Sopenharmony_ci	if (!child)
13562306a36Sopenharmony_ci		return NULL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	pci_bus_insert_busn_res(child, busnr, busnr);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return child;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	if (physbus != virtbus && list_empty(&virtbus->devices))
14562306a36Sopenharmony_ci		pci_remove_bus(virtbus);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciresource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (!dev->is_physfn)
15162306a36Sopenharmony_ci		return 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void pci_read_vf_config_common(struct pci_dev *virtfn)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct pci_dev *physfn = virtfn->physfn;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Some config registers are the same across all associated VFs.
16262306a36Sopenharmony_ci	 * Read them once from VF0 so we can skip reading them from the
16362306a36Sopenharmony_ci	 * other VFs.
16462306a36Sopenharmony_ci	 *
16562306a36Sopenharmony_ci	 * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
16662306a36Sopenharmony_ci	 * have the same Revision ID and Subsystem ID, but we assume they
16762306a36Sopenharmony_ci	 * do.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
17062306a36Sopenharmony_ci			      &physfn->sriov->class);
17162306a36Sopenharmony_ci	pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
17262306a36Sopenharmony_ci			     &physfn->sriov->hdr_type);
17362306a36Sopenharmony_ci	pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
17462306a36Sopenharmony_ci			     &physfn->sriov->subsystem_vendor);
17562306a36Sopenharmony_ci	pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
17662306a36Sopenharmony_ci			     &physfn->sriov->subsystem_device);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint pci_iov_sysfs_link(struct pci_dev *dev,
18062306a36Sopenharmony_ci		struct pci_dev *virtfn, int id)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	char buf[VIRTFN_ID_LEN];
18362306a36Sopenharmony_ci	int rc;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	sprintf(buf, "virtfn%u", id);
18662306a36Sopenharmony_ci	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
18762306a36Sopenharmony_ci	if (rc)
18862306a36Sopenharmony_ci		goto failed;
18962306a36Sopenharmony_ci	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
19062306a36Sopenharmony_ci	if (rc)
19162306a36Sopenharmony_ci		goto failed1;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cifailed1:
19862306a36Sopenharmony_ci	sysfs_remove_link(&dev->dev.kobj, buf);
19962306a36Sopenharmony_cifailed:
20062306a36Sopenharmony_ci	return rc;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
20462306a36Sopenharmony_cistatic ssize_t sriov_vf_total_msix_show(struct device *dev,
20562306a36Sopenharmony_ci					struct device_attribute *attr,
20662306a36Sopenharmony_ci					char *buf)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
20962306a36Sopenharmony_ci	u32 vf_total_msix = 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	device_lock(dev);
21262306a36Sopenharmony_ci	if (!pdev->driver || !pdev->driver->sriov_get_vf_total_msix)
21362306a36Sopenharmony_ci		goto unlock;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	vf_total_msix = pdev->driver->sriov_get_vf_total_msix(pdev);
21662306a36Sopenharmony_ciunlock:
21762306a36Sopenharmony_ci	device_unlock(dev);
21862306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", vf_total_msix);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_vf_total_msix);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic ssize_t sriov_vf_msix_count_store(struct device *dev,
22362306a36Sopenharmony_ci					 struct device_attribute *attr,
22462306a36Sopenharmony_ci					 const char *buf, size_t count)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct pci_dev *vf_dev = to_pci_dev(dev);
22762306a36Sopenharmony_ci	struct pci_dev *pdev = pci_physfn(vf_dev);
22862306a36Sopenharmony_ci	int val, ret = 0;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (kstrtoint(buf, 0, &val) < 0)
23162306a36Sopenharmony_ci		return -EINVAL;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (val < 0)
23462306a36Sopenharmony_ci		return -EINVAL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	device_lock(&pdev->dev);
23762306a36Sopenharmony_ci	if (!pdev->driver || !pdev->driver->sriov_set_msix_vec_count) {
23862306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
23962306a36Sopenharmony_ci		goto err_pdev;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	device_lock(&vf_dev->dev);
24362306a36Sopenharmony_ci	if (vf_dev->driver) {
24462306a36Sopenharmony_ci		/*
24562306a36Sopenharmony_ci		 * A driver is already attached to this VF and has configured
24662306a36Sopenharmony_ci		 * itself based on the current MSI-X vector count. Changing
24762306a36Sopenharmony_ci		 * the vector size could mess up the driver, so block it.
24862306a36Sopenharmony_ci		 */
24962306a36Sopenharmony_ci		ret = -EBUSY;
25062306a36Sopenharmony_ci		goto err_dev;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = pdev->driver->sriov_set_msix_vec_count(vf_dev, val);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cierr_dev:
25662306a36Sopenharmony_ci	device_unlock(&vf_dev->dev);
25762306a36Sopenharmony_cierr_pdev:
25862306a36Sopenharmony_ci	device_unlock(&pdev->dev);
25962306a36Sopenharmony_ci	return ret ? : count;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(sriov_vf_msix_count);
26262306a36Sopenharmony_ci#endif
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic struct attribute *sriov_vf_dev_attrs[] = {
26562306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
26662306a36Sopenharmony_ci	&dev_attr_sriov_vf_msix_count.attr,
26762306a36Sopenharmony_ci#endif
26862306a36Sopenharmony_ci	NULL,
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic umode_t sriov_vf_attrs_are_visible(struct kobject *kobj,
27262306a36Sopenharmony_ci					  struct attribute *a, int n)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
27562306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!pdev->is_virtfn)
27862306a36Sopenharmony_ci		return 0;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return a->mode;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciconst struct attribute_group sriov_vf_dev_attr_group = {
28462306a36Sopenharmony_ci	.attrs = sriov_vf_dev_attrs,
28562306a36Sopenharmony_ci	.is_visible = sriov_vf_attrs_are_visible,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint pci_iov_add_virtfn(struct pci_dev *dev, int id)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int i;
29162306a36Sopenharmony_ci	int rc = -ENOMEM;
29262306a36Sopenharmony_ci	u64 size;
29362306a36Sopenharmony_ci	struct pci_dev *virtfn;
29462306a36Sopenharmony_ci	struct resource *res;
29562306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
29662306a36Sopenharmony_ci	struct pci_bus *bus;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
29962306a36Sopenharmony_ci	if (!bus)
30062306a36Sopenharmony_ci		goto failed;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	virtfn = pci_alloc_dev(bus);
30362306a36Sopenharmony_ci	if (!virtfn)
30462306a36Sopenharmony_ci		goto failed0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
30762306a36Sopenharmony_ci	virtfn->vendor = dev->vendor;
30862306a36Sopenharmony_ci	virtfn->device = iov->vf_device;
30962306a36Sopenharmony_ci	virtfn->is_virtfn = 1;
31062306a36Sopenharmony_ci	virtfn->physfn = pci_dev_get(dev);
31162306a36Sopenharmony_ci	virtfn->no_command_memory = 1;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (id == 0)
31462306a36Sopenharmony_ci		pci_read_vf_config_common(virtfn);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	rc = pci_setup_device(virtfn);
31762306a36Sopenharmony_ci	if (rc)
31862306a36Sopenharmony_ci		goto failed1;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	virtfn->dev.parent = dev->dev.parent;
32162306a36Sopenharmony_ci	virtfn->multifunction = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
32462306a36Sopenharmony_ci		res = &dev->resource[i + PCI_IOV_RESOURCES];
32562306a36Sopenharmony_ci		if (!res->parent)
32662306a36Sopenharmony_ci			continue;
32762306a36Sopenharmony_ci		virtfn->resource[i].name = pci_name(virtfn);
32862306a36Sopenharmony_ci		virtfn->resource[i].flags = res->flags;
32962306a36Sopenharmony_ci		size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
33062306a36Sopenharmony_ci		virtfn->resource[i].start = res->start + size * id;
33162306a36Sopenharmony_ci		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
33262306a36Sopenharmony_ci		rc = request_resource(res, &virtfn->resource[i]);
33362306a36Sopenharmony_ci		BUG_ON(rc);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	pci_device_add(virtfn, virtfn->bus);
33762306a36Sopenharmony_ci	rc = pci_iov_sysfs_link(dev, virtfn, id);
33862306a36Sopenharmony_ci	if (rc)
33962306a36Sopenharmony_ci		goto failed1;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	pci_bus_add_device(virtfn);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cifailed1:
34662306a36Sopenharmony_ci	pci_stop_and_remove_bus_device(virtfn);
34762306a36Sopenharmony_ci	pci_dev_put(dev);
34862306a36Sopenharmony_cifailed0:
34962306a36Sopenharmony_ci	virtfn_remove_bus(dev->bus, bus);
35062306a36Sopenharmony_cifailed:
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return rc;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_civoid pci_iov_remove_virtfn(struct pci_dev *dev, int id)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	char buf[VIRTFN_ID_LEN];
35862306a36Sopenharmony_ci	struct pci_dev *virtfn;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
36162306a36Sopenharmony_ci					     pci_iov_virtfn_bus(dev, id),
36262306a36Sopenharmony_ci					     pci_iov_virtfn_devfn(dev, id));
36362306a36Sopenharmony_ci	if (!virtfn)
36462306a36Sopenharmony_ci		return;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	sprintf(buf, "virtfn%u", id);
36762306a36Sopenharmony_ci	sysfs_remove_link(&dev->dev.kobj, buf);
36862306a36Sopenharmony_ci	/*
36962306a36Sopenharmony_ci	 * pci_stop_dev() could have been called for this virtfn already,
37062306a36Sopenharmony_ci	 * so the directory for the virtfn may have been removed before.
37162306a36Sopenharmony_ci	 * Double check to avoid spurious sysfs warnings.
37262306a36Sopenharmony_ci	 */
37362306a36Sopenharmony_ci	if (virtfn->dev.kobj.sd)
37462306a36Sopenharmony_ci		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pci_stop_and_remove_bus_device(virtfn);
37762306a36Sopenharmony_ci	virtfn_remove_bus(dev->bus, virtfn->bus);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* balance pci_get_domain_bus_and_slot() */
38062306a36Sopenharmony_ci	pci_dev_put(virtfn);
38162306a36Sopenharmony_ci	pci_dev_put(dev);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic ssize_t sriov_totalvfs_show(struct device *dev,
38562306a36Sopenharmony_ci				   struct device_attribute *attr,
38662306a36Sopenharmony_ci				   char *buf)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic ssize_t sriov_numvfs_show(struct device *dev,
39462306a36Sopenharmony_ci				 struct device_attribute *attr,
39562306a36Sopenharmony_ci				 char *buf)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
39862306a36Sopenharmony_ci	u16 num_vfs;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Serialize vs sriov_numvfs_store() so readers see valid num_VFs */
40162306a36Sopenharmony_ci	device_lock(&pdev->dev);
40262306a36Sopenharmony_ci	num_vfs = pdev->sriov->num_VFs;
40362306a36Sopenharmony_ci	device_unlock(&pdev->dev);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", num_vfs);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/*
40962306a36Sopenharmony_ci * num_vfs > 0; number of VFs to enable
41062306a36Sopenharmony_ci * num_vfs = 0; disable all VFs
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * Note: SRIOV spec does not allow partial VF
41362306a36Sopenharmony_ci *	 disable, so it's all or none.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_cistatic ssize_t sriov_numvfs_store(struct device *dev,
41662306a36Sopenharmony_ci				  struct device_attribute *attr,
41762306a36Sopenharmony_ci				  const char *buf, size_t count)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
42062306a36Sopenharmony_ci	int ret = 0;
42162306a36Sopenharmony_ci	u16 num_vfs;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (kstrtou16(buf, 0, &num_vfs) < 0)
42462306a36Sopenharmony_ci		return -EINVAL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (num_vfs > pci_sriov_get_totalvfs(pdev))
42762306a36Sopenharmony_ci		return -ERANGE;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	device_lock(&pdev->dev);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (num_vfs == pdev->sriov->num_VFs)
43262306a36Sopenharmony_ci		goto exit;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* is PF driver loaded */
43562306a36Sopenharmony_ci	if (!pdev->driver) {
43662306a36Sopenharmony_ci		pci_info(pdev, "no driver bound to device; cannot configure SR-IOV\n");
43762306a36Sopenharmony_ci		ret = -ENOENT;
43862306a36Sopenharmony_ci		goto exit;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* is PF driver loaded w/callback */
44262306a36Sopenharmony_ci	if (!pdev->driver->sriov_configure) {
44362306a36Sopenharmony_ci		pci_info(pdev, "driver does not support SR-IOV configuration via sysfs\n");
44462306a36Sopenharmony_ci		ret = -ENOENT;
44562306a36Sopenharmony_ci		goto exit;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (num_vfs == 0) {
44962306a36Sopenharmony_ci		/* disable VFs */
45062306a36Sopenharmony_ci		ret = pdev->driver->sriov_configure(pdev, 0);
45162306a36Sopenharmony_ci		goto exit;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* enable VFs */
45562306a36Sopenharmony_ci	if (pdev->sriov->num_VFs) {
45662306a36Sopenharmony_ci		pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n",
45762306a36Sopenharmony_ci			 pdev->sriov->num_VFs, num_vfs);
45862306a36Sopenharmony_ci		ret = -EBUSY;
45962306a36Sopenharmony_ci		goto exit;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ret = pdev->driver->sriov_configure(pdev, num_vfs);
46362306a36Sopenharmony_ci	if (ret < 0)
46462306a36Sopenharmony_ci		goto exit;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (ret != num_vfs)
46762306a36Sopenharmony_ci		pci_warn(pdev, "%d VFs requested; only %d enabled\n",
46862306a36Sopenharmony_ci			 num_vfs, ret);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciexit:
47162306a36Sopenharmony_ci	device_unlock(&pdev->dev);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (ret < 0)
47462306a36Sopenharmony_ci		return ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return count;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic ssize_t sriov_offset_show(struct device *dev,
48062306a36Sopenharmony_ci				 struct device_attribute *attr,
48162306a36Sopenharmony_ci				 char *buf)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", pdev->sriov->offset);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic ssize_t sriov_stride_show(struct device *dev,
48962306a36Sopenharmony_ci				 struct device_attribute *attr,
49062306a36Sopenharmony_ci				 char *buf)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", pdev->sriov->stride);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic ssize_t sriov_vf_device_show(struct device *dev,
49862306a36Sopenharmony_ci				    struct device_attribute *attr,
49962306a36Sopenharmony_ci				    char *buf)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return sysfs_emit(buf, "%x\n", pdev->sriov->vf_device);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic ssize_t sriov_drivers_autoprobe_show(struct device *dev,
50762306a36Sopenharmony_ci					    struct device_attribute *attr,
50862306a36Sopenharmony_ci					    char *buf)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", pdev->sriov->drivers_autoprobe);
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic ssize_t sriov_drivers_autoprobe_store(struct device *dev,
51662306a36Sopenharmony_ci					     struct device_attribute *attr,
51762306a36Sopenharmony_ci					     const char *buf, size_t count)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
52062306a36Sopenharmony_ci	bool drivers_autoprobe;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (kstrtobool(buf, &drivers_autoprobe) < 0)
52362306a36Sopenharmony_ci		return -EINVAL;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	pdev->sriov->drivers_autoprobe = drivers_autoprobe;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return count;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_totalvfs);
53162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sriov_numvfs);
53262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_offset);
53362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_stride);
53462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_vf_device);
53562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sriov_drivers_autoprobe);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic struct attribute *sriov_pf_dev_attrs[] = {
53862306a36Sopenharmony_ci	&dev_attr_sriov_totalvfs.attr,
53962306a36Sopenharmony_ci	&dev_attr_sriov_numvfs.attr,
54062306a36Sopenharmony_ci	&dev_attr_sriov_offset.attr,
54162306a36Sopenharmony_ci	&dev_attr_sriov_stride.attr,
54262306a36Sopenharmony_ci	&dev_attr_sriov_vf_device.attr,
54362306a36Sopenharmony_ci	&dev_attr_sriov_drivers_autoprobe.attr,
54462306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
54562306a36Sopenharmony_ci	&dev_attr_sriov_vf_total_msix.attr,
54662306a36Sopenharmony_ci#endif
54762306a36Sopenharmony_ci	NULL,
54862306a36Sopenharmony_ci};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic umode_t sriov_pf_attrs_are_visible(struct kobject *kobj,
55162306a36Sopenharmony_ci					  struct attribute *a, int n)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (!dev_is_pf(dev))
55662306a36Sopenharmony_ci		return 0;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return a->mode;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ciconst struct attribute_group sriov_pf_dev_attr_group = {
56262306a36Sopenharmony_ci	.attrs = sriov_pf_dev_attrs,
56362306a36Sopenharmony_ci	.is_visible = sriov_pf_attrs_are_visible,
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ciint __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciint __weak pcibios_sriov_disable(struct pci_dev *pdev)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	return 0;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	unsigned int i;
57962306a36Sopenharmony_ci	int rc;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (dev->no_vf_scan)
58262306a36Sopenharmony_ci		return 0;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	for (i = 0; i < num_vfs; i++) {
58562306a36Sopenharmony_ci		rc = pci_iov_add_virtfn(dev, i);
58662306a36Sopenharmony_ci		if (rc)
58762306a36Sopenharmony_ci			goto failed;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_cifailed:
59162306a36Sopenharmony_ci	while (i--)
59262306a36Sopenharmony_ci		pci_iov_remove_virtfn(dev, i);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return rc;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int sriov_enable(struct pci_dev *dev, int nr_virtfn)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	int rc;
60062306a36Sopenharmony_ci	int i;
60162306a36Sopenharmony_ci	int nres;
60262306a36Sopenharmony_ci	u16 initial;
60362306a36Sopenharmony_ci	struct resource *res;
60462306a36Sopenharmony_ci	struct pci_dev *pdev;
60562306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
60662306a36Sopenharmony_ci	int bars = 0;
60762306a36Sopenharmony_ci	int bus;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (!nr_virtfn)
61062306a36Sopenharmony_ci		return 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (iov->num_VFs)
61362306a36Sopenharmony_ci		return -EINVAL;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
61662306a36Sopenharmony_ci	if (initial > iov->total_VFs ||
61762306a36Sopenharmony_ci	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
61862306a36Sopenharmony_ci		return -EIO;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
62162306a36Sopenharmony_ci	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
62262306a36Sopenharmony_ci		return -EINVAL;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	nres = 0;
62562306a36Sopenharmony_ci	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
62662306a36Sopenharmony_ci		bars |= (1 << (i + PCI_IOV_RESOURCES));
62762306a36Sopenharmony_ci		res = &dev->resource[i + PCI_IOV_RESOURCES];
62862306a36Sopenharmony_ci		if (res->parent)
62962306a36Sopenharmony_ci			nres++;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	if (nres != iov->nres) {
63262306a36Sopenharmony_ci		pci_err(dev, "not enough MMIO resources for SR-IOV\n");
63362306a36Sopenharmony_ci		return -ENOMEM;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
63762306a36Sopenharmony_ci	if (bus > dev->bus->busn_res.end) {
63862306a36Sopenharmony_ci		pci_err(dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
63962306a36Sopenharmony_ci			nr_virtfn, bus, &dev->bus->busn_res);
64062306a36Sopenharmony_ci		return -ENOMEM;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (pci_enable_resources(dev, bars)) {
64462306a36Sopenharmony_ci		pci_err(dev, "SR-IOV: IOV BARS not allocated\n");
64562306a36Sopenharmony_ci		return -ENOMEM;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (iov->link != dev->devfn) {
64962306a36Sopenharmony_ci		pdev = pci_get_slot(dev->bus, iov->link);
65062306a36Sopenharmony_ci		if (!pdev)
65162306a36Sopenharmony_ci			return -ENODEV;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		if (!pdev->is_physfn) {
65462306a36Sopenharmony_ci			pci_dev_put(pdev);
65562306a36Sopenharmony_ci			return -ENOSYS;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		rc = sysfs_create_link(&dev->dev.kobj,
65962306a36Sopenharmony_ci					&pdev->dev.kobj, "dep_link");
66062306a36Sopenharmony_ci		pci_dev_put(pdev);
66162306a36Sopenharmony_ci		if (rc)
66262306a36Sopenharmony_ci			return rc;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	iov->initial_VFs = initial;
66662306a36Sopenharmony_ci	if (nr_virtfn < initial)
66762306a36Sopenharmony_ci		initial = nr_virtfn;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	rc = pcibios_sriov_enable(dev, initial);
67062306a36Sopenharmony_ci	if (rc) {
67162306a36Sopenharmony_ci		pci_err(dev, "failure %d from pcibios_sriov_enable()\n", rc);
67262306a36Sopenharmony_ci		goto err_pcibios;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	pci_iov_set_numvfs(dev, nr_virtfn);
67662306a36Sopenharmony_ci	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
67762306a36Sopenharmony_ci	pci_cfg_access_lock(dev);
67862306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
67962306a36Sopenharmony_ci	msleep(100);
68062306a36Sopenharmony_ci	pci_cfg_access_unlock(dev);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	rc = sriov_add_vfs(dev, initial);
68362306a36Sopenharmony_ci	if (rc)
68462306a36Sopenharmony_ci		goto err_pcibios;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
68762306a36Sopenharmony_ci	iov->num_VFs = nr_virtfn;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cierr_pcibios:
69262306a36Sopenharmony_ci	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
69362306a36Sopenharmony_ci	pci_cfg_access_lock(dev);
69462306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
69562306a36Sopenharmony_ci	ssleep(1);
69662306a36Sopenharmony_ci	pci_cfg_access_unlock(dev);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	pcibios_sriov_disable(dev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (iov->link != dev->devfn)
70162306a36Sopenharmony_ci		sysfs_remove_link(&dev->dev.kobj, "dep_link");
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	pci_iov_set_numvfs(dev, 0);
70462306a36Sopenharmony_ci	return rc;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void sriov_del_vfs(struct pci_dev *dev)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
71062306a36Sopenharmony_ci	int i;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	for (i = 0; i < iov->num_VFs; i++)
71362306a36Sopenharmony_ci		pci_iov_remove_virtfn(dev, i);
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic void sriov_disable(struct pci_dev *dev)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (!iov->num_VFs)
72162306a36Sopenharmony_ci		return;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	sriov_del_vfs(dev);
72462306a36Sopenharmony_ci	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
72562306a36Sopenharmony_ci	pci_cfg_access_lock(dev);
72662306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
72762306a36Sopenharmony_ci	ssleep(1);
72862306a36Sopenharmony_ci	pci_cfg_access_unlock(dev);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	pcibios_sriov_disable(dev);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (iov->link != dev->devfn)
73362306a36Sopenharmony_ci		sysfs_remove_link(&dev->dev.kobj, "dep_link");
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	iov->num_VFs = 0;
73662306a36Sopenharmony_ci	pci_iov_set_numvfs(dev, 0);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int sriov_init(struct pci_dev *dev, int pos)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	int i, bar64;
74262306a36Sopenharmony_ci	int rc;
74362306a36Sopenharmony_ci	int nres;
74462306a36Sopenharmony_ci	u32 pgsz;
74562306a36Sopenharmony_ci	u16 ctrl, total;
74662306a36Sopenharmony_ci	struct pci_sriov *iov;
74762306a36Sopenharmony_ci	struct resource *res;
74862306a36Sopenharmony_ci	struct pci_dev *pdev;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
75162306a36Sopenharmony_ci	if (ctrl & PCI_SRIOV_CTRL_VFE) {
75262306a36Sopenharmony_ci		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
75362306a36Sopenharmony_ci		ssleep(1);
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	ctrl = 0;
75762306a36Sopenharmony_ci	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
75862306a36Sopenharmony_ci		if (pdev->is_physfn)
75962306a36Sopenharmony_ci			goto found;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	pdev = NULL;
76262306a36Sopenharmony_ci	if (pci_ari_enabled(dev->bus))
76362306a36Sopenharmony_ci		ctrl |= PCI_SRIOV_CTRL_ARI;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cifound:
76662306a36Sopenharmony_ci	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
76962306a36Sopenharmony_ci	if (!total)
77062306a36Sopenharmony_ci		return 0;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
77362306a36Sopenharmony_ci	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
77462306a36Sopenharmony_ci	pgsz &= ~((1 << i) - 1);
77562306a36Sopenharmony_ci	if (!pgsz)
77662306a36Sopenharmony_ci		return -EIO;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	pgsz &= ~(pgsz - 1);
77962306a36Sopenharmony_ci	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
78262306a36Sopenharmony_ci	if (!iov)
78362306a36Sopenharmony_ci		return -ENOMEM;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	nres = 0;
78662306a36Sopenharmony_ci	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
78762306a36Sopenharmony_ci		res = &dev->resource[i + PCI_IOV_RESOURCES];
78862306a36Sopenharmony_ci		/*
78962306a36Sopenharmony_ci		 * If it is already FIXED, don't change it, something
79062306a36Sopenharmony_ci		 * (perhaps EA or header fixups) wants it this way.
79162306a36Sopenharmony_ci		 */
79262306a36Sopenharmony_ci		if (res->flags & IORESOURCE_PCI_FIXED)
79362306a36Sopenharmony_ci			bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
79462306a36Sopenharmony_ci		else
79562306a36Sopenharmony_ci			bar64 = __pci_read_base(dev, pci_bar_unknown, res,
79662306a36Sopenharmony_ci						pos + PCI_SRIOV_BAR + i * 4);
79762306a36Sopenharmony_ci		if (!res->flags)
79862306a36Sopenharmony_ci			continue;
79962306a36Sopenharmony_ci		if (resource_size(res) & (PAGE_SIZE - 1)) {
80062306a36Sopenharmony_ci			rc = -EIO;
80162306a36Sopenharmony_ci			goto failed;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci		iov->barsz[i] = resource_size(res);
80462306a36Sopenharmony_ci		res->end = res->start + resource_size(res) * total - 1;
80562306a36Sopenharmony_ci		pci_info(dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n",
80662306a36Sopenharmony_ci			 i, res, i, total);
80762306a36Sopenharmony_ci		i += bar64;
80862306a36Sopenharmony_ci		nres++;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	iov->pos = pos;
81262306a36Sopenharmony_ci	iov->nres = nres;
81362306a36Sopenharmony_ci	iov->ctrl = ctrl;
81462306a36Sopenharmony_ci	iov->total_VFs = total;
81562306a36Sopenharmony_ci	iov->driver_max_VFs = total;
81662306a36Sopenharmony_ci	pci_read_config_word(dev, pos + PCI_SRIOV_VF_DID, &iov->vf_device);
81762306a36Sopenharmony_ci	iov->pgsz = pgsz;
81862306a36Sopenharmony_ci	iov->self = dev;
81962306a36Sopenharmony_ci	iov->drivers_autoprobe = true;
82062306a36Sopenharmony_ci	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
82162306a36Sopenharmony_ci	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
82262306a36Sopenharmony_ci	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
82362306a36Sopenharmony_ci		iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (pdev)
82662306a36Sopenharmony_ci		iov->dev = pci_dev_get(pdev);
82762306a36Sopenharmony_ci	else
82862306a36Sopenharmony_ci		iov->dev = dev;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	dev->sriov = iov;
83162306a36Sopenharmony_ci	dev->is_physfn = 1;
83262306a36Sopenharmony_ci	rc = compute_max_vf_buses(dev);
83362306a36Sopenharmony_ci	if (rc)
83462306a36Sopenharmony_ci		goto fail_max_buses;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cifail_max_buses:
83962306a36Sopenharmony_ci	dev->sriov = NULL;
84062306a36Sopenharmony_ci	dev->is_physfn = 0;
84162306a36Sopenharmony_cifailed:
84262306a36Sopenharmony_ci	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
84362306a36Sopenharmony_ci		res = &dev->resource[i + PCI_IOV_RESOURCES];
84462306a36Sopenharmony_ci		res->flags = 0;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	kfree(iov);
84862306a36Sopenharmony_ci	return rc;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic void sriov_release(struct pci_dev *dev)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	BUG_ON(dev->sriov->num_VFs);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (dev != dev->sriov->dev)
85662306a36Sopenharmony_ci		pci_dev_put(dev->sriov->dev);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	kfree(dev->sriov);
85962306a36Sopenharmony_ci	dev->sriov = NULL;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic void sriov_restore_state(struct pci_dev *dev)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	int i;
86562306a36Sopenharmony_ci	u16 ctrl;
86662306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
86962306a36Sopenharmony_ci	if (ctrl & PCI_SRIOV_CTRL_VFE)
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/*
87362306a36Sopenharmony_ci	 * Restore PCI_SRIOV_CTRL_ARI before pci_iov_set_numvfs() because
87462306a36Sopenharmony_ci	 * it reads offset & stride, which depend on PCI_SRIOV_CTRL_ARI.
87562306a36Sopenharmony_ci	 */
87662306a36Sopenharmony_ci	ctrl &= ~PCI_SRIOV_CTRL_ARI;
87762306a36Sopenharmony_ci	ctrl |= iov->ctrl & PCI_SRIOV_CTRL_ARI;
87862306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++)
88162306a36Sopenharmony_ci		pci_update_resource(dev, i + PCI_IOV_RESOURCES);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
88462306a36Sopenharmony_ci	pci_iov_set_numvfs(dev, iov->num_VFs);
88562306a36Sopenharmony_ci	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
88662306a36Sopenharmony_ci	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
88762306a36Sopenharmony_ci		msleep(100);
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci/**
89162306a36Sopenharmony_ci * pci_iov_init - initialize the IOV capability
89262306a36Sopenharmony_ci * @dev: the PCI device
89362306a36Sopenharmony_ci *
89462306a36Sopenharmony_ci * Returns 0 on success, or negative on failure.
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_ciint pci_iov_init(struct pci_dev *dev)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	int pos;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (!pci_is_pcie(dev))
90162306a36Sopenharmony_ci		return -ENODEV;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
90462306a36Sopenharmony_ci	if (pos)
90562306a36Sopenharmony_ci		return sriov_init(dev, pos);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return -ENODEV;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/**
91162306a36Sopenharmony_ci * pci_iov_release - release resources used by the IOV capability
91262306a36Sopenharmony_ci * @dev: the PCI device
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_civoid pci_iov_release(struct pci_dev *dev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	if (dev->is_physfn)
91762306a36Sopenharmony_ci		sriov_release(dev);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci/**
92162306a36Sopenharmony_ci * pci_iov_remove - clean up SR-IOV state after PF driver is detached
92262306a36Sopenharmony_ci * @dev: the PCI device
92362306a36Sopenharmony_ci */
92462306a36Sopenharmony_civoid pci_iov_remove(struct pci_dev *dev)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct pci_sriov *iov = dev->sriov;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (!dev->is_physfn)
92962306a36Sopenharmony_ci		return;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	iov->driver_max_VFs = iov->total_VFs;
93262306a36Sopenharmony_ci	if (iov->num_VFs)
93362306a36Sopenharmony_ci		pci_warn(dev, "driver left SR-IOV enabled after remove\n");
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci/**
93762306a36Sopenharmony_ci * pci_iov_update_resource - update a VF BAR
93862306a36Sopenharmony_ci * @dev: the PCI device
93962306a36Sopenharmony_ci * @resno: the resource number
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci * Update a VF BAR in the SR-IOV capability of a PF.
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_civoid pci_iov_update_resource(struct pci_dev *dev, int resno)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL;
94662306a36Sopenharmony_ci	struct resource *res = dev->resource + resno;
94762306a36Sopenharmony_ci	int vf_bar = resno - PCI_IOV_RESOURCES;
94862306a36Sopenharmony_ci	struct pci_bus_region region;
94962306a36Sopenharmony_ci	u16 cmd;
95062306a36Sopenharmony_ci	u32 new;
95162306a36Sopenharmony_ci	int reg;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/*
95462306a36Sopenharmony_ci	 * The generic pci_restore_bars() path calls this for all devices,
95562306a36Sopenharmony_ci	 * including VFs and non-SR-IOV devices.  If this is not a PF, we
95662306a36Sopenharmony_ci	 * have nothing to do.
95762306a36Sopenharmony_ci	 */
95862306a36Sopenharmony_ci	if (!iov)
95962306a36Sopenharmony_ci		return;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd);
96262306a36Sopenharmony_ci	if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) {
96362306a36Sopenharmony_ci		dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n",
96462306a36Sopenharmony_ci			 vf_bar, res);
96562306a36Sopenharmony_ci		return;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/*
96962306a36Sopenharmony_ci	 * Ignore unimplemented BARs, unused resource slots for 64-bit
97062306a36Sopenharmony_ci	 * BARs, and non-movable resources, e.g., those described via
97162306a36Sopenharmony_ci	 * Enhanced Allocation.
97262306a36Sopenharmony_ci	 */
97362306a36Sopenharmony_ci	if (!res->flags)
97462306a36Sopenharmony_ci		return;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	if (res->flags & IORESOURCE_UNSET)
97762306a36Sopenharmony_ci		return;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (res->flags & IORESOURCE_PCI_FIXED)
98062306a36Sopenharmony_ci		return;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	pcibios_resource_to_bus(dev->bus, &region, res);
98362306a36Sopenharmony_ci	new = region.start;
98462306a36Sopenharmony_ci	new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar;
98762306a36Sopenharmony_ci	pci_write_config_dword(dev, reg, new);
98862306a36Sopenharmony_ci	if (res->flags & IORESOURCE_MEM_64) {
98962306a36Sopenharmony_ci		new = region.start >> 16 >> 16;
99062306a36Sopenharmony_ci		pci_write_config_dword(dev, reg + 4, new);
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ciresource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
99562306a36Sopenharmony_ci						      int resno)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	return pci_iov_resource_size(dev, resno);
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci/**
100162306a36Sopenharmony_ci * pci_sriov_resource_alignment - get resource alignment for VF BAR
100262306a36Sopenharmony_ci * @dev: the PCI device
100362306a36Sopenharmony_ci * @resno: the resource number
100462306a36Sopenharmony_ci *
100562306a36Sopenharmony_ci * Returns the alignment of the VF BAR found in the SR-IOV capability.
100662306a36Sopenharmony_ci * This is not the same as the resource size which is defined as
100762306a36Sopenharmony_ci * the VF BAR size multiplied by the number of VFs.  The alignment
100862306a36Sopenharmony_ci * is just the VF BAR size.
100962306a36Sopenharmony_ci */
101062306a36Sopenharmony_ciresource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	return pcibios_iov_resource_alignment(dev, resno);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci/**
101662306a36Sopenharmony_ci * pci_restore_iov_state - restore the state of the IOV capability
101762306a36Sopenharmony_ci * @dev: the PCI device
101862306a36Sopenharmony_ci */
101962306a36Sopenharmony_civoid pci_restore_iov_state(struct pci_dev *dev)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	if (dev->is_physfn)
102262306a36Sopenharmony_ci		sriov_restore_state(dev);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci/**
102662306a36Sopenharmony_ci * pci_vf_drivers_autoprobe - set PF property drivers_autoprobe for VFs
102762306a36Sopenharmony_ci * @dev: the PCI device
102862306a36Sopenharmony_ci * @auto_probe: set VF drivers auto probe flag
102962306a36Sopenharmony_ci */
103062306a36Sopenharmony_civoid pci_vf_drivers_autoprobe(struct pci_dev *dev, bool auto_probe)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	if (dev->is_physfn)
103362306a36Sopenharmony_ci		dev->sriov->drivers_autoprobe = auto_probe;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci/**
103762306a36Sopenharmony_ci * pci_iov_bus_range - find bus range used by Virtual Function
103862306a36Sopenharmony_ci * @bus: the PCI bus
103962306a36Sopenharmony_ci *
104062306a36Sopenharmony_ci * Returns max number of buses (exclude current one) used by Virtual
104162306a36Sopenharmony_ci * Functions.
104262306a36Sopenharmony_ci */
104362306a36Sopenharmony_ciint pci_iov_bus_range(struct pci_bus *bus)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	int max = 0;
104662306a36Sopenharmony_ci	struct pci_dev *dev;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	list_for_each_entry(dev, &bus->devices, bus_list) {
104962306a36Sopenharmony_ci		if (!dev->is_physfn)
105062306a36Sopenharmony_ci			continue;
105162306a36Sopenharmony_ci		if (dev->sriov->max_VF_buses > max)
105262306a36Sopenharmony_ci			max = dev->sriov->max_VF_buses;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return max ? max - bus->number : 0;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci/**
105962306a36Sopenharmony_ci * pci_enable_sriov - enable the SR-IOV capability
106062306a36Sopenharmony_ci * @dev: the PCI device
106162306a36Sopenharmony_ci * @nr_virtfn: number of virtual functions to enable
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * Returns 0 on success, or negative on failure.
106462306a36Sopenharmony_ci */
106562306a36Sopenharmony_ciint pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	might_sleep();
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (!dev->is_physfn)
107062306a36Sopenharmony_ci		return -ENOSYS;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	return sriov_enable(dev, nr_virtfn);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_enable_sriov);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci/**
107762306a36Sopenharmony_ci * pci_disable_sriov - disable the SR-IOV capability
107862306a36Sopenharmony_ci * @dev: the PCI device
107962306a36Sopenharmony_ci */
108062306a36Sopenharmony_civoid pci_disable_sriov(struct pci_dev *dev)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	might_sleep();
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (!dev->is_physfn)
108562306a36Sopenharmony_ci		return;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	sriov_disable(dev);
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_disable_sriov);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci/**
109262306a36Sopenharmony_ci * pci_num_vf - return number of VFs associated with a PF device_release_driver
109362306a36Sopenharmony_ci * @dev: the PCI device
109462306a36Sopenharmony_ci *
109562306a36Sopenharmony_ci * Returns number of VFs, or 0 if SR-IOV is not enabled.
109662306a36Sopenharmony_ci */
109762306a36Sopenharmony_ciint pci_num_vf(struct pci_dev *dev)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	if (!dev->is_physfn)
110062306a36Sopenharmony_ci		return 0;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	return dev->sriov->num_VFs;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_num_vf);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci/**
110762306a36Sopenharmony_ci * pci_vfs_assigned - returns number of VFs are assigned to a guest
110862306a36Sopenharmony_ci * @dev: the PCI device
110962306a36Sopenharmony_ci *
111062306a36Sopenharmony_ci * Returns number of VFs belonging to this device that are assigned to a guest.
111162306a36Sopenharmony_ci * If device is not a physical function returns 0.
111262306a36Sopenharmony_ci */
111362306a36Sopenharmony_ciint pci_vfs_assigned(struct pci_dev *dev)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	struct pci_dev *vfdev;
111662306a36Sopenharmony_ci	unsigned int vfs_assigned = 0;
111762306a36Sopenharmony_ci	unsigned short dev_id;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* only search if we are a PF */
112062306a36Sopenharmony_ci	if (!dev->is_physfn)
112162306a36Sopenharmony_ci		return 0;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/*
112462306a36Sopenharmony_ci	 * determine the device ID for the VFs, the vendor ID will be the
112562306a36Sopenharmony_ci	 * same as the PF so there is no need to check for that one
112662306a36Sopenharmony_ci	 */
112762306a36Sopenharmony_ci	dev_id = dev->sriov->vf_device;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	/* loop through all the VFs to see if we own any that are assigned */
113062306a36Sopenharmony_ci	vfdev = pci_get_device(dev->vendor, dev_id, NULL);
113162306a36Sopenharmony_ci	while (vfdev) {
113262306a36Sopenharmony_ci		/*
113362306a36Sopenharmony_ci		 * It is considered assigned if it is a virtual function with
113462306a36Sopenharmony_ci		 * our dev as the physical function and the assigned bit is set
113562306a36Sopenharmony_ci		 */
113662306a36Sopenharmony_ci		if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
113762306a36Sopenharmony_ci			pci_is_dev_assigned(vfdev))
113862306a36Sopenharmony_ci			vfs_assigned++;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci		vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return vfs_assigned;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_vfs_assigned);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci/**
114862306a36Sopenharmony_ci * pci_sriov_set_totalvfs -- reduce the TotalVFs available
114962306a36Sopenharmony_ci * @dev: the PCI PF device
115062306a36Sopenharmony_ci * @numvfs: number that should be used for TotalVFs supported
115162306a36Sopenharmony_ci *
115262306a36Sopenharmony_ci * Should be called from PF driver's probe routine with
115362306a36Sopenharmony_ci * device's mutex held.
115462306a36Sopenharmony_ci *
115562306a36Sopenharmony_ci * Returns 0 if PF is an SRIOV-capable device and
115662306a36Sopenharmony_ci * value of numvfs valid. If not a PF return -ENOSYS;
115762306a36Sopenharmony_ci * if numvfs is invalid return -EINVAL;
115862306a36Sopenharmony_ci * if VFs already enabled, return -EBUSY.
115962306a36Sopenharmony_ci */
116062306a36Sopenharmony_ciint pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	if (!dev->is_physfn)
116362306a36Sopenharmony_ci		return -ENOSYS;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (numvfs > dev->sriov->total_VFs)
116662306a36Sopenharmony_ci		return -EINVAL;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	/* Shouldn't change if VFs already enabled */
116962306a36Sopenharmony_ci	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
117062306a36Sopenharmony_ci		return -EBUSY;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	dev->sriov->driver_max_VFs = numvfs;
117362306a36Sopenharmony_ci	return 0;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci/**
117862306a36Sopenharmony_ci * pci_sriov_get_totalvfs -- get total VFs supported on this device
117962306a36Sopenharmony_ci * @dev: the PCI PF device
118062306a36Sopenharmony_ci *
118162306a36Sopenharmony_ci * For a PCIe device with SRIOV support, return the PCIe
118262306a36Sopenharmony_ci * SRIOV capability value of TotalVFs or the value of driver_max_VFs
118362306a36Sopenharmony_ci * if the driver reduced it.  Otherwise 0.
118462306a36Sopenharmony_ci */
118562306a36Sopenharmony_ciint pci_sriov_get_totalvfs(struct pci_dev *dev)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	if (!dev->is_physfn)
118862306a36Sopenharmony_ci		return 0;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return dev->sriov->driver_max_VFs;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci/**
119562306a36Sopenharmony_ci * pci_sriov_configure_simple - helper to configure SR-IOV
119662306a36Sopenharmony_ci * @dev: the PCI device
119762306a36Sopenharmony_ci * @nr_virtfn: number of virtual functions to enable, 0 to disable
119862306a36Sopenharmony_ci *
119962306a36Sopenharmony_ci * Enable or disable SR-IOV for devices that don't require any PF setup
120062306a36Sopenharmony_ci * before enabling SR-IOV.  Return value is negative on error, or number of
120162306a36Sopenharmony_ci * VFs allocated on success.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_ciint pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	int rc;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	might_sleep();
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (!dev->is_physfn)
121062306a36Sopenharmony_ci		return -ENODEV;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (pci_vfs_assigned(dev)) {
121362306a36Sopenharmony_ci		pci_warn(dev, "Cannot modify SR-IOV while VFs are assigned\n");
121462306a36Sopenharmony_ci		return -EPERM;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (nr_virtfn == 0) {
121862306a36Sopenharmony_ci		sriov_disable(dev);
121962306a36Sopenharmony_ci		return 0;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	rc = sriov_enable(dev, nr_virtfn);
122362306a36Sopenharmony_ci	if (rc < 0)
122462306a36Sopenharmony_ci		return rc;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	return nr_virtfn;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_configure_simple);
1229