xref: /linux/drivers/pci/iov.c (revision a28724b0fb909d247229a70761c90bb37b13366a)
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 
16d1b054daSYu Zhao 
17*a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id)
18*a28724b0SYu Zhao {
19*a28724b0SYu Zhao 	return dev->bus->number + ((dev->devfn + dev->sriov->offset +
20*a28724b0SYu Zhao 				    dev->sriov->stride * id) >> 8);
21*a28724b0SYu Zhao }
22*a28724b0SYu Zhao 
23*a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
24*a28724b0SYu Zhao {
25*a28724b0SYu Zhao 	return (dev->devfn + dev->sriov->offset +
26*a28724b0SYu Zhao 		dev->sriov->stride * id) & 0xff;
27*a28724b0SYu Zhao }
28*a28724b0SYu Zhao 
29d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos)
30d1b054daSYu Zhao {
31d1b054daSYu Zhao 	int i;
32d1b054daSYu Zhao 	int rc;
33d1b054daSYu Zhao 	int nres;
34d1b054daSYu Zhao 	u32 pgsz;
35d1b054daSYu Zhao 	u16 ctrl, total, offset, stride;
36d1b054daSYu Zhao 	struct pci_sriov *iov;
37d1b054daSYu Zhao 	struct resource *res;
38d1b054daSYu Zhao 	struct pci_dev *pdev;
39d1b054daSYu Zhao 
40d1b054daSYu Zhao 	if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
41d1b054daSYu Zhao 	    dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
42d1b054daSYu Zhao 		return -ENODEV;
43d1b054daSYu Zhao 
44d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
45d1b054daSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE) {
46d1b054daSYu Zhao 		pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
47d1b054daSYu Zhao 		ssleep(1);
48d1b054daSYu Zhao 	}
49d1b054daSYu Zhao 
50d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
51d1b054daSYu Zhao 	if (!total)
52d1b054daSYu Zhao 		return 0;
53d1b054daSYu Zhao 
54d1b054daSYu Zhao 	ctrl = 0;
55d1b054daSYu Zhao 	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
56d1b054daSYu Zhao 		if (pdev->is_physfn)
57d1b054daSYu Zhao 			goto found;
58d1b054daSYu Zhao 
59d1b054daSYu Zhao 	pdev = NULL;
60d1b054daSYu Zhao 	if (pci_ari_enabled(dev->bus))
61d1b054daSYu Zhao 		ctrl |= PCI_SRIOV_CTRL_ARI;
62d1b054daSYu Zhao 
63d1b054daSYu Zhao found:
64d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
65d1b054daSYu Zhao 	pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
66d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
67d1b054daSYu Zhao 	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
68d1b054daSYu Zhao 	if (!offset || (total > 1 && !stride))
69d1b054daSYu Zhao 		return -EIO;
70d1b054daSYu Zhao 
71d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
72d1b054daSYu Zhao 	i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
73d1b054daSYu Zhao 	pgsz &= ~((1 << i) - 1);
74d1b054daSYu Zhao 	if (!pgsz)
75d1b054daSYu Zhao 		return -EIO;
76d1b054daSYu Zhao 
77d1b054daSYu Zhao 	pgsz &= ~(pgsz - 1);
78d1b054daSYu Zhao 	pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
79d1b054daSYu Zhao 
80d1b054daSYu Zhao 	nres = 0;
81d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
82d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
83d1b054daSYu Zhao 		i += __pci_read_base(dev, pci_bar_unknown, res,
84d1b054daSYu Zhao 				     pos + PCI_SRIOV_BAR + i * 4);
85d1b054daSYu Zhao 		if (!res->flags)
86d1b054daSYu Zhao 			continue;
87d1b054daSYu Zhao 		if (resource_size(res) & (PAGE_SIZE - 1)) {
88d1b054daSYu Zhao 			rc = -EIO;
89d1b054daSYu Zhao 			goto failed;
90d1b054daSYu Zhao 		}
91d1b054daSYu Zhao 		res->end = res->start + resource_size(res) * total - 1;
92d1b054daSYu Zhao 		nres++;
93d1b054daSYu Zhao 	}
94d1b054daSYu Zhao 
95d1b054daSYu Zhao 	iov = kzalloc(sizeof(*iov), GFP_KERNEL);
96d1b054daSYu Zhao 	if (!iov) {
97d1b054daSYu Zhao 		rc = -ENOMEM;
98d1b054daSYu Zhao 		goto failed;
99d1b054daSYu Zhao 	}
100d1b054daSYu Zhao 
101d1b054daSYu Zhao 	iov->pos = pos;
102d1b054daSYu Zhao 	iov->nres = nres;
103d1b054daSYu Zhao 	iov->ctrl = ctrl;
104d1b054daSYu Zhao 	iov->total = total;
105d1b054daSYu Zhao 	iov->offset = offset;
106d1b054daSYu Zhao 	iov->stride = stride;
107d1b054daSYu Zhao 	iov->pgsz = pgsz;
108d1b054daSYu Zhao 	iov->self = dev;
109d1b054daSYu Zhao 	pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
110d1b054daSYu Zhao 	pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
111d1b054daSYu Zhao 
112d1b054daSYu Zhao 	if (pdev)
113d1b054daSYu Zhao 		iov->dev = pci_dev_get(pdev);
114d1b054daSYu Zhao 	else {
115d1b054daSYu Zhao 		iov->dev = dev;
116d1b054daSYu Zhao 		mutex_init(&iov->lock);
117d1b054daSYu Zhao 	}
118d1b054daSYu Zhao 
119d1b054daSYu Zhao 	dev->sriov = iov;
120d1b054daSYu Zhao 	dev->is_physfn = 1;
121d1b054daSYu Zhao 
122d1b054daSYu Zhao 	return 0;
123d1b054daSYu Zhao 
124d1b054daSYu Zhao failed:
125d1b054daSYu Zhao 	for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
126d1b054daSYu Zhao 		res = dev->resource + PCI_IOV_RESOURCES + i;
127d1b054daSYu Zhao 		res->flags = 0;
128d1b054daSYu Zhao 	}
129d1b054daSYu Zhao 
130d1b054daSYu Zhao 	return rc;
131d1b054daSYu Zhao }
132d1b054daSYu Zhao 
133d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev)
134d1b054daSYu Zhao {
135d1b054daSYu Zhao 	if (dev == dev->sriov->dev)
136d1b054daSYu Zhao 		mutex_destroy(&dev->sriov->lock);
137d1b054daSYu Zhao 	else
138d1b054daSYu Zhao 		pci_dev_put(dev->sriov->dev);
139d1b054daSYu Zhao 
140d1b054daSYu Zhao 	kfree(dev->sriov);
141d1b054daSYu Zhao 	dev->sriov = NULL;
142d1b054daSYu Zhao }
143d1b054daSYu Zhao 
1448c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev)
1458c5cdb6aSYu Zhao {
1468c5cdb6aSYu Zhao 	int i;
1478c5cdb6aSYu Zhao 	u16 ctrl;
1488c5cdb6aSYu Zhao 	struct pci_sriov *iov = dev->sriov;
1498c5cdb6aSYu Zhao 
1508c5cdb6aSYu Zhao 	pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
1518c5cdb6aSYu Zhao 	if (ctrl & PCI_SRIOV_CTRL_VFE)
1528c5cdb6aSYu Zhao 		return;
1538c5cdb6aSYu Zhao 
1548c5cdb6aSYu Zhao 	for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
1558c5cdb6aSYu Zhao 		pci_update_resource(dev, i);
1568c5cdb6aSYu Zhao 
1578c5cdb6aSYu Zhao 	pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
1588c5cdb6aSYu Zhao 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
1598c5cdb6aSYu Zhao 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
1608c5cdb6aSYu Zhao 		msleep(100);
1618c5cdb6aSYu Zhao }
1628c5cdb6aSYu Zhao 
163d1b054daSYu Zhao /**
164d1b054daSYu Zhao  * pci_iov_init - initialize the IOV capability
165d1b054daSYu Zhao  * @dev: the PCI device
166d1b054daSYu Zhao  *
167d1b054daSYu Zhao  * Returns 0 on success, or negative on failure.
168d1b054daSYu Zhao  */
169d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev)
170d1b054daSYu Zhao {
171d1b054daSYu Zhao 	int pos;
172d1b054daSYu Zhao 
173d1b054daSYu Zhao 	if (!dev->is_pcie)
174d1b054daSYu Zhao 		return -ENODEV;
175d1b054daSYu Zhao 
176d1b054daSYu Zhao 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
177d1b054daSYu Zhao 	if (pos)
178d1b054daSYu Zhao 		return sriov_init(dev, pos);
179d1b054daSYu Zhao 
180d1b054daSYu Zhao 	return -ENODEV;
181d1b054daSYu Zhao }
182d1b054daSYu Zhao 
183d1b054daSYu Zhao /**
184d1b054daSYu Zhao  * pci_iov_release - release resources used by the IOV capability
185d1b054daSYu Zhao  * @dev: the PCI device
186d1b054daSYu Zhao  */
187d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev)
188d1b054daSYu Zhao {
189d1b054daSYu Zhao 	if (dev->is_physfn)
190d1b054daSYu Zhao 		sriov_release(dev);
191d1b054daSYu Zhao }
192d1b054daSYu Zhao 
193d1b054daSYu Zhao /**
194d1b054daSYu Zhao  * pci_iov_resource_bar - get position of the SR-IOV BAR
195d1b054daSYu Zhao  * @dev: the PCI device
196d1b054daSYu Zhao  * @resno: the resource number
197d1b054daSYu Zhao  * @type: the BAR type to be filled in
198d1b054daSYu Zhao  *
199d1b054daSYu Zhao  * Returns position of the BAR encapsulated in the SR-IOV capability.
200d1b054daSYu Zhao  */
201d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno,
202d1b054daSYu Zhao 			 enum pci_bar_type *type)
203d1b054daSYu Zhao {
204d1b054daSYu Zhao 	if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
205d1b054daSYu Zhao 		return 0;
206d1b054daSYu Zhao 
207d1b054daSYu Zhao 	BUG_ON(!dev->is_physfn);
208d1b054daSYu Zhao 
209d1b054daSYu Zhao 	*type = pci_bar_unknown;
210d1b054daSYu Zhao 
211d1b054daSYu Zhao 	return dev->sriov->pos + PCI_SRIOV_BAR +
212d1b054daSYu Zhao 		4 * (resno - PCI_IOV_RESOURCES);
213d1b054daSYu Zhao }
2148c5cdb6aSYu Zhao 
2158c5cdb6aSYu Zhao /**
2168c5cdb6aSYu Zhao  * pci_restore_iov_state - restore the state of the IOV capability
2178c5cdb6aSYu Zhao  * @dev: the PCI device
2188c5cdb6aSYu Zhao  */
2198c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev)
2208c5cdb6aSYu Zhao {
2218c5cdb6aSYu Zhao 	if (dev->is_physfn)
2228c5cdb6aSYu Zhao 		sriov_restore_state(dev);
2238c5cdb6aSYu Zhao }
224*a28724b0SYu Zhao 
225*a28724b0SYu Zhao /**
226*a28724b0SYu Zhao  * pci_iov_bus_range - find bus range used by Virtual Function
227*a28724b0SYu Zhao  * @bus: the PCI bus
228*a28724b0SYu Zhao  *
229*a28724b0SYu Zhao  * Returns max number of buses (exclude current one) used by Virtual
230*a28724b0SYu Zhao  * Functions.
231*a28724b0SYu Zhao  */
232*a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus)
233*a28724b0SYu Zhao {
234*a28724b0SYu Zhao 	int max = 0;
235*a28724b0SYu Zhao 	u8 busnr;
236*a28724b0SYu Zhao 	struct pci_dev *dev;
237*a28724b0SYu Zhao 
238*a28724b0SYu Zhao 	list_for_each_entry(dev, &bus->devices, bus_list) {
239*a28724b0SYu Zhao 		if (!dev->is_physfn)
240*a28724b0SYu Zhao 			continue;
241*a28724b0SYu Zhao 		busnr = virtfn_bus(dev, dev->sriov->total - 1);
242*a28724b0SYu Zhao 		if (busnr > max)
243*a28724b0SYu Zhao 			max = busnr;
244*a28724b0SYu Zhao 	}
245*a28724b0SYu Zhao 
246*a28724b0SYu Zhao 	return max ? max - bus->number : 0;
247*a28724b0SYu Zhao }
248