xref: /linux/drivers/pci/iov.c (revision 0e6c9122a6ec96d19f1db61e9750287d86b6829c)
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 
60*0e6c9122SWei Yang resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
61*0e6c9122SWei Yang {
62*0e6c9122SWei Yang 	if (!dev->is_physfn)
63*0e6c9122SWei Yang 		return 0;
64*0e6c9122SWei Yang 
65*0e6c9122SWei Yang 	return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
66*0e6c9122SWei Yang }
67*0e6c9122SWei Yang 
68dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset)
69dd7cc44dSYu Zhao {
70dd7cc44dSYu Zhao 	int i;
71dc087f2fSJiang Liu 	int rc = -ENOMEM;
72dd7cc44dSYu Zhao 	u64 size;
73dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
74dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
75dd7cc44dSYu Zhao 	struct resource *res;
76dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
778b1fce04SGu Zheng 	struct pci_bus *bus;
78dd7cc44dSYu Zhao 
79dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
808b1fce04SGu Zheng 	bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
81dc087f2fSJiang Liu 	if (!bus)
82dc087f2fSJiang Liu 		goto failed;
83dc087f2fSJiang Liu 
84dc087f2fSJiang Liu 	virtfn = pci_alloc_dev(bus);
85dc087f2fSJiang Liu 	if (!virtfn)
86dc087f2fSJiang Liu 		goto failed0;
87dc087f2fSJiang Liu 
88dd7cc44dSYu Zhao 	virtfn->devfn = virtfn_devfn(dev, id);
89dd7cc44dSYu Zhao 	virtfn->vendor = dev->vendor;
90dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
91dd7cc44dSYu Zhao 	pci_setup_device(virtfn);
92dd7cc44dSYu Zhao 	virtfn->dev.parent = dev->dev.parent;
93fbf33f51SXudong Hao 	virtfn->physfn = pci_dev_get(dev);
94fbf33f51SXudong Hao 	virtfn->is_virtfn = 1;
95aa931977SAlex Williamson 	virtfn->multifunction = 0;
96dd7cc44dSYu Zhao 
97dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
98dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
99dd7cc44dSYu Zhao 		if (!res->parent)
100dd7cc44dSYu Zhao 			continue;
101dd7cc44dSYu Zhao 		virtfn->resource[i].name = pci_name(virtfn);
102dd7cc44dSYu Zhao 		virtfn->resource[i].flags = res->flags;
103*0e6c9122SWei Yang 		size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
104dd7cc44dSYu Zhao 		virtfn->resource[i].start = res->start + size * id;
105dd7cc44dSYu Zhao 		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
106dd7cc44dSYu Zhao 		rc = request_resource(res, &virtfn->resource[i]);
107dd7cc44dSYu Zhao 		BUG_ON(rc);
108dd7cc44dSYu Zhao 	}
109dd7cc44dSYu Zhao 
110dd7cc44dSYu Zhao 	if (reset)
1118c1c699fSYu Zhao 		__pci_reset_function(virtfn);
112dd7cc44dSYu Zhao 
113dd7cc44dSYu Zhao 	pci_device_add(virtfn, virtfn->bus);
114dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
115dd7cc44dSYu Zhao 
116c893d133SYijing Wang 	pci_bus_add_device(virtfn);
117dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
118dd7cc44dSYu Zhao 	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
119dd7cc44dSYu Zhao 	if (rc)
120dd7cc44dSYu Zhao 		goto failed1;
121dd7cc44dSYu Zhao 	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
122dd7cc44dSYu Zhao 	if (rc)
123dd7cc44dSYu Zhao 		goto failed2;
124dd7cc44dSYu Zhao 
125dd7cc44dSYu Zhao 	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
126dd7cc44dSYu Zhao 
127dd7cc44dSYu Zhao 	return 0;
128dd7cc44dSYu Zhao 
129dd7cc44dSYu Zhao failed2:
130dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
131dd7cc44dSYu Zhao failed1:
132dd7cc44dSYu Zhao 	pci_dev_put(dev);
133dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
134210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
135dc087f2fSJiang Liu failed0:
136dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, bus);
137dc087f2fSJiang Liu failed:
138dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
139dd7cc44dSYu Zhao 
140dd7cc44dSYu Zhao 	return rc;
141dd7cc44dSYu Zhao }
142dd7cc44dSYu Zhao 
143dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset)
144dd7cc44dSYu Zhao {
145dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
146dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
147dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
148dd7cc44dSYu Zhao 
149dc087f2fSJiang Liu 	virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
150dc087f2fSJiang Liu 					     virtfn_bus(dev, id),
151dc087f2fSJiang Liu 					     virtfn_devfn(dev, id));
152dd7cc44dSYu Zhao 	if (!virtfn)
153dd7cc44dSYu Zhao 		return;
154dd7cc44dSYu Zhao 
155dd7cc44dSYu Zhao 	if (reset) {
156dd7cc44dSYu Zhao 		device_release_driver(&virtfn->dev);
1578c1c699fSYu Zhao 		__pci_reset_function(virtfn);
158dd7cc44dSYu Zhao 	}
159dd7cc44dSYu Zhao 
160dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
161dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
16209cedbefSYinghai Lu 	/*
16309cedbefSYinghai Lu 	 * pci_stop_dev() could have been called for this virtfn already,
16409cedbefSYinghai Lu 	 * so the directory for the virtfn may have been removed before.
16509cedbefSYinghai Lu 	 * Double check to avoid spurious sysfs warnings.
16609cedbefSYinghai Lu 	 */
16709cedbefSYinghai Lu 	if (virtfn->dev.kobj.sd)
168dd7cc44dSYu Zhao 		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
169dd7cc44dSYu Zhao 
170dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
171210647afSYinghai Lu 	pci_stop_and_remove_bus_device(virtfn);
172dc087f2fSJiang Liu 	virtfn_remove_bus(dev->bus, virtfn->bus);
173dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
174dd7cc44dSYu Zhao 
175dc087f2fSJiang Liu 	/* balance pci_get_domain_bus_and_slot() */
176dc087f2fSJiang Liu 	pci_dev_put(virtfn);
177dd7cc44dSYu Zhao 	pci_dev_put(dev);
178dd7cc44dSYu Zhao }
179dd7cc44dSYu Zhao 
180dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
181dd7cc44dSYu Zhao {
182dd7cc44dSYu Zhao 	int rc;
183dd7cc44dSYu Zhao 	int i, j;
184dd7cc44dSYu Zhao 	int nres;
185dd7cc44dSYu Zhao 	u16 offset, stride, initial;
186dd7cc44dSYu Zhao 	struct resource *res;
187dd7cc44dSYu Zhao 	struct pci_dev *pdev;
188dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
189bbef98abSRam Pai 	int bars = 0;
19068f8e9faSBjorn Helgaas 	u8 bus;
191dd7cc44dSYu Zhao 
192dd7cc44dSYu Zhao 	if (!nr_virtfn)
193dd7cc44dSYu Zhao 		return 0;
194dd7cc44dSYu Zhao 
1956b136724SBjorn Helgaas 	if (iov->num_VFs)
196dd7cc44dSYu Zhao 		return -EINVAL;
197dd7cc44dSYu Zhao 
198dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
1996b136724SBjorn Helgaas 	if (initial > iov->total_VFs ||
2006b136724SBjorn Helgaas 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
201dd7cc44dSYu Zhao 		return -EIO;
202dd7cc44dSYu Zhao 
2036b136724SBjorn Helgaas 	if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
204dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
205dd7cc44dSYu Zhao 		return -EINVAL;
206dd7cc44dSYu Zhao 
207dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
208dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
209dd7cc44dSYu Zhao 	if (!offset || (nr_virtfn > 1 && !stride))
210dd7cc44dSYu Zhao 		return -EIO;
211dd7cc44dSYu Zhao 
212dd7cc44dSYu Zhao 	nres = 0;
213dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
214bbef98abSRam Pai 		bars |= (1 << (i + PCI_IOV_RESOURCES));
215dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
216dd7cc44dSYu Zhao 		if (res->parent)
217dd7cc44dSYu Zhao 			nres++;
218dd7cc44dSYu Zhao 	}
219dd7cc44dSYu Zhao 	if (nres != iov->nres) {
220dd7cc44dSYu Zhao 		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
221dd7cc44dSYu Zhao 		return -ENOMEM;
222dd7cc44dSYu Zhao 	}
223dd7cc44dSYu Zhao 
224dd7cc44dSYu Zhao 	iov->offset = offset;
225dd7cc44dSYu Zhao 	iov->stride = stride;
226dd7cc44dSYu Zhao 
22768f8e9faSBjorn Helgaas 	bus = virtfn_bus(dev, nr_virtfn - 1);
22868f8e9faSBjorn Helgaas 	if (bus > dev->bus->busn_res.end) {
22968f8e9faSBjorn Helgaas 		dev_err(&dev->dev, "can't enable %d VFs (bus %02x out of range of %pR)\n",
23068f8e9faSBjorn Helgaas 			nr_virtfn, bus, &dev->bus->busn_res);
231dd7cc44dSYu Zhao 		return -ENOMEM;
232dd7cc44dSYu Zhao 	}
233dd7cc44dSYu Zhao 
234bbef98abSRam Pai 	if (pci_enable_resources(dev, bars)) {
235bbef98abSRam Pai 		dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
236bbef98abSRam Pai 		return -ENOMEM;
237bbef98abSRam Pai 	}
238bbef98abSRam Pai 
239dd7cc44dSYu Zhao 	if (iov->link != dev->devfn) {
240dd7cc44dSYu Zhao 		pdev = pci_get_slot(dev->bus, iov->link);
241dd7cc44dSYu Zhao 		if (!pdev)
242dd7cc44dSYu Zhao 			return -ENODEV;
243dd7cc44dSYu Zhao 
244dc087f2fSJiang Liu 		if (!pdev->is_physfn) {
245dd7cc44dSYu Zhao 			pci_dev_put(pdev);
246652d1100SStefan Assmann 			return -ENOSYS;
247dc087f2fSJiang Liu 		}
248dd7cc44dSYu Zhao 
249dd7cc44dSYu Zhao 		rc = sysfs_create_link(&dev->dev.kobj,
250dd7cc44dSYu Zhao 					&pdev->dev.kobj, "dep_link");
251dc087f2fSJiang Liu 		pci_dev_put(pdev);
252dd7cc44dSYu Zhao 		if (rc)
253dd7cc44dSYu Zhao 			return rc;
254dd7cc44dSYu Zhao 	}
255dd7cc44dSYu Zhao 
25619b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
257dd7cc44dSYu Zhao 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
258fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
259dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
260dd7cc44dSYu Zhao 	msleep(100);
261fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
262dd7cc44dSYu Zhao 
2636b136724SBjorn Helgaas 	iov->initial_VFs = initial;
264dd7cc44dSYu Zhao 	if (nr_virtfn < initial)
265dd7cc44dSYu Zhao 		initial = nr_virtfn;
266dd7cc44dSYu Zhao 
267dd7cc44dSYu Zhao 	for (i = 0; i < initial; i++) {
268dd7cc44dSYu Zhao 		rc = virtfn_add(dev, i, 0);
269dd7cc44dSYu Zhao 		if (rc)
270dd7cc44dSYu Zhao 			goto failed;
271dd7cc44dSYu Zhao 	}
272dd7cc44dSYu Zhao 
273dd7cc44dSYu Zhao 	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
2746b136724SBjorn Helgaas 	iov->num_VFs = nr_virtfn;
275dd7cc44dSYu Zhao 
276dd7cc44dSYu Zhao 	return 0;
277dd7cc44dSYu Zhao 
278dd7cc44dSYu Zhao failed:
279dd7cc44dSYu Zhao 	for (j = 0; j < i; j++)
280dd7cc44dSYu Zhao 		virtfn_remove(dev, j, 0);
281dd7cc44dSYu Zhao 
282dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
283fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
284dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
28519b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
286dd7cc44dSYu Zhao 	ssleep(1);
287fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
288dd7cc44dSYu Zhao 
289dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
290dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
291dd7cc44dSYu Zhao 
292dd7cc44dSYu Zhao 	return rc;
293dd7cc44dSYu Zhao }
294dd7cc44dSYu Zhao 
295dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev)
296dd7cc44dSYu Zhao {
297dd7cc44dSYu Zhao 	int i;
298dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
299dd7cc44dSYu Zhao 
3006b136724SBjorn Helgaas 	if (!iov->num_VFs)
301dd7cc44dSYu Zhao 		return;
302dd7cc44dSYu Zhao 
3036b136724SBjorn Helgaas 	for (i = 0; i < iov->num_VFs; i++)
304dd7cc44dSYu Zhao 		virtfn_remove(dev, i, 0);
305dd7cc44dSYu Zhao 
306dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
307fb51ccbfSJan Kiszka 	pci_cfg_access_lock(dev);
308dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
309dd7cc44dSYu Zhao 	ssleep(1);
310fb51ccbfSJan Kiszka 	pci_cfg_access_unlock(dev);
311dd7cc44dSYu Zhao 
312dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
313dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
314dd7cc44dSYu Zhao 
3156b136724SBjorn Helgaas 	iov->num_VFs = 0;
31619b6984eSYijing Wang 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
317dd7cc44dSYu Zhao }
318dd7cc44dSYu Zhao 
319d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
320d1b054daSYu Zhao {
321*0e6c9122SWei Yang 	int i, bar64;
322d1b054daSYu Zhao 	int rc;
323d1b054daSYu Zhao 	int nres;
324d1b054daSYu Zhao 	u32 pgsz;
325d1b054daSYu Zhao 	u16 ctrl, total, offset, stride;
326d1b054daSYu Zhao 	struct pci_sriov *iov;
327d1b054daSYu Zhao 	struct resource *res;
328d1b054daSYu Zhao 	struct pci_dev *pdev;
329d1b054daSYu Zhao 
33062f87c0eSYijing Wang 	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
33162f87c0eSYijing Wang 	    pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
332d1b054daSYu Zhao 		return -ENODEV;
333d1b054daSYu Zhao 
334d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
335d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
336d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
337d1b054daSYu Zhao 		ssleep(1);
338d1b054daSYu Zhao 	}
339d1b054daSYu Zhao 
340d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
341d1b054daSYu Zhao 	if (!total)
342d1b054daSYu Zhao 		return 0;
343d1b054daSYu Zhao 
344d1b054daSYu Zhao 	ctrl = 0;
345d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
346d1b054daSYu Zhao 		if (pdev->is_physfn)
347d1b054daSYu Zhao 			goto found;
348d1b054daSYu Zhao 
349d1b054daSYu Zhao 	pdev = NULL;
350d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
351d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
352d1b054daSYu Zhao 
353d1b054daSYu Zhao found:
354d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
355045cc22eSethan.zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
356d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
357d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
358d1b054daSYu Zhao 	if (!offset || (total > 1 && !stride))
359d1b054daSYu Zhao 		return -EIO;
360d1b054daSYu Zhao 
361d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
362d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
363d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
364d1b054daSYu Zhao 	if (!pgsz)
365d1b054daSYu Zhao 		return -EIO;
366d1b054daSYu Zhao 
367d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
3688161fe91SVaidyanathan Srinivasan 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
369d1b054daSYu Zhao 
370*0e6c9122SWei Yang 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
371*0e6c9122SWei Yang 	if (!iov)
372*0e6c9122SWei Yang 		return -ENOMEM;
373*0e6c9122SWei Yang 
374d1b054daSYu Zhao 	nres = 0;
375d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
376d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
377*0e6c9122SWei Yang 		bar64 = __pci_read_base(dev, pci_bar_unknown, res,
378d1b054daSYu Zhao 					pos + PCI_SRIOV_BAR + i * 4);
379d1b054daSYu Zhao 		if (!res->flags)
380d1b054daSYu Zhao 			continue;
381d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
382d1b054daSYu Zhao 			rc = -EIO;
383d1b054daSYu Zhao 			goto failed;
384d1b054daSYu Zhao 		}
385*0e6c9122SWei Yang 		iov->barsz[i] = resource_size(res);
386d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
387e88ae01dSWei Yang 		dev_info(&dev->dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n",
388e88ae01dSWei Yang 			 i, res, i, total);
389*0e6c9122SWei Yang 		i += bar64;
390d1b054daSYu Zhao 		nres++;
391d1b054daSYu Zhao 	}
392d1b054daSYu Zhao 
393d1b054daSYu Zhao 	iov->pos = pos;
394d1b054daSYu Zhao 	iov->nres = nres;
395d1b054daSYu Zhao 	iov->ctrl = ctrl;
3966b136724SBjorn Helgaas 	iov->total_VFs = total;
397d1b054daSYu Zhao 	iov->offset = offset;
398d1b054daSYu Zhao 	iov->stride = stride;
399d1b054daSYu Zhao 	iov->pgsz = pgsz;
400d1b054daSYu Zhao 	iov->self = dev;
401d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
402d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
40362f87c0eSYijing Wang 	if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
4044d135dbeSYu Zhao 		iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
405d1b054daSYu Zhao 
406d1b054daSYu Zhao 	if (pdev)
407d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
408e277d2fcSYu Zhao 	else
409d1b054daSYu Zhao 		iov->dev = dev;
410e277d2fcSYu Zhao 
411d1b054daSYu Zhao 	mutex_init(&iov->lock);
412d1b054daSYu Zhao 
413d1b054daSYu Zhao 	dev->sriov = iov;
414d1b054daSYu Zhao 	dev->is_physfn = 1;
415d1b054daSYu Zhao 
416d1b054daSYu Zhao 	return 0;
417d1b054daSYu Zhao 
418d1b054daSYu Zhao failed:
419d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
420d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
421d1b054daSYu Zhao 		res->flags = 0;
422d1b054daSYu Zhao 	}
423d1b054daSYu Zhao 
424*0e6c9122SWei Yang 	kfree(iov);
425d1b054daSYu Zhao 	return rc;
426d1b054daSYu Zhao }
427d1b054daSYu Zhao 
428d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
429d1b054daSYu Zhao {
4306b136724SBjorn Helgaas 	BUG_ON(dev->sriov->num_VFs);
431dd7cc44dSYu Zhao 
432e277d2fcSYu Zhao 	if (dev != dev->sriov->dev)
433d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
434d1b054daSYu Zhao 
435e277d2fcSYu Zhao 	mutex_destroy(&dev->sriov->lock);
436e277d2fcSYu Zhao 
437d1b054daSYu Zhao 	kfree(dev->sriov);
438d1b054daSYu Zhao 	dev->sriov = NULL;
439d1b054daSYu Zhao }
440d1b054daSYu Zhao 
4418c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
4428c5cdb6aSYu Zhao {
4438c5cdb6aSYu Zhao 	int i;
4448c5cdb6aSYu Zhao 	u16 ctrl;
4458c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
4468c5cdb6aSYu Zhao 
4478c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
4488c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
4498c5cdb6aSYu Zhao 		return;
4508c5cdb6aSYu Zhao 
4518c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
4528c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
4538c5cdb6aSYu Zhao 
4548c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
4556b136724SBjorn Helgaas 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
4568c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
4578c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
4588c5cdb6aSYu Zhao 		msleep(100);
4598c5cdb6aSYu Zhao }
4608c5cdb6aSYu Zhao 
461d1b054daSYu Zhao /**
462d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
463d1b054daSYu Zhao  * @dev: the PCI device
464d1b054daSYu Zhao  *
465d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
466d1b054daSYu Zhao  */
467d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
468d1b054daSYu Zhao {
469d1b054daSYu Zhao 	int pos;
470d1b054daSYu Zhao 
4715f4d91a1SKenji Kaneshige 	if (!pci_is_pcie(dev))
472d1b054daSYu Zhao 		return -ENODEV;
473d1b054daSYu Zhao 
474d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
475d1b054daSYu Zhao 	if (pos)
476d1b054daSYu Zhao 		return sriov_init(dev, pos);
477d1b054daSYu Zhao 
478d1b054daSYu Zhao 	return -ENODEV;
479d1b054daSYu Zhao }
480d1b054daSYu Zhao 
481d1b054daSYu Zhao /**
482d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
483d1b054daSYu Zhao  * @dev: the PCI device
484d1b054daSYu Zhao  */
485d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
486d1b054daSYu Zhao {
487d1b054daSYu Zhao 	if (dev->is_physfn)
488d1b054daSYu Zhao 		sriov_release(dev);
489d1b054daSYu Zhao }
490d1b054daSYu Zhao 
491d1b054daSYu Zhao /**
492d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
493d1b054daSYu Zhao  * @dev: the PCI device
494d1b054daSYu Zhao  * @resno: the resource number
495d1b054daSYu Zhao  *
496d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
497d1b054daSYu Zhao  */
49826ff46c6SMyron Stowe int pci_iov_resource_bar(struct pci_dev *dev, int resno)
499d1b054daSYu Zhao {
500d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
501d1b054daSYu Zhao 		return 0;
502d1b054daSYu Zhao 
503d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
504d1b054daSYu Zhao 
505d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
506d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
507d1b054daSYu Zhao }
5088c5cdb6aSYu Zhao 
5098c5cdb6aSYu Zhao /**
5106faf17f6SChris Wright  * pci_sriov_resource_alignment - get resource alignment for VF BAR
5116faf17f6SChris Wright  * @dev: the PCI device
5126faf17f6SChris Wright  * @resno: the resource number
5136faf17f6SChris Wright  *
5146faf17f6SChris Wright  * Returns the alignment of the VF BAR found in the SR-IOV capability.
5156faf17f6SChris Wright  * This is not the same as the resource size which is defined as
5166faf17f6SChris Wright  * the VF BAR size multiplied by the number of VFs.  The alignment
5176faf17f6SChris Wright  * is just the VF BAR size.
5186faf17f6SChris Wright  */
5190e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
5206faf17f6SChris Wright {
521*0e6c9122SWei Yang 	return pci_iov_resource_size(dev, resno);
5226faf17f6SChris Wright }
5236faf17f6SChris Wright 
5246faf17f6SChris Wright /**
5258c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
5268c5cdb6aSYu Zhao  * @dev: the PCI device
5278c5cdb6aSYu Zhao  */
5288c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
5298c5cdb6aSYu Zhao {
5308c5cdb6aSYu Zhao 	if (dev->is_physfn)
5318c5cdb6aSYu Zhao 		sriov_restore_state(dev);
5328c5cdb6aSYu Zhao }
533a28724b0SYu Zhao 
534a28724b0SYu Zhao /**
535a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
536a28724b0SYu Zhao  * @bus: the PCI bus
537a28724b0SYu Zhao  *
538a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
539a28724b0SYu Zhao  * Functions.
540a28724b0SYu Zhao  */
541a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
542a28724b0SYu Zhao {
543a28724b0SYu Zhao 	int max = 0;
544a28724b0SYu Zhao 	u8 busnr;
545a28724b0SYu Zhao 	struct pci_dev *dev;
546a28724b0SYu Zhao 
547a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
548a28724b0SYu Zhao 		if (!dev->is_physfn)
549a28724b0SYu Zhao 			continue;
5506b136724SBjorn Helgaas 		busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
551a28724b0SYu Zhao 		if (busnr > max)
552a28724b0SYu Zhao 			max = busnr;
553a28724b0SYu Zhao 	}
554a28724b0SYu Zhao 
555a28724b0SYu Zhao 	return max ? max - bus->number : 0;
556a28724b0SYu Zhao }
557dd7cc44dSYu Zhao 
558dd7cc44dSYu Zhao /**
559dd7cc44dSYu Zhao  * pci_enable_sriov - enable the SR-IOV capability
560dd7cc44dSYu Zhao  * @dev: the PCI device
56152a8873bSRandy Dunlap  * @nr_virtfn: number of virtual functions to enable
562dd7cc44dSYu Zhao  *
563dd7cc44dSYu Zhao  * Returns 0 on success, or negative on failure.
564dd7cc44dSYu Zhao  */
565dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
566dd7cc44dSYu Zhao {
567dd7cc44dSYu Zhao 	might_sleep();
568dd7cc44dSYu Zhao 
569dd7cc44dSYu Zhao 	if (!dev->is_physfn)
570652d1100SStefan Assmann 		return -ENOSYS;
571dd7cc44dSYu Zhao 
572dd7cc44dSYu Zhao 	return sriov_enable(dev, nr_virtfn);
573dd7cc44dSYu Zhao }
574dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov);
575dd7cc44dSYu Zhao 
576dd7cc44dSYu Zhao /**
577dd7cc44dSYu Zhao  * pci_disable_sriov - disable the SR-IOV capability
578dd7cc44dSYu Zhao  * @dev: the PCI device
579dd7cc44dSYu Zhao  */
580dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev)
581dd7cc44dSYu Zhao {
582dd7cc44dSYu Zhao 	might_sleep();
583dd7cc44dSYu Zhao 
584dd7cc44dSYu Zhao 	if (!dev->is_physfn)
585dd7cc44dSYu Zhao 		return;
586dd7cc44dSYu Zhao 
587dd7cc44dSYu Zhao 	sriov_disable(dev);
588dd7cc44dSYu Zhao }
589dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov);
59074bb1bccSYu Zhao 
59174bb1bccSYu Zhao /**
592fb8a0d9dSWilliams, Mitch A  * pci_num_vf - return number of VFs associated with a PF device_release_driver
593fb8a0d9dSWilliams, Mitch A  * @dev: the PCI device
594fb8a0d9dSWilliams, Mitch A  *
595fb8a0d9dSWilliams, Mitch A  * Returns number of VFs, or 0 if SR-IOV is not enabled.
596fb8a0d9dSWilliams, Mitch A  */
597fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev)
598fb8a0d9dSWilliams, Mitch A {
5991452cd76SBjorn Helgaas 	if (!dev->is_physfn)
600fb8a0d9dSWilliams, Mitch A 		return 0;
6011452cd76SBjorn Helgaas 
6026b136724SBjorn Helgaas 	return dev->sriov->num_VFs;
603fb8a0d9dSWilliams, Mitch A }
604fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf);
605bff73156SDonald Dutile 
606bff73156SDonald Dutile /**
6075a8eb242SAlexander Duyck  * pci_vfs_assigned - returns number of VFs are assigned to a guest
6085a8eb242SAlexander Duyck  * @dev: the PCI device
6095a8eb242SAlexander Duyck  *
6105a8eb242SAlexander Duyck  * Returns number of VFs belonging to this device that are assigned to a guest.
611652d1100SStefan Assmann  * If device is not a physical function returns 0.
6125a8eb242SAlexander Duyck  */
6135a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev)
6145a8eb242SAlexander Duyck {
6155a8eb242SAlexander Duyck 	struct pci_dev *vfdev;
6165a8eb242SAlexander Duyck 	unsigned int vfs_assigned = 0;
6175a8eb242SAlexander Duyck 	unsigned short dev_id;
6185a8eb242SAlexander Duyck 
6195a8eb242SAlexander Duyck 	/* only search if we are a PF */
6205a8eb242SAlexander Duyck 	if (!dev->is_physfn)
6215a8eb242SAlexander Duyck 		return 0;
6225a8eb242SAlexander Duyck 
6235a8eb242SAlexander Duyck 	/*
6245a8eb242SAlexander Duyck 	 * determine the device ID for the VFs, the vendor ID will be the
6255a8eb242SAlexander Duyck 	 * same as the PF so there is no need to check for that one
6265a8eb242SAlexander Duyck 	 */
6275a8eb242SAlexander Duyck 	pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
6285a8eb242SAlexander Duyck 
6295a8eb242SAlexander Duyck 	/* loop through all the VFs to see if we own any that are assigned */
6305a8eb242SAlexander Duyck 	vfdev = pci_get_device(dev->vendor, dev_id, NULL);
6315a8eb242SAlexander Duyck 	while (vfdev) {
6325a8eb242SAlexander Duyck 		/*
6335a8eb242SAlexander Duyck 		 * It is considered assigned if it is a virtual function with
6345a8eb242SAlexander Duyck 		 * our dev as the physical function and the assigned bit is set
6355a8eb242SAlexander Duyck 		 */
6365a8eb242SAlexander Duyck 		if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
637be63497cSEthan Zhao 			pci_is_dev_assigned(vfdev))
6385a8eb242SAlexander Duyck 			vfs_assigned++;
6395a8eb242SAlexander Duyck 
6405a8eb242SAlexander Duyck 		vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
6415a8eb242SAlexander Duyck 	}
6425a8eb242SAlexander Duyck 
6435a8eb242SAlexander Duyck 	return vfs_assigned;
6445a8eb242SAlexander Duyck }
6455a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned);
6465a8eb242SAlexander Duyck 
6475a8eb242SAlexander Duyck /**
648bff73156SDonald Dutile  * pci_sriov_set_totalvfs -- reduce the TotalVFs available
649bff73156SDonald Dutile  * @dev: the PCI PF device
6502094f167SRandy Dunlap  * @numvfs: number that should be used for TotalVFs supported
651bff73156SDonald Dutile  *
652bff73156SDonald Dutile  * Should be called from PF driver's probe routine with
653bff73156SDonald Dutile  * device's mutex held.
654bff73156SDonald Dutile  *
655bff73156SDonald Dutile  * Returns 0 if PF is an SRIOV-capable device and
656652d1100SStefan Assmann  * value of numvfs valid. If not a PF return -ENOSYS;
657652d1100SStefan Assmann  * if numvfs is invalid return -EINVAL;
658bff73156SDonald Dutile  * if VFs already enabled, return -EBUSY.
659bff73156SDonald Dutile  */
660bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
661bff73156SDonald Dutile {
662652d1100SStefan Assmann 	if (!dev->is_physfn)
663652d1100SStefan Assmann 		return -ENOSYS;
664652d1100SStefan Assmann 	if (numvfs > dev->sriov->total_VFs)
665bff73156SDonald Dutile 		return -EINVAL;
666bff73156SDonald Dutile 
667bff73156SDonald Dutile 	/* Shouldn't change if VFs already enabled */
668bff73156SDonald Dutile 	if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
669bff73156SDonald Dutile 		return -EBUSY;
670bff73156SDonald Dutile 	else
6716b136724SBjorn Helgaas 		dev->sriov->driver_max_VFs = numvfs;
672bff73156SDonald Dutile 
673bff73156SDonald Dutile 	return 0;
674bff73156SDonald Dutile }
675bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
676bff73156SDonald Dutile 
677bff73156SDonald Dutile /**
678ddc191f5SJonghwan Choi  * pci_sriov_get_totalvfs -- get total VFs supported on this device
679bff73156SDonald Dutile  * @dev: the PCI PF device
680bff73156SDonald Dutile  *
681bff73156SDonald Dutile  * For a PCIe device with SRIOV support, return the PCIe
6826b136724SBjorn Helgaas  * SRIOV capability value of TotalVFs or the value of driver_max_VFs
683652d1100SStefan Assmann  * if the driver reduced it.  Otherwise 0.
684bff73156SDonald Dutile  */
685bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev)
686bff73156SDonald Dutile {
6871452cd76SBjorn Helgaas 	if (!dev->is_physfn)
688652d1100SStefan Assmann 		return 0;
689bff73156SDonald Dutile 
6906b136724SBjorn Helgaas 	if (dev->sriov->driver_max_VFs)
6916b136724SBjorn Helgaas 		return dev->sriov->driver_max_VFs;
6921452cd76SBjorn Helgaas 
6936b136724SBjorn Helgaas 	return dev->sriov->total_VFs;
694bff73156SDonald Dutile }
695bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
696