diff options
Diffstat (limited to 'drivers/net/enic/enic_res.c')
-rw-r--r-- | drivers/net/enic/enic_res.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c new file mode 100644 index 000000000000..95184b9108ef --- /dev/null +++ b/drivers/net/enic/enic_res.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /* | ||
2 | * Copyright 2008 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 | printk(KERN_ERR PFX "Error getting MAC addr, %d\n", err); | ||
50 | return err; | ||
51 | } | ||
52 | |||
53 | #define GET_CONFIG(m) \ | ||
54 | do { \ | ||
55 | err = vnic_dev_spec(enic->vdev, \ | ||
56 | offsetof(struct vnic_enet_config, m), \ | ||
57 | sizeof(c->m), &c->m); \ | ||
58 | if (err) { \ | ||
59 | printk(KERN_ERR PFX \ | ||
60 | "Error getting %s, %d\n", #m, err); \ | ||
61 | return err; \ | ||
62 | } \ | ||
63 | } while (0) | ||
64 | |||
65 | GET_CONFIG(flags); | ||
66 | GET_CONFIG(wq_desc_count); | ||
67 | GET_CONFIG(rq_desc_count); | ||
68 | GET_CONFIG(mtu); | ||
69 | GET_CONFIG(intr_timer); | ||
70 | GET_CONFIG(intr_timer_type); | ||
71 | GET_CONFIG(intr_mode); | ||
72 | |||
73 | c->wq_desc_count = | ||
74 | min_t(u32, ENIC_MAX_WQ_DESCS, | ||
75 | max_t(u32, ENIC_MIN_WQ_DESCS, | ||
76 | c->wq_desc_count)); | ||
77 | c->wq_desc_count &= 0xfffffff0; /* must be aligned to groups of 16 */ | ||
78 | |||
79 | c->rq_desc_count = | ||
80 | min_t(u32, ENIC_MAX_RQ_DESCS, | ||
81 | max_t(u32, ENIC_MIN_RQ_DESCS, | ||
82 | c->rq_desc_count)); | ||
83 | c->rq_desc_count &= 0xfffffff0; /* must be aligned to groups of 16 */ | ||
84 | |||
85 | if (c->mtu == 0) | ||
86 | c->mtu = 1500; | ||
87 | c->mtu = min_t(u16, ENIC_MAX_MTU, | ||
88 | max_t(u16, ENIC_MIN_MTU, | ||
89 | c->mtu)); | ||
90 | |||
91 | c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer); | ||
92 | |||
93 | printk(KERN_INFO PFX "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x " | ||
94 | "wq/rq %d/%d\n", | ||
95 | enic->mac_addr[0], enic->mac_addr[1], enic->mac_addr[2], | ||
96 | enic->mac_addr[3], enic->mac_addr[4], enic->mac_addr[5], | ||
97 | c->wq_desc_count, c->rq_desc_count); | ||
98 | printk(KERN_INFO PFX "vNIC mtu %d csum tx/rx %d/%d tso/lro %d/%d " | ||
99 | "intr timer %d\n", | ||
100 | c->mtu, ENIC_SETTING(enic, TXCSUM), | ||
101 | ENIC_SETTING(enic, RXCSUM), ENIC_SETTING(enic, TSO), | ||
102 | ENIC_SETTING(enic, LRO), c->intr_timer); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | void enic_add_station_addr(struct enic *enic) | ||
108 | { | ||
109 | vnic_dev_add_addr(enic->vdev, enic->mac_addr); | ||
110 | } | ||
111 | |||
112 | void enic_add_multicast_addr(struct enic *enic, u8 *addr) | ||
113 | { | ||
114 | vnic_dev_add_addr(enic->vdev, addr); | ||
115 | } | ||
116 | |||
117 | void enic_del_multicast_addr(struct enic *enic, u8 *addr) | ||
118 | { | ||
119 | vnic_dev_del_addr(enic->vdev, addr); | ||
120 | } | ||
121 | |||
122 | void enic_add_vlan(struct enic *enic, u16 vlanid) | ||
123 | { | ||
124 | u64 a0 = vlanid, a1 = 0; | ||
125 | int wait = 1000; | ||
126 | int err; | ||
127 | |||
128 | err = vnic_dev_cmd(enic->vdev, CMD_VLAN_ADD, &a0, &a1, wait); | ||
129 | if (err) | ||
130 | printk(KERN_ERR PFX "Can't add vlan id, %d\n", err); | ||
131 | } | ||
132 | |||
133 | void enic_del_vlan(struct enic *enic, u16 vlanid) | ||
134 | { | ||
135 | u64 a0 = vlanid, a1 = 0; | ||
136 | int wait = 1000; | ||
137 | int err; | ||
138 | |||
139 | err = vnic_dev_cmd(enic->vdev, CMD_VLAN_DEL, &a0, &a1, wait); | ||
140 | if (err) | ||
141 | printk(KERN_ERR PFX "Can't delete vlan id, %d\n", err); | ||
142 | } | ||
143 | |||
144 | int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, | ||
145 | u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en, | ||
146 | u8 ig_vlan_strip_en) | ||
147 | { | ||
148 | u64 a0, a1; | ||
149 | u32 nic_cfg; | ||
150 | int wait = 1000; | ||
151 | |||
152 | vnic_set_nic_cfg(&nic_cfg, rss_default_cpu, | ||
153 | rss_hash_type, rss_hash_bits, rss_base_cpu, | ||
154 | rss_enable, tso_ipid_split_en, ig_vlan_strip_en); | ||
155 | |||
156 | a0 = nic_cfg; | ||
157 | a1 = 0; | ||
158 | |||
159 | return vnic_dev_cmd(enic->vdev, CMD_NIC_CFG, &a0, &a1, wait); | ||
160 | } | ||
161 | |||
162 | void enic_free_vnic_resources(struct enic *enic) | ||
163 | { | ||
164 | unsigned int i; | ||
165 | |||
166 | for (i = 0; i < enic->wq_count; i++) | ||
167 | vnic_wq_free(&enic->wq[i]); | ||
168 | for (i = 0; i < enic->rq_count; i++) | ||
169 | vnic_rq_free(&enic->rq[i]); | ||
170 | for (i = 0; i < enic->cq_count; i++) | ||
171 | vnic_cq_free(&enic->cq[i]); | ||
172 | for (i = 0; i < enic->intr_count; i++) | ||
173 | vnic_intr_free(&enic->intr[i]); | ||
174 | } | ||
175 | |||
176 | void enic_get_res_counts(struct enic *enic) | ||
177 | { | ||
178 | enic->wq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ); | ||
179 | enic->rq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_RQ); | ||
180 | enic->cq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_CQ); | ||
181 | enic->intr_count = vnic_dev_get_res_count(enic->vdev, | ||
182 | RES_TYPE_INTR_CTRL); | ||
183 | |||
184 | printk(KERN_INFO PFX "vNIC resources avail: " | ||
185 | "wq %d rq %d cq %d intr %d\n", | ||
186 | enic->wq_count, enic->rq_count, | ||
187 | enic->cq_count, enic->intr_count); | ||
188 | } | ||
189 | |||
190 | void enic_init_vnic_resources(struct enic *enic) | ||
191 | { | ||
192 | enum vnic_dev_intr_mode intr_mode; | ||
193 | unsigned int mask_on_assertion; | ||
194 | unsigned int interrupt_offset; | ||
195 | unsigned int error_interrupt_enable; | ||
196 | unsigned int error_interrupt_offset; | ||
197 | unsigned int cq_index; | ||
198 | unsigned int i; | ||
199 | |||
200 | intr_mode = vnic_dev_get_intr_mode(enic->vdev); | ||
201 | |||
202 | /* Init RQ/WQ resources. | ||
203 | * | ||
204 | * RQ[0 - n-1] point to CQ[0 - n-1] | ||
205 | * WQ[0 - m-1] point to CQ[n - n+m-1] | ||
206 | * | ||
207 | * Error interrupt is not enabled for MSI. | ||
208 | */ | ||
209 | |||
210 | switch (intr_mode) { | ||
211 | case VNIC_DEV_INTR_MODE_INTX: | ||
212 | case VNIC_DEV_INTR_MODE_MSIX: | ||
213 | error_interrupt_enable = 1; | ||
214 | error_interrupt_offset = enic->intr_count - 2; | ||
215 | break; | ||
216 | default: | ||
217 | error_interrupt_enable = 0; | ||
218 | error_interrupt_offset = 0; | ||
219 | break; | ||
220 | } | ||
221 | |||
222 | for (i = 0; i < enic->rq_count; i++) { | ||
223 | cq_index = i; | ||
224 | vnic_rq_init(&enic->rq[i], | ||
225 | cq_index, | ||
226 | error_interrupt_enable, | ||
227 | error_interrupt_offset); | ||
228 | } | ||
229 | |||
230 | for (i = 0; i < enic->wq_count; i++) { | ||
231 | cq_index = enic->rq_count + i; | ||
232 | vnic_wq_init(&enic->wq[i], | ||
233 | cq_index, | ||
234 | error_interrupt_enable, | ||
235 | error_interrupt_offset); | ||
236 | } | ||
237 | |||
238 | /* Init CQ resources | ||
239 | * | ||
240 | * CQ[0 - n+m-1] point to INTR[0] for INTx, MSI | ||
241 | * CQ[0 - n+m-1] point to INTR[0 - n+m-1] for MSI-X | ||
242 | */ | ||
243 | |||
244 | for (i = 0; i < enic->cq_count; i++) { | ||
245 | |||
246 | switch (intr_mode) { | ||
247 | case VNIC_DEV_INTR_MODE_MSIX: | ||
248 | interrupt_offset = i; | ||
249 | break; | ||
250 | default: | ||
251 | interrupt_offset = 0; | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | vnic_cq_init(&enic->cq[i], | ||
256 | 0 /* flow_control_enable */, | ||
257 | 1 /* color_enable */, | ||
258 | 0 /* cq_head */, | ||
259 | 0 /* cq_tail */, | ||
260 | 1 /* cq_tail_color */, | ||
261 | 1 /* interrupt_enable */, | ||
262 | 1 /* cq_entry_enable */, | ||
263 | 0 /* cq_message_enable */, | ||
264 | interrupt_offset, | ||
265 | 0 /* cq_message_addr */); | ||
266 | } | ||
267 | |||
268 | /* Init INTR resources | ||
269 | * | ||
270 | * mask_on_assertion is not used for INTx due to the level- | ||
271 | * triggered nature of INTx | ||
272 | */ | ||
273 | |||
274 | switch (intr_mode) { | ||
275 | case VNIC_DEV_INTR_MODE_MSI: | ||
276 | case VNIC_DEV_INTR_MODE_MSIX: | ||
277 | mask_on_assertion = 1; | ||
278 | break; | ||
279 | default: | ||
280 | mask_on_assertion = 0; | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | for (i = 0; i < enic->intr_count; i++) { | ||
285 | vnic_intr_init(&enic->intr[i], | ||
286 | enic->config.intr_timer, | ||
287 | enic->config.intr_timer_type, | ||
288 | mask_on_assertion); | ||
289 | } | ||
290 | |||
291 | /* Clear LIF stats | ||
292 | */ | ||
293 | |||
294 | vnic_dev_stats_clear(enic->vdev); | ||
295 | } | ||
296 | |||
297 | int enic_alloc_vnic_resources(struct enic *enic) | ||
298 | { | ||
299 | enum vnic_dev_intr_mode intr_mode; | ||
300 | unsigned int i; | ||
301 | int err; | ||
302 | |||
303 | intr_mode = vnic_dev_get_intr_mode(enic->vdev); | ||
304 | |||
305 | printk(KERN_INFO PFX "vNIC resources used: " | ||
306 | "wq %d rq %d cq %d intr %d intr mode %s\n", | ||
307 | enic->wq_count, enic->rq_count, | ||
308 | enic->cq_count, enic->intr_count, | ||
309 | intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" : | ||
310 | intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" : | ||
311 | intr_mode == VNIC_DEV_INTR_MODE_MSIX ? "MSI-X" : | ||
312 | "unknown" | ||
313 | ); | ||
314 | |||
315 | /* Allocate queue resources | ||
316 | */ | ||
317 | |||
318 | for (i = 0; i < enic->wq_count; i++) { | ||
319 | err = vnic_wq_alloc(enic->vdev, &enic->wq[i], i, | ||
320 | enic->config.wq_desc_count, | ||
321 | sizeof(struct wq_enet_desc)); | ||
322 | if (err) | ||
323 | goto err_out_cleanup; | ||
324 | } | ||
325 | |||
326 | for (i = 0; i < enic->rq_count; i++) { | ||
327 | err = vnic_rq_alloc(enic->vdev, &enic->rq[i], i, | ||
328 | enic->config.rq_desc_count, | ||
329 | sizeof(struct rq_enet_desc)); | ||
330 | if (err) | ||
331 | goto err_out_cleanup; | ||
332 | } | ||
333 | |||
334 | for (i = 0; i < enic->cq_count; i++) { | ||
335 | if (i < enic->rq_count) | ||
336 | err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, | ||
337 | enic->config.rq_desc_count, | ||
338 | sizeof(struct cq_enet_rq_desc)); | ||
339 | else | ||
340 | err = vnic_cq_alloc(enic->vdev, &enic->cq[i], i, | ||
341 | enic->config.wq_desc_count, | ||
342 | sizeof(struct cq_enet_wq_desc)); | ||
343 | if (err) | ||
344 | goto err_out_cleanup; | ||
345 | } | ||
346 | |||
347 | for (i = 0; i < enic->intr_count; i++) { | ||
348 | err = vnic_intr_alloc(enic->vdev, &enic->intr[i], i); | ||
349 | if (err) | ||
350 | goto err_out_cleanup; | ||
351 | } | ||
352 | |||
353 | /* Hook remaining resource | ||
354 | */ | ||
355 | |||
356 | enic->legacy_pba = vnic_dev_get_res(enic->vdev, | ||
357 | RES_TYPE_INTR_PBA_LEGACY, 0); | ||
358 | if (!enic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) { | ||
359 | printk(KERN_ERR PFX "Failed to hook legacy pba resource\n"); | ||
360 | err = -ENODEV; | ||
361 | goto err_out_cleanup; | ||
362 | } | ||
363 | |||
364 | return 0; | ||
365 | |||
366 | err_out_cleanup: | ||
367 | enic_free_vnic_resources(enic); | ||
368 | |||
369 | return err; | ||
370 | } | ||