xref: /linux/drivers/pci/iov.c (revision 156c55325d30261d250e88ed3a39f22008f4ca16)
1d1b054daSYu Zhao /*
2d1b054daSYu Zhao  * drivers/pci/iov.c
3d1b054daSYu Zhao  *
4d1b054daSYu Zhao  * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
5d1b054daSYu Zhao  *
6d1b054daSYu Zhao  * PCI Express I/O Virtualization (IOV) support.
7d1b054daSYu Zhao  *   Single Root IOV 1.0
8302b4215SYu Zhao  *   Address Translation Service 1.0
9d1b054daSYu Zhao  */
10d1b054daSYu Zhao 
11d1b054daSYu Zhao #include <linux/pci.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
13d1b054daSYu Zhao #include <linux/mutex.h>
14363c75dbSPaul Gortmaker #include <linux/export.h>
15d1b054daSYu Zhao #include <linux/string.h>
16d1b054daSYu Zhao #include <linux/delay.h>
175cdede24SJoerg Roedel #include <linux/pci-ats.h>
18d1b054daSYu Zhao #include "pci.h"
19d1b054daSYu Zhao 
20dd7cc44dSYu Zhao #define VIRTFN_ID_LEN	16
21d1b054daSYu Zhao 
22b07579c0SWei Yang int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)
23a28724b0SYu Zhao {
24b07579c0SWei Yang 	if (!dev->is_physfn)
25b07579c0SWei Yang 		return -EINVAL;
26a28724b0SYu Zhao 	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
27b07579c0SWei Yang 				    dev->sriov->stride * vf_id) >> 8);
28a28724b0SYu Zhao }
29a28724b0SYu Zhao 
30b07579c0SWei Yang int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)
31a28724b0SYu Zhao {
32b07579c0SWei Yang 	if (!dev->is_physfn)
33b07579c0SWei Yang 		return -EINVAL;
34a28724b0SYu Zhao 	return (dev->devfn + dev->sriov->offset +
35b07579c0SWei Yang 		dev->sriov->stride * vf_id) & 0xff;
36a28724b0SYu Zhao }
37a28724b0SYu Zhao 
38f59dca27SWei Yang /*
39f59dca27SWei Yang  * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
40f59dca27SWei Yang  * change when NumVFs changes.
41f59dca27SWei Yang  *
42f59dca27SWei Yang  * Update iov->offset and iov->stride when NumVFs is written.
43f59dca27SWei Yang  */
44f59dca27SWei Yang static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
45f59dca27SWei Yang {
46f59dca27SWei Yang 	struct pci_sriov *iov = dev->sriov;
47f59dca27SWei Yang 
48f59dca27SWei Yang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
49f59dca27SWei Yang 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset);
50f59dca27SWei Yang 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride);
51f59dca27SWei Yang }
52f59dca27SWei Yang 
534449f079SWei Yang /*
544449f079SWei Yang  * The PF consumes one bus number.  NumVFs, First VF Offset, and VF Stride
554449f079SWei Yang  * determine how many additional bus numbers will be consumed by VFs.
564449f079SWei Yang  *
57ea9a8854SAlexander Duyck  * Iterate over all valid NumVFs, validate offset and stride, and calculate
58ea9a8854SAlexander Duyck  * the maximum number of bus numbers that could ever be required.
594449f079SWei Yang  */
60ea9a8854SAlexander Duyck static int compute_max_vf_buses(struct pci_dev *dev)
614449f079SWei Yang {
624449f079SWei Yang 	struct pci_sriov *iov = dev->sriov;
63ea9a8854SAlexander Duyck 	int nr_virtfn, busnr, rc = 0;
644449f079SWei Yang 
65ea9a8854SAlexander Duyck 	for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) {
664449f079SWei Yang 		pci_iov_set_numvfs(dev, nr_virtfn);
67ea9a8854SAlexander Duyck 		if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) {
68ea9a8854SAlexander Duyck 			rc = -EIO;
69ea9a8854SAlexander Duyck 			goto out;
704449f079SWei Yang 		}
714449f079SWei Yang 
72ea9a8854SAlexander Duyck 		busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
73ea9a8854SAlexander Duyck 		if (busnr > iov->max_VF_buses)
74ea9a8854SAlexander Duyck 			iov->max_VF_buses = busnr;
75ea9a8854SAlexander Duyck 	}
76ea9a8854SAlexander Duyck 
77ea9a8854SAlexander Duyck out:
78ea9a8854SAlexander Duyck 	pci_iov_set_numvfs(dev, 0);
79ea9a8854SAlexander Duyck 	return rc;
804449f079SWei Yang }
814449f079SWei Yang 
82dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
83dd7cc44dSYu Zhao {
84dd7cc44dSYu Zhao 	struct pci_bus *child;
85dd7cc44dSYu Zhao 
86dd7cc44dSYu Zhao 	if (bus->number == busnr)
87dd7cc44dSYu Zhao 		return bus;
88dd7cc44dSYu Zhao 
89dd7cc44dSYu Zhao 	child = pci_find_bus(pci_domain_nr(bus), busnr);
90dd7cc44dSYu Zhao 	if (child)
91dd7cc44dSYu Zhao 		return child;
92dd7cc44dSYu Zhao 
93dd7cc44dSYu Zhao 	child = pci_add_new_bus(bus, NULL, busnr);
94dd7cc44dSYu Zhao 	if (!child)
95dd7cc44dSYu Zhao 		return NULL;
96dd7cc44dSYu Zhao 
97b7eac055SYinghai Lu 	pci_bus_insert_busn_res(child, busnr, busnr);
98dd7cc44dSYu Zhao 
99dd7cc44dSYu Zhao 	return child;
100dd7cc44dSYu Zhao }
101dd7cc44dSYu Zhao 
102dc087f2fSJiang Liu static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
103dd7cc44dSYu Zhao {
104dc087f2fSJiang Liu 	if (physbus != virtbus && list_empty(&virtbus->devices))
105dc087f2fSJiang Liu 		pci_remove_bus(virtbus);
106dd7cc44dSYu Zhao }
107dd7cc44dSYu Zhao 
1080e6c9122SWei Yang resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
1090e6c9122SWei Yang {
1100e6c9122SWei Yang 	if (!dev->is_physfn)
1110e6c9122SWei Yang 		return 0;
1120e6c9122SWei Yang 
1130e6c9122SWei Yang 	return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
1140e6c9122SWei Yang }
1150e6c9122SWei Yang 
116c194f7eaSWei Yang int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset)
117dd7cc44dSYu Zhao {
118dd7cc44dSYu Zhao 	int i;
119dc087f2fSJiang Liu 	int rc = -ENOMEM;
120dd7cc44dSYu Zhao 	u64 size;
121dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
122dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
123dd7cc44dSYu Zhao 	struct resource *res;
124dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
1258b1fce04SGu Zheng 	struct pci_bus *bus;
126dd7cc44dSYu Zhao 
127dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
128b07579c0SWei Yang 	bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
129dc087f2fSJiang Liu 	if (!bus)
130dc087f2fSJiang Liu 		goto failed;
131dc087f2fSJiang Liu 
132dc087f2fSJiang Liu 	virtfn = pci_alloc_dev(bus);
133dc087f2fSJiang Liu 	if (!virtfn)
134dc087f2fSJiang Liu 		goto failed0;
135dc087f2fSJiang Liu 
136b07579c0SWei Yang 	virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
137dd7cc44dSYu Zhao 	virtfn->vendor = dev->vendor;
138dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
139*156c5532SPo Liu 	rc = pci_setup_device(virtfn);
140*156c5532SPo Liu 	if (rc)
141*156c5532SPo Liu 		goto failed0;
142*156c5532SPo Liu 
143dd7cc44dSYu Zhao 	virtfn->dev.parent = dev->dev.parent;
144fbf33f51SXudong Hao 	virtfn->physfn = pci_dev_get(dev);
145fbf33f51SXudong Hao 	virtfn->is_virtfn = 1;
146aa931977SAlex Williamson 	virtfn->multifunction = 0;
147dd7cc44dSYu Zhao 
148dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
149c1fe1f96SBjorn Helgaas 		res = &dev->resource[i + PCI_IOV_RESOURCES];
150dd7cc44dSYu Zhao 		if (!res->parent)
151dd7cc44dSYu Zhao 			continue;
152dd7cc44dSYu Zhao 		virtfn->resource[i].name = pci_name(virtfn);
153dd7cc44dSYu Zhao 		virtfn->resource[i].flags = res->flags;
1540e6c9122SWei Yang 		size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
155dd7cc44dSYu Zhao 		virtfn->resource[i].start = res->start + size * id;
156dd7cc44dSYu Zhao 		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
157dd7cc44dSYu Zhao 		rc = request_resource(res, &virtfn->resource[i]);
158dd7cc44dSYu Zhao 		BUG_ON(rc);
159dd7cc44dSYu Zhao 	}
160dd7cc44dSYu Zhao 
161dd7cc44dSYu Zhao 	if (reset)
1628c1c699fSYu Zhao 		__pci_reset_function(virtfn);
163dd7cc44dSYu Zhao 
164dd7cc44dSYu Zhao 	pci_device_add(virtfn, virtfn->bus);
165dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
166dd7cc44dSYu Zhao 
167c893d133SYijing Wang 	pci_bus_add_device(virtfn);
168dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
169dd7cc44dSYu Zhao 	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
170dd7cc44dSYu Zhao 	if (rc)
171dd7cc44dSYu Zhao 		goto failed1;
172dd7cc44dSYu Zhao 	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
173dd7cc44dSYu Zhao 	if (rc)
174dd7cc44dSYu Zhao 		goto failed2;
175dd7cc44dSYu Zhao 
176dd7cc44dSYu Zhao 	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
177dd7cc44dSYu Zhao 
178dd7cc44dSYu Zhao 	return 0;
179dd7cc44dSYu Zhao 
180dd7cc44dSYu Zhao failed2:
181dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
182dd7cc44dSYu Zhao failed1:
183dd7cc44dSYu Zhao 	pci_dev_put(dev);
184dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
185210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
186dc087f2fSJiang Liu failed0:
187dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, bus);
188dc087f2fSJiang Liu failed:
189dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
190dd7cc44dSYu Zhao 
191dd7cc44dSYu Zhao 	return rc;
192dd7cc44dSYu Zhao }
193dd7cc44dSYu Zhao 
194c194f7eaSWei Yang void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset)
195dd7cc44dSYu Zhao {
196dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
197dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
198dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
199dd7cc44dSYu Zhao 
200dc087f2fSJiang Liu 	virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
201b07579c0SWei Yang 					     pci_iov_virtfn_bus(dev, id),
202b07579c0SWei Yang 					     pci_iov_virtfn_devfn(dev, id));
203dd7cc44dSYu Zhao 	if (!virtfn)
204dd7cc44dSYu Zhao 		return;
205dd7cc44dSYu Zhao 
206dd7cc44dSYu Zhao 	if (reset) {
207dd7cc44dSYu Zhao 		device_release_driver(&virtfn->dev);
2088c1c699fSYu Zhao 		__pci_reset_function(virtfn);
209dd7cc44dSYu Zhao 	}
210dd7cc44dSYu Zhao 
211dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
212dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
21309cedbefSYinghai Lu 	/*
21409cedbefSYinghai Lu 	 * pci_stop_dev() could have been called for this virtfn already,
21509cedbefSYinghai Lu 	 * so the directory for the virtfn may have been removed before.
21609cedbefSYinghai Lu 	 * Double check to avoid spurious sysfs warnings.
21709cedbefSYinghai Lu 	 */
21809cedbefSYinghai Lu 	if (virtfn->dev.kobj.sd)
219dd7cc44dSYu Zhao 		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
220dd7cc44dSYu Zhao 
221dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
222210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
223dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, virtfn->bus);
224dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
225dd7cc44dSYu Zhao 
226dc087f2fSJiang Liu 	/* balance pci_get_domain_bus_and_slot() */
227dc087f2fSJiang Liu 	pci_dev_put(virtfn);
228dd7cc44dSYu Zhao 	pci_dev_put(dev);
229dd7cc44dSYu Zhao }
230dd7cc44dSYu Zhao 
231995df527SWei Yang int __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs)
232995df527SWei Yang {
233995df527SWei Yang 	return 0;
234995df527SWei Yang }
235995df527SWei Yang 
236a39e3fcdSAlexander Duyck int __weak pcibios_sriov_disable(struct pci_dev *pdev)
237a39e3fcdSAlexander Duyck {
238a39e3fcdSAlexander Duyck 	return 0;
239a39e3fcdSAlexander Duyck }
240a39e3fcdSAlexander Duyck 
241dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
242dd7cc44dSYu Zhao {
243dd7cc44dSYu Zhao 	int rc;
2443443c382SAlexander Duyck 	int i;
245dd7cc44dSYu Zhao 	int nres;
246ce288ec3SAlexander Duyck 	u16 initial;
247dd7cc44dSYu Zhao 	struct resource *res;
248dd7cc44dSYu Zhao 	struct pci_dev *pdev;
249dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
250bbef98abSRam Pai 	int bars = 0;
251b07579c0SWei Yang 	int bus;
252dd7cc44dSYu Zhao 
253dd7cc44dSYu Zhao 	if (!nr_virtfn)
254dd7cc44dSYu Zhao 		return 0;
255dd7cc44dSYu Zhao 
2566b136724SBjorn Helgaas 	if (iov->num_VFs)
257dd7cc44dSYu Zhao 		return -EINVAL;
258dd7cc44dSYu Zhao 
259dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
2606b136724SBjorn Helgaas 	if (initial > iov->total_VFs ||
2616b136724SBjorn Helgaas 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
262dd7cc44dSYu Zhao 		return -EIO;
263dd7cc44dSYu Zhao 
2646b136724SBjorn Helgaas 	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
265dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
266dd7cc44dSYu Zhao 		return -EINVAL;
267dd7cc44dSYu Zhao 
268dd7cc44dSYu Zhao 	nres = 0;
269dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
270bbef98abSRam Pai 		bars |= (1 << (i + PCI_IOV_RESOURCES));
271c1fe1f96SBjorn Helgaas 		res = &dev->resource[i + PCI_IOV_RESOURCES];
272dd7cc44dSYu Zhao 		if (res->parent)
273dd7cc44dSYu Zhao 			nres++;
274dd7cc44dSYu Zhao 	}
275dd7cc44dSYu Zhao 	if (nres != iov->nres) {
276dd7cc44dSYu Zhao 		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
277dd7cc44dSYu Zhao 		return -ENOMEM;
278dd7cc44dSYu Zhao 	}
279dd7cc44dSYu Zhao 
280b07579c0SWei Yang 	bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1);
28168f8e9faSBjorn Helgaas 	if (bus > dev->bus->busn_res.end) {
28268f8e9faSBjorn Helgaas 		dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
28368f8e9faSBjorn Helgaas 			nr_virtfn, bus, &dev->bus->busn_res);
284dd7cc44dSYu Zhao 		return -ENOMEM;
285dd7cc44dSYu Zhao 	}
286dd7cc44dSYu Zhao 
287bbef98abSRam Pai 	if (pci_enable_resources(dev, bars)) {
288bbef98abSRam Pai 		dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
289bbef98abSRam Pai 		return -ENOMEM;
290bbef98abSRam Pai 	}
291bbef98abSRam Pai 
292dd7cc44dSYu Zhao 	if (iov->link != dev->devfn) {
293dd7cc44dSYu Zhao 		pdev = pci_get_slot(dev->bus, iov->link);
294dd7cc44dSYu Zhao 		if (!pdev)
295dd7cc44dSYu Zhao 			return -ENODEV;
296dd7cc44dSYu Zhao 
297dc087f2fSJiang Liu 		if (!pdev->is_physfn) {
298dd7cc44dSYu Zhao 			pci_dev_put(pdev);
299652d1100SStefan Assmann 			return -ENOSYS;
300dc087f2fSJiang Liu 		}
301dd7cc44dSYu Zhao 
302dd7cc44dSYu Zhao 		rc = sysfs_create_link(&dev->dev.kobj,
303dd7cc44dSYu Zhao 					&pdev->dev.kobj, "dep_link");
304dc087f2fSJiang Liu 		pci_dev_put(pdev);
305dd7cc44dSYu Zhao 		if (rc)
306dd7cc44dSYu Zhao 			return rc;
307dd7cc44dSYu Zhao 	}
308dd7cc44dSYu Zhao 
309f59dca27SWei Yang 	pci_iov_set_numvfs(dev, nr_virtfn);
310dd7cc44dSYu Zhao 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
311fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
312dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
313dd7cc44dSYu Zhao 	msleep(100);
314fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
315dd7cc44dSYu Zhao 
3166b136724SBjorn Helgaas 	iov->initial_VFs = initial;
317dd7cc44dSYu Zhao 	if (nr_virtfn < initial)
318dd7cc44dSYu Zhao 		initial = nr_virtfn;
319dd7cc44dSYu Zhao 
320c23b6135SAlexander Duyck 	rc = pcibios_sriov_enable(dev, initial);
321c23b6135SAlexander Duyck 	if (rc) {
322c23b6135SAlexander Duyck 		dev_err(&dev->dev, "failure %d from pcibios_sriov_enable()\n", rc);
323c23b6135SAlexander Duyck 		goto err_pcibios;
324995df527SWei Yang 	}
325995df527SWei Yang 
326dd7cc44dSYu Zhao 	for (i = 0; i < initial; i++) {
327c194f7eaSWei Yang 		rc = pci_iov_add_virtfn(dev, i, 0);
328dd7cc44dSYu Zhao 		if (rc)
329dd7cc44dSYu Zhao 			goto failed;
330dd7cc44dSYu Zhao 	}
331dd7cc44dSYu Zhao 
332dd7cc44dSYu Zhao 	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
3336b136724SBjorn Helgaas 	iov->num_VFs = nr_virtfn;
334dd7cc44dSYu Zhao 
335dd7cc44dSYu Zhao 	return 0;
336dd7cc44dSYu Zhao 
337dd7cc44dSYu Zhao failed:
3383443c382SAlexander Duyck 	while (i--)
339c194f7eaSWei Yang 		pci_iov_remove_virtfn(dev, i, 0);
340dd7cc44dSYu Zhao 
341c23b6135SAlexander Duyck 	pcibios_sriov_disable(dev);
342c23b6135SAlexander Duyck err_pcibios:
343dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
344fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
345dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
346dd7cc44dSYu Zhao 	ssleep(1);
347fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
348dd7cc44dSYu Zhao 
349dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
350dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
351dd7cc44dSYu Zhao 
352b3908644SAlexander Duyck 	pci_iov_set_numvfs(dev, 0);
353dd7cc44dSYu Zhao 	return rc;
354dd7cc44dSYu Zhao }
355dd7cc44dSYu Zhao 
356dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev)
357dd7cc44dSYu Zhao {
358dd7cc44dSYu Zhao 	int i;
359dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
360dd7cc44dSYu Zhao 
3616b136724SBjorn Helgaas 	if (!iov->num_VFs)
362dd7cc44dSYu Zhao 		return;
363dd7cc44dSYu Zhao 
3646b136724SBjorn Helgaas 	for (i = 0; i < iov->num_VFs; i++)
365c194f7eaSWei Yang 		pci_iov_remove_virtfn(dev, i, 0);
366dd7cc44dSYu Zhao 
367995df527SWei Yang 	pcibios_sriov_disable(dev);
368995df527SWei Yang 
369dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
370fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
371dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
372dd7cc44dSYu Zhao 	ssleep(1);
373fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
374dd7cc44dSYu Zhao 
375dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
376dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
377dd7cc44dSYu Zhao 
3786b136724SBjorn Helgaas 	iov->num_VFs = 0;
379f59dca27SWei Yang 	pci_iov_set_numvfs(dev, 0);
380dd7cc44dSYu Zhao }
381dd7cc44dSYu Zhao 
382d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
383d1b054daSYu Zhao {
3840e6c9122SWei Yang 	int i, bar64;
385d1b054daSYu Zhao 	int rc;
386d1b054daSYu Zhao 	int nres;
387d1b054daSYu Zhao 	u32 pgsz;
388ea9a8854SAlexander Duyck 	u16 ctrl, total;
389d1b054daSYu Zhao 	struct pci_sriov *iov;
390d1b054daSYu Zhao 	struct resource *res;
391d1b054daSYu Zhao 	struct pci_dev *pdev;
392d1b054daSYu Zhao 
393d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
394d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
395d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
396d1b054daSYu Zhao 		ssleep(1);
397d1b054daSYu Zhao 	}
398d1b054daSYu Zhao 
399d1b054daSYu Zhao 	ctrl = 0;
400d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
401d1b054daSYu Zhao 		if (pdev->is_physfn)
402d1b054daSYu Zhao 			goto found;
403d1b054daSYu Zhao 
404d1b054daSYu Zhao 	pdev = NULL;
405d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
406d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
407d1b054daSYu Zhao 
408d1b054daSYu Zhao found:
409d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
410d1b054daSYu Zhao 
411ff45f9ddSBen Shelton 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
412ff45f9ddSBen Shelton 	if (!total)
413ff45f9ddSBen Shelton 		return 0;
414d1b054daSYu Zhao 
415d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
416d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
417d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
418d1b054daSYu Zhao 	if (!pgsz)
419d1b054daSYu Zhao 		return -EIO;
420d1b054daSYu Zhao 
421d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
4228161fe91SVaidyanathan Srinivasan 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
423d1b054daSYu Zhao 
4240e6c9122SWei Yang 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
4250e6c9122SWei Yang 	if (!iov)
4260e6c9122SWei Yang 		return -ENOMEM;
4270e6c9122SWei Yang 
428d1b054daSYu Zhao 	nres = 0;
429d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
430c1fe1f96SBjorn Helgaas 		res = &dev->resource[i + PCI_IOV_RESOURCES];
43111183991SDavid Daney 		/*
43211183991SDavid Daney 		 * If it is already FIXED, don't change it, something
43311183991SDavid Daney 		 * (perhaps EA or header fixups) wants it this way.
43411183991SDavid Daney 		 */
43511183991SDavid Daney 		if (res->flags & IORESOURCE_PCI_FIXED)
43611183991SDavid Daney 			bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
43711183991SDavid Daney 		else
4380e6c9122SWei Yang 			bar64 = __pci_read_base(dev, pci_bar_unknown, res,
439d1b054daSYu Zhao 						pos + PCI_SRIOV_BAR + i * 4);
440d1b054daSYu Zhao 		if (!res->flags)
441d1b054daSYu Zhao 			continue;
442d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
443d1b054daSYu Zhao 			rc = -EIO;
444d1b054daSYu Zhao 			goto failed;
445d1b054daSYu Zhao 		}
4460e6c9122SWei Yang 		iov->barsz[i] = resource_size(res);
447d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
448e88ae01dSWei Yang 		dev_info(&dev->dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n",
449e88ae01dSWei Yang 			 i, res, i, total);
4500e6c9122SWei Yang 		i += bar64;
451d1b054daSYu Zhao 		nres++;
452d1b054daSYu Zhao 	}
453d1b054daSYu Zhao 
454d1b054daSYu Zhao 	iov->pos = pos;
455d1b054daSYu Zhao 	iov->nres = nres;
456d1b054daSYu Zhao 	iov->ctrl = ctrl;
4576b136724SBjorn Helgaas 	iov->total_VFs = total;
458d1b054daSYu Zhao 	iov->pgsz = pgsz;
459d1b054daSYu Zhao 	iov->self = dev;
460d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
461d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
46262f87c0eSYijing Wang 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
4634d135dbeSYu Zhao 		iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
464d1b054daSYu Zhao 
465d1b054daSYu Zhao 	if (pdev)
466d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
467e277d2fcSYu Zhao 	else
468d1b054daSYu Zhao 		iov->dev = dev;
469e277d2fcSYu Zhao 
470d1b054daSYu Zhao 	mutex_init(&iov->lock);
471d1b054daSYu Zhao 
472d1b054daSYu Zhao 	dev->sriov = iov;
473d1b054daSYu Zhao 	dev->is_physfn = 1;
474ea9a8854SAlexander Duyck 	rc = compute_max_vf_buses(dev);
475ea9a8854SAlexander Duyck 	if (rc)
476ea9a8854SAlexander Duyck 		goto fail_max_buses;
477d1b054daSYu Zhao 
478d1b054daSYu Zhao 	return 0;
479d1b054daSYu Zhao 
480ea9a8854SAlexander Duyck fail_max_buses:
481ea9a8854SAlexander Duyck 	dev->sriov = NULL;
482ea9a8854SAlexander Duyck 	dev->is_physfn = 0;
483d1b054daSYu Zhao failed:
484d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
485c1fe1f96SBjorn Helgaas 		res = &dev->resource[i + PCI_IOV_RESOURCES];
486d1b054daSYu Zhao 		res->flags = 0;
487d1b054daSYu Zhao 	}
488d1b054daSYu Zhao 
4890e6c9122SWei Yang 	kfree(iov);
490d1b054daSYu Zhao 	return rc;
491d1b054daSYu Zhao }
492d1b054daSYu Zhao 
493d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
494d1b054daSYu Zhao {
4956b136724SBjorn Helgaas 	BUG_ON(dev->sriov->num_VFs);
496dd7cc44dSYu Zhao 
497e277d2fcSYu Zhao 	if (dev != dev->sriov->dev)
498d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
499d1b054daSYu Zhao 
500e277d2fcSYu Zhao 	mutex_destroy(&dev->sriov->lock);
501e277d2fcSYu Zhao 
502d1b054daSYu Zhao 	kfree(dev->sriov);
503d1b054daSYu Zhao 	dev->sriov = NULL;
504d1b054daSYu Zhao }
505d1b054daSYu Zhao 
5068c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
5078c5cdb6aSYu Zhao {
5088c5cdb6aSYu Zhao 	int i;
5098c5cdb6aSYu Zhao 	u16 ctrl;
5108c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
5118c5cdb6aSYu Zhao 
5128c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
5138c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
5148c5cdb6aSYu Zhao 		return;
5158c5cdb6aSYu Zhao 
5168c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
5178c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
5188c5cdb6aSYu Zhao 
5198c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
520f59dca27SWei Yang 	pci_iov_set_numvfs(dev, iov->num_VFs);
5218c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
5228c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
5238c5cdb6aSYu Zhao 		msleep(100);
5248c5cdb6aSYu Zhao }
5258c5cdb6aSYu Zhao 
526d1b054daSYu Zhao /**
527d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
528d1b054daSYu Zhao  * @dev: the PCI device
529d1b054daSYu Zhao  *
530d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
531d1b054daSYu Zhao  */
532d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
533d1b054daSYu Zhao {
534d1b054daSYu Zhao 	int pos;
535d1b054daSYu Zhao 
5365f4d91a1SKenji Kaneshige 	if (!pci_is_pcie(dev))
537d1b054daSYu Zhao 		return -ENODEV;
538d1b054daSYu Zhao 
539d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
540d1b054daSYu Zhao 	if (pos)
541d1b054daSYu Zhao 		return sriov_init(dev, pos);
542d1b054daSYu Zhao 
543d1b054daSYu Zhao 	return -ENODEV;
544d1b054daSYu Zhao }
545d1b054daSYu Zhao 
546d1b054daSYu Zhao /**
547d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
548d1b054daSYu Zhao  * @dev: the PCI device
549d1b054daSYu Zhao  */
550d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
551d1b054daSYu Zhao {
552d1b054daSYu Zhao 	if (dev->is_physfn)
553d1b054daSYu Zhao 		sriov_release(dev);
554d1b054daSYu Zhao }
555d1b054daSYu Zhao 
556d1b054daSYu Zhao /**
557d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
558d1b054daSYu Zhao  * @dev: the PCI device
559d1b054daSYu Zhao  * @resno: the resource number
560d1b054daSYu Zhao  *
561d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
562d1b054daSYu Zhao  */
56326ff46c6SMyron Stowe int pci_iov_resource_bar(struct pci_dev *dev, int resno)
564d1b054daSYu Zhao {
565d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
566d1b054daSYu Zhao 		return 0;
567d1b054daSYu Zhao 
568d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
569d1b054daSYu Zhao 
570d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
571d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
572d1b054daSYu Zhao }
5738c5cdb6aSYu Zhao 
574978d2d68SWei Yang resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
575978d2d68SWei Yang 						      int resno)
576978d2d68SWei Yang {
577978d2d68SWei Yang 	return pci_iov_resource_size(dev, resno);
578978d2d68SWei Yang }
579978d2d68SWei Yang 
5808c5cdb6aSYu Zhao /**
5816faf17f6SChris Wright  * pci_sriov_resource_alignment - get resource alignment for VF BAR
5826faf17f6SChris Wright  * @dev: the PCI device
5836faf17f6SChris Wright  * @resno: the resource number
5846faf17f6SChris Wright  *
5856faf17f6SChris Wright  * Returns the alignment of the VF BAR found in the SR-IOV capability.
5866faf17f6SChris Wright  * This is not the same as the resource size which is defined as
5876faf17f6SChris Wright  * the VF BAR size multiplied by the number of VFs.  The alignment
5886faf17f6SChris Wright  * is just the VF BAR size.
5896faf17f6SChris Wright  */
5900e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
5916faf17f6SChris Wright {
592978d2d68SWei Yang 	return pcibios_iov_resource_alignment(dev, resno);
5936faf17f6SChris Wright }
5946faf17f6SChris Wright 
5956faf17f6SChris Wright /**
5968c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
5978c5cdb6aSYu Zhao  * @dev: the PCI device
5988c5cdb6aSYu Zhao  */
5998c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
6008c5cdb6aSYu Zhao {
6018c5cdb6aSYu Zhao 	if (dev->is_physfn)
6028c5cdb6aSYu Zhao 		sriov_restore_state(dev);
6038c5cdb6aSYu Zhao }
604a28724b0SYu Zhao 
605a28724b0SYu Zhao /**
606a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
607a28724b0SYu Zhao  * @bus: the PCI bus
608a28724b0SYu Zhao  *
609a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
610a28724b0SYu Zhao  * Functions.
611a28724b0SYu Zhao  */
612a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
613a28724b0SYu Zhao {
614a28724b0SYu Zhao 	int max = 0;
615a28724b0SYu Zhao 	struct pci_dev *dev;
616a28724b0SYu Zhao 
617a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
618a28724b0SYu Zhao 		if (!dev->is_physfn)
619a28724b0SYu Zhao 			continue;
6204449f079SWei Yang 		if (dev->sriov->max_VF_buses > max)
6214449f079SWei Yang 			max = dev->sriov->max_VF_buses;
622a28724b0SYu Zhao 	}
623a28724b0SYu Zhao 
624a28724b0SYu Zhao 	return max ? max - bus->number : 0;
625a28724b0SYu Zhao }
626dd7cc44dSYu Zhao 
627dd7cc44dSYu Zhao /**
628dd7cc44dSYu Zhao  * pci_enable_sriov - enable the SR-IOV capability
629dd7cc44dSYu Zhao  * @dev: the PCI device
63052a8873bSRandy Dunlap  * @nr_virtfn: number of virtual functions to enable
631dd7cc44dSYu Zhao  *
632dd7cc44dSYu Zhao  * Returns 0 on success, or negative on failure.
633dd7cc44dSYu Zhao  */
634dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
635dd7cc44dSYu Zhao {
636dd7cc44dSYu Zhao 	might_sleep();
637dd7cc44dSYu Zhao 
638dd7cc44dSYu Zhao 	if (!dev->is_physfn)
639652d1100SStefan Assmann 		return -ENOSYS;
640dd7cc44dSYu Zhao 
641dd7cc44dSYu Zhao 	return sriov_enable(dev, nr_virtfn);
642dd7cc44dSYu Zhao }
643dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov);
644dd7cc44dSYu Zhao 
645dd7cc44dSYu Zhao /**
646dd7cc44dSYu Zhao  * pci_disable_sriov - disable the SR-IOV capability
647dd7cc44dSYu Zhao  * @dev: the PCI device
648dd7cc44dSYu Zhao  */
649dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev)
650dd7cc44dSYu Zhao {
651dd7cc44dSYu Zhao 	might_sleep();
652dd7cc44dSYu Zhao 
653dd7cc44dSYu Zhao 	if (!dev->is_physfn)
654dd7cc44dSYu Zhao 		return;
655dd7cc44dSYu Zhao 
656dd7cc44dSYu Zhao 	sriov_disable(dev);
657dd7cc44dSYu Zhao }
658dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov);
65974bb1bccSYu Zhao 
66074bb1bccSYu Zhao /**
661fb8a0d9dSWilliams, Mitch A  * pci_num_vf - return number of VFs associated with a PF device_release_driver
662fb8a0d9dSWilliams, Mitch A  * @dev: the PCI device
663fb8a0d9dSWilliams, Mitch A  *
664fb8a0d9dSWilliams, Mitch A  * Returns number of VFs, or 0 if SR-IOV is not enabled.
665fb8a0d9dSWilliams, Mitch A  */
666fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev)
667fb8a0d9dSWilliams, Mitch A {
6681452cd76SBjorn Helgaas 	if (!dev->is_physfn)
669fb8a0d9dSWilliams, Mitch A 		return 0;
6701452cd76SBjorn Helgaas 
6716b136724SBjorn Helgaas 	return dev->sriov->num_VFs;
672fb8a0d9dSWilliams, Mitch A }
673fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf);
674bff73156SDonald Dutile 
675bff73156SDonald Dutile /**
6765a8eb242SAlexander Duyck  * pci_vfs_assigned - returns number of VFs are assigned to a guest
6775a8eb242SAlexander Duyck  * @dev: the PCI device
6785a8eb242SAlexander Duyck  *
6795a8eb242SAlexander Duyck  * Returns number of VFs belonging to this device that are assigned to a guest.
680652d1100SStefan Assmann  * If device is not a physical function returns 0.
6815a8eb242SAlexander Duyck  */
6825a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev)
6835a8eb242SAlexander Duyck {
6845a8eb242SAlexander Duyck 	struct pci_dev *vfdev;
6855a8eb242SAlexander Duyck 	unsigned int vfs_assigned = 0;
6865a8eb242SAlexander Duyck 	unsigned short dev_id;
6875a8eb242SAlexander Duyck 
6885a8eb242SAlexander Duyck 	/* only search if we are a PF */
6895a8eb242SAlexander Duyck 	if (!dev->is_physfn)
6905a8eb242SAlexander Duyck 		return 0;
6915a8eb242SAlexander Duyck 
6925a8eb242SAlexander Duyck 	/*
6935a8eb242SAlexander Duyck 	 * determine the device ID for the VFs, the vendor ID will be the
6945a8eb242SAlexander Duyck 	 * same as the PF so there is no need to check for that one
6955a8eb242SAlexander Duyck 	 */
6965a8eb242SAlexander Duyck 	pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
6975a8eb242SAlexander Duyck 
6985a8eb242SAlexander Duyck 	/* loop through all the VFs to see if we own any that are assigned */
6995a8eb242SAlexander Duyck 	vfdev = pci_get_device(dev->vendor, dev_id, NULL);
7005a8eb242SAlexander Duyck 	while (vfdev) {
7015a8eb242SAlexander Duyck 		/*
7025a8eb242SAlexander Duyck 		 * It is considered assigned if it is a virtual function with
7035a8eb242SAlexander Duyck 		 * our dev as the physical function and the assigned bit is set
7045a8eb242SAlexander Duyck 		 */
7055a8eb242SAlexander Duyck 		if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
706be63497cSEthan Zhao 			pci_is_dev_assigned(vfdev))
7075a8eb242SAlexander Duyck 			vfs_assigned++;
7085a8eb242SAlexander Duyck 
7095a8eb242SAlexander Duyck 		vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
7105a8eb242SAlexander Duyck 	}
7115a8eb242SAlexander Duyck 
7125a8eb242SAlexander Duyck 	return vfs_assigned;
7135a8eb242SAlexander Duyck }
7145a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned);
7155a8eb242SAlexander Duyck 
7165a8eb242SAlexander Duyck /**
717bff73156SDonald Dutile  * pci_sriov_set_totalvfs -- reduce the TotalVFs available
718bff73156SDonald Dutile  * @dev: the PCI PF device
7192094f167SRandy Dunlap  * @numvfs: number that should be used for TotalVFs supported
720bff73156SDonald Dutile  *
721bff73156SDonald Dutile  * Should be called from PF driver's probe routine with
722bff73156SDonald Dutile  * device's mutex held.
723bff73156SDonald Dutile  *
724bff73156SDonald Dutile  * Returns 0 if PF is an SRIOV-capable device and
725652d1100SStefan Assmann  * value of numvfs valid. If not a PF return -ENOSYS;
726652d1100SStefan Assmann  * if numvfs is invalid return -EINVAL;
727bff73156SDonald Dutile  * if VFs already enabled, return -EBUSY.
728bff73156SDonald Dutile  */
729bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
730bff73156SDonald Dutile {
731652d1100SStefan Assmann 	if (!dev->is_physfn)
732652d1100SStefan Assmann 		return -ENOSYS;
733652d1100SStefan Assmann 	if (numvfs > dev->sriov->total_VFs)
734bff73156SDonald Dutile 		return -EINVAL;
735bff73156SDonald Dutile 
736bff73156SDonald Dutile 	/* Shouldn't change if VFs already enabled */
737bff73156SDonald Dutile 	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
738bff73156SDonald Dutile 		return -EBUSY;
739bff73156SDonald Dutile 	else
7406b136724SBjorn Helgaas 		dev->sriov->driver_max_VFs = numvfs;
741bff73156SDonald Dutile 
742bff73156SDonald Dutile 	return 0;
743bff73156SDonald Dutile }
744bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
745bff73156SDonald Dutile 
746bff73156SDonald Dutile /**
747ddc191f5SJonghwan Choi  * pci_sriov_get_totalvfs -- get total VFs supported on this device
748bff73156SDonald Dutile  * @dev: the PCI PF device
749bff73156SDonald Dutile  *
750bff73156SDonald Dutile  * For a PCIe device with SRIOV support, return the PCIe
7516b136724SBjorn Helgaas  * SRIOV capability value of TotalVFs or the value of driver_max_VFs
752652d1100SStefan Assmann  * if the driver reduced it.  Otherwise 0.
753bff73156SDonald Dutile  */
754bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev)
755bff73156SDonald Dutile {
7561452cd76SBjorn Helgaas 	if (!dev->is_physfn)
757652d1100SStefan Assmann 		return 0;
758bff73156SDonald Dutile 
7596b136724SBjorn Helgaas 	if (dev->sriov->driver_max_VFs)
7606b136724SBjorn Helgaas 		return dev->sriov->driver_max_VFs;
7611452cd76SBjorn Helgaas 
7626b136724SBjorn Helgaas 	return dev->sriov->total_VFs;
763bff73156SDonald Dutile }
764bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
765