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