diff options
Diffstat (limited to 'drivers/net/enic/enic_res.c')
-rw-r--r-- | drivers/net/enic/enic_res.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c new file mode 100644 index 00000000000..4a35367de79 --- /dev/null +++ b/drivers/net/enic/enic_res.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* | ||
2 | * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. | ||
3 | * Copyright 2007 Nuova Systems, Inc. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you may redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; version 2 of the License. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
10 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
11 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
12 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
13 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
15 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
16 | * SOFTWARE. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/netdevice.h> | ||
25 | |||
26 | #include "wq_enet_desc.h" | ||
27 | #include "rq_enet_desc.h" | ||
28 | #include "cq_enet_desc.h" | ||
29 | #include "vnic_resource.h" | ||
30 | #include "vnic_enet.h" | ||
31 | #include "vnic_dev.h" | ||
32 | #include "vnic_wq.h" | ||
33 | #include "vnic_rq.h" | ||
34 | #include "vnic_cq.h" | ||
35 | #include "vnic_intr.h" | ||
36 | #include "vnic_stats.h" | ||
37 | #include "vnic_nic.h" | ||
38 | #include "vnic_rss.h" | ||
39 | #include "enic_res.h" | ||
40 | #include "enic.h" | ||
41 | |||
42 | int enic_get_vnic_config(struct enic *enic) | ||
43 | { | ||
44 | struct vnic_enet_config *c = &enic->config; | ||
45 | int err; | ||
46 | |||
47 | err = vnic_dev_mac_addr(enic->vdev, enic->mac_addr); | ||
48 | if (err) { | ||
49 | dev_err(enic_get_dev(enic), | ||
50 | "Error getting MAC addr, %d\n", err); | ||
51 | return err; | ||
52 | } | ||
53 | |||
54 | #define GET_CONFIG(m) \ | ||
55 | do { \ | ||
56 | err = vnic_dev_spec(enic->vdev, \ | ||
57 | offsetof(struct vnic_enet_config, m), \ | ||
58 | sizeof(c->m), &c->m); \ | ||
59 | if (err) { \ | ||
60 | dev_err(enic_get_dev(enic), \ | ||
61 | "Error getting %s, %d\n", #m, err); \ | ||
62 | return err; \ | ||
63 | } \ | ||
64 | } while (0) | ||
65 | |||
66 | GET_CONFIG(flags); | ||
67 | GET_CONFIG(wq_desc_count); | ||
68 | GET_CONFIG(rq_desc_count); | ||
69 | GET_CONFIG(mtu); | ||
70 | GET_CONFIG(intr_timer_type); | ||
71 | GET_CONFIG(intr_mode); | ||
72 | GET_CONFIG(intr_timer_usec); | ||
73 | GET_CONFIG(loop_tag); | ||
74 | |||
75 | c->wq_desc_count = | ||
76 | min_t(u32, ENIC_MAX_WQ_DESCS, | ||
77 | max_t(u32, ENIC_MIN_WQ_DESCS, | ||
78 | c->wq_desc_count)); | ||
79 | c->wq_desc_count &= 0xffffffe0; /* must be aligned to groups of 32 */ | ||
80 | |||
81 | c->rq_desc_count = | ||
82 | min_t(u32, ENIC_MAX_RQ_DESCS, | ||
83 | max_t(u32, ENIC_MIN_RQ_DESCS, | ||
84 | c->rq_desc_count)); | ||
85 | c->rq_desc_count &= 0xffffffe0; /* must be aligned to groups of 32 */ | ||
86 | |||
87 | if (c->mtu == 0) | ||
88 | c->mtu = 1500; | ||
89 | c->mtu = min_t(u16, ENIC_MAX_MTU, | ||
90 | max_t(u16, ENIC_MIN_MTU, | ||
91 | c->mtu)); | ||
92 | |||
93 | c->intr_timer_usec = min_t(u32, c->intr_timer_usec, | ||
94 | vnic_dev_get_intr_coal_timer_max(enic->vdev)); | ||
95 | |||
96 | dev_info(enic_get_dev(enic), | ||
97 | "vNIC MAC addr %pM wq/rq %d/%d mtu %d\n", | ||
98 | enic->mac_addr, c->wq_desc_count, c->rq_desc_count, c->mtu); | ||
99 | |||
100 | dev_info(enic_get_dev(enic), "vNIC csum tx/rx %s/%s " | ||
101 | "tso/lro %s/%s rss %s intr mode %s type %s timer %d usec " | ||
102 | "loopback tag 0x%04x\n", | ||
103 | ENIC_SETTING(enic, TXCSUM) ? "yes" : "no", | ||
104 | ENIC_SETTING(enic, RXCSUM) ? "yes" : "no", | ||
105 | ENIC_SETTING(enic, TSO) ? "yes" : "no", | ||
106 | ENIC_SETTING(enic, LRO) ? "yes" : "no", | ||
107 | ENIC_SETTING(enic, RSS) ? "yes" : "no", | ||
108 | c->intr_mode == VENET_INTR_MODE_INTX ? "INTx" : | ||
109 | c->intr_mode == VENET_INTR_MODE_MSI ? "MSI" : | ||
110 | c->intr_mode == VENET_INTR_MODE_ANY ? "any" : | ||
111 | "unknown", | ||
112 | c->intr_timer_type == VENET_INTR_TYPE_MIN ? "min" : | ||
113 | c->intr_timer_type == VENET_INTR_TYPE_IDLE ? "idle" : | ||
114 | "unknown", | ||
115 | c->intr_timer_usec, | ||
116 | c->loop_tag); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | int enic_add_vlan(struct enic *enic, u16 vlanid) | ||
122 | { | ||
123 | u64 a0 = vlanid, a1 = 0; | ||
124 | int wait = 1000; | ||
125 | int err; | ||
126 | |||
127 | err = vnic_dev_cmd(enic->vdev, CMD_VLAN_ADD, &a0, &a1, wait); | ||
128 | if (err) | ||
129 | dev_err(enic_get_dev(enic), "Can't add vlan id, %d\n", err); | ||
130 | |||
131 | return err; | ||
132 | } | ||
133 | |||
134 | int enic_del_vlan(struct enic *enic, u16 vlanid) | ||
135 | { | ||
136 | u64 a0 = vlanid, a1 = 0; | ||
137 | int wait = 1000; | ||
138 | int err; | ||
139 | |||
140 | err = vnic_dev_cmd(enic->vdev, CMD_VLAN_DEL, &a0, &a1, wait); | ||
141 | if (err) | ||
142 | dev_err(enic_get_dev(enic), "Can't delete vlan id, %d\n", err); | ||
143 | |||
144 | return err; | ||
145 | } | ||
146 | |||
147 | int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, | ||
148 | u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en, | ||
149 | u8 ig_vlan_strip_en) | ||
150 | { | ||
151 | u64 a0, a1; | ||
152 | u32 nic_cfg; | ||
153 | int wait = 1000; | ||
154 | |||
155 | vnic_set_nic_cfg(&nic_cfg, rss_default_cpu, | ||
156 | rss_hash_type, rss_hash_bits, rss_base_cpu, | ||
157 | rss_enable, tso_ipid_split_en, ig_vlan_strip_en); | ||
158 | |||
159 | a0 = nic_cfg; | ||
160 | a1 = 0; | ||
161 | |||
162 | return vnic_dev_cmd(enic->vdev, CMD_NIC_CFG, &a0, &a1, wait); | ||
163 | } | ||
164 | |||
165 | int enic_set_rss_key(struct enic *enic, dma_addr_t key_pa, u64 len) | ||
166 | { | ||
167 | u64 a0 = (u64)key_pa, a1 = len; | ||
168 | int wait = 1000; | ||
169 | |||
170 | return vnic_dev_cmd(enic->vdev, CMD_RSS_KEY, &a0, &a1, wait); | ||
171 | } | ||
172 | |||
173 | int enic_set_rss_cpu(struct enic *enic, dma_addr_t cpu_pa, u64 len) | ||
174 | { | ||
175 | u64 a0 = (u64)cpu_pa, a1 = len; | ||
176 | int wait = 1000; | ||
177 | |||
178 | return vnic_dev_cmd(enic->vdev, CMD_RSS_CPU, &a0, &a1, wait); | ||
179 | } | ||
180 | |||
181 | void enic_free_vnic_resources(struct enic *enic) | ||
182 | { | ||
183 | unsigned int i; | ||
184 | |||
185 | for (i = 0; i < enic->wq_count; i++) | ||
186 | vnic_wq_free(&enic->wq[i]); | ||
187 | for (i = 0; i < enic->rq_count; i++) | ||
188 | vnic_rq_free(&enic->rq[i]); | ||
189 | for (i = 0; i < enic->cq_count; i++) | ||
190 | vnic_cq_free(&enic->cq[i]); | ||
191 | for (i = 0; i < enic->intr_count; i++) | ||
192 | vnic_intr_free(&enic->intr[i]); | ||
193 | } | ||
194 | |||
195 | void enic_get_res_counts(struct enic *enic) | ||
196 | { | ||
197 | enic->wq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ); | ||
198 | enic->rq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_RQ); | ||
199 | enic->cq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_CQ); | ||
200 | enic->intr_count = vnic_dev_get_res_count(enic->vdev, | ||
201 | RES_TYPE_INTR_CTRL); | ||
202 | |||
203 | dev_info(enic_get_dev(enic), | ||
204 | "vNIC resources avail: wq %d rq %d cq %d intr %d\n", | ||
205 | enic->wq_count, enic->rq_count, | ||
206 | enic->cq_count, enic->intr_count); | ||
207 | } | ||
208 | |||
209 | void enic_init_vnic_resources(struct enic *enic) | ||
210 | { | ||
211 | enum vnic_dev_intr_mode intr_mode; | ||
212 | unsigned int mask_on_assertion; | ||
213 | unsigned int interrupt_offset; | ||
214 | unsigned int error_interrupt_enable; | ||
215 | unsigned int error_interrupt_offset; | ||
216 | unsigned int cq_index; | ||
217 | unsigned int i; | ||
218 | |||
219 | intr_mode = vnic_dev_get_intr_mode(enic->vdev); | ||
220 | |||
221 | /* Init RQ/WQ resources. | ||
222 | * | ||
223 | * RQ[0 - n-1] point to CQ[0 - n-1] | ||
224 | * WQ[0 - m-1] point to CQ[n - n+m-1] | ||
225 | * | ||
226 | * Error interrupt is not enabled for MSI. | ||
227 | */ | ||
228 | |||
229 | switch (intr_mode) { | ||
230 | case VNIC_DEV_INTR_MODE_INTX: | ||
231 | case VNIC_DEV_INTR_MODE_MSIX: | ||
232 | error_interrupt_enable = 1; | ||
233 | error_interrupt_offset = enic->intr_count - 2; | ||
234 | break; | ||
235 | default: | ||
236 | error_interrupt_enable = 0; | ||
237 | error_interrupt_offset = 0; | ||
238 | break; | ||
239 | } | ||
240 | |||
241 | for (i = 0; i < enic->rq_count; i++) { | ||
242 | cq_index = i; | ||
243 | vnic_rq_init(&enic->rq[i], | ||
244 | cq_index, | ||
245 | error_interrupt_enable, | ||
246 | error_interrupt_offset); | ||
247 | } | ||
248 | |||
249 | for (i = 0; i < enic->wq_count; i++) { | ||
250 | cq_index = enic->rq_count + i; | ||
251 | vnic_wq_init(&enic->wq[i], | ||
252 | cq_index, | ||
253 | error_interrupt_enable, | ||
254 | error_interrupt_offset); | ||
255 | } | ||
256 | |||
257 | /* Init CQ resources | ||
258 | * | ||
259 | * CQ[0 - n+m-1] point to INTR[0] for INTx, MSI | ||
260 | * CQ[0 - n+m-1] point to INTR[0 - n+m-1] for MSI-X | ||
261 | */ | ||
262 | |||
263 | for (i = 0; i < enic->cq_count; i++) { | ||
264 | |||
265 | switch (intr_mode) { | ||
266 | case VNIC_DEV_INTR_MODE_MSIX: | ||
267 | interrupt_offset = i; | ||
268 | break; | ||
269 | default: | ||
270 | interrupt_offset = 0; | ||
271 | break; | ||
272 | } | ||
273 | |||
274 | vnic_cq_init(&enic->cq[i], | ||
275 | 0 /* flow_control_enable */, | ||
276 | 1 /* color_enable */, | ||
277 | 0 /* cq_head */, | ||
278 | 0 /* cq_tail */, | ||
279 | 1 /* cq_tail_color */, | ||
280 | 1 /* interrupt_enable */, | ||
281 | 1 /* cq_entry_enable */, | ||
282 | 0 /* cq_message_enable */, | ||
283 | interrupt_offset, | ||
284 | 0 /* cq_message_addr */); | ||
285 | } | ||
286 | |||
287 | /* Init INTR resources | ||
288 | * | ||
289 | * mask_on_assertion is not used for INTx due to the level- | ||
290 | * triggered nature of INTx | ||
291 | */ | ||
292 | |||
293 | switch (intr_mode) { | ||
294 | case VNIC_DEV_INTR_MODE_MSI: | ||
295 | case VNIC_DEV_INTR_MODE_MSIX: | ||
296 | mask_on_assertion = 1; | ||
297 | break; | ||
298 | default: | ||
299 | mask_on_assertion = 0; | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | for (i = 0; i < enic->intr_count; i++) { | ||
304 | vnic_intr_init(&enic->intr[i], | ||
305 | enic->config.intr_timer_usec, | ||
306 | enic->config.intr_timer_type, | ||
307 | mask_on_assertion); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | int enic_alloc_vnic_resources(struct enic *enic) | ||
312 | { | ||
313 | enum vnic_dev_intr_mode intr_mode; | ||
314 | unsigned int i; | ||
315 | int err; | ||
316 | |||
317 | intr_mode = vnic_dev_get_intr_mode(enic->vdev); | ||
318 | |||
319 | dev_info(enic_get_dev(enic), "vNIC resources used: " | ||
320 | "wq %d rq %d cq %d intr %d intr mode %s\n", | ||
321 | enic->wq_count, enic->rq_count, | ||
322 | enic->cq_count, enic->intr_count, | ||
323 | intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" : | ||
324 | intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" : | ||
325 | intr_mode == VNIC_DEV_INTR_MODE_MSIX ? "MSI-X" : | ||
326 | "unknown"); | ||
327 | |||
328 | /* Allocate queue resources | ||
329 | */ | ||
330 | |||
331 | for (i = 0; i < enic->wq_count; i++) { | ||
332 | err = vnic_wq_alloc(enic->vdev, &enic->wq[i], i, | ||
333 | enic->config.wq_desc_count, | ||
334 | sizeof(struct wq_enet_desc)); | ||
335 | if (err) | ||
336 | goto err_out_cleanup; | ||
337 | } | ||
338 | |||
339 | for (i = 0; i < enic->rq_count; i++) { | ||
340 | err = vnic_rq_alloc(enic->vdev, &enic->rq[i], i, | ||
341 | enic->config.rq_desc_count, | ||
342 | sizeof(struct rq_enet_desc)); | ||
343 | if (err) | ||
344 | goto err_out_cleanup; | ||
345 | } | ||
346 | |||
347 | for (i = 0; i < enic->cq_count; i++) { | ||
348 | if (i < enic->rq_count) | ||
349 | err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, | ||
350 | enic->config.rq_desc_count, | ||
351 | sizeof(struct cq_enet_rq_desc)); | ||
352 | else | ||
353 | err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, | ||
354 | enic->config.wq_desc_count, | ||
355 | sizeof(struct cq_enet_wq_desc)); | ||
356 | if (err) | ||
357 | goto err_out_cleanup; | ||
358 | } | ||
359 | |||
360 | for (i = 0; i < enic->intr_count; i++) { | ||
361 | err = vnic_intr_alloc(enic->vdev, &enic->intr[i], i); | ||
362 | if (err) | ||
363 | goto err_out_cleanup; | ||
364 | } | ||
365 | |||
366 | /* Hook remaining resource | ||
367 | */ | ||
368 | |||
369 | enic->legacy_pba = vnic_dev_get_res(enic->vdev, | ||
370 | RES_TYPE_INTR_PBA_LEGACY, 0); | ||
371 | if (!enic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) { | ||
372 | dev_err(enic_get_dev(enic), | ||
373 | "Failed to hook legacy pba resource\n"); | ||
374 | err = -ENODEV; | ||
375 | goto err_out_cleanup; | ||
376 | } | ||
377 | |||
378 | return 0; | ||
379 | |||
380 | err_out_cleanup: | ||
381 | enic_free_vnic_resources(enic); | ||
382 | |||
383 | return err; | ||
384 | } | ||