xref: /linux/drivers/pci/iov.c (revision dd7cc44d0bcec5e9c42fe52e88dc254ae62eac8d)
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
8d1b054daSYu Zhao  */
9d1b054daSYu Zhao 
10d1b054daSYu Zhao #include <linux/pci.h>
11d1b054daSYu Zhao #include <linux/mutex.h>
12d1b054daSYu Zhao #include <linux/string.h>
13d1b054daSYu Zhao #include <linux/delay.h>
14d1b054daSYu Zhao #include "pci.h"
15d1b054daSYu Zhao 
16*dd7cc44dSYu Zhao #define VIRTFN_ID_LEN	16
17d1b054daSYu Zhao 
18a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id)
19a28724b0SYu Zhao {
20a28724b0SYu Zhao 	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
21a28724b0SYu Zhao 				    dev->sriov->stride * id) >> 8);
22a28724b0SYu Zhao }
23a28724b0SYu Zhao 
24a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
25a28724b0SYu Zhao {
26a28724b0SYu Zhao 	return (dev->devfn + dev->sriov->offset +
27a28724b0SYu Zhao 		dev->sriov->stride * id) & 0xff;
28a28724b0SYu Zhao }
29a28724b0SYu Zhao 
30*dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
31*dd7cc44dSYu Zhao {
32*dd7cc44dSYu Zhao 	int rc;
33*dd7cc44dSYu Zhao 	struct pci_bus *child;
34*dd7cc44dSYu Zhao 
35*dd7cc44dSYu Zhao 	if (bus->number == busnr)
36*dd7cc44dSYu Zhao 		return bus;
37*dd7cc44dSYu Zhao 
38*dd7cc44dSYu Zhao 	child = pci_find_bus(pci_domain_nr(bus), busnr);
39*dd7cc44dSYu Zhao 	if (child)
40*dd7cc44dSYu Zhao 		return child;
41*dd7cc44dSYu Zhao 
42*dd7cc44dSYu Zhao 	child = pci_add_new_bus(bus, NULL, busnr);
43*dd7cc44dSYu Zhao 	if (!child)
44*dd7cc44dSYu Zhao 		return NULL;
45*dd7cc44dSYu Zhao 
46*dd7cc44dSYu Zhao 	child->subordinate = busnr;
47*dd7cc44dSYu Zhao 	child->dev.parent = bus->bridge;
48*dd7cc44dSYu Zhao 	rc = pci_bus_add_child(child);
49*dd7cc44dSYu Zhao 	if (rc) {
50*dd7cc44dSYu Zhao 		pci_remove_bus(child);
51*dd7cc44dSYu Zhao 		return NULL;
52*dd7cc44dSYu Zhao 	}
53*dd7cc44dSYu Zhao 
54*dd7cc44dSYu Zhao 	return child;
55*dd7cc44dSYu Zhao }
56*dd7cc44dSYu Zhao 
57*dd7cc44dSYu Zhao static void virtfn_remove_bus(struct pci_bus *bus, int busnr)
58*dd7cc44dSYu Zhao {
59*dd7cc44dSYu Zhao 	struct pci_bus *child;
60*dd7cc44dSYu Zhao 
61*dd7cc44dSYu Zhao 	if (bus->number == busnr)
62*dd7cc44dSYu Zhao 		return;
63*dd7cc44dSYu Zhao 
64*dd7cc44dSYu Zhao 	child = pci_find_bus(pci_domain_nr(bus), busnr);
65*dd7cc44dSYu Zhao 	BUG_ON(!child);
66*dd7cc44dSYu Zhao 
67*dd7cc44dSYu Zhao 	if (list_empty(&child->devices))
68*dd7cc44dSYu Zhao 		pci_remove_bus(child);
69*dd7cc44dSYu Zhao }
70*dd7cc44dSYu Zhao 
71*dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset)
72*dd7cc44dSYu Zhao {
73*dd7cc44dSYu Zhao 	int i;
74*dd7cc44dSYu Zhao 	int rc;
75*dd7cc44dSYu Zhao 	u64 size;
76*dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
77*dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
78*dd7cc44dSYu Zhao 	struct resource *res;
79*dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
80*dd7cc44dSYu Zhao 
81*dd7cc44dSYu Zhao 	virtfn = alloc_pci_dev();
82*dd7cc44dSYu Zhao 	if (!virtfn)
83*dd7cc44dSYu Zhao 		return -ENOMEM;
84*dd7cc44dSYu Zhao 
85*dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
86*dd7cc44dSYu Zhao 	virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
87*dd7cc44dSYu Zhao 	if (!virtfn->bus) {
88*dd7cc44dSYu Zhao 		kfree(virtfn);
89*dd7cc44dSYu Zhao 		mutex_unlock(&iov->dev->sriov->lock);
90*dd7cc44dSYu Zhao 		return -ENOMEM;
91*dd7cc44dSYu Zhao 	}
92*dd7cc44dSYu Zhao 	virtfn->devfn = virtfn_devfn(dev, id);
93*dd7cc44dSYu Zhao 	virtfn->vendor = dev->vendor;
94*dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
95*dd7cc44dSYu Zhao 	pci_setup_device(virtfn);
96*dd7cc44dSYu Zhao 	virtfn->dev.parent = dev->dev.parent;
97*dd7cc44dSYu Zhao 
98*dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
99*dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
100*dd7cc44dSYu Zhao 		if (!res->parent)
101*dd7cc44dSYu Zhao 			continue;
102*dd7cc44dSYu Zhao 		virtfn->resource[i].name = pci_name(virtfn);
103*dd7cc44dSYu Zhao 		virtfn->resource[i].flags = res->flags;
104*dd7cc44dSYu Zhao 		size = resource_size(res);
105*dd7cc44dSYu Zhao 		do_div(size, iov->total);
106*dd7cc44dSYu Zhao 		virtfn->resource[i].start = res->start + size * id;
107*dd7cc44dSYu Zhao 		virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
108*dd7cc44dSYu Zhao 		rc = request_resource(res, &virtfn->resource[i]);
109*dd7cc44dSYu Zhao 		BUG_ON(rc);
110*dd7cc44dSYu Zhao 	}
111*dd7cc44dSYu Zhao 
112*dd7cc44dSYu Zhao 	if (reset)
113*dd7cc44dSYu Zhao 		pci_execute_reset_function(virtfn);
114*dd7cc44dSYu Zhao 
115*dd7cc44dSYu Zhao 	pci_device_add(virtfn, virtfn->bus);
116*dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
117*dd7cc44dSYu Zhao 
118*dd7cc44dSYu Zhao 	virtfn->physfn = pci_dev_get(dev);
119*dd7cc44dSYu Zhao 	virtfn->is_virtfn = 1;
120*dd7cc44dSYu Zhao 
121*dd7cc44dSYu Zhao 	rc = pci_bus_add_device(virtfn);
122*dd7cc44dSYu Zhao 	if (rc)
123*dd7cc44dSYu Zhao 		goto failed1;
124*dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
125*dd7cc44dSYu Zhao 	rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
126*dd7cc44dSYu Zhao 	if (rc)
127*dd7cc44dSYu Zhao 		goto failed1;
128*dd7cc44dSYu Zhao 	rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
129*dd7cc44dSYu Zhao 	if (rc)
130*dd7cc44dSYu Zhao 		goto failed2;
131*dd7cc44dSYu Zhao 
132*dd7cc44dSYu Zhao 	kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
133*dd7cc44dSYu Zhao 
134*dd7cc44dSYu Zhao 	return 0;
135*dd7cc44dSYu Zhao 
136*dd7cc44dSYu Zhao failed2:
137*dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
138*dd7cc44dSYu Zhao failed1:
139*dd7cc44dSYu Zhao 	pci_dev_put(dev);
140*dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
141*dd7cc44dSYu Zhao 	pci_remove_bus_device(virtfn);
142*dd7cc44dSYu Zhao 	virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
143*dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
144*dd7cc44dSYu Zhao 
145*dd7cc44dSYu Zhao 	return rc;
146*dd7cc44dSYu Zhao }
147*dd7cc44dSYu Zhao 
148*dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset)
149*dd7cc44dSYu Zhao {
150*dd7cc44dSYu Zhao 	char buf[VIRTFN_ID_LEN];
151*dd7cc44dSYu Zhao 	struct pci_bus *bus;
152*dd7cc44dSYu Zhao 	struct pci_dev *virtfn;
153*dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
154*dd7cc44dSYu Zhao 
155*dd7cc44dSYu Zhao 	bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id));
156*dd7cc44dSYu Zhao 	if (!bus)
157*dd7cc44dSYu Zhao 		return;
158*dd7cc44dSYu Zhao 
159*dd7cc44dSYu Zhao 	virtfn = pci_get_slot(bus, virtfn_devfn(dev, id));
160*dd7cc44dSYu Zhao 	if (!virtfn)
161*dd7cc44dSYu Zhao 		return;
162*dd7cc44dSYu Zhao 
163*dd7cc44dSYu Zhao 	pci_dev_put(virtfn);
164*dd7cc44dSYu Zhao 
165*dd7cc44dSYu Zhao 	if (reset) {
166*dd7cc44dSYu Zhao 		device_release_driver(&virtfn->dev);
167*dd7cc44dSYu Zhao 		pci_execute_reset_function(virtfn);
168*dd7cc44dSYu Zhao 	}
169*dd7cc44dSYu Zhao 
170*dd7cc44dSYu Zhao 	sprintf(buf, "virtfn%u", id);
171*dd7cc44dSYu Zhao 	sysfs_remove_link(&dev->dev.kobj, buf);
172*dd7cc44dSYu Zhao 	sysfs_remove_link(&virtfn->dev.kobj, "physfn");
173*dd7cc44dSYu Zhao 
174*dd7cc44dSYu Zhao 	mutex_lock(&iov->dev->sriov->lock);
175*dd7cc44dSYu Zhao 	pci_remove_bus_device(virtfn);
176*dd7cc44dSYu Zhao 	virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
177*dd7cc44dSYu Zhao 	mutex_unlock(&iov->dev->sriov->lock);
178*dd7cc44dSYu Zhao 
179*dd7cc44dSYu Zhao 	pci_dev_put(dev);
180*dd7cc44dSYu Zhao }
181*dd7cc44dSYu Zhao 
182*dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
183*dd7cc44dSYu Zhao {
184*dd7cc44dSYu Zhao 	int rc;
185*dd7cc44dSYu Zhao 	int i, j;
186*dd7cc44dSYu Zhao 	int nres;
187*dd7cc44dSYu Zhao 	u16 offset, stride, initial;
188*dd7cc44dSYu Zhao 	struct resource *res;
189*dd7cc44dSYu Zhao 	struct pci_dev *pdev;
190*dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
191*dd7cc44dSYu Zhao 
192*dd7cc44dSYu Zhao 	if (!nr_virtfn)
193*dd7cc44dSYu Zhao 		return 0;
194*dd7cc44dSYu Zhao 
195*dd7cc44dSYu Zhao 	if (iov->nr_virtfn)
196*dd7cc44dSYu Zhao 		return -EINVAL;
197*dd7cc44dSYu Zhao 
198*dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
199*dd7cc44dSYu Zhao 	if (initial > iov->total ||
200*dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
201*dd7cc44dSYu Zhao 		return -EIO;
202*dd7cc44dSYu Zhao 
203*dd7cc44dSYu Zhao 	if (nr_virtfn < 0 || nr_virtfn > iov->total ||
204*dd7cc44dSYu Zhao 	    (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
205*dd7cc44dSYu Zhao 		return -EINVAL;
206*dd7cc44dSYu Zhao 
207*dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
208*dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
209*dd7cc44dSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
210*dd7cc44dSYu Zhao 	if (!offset || (nr_virtfn > 1 && !stride))
211*dd7cc44dSYu Zhao 		return -EIO;
212*dd7cc44dSYu Zhao 
213*dd7cc44dSYu Zhao 	nres = 0;
214*dd7cc44dSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
215*dd7cc44dSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
216*dd7cc44dSYu Zhao 		if (res->parent)
217*dd7cc44dSYu Zhao 			nres++;
218*dd7cc44dSYu Zhao 	}
219*dd7cc44dSYu Zhao 	if (nres != iov->nres) {
220*dd7cc44dSYu Zhao 		dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
221*dd7cc44dSYu Zhao 		return -ENOMEM;
222*dd7cc44dSYu Zhao 	}
223*dd7cc44dSYu Zhao 
224*dd7cc44dSYu Zhao 	iov->offset = offset;
225*dd7cc44dSYu Zhao 	iov->stride = stride;
226*dd7cc44dSYu Zhao 
227*dd7cc44dSYu Zhao 	if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) {
228*dd7cc44dSYu Zhao 		dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
229*dd7cc44dSYu Zhao 		return -ENOMEM;
230*dd7cc44dSYu Zhao 	}
231*dd7cc44dSYu Zhao 
232*dd7cc44dSYu Zhao 	if (iov->link != dev->devfn) {
233*dd7cc44dSYu Zhao 		pdev = pci_get_slot(dev->bus, iov->link);
234*dd7cc44dSYu Zhao 		if (!pdev)
235*dd7cc44dSYu Zhao 			return -ENODEV;
236*dd7cc44dSYu Zhao 
237*dd7cc44dSYu Zhao 		pci_dev_put(pdev);
238*dd7cc44dSYu Zhao 
239*dd7cc44dSYu Zhao 		if (!pdev->is_physfn)
240*dd7cc44dSYu Zhao 			return -ENODEV;
241*dd7cc44dSYu Zhao 
242*dd7cc44dSYu Zhao 		rc = sysfs_create_link(&dev->dev.kobj,
243*dd7cc44dSYu Zhao 					&pdev->dev.kobj, "dep_link");
244*dd7cc44dSYu Zhao 		if (rc)
245*dd7cc44dSYu Zhao 			return rc;
246*dd7cc44dSYu Zhao 	}
247*dd7cc44dSYu Zhao 
248*dd7cc44dSYu Zhao 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
249*dd7cc44dSYu Zhao 	pci_block_user_cfg_access(dev);
250*dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
251*dd7cc44dSYu Zhao 	msleep(100);
252*dd7cc44dSYu Zhao 	pci_unblock_user_cfg_access(dev);
253*dd7cc44dSYu Zhao 
254*dd7cc44dSYu Zhao 	iov->initial = initial;
255*dd7cc44dSYu Zhao 	if (nr_virtfn < initial)
256*dd7cc44dSYu Zhao 		initial = nr_virtfn;
257*dd7cc44dSYu Zhao 
258*dd7cc44dSYu Zhao 	for (i = 0; i < initial; i++) {
259*dd7cc44dSYu Zhao 		rc = virtfn_add(dev, i, 0);
260*dd7cc44dSYu Zhao 		if (rc)
261*dd7cc44dSYu Zhao 			goto failed;
262*dd7cc44dSYu Zhao 	}
263*dd7cc44dSYu Zhao 
264*dd7cc44dSYu Zhao 	kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
265*dd7cc44dSYu Zhao 	iov->nr_virtfn = nr_virtfn;
266*dd7cc44dSYu Zhao 
267*dd7cc44dSYu Zhao 	return 0;
268*dd7cc44dSYu Zhao 
269*dd7cc44dSYu Zhao failed:
270*dd7cc44dSYu Zhao 	for (j = 0; j < i; j++)
271*dd7cc44dSYu Zhao 		virtfn_remove(dev, j, 0);
272*dd7cc44dSYu Zhao 
273*dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
274*dd7cc44dSYu Zhao 	pci_block_user_cfg_access(dev);
275*dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
276*dd7cc44dSYu Zhao 	ssleep(1);
277*dd7cc44dSYu Zhao 	pci_unblock_user_cfg_access(dev);
278*dd7cc44dSYu Zhao 
279*dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
280*dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
281*dd7cc44dSYu Zhao 
282*dd7cc44dSYu Zhao 	return rc;
283*dd7cc44dSYu Zhao }
284*dd7cc44dSYu Zhao 
285*dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev)
286*dd7cc44dSYu Zhao {
287*dd7cc44dSYu Zhao 	int i;
288*dd7cc44dSYu Zhao 	struct pci_sriov *iov = dev->sriov;
289*dd7cc44dSYu Zhao 
290*dd7cc44dSYu Zhao 	if (!iov->nr_virtfn)
291*dd7cc44dSYu Zhao 		return;
292*dd7cc44dSYu Zhao 
293*dd7cc44dSYu Zhao 	for (i = 0; i < iov->nr_virtfn; i++)
294*dd7cc44dSYu Zhao 		virtfn_remove(dev, i, 0);
295*dd7cc44dSYu Zhao 
296*dd7cc44dSYu Zhao 	iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
297*dd7cc44dSYu Zhao 	pci_block_user_cfg_access(dev);
298*dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
299*dd7cc44dSYu Zhao 	ssleep(1);
300*dd7cc44dSYu Zhao 	pci_unblock_user_cfg_access(dev);
301*dd7cc44dSYu Zhao 
302*dd7cc44dSYu Zhao 	if (iov->link != dev->devfn)
303*dd7cc44dSYu Zhao 		sysfs_remove_link(&dev->dev.kobj, "dep_link");
304*dd7cc44dSYu Zhao 
305*dd7cc44dSYu Zhao 	iov->nr_virtfn = 0;
306*dd7cc44dSYu Zhao }
307*dd7cc44dSYu Zhao 
308d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
309d1b054daSYu Zhao {
310d1b054daSYu Zhao 	int i;
311d1b054daSYu Zhao 	int rc;
312d1b054daSYu Zhao 	int nres;
313d1b054daSYu Zhao 	u32 pgsz;
314d1b054daSYu Zhao 	u16 ctrl, total, offset, stride;
315d1b054daSYu Zhao 	struct pci_sriov *iov;
316d1b054daSYu Zhao 	struct resource *res;
317d1b054daSYu Zhao 	struct pci_dev *pdev;
318d1b054daSYu Zhao 
319d1b054daSYu Zhao 	if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
320d1b054daSYu Zhao 	    dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
321d1b054daSYu Zhao 		return -ENODEV;
322d1b054daSYu Zhao 
323d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
324d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
325d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
326d1b054daSYu Zhao 		ssleep(1);
327d1b054daSYu Zhao 	}
328d1b054daSYu Zhao 
329d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
330d1b054daSYu Zhao 	if (!total)
331d1b054daSYu Zhao 		return 0;
332d1b054daSYu Zhao 
333d1b054daSYu Zhao 	ctrl = 0;
334d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
335d1b054daSYu Zhao 		if (pdev->is_physfn)
336d1b054daSYu Zhao 			goto found;
337d1b054daSYu Zhao 
338d1b054daSYu Zhao 	pdev = NULL;
339d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
340d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
341d1b054daSYu Zhao 
342d1b054daSYu Zhao found:
343d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
344d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
345d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
346d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
347d1b054daSYu Zhao 	if (!offset || (total > 1 && !stride))
348d1b054daSYu Zhao 		return -EIO;
349d1b054daSYu Zhao 
350d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
351d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
352d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
353d1b054daSYu Zhao 	if (!pgsz)
354d1b054daSYu Zhao 		return -EIO;
355d1b054daSYu Zhao 
356d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
357d1b054daSYu Zhao 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
358d1b054daSYu Zhao 
359d1b054daSYu Zhao 	nres = 0;
360d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
361d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
362d1b054daSYu Zhao 		i += __pci_read_base(dev, pci_bar_unknown, res,
363d1b054daSYu Zhao 				     pos + PCI_SRIOV_BAR + i * 4);
364d1b054daSYu Zhao 		if (!res->flags)
365d1b054daSYu Zhao 			continue;
366d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
367d1b054daSYu Zhao 			rc = -EIO;
368d1b054daSYu Zhao 			goto failed;
369d1b054daSYu Zhao 		}
370d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
371d1b054daSYu Zhao 		nres++;
372d1b054daSYu Zhao 	}
373d1b054daSYu Zhao 
374d1b054daSYu Zhao 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
375d1b054daSYu Zhao 	if (!iov) {
376d1b054daSYu Zhao 		rc = -ENOMEM;
377d1b054daSYu Zhao 		goto failed;
378d1b054daSYu Zhao 	}
379d1b054daSYu Zhao 
380d1b054daSYu Zhao 	iov->pos = pos;
381d1b054daSYu Zhao 	iov->nres = nres;
382d1b054daSYu Zhao 	iov->ctrl = ctrl;
383d1b054daSYu Zhao 	iov->total = total;
384d1b054daSYu Zhao 	iov->offset = offset;
385d1b054daSYu Zhao 	iov->stride = stride;
386d1b054daSYu Zhao 	iov->pgsz = pgsz;
387d1b054daSYu Zhao 	iov->self = dev;
388d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
389d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
390d1b054daSYu Zhao 
391d1b054daSYu Zhao 	if (pdev)
392d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
393d1b054daSYu Zhao 	else {
394d1b054daSYu Zhao 		iov->dev = dev;
395d1b054daSYu Zhao 		mutex_init(&iov->lock);
396d1b054daSYu Zhao 	}
397d1b054daSYu Zhao 
398d1b054daSYu Zhao 	dev->sriov = iov;
399d1b054daSYu Zhao 	dev->is_physfn = 1;
400d1b054daSYu Zhao 
401d1b054daSYu Zhao 	return 0;
402d1b054daSYu Zhao 
403d1b054daSYu Zhao failed:
404d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
405d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
406d1b054daSYu Zhao 		res->flags = 0;
407d1b054daSYu Zhao 	}
408d1b054daSYu Zhao 
409d1b054daSYu Zhao 	return rc;
410d1b054daSYu Zhao }
411d1b054daSYu Zhao 
412d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
413d1b054daSYu Zhao {
414*dd7cc44dSYu Zhao 	BUG_ON(dev->sriov->nr_virtfn);
415*dd7cc44dSYu Zhao 
416d1b054daSYu Zhao 	if (dev == dev->sriov->dev)
417d1b054daSYu Zhao 		mutex_destroy(&dev->sriov->lock);
418d1b054daSYu Zhao 	else
419d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
420d1b054daSYu Zhao 
421d1b054daSYu Zhao 	kfree(dev->sriov);
422d1b054daSYu Zhao 	dev->sriov = NULL;
423d1b054daSYu Zhao }
424d1b054daSYu Zhao 
4258c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
4268c5cdb6aSYu Zhao {
4278c5cdb6aSYu Zhao 	int i;
4288c5cdb6aSYu Zhao 	u16 ctrl;
4298c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
4308c5cdb6aSYu Zhao 
4318c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
4328c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
4338c5cdb6aSYu Zhao 		return;
4348c5cdb6aSYu Zhao 
4358c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
4368c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
4378c5cdb6aSYu Zhao 
4388c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
439*dd7cc44dSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
4408c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
4418c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
4428c5cdb6aSYu Zhao 		msleep(100);
4438c5cdb6aSYu Zhao }
4448c5cdb6aSYu Zhao 
445d1b054daSYu Zhao /**
446d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
447d1b054daSYu Zhao  * @dev: the PCI device
448d1b054daSYu Zhao  *
449d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
450d1b054daSYu Zhao  */
451d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
452d1b054daSYu Zhao {
453d1b054daSYu Zhao 	int pos;
454d1b054daSYu Zhao 
455d1b054daSYu Zhao 	if (!dev->is_pcie)
456d1b054daSYu Zhao 		return -ENODEV;
457d1b054daSYu Zhao 
458d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
459d1b054daSYu Zhao 	if (pos)
460d1b054daSYu Zhao 		return sriov_init(dev, pos);
461d1b054daSYu Zhao 
462d1b054daSYu Zhao 	return -ENODEV;
463d1b054daSYu Zhao }
464d1b054daSYu Zhao 
465d1b054daSYu Zhao /**
466d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
467d1b054daSYu Zhao  * @dev: the PCI device
468d1b054daSYu Zhao  */
469d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
470d1b054daSYu Zhao {
471d1b054daSYu Zhao 	if (dev->is_physfn)
472d1b054daSYu Zhao 		sriov_release(dev);
473d1b054daSYu Zhao }
474d1b054daSYu Zhao 
475d1b054daSYu Zhao /**
476d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
477d1b054daSYu Zhao  * @dev: the PCI device
478d1b054daSYu Zhao  * @resno: the resource number
479d1b054daSYu Zhao  * @type: the BAR type to be filled in
480d1b054daSYu Zhao  *
481d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
482d1b054daSYu Zhao  */
483d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno,
484d1b054daSYu Zhao 			 enum pci_bar_type *type)
485d1b054daSYu Zhao {
486d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
487d1b054daSYu Zhao 		return 0;
488d1b054daSYu Zhao 
489d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
490d1b054daSYu Zhao 
491d1b054daSYu Zhao 	*type = pci_bar_unknown;
492d1b054daSYu Zhao 
493d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
494d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
495d1b054daSYu Zhao }
4968c5cdb6aSYu Zhao 
4978c5cdb6aSYu Zhao /**
4988c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
4998c5cdb6aSYu Zhao  * @dev: the PCI device
5008c5cdb6aSYu Zhao  */
5018c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
5028c5cdb6aSYu Zhao {
5038c5cdb6aSYu Zhao 	if (dev->is_physfn)
5048c5cdb6aSYu Zhao 		sriov_restore_state(dev);
5058c5cdb6aSYu Zhao }
506a28724b0SYu Zhao 
507a28724b0SYu Zhao /**
508a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
509a28724b0SYu Zhao  * @bus: the PCI bus
510a28724b0SYu Zhao  *
511a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
512a28724b0SYu Zhao  * Functions.
513a28724b0SYu Zhao  */
514a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
515a28724b0SYu Zhao {
516a28724b0SYu Zhao 	int max = 0;
517a28724b0SYu Zhao 	u8 busnr;
518a28724b0SYu Zhao 	struct pci_dev *dev;
519a28724b0SYu Zhao 
520a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
521a28724b0SYu Zhao 		if (!dev->is_physfn)
522a28724b0SYu Zhao 			continue;
523a28724b0SYu Zhao 		busnr = virtfn_bus(dev, dev->sriov->total - 1);
524a28724b0SYu Zhao 		if (busnr > max)
525a28724b0SYu Zhao 			max = busnr;
526a28724b0SYu Zhao 	}
527a28724b0SYu Zhao 
528a28724b0SYu Zhao 	return max ? max - bus->number : 0;
529a28724b0SYu Zhao }
530*dd7cc44dSYu Zhao 
531*dd7cc44dSYu Zhao /**
532*dd7cc44dSYu Zhao  * pci_enable_sriov - enable the SR-IOV capability
533*dd7cc44dSYu Zhao  * @dev: the PCI device
534*dd7cc44dSYu Zhao  *
535*dd7cc44dSYu Zhao  * Returns 0 on success, or negative on failure.
536*dd7cc44dSYu Zhao  */
537*dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
538*dd7cc44dSYu Zhao {
539*dd7cc44dSYu Zhao 	might_sleep();
540*dd7cc44dSYu Zhao 
541*dd7cc44dSYu Zhao 	if (!dev->is_physfn)
542*dd7cc44dSYu Zhao 		return -ENODEV;
543*dd7cc44dSYu Zhao 
544*dd7cc44dSYu Zhao 	return sriov_enable(dev, nr_virtfn);
545*dd7cc44dSYu Zhao }
546*dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov);
547*dd7cc44dSYu Zhao 
548*dd7cc44dSYu Zhao /**
549*dd7cc44dSYu Zhao  * pci_disable_sriov - disable the SR-IOV capability
550*dd7cc44dSYu Zhao  * @dev: the PCI device
551*dd7cc44dSYu Zhao  */
552*dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev)
553*dd7cc44dSYu Zhao {
554*dd7cc44dSYu Zhao 	might_sleep();
555*dd7cc44dSYu Zhao 
556*dd7cc44dSYu Zhao 	if (!dev->is_physfn)
557*dd7cc44dSYu Zhao 		return;
558*dd7cc44dSYu Zhao 
559*dd7cc44dSYu Zhao 	sriov_disable(dev);
560*dd7cc44dSYu Zhao }
561*dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov);
562