1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ |
3 | |
4 | /* |
5 | * nfp_net_main.c |
6 | * Netronome network device driver: Main entry point |
7 | * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> |
8 | * Alejandro Lucero <alejandro.lucero@netronome.com> |
9 | * Jason McMullan <jason.mcmullan@netronome.com> |
10 | * Rolf Neugebauer <rolf.neugebauer@netronome.com> |
11 | */ |
12 | |
13 | #include <linux/etherdevice.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> |
16 | #include <linux/lockdep.h> |
17 | #include <linux/pci.h> |
18 | #include <linux/pci_regs.h> |
19 | #include <linux/random.h> |
20 | #include <linux/rtnetlink.h> |
21 | |
22 | #include "nfpcore/nfp.h" |
23 | #include "nfpcore/nfp_cpp.h" |
24 | #include "nfpcore/nfp_dev.h" |
25 | #include "nfpcore/nfp_nffw.h" |
26 | #include "nfpcore/nfp_nsp.h" |
27 | #include "nfpcore/nfp6000_pcie.h" |
28 | #include "nfp_app.h" |
29 | #include "nfp_net_ctrl.h" |
30 | #include "nfp_net_sriov.h" |
31 | #include "nfp_net.h" |
32 | #include "nfp_main.h" |
33 | #include "nfp_port.h" |
34 | |
35 | #define NFP_PF_CSR_SLICE_SIZE (32 * 1024) |
36 | |
37 | /** |
38 | * nfp_net_get_mac_addr() - Get the MAC address. |
39 | * @pf: NFP PF handle |
40 | * @netdev: net_device to set MAC address on |
41 | * @port: NFP port structure |
42 | * |
43 | * First try to get the MAC address from NSP ETH table. If that |
44 | * fails generate a random address. |
45 | */ |
46 | void |
47 | nfp_net_get_mac_addr(struct nfp_pf *pf, struct net_device *netdev, |
48 | struct nfp_port *port) |
49 | { |
50 | struct nfp_eth_table_port *eth_port; |
51 | |
52 | eth_port = __nfp_port_get_eth_port(port); |
53 | if (!eth_port) { |
54 | eth_hw_addr_random(dev: netdev); |
55 | return; |
56 | } |
57 | |
58 | eth_hw_addr_set(dev: netdev, addr: eth_port->mac_addr); |
59 | ether_addr_copy(dst: netdev->perm_addr, src: eth_port->mac_addr); |
60 | } |
61 | |
62 | static struct nfp_eth_table_port * |
63 | nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int index) |
64 | { |
65 | int i; |
66 | |
67 | for (i = 0; eth_tbl && i < eth_tbl->count; i++) |
68 | if (eth_tbl->ports[i].index == index) |
69 | return ð_tbl->ports[i]; |
70 | |
71 | return NULL; |
72 | } |
73 | |
74 | static int nfp_net_pf_get_num_ports(struct nfp_pf *pf) |
75 | { |
76 | return nfp_pf_rtsym_read_optional(pf, format: "nfd_cfg_pf%u_num_ports" , default_val: 1); |
77 | } |
78 | |
79 | static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
80 | { |
81 | if (nfp_net_is_data_vnic(nn)) |
82 | nfp_app_vnic_free(app: pf->app, nn); |
83 | nfp_port_free(port: nn->port); |
84 | list_del(entry: &nn->vnic_list); |
85 | pf->num_vnics--; |
86 | nfp_net_free(nn); |
87 | } |
88 | |
89 | static void nfp_net_pf_free_vnics(struct nfp_pf *pf) |
90 | { |
91 | struct nfp_net *nn, *next; |
92 | |
93 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) |
94 | if (nfp_net_is_data_vnic(nn)) |
95 | nfp_net_pf_free_vnic(pf, nn); |
96 | } |
97 | |
98 | static struct nfp_net * |
99 | nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, |
100 | void __iomem *ctrl_bar, void __iomem *qc_bar, |
101 | int stride, unsigned int id) |
102 | { |
103 | u32 tx_base, rx_base, n_tx_rings, n_rx_rings; |
104 | struct nfp_net *nn; |
105 | int err; |
106 | |
107 | tx_base = readl(addr: ctrl_bar + NFP_NET_CFG_START_TXQ); |
108 | rx_base = readl(addr: ctrl_bar + NFP_NET_CFG_START_RXQ); |
109 | n_tx_rings = readl(addr: ctrl_bar + NFP_NET_CFG_MAX_TXRINGS); |
110 | n_rx_rings = readl(addr: ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); |
111 | |
112 | /* Allocate and initialise the vNIC */ |
113 | nn = nfp_net_alloc(pdev: pf->pdev, dev_info: pf->dev_info, ctrl_bar, needs_netdev, |
114 | max_tx_rings: n_tx_rings, max_rx_rings: n_rx_rings); |
115 | if (IS_ERR(ptr: nn)) |
116 | return nn; |
117 | |
118 | nn->app = pf->app; |
119 | nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ; |
120 | nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ; |
121 | nn->dp.is_vf = 0; |
122 | nn->stride_rx = stride; |
123 | nn->stride_tx = stride; |
124 | |
125 | if (needs_netdev) { |
126 | err = nfp_app_vnic_alloc(app: pf->app, nn, id); |
127 | if (err) { |
128 | nfp_net_free(nn); |
129 | return ERR_PTR(error: err); |
130 | } |
131 | } |
132 | |
133 | pf->num_vnics++; |
134 | list_add_tail(new: &nn->vnic_list, head: &pf->vnics); |
135 | |
136 | return nn; |
137 | } |
138 | |
139 | static int |
140 | nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) |
141 | { |
142 | int err; |
143 | |
144 | nn->id = id; |
145 | |
146 | if (nn->port) { |
147 | err = nfp_devlink_port_register(app: pf->app, port: nn->port); |
148 | if (err) |
149 | return err; |
150 | } |
151 | |
152 | err = nfp_net_init(nn); |
153 | if (err) |
154 | goto err_devlink_port_clean; |
155 | |
156 | nfp_net_debugfs_vnic_add(nn, ddir: pf->ddir); |
157 | |
158 | nfp_net_info(nn); |
159 | |
160 | if (nfp_net_is_data_vnic(nn)) { |
161 | err = nfp_app_vnic_init(app: pf->app, nn); |
162 | if (err) |
163 | goto err_debugfs_vnic_clean; |
164 | } |
165 | |
166 | return 0; |
167 | |
168 | err_debugfs_vnic_clean: |
169 | nfp_net_debugfs_dir_clean(dir: &nn->debugfs_dir); |
170 | nfp_net_clean(nn); |
171 | err_devlink_port_clean: |
172 | if (nn->port) |
173 | nfp_devlink_port_unregister(port: nn->port); |
174 | return err; |
175 | } |
176 | |
177 | static int |
178 | nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar, |
179 | void __iomem *qc_bar, int stride) |
180 | { |
181 | struct nfp_net *nn; |
182 | unsigned int i; |
183 | int err; |
184 | |
185 | for (i = 0; i < pf->max_data_vnics; i++) { |
186 | nn = nfp_net_pf_alloc_vnic(pf, needs_netdev: true, ctrl_bar, qc_bar, |
187 | stride, id: i); |
188 | if (IS_ERR(ptr: nn)) { |
189 | err = PTR_ERR(ptr: nn); |
190 | goto err_free_prev; |
191 | } |
192 | |
193 | if (nn->port) |
194 | nn->port->link_cb = nfp_net_refresh_port_table; |
195 | |
196 | ctrl_bar += NFP_PF_CSR_SLICE_SIZE; |
197 | |
198 | /* Kill the vNIC if app init marked it as invalid */ |
199 | if (nn->port && nn->port->type == NFP_PORT_INVALID) |
200 | nfp_net_pf_free_vnic(pf, nn); |
201 | } |
202 | |
203 | if (list_empty(head: &pf->vnics)) |
204 | return -ENODEV; |
205 | |
206 | return 0; |
207 | |
208 | err_free_prev: |
209 | nfp_net_pf_free_vnics(pf); |
210 | return err; |
211 | } |
212 | |
213 | static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
214 | { |
215 | if (nfp_net_is_data_vnic(nn)) |
216 | nfp_app_vnic_clean(app: pf->app, nn); |
217 | nfp_net_debugfs_dir_clean(dir: &nn->debugfs_dir); |
218 | nfp_net_clean(nn); |
219 | if (nn->port) |
220 | nfp_devlink_port_unregister(port: nn->port); |
221 | } |
222 | |
223 | static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf) |
224 | { |
225 | unsigned int wanted_irqs, num_irqs, vnics_left, irqs_left; |
226 | struct nfp_net *nn; |
227 | |
228 | /* Get MSI-X vectors */ |
229 | wanted_irqs = 0; |
230 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
231 | wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs; |
232 | pf->irq_entries = kcalloc(n: wanted_irqs, size: sizeof(*pf->irq_entries), |
233 | GFP_KERNEL); |
234 | if (!pf->irq_entries) |
235 | return -ENOMEM; |
236 | |
237 | num_irqs = nfp_net_irqs_alloc(pdev: pf->pdev, irq_entries: pf->irq_entries, |
238 | NFP_NET_MIN_VNIC_IRQS * pf->num_vnics, |
239 | want_irqs: wanted_irqs); |
240 | if (!num_irqs) { |
241 | nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors\n" ); |
242 | kfree(objp: pf->irq_entries); |
243 | return -ENOMEM; |
244 | } |
245 | |
246 | /* Distribute IRQs to vNICs */ |
247 | irqs_left = num_irqs; |
248 | vnics_left = pf->num_vnics; |
249 | list_for_each_entry(nn, &pf->vnics, vnic_list) { |
250 | unsigned int n; |
251 | |
252 | n = min(NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs, |
253 | DIV_ROUND_UP(irqs_left, vnics_left)); |
254 | nfp_net_irqs_assign(nn, irq_entries: &pf->irq_entries[num_irqs - irqs_left], |
255 | n); |
256 | irqs_left -= n; |
257 | vnics_left--; |
258 | } |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static void nfp_net_pf_free_irqs(struct nfp_pf *pf) |
264 | { |
265 | nfp_net_irqs_disable(pdev: pf->pdev); |
266 | kfree(objp: pf->irq_entries); |
267 | } |
268 | |
269 | static int nfp_net_pf_init_vnics(struct nfp_pf *pf) |
270 | { |
271 | struct nfp_net *nn; |
272 | unsigned int id; |
273 | int err; |
274 | |
275 | /* Finish vNIC init and register */ |
276 | id = 0; |
277 | list_for_each_entry(nn, &pf->vnics, vnic_list) { |
278 | if (!nfp_net_is_data_vnic(nn)) |
279 | continue; |
280 | err = nfp_net_pf_init_vnic(pf, nn, id); |
281 | if (err) |
282 | goto err_prev_deinit; |
283 | |
284 | id++; |
285 | } |
286 | |
287 | return 0; |
288 | |
289 | err_prev_deinit: |
290 | list_for_each_entry_continue_reverse(nn, &pf->vnics, vnic_list) |
291 | if (nfp_net_is_data_vnic(nn)) |
292 | nfp_net_pf_clean_vnic(pf, nn); |
293 | return err; |
294 | } |
295 | |
296 | static int |
297 | nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) |
298 | { |
299 | struct devlink *devlink = priv_to_devlink(priv: pf); |
300 | u8 __iomem *ctrl_bar; |
301 | int err; |
302 | |
303 | pf->app = nfp_app_alloc(pf, id: nfp_net_pf_get_app_id(pf)); |
304 | if (IS_ERR(ptr: pf->app)) |
305 | return PTR_ERR(ptr: pf->app); |
306 | |
307 | devl_lock(devlink); |
308 | err = nfp_app_init(app: pf->app); |
309 | devl_unlock(devlink); |
310 | if (err) |
311 | goto err_free; |
312 | |
313 | if (!nfp_app_needs_ctrl_vnic(app: pf->app)) |
314 | return 0; |
315 | |
316 | ctrl_bar = nfp_pf_map_rtsym(pf, name: "net.ctrl" , sym_fmt: "_pf%u_net_ctrl_bar" , |
317 | NFP_PF_CSR_SLICE_SIZE, area: &pf->ctrl_vnic_bar); |
318 | if (IS_ERR(ptr: ctrl_bar)) { |
319 | nfp_err(pf->cpp, "Failed to find ctrl vNIC memory symbol\n" ); |
320 | err = PTR_ERR(ptr: ctrl_bar); |
321 | goto err_app_clean; |
322 | } |
323 | |
324 | pf->ctrl_vnic = nfp_net_pf_alloc_vnic(pf, needs_netdev: false, ctrl_bar, qc_bar, |
325 | stride, id: 0); |
326 | if (IS_ERR(ptr: pf->ctrl_vnic)) { |
327 | err = PTR_ERR(ptr: pf->ctrl_vnic); |
328 | goto err_unmap; |
329 | } |
330 | |
331 | return 0; |
332 | |
333 | err_unmap: |
334 | nfp_cpp_area_release_free(area: pf->ctrl_vnic_bar); |
335 | err_app_clean: |
336 | devl_lock(devlink); |
337 | nfp_app_clean(app: pf->app); |
338 | devl_unlock(devlink); |
339 | err_free: |
340 | nfp_app_free(app: pf->app); |
341 | pf->app = NULL; |
342 | return err; |
343 | } |
344 | |
345 | static void nfp_net_pf_app_clean(struct nfp_pf *pf) |
346 | { |
347 | struct devlink *devlink = priv_to_devlink(priv: pf); |
348 | |
349 | if (pf->ctrl_vnic) { |
350 | nfp_net_pf_free_vnic(pf, nn: pf->ctrl_vnic); |
351 | nfp_cpp_area_release_free(area: pf->ctrl_vnic_bar); |
352 | } |
353 | |
354 | devl_lock(devlink); |
355 | nfp_app_clean(app: pf->app); |
356 | devl_unlock(devlink); |
357 | |
358 | nfp_app_free(app: pf->app); |
359 | pf->app = NULL; |
360 | } |
361 | |
362 | static int nfp_net_pf_app_start_ctrl(struct nfp_pf *pf) |
363 | { |
364 | int err; |
365 | |
366 | if (!pf->ctrl_vnic) |
367 | return 0; |
368 | err = nfp_net_pf_init_vnic(pf, nn: pf->ctrl_vnic, id: 0); |
369 | if (err) |
370 | return err; |
371 | |
372 | err = nfp_ctrl_open(nn: pf->ctrl_vnic); |
373 | if (err) |
374 | goto err_clean_ctrl; |
375 | |
376 | return 0; |
377 | |
378 | err_clean_ctrl: |
379 | nfp_net_pf_clean_vnic(pf, nn: pf->ctrl_vnic); |
380 | return err; |
381 | } |
382 | |
383 | static void nfp_net_pf_app_stop_ctrl(struct nfp_pf *pf) |
384 | { |
385 | if (!pf->ctrl_vnic) |
386 | return; |
387 | nfp_ctrl_close(nn: pf->ctrl_vnic); |
388 | nfp_net_pf_clean_vnic(pf, nn: pf->ctrl_vnic); |
389 | } |
390 | |
391 | static int nfp_net_pf_app_start(struct nfp_pf *pf) |
392 | { |
393 | int err; |
394 | |
395 | err = nfp_net_pf_app_start_ctrl(pf); |
396 | if (err) |
397 | return err; |
398 | |
399 | err = nfp_app_start(app: pf->app, ctrl: pf->ctrl_vnic); |
400 | if (err) |
401 | goto err_ctrl_stop; |
402 | |
403 | if (pf->num_vfs) { |
404 | err = nfp_app_sriov_enable(app: pf->app, num_vfs: pf->num_vfs); |
405 | if (err) |
406 | goto err_app_stop; |
407 | } |
408 | |
409 | return 0; |
410 | |
411 | err_app_stop: |
412 | nfp_app_stop(app: pf->app); |
413 | err_ctrl_stop: |
414 | nfp_net_pf_app_stop_ctrl(pf); |
415 | return err; |
416 | } |
417 | |
418 | static void nfp_net_pf_app_stop(struct nfp_pf *pf) |
419 | { |
420 | if (pf->num_vfs) |
421 | nfp_app_sriov_disable(app: pf->app); |
422 | nfp_app_stop(app: pf->app); |
423 | nfp_net_pf_app_stop_ctrl(pf); |
424 | } |
425 | |
426 | static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) |
427 | { |
428 | if (pf->vfcfg_tbl2_area) |
429 | nfp_cpp_area_release_free(area: pf->vfcfg_tbl2_area); |
430 | if (pf->vf_cfg_bar) |
431 | nfp_cpp_area_release_free(area: pf->vf_cfg_bar); |
432 | if (pf->mac_stats_bar) |
433 | nfp_cpp_area_release_free(area: pf->mac_stats_bar); |
434 | nfp_cpp_area_release_free(area: pf->qc_area); |
435 | nfp_cpp_area_release_free(area: pf->data_vnic_bar); |
436 | } |
437 | |
438 | static int nfp_net_pci_map_mem(struct nfp_pf *pf) |
439 | { |
440 | u32 min_size, cpp_id; |
441 | u8 __iomem *mem; |
442 | int err; |
443 | |
444 | min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; |
445 | mem = nfp_pf_map_rtsym(pf, name: "net.bar0" , sym_fmt: "_pf%d_net_bar0" , |
446 | min_size, area: &pf->data_vnic_bar); |
447 | if (IS_ERR(ptr: mem)) { |
448 | nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n" ); |
449 | return PTR_ERR(ptr: mem); |
450 | } |
451 | |
452 | if (pf->eth_tbl) { |
453 | min_size = NFP_MAC_STATS_SIZE * (pf->eth_tbl->max_index + 1); |
454 | pf->mac_stats_mem = nfp_rtsym_map(rtbl: pf->rtbl, name: "_mac_stats" , |
455 | id: "net.macstats" , min_size, |
456 | area: &pf->mac_stats_bar); |
457 | if (IS_ERR(ptr: pf->mac_stats_mem)) { |
458 | if (PTR_ERR(ptr: pf->mac_stats_mem) != -ENOENT) { |
459 | err = PTR_ERR(ptr: pf->mac_stats_mem); |
460 | goto err_unmap_ctrl; |
461 | } |
462 | pf->mac_stats_mem = NULL; |
463 | } |
464 | } |
465 | |
466 | pf->vf_cfg_mem = nfp_pf_map_rtsym(pf, name: "net.vfcfg" , sym_fmt: "_pf%d_net_vf_bar" , |
467 | NFP_NET_CFG_BAR_SZ * pf->limit_vfs, |
468 | area: &pf->vf_cfg_bar); |
469 | if (IS_ERR(ptr: pf->vf_cfg_mem)) { |
470 | if (PTR_ERR(ptr: pf->vf_cfg_mem) != -ENOENT) { |
471 | err = PTR_ERR(ptr: pf->vf_cfg_mem); |
472 | goto err_unmap_mac_stats; |
473 | } |
474 | pf->vf_cfg_mem = NULL; |
475 | } |
476 | |
477 | min_size = NFP_NET_VF_CFG_SZ * pf->limit_vfs + NFP_NET_VF_CFG_MB_SZ; |
478 | pf->vfcfg_tbl2 = nfp_pf_map_rtsym(pf, name: "net.vfcfg_tbl2" , |
479 | sym_fmt: "_pf%d_net_vf_cfg2" , |
480 | min_size, area: &pf->vfcfg_tbl2_area); |
481 | if (IS_ERR(ptr: pf->vfcfg_tbl2)) { |
482 | if (PTR_ERR(ptr: pf->vfcfg_tbl2) != -ENOENT) { |
483 | err = PTR_ERR(ptr: pf->vfcfg_tbl2); |
484 | goto err_unmap_vf_cfg; |
485 | } |
486 | pf->vfcfg_tbl2 = NULL; |
487 | } |
488 | |
489 | cpp_id = NFP_CPP_ISLAND_ID(0, NFP_CPP_ACTION_RW, 0, 0); |
490 | mem = nfp_cpp_map_area(cpp: pf->cpp, name: "net.qc" , cpp_id, |
491 | addr: nfp_qcp_queue_offset(dev_info: pf->dev_info, queue: 0), |
492 | size: pf->dev_info->qc_area_sz, area: &pf->qc_area); |
493 | if (IS_ERR(ptr: mem)) { |
494 | nfp_err(pf->cpp, "Failed to map Queue Controller area.\n" ); |
495 | err = PTR_ERR(ptr: mem); |
496 | goto err_unmap_vfcfg_tbl2; |
497 | } |
498 | |
499 | return 0; |
500 | |
501 | err_unmap_vfcfg_tbl2: |
502 | if (pf->vfcfg_tbl2_area) |
503 | nfp_cpp_area_release_free(area: pf->vfcfg_tbl2_area); |
504 | err_unmap_vf_cfg: |
505 | if (pf->vf_cfg_bar) |
506 | nfp_cpp_area_release_free(area: pf->vf_cfg_bar); |
507 | err_unmap_mac_stats: |
508 | if (pf->mac_stats_bar) |
509 | nfp_cpp_area_release_free(area: pf->mac_stats_bar); |
510 | err_unmap_ctrl: |
511 | nfp_cpp_area_release_free(area: pf->data_vnic_bar); |
512 | return err; |
513 | } |
514 | |
515 | static const unsigned int lr_to_speed[] = { |
516 | [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0, |
517 | [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN, |
518 | [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000, |
519 | [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000, |
520 | [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000, |
521 | [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000, |
522 | [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000, |
523 | [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000, |
524 | }; |
525 | |
526 | unsigned int nfp_net_lr2speed(unsigned int linkrate) |
527 | { |
528 | if (linkrate < ARRAY_SIZE(lr_to_speed)) |
529 | return lr_to_speed[linkrate]; |
530 | |
531 | return SPEED_UNKNOWN; |
532 | } |
533 | |
534 | unsigned int nfp_net_speed2lr(unsigned int speed) |
535 | { |
536 | int i; |
537 | |
538 | for (i = 0; i < ARRAY_SIZE(lr_to_speed); i++) { |
539 | if (speed == lr_to_speed[i]) |
540 | return i; |
541 | } |
542 | |
543 | return NFP_NET_CFG_STS_LINK_RATE_UNKNOWN; |
544 | } |
545 | |
546 | static void nfp_net_notify_port_speed(struct nfp_port *port) |
547 | { |
548 | struct net_device *netdev = port->netdev; |
549 | struct nfp_net *nn; |
550 | u16 sts; |
551 | |
552 | if (!nfp_netdev_is_nfp_net(netdev)) |
553 | return; |
554 | |
555 | nn = netdev_priv(dev: netdev); |
556 | sts = nn_readw(nn, NFP_NET_CFG_STS); |
557 | |
558 | if (!(sts & NFP_NET_CFG_STS_LINK)) { |
559 | nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, NFP_NET_CFG_STS_LINK_RATE_UNKNOWN); |
560 | return; |
561 | } |
562 | |
563 | nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, val: nfp_net_speed2lr(speed: port->eth_port->speed)); |
564 | } |
565 | |
566 | static int |
567 | nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, |
568 | struct nfp_eth_table *eth_table) |
569 | { |
570 | struct nfp_eth_table_port *eth_port; |
571 | |
572 | ASSERT_RTNL(); |
573 | |
574 | eth_port = nfp_net_find_port(eth_tbl: eth_table, index: port->eth_id); |
575 | if (!eth_port) { |
576 | set_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
577 | nfp_warn(cpp, "Warning: port #%d not present after reconfig\n" , |
578 | port->eth_id); |
579 | return -EIO; |
580 | } |
581 | if (eth_port->override_changed) { |
582 | nfp_warn(cpp, "Port #%d config changed, unregistering. Driver reload required before port will be operational again.\n" , port->eth_id); |
583 | port->type = NFP_PORT_INVALID; |
584 | } |
585 | |
586 | memcpy(port->eth_port, eth_port, sizeof(*eth_port)); |
587 | nfp_net_notify_port_speed(port); |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | int nfp_net_refresh_port_table_sync(struct nfp_pf *pf) |
593 | { |
594 | struct devlink *devlink = priv_to_devlink(priv: pf); |
595 | struct nfp_eth_table *eth_table; |
596 | struct nfp_net *nn, *next; |
597 | struct nfp_port *port; |
598 | int err; |
599 | |
600 | devl_assert_locked(devlink); |
601 | |
602 | /* Check for nfp_net_pci_remove() racing against us */ |
603 | if (list_empty(head: &pf->vnics)) |
604 | return 0; |
605 | |
606 | /* Update state of all ports */ |
607 | rtnl_lock(); |
608 | list_for_each_entry(port, &pf->ports, port_list) |
609 | clear_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
610 | |
611 | eth_table = nfp_eth_read_ports(cpp: pf->cpp); |
612 | if (!eth_table) { |
613 | list_for_each_entry(port, &pf->ports, port_list) |
614 | if (__nfp_port_get_eth_port(port)) |
615 | set_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
616 | rtnl_unlock(); |
617 | nfp_err(pf->cpp, "Error refreshing port config!\n" ); |
618 | return -EIO; |
619 | } |
620 | |
621 | list_for_each_entry(port, &pf->ports, port_list) |
622 | if (__nfp_port_get_eth_port(port)) |
623 | nfp_net_eth_port_update(cpp: pf->cpp, port, eth_table); |
624 | rtnl_unlock(); |
625 | |
626 | kfree(objp: eth_table); |
627 | |
628 | /* Resync repr state. This may cause reprs to be removed. */ |
629 | err = nfp_reprs_resync_phys_ports(app: pf->app); |
630 | if (err) |
631 | return err; |
632 | |
633 | /* Shoot off the ports which became invalid */ |
634 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { |
635 | if (!nn->port || nn->port->type != NFP_PORT_INVALID) |
636 | continue; |
637 | |
638 | nfp_net_pf_clean_vnic(pf, nn); |
639 | nfp_net_pf_free_vnic(pf, nn); |
640 | } |
641 | |
642 | return 0; |
643 | } |
644 | |
645 | static void nfp_net_refresh_vnics(struct work_struct *work) |
646 | { |
647 | struct nfp_pf *pf = container_of(work, struct nfp_pf, |
648 | port_refresh_work); |
649 | struct devlink *devlink = priv_to_devlink(priv: pf); |
650 | |
651 | devl_lock(devlink); |
652 | nfp_net_refresh_port_table_sync(pf); |
653 | devl_unlock(devlink); |
654 | } |
655 | |
656 | void nfp_net_refresh_port_table(struct nfp_port *port) |
657 | { |
658 | struct nfp_pf *pf = port->app->pf; |
659 | |
660 | set_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
661 | |
662 | queue_work(wq: pf->wq, work: &pf->port_refresh_work); |
663 | } |
664 | |
665 | int nfp_net_refresh_eth_port(struct nfp_port *port) |
666 | { |
667 | struct nfp_cpp *cpp = port->app->cpp; |
668 | struct nfp_eth_table *eth_table; |
669 | int ret; |
670 | |
671 | clear_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
672 | |
673 | eth_table = nfp_eth_read_ports(cpp); |
674 | if (!eth_table) { |
675 | set_bit(nr: NFP_PORT_CHANGED, addr: &port->flags); |
676 | nfp_err(cpp, "Error refreshing port state table!\n" ); |
677 | return -EIO; |
678 | } |
679 | |
680 | ret = nfp_net_eth_port_update(cpp, port, eth_table); |
681 | |
682 | kfree(objp: eth_table); |
683 | |
684 | return ret; |
685 | } |
686 | |
687 | /* |
688 | * PCI device functions |
689 | */ |
690 | int nfp_net_pci_probe(struct nfp_pf *pf) |
691 | { |
692 | struct devlink *devlink = priv_to_devlink(priv: pf); |
693 | struct nfp_net_fw_version fw_ver; |
694 | u8 __iomem *ctrl_bar, *qc_bar; |
695 | int stride; |
696 | int err; |
697 | |
698 | INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_vnics); |
699 | |
700 | if (!pf->rtbl) { |
701 | nfp_err(pf->cpp, "No %s, giving up.\n" , |
702 | pf->fw_loaded ? "symbol table" : "firmware found" ); |
703 | return -EINVAL; |
704 | } |
705 | |
706 | pf->max_data_vnics = nfp_net_pf_get_num_ports(pf); |
707 | if ((int)pf->max_data_vnics < 0) |
708 | return pf->max_data_vnics; |
709 | |
710 | err = nfp_net_pci_map_mem(pf); |
711 | if (err) |
712 | return err; |
713 | |
714 | ctrl_bar = nfp_cpp_area_iomem(area: pf->data_vnic_bar); |
715 | qc_bar = nfp_cpp_area_iomem(area: pf->qc_area); |
716 | if (!ctrl_bar || !qc_bar) { |
717 | err = -EIO; |
718 | goto err_unmap; |
719 | } |
720 | |
721 | nfp_net_get_fw_version(fw_ver: &fw_ver, ctrl_bar); |
722 | if (fw_ver.extend & NFP_NET_CFG_VERSION_RESERVED_MASK || |
723 | fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { |
724 | nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n" , |
725 | fw_ver.extend, fw_ver.class, |
726 | fw_ver.major, fw_ver.minor); |
727 | err = -EINVAL; |
728 | goto err_unmap; |
729 | } |
730 | |
731 | /* Determine stride */ |
732 | if (nfp_net_fw_ver_eq(fw_ver: &fw_ver, extend: 0, class: 0, major: 0, minor: 1)) { |
733 | stride = 2; |
734 | nfp_warn(pf->cpp, "OBSOLETE Firmware detected - VF isolation not available\n" ); |
735 | } else { |
736 | switch (fw_ver.major) { |
737 | case 1 ... 5: |
738 | stride = 4; |
739 | break; |
740 | default: |
741 | nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n" , |
742 | fw_ver.extend, fw_ver.class, |
743 | fw_ver.major, fw_ver.minor); |
744 | err = -EINVAL; |
745 | goto err_unmap; |
746 | } |
747 | } |
748 | |
749 | err = nfp_net_pf_app_init(pf, qc_bar, stride); |
750 | if (err) |
751 | goto err_unmap; |
752 | |
753 | err = nfp_shared_buf_register(pf); |
754 | if (err) |
755 | goto err_devlink_unreg; |
756 | |
757 | devl_lock(devlink); |
758 | err = nfp_devlink_params_register(pf); |
759 | if (err) |
760 | goto err_shared_buf_unreg; |
761 | |
762 | pf->ddir = nfp_net_debugfs_device_add(pdev: pf->pdev); |
763 | |
764 | /* Allocate the vnics and do basic init */ |
765 | err = nfp_net_pf_alloc_vnics(pf, ctrl_bar, qc_bar, stride); |
766 | if (err) |
767 | goto err_clean_ddir; |
768 | |
769 | err = nfp_net_pf_alloc_irqs(pf); |
770 | if (err) |
771 | goto err_free_vnics; |
772 | |
773 | err = nfp_net_pf_app_start(pf); |
774 | if (err) |
775 | goto err_free_irqs; |
776 | |
777 | err = nfp_net_pf_init_vnics(pf); |
778 | if (err) |
779 | goto err_stop_app; |
780 | |
781 | devl_unlock(devlink); |
782 | devlink_register(devlink); |
783 | |
784 | return 0; |
785 | |
786 | err_stop_app: |
787 | nfp_net_pf_app_stop(pf); |
788 | err_free_irqs: |
789 | nfp_net_pf_free_irqs(pf); |
790 | err_free_vnics: |
791 | nfp_net_pf_free_vnics(pf); |
792 | err_clean_ddir: |
793 | nfp_net_debugfs_dir_clean(dir: &pf->ddir); |
794 | nfp_devlink_params_unregister(pf); |
795 | err_shared_buf_unreg: |
796 | devl_unlock(devlink); |
797 | nfp_shared_buf_unregister(pf); |
798 | err_devlink_unreg: |
799 | cancel_work_sync(work: &pf->port_refresh_work); |
800 | nfp_net_pf_app_clean(pf); |
801 | err_unmap: |
802 | nfp_net_pci_unmap_mem(pf); |
803 | return err; |
804 | } |
805 | |
806 | void nfp_net_pci_remove(struct nfp_pf *pf) |
807 | { |
808 | struct devlink *devlink = priv_to_devlink(priv: pf); |
809 | struct nfp_net *nn, *next; |
810 | |
811 | devlink_unregister(devlink: priv_to_devlink(priv: pf)); |
812 | devl_lock(devlink); |
813 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { |
814 | if (!nfp_net_is_data_vnic(nn)) |
815 | continue; |
816 | nfp_net_pf_clean_vnic(pf, nn); |
817 | nfp_net_pf_free_vnic(pf, nn); |
818 | } |
819 | |
820 | nfp_net_pf_app_stop(pf); |
821 | /* stop app first, to avoid double free of ctrl vNIC's ddir */ |
822 | nfp_net_debugfs_dir_clean(dir: &pf->ddir); |
823 | |
824 | nfp_devlink_params_unregister(pf); |
825 | |
826 | devl_unlock(devlink); |
827 | |
828 | nfp_shared_buf_unregister(pf); |
829 | |
830 | nfp_net_pf_free_irqs(pf); |
831 | nfp_net_pf_app_clean(pf); |
832 | nfp_net_pci_unmap_mem(pf); |
833 | |
834 | cancel_work_sync(work: &pf->port_refresh_work); |
835 | } |
836 | |