Commit | Line | Data |
---|---|---|
d1b054da YZ |
1 | /* |
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 | ||
a28724b0 YZ |
17 | static 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 | ||
23 | static 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 | ||
d1b054da YZ |
29 | static 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 | ||
63 | found: | |
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 | ||
124 | failed: | |
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 | ||
133 | static 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 | ||
8c5cdb6a YZ |
144 | static 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 | ||
d1b054da YZ |
163 | /** |
164 | * pci_iov_init - initialize the IOV capability | |
165 | * @dev: the PCI device | |
166 | * | |
167 | * Returns 0 on success, or negative on failure. | |
168 | */ | |
169 | int 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 | */ | |
187 | void 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 | */ | |
201 | int 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 | } | |
8c5cdb6a YZ |
214 | |
215 | /** | |
216 | * pci_restore_iov_state - restore the state of the IOV capability | |
217 | * @dev: the PCI device | |
218 | */ | |
219 | void pci_restore_iov_state(struct pci_dev *dev) | |
220 | { | |
221 | if (dev->is_physfn) | |
222 | sriov_restore_state(dev); | |
223 | } | |
a28724b0 YZ |
224 | |
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 | */ | |
232 | int 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 | } |