xref: /linux/drivers/pci/iov.c (revision 68f8e9fa56ce6b5cb18323e8d8fa536fee0f89ca)
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 
22a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id)
23a28724b0SYu Zhao {
24a28724b0SYu Zhao 	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
25a28724b0SYu Zhao 				    dev->sriov->stride * id) >> 8);
26a28724b0SYu Zhao }
27a28724b0SYu Zhao 
28a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
29a28724b0SYu Zhao {
30a28724b0SYu Zhao 	return (dev->devfn + dev->sriov->offset +
31a28724b0SYu Zhao 		dev->sriov->stride * id) & 0xff;
32a28724b0SYu Zhao }
33a28724b0SYu Zhao 
34dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
35dd7cc44dSYu Zhao {
36dd7cc44dSYu Zhao 	struct pci_bus *child;
37dd7cc44dSYu Zhao 
38dd7cc44dSYu Zhao 	if (bus->number == busnr)
39dd7cc44dSYu Zhao 		return bus;
40dd7cc44dSYu Zhao 
41dd7cc44dSYu Zhao 	child = pci_find_bus(pci_domain_nr(bus), busnr);
42dd7cc44dSYu Zhao 	if (child)
43dd7cc44dSYu Zhao 		return child;
44dd7cc44dSYu Zhao 
45dd7cc44dSYu Zhao 	child = pci_add_new_bus(bus, NULL, busnr);
46dd7cc44dSYu Zhao 	if (!child)
47dd7cc44dSYu Zhao 		return NULL;
48dd7cc44dSYu Zhao 
49b7eac055SYinghai Lu 	pci_bus_insert_busn_res(child, busnr, busnr);
50dd7cc44dSYu Zhao 
51dd7cc44dSYu Zhao 	return child;
52dd7cc44dSYu Zhao }
53dd7cc44dSYu Zhao 
54dc087f2fSJiang Liu static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
55dd7cc44dSYu Zhao {
56dc087f2fSJiang Liu 	if (physbus != virtbus && list_empty(&virtbus->devices))
57dc087f2fSJiang Liu 		pci_remove_bus(virtbus);
58dd7cc44dSYu Zhao }
59dd7cc44dSYu Zhao 
60dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset)
61dd7cc44dSYu Zhao {
62dd7cc44dSYu Zhao 	int i;
63dc087f2fSJiang Liu 	int rc = -ENOMEM;
64dd7cc44dSYu Zhao 	u64 size;
65dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
66dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
67dd7cc44dSYu Zhao 	struct resource *res;
68dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
698b1fce04SGu Zheng 	struct pci_bus *bus;
70dd7cc44dSYu Zhao 
71dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
728b1fce04SGu Zheng 	bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
73dc087f2fSJiang Liu 	if (!bus)
74dc087f2fSJiang Liu 		goto failed;
75dc087f2fSJiang Liu 
76dc087f2fSJiang Liu 	virtfn = pci_alloc_dev(bus);
77dc087f2fSJiang Liu 	if (!virtfn)
78dc087f2fSJiang Liu 		goto failed0;
79dc087f2fSJiang Liu 
80dd7cc44dSYu Zhao 	virtfn->devfn = virtfn_devfn(dev, id);
81dd7cc44dSYu Zhao 	virtfn->vendor = dev->vendor;
82dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
83dd7cc44dSYu Zhao 	pci_setup_device(virtfn);
84dd7cc44dSYu Zhao 	virtfn->dev.parent = dev->dev.parent;
85fbf33f51SXudong Hao 	virtfn->physfn = pci_dev_get(dev);
86fbf33f51SXudong Hao 	virtfn->is_virtfn = 1;
87aa931977SAlex Williamson 	virtfn->multifunction = 0;
88dd7cc44dSYu Zhao 
89dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
90dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
91dd7cc44dSYu Zhao 		if (!res->parent)
92dd7cc44dSYu Zhao 			continue;
93dd7cc44dSYu Zhao 		virtfn->resource[i].name = pci_name(virtfn);
94dd7cc44dSYu Zhao 		virtfn->resource[i].flags = res->flags;
95dd7cc44dSYu Zhao 		size = resource_size(res);
966b136724SBjorn Helgaas 		do_div(size, iov->total_VFs);
97dd7cc44dSYu Zhao 		virtfn->resource[i].start = res->start + size * id;
98dd7cc44dSYu Zhao 		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
99dd7cc44dSYu Zhao 		rc = request_resource(res, &virtfn->resource[i]);
100dd7cc44dSYu Zhao 		BUG_ON(rc);
101dd7cc44dSYu Zhao 	}
102dd7cc44dSYu Zhao 
103dd7cc44dSYu Zhao 	if (reset)
1048c1c699fSYu Zhao 		__pci_reset_function(virtfn);
105dd7cc44dSYu Zhao 
106dd7cc44dSYu Zhao 	pci_device_add(virtfn, virtfn->bus);
107dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
108dd7cc44dSYu Zhao 
109c893d133SYijing Wang 	pci_bus_add_device(virtfn);
110dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
111dd7cc44dSYu Zhao 	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
112dd7cc44dSYu Zhao 	if (rc)
113dd7cc44dSYu Zhao 		goto failed1;
114dd7cc44dSYu Zhao 	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
115dd7cc44dSYu Zhao 	if (rc)
116dd7cc44dSYu Zhao 		goto failed2;
117dd7cc44dSYu Zhao 
118dd7cc44dSYu Zhao 	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
119dd7cc44dSYu Zhao 
120dd7cc44dSYu Zhao 	return 0;
121dd7cc44dSYu Zhao 
122dd7cc44dSYu Zhao failed2:
123dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
124dd7cc44dSYu Zhao failed1:
125dd7cc44dSYu Zhao 	pci_dev_put(dev);
126dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
127210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
128dc087f2fSJiang Liu failed0:
129dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, bus);
130dc087f2fSJiang Liu failed:
131dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
132dd7cc44dSYu Zhao 
133dd7cc44dSYu Zhao 	return rc;
134dd7cc44dSYu Zhao }
135dd7cc44dSYu Zhao 
136dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset)
137dd7cc44dSYu Zhao {
138dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
139dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
140dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
141dd7cc44dSYu Zhao 
142dc087f2fSJiang Liu 	virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
143dc087f2fSJiang Liu 					     virtfn_bus(dev, id),
144dc087f2fSJiang Liu 					     virtfn_devfn(dev, id));
145dd7cc44dSYu Zhao 	if (!virtfn)
146dd7cc44dSYu Zhao 		return;
147dd7cc44dSYu Zhao 
148dd7cc44dSYu Zhao 	if (reset) {
149dd7cc44dSYu Zhao 		device_release_driver(&virtfn->dev);
1508c1c699fSYu Zhao 		__pci_reset_function(virtfn);
151dd7cc44dSYu Zhao 	}
152dd7cc44dSYu Zhao 
153dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
154dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
15509cedbefSYinghai Lu 	/*
15609cedbefSYinghai Lu 	 * pci_stop_dev() could have been called for this virtfn already,
15709cedbefSYinghai Lu 	 * so the directory for the virtfn may have been removed before.
15809cedbefSYinghai Lu 	 * Double check to avoid spurious sysfs warnings.
15909cedbefSYinghai Lu 	 */
16009cedbefSYinghai Lu 	if (virtfn->dev.kobj.sd)
161dd7cc44dSYu Zhao 		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
162dd7cc44dSYu Zhao 
163dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
164210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
165dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, virtfn->bus);
166dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
167dd7cc44dSYu Zhao 
168dc087f2fSJiang Liu 	/* balance pci_get_domain_bus_and_slot() */
169dc087f2fSJiang Liu 	pci_dev_put(virtfn);
170dd7cc44dSYu Zhao 	pci_dev_put(dev);
171dd7cc44dSYu Zhao }
172dd7cc44dSYu Zhao 
173dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
174dd7cc44dSYu Zhao {
175dd7cc44dSYu Zhao 	int rc;
176dd7cc44dSYu Zhao 	int i, j;
177dd7cc44dSYu Zhao 	int nres;
178dd7cc44dSYu Zhao 	u16 offset, stride, initial;
179dd7cc44dSYu Zhao 	struct resource *res;
180dd7cc44dSYu Zhao 	struct pci_dev *pdev;
181dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
182bbef98abSRam Pai 	int bars = 0;
183*68f8e9faSBjorn Helgaas 	u8 bus;
184dd7cc44dSYu Zhao 
185dd7cc44dSYu Zhao 	if (!nr_virtfn)
186dd7cc44dSYu Zhao 		return 0;
187dd7cc44dSYu Zhao 
1886b136724SBjorn Helgaas 	if (iov->num_VFs)
189dd7cc44dSYu Zhao 		return -EINVAL;
190dd7cc44dSYu Zhao 
191dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
1926b136724SBjorn Helgaas 	if (initial > iov->total_VFs ||
1936b136724SBjorn Helgaas 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
194dd7cc44dSYu Zhao 		return -EIO;
195dd7cc44dSYu Zhao 
1966b136724SBjorn Helgaas 	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
197dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
198dd7cc44dSYu Zhao 		return -EINVAL;
199dd7cc44dSYu Zhao 
200dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
201dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
202dd7cc44dSYu Zhao 	if (!offset || (nr_virtfn > 1 && !stride))
203dd7cc44dSYu Zhao 		return -EIO;
204dd7cc44dSYu Zhao 
205dd7cc44dSYu Zhao 	nres = 0;
206dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
207bbef98abSRam Pai 		bars |= (1 << (i + PCI_IOV_RESOURCES));
208dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
209dd7cc44dSYu Zhao 		if (res->parent)
210dd7cc44dSYu Zhao 			nres++;
211dd7cc44dSYu Zhao 	}
212dd7cc44dSYu Zhao 	if (nres != iov->nres) {
213dd7cc44dSYu Zhao 		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
214dd7cc44dSYu Zhao 		return -ENOMEM;
215dd7cc44dSYu Zhao 	}
216dd7cc44dSYu Zhao 
217dd7cc44dSYu Zhao 	iov->offset = offset;
218dd7cc44dSYu Zhao 	iov->stride = stride;
219dd7cc44dSYu Zhao 
220*68f8e9faSBjorn Helgaas 	bus = virtfn_bus(dev, nr_virtfn - 1);
221*68f8e9faSBjorn Helgaas 	if (bus > dev->bus->busn_res.end) {
222*68f8e9faSBjorn Helgaas 		dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
223*68f8e9faSBjorn Helgaas 			nr_virtfn, bus, &dev->bus->busn_res);
224dd7cc44dSYu Zhao 		return -ENOMEM;
225dd7cc44dSYu Zhao 	}
226dd7cc44dSYu Zhao 
227bbef98abSRam Pai 	if (pci_enable_resources(dev, bars)) {
228bbef98abSRam Pai 		dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
229bbef98abSRam Pai 		return -ENOMEM;
230bbef98abSRam Pai 	}
231bbef98abSRam Pai 
232dd7cc44dSYu Zhao 	if (iov->link != dev->devfn) {
233dd7cc44dSYu Zhao 		pdev = pci_get_slot(dev->bus, iov->link);
234dd7cc44dSYu Zhao 		if (!pdev)
235dd7cc44dSYu Zhao 			return -ENODEV;
236dd7cc44dSYu Zhao 
237dc087f2fSJiang Liu 		if (!pdev->is_physfn) {
238dd7cc44dSYu Zhao 			pci_dev_put(pdev);
239652d1100SStefan Assmann 			return -ENOSYS;
240dc087f2fSJiang Liu 		}
241dd7cc44dSYu Zhao 
242dd7cc44dSYu Zhao 		rc = sysfs_create_link(&dev->dev.kobj,
243dd7cc44dSYu Zhao 					&pdev->dev.kobj, "dep_link");
244dc087f2fSJiang Liu 		pci_dev_put(pdev);
245dd7cc44dSYu Zhao 		if (rc)
246dd7cc44dSYu Zhao 			return rc;
247dd7cc44dSYu Zhao 	}
248dd7cc44dSYu Zhao 
24919b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
250dd7cc44dSYu Zhao 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
251fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
252dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
253dd7cc44dSYu Zhao 	msleep(100);
254fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
255dd7cc44dSYu Zhao 
2566b136724SBjorn Helgaas 	iov->initial_VFs = initial;
257dd7cc44dSYu Zhao 	if (nr_virtfn < initial)
258dd7cc44dSYu Zhao 		initial = nr_virtfn;
259dd7cc44dSYu Zhao 
260dd7cc44dSYu Zhao 	for (i = 0; i < initial; i++) {
261dd7cc44dSYu Zhao 		rc = virtfn_add(dev, i, 0);
262dd7cc44dSYu Zhao 		if (rc)
263dd7cc44dSYu Zhao 			goto failed;
264dd7cc44dSYu Zhao 	}
265dd7cc44dSYu Zhao 
266dd7cc44dSYu Zhao 	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
2676b136724SBjorn Helgaas 	iov->num_VFs = nr_virtfn;
268dd7cc44dSYu Zhao 
269dd7cc44dSYu Zhao 	return 0;
270dd7cc44dSYu Zhao 
271dd7cc44dSYu Zhao failed:
272dd7cc44dSYu Zhao 	for (j = 0; j < i; j++)
273dd7cc44dSYu Zhao 		virtfn_remove(dev, j, 0);
274dd7cc44dSYu Zhao 
275dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
276fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
277dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
27819b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
279dd7cc44dSYu Zhao 	ssleep(1);
280fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
281dd7cc44dSYu Zhao 
282dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
283dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
284dd7cc44dSYu Zhao 
285dd7cc44dSYu Zhao 	return rc;
286dd7cc44dSYu Zhao }
287dd7cc44dSYu Zhao 
288dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev)
289dd7cc44dSYu Zhao {
290dd7cc44dSYu Zhao 	int i;
291dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
292dd7cc44dSYu Zhao 
2936b136724SBjorn Helgaas 	if (!iov->num_VFs)
294dd7cc44dSYu Zhao 		return;
295dd7cc44dSYu Zhao 
2966b136724SBjorn Helgaas 	for (i = 0; i < iov->num_VFs; i++)
297dd7cc44dSYu Zhao 		virtfn_remove(dev, i, 0);
298dd7cc44dSYu Zhao 
299dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
300fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
301dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
302dd7cc44dSYu Zhao 	ssleep(1);
303fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
304dd7cc44dSYu Zhao 
305dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
306dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
307dd7cc44dSYu Zhao 
3086b136724SBjorn Helgaas 	iov->num_VFs = 0;
30919b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
310dd7cc44dSYu Zhao }
311dd7cc44dSYu Zhao 
312d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
313d1b054daSYu Zhao {
314d1b054daSYu Zhao 	int i;
315d1b054daSYu Zhao 	int rc;
316d1b054daSYu Zhao 	int nres;
317d1b054daSYu Zhao 	u32 pgsz;
318d1b054daSYu Zhao 	u16 ctrl, total, offset, stride;
319d1b054daSYu Zhao 	struct pci_sriov *iov;
320d1b054daSYu Zhao 	struct resource *res;
321d1b054daSYu Zhao 	struct pci_dev *pdev;
322d1b054daSYu Zhao 
32362f87c0eSYijing Wang 	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
32462f87c0eSYijing Wang 	    pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
325d1b054daSYu Zhao 		return -ENODEV;
326d1b054daSYu Zhao 
327d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
328d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
329d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
330d1b054daSYu Zhao 		ssleep(1);
331d1b054daSYu Zhao 	}
332d1b054daSYu Zhao 
333d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
334d1b054daSYu Zhao 	if (!total)
335d1b054daSYu Zhao 		return 0;
336d1b054daSYu Zhao 
337d1b054daSYu Zhao 	ctrl = 0;
338d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
339d1b054daSYu Zhao 		if (pdev->is_physfn)
340d1b054daSYu Zhao 			goto found;
341d1b054daSYu Zhao 
342d1b054daSYu Zhao 	pdev = NULL;
343d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
344d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
345d1b054daSYu Zhao 
346d1b054daSYu Zhao found:
347d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
348045cc22eSethan.zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
349d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
350d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
351d1b054daSYu Zhao 	if (!offset || (total > 1 && !stride))
352d1b054daSYu Zhao 		return -EIO;
353d1b054daSYu Zhao 
354d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
355d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
356d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
357d1b054daSYu Zhao 	if (!pgsz)
358d1b054daSYu Zhao 		return -EIO;
359d1b054daSYu Zhao 
360d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
3618161fe91SVaidyanathan Srinivasan 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
362d1b054daSYu Zhao 
363d1b054daSYu Zhao 	nres = 0;
364d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
365d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
366d1b054daSYu Zhao 		i += __pci_read_base(dev, pci_bar_unknown, res,
367d1b054daSYu Zhao 				     pos + PCI_SRIOV_BAR + i * 4);
368d1b054daSYu Zhao 		if (!res->flags)
369d1b054daSYu Zhao 			continue;
370d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
371d1b054daSYu Zhao 			rc = -EIO;
372d1b054daSYu Zhao 			goto failed;
373d1b054daSYu Zhao 		}
374d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
375d1b054daSYu Zhao 		nres++;
376d1b054daSYu Zhao 	}
377d1b054daSYu Zhao 
378d1b054daSYu Zhao 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
379d1b054daSYu Zhao 	if (!iov) {
380d1b054daSYu Zhao 		rc = -ENOMEM;
381d1b054daSYu Zhao 		goto failed;
382d1b054daSYu Zhao 	}
383d1b054daSYu Zhao 
384d1b054daSYu Zhao 	iov->pos = pos;
385d1b054daSYu Zhao 	iov->nres = nres;
386d1b054daSYu Zhao 	iov->ctrl = ctrl;
3876b136724SBjorn Helgaas 	iov->total_VFs = total;
388d1b054daSYu Zhao 	iov->offset = offset;
389d1b054daSYu Zhao 	iov->stride = stride;
390d1b054daSYu Zhao 	iov->pgsz = pgsz;
391d1b054daSYu Zhao 	iov->self = dev;
392d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
393d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
39462f87c0eSYijing Wang 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
3954d135dbeSYu Zhao 		iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
396d1b054daSYu Zhao 
397d1b054daSYu Zhao 	if (pdev)
398d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
399e277d2fcSYu Zhao 	else
400d1b054daSYu Zhao 		iov->dev = dev;
401e277d2fcSYu Zhao 
402d1b054daSYu Zhao 	mutex_init(&iov->lock);
403d1b054daSYu Zhao 
404d1b054daSYu Zhao 	dev->sriov = iov;
405d1b054daSYu Zhao 	dev->is_physfn = 1;
406d1b054daSYu Zhao 
407d1b054daSYu Zhao 	return 0;
408d1b054daSYu Zhao 
409d1b054daSYu Zhao failed:
410d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
411d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
412d1b054daSYu Zhao 		res->flags = 0;
413d1b054daSYu Zhao 	}
414d1b054daSYu Zhao 
415d1b054daSYu Zhao 	return rc;
416d1b054daSYu Zhao }
417d1b054daSYu Zhao 
418d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
419d1b054daSYu Zhao {
4206b136724SBjorn Helgaas 	BUG_ON(dev->sriov->num_VFs);
421dd7cc44dSYu Zhao 
422e277d2fcSYu Zhao 	if (dev != dev->sriov->dev)
423d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
424d1b054daSYu Zhao 
425e277d2fcSYu Zhao 	mutex_destroy(&dev->sriov->lock);
426e277d2fcSYu Zhao 
427d1b054daSYu Zhao 	kfree(dev->sriov);
428d1b054daSYu Zhao 	dev->sriov = NULL;
429d1b054daSYu Zhao }
430d1b054daSYu Zhao 
4318c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
4328c5cdb6aSYu Zhao {
4338c5cdb6aSYu Zhao 	int i;
4348c5cdb6aSYu Zhao 	u16 ctrl;
4358c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
4368c5cdb6aSYu Zhao 
4378c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
4388c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
4398c5cdb6aSYu Zhao 		return;
4408c5cdb6aSYu Zhao 
4418c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
4428c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
4438c5cdb6aSYu Zhao 
4448c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
4456b136724SBjorn Helgaas 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
4468c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
4478c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
4488c5cdb6aSYu Zhao 		msleep(100);
4498c5cdb6aSYu Zhao }
4508c5cdb6aSYu Zhao 
451d1b054daSYu Zhao /**
452d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
453d1b054daSYu Zhao  * @dev: the PCI device
454d1b054daSYu Zhao  *
455d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
456d1b054daSYu Zhao  */
457d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
458d1b054daSYu Zhao {
459d1b054daSYu Zhao 	int pos;
460d1b054daSYu Zhao 
4615f4d91a1SKenji Kaneshige 	if (!pci_is_pcie(dev))
462d1b054daSYu Zhao 		return -ENODEV;
463d1b054daSYu Zhao 
464d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
465d1b054daSYu Zhao 	if (pos)
466d1b054daSYu Zhao 		return sriov_init(dev, pos);
467d1b054daSYu Zhao 
468d1b054daSYu Zhao 	return -ENODEV;
469d1b054daSYu Zhao }
470d1b054daSYu Zhao 
471d1b054daSYu Zhao /**
472d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
473d1b054daSYu Zhao  * @dev: the PCI device
474d1b054daSYu Zhao  */
475d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
476d1b054daSYu Zhao {
477d1b054daSYu Zhao 	if (dev->is_physfn)
478d1b054daSYu Zhao 		sriov_release(dev);
479d1b054daSYu Zhao }
480d1b054daSYu Zhao 
481d1b054daSYu Zhao /**
482d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
483d1b054daSYu Zhao  * @dev: the PCI device
484d1b054daSYu Zhao  * @resno: the resource number
485d1b054daSYu Zhao  *
486d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
487d1b054daSYu Zhao  */
48826ff46c6SMyron Stowe int pci_iov_resource_bar(struct pci_dev *dev, int resno)
489d1b054daSYu Zhao {
490d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
491d1b054daSYu Zhao 		return 0;
492d1b054daSYu Zhao 
493d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
494d1b054daSYu Zhao 
495d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
496d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
497d1b054daSYu Zhao }
4988c5cdb6aSYu Zhao 
4998c5cdb6aSYu Zhao /**
5006faf17f6SChris Wright  * pci_sriov_resource_alignment - get resource alignment for VF BAR
5016faf17f6SChris Wright  * @dev: the PCI device
5026faf17f6SChris Wright  * @resno: the resource number
5036faf17f6SChris Wright  *
5046faf17f6SChris Wright  * Returns the alignment of the VF BAR found in the SR-IOV capability.
5056faf17f6SChris Wright  * This is not the same as the resource size which is defined as
5066faf17f6SChris Wright  * the VF BAR size multiplied by the number of VFs.  The alignment
5076faf17f6SChris Wright  * is just the VF BAR size.
5086faf17f6SChris Wright  */
5090e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
5106faf17f6SChris Wright {
5116faf17f6SChris Wright 	struct resource tmp;
51226ff46c6SMyron Stowe 	int reg = pci_iov_resource_bar(dev, resno);
5136faf17f6SChris Wright 
5146faf17f6SChris Wright 	if (!reg)
5156faf17f6SChris Wright 		return 0;
5166faf17f6SChris Wright 
51726ff46c6SMyron Stowe 	 __pci_read_base(dev, pci_bar_unknown, &tmp, reg);
5186faf17f6SChris Wright 	return resource_alignment(&tmp);
5196faf17f6SChris Wright }
5206faf17f6SChris Wright 
5216faf17f6SChris Wright /**
5228c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
5238c5cdb6aSYu Zhao  * @dev: the PCI device
5248c5cdb6aSYu Zhao  */
5258c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
5268c5cdb6aSYu Zhao {
5278c5cdb6aSYu Zhao 	if (dev->is_physfn)
5288c5cdb6aSYu Zhao 		sriov_restore_state(dev);
5298c5cdb6aSYu Zhao }
530a28724b0SYu Zhao 
531a28724b0SYu Zhao /**
532a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
533a28724b0SYu Zhao  * @bus: the PCI bus
534a28724b0SYu Zhao  *
535a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
536a28724b0SYu Zhao  * Functions.
537a28724b0SYu Zhao  */
538a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
539a28724b0SYu Zhao {
540a28724b0SYu Zhao 	int max = 0;
541a28724b0SYu Zhao 	u8 busnr;
542a28724b0SYu Zhao 	struct pci_dev *dev;
543a28724b0SYu Zhao 
544a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
545a28724b0SYu Zhao 		if (!dev->is_physfn)
546a28724b0SYu Zhao 			continue;
5476b136724SBjorn Helgaas 		busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
548a28724b0SYu Zhao 		if (busnr > max)
549a28724b0SYu Zhao 			max = busnr;
550a28724b0SYu Zhao 	}
551a28724b0SYu Zhao 
552a28724b0SYu Zhao 	return max ? max - bus->number : 0;
553a28724b0SYu Zhao }
554dd7cc44dSYu Zhao 
555dd7cc44dSYu Zhao /**
556dd7cc44dSYu Zhao  * pci_enable_sriov - enable the SR-IOV capability
557dd7cc44dSYu Zhao  * @dev: the PCI device
55852a8873bSRandy Dunlap  * @nr_virtfn: number of virtual functions to enable
559dd7cc44dSYu Zhao  *
560dd7cc44dSYu Zhao  * Returns 0 on success, or negative on failure.
561dd7cc44dSYu Zhao  */
562dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
563dd7cc44dSYu Zhao {
564dd7cc44dSYu Zhao 	might_sleep();
565dd7cc44dSYu Zhao 
566dd7cc44dSYu Zhao 	if (!dev->is_physfn)
567652d1100SStefan Assmann 		return -ENOSYS;
568dd7cc44dSYu Zhao 
569dd7cc44dSYu Zhao 	return sriov_enable(dev, nr_virtfn);
570dd7cc44dSYu Zhao }
571dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov);
572dd7cc44dSYu Zhao 
573dd7cc44dSYu Zhao /**
574dd7cc44dSYu Zhao  * pci_disable_sriov - disable the SR-IOV capability
575dd7cc44dSYu Zhao  * @dev: the PCI device
576dd7cc44dSYu Zhao  */
577dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev)
578dd7cc44dSYu Zhao {
579dd7cc44dSYu Zhao 	might_sleep();
580dd7cc44dSYu Zhao 
581dd7cc44dSYu Zhao 	if (!dev->is_physfn)
582dd7cc44dSYu Zhao 		return;
583dd7cc44dSYu Zhao 
584dd7cc44dSYu Zhao 	sriov_disable(dev);
585dd7cc44dSYu Zhao }
586dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov);
58774bb1bccSYu Zhao 
58874bb1bccSYu Zhao /**
589fb8a0d9dSWilliams, Mitch A  * pci_num_vf - return number of VFs associated with a PF device_release_driver
590fb8a0d9dSWilliams, Mitch A  * @dev: the PCI device
591fb8a0d9dSWilliams, Mitch A  *
592fb8a0d9dSWilliams, Mitch A  * Returns number of VFs, or 0 if SR-IOV is not enabled.
593fb8a0d9dSWilliams, Mitch A  */
594fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev)
595fb8a0d9dSWilliams, Mitch A {
5961452cd76SBjorn Helgaas 	if (!dev->is_physfn)
597fb8a0d9dSWilliams, Mitch A 		return 0;
5981452cd76SBjorn Helgaas 
5996b136724SBjorn Helgaas 	return dev->sriov->num_VFs;
600fb8a0d9dSWilliams, Mitch A }
601fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf);
602bff73156SDonald Dutile 
603bff73156SDonald Dutile /**
6045a8eb242SAlexander Duyck  * pci_vfs_assigned - returns number of VFs are assigned to a guest
6055a8eb242SAlexander Duyck  * @dev: the PCI device
6065a8eb242SAlexander Duyck  *
6075a8eb242SAlexander Duyck  * Returns number of VFs belonging to this device that are assigned to a guest.
608652d1100SStefan Assmann  * If device is not a physical function returns 0.
6095a8eb242SAlexander Duyck  */
6105a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev)
6115a8eb242SAlexander Duyck {
6125a8eb242SAlexander Duyck 	struct pci_dev *vfdev;
6135a8eb242SAlexander Duyck 	unsigned int vfs_assigned = 0;
6145a8eb242SAlexander Duyck 	unsigned short dev_id;
6155a8eb242SAlexander Duyck 
6165a8eb242SAlexander Duyck 	/* only search if we are a PF */
6175a8eb242SAlexander Duyck 	if (!dev->is_physfn)
6185a8eb242SAlexander Duyck 		return 0;
6195a8eb242SAlexander Duyck 
6205a8eb242SAlexander Duyck 	/*
6215a8eb242SAlexander Duyck 	 * determine the device ID for the VFs, the vendor ID will be the
6225a8eb242SAlexander Duyck 	 * same as the PF so there is no need to check for that one
6235a8eb242SAlexander Duyck 	 */
6245a8eb242SAlexander Duyck 	pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
6255a8eb242SAlexander Duyck 
6265a8eb242SAlexander Duyck 	/* loop through all the VFs to see if we own any that are assigned */
6275a8eb242SAlexander Duyck 	vfdev = pci_get_device(dev->vendor, dev_id, NULL);
6285a8eb242SAlexander Duyck 	while (vfdev) {
6295a8eb242SAlexander Duyck 		/*
6305a8eb242SAlexander Duyck 		 * It is considered assigned if it is a virtual function with
6315a8eb242SAlexander Duyck 		 * our dev as the physical function and the assigned bit is set
6325a8eb242SAlexander Duyck 		 */
6335a8eb242SAlexander Duyck 		if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
634be63497cSEthan Zhao 			pci_is_dev_assigned(vfdev))
6355a8eb242SAlexander Duyck 			vfs_assigned++;
6365a8eb242SAlexander Duyck 
6375a8eb242SAlexander Duyck 		vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
6385a8eb242SAlexander Duyck 	}
6395a8eb242SAlexander Duyck 
6405a8eb242SAlexander Duyck 	return vfs_assigned;
6415a8eb242SAlexander Duyck }
6425a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned);
6435a8eb242SAlexander Duyck 
6445a8eb242SAlexander Duyck /**
645bff73156SDonald Dutile  * pci_sriov_set_totalvfs -- reduce the TotalVFs available
646bff73156SDonald Dutile  * @dev: the PCI PF device
6472094f167SRandy Dunlap  * @numvfs: number that should be used for TotalVFs supported
648bff73156SDonald Dutile  *
649bff73156SDonald Dutile  * Should be called from PF driver's probe routine with
650bff73156SDonald Dutile  * device's mutex held.
651bff73156SDonald Dutile  *
652bff73156SDonald Dutile  * Returns 0 if PF is an SRIOV-capable device and
653652d1100SStefan Assmann  * value of numvfs valid. If not a PF return -ENOSYS;
654652d1100SStefan Assmann  * if numvfs is invalid return -EINVAL;
655bff73156SDonald Dutile  * if VFs already enabled, return -EBUSY.
656bff73156SDonald Dutile  */
657bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
658bff73156SDonald Dutile {
659652d1100SStefan Assmann 	if (!dev->is_physfn)
660652d1100SStefan Assmann 		return -ENOSYS;
661652d1100SStefan Assmann 	if (numvfs > dev->sriov->total_VFs)
662bff73156SDonald Dutile 		return -EINVAL;
663bff73156SDonald Dutile 
664bff73156SDonald Dutile 	/* Shouldn't change if VFs already enabled */
665bff73156SDonald Dutile 	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
666bff73156SDonald Dutile 		return -EBUSY;
667bff73156SDonald Dutile 	else
6686b136724SBjorn Helgaas 		dev->sriov->driver_max_VFs = numvfs;
669bff73156SDonald Dutile 
670bff73156SDonald Dutile 	return 0;
671bff73156SDonald Dutile }
672bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
673bff73156SDonald Dutile 
674bff73156SDonald Dutile /**
675ddc191f5SJonghwan Choi  * pci_sriov_get_totalvfs -- get total VFs supported on this device
676bff73156SDonald Dutile  * @dev: the PCI PF device
677bff73156SDonald Dutile  *
678bff73156SDonald Dutile  * For a PCIe device with SRIOV support, return the PCIe
6796b136724SBjorn Helgaas  * SRIOV capability value of TotalVFs or the value of driver_max_VFs
680652d1100SStefan Assmann  * if the driver reduced it.  Otherwise 0.
681bff73156SDonald Dutile  */
682bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev)
683bff73156SDonald Dutile {
6841452cd76SBjorn Helgaas 	if (!dev->is_physfn)
685652d1100SStefan Assmann 		return 0;
686bff73156SDonald Dutile 
6876b136724SBjorn Helgaas 	if (dev->sriov->driver_max_VFs)
6886b136724SBjorn Helgaas 		return dev->sriov->driver_max_VFs;
6891452cd76SBjorn Helgaas 
6906b136724SBjorn Helgaas 	return dev->sriov->total_VFs;
691bff73156SDonald Dutile }
692bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
693