blob: 5ddfc09a8d3f7eede37401328373b59e125f5cef [file] [log] [blame]
Yu Zhaod1b054d2009-03-20 11:25:11 +08001/*
2 * drivers/pci/iov.c
3 *
4 * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
5 *
6 * PCI Express I/O Virtualization (IOV) support.
7 * Single Root IOV 1.0
8 */
9
10#include <linux/pci.h>
11#include <linux/mutex.h>
12#include <linux/string.h>
13#include <linux/delay.h>
14#include "pci.h"
15
16
Yu Zhaoa28724b2009-03-20 11:25:13 +080017static inline u8 virtfn_bus(struct pci_dev *dev, int id)
18{
19 return dev->bus->number + ((dev->devfn + dev->sriov->offset +
20 dev->sriov->stride * id) >> 8);
21}
22
23static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
24{
25 return (dev->devfn + dev->sriov->offset +
26 dev->sriov->stride * id) & 0xff;
27}
28
Yu Zhaod1b054d2009-03-20 11:25:11 +080029static int sriov_init(struct pci_dev *dev, int pos)
30{
31 int i;
32 int rc;
33 int nres;
34 u32 pgsz;
35 u16 ctrl, total, offset, stride;
36 struct pci_sriov *iov;
37 struct resource *res;
38 struct pci_dev *pdev;
39
40 if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
41 dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
42 return -ENODEV;
43
44 pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
45 if (ctrl & PCI_SRIOV_CTRL_VFE) {
46 pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
47 ssleep(1);
48 }
49
50 pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
51 if (!total)
52 return 0;
53
54 ctrl = 0;
55 list_for_each_entry(pdev, &dev->bus->devices, bus_list)
56 if (pdev->is_physfn)
57 goto found;
58
59 pdev = NULL;
60 if (pci_ari_enabled(dev->bus))
61 ctrl |= PCI_SRIOV_CTRL_ARI;
62
63found:
64 pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
65 pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
66 pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
67 pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
68 if (!offset || (total > 1 && !stride))
69 return -EIO;
70
71 pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
72 i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
73 pgsz &= ~((1 << i) - 1);
74 if (!pgsz)
75 return -EIO;
76
77 pgsz &= ~(pgsz - 1);
78 pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
79
80 nres = 0;
81 for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
82 res = dev->resource + PCI_IOV_RESOURCES + i;
83 i += __pci_read_base(dev, pci_bar_unknown, res,
84 pos + PCI_SRIOV_BAR + i * 4);
85 if (!res->flags)
86 continue;
87 if (resource_size(res) & (PAGE_SIZE - 1)) {
88 rc = -EIO;
89 goto failed;
90 }
91 res->end = res->start + resource_size(res) * total - 1;
92 nres++;
93 }
94
95 iov = kzalloc(sizeof(*iov), GFP_KERNEL);
96 if (!iov) {
97 rc = -ENOMEM;
98 goto failed;
99 }
100
101 iov->pos = pos;
102 iov->nres = nres;
103 iov->ctrl = ctrl;
104 iov->total = total;
105 iov->offset = offset;
106 iov->stride = stride;
107 iov->pgsz = pgsz;
108 iov->self = dev;
109 pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
110 pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
111
112 if (pdev)
113 iov->dev = pci_dev_get(pdev);
114 else {
115 iov->dev = dev;
116 mutex_init(&iov->lock);
117 }
118
119 dev->sriov = iov;
120 dev->is_physfn = 1;
121
122 return 0;
123
124failed:
125 for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
126 res = dev->resource + PCI_IOV_RESOURCES + i;
127 res->flags = 0;
128 }
129
130 return rc;
131}
132
133static void sriov_release(struct pci_dev *dev)
134{
135 if (dev == dev->sriov->dev)
136 mutex_destroy(&dev->sriov->lock);
137 else
138 pci_dev_put(dev->sriov->dev);
139
140 kfree(dev->sriov);
141 dev->sriov = NULL;
142}
143
Yu Zhao8c5cdb62009-03-20 11:25:12 +0800144static void sriov_restore_state(struct pci_dev *dev)
145{
146 int i;
147 u16 ctrl;
148 struct pci_sriov *iov = dev->sriov;
149
150 pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
151 if (ctrl & PCI_SRIOV_CTRL_VFE)
152 return;
153
154 for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
155 pci_update_resource(dev, i);
156
157 pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
158 pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
159 if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
160 msleep(100);
161}
162
Yu Zhaod1b054d2009-03-20 11:25:11 +0800163/**
164 * pci_iov_init - initialize the IOV capability
165 * @dev: the PCI device
166 *
167 * Returns 0 on success, or negative on failure.
168 */
169int pci_iov_init(struct pci_dev *dev)
170{
171 int pos;
172
173 if (!dev->is_pcie)
174 return -ENODEV;
175
176 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
177 if (pos)
178 return sriov_init(dev, pos);
179
180 return -ENODEV;
181}
182
183/**
184 * pci_iov_release - release resources used by the IOV capability
185 * @dev: the PCI device
186 */
187void pci_iov_release(struct pci_dev *dev)
188{
189 if (dev->is_physfn)
190 sriov_release(dev);
191}
192
193/**
194 * pci_iov_resource_bar - get position of the SR-IOV BAR
195 * @dev: the PCI device
196 * @resno: the resource number
197 * @type: the BAR type to be filled in
198 *
199 * Returns position of the BAR encapsulated in the SR-IOV capability.
200 */
201int pci_iov_resource_bar(struct pci_dev *dev, int resno,
202 enum pci_bar_type *type)
203{
204 if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
205 return 0;
206
207 BUG_ON(!dev->is_physfn);
208
209 *type = pci_bar_unknown;
210
211 return dev->sriov->pos + PCI_SRIOV_BAR +
212 4 * (resno - PCI_IOV_RESOURCES);
213}
Yu Zhao8c5cdb62009-03-20 11:25:12 +0800214
215/**
216 * pci_restore_iov_state - restore the state of the IOV capability
217 * @dev: the PCI device
218 */
219void pci_restore_iov_state(struct pci_dev *dev)
220{
221 if (dev->is_physfn)
222 sriov_restore_state(dev);
223}
Yu Zhaoa28724b2009-03-20 11:25:13 +0800224
225/**
226 * pci_iov_bus_range - find bus range used by Virtual Function
227 * @bus: the PCI bus
228 *
229 * Returns max number of buses (exclude current one) used by Virtual
230 * Functions.
231 */
232int pci_iov_bus_range(struct pci_bus *bus)
233{
234 int max = 0;
235 u8 busnr;
236 struct pci_dev *dev;
237
238 list_for_each_entry(dev, &bus->devices, bus_list) {
239 if (!dev->is_physfn)
240 continue;
241 busnr = virtfn_bus(dev, dev->sriov->total - 1);
242 if (busnr > max)
243 max = busnr;
244 }
245
246 return max ? max - bus->number : 0;
247}