xref: /openbmc/linux/drivers/pci/iov.c (revision 26ff46c6)
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;
183dd7cc44dSYu Zhao 
184dd7cc44dSYu Zhao 	if (!nr_virtfn)
185dd7cc44dSYu Zhao 		return 0;
186dd7cc44dSYu Zhao 
1876b136724SBjorn Helgaas 	if (iov->num_VFs)
188dd7cc44dSYu Zhao 		return -EINVAL;
189dd7cc44dSYu Zhao 
190dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
1916b136724SBjorn Helgaas 	if (initial > iov->total_VFs ||
1926b136724SBjorn Helgaas 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
193dd7cc44dSYu Zhao 		return -EIO;
194dd7cc44dSYu Zhao 
1956b136724SBjorn Helgaas 	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
196dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
197dd7cc44dSYu Zhao 		return -EINVAL;
198dd7cc44dSYu Zhao 
199dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
200dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
201dd7cc44dSYu Zhao 	if (!offset || (nr_virtfn > 1 && !stride))
202dd7cc44dSYu Zhao 		return -EIO;
203dd7cc44dSYu Zhao 
204dd7cc44dSYu Zhao 	nres = 0;
205dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
206bbef98abSRam Pai 		bars |= (1 << (i + PCI_IOV_RESOURCES));
207dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
208dd7cc44dSYu Zhao 		if (res->parent)
209dd7cc44dSYu Zhao 			nres++;
210dd7cc44dSYu Zhao 	}
211dd7cc44dSYu Zhao 	if (nres != iov->nres) {
212dd7cc44dSYu Zhao 		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
213dd7cc44dSYu Zhao 		return -ENOMEM;
214dd7cc44dSYu Zhao 	}
215dd7cc44dSYu Zhao 
216dd7cc44dSYu Zhao 	iov->offset = offset;
217dd7cc44dSYu Zhao 	iov->stride = stride;
218dd7cc44dSYu Zhao 
219b918c62eSYinghai Lu 	if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) {
220dd7cc44dSYu Zhao 		dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
221dd7cc44dSYu Zhao 		return -ENOMEM;
222dd7cc44dSYu Zhao 	}
223dd7cc44dSYu Zhao 
224bbef98abSRam Pai 	if (pci_enable_resources(dev, bars)) {
225bbef98abSRam Pai 		dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
226bbef98abSRam Pai 		return -ENOMEM;
227bbef98abSRam Pai 	}
228bbef98abSRam Pai 
229dd7cc44dSYu Zhao 	if (iov->link != dev->devfn) {
230dd7cc44dSYu Zhao 		pdev = pci_get_slot(dev->bus, iov->link);
231dd7cc44dSYu Zhao 		if (!pdev)
232dd7cc44dSYu Zhao 			return -ENODEV;
233dd7cc44dSYu Zhao 
234dc087f2fSJiang Liu 		if (!pdev->is_physfn) {
235dd7cc44dSYu Zhao 			pci_dev_put(pdev);
236652d1100SStefan Assmann 			return -ENOSYS;
237dc087f2fSJiang Liu 		}
238dd7cc44dSYu Zhao 
239dd7cc44dSYu Zhao 		rc = sysfs_create_link(&dev->dev.kobj,
240dd7cc44dSYu Zhao 					&pdev->dev.kobj, "dep_link");
241dc087f2fSJiang Liu 		pci_dev_put(pdev);
242dd7cc44dSYu Zhao 		if (rc)
243dd7cc44dSYu Zhao 			return rc;
244dd7cc44dSYu Zhao 	}
245dd7cc44dSYu Zhao 
24619b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
247dd7cc44dSYu Zhao 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
248fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
249dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
250dd7cc44dSYu Zhao 	msleep(100);
251fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
252dd7cc44dSYu Zhao 
2536b136724SBjorn Helgaas 	iov->initial_VFs = initial;
254dd7cc44dSYu Zhao 	if (nr_virtfn < initial)
255dd7cc44dSYu Zhao 		initial = nr_virtfn;
256dd7cc44dSYu Zhao 
257dd7cc44dSYu Zhao 	for (i = 0; i < initial; i++) {
258dd7cc44dSYu Zhao 		rc = virtfn_add(dev, i, 0);
259dd7cc44dSYu Zhao 		if (rc)
260dd7cc44dSYu Zhao 			goto failed;
261dd7cc44dSYu Zhao 	}
262dd7cc44dSYu Zhao 
263dd7cc44dSYu Zhao 	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
2646b136724SBjorn Helgaas 	iov->num_VFs = nr_virtfn;
265dd7cc44dSYu Zhao 
266dd7cc44dSYu Zhao 	return 0;
267dd7cc44dSYu Zhao 
268dd7cc44dSYu Zhao failed:
269dd7cc44dSYu Zhao 	for (j = 0; j < i; j++)
270dd7cc44dSYu Zhao 		virtfn_remove(dev, j, 0);
271dd7cc44dSYu Zhao 
272dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
273fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
274dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
27519b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
276dd7cc44dSYu Zhao 	ssleep(1);
277fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
278dd7cc44dSYu Zhao 
279dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
280dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
281dd7cc44dSYu Zhao 
282dd7cc44dSYu Zhao 	return rc;
283dd7cc44dSYu Zhao }
284dd7cc44dSYu Zhao 
285dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev)
286dd7cc44dSYu Zhao {
287dd7cc44dSYu Zhao 	int i;
288dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
289dd7cc44dSYu Zhao 
2906b136724SBjorn Helgaas 	if (!iov->num_VFs)
291dd7cc44dSYu Zhao 		return;
292dd7cc44dSYu Zhao 
2936b136724SBjorn Helgaas 	for (i = 0; i < iov->num_VFs; i++)
294dd7cc44dSYu Zhao 		virtfn_remove(dev, i, 0);
295dd7cc44dSYu Zhao 
296dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
297fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
298dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
299dd7cc44dSYu Zhao 	ssleep(1);
300fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
301dd7cc44dSYu Zhao 
302dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
303dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
304dd7cc44dSYu Zhao 
3056b136724SBjorn Helgaas 	iov->num_VFs = 0;
30619b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
307dd7cc44dSYu Zhao }
308dd7cc44dSYu Zhao 
309d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
310d1b054daSYu Zhao {
311d1b054daSYu Zhao 	int i;
312d1b054daSYu Zhao 	int rc;
313d1b054daSYu Zhao 	int nres;
314d1b054daSYu Zhao 	u32 pgsz;
315d1b054daSYu Zhao 	u16 ctrl, total, offset, stride;
316d1b054daSYu Zhao 	struct pci_sriov *iov;
317d1b054daSYu Zhao 	struct resource *res;
318d1b054daSYu Zhao 	struct pci_dev *pdev;
319d1b054daSYu Zhao 
32062f87c0eSYijing Wang 	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
32162f87c0eSYijing Wang 	    pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
322d1b054daSYu Zhao 		return -ENODEV;
323d1b054daSYu Zhao 
324d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
325d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
326d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
327d1b054daSYu Zhao 		ssleep(1);
328d1b054daSYu Zhao 	}
329d1b054daSYu Zhao 
330d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
331d1b054daSYu Zhao 	if (!total)
332d1b054daSYu Zhao 		return 0;
333d1b054daSYu Zhao 
334d1b054daSYu Zhao 	ctrl = 0;
335d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
336d1b054daSYu Zhao 		if (pdev->is_physfn)
337d1b054daSYu Zhao 			goto found;
338d1b054daSYu Zhao 
339d1b054daSYu Zhao 	pdev = NULL;
340d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
341d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
342d1b054daSYu Zhao 
343d1b054daSYu Zhao found:
344d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
345045cc22eSethan.zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
346d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
347d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
348d1b054daSYu Zhao 	if (!offset || (total > 1 && !stride))
349d1b054daSYu Zhao 		return -EIO;
350d1b054daSYu Zhao 
351d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
352d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
353d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
354d1b054daSYu Zhao 	if (!pgsz)
355d1b054daSYu Zhao 		return -EIO;
356d1b054daSYu Zhao 
357d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
3588161fe91SVaidyanathan Srinivasan 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
359d1b054daSYu Zhao 
360d1b054daSYu Zhao 	nres = 0;
361d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
362d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
363d1b054daSYu Zhao 		i += __pci_read_base(dev, pci_bar_unknown, res,
364d1b054daSYu Zhao 				     pos + PCI_SRIOV_BAR + i * 4);
365d1b054daSYu Zhao 		if (!res->flags)
366d1b054daSYu Zhao 			continue;
367d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
368d1b054daSYu Zhao 			rc = -EIO;
369d1b054daSYu Zhao 			goto failed;
370d1b054daSYu Zhao 		}
371d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
372d1b054daSYu Zhao 		nres++;
373d1b054daSYu Zhao 	}
374d1b054daSYu Zhao 
375d1b054daSYu Zhao 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
376d1b054daSYu Zhao 	if (!iov) {
377d1b054daSYu Zhao 		rc = -ENOMEM;
378d1b054daSYu Zhao 		goto failed;
379d1b054daSYu Zhao 	}
380d1b054daSYu Zhao 
381d1b054daSYu Zhao 	iov->pos = pos;
382d1b054daSYu Zhao 	iov->nres = nres;
383d1b054daSYu Zhao 	iov->ctrl = ctrl;
3846b136724SBjorn Helgaas 	iov->total_VFs = total;
385d1b054daSYu Zhao 	iov->offset = offset;
386d1b054daSYu Zhao 	iov->stride = stride;
387d1b054daSYu Zhao 	iov->pgsz = pgsz;
388d1b054daSYu Zhao 	iov->self = dev;
389d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
390d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
39162f87c0eSYijing Wang 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
3924d135dbeSYu Zhao 		iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
393d1b054daSYu Zhao 
394d1b054daSYu Zhao 	if (pdev)
395d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
396e277d2fcSYu Zhao 	else
397d1b054daSYu Zhao 		iov->dev = dev;
398e277d2fcSYu Zhao 
399d1b054daSYu Zhao 	mutex_init(&iov->lock);
400d1b054daSYu Zhao 
401d1b054daSYu Zhao 	dev->sriov = iov;
402d1b054daSYu Zhao 	dev->is_physfn = 1;
403d1b054daSYu Zhao 
404d1b054daSYu Zhao 	return 0;
405d1b054daSYu Zhao 
406d1b054daSYu Zhao failed:
407d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
408d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
409d1b054daSYu Zhao 		res->flags = 0;
410d1b054daSYu Zhao 	}
411d1b054daSYu Zhao 
412d1b054daSYu Zhao 	return rc;
413d1b054daSYu Zhao }
414d1b054daSYu Zhao 
415d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
416d1b054daSYu Zhao {
4176b136724SBjorn Helgaas 	BUG_ON(dev->sriov->num_VFs);
418dd7cc44dSYu Zhao 
419e277d2fcSYu Zhao 	if (dev != dev->sriov->dev)
420d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
421d1b054daSYu Zhao 
422e277d2fcSYu Zhao 	mutex_destroy(&dev->sriov->lock);
423e277d2fcSYu Zhao 
424d1b054daSYu Zhao 	kfree(dev->sriov);
425d1b054daSYu Zhao 	dev->sriov = NULL;
426d1b054daSYu Zhao }
427d1b054daSYu Zhao 
4288c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
4298c5cdb6aSYu Zhao {
4308c5cdb6aSYu Zhao 	int i;
4318c5cdb6aSYu Zhao 	u16 ctrl;
4328c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
4338c5cdb6aSYu Zhao 
4348c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
4358c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
4368c5cdb6aSYu Zhao 		return;
4378c5cdb6aSYu Zhao 
4388c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
4398c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
4408c5cdb6aSYu Zhao 
4418c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
4426b136724SBjorn Helgaas 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
4438c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
4448c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
4458c5cdb6aSYu Zhao 		msleep(100);
4468c5cdb6aSYu Zhao }
4478c5cdb6aSYu Zhao 
448d1b054daSYu Zhao /**
449d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
450d1b054daSYu Zhao  * @dev: the PCI device
451d1b054daSYu Zhao  *
452d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
453d1b054daSYu Zhao  */
454d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
455d1b054daSYu Zhao {
456d1b054daSYu Zhao 	int pos;
457d1b054daSYu Zhao 
4585f4d91a1SKenji Kaneshige 	if (!pci_is_pcie(dev))
459d1b054daSYu Zhao 		return -ENODEV;
460d1b054daSYu Zhao 
461d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
462d1b054daSYu Zhao 	if (pos)
463d1b054daSYu Zhao 		return sriov_init(dev, pos);
464d1b054daSYu Zhao 
465d1b054daSYu Zhao 	return -ENODEV;
466d1b054daSYu Zhao }
467d1b054daSYu Zhao 
468d1b054daSYu Zhao /**
469d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
470d1b054daSYu Zhao  * @dev: the PCI device
471d1b054daSYu Zhao  */
472d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
473d1b054daSYu Zhao {
474d1b054daSYu Zhao 	if (dev->is_physfn)
475d1b054daSYu Zhao 		sriov_release(dev);
476d1b054daSYu Zhao }
477d1b054daSYu Zhao 
478d1b054daSYu Zhao /**
479d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
480d1b054daSYu Zhao  * @dev: the PCI device
481d1b054daSYu Zhao  * @resno: the resource number
482d1b054daSYu Zhao  *
483d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
484d1b054daSYu Zhao  */
48526ff46c6SMyron Stowe int pci_iov_resource_bar(struct pci_dev *dev, int resno)
486d1b054daSYu Zhao {
487d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
488d1b054daSYu Zhao 		return 0;
489d1b054daSYu Zhao 
490d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
491d1b054daSYu Zhao 
492d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
493d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
494d1b054daSYu Zhao }
4958c5cdb6aSYu Zhao 
4968c5cdb6aSYu Zhao /**
4976faf17f6SChris Wright  * pci_sriov_resource_alignment - get resource alignment for VF BAR
4986faf17f6SChris Wright  * @dev: the PCI device
4996faf17f6SChris Wright  * @resno: the resource number
5006faf17f6SChris Wright  *
5016faf17f6SChris Wright  * Returns the alignment of the VF BAR found in the SR-IOV capability.
5026faf17f6SChris Wright  * This is not the same as the resource size which is defined as
5036faf17f6SChris Wright  * the VF BAR size multiplied by the number of VFs.  The alignment
5046faf17f6SChris Wright  * is just the VF BAR size.
5056faf17f6SChris Wright  */
5060e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
5076faf17f6SChris Wright {
5086faf17f6SChris Wright 	struct resource tmp;
50926ff46c6SMyron Stowe 	int reg = pci_iov_resource_bar(dev, resno);
5106faf17f6SChris Wright 
5116faf17f6SChris Wright 	if (!reg)
5126faf17f6SChris Wright 		return 0;
5136faf17f6SChris Wright 
51426ff46c6SMyron Stowe 	 __pci_read_base(dev, pci_bar_unknown, &tmp, reg);
5156faf17f6SChris Wright 	return resource_alignment(&tmp);
5166faf17f6SChris Wright }
5176faf17f6SChris Wright 
5186faf17f6SChris Wright /**
5198c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
5208c5cdb6aSYu Zhao  * @dev: the PCI device
5218c5cdb6aSYu Zhao  */
5228c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
5238c5cdb6aSYu Zhao {
5248c5cdb6aSYu Zhao 	if (dev->is_physfn)
5258c5cdb6aSYu Zhao 		sriov_restore_state(dev);
5268c5cdb6aSYu Zhao }
527a28724b0SYu Zhao 
528a28724b0SYu Zhao /**
529a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
530a28724b0SYu Zhao  * @bus: the PCI bus
531a28724b0SYu Zhao  *
532a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
533a28724b0SYu Zhao  * Functions.
534a28724b0SYu Zhao  */
535a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
536a28724b0SYu Zhao {
537a28724b0SYu Zhao 	int max = 0;
538a28724b0SYu Zhao 	u8 busnr;
539a28724b0SYu Zhao 	struct pci_dev *dev;
540a28724b0SYu Zhao 
541a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
542a28724b0SYu Zhao 		if (!dev->is_physfn)
543a28724b0SYu Zhao 			continue;
5446b136724SBjorn Helgaas 		busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
545a28724b0SYu Zhao 		if (busnr > max)
546a28724b0SYu Zhao 			max = busnr;
547a28724b0SYu Zhao 	}
548a28724b0SYu Zhao 
549a28724b0SYu Zhao 	return max ? max - bus->number : 0;
550a28724b0SYu Zhao }
551dd7cc44dSYu Zhao 
552dd7cc44dSYu Zhao /**
553dd7cc44dSYu Zhao  * pci_enable_sriov - enable the SR-IOV capability
554dd7cc44dSYu Zhao  * @dev: the PCI device
55552a8873bSRandy Dunlap  * @nr_virtfn: number of virtual functions to enable
556dd7cc44dSYu Zhao  *
557dd7cc44dSYu Zhao  * Returns 0 on success, or negative on failure.
558dd7cc44dSYu Zhao  */
559dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
560dd7cc44dSYu Zhao {
561dd7cc44dSYu Zhao 	might_sleep();
562dd7cc44dSYu Zhao 
563dd7cc44dSYu Zhao 	if (!dev->is_physfn)
564652d1100SStefan Assmann 		return -ENOSYS;
565dd7cc44dSYu Zhao 
566dd7cc44dSYu Zhao 	return sriov_enable(dev, nr_virtfn);
567dd7cc44dSYu Zhao }
568dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov);
569dd7cc44dSYu Zhao 
570dd7cc44dSYu Zhao /**
571dd7cc44dSYu Zhao  * pci_disable_sriov - disable the SR-IOV capability
572dd7cc44dSYu Zhao  * @dev: the PCI device
573dd7cc44dSYu Zhao  */
574dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev)
575dd7cc44dSYu Zhao {
576dd7cc44dSYu Zhao 	might_sleep();
577dd7cc44dSYu Zhao 
578dd7cc44dSYu Zhao 	if (!dev->is_physfn)
579dd7cc44dSYu Zhao 		return;
580dd7cc44dSYu Zhao 
581dd7cc44dSYu Zhao 	sriov_disable(dev);
582dd7cc44dSYu Zhao }
583dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov);
58474bb1bccSYu Zhao 
58574bb1bccSYu Zhao /**
586fb8a0d9dSWilliams, Mitch A  * pci_num_vf - return number of VFs associated with a PF device_release_driver
587fb8a0d9dSWilliams, Mitch A  * @dev: the PCI device
588fb8a0d9dSWilliams, Mitch A  *
589fb8a0d9dSWilliams, Mitch A  * Returns number of VFs, or 0 if SR-IOV is not enabled.
590fb8a0d9dSWilliams, Mitch A  */
591fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev)
592fb8a0d9dSWilliams, Mitch A {
5931452cd76SBjorn Helgaas 	if (!dev->is_physfn)
594fb8a0d9dSWilliams, Mitch A 		return 0;
5951452cd76SBjorn Helgaas 
5966b136724SBjorn Helgaas 	return dev->sriov->num_VFs;
597fb8a0d9dSWilliams, Mitch A }
598fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf);
599bff73156SDonald Dutile 
600bff73156SDonald Dutile /**
6015a8eb242SAlexander Duyck  * pci_vfs_assigned - returns number of VFs are assigned to a guest
6025a8eb242SAlexander Duyck  * @dev: the PCI device
6035a8eb242SAlexander Duyck  *
6045a8eb242SAlexander Duyck  * Returns number of VFs belonging to this device that are assigned to a guest.
605652d1100SStefan Assmann  * If device is not a physical function returns 0.
6065a8eb242SAlexander Duyck  */
6075a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev)
6085a8eb242SAlexander Duyck {
6095a8eb242SAlexander Duyck 	struct pci_dev *vfdev;
6105a8eb242SAlexander Duyck 	unsigned int vfs_assigned = 0;
6115a8eb242SAlexander Duyck 	unsigned short dev_id;
6125a8eb242SAlexander Duyck 
6135a8eb242SAlexander Duyck 	/* only search if we are a PF */
6145a8eb242SAlexander Duyck 	if (!dev->is_physfn)
6155a8eb242SAlexander Duyck 		return 0;
6165a8eb242SAlexander Duyck 
6175a8eb242SAlexander Duyck 	/*
6185a8eb242SAlexander Duyck 	 * determine the device ID for the VFs, the vendor ID will be the
6195a8eb242SAlexander Duyck 	 * same as the PF so there is no need to check for that one
6205a8eb242SAlexander Duyck 	 */
6215a8eb242SAlexander Duyck 	pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
6225a8eb242SAlexander Duyck 
6235a8eb242SAlexander Duyck 	/* loop through all the VFs to see if we own any that are assigned */
6245a8eb242SAlexander Duyck 	vfdev = pci_get_device(dev->vendor, dev_id, NULL);
6255a8eb242SAlexander Duyck 	while (vfdev) {
6265a8eb242SAlexander Duyck 		/*
6275a8eb242SAlexander Duyck 		 * It is considered assigned if it is a virtual function with
6285a8eb242SAlexander Duyck 		 * our dev as the physical function and the assigned bit is set
6295a8eb242SAlexander Duyck 		 */
6305a8eb242SAlexander Duyck 		if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
631be63497cSEthan Zhao 			pci_is_dev_assigned(vfdev))
6325a8eb242SAlexander Duyck 			vfs_assigned++;
6335a8eb242SAlexander Duyck 
6345a8eb242SAlexander Duyck 		vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
6355a8eb242SAlexander Duyck 	}
6365a8eb242SAlexander Duyck 
6375a8eb242SAlexander Duyck 	return vfs_assigned;
6385a8eb242SAlexander Duyck }
6395a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned);
6405a8eb242SAlexander Duyck 
6415a8eb242SAlexander Duyck /**
642bff73156SDonald Dutile  * pci_sriov_set_totalvfs -- reduce the TotalVFs available
643bff73156SDonald Dutile  * @dev: the PCI PF device
6442094f167SRandy Dunlap  * @numvfs: number that should be used for TotalVFs supported
645bff73156SDonald Dutile  *
646bff73156SDonald Dutile  * Should be called from PF driver's probe routine with
647bff73156SDonald Dutile  * device's mutex held.
648bff73156SDonald Dutile  *
649bff73156SDonald Dutile  * Returns 0 if PF is an SRIOV-capable device and
650652d1100SStefan Assmann  * value of numvfs valid. If not a PF return -ENOSYS;
651652d1100SStefan Assmann  * if numvfs is invalid return -EINVAL;
652bff73156SDonald Dutile  * if VFs already enabled, return -EBUSY.
653bff73156SDonald Dutile  */
654bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
655bff73156SDonald Dutile {
656652d1100SStefan Assmann 	if (!dev->is_physfn)
657652d1100SStefan Assmann 		return -ENOSYS;
658652d1100SStefan Assmann 	if (numvfs > dev->sriov->total_VFs)
659bff73156SDonald Dutile 		return -EINVAL;
660bff73156SDonald Dutile 
661bff73156SDonald Dutile 	/* Shouldn't change if VFs already enabled */
662bff73156SDonald Dutile 	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
663bff73156SDonald Dutile 		return -EBUSY;
664bff73156SDonald Dutile 	else
6656b136724SBjorn Helgaas 		dev->sriov->driver_max_VFs = numvfs;
666bff73156SDonald Dutile 
667bff73156SDonald Dutile 	return 0;
668bff73156SDonald Dutile }
669bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
670bff73156SDonald Dutile 
671bff73156SDonald Dutile /**
672ddc191f5SJonghwan Choi  * pci_sriov_get_totalvfs -- get total VFs supported on this device
673bff73156SDonald Dutile  * @dev: the PCI PF device
674bff73156SDonald Dutile  *
675bff73156SDonald Dutile  * For a PCIe device with SRIOV support, return the PCIe
6766b136724SBjorn Helgaas  * SRIOV capability value of TotalVFs or the value of driver_max_VFs
677652d1100SStefan Assmann  * if the driver reduced it.  Otherwise 0.
678bff73156SDonald Dutile  */
679bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev)
680bff73156SDonald Dutile {
6811452cd76SBjorn Helgaas 	if (!dev->is_physfn)
682652d1100SStefan Assmann 		return 0;
683bff73156SDonald Dutile 
6846b136724SBjorn Helgaas 	if (dev->sriov->driver_max_VFs)
6856b136724SBjorn Helgaas 		return dev->sriov->driver_max_VFs;
6861452cd76SBjorn Helgaas 
6876b136724SBjorn Helgaas 	return dev->sriov->total_VFs;
688bff73156SDonald Dutile }
689bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
690