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 | ||
17 | static int sriov_init(struct pci_dev *dev, int pos) | |
18 | { | |
19 | int i; | |
20 | int rc; | |
21 | int nres; | |
22 | u32 pgsz; | |
23 | u16 ctrl, total, offset, stride; | |
24 | struct pci_sriov *iov; | |
25 | struct resource *res; | |
26 | struct pci_dev *pdev; | |
27 | ||
28 | if (dev->pcie_type != PCI_EXP_TYPE_RC_END && | |
29 | dev->pcie_type != PCI_EXP_TYPE_ENDPOINT) | |
30 | return -ENODEV; | |
31 | ||
32 | pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); | |
33 | if (ctrl & PCI_SRIOV_CTRL_VFE) { | |
34 | pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); | |
35 | ssleep(1); | |
36 | } | |
37 | ||
38 | pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); | |
39 | if (!total) | |
40 | return 0; | |
41 | ||
42 | ctrl = 0; | |
43 | list_for_each_entry(pdev, &dev->bus->devices, bus_list) | |
44 | if (pdev->is_physfn) | |
45 | goto found; | |
46 | ||
47 | pdev = NULL; | |
48 | if (pci_ari_enabled(dev->bus)) | |
49 | ctrl |= PCI_SRIOV_CTRL_ARI; | |
50 | ||
51 | found: | |
52 | pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); | |
53 | pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total); | |
54 | pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); | |
55 | pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); | |
56 | if (!offset || (total > 1 && !stride)) | |
57 | return -EIO; | |
58 | ||
59 | pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); | |
60 | i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; | |
61 | pgsz &= ~((1 << i) - 1); | |
62 | if (!pgsz) | |
63 | return -EIO; | |
64 | ||
65 | pgsz &= ~(pgsz - 1); | |
66 | pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); | |
67 | ||
68 | nres = 0; | |
69 | for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { | |
70 | res = dev->resource + PCI_IOV_RESOURCES + i; | |
71 | i += __pci_read_base(dev, pci_bar_unknown, res, | |
72 | pos + PCI_SRIOV_BAR + i * 4); | |
73 | if (!res->flags) | |
74 | continue; | |
75 | if (resource_size(res) & (PAGE_SIZE - 1)) { | |
76 | rc = -EIO; | |
77 | goto failed; | |
78 | } | |
79 | res->end = res->start + resource_size(res) * total - 1; | |
80 | nres++; | |
81 | } | |
82 | ||
83 | iov = kzalloc(sizeof(*iov), GFP_KERNEL); | |
84 | if (!iov) { | |
85 | rc = -ENOMEM; | |
86 | goto failed; | |
87 | } | |
88 | ||
89 | iov->pos = pos; | |
90 | iov->nres = nres; | |
91 | iov->ctrl = ctrl; | |
92 | iov->total = total; | |
93 | iov->offset = offset; | |
94 | iov->stride = stride; | |
95 | iov->pgsz = pgsz; | |
96 | iov->self = dev; | |
97 | pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); | |
98 | pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); | |
99 | ||
100 | if (pdev) | |
101 | iov->dev = pci_dev_get(pdev); | |
102 | else { | |
103 | iov->dev = dev; | |
104 | mutex_init(&iov->lock); | |
105 | } | |
106 | ||
107 | dev->sriov = iov; | |
108 | dev->is_physfn = 1; | |
109 | ||
110 | return 0; | |
111 | ||
112 | failed: | |
113 | for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { | |
114 | res = dev->resource + PCI_IOV_RESOURCES + i; | |
115 | res->flags = 0; | |
116 | } | |
117 | ||
118 | return rc; | |
119 | } | |
120 | ||
121 | static void sriov_release(struct pci_dev *dev) | |
122 | { | |
123 | if (dev == dev->sriov->dev) | |
124 | mutex_destroy(&dev->sriov->lock); | |
125 | else | |
126 | pci_dev_put(dev->sriov->dev); | |
127 | ||
128 | kfree(dev->sriov); | |
129 | dev->sriov = NULL; | |
130 | } | |
131 | ||
8c5cdb6a YZ |
132 | static void sriov_restore_state(struct pci_dev *dev) |
133 | { | |
134 | int i; | |
135 | u16 ctrl; | |
136 | struct pci_sriov *iov = dev->sriov; | |
137 | ||
138 | pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); | |
139 | if (ctrl & PCI_SRIOV_CTRL_VFE) | |
140 | return; | |
141 | ||
142 | for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) | |
143 | pci_update_resource(dev, i); | |
144 | ||
145 | pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); | |
146 | pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); | |
147 | if (iov->ctrl & PCI_SRIOV_CTRL_VFE) | |
148 | msleep(100); | |
149 | } | |
150 | ||
d1b054da YZ |
151 | /** |
152 | * pci_iov_init - initialize the IOV capability | |
153 | * @dev: the PCI device | |
154 | * | |
155 | * Returns 0 on success, or negative on failure. | |
156 | */ | |
157 | int pci_iov_init(struct pci_dev *dev) | |
158 | { | |
159 | int pos; | |
160 | ||
161 | if (!dev->is_pcie) | |
162 | return -ENODEV; | |
163 | ||
164 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); | |
165 | if (pos) | |
166 | return sriov_init(dev, pos); | |
167 | ||
168 | return -ENODEV; | |
169 | } | |
170 | ||
171 | /** | |
172 | * pci_iov_release - release resources used by the IOV capability | |
173 | * @dev: the PCI device | |
174 | */ | |
175 | void pci_iov_release(struct pci_dev *dev) | |
176 | { | |
177 | if (dev->is_physfn) | |
178 | sriov_release(dev); | |
179 | } | |
180 | ||
181 | /** | |
182 | * pci_iov_resource_bar - get position of the SR-IOV BAR | |
183 | * @dev: the PCI device | |
184 | * @resno: the resource number | |
185 | * @type: the BAR type to be filled in | |
186 | * | |
187 | * Returns position of the BAR encapsulated in the SR-IOV capability. | |
188 | */ | |
189 | int pci_iov_resource_bar(struct pci_dev *dev, int resno, | |
190 | enum pci_bar_type *type) | |
191 | { | |
192 | if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) | |
193 | return 0; | |
194 | ||
195 | BUG_ON(!dev->is_physfn); | |
196 | ||
197 | *type = pci_bar_unknown; | |
198 | ||
199 | return dev->sriov->pos + PCI_SRIOV_BAR + | |
200 | 4 * (resno - PCI_IOV_RESOURCES); | |
201 | } | |
8c5cdb6a YZ |
202 | |
203 | /** | |
204 | * pci_restore_iov_state - restore the state of the IOV capability | |
205 | * @dev: the PCI device | |
206 | */ | |
207 | void pci_restore_iov_state(struct pci_dev *dev) | |
208 | { | |
209 | if (dev->is_physfn) | |
210 | sriov_restore_state(dev); | |
211 | } |