Commit | Line | Data |
---|---|---|
63461a02 JK |
1 | /* |
2 | * Copyright (C) 2015-2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * nfp_net_main.c | |
36 | * Netronome network device driver: Main entry point | |
37 | * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> | |
38 | * Alejandro Lucero <alejandro.lucero@netronome.com> | |
39 | * Jason McMullan <jason.mcmullan@netronome.com> | |
40 | * Rolf Neugebauer <rolf.neugebauer@netronome.com> | |
41 | */ | |
42 | ||
43 | #include <linux/etherdevice.h> | |
44 | #include <linux/kernel.h> | |
45 | #include <linux/init.h> | |
ec8b1fbe | 46 | #include <linux/lockdep.h> |
63461a02 JK |
47 | #include <linux/pci.h> |
48 | #include <linux/pci_regs.h> | |
49 | #include <linux/msi.h> | |
50 | #include <linux/random.h> | |
172f638c | 51 | #include <linux/rtnetlink.h> |
63461a02 JK |
52 | |
53 | #include "nfpcore/nfp.h" | |
54 | #include "nfpcore/nfp_cpp.h" | |
55 | #include "nfpcore/nfp_nffw.h" | |
ce22f5a2 | 56 | #include "nfpcore/nfp_nsp.h" |
63461a02 | 57 | #include "nfpcore/nfp6000_pcie.h" |
7ac9ebd5 | 58 | #include "nfp_app.h" |
63461a02 JK |
59 | #include "nfp_net_ctrl.h" |
60 | #include "nfp_net.h" | |
61 | #include "nfp_main.h" | |
eb488c26 | 62 | #include "nfp_port.h" |
63461a02 JK |
63 | |
64 | #define NFP_PF_CSR_SLICE_SIZE (32 * 1024) | |
65 | ||
9baa4885 | 66 | static int nfp_is_ready(struct nfp_pf *pf) |
63461a02 JK |
67 | { |
68 | const char *cp; | |
69 | long state; | |
70 | int err; | |
71 | ||
9baa4885 | 72 | cp = nfp_hwinfo_lookup(pf->hwinfo, "board.state"); |
63461a02 JK |
73 | if (!cp) |
74 | return 0; | |
75 | ||
76 | err = kstrtol(cp, 0, &state); | |
77 | if (err < 0) | |
78 | return 0; | |
79 | ||
80 | return state == 15; | |
81 | } | |
82 | ||
b9de0077 JK |
83 | /** |
84 | * nfp_net_get_mac_addr() - Get the MAC address. | |
9baa4885 | 85 | * @pf: NFP PF handle |
93da7d96 | 86 | * @port: NFP port structure |
b9de0077 JK |
87 | * |
88 | * First try to get the MAC address from NSP ETH table. If that | |
cb2cda48 | 89 | * fails generate a random address. |
b9de0077 | 90 | */ |
cb2cda48 | 91 | void nfp_net_get_mac_addr(struct nfp_pf *pf, struct nfp_port *port) |
63461a02 | 92 | { |
eb488c26 | 93 | struct nfp_eth_table_port *eth_port; |
63461a02 | 94 | |
93da7d96 | 95 | eth_port = __nfp_port_get_eth_port(port); |
cb2cda48 | 96 | if (!eth_port) { |
93da7d96 | 97 | eth_hw_addr_random(port->netdev); |
63461a02 JK |
98 | return; |
99 | } | |
100 | ||
cb2cda48 JK |
101 | ether_addr_copy(port->netdev->dev_addr, eth_port->mac_addr); |
102 | ether_addr_copy(port->netdev->perm_addr, eth_port->mac_addr); | |
63461a02 JK |
103 | } |
104 | ||
2eb333c4 JK |
105 | static struct nfp_eth_table_port * |
106 | nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int index) | |
63461a02 JK |
107 | { |
108 | int i; | |
109 | ||
90fdc561 | 110 | for (i = 0; eth_tbl && i < eth_tbl->count; i++) |
2eb333c4 | 111 | if (eth_tbl->ports[i].index == index) |
90fdc561 | 112 | return ð_tbl->ports[i]; |
47465aed | 113 | |
b9de0077 | 114 | return NULL; |
63461a02 JK |
115 | } |
116 | ||
69394af5 JK |
117 | static int |
118 | nfp_net_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format, | |
119 | unsigned int default_val) | |
63461a02 JK |
120 | { |
121 | char name[256]; | |
63461a02 JK |
122 | int err = 0; |
123 | u64 val; | |
124 | ||
69394af5 | 125 | snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp)); |
63461a02 | 126 | |
af4fa7ea | 127 | val = nfp_rtsym_read_le(pf->rtbl, name, &err); |
63461a02 | 128 | if (err) { |
69394af5 JK |
129 | if (err == -ENOENT) |
130 | return default_val; | |
131 | nfp_err(pf->cpp, "Unable to read symbol %s\n", name); | |
132 | return err; | |
63461a02 JK |
133 | } |
134 | ||
135 | return val; | |
136 | } | |
137 | ||
69394af5 JK |
138 | static int nfp_net_pf_get_num_ports(struct nfp_pf *pf) |
139 | { | |
140 | return nfp_net_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1); | |
141 | } | |
142 | ||
8aa0cb00 JK |
143 | static int nfp_net_pf_get_app_id(struct nfp_pf *pf) |
144 | { | |
145 | return nfp_net_pf_rtsym_read_optional(pf, "_pf%u_net_app_id", | |
146 | NFP_APP_CORE_NIC); | |
147 | } | |
148 | ||
c24ca95f JK |
149 | static u8 __iomem * |
150 | nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt, | |
151 | unsigned int min_size, struct nfp_cpp_area **area) | |
63461a02 | 152 | { |
63461a02 | 153 | char pf_symbol[256]; |
63461a02 | 154 | |
c24ca95f | 155 | snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt, |
cd6f8db9 | 156 | nfp_cppcore_pcie_unit(pf->cpp)); |
63461a02 | 157 | |
f8473024 | 158 | return nfp_rtsym_map(pf->rtbl, pf_symbol, name, min_size, area); |
63461a02 JK |
159 | } |
160 | ||
9140b30d JK |
161 | static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
162 | { | |
eb488c26 | 163 | nfp_port_free(nn->port); |
9140b30d JK |
164 | list_del(&nn->vnic_list); |
165 | pf->num_vnics--; | |
166 | nfp_net_free(nn); | |
167 | } | |
168 | ||
d4e7f092 | 169 | static void nfp_net_pf_free_vnics(struct nfp_pf *pf) |
63461a02 | 170 | { |
02082701 | 171 | struct nfp_net *nn, *next; |
63461a02 | 172 | |
02082701 JK |
173 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) |
174 | if (nfp_net_is_data_vnic(nn)) | |
175 | nfp_net_pf_free_vnic(pf, nn); | |
63461a02 JK |
176 | } |
177 | ||
178 | static struct nfp_net * | |
a7b1ad08 | 179 | nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, |
73e253f0 | 180 | void __iomem *ctrl_bar, void __iomem *qc_bar, |
2eb333c4 | 181 | int stride, unsigned int id) |
63461a02 | 182 | { |
73e253f0 | 183 | u32 tx_base, rx_base, n_tx_rings, n_rx_rings; |
63461a02 | 184 | struct nfp_net *nn; |
8aa0cb00 | 185 | int err; |
63461a02 | 186 | |
73e253f0 JK |
187 | tx_base = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); |
188 | rx_base = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); | |
63461a02 JK |
189 | n_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS); |
190 | n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); | |
191 | ||
d4e7f092 | 192 | /* Allocate and initialise the vNIC */ |
a7b1ad08 | 193 | nn = nfp_net_alloc(pf->pdev, needs_netdev, n_tx_rings, n_rx_rings); |
63461a02 JK |
194 | if (IS_ERR(nn)) |
195 | return nn; | |
196 | ||
7ac9ebd5 | 197 | nn->app = pf->app; |
21537bc7 | 198 | nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar); |
d2b84397 | 199 | nn->dp.ctrl_bar = ctrl_bar; |
73e253f0 JK |
200 | nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ; |
201 | nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ; | |
79c12a75 | 202 | nn->dp.is_vf = 0; |
63461a02 JK |
203 | nn->stride_rx = stride; |
204 | nn->stride_tx = stride; | |
eb488c26 | 205 | |
02082701 | 206 | if (needs_netdev) { |
2eb333c4 | 207 | err = nfp_app_vnic_init(pf->app, nn, id); |
02082701 JK |
208 | if (err) { |
209 | nfp_net_free(nn); | |
210 | return ERR_PTR(err); | |
211 | } | |
eb488c26 | 212 | } |
d88b0a23 JK |
213 | |
214 | pf->num_vnics++; | |
215 | list_add_tail(&nn->vnic_list, &pf->vnics); | |
63461a02 JK |
216 | |
217 | return nn; | |
218 | } | |
219 | ||
220 | static int | |
d4e7f092 | 221 | nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) |
63461a02 JK |
222 | { |
223 | int err; | |
224 | ||
63461a02 JK |
225 | /* Get ME clock frequency from ctrl BAR |
226 | * XXX for now frequency is hardcoded until we figure out how | |
227 | * to get the value from nfp-hwinfo into ctrl bar | |
228 | */ | |
229 | nn->me_freq_mhz = 1200; | |
230 | ||
beba69ca | 231 | err = nfp_net_init(nn); |
63461a02 JK |
232 | if (err) |
233 | return err; | |
234 | ||
d4e7f092 | 235 | nfp_net_debugfs_vnic_add(nn, pf->ddir, id); |
63461a02 | 236 | |
53e7a08f JK |
237 | if (nn->port) { |
238 | err = nfp_devlink_port_register(pf->app, nn->port); | |
239 | if (err) | |
240 | goto err_dfs_clean; | |
241 | } | |
242 | ||
63461a02 JK |
243 | nfp_net_info(nn); |
244 | ||
245 | return 0; | |
53e7a08f JK |
246 | |
247 | err_dfs_clean: | |
248 | nfp_net_debugfs_dir_clean(&nn->debugfs_dir); | |
249 | nfp_net_clean(nn); | |
250 | return err; | |
63461a02 JK |
251 | } |
252 | ||
253 | static int | |
d4e7f092 | 254 | nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar, |
21537bc7 | 255 | void __iomem *qc_bar, int stride) |
63461a02 | 256 | { |
63461a02 JK |
257 | struct nfp_net *nn; |
258 | unsigned int i; | |
259 | int err; | |
260 | ||
d4e7f092 | 261 | for (i = 0; i < pf->max_data_vnics; i++) { |
73e253f0 | 262 | nn = nfp_net_pf_alloc_vnic(pf, true, ctrl_bar, qc_bar, |
21537bc7 | 263 | stride, i); |
d88b0a23 JK |
264 | if (IS_ERR(nn)) { |
265 | err = PTR_ERR(nn); | |
266 | goto err_free_prev; | |
63461a02 | 267 | } |
63461a02 JK |
268 | |
269 | ctrl_bar += NFP_PF_CSR_SLICE_SIZE; | |
d88b0a23 | 270 | |
8aa0cb00 JK |
271 | /* Kill the vNIC if app init marked it as invalid */ |
272 | if (nn->port && nn->port->type == NFP_PORT_INVALID) { | |
d88b0a23 JK |
273 | nfp_net_pf_free_vnic(pf, nn); |
274 | continue; | |
275 | } | |
63461a02 JK |
276 | } |
277 | ||
d4e7f092 | 278 | if (list_empty(&pf->vnics)) |
b9de0077 JK |
279 | return -ENODEV; |
280 | ||
63461a02 JK |
281 | return 0; |
282 | ||
283 | err_free_prev: | |
d4e7f092 | 284 | nfp_net_pf_free_vnics(pf); |
63461a02 JK |
285 | return err; |
286 | } | |
287 | ||
71f8a116 JK |
288 | static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
289 | { | |
53e7a08f JK |
290 | if (nn->port) |
291 | nfp_devlink_port_unregister(nn->port); | |
71f8a116 JK |
292 | nfp_net_debugfs_dir_clean(&nn->debugfs_dir); |
293 | nfp_net_clean(nn); | |
bb45e51c | 294 | nfp_app_vnic_clean(pf->app, nn); |
71f8a116 JK |
295 | } |
296 | ||
6d4b0d8e | 297 | static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf) |
63461a02 | 298 | { |
6d4b0d8e | 299 | unsigned int wanted_irqs, num_irqs, vnics_left, irqs_left; |
63461a02 | 300 | struct nfp_net *nn; |
63461a02 JK |
301 | |
302 | /* Get MSI-X vectors */ | |
303 | wanted_irqs = 0; | |
d4e7f092 | 304 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
79c12a75 | 305 | wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs; |
63461a02 JK |
306 | pf->irq_entries = kcalloc(wanted_irqs, sizeof(*pf->irq_entries), |
307 | GFP_KERNEL); | |
6d4b0d8e JK |
308 | if (!pf->irq_entries) |
309 | return -ENOMEM; | |
63461a02 JK |
310 | |
311 | num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries, | |
d4e7f092 | 312 | NFP_NET_MIN_VNIC_IRQS * pf->num_vnics, |
63461a02 JK |
313 | wanted_irqs); |
314 | if (!num_irqs) { | |
6d4b0d8e JK |
315 | nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors\n"); |
316 | kfree(pf->irq_entries); | |
317 | return -ENOMEM; | |
63461a02 JK |
318 | } |
319 | ||
d4e7f092 | 320 | /* Distribute IRQs to vNICs */ |
63461a02 | 321 | irqs_left = num_irqs; |
d4e7f092 JK |
322 | vnics_left = pf->num_vnics; |
323 | list_for_each_entry(nn, &pf->vnics, vnic_list) { | |
63461a02 JK |
324 | unsigned int n; |
325 | ||
2c7e41c0 JK |
326 | n = min(NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs, |
327 | DIV_ROUND_UP(irqs_left, vnics_left)); | |
63461a02 JK |
328 | nfp_net_irqs_assign(nn, &pf->irq_entries[num_irqs - irqs_left], |
329 | n); | |
330 | irqs_left -= n; | |
d4e7f092 | 331 | vnics_left--; |
63461a02 JK |
332 | } |
333 | ||
6d4b0d8e JK |
334 | return 0; |
335 | } | |
336 | ||
337 | static void nfp_net_pf_free_irqs(struct nfp_pf *pf) | |
338 | { | |
339 | nfp_net_irqs_disable(pf->pdev); | |
340 | kfree(pf->irq_entries); | |
341 | } | |
342 | ||
343 | static int nfp_net_pf_init_vnics(struct nfp_pf *pf) | |
344 | { | |
345 | struct nfp_net *nn; | |
346 | unsigned int id; | |
347 | int err; | |
348 | ||
d4e7f092 | 349 | /* Finish vNIC init and register */ |
63461a02 | 350 | id = 0; |
d4e7f092 | 351 | list_for_each_entry(nn, &pf->vnics, vnic_list) { |
02082701 JK |
352 | if (!nfp_net_is_data_vnic(nn)) |
353 | continue; | |
d4e7f092 | 354 | err = nfp_net_pf_init_vnic(pf, nn, id); |
63461a02 JK |
355 | if (err) |
356 | goto err_prev_deinit; | |
357 | ||
358 | id++; | |
359 | } | |
360 | ||
361 | return 0; | |
362 | ||
363 | err_prev_deinit: | |
71f8a116 | 364 | list_for_each_entry_continue_reverse(nn, &pf->vnics, vnic_list) |
02082701 JK |
365 | if (nfp_net_is_data_vnic(nn)) |
366 | nfp_net_pf_clean_vnic(pf, nn); | |
63461a02 JK |
367 | return err; |
368 | } | |
369 | ||
02082701 JK |
370 | static int |
371 | nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) | |
7ac9ebd5 | 372 | { |
02082701 | 373 | u8 __iomem *ctrl_bar; |
8aa0cb00 JK |
374 | int err; |
375 | ||
376 | pf->app = nfp_app_alloc(pf, nfp_net_pf_get_app_id(pf)); | |
377 | if (IS_ERR(pf->app)) | |
378 | return PTR_ERR(pf->app); | |
379 | ||
380 | err = nfp_app_init(pf->app); | |
381 | if (err) | |
382 | goto err_free; | |
383 | ||
02082701 JK |
384 | if (!nfp_app_needs_ctrl_vnic(pf->app)) |
385 | return 0; | |
386 | ||
387 | ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%u_net_ctrl_bar", | |
388 | NFP_PF_CSR_SLICE_SIZE, | |
389 | &pf->ctrl_vnic_bar); | |
390 | if (IS_ERR(ctrl_bar)) { | |
a5950182 | 391 | nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); |
02082701 | 392 | err = PTR_ERR(ctrl_bar); |
9ce6bbbb | 393 | goto err_app_clean; |
02082701 JK |
394 | } |
395 | ||
396 | pf->ctrl_vnic = nfp_net_pf_alloc_vnic(pf, false, ctrl_bar, qc_bar, | |
397 | stride, 0); | |
398 | if (IS_ERR(pf->ctrl_vnic)) { | |
399 | err = PTR_ERR(pf->ctrl_vnic); | |
400 | goto err_unmap; | |
401 | } | |
402 | ||
8aa0cb00 | 403 | return 0; |
7ac9ebd5 | 404 | |
02082701 JK |
405 | err_unmap: |
406 | nfp_cpp_area_release_free(pf->ctrl_vnic_bar); | |
9ce6bbbb JK |
407 | err_app_clean: |
408 | nfp_app_clean(pf->app); | |
8aa0cb00 JK |
409 | err_free: |
410 | nfp_app_free(pf->app); | |
9ce6bbbb | 411 | pf->app = NULL; |
8aa0cb00 | 412 | return err; |
7ac9ebd5 JK |
413 | } |
414 | ||
415 | static void nfp_net_pf_app_clean(struct nfp_pf *pf) | |
416 | { | |
02082701 JK |
417 | if (pf->ctrl_vnic) { |
418 | nfp_net_pf_free_vnic(pf, pf->ctrl_vnic); | |
419 | nfp_cpp_area_release_free(pf->ctrl_vnic_bar); | |
420 | } | |
9ce6bbbb | 421 | nfp_app_clean(pf->app); |
7ac9ebd5 | 422 | nfp_app_free(pf->app); |
1851f93f | 423 | pf->app = NULL; |
7ac9ebd5 JK |
424 | } |
425 | ||
02082701 JK |
426 | static int nfp_net_pf_app_start_ctrl(struct nfp_pf *pf) |
427 | { | |
428 | int err; | |
429 | ||
430 | if (!pf->ctrl_vnic) | |
431 | return 0; | |
432 | err = nfp_net_pf_init_vnic(pf, pf->ctrl_vnic, 0); | |
433 | if (err) | |
434 | return err; | |
435 | ||
436 | err = nfp_ctrl_open(pf->ctrl_vnic); | |
437 | if (err) | |
438 | goto err_clean_ctrl; | |
439 | ||
440 | return 0; | |
441 | ||
442 | err_clean_ctrl: | |
443 | nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic); | |
444 | return err; | |
445 | } | |
446 | ||
447 | static void nfp_net_pf_app_stop_ctrl(struct nfp_pf *pf) | |
448 | { | |
449 | if (!pf->ctrl_vnic) | |
450 | return; | |
451 | nfp_ctrl_close(pf->ctrl_vnic); | |
452 | nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic); | |
453 | } | |
454 | ||
455 | static int nfp_net_pf_app_start(struct nfp_pf *pf) | |
456 | { | |
457 | int err; | |
458 | ||
459 | err = nfp_net_pf_app_start_ctrl(pf); | |
460 | if (err) | |
461 | return err; | |
462 | ||
463 | err = nfp_app_start(pf->app, pf->ctrl_vnic); | |
464 | if (err) | |
465 | goto err_ctrl_stop; | |
466 | ||
e3f28473 JK |
467 | if (pf->num_vfs) { |
468 | err = nfp_app_sriov_enable(pf->app, pf->num_vfs); | |
469 | if (err) | |
470 | goto err_app_stop; | |
471 | } | |
472 | ||
02082701 JK |
473 | return 0; |
474 | ||
e3f28473 JK |
475 | err_app_stop: |
476 | nfp_app_stop(pf->app); | |
02082701 JK |
477 | err_ctrl_stop: |
478 | nfp_net_pf_app_stop_ctrl(pf); | |
479 | return err; | |
480 | } | |
481 | ||
482 | static void nfp_net_pf_app_stop(struct nfp_pf *pf) | |
483 | { | |
e3f28473 JK |
484 | if (pf->num_vfs) |
485 | nfp_app_sriov_disable(pf->app); | |
02082701 JK |
486 | nfp_app_stop(pf->app); |
487 | nfp_net_pf_app_stop_ctrl(pf); | |
488 | } | |
489 | ||
a5950182 SH |
490 | static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) |
491 | { | |
492 | if (pf->vf_cfg_bar) | |
493 | nfp_cpp_area_release_free(pf->vf_cfg_bar); | |
494 | if (pf->mac_stats_bar) | |
495 | nfp_cpp_area_release_free(pf->mac_stats_bar); | |
496 | nfp_cpp_area_release_free(pf->qc_area); | |
497 | nfp_cpp_area_release_free(pf->data_vnic_bar); | |
498 | } | |
499 | ||
500 | static int nfp_net_pci_map_mem(struct nfp_pf *pf) | |
501 | { | |
a5950182 | 502 | u8 __iomem *mem; |
f8473024 | 503 | u32 min_size; |
a5950182 SH |
504 | int err; |
505 | ||
f8473024 | 506 | min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; |
a5950182 | 507 | mem = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0", |
f8473024 | 508 | min_size, &pf->data_vnic_bar); |
a5950182 SH |
509 | if (IS_ERR(mem)) { |
510 | nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); | |
d557ee6b | 511 | return PTR_ERR(mem); |
a5950182 SH |
512 | } |
513 | ||
f8473024 JK |
514 | min_size = NFP_MAC_STATS_SIZE * (pf->eth_tbl->max_index + 1); |
515 | pf->mac_stats_mem = nfp_rtsym_map(pf->rtbl, "_mac_stats", | |
516 | "net.macstats", min_size, | |
517 | &pf->mac_stats_bar); | |
a5950182 SH |
518 | if (IS_ERR(pf->mac_stats_mem)) { |
519 | if (PTR_ERR(pf->mac_stats_mem) != -ENOENT) { | |
520 | err = PTR_ERR(pf->mac_stats_mem); | |
521 | goto err_unmap_ctrl; | |
522 | } | |
523 | pf->mac_stats_mem = NULL; | |
524 | } | |
525 | ||
526 | pf->vf_cfg_mem = nfp_net_pf_map_rtsym(pf, "net.vfcfg", | |
527 | "_pf%d_net_vf_bar", | |
528 | NFP_NET_CFG_BAR_SZ * | |
529 | pf->limit_vfs, &pf->vf_cfg_bar); | |
530 | if (IS_ERR(pf->vf_cfg_mem)) { | |
531 | if (PTR_ERR(pf->vf_cfg_mem) != -ENOENT) { | |
532 | err = PTR_ERR(pf->vf_cfg_mem); | |
533 | goto err_unmap_mac_stats; | |
534 | } | |
535 | pf->vf_cfg_mem = NULL; | |
536 | } | |
537 | ||
064dc319 | 538 | mem = nfp_cpp_map_area(pf->cpp, "net.qc", 0, 0, |
a5950182 SH |
539 | NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ, |
540 | &pf->qc_area); | |
541 | if (IS_ERR(mem)) { | |
542 | nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); | |
543 | err = PTR_ERR(mem); | |
544 | goto err_unmap_vf_cfg; | |
545 | } | |
546 | ||
547 | return 0; | |
548 | ||
549 | err_unmap_vf_cfg: | |
550 | if (pf->vf_cfg_bar) | |
551 | nfp_cpp_area_release_free(pf->vf_cfg_bar); | |
552 | err_unmap_mac_stats: | |
553 | if (pf->mac_stats_bar) | |
554 | nfp_cpp_area_release_free(pf->mac_stats_bar); | |
555 | err_unmap_ctrl: | |
556 | nfp_cpp_area_release_free(pf->data_vnic_bar); | |
557 | return err; | |
558 | } | |
559 | ||
172f638c JK |
560 | static void nfp_net_pci_remove_finish(struct nfp_pf *pf) |
561 | { | |
02082701 JK |
562 | nfp_net_pf_app_stop(pf); |
563 | /* stop app first, to avoid double free of ctrl vNIC's ddir */ | |
172f638c JK |
564 | nfp_net_debugfs_dir_clean(&pf->ddir); |
565 | ||
6d4b0d8e | 566 | nfp_net_pf_free_irqs(pf); |
7ac9ebd5 | 567 | nfp_net_pf_app_clean(pf); |
a5950182 | 568 | nfp_net_pci_unmap_mem(pf); |
172f638c JK |
569 | } |
570 | ||
3d4ed1e7 JK |
571 | static int |
572 | nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, | |
573 | struct nfp_eth_table *eth_table) | |
574 | { | |
575 | struct nfp_eth_table_port *eth_port; | |
576 | ||
577 | ASSERT_RTNL(); | |
578 | ||
579 | eth_port = nfp_net_find_port(eth_table, port->eth_id); | |
580 | if (!eth_port) { | |
46b25031 | 581 | set_bit(NFP_PORT_CHANGED, &port->flags); |
3d4ed1e7 JK |
582 | nfp_warn(cpp, "Warning: port #%d not present after reconfig\n", |
583 | port->eth_id); | |
584 | return -EIO; | |
585 | } | |
586 | if (eth_port->override_changed) { | |
587 | nfp_warn(cpp, "Port #%d config changed, unregistering. Reboot required before port will be operational again.\n", port->eth_id); | |
588 | port->type = NFP_PORT_INVALID; | |
589 | } | |
590 | ||
591 | memcpy(port->eth_port, eth_port, sizeof(*eth_port)); | |
592 | ||
593 | return 0; | |
594 | } | |
595 | ||
ec8b1fbe | 596 | int nfp_net_refresh_port_table_sync(struct nfp_pf *pf) |
172f638c | 597 | { |
90fdc561 | 598 | struct nfp_eth_table *eth_table; |
172f638c | 599 | struct nfp_net *nn, *next; |
3eb3b74a | 600 | struct nfp_port *port; |
172f638c | 601 | |
ec8b1fbe | 602 | lockdep_assert_held(&pf->lock); |
172f638c JK |
603 | |
604 | /* Check for nfp_net_pci_remove() racing against us */ | |
d4e7f092 | 605 | if (list_empty(&pf->vnics)) |
ec8b1fbe | 606 | return 0; |
172f638c | 607 | |
6d4f8cba JK |
608 | /* Update state of all ports */ |
609 | rtnl_lock(); | |
3eb3b74a JK |
610 | list_for_each_entry(port, &pf->ports, port_list) |
611 | clear_bit(NFP_PORT_CHANGED, &port->flags); | |
90fdc561 JK |
612 | |
613 | eth_table = nfp_eth_read_ports(pf->cpp); | |
614 | if (!eth_table) { | |
46b25031 JK |
615 | list_for_each_entry(port, &pf->ports, port_list) |
616 | if (__nfp_port_get_eth_port(port)) | |
617 | set_bit(NFP_PORT_CHANGED, &port->flags); | |
6d4f8cba | 618 | rtnl_unlock(); |
90fdc561 | 619 | nfp_err(pf->cpp, "Error refreshing port config!\n"); |
ec8b1fbe | 620 | return -EIO; |
90fdc561 JK |
621 | } |
622 | ||
3eb3b74a JK |
623 | list_for_each_entry(port, &pf->ports, port_list) |
624 | if (__nfp_port_get_eth_port(port)) | |
625 | nfp_net_eth_port_update(pf->cpp, port, eth_table); | |
90fdc561 JK |
626 | rtnl_unlock(); |
627 | ||
3d4ed1e7 | 628 | kfree(eth_table); |
90fdc561 | 629 | |
6d4f8cba | 630 | /* Shoot off the ports which became invalid */ |
d4e7f092 | 631 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { |
eb488c26 | 632 | if (!nn->port || nn->port->type != NFP_PORT_INVALID) |
172f638c JK |
633 | continue; |
634 | ||
71f8a116 | 635 | nfp_net_pf_clean_vnic(pf, nn); |
9140b30d | 636 | nfp_net_pf_free_vnic(pf, nn); |
172f638c JK |
637 | } |
638 | ||
d4e7f092 | 639 | if (list_empty(&pf->vnics)) |
172f638c | 640 | nfp_net_pci_remove_finish(pf); |
ec8b1fbe JK |
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 | ||
650 | mutex_lock(&pf->lock); | |
651 | nfp_net_refresh_port_table_sync(pf); | |
d4e7f092 | 652 | mutex_unlock(&pf->lock); |
172f638c JK |
653 | } |
654 | ||
eb488c26 | 655 | void nfp_net_refresh_port_table(struct nfp_port *port) |
172f638c | 656 | { |
eb488c26 | 657 | struct nfp_pf *pf = port->app->pf; |
172f638c | 658 | |
1f60a581 JK |
659 | set_bit(NFP_PORT_CHANGED, &port->flags); |
660 | ||
6d48ceb2 | 661 | queue_work(pf->wq, &pf->port_refresh_work); |
90fdc561 | 662 | } |
172f638c | 663 | |
eb488c26 | 664 | int nfp_net_refresh_eth_port(struct nfp_port *port) |
90fdc561 | 665 | { |
eb488c26 | 666 | struct nfp_cpp *cpp = port->app->cpp; |
90fdc561 | 667 | struct nfp_eth_table *eth_table; |
3d4ed1e7 | 668 | int ret; |
172f638c | 669 | |
6d4f8cba JK |
670 | clear_bit(NFP_PORT_CHANGED, &port->flags); |
671 | ||
eb488c26 | 672 | eth_table = nfp_eth_read_ports(cpp); |
90fdc561 | 673 | if (!eth_table) { |
46b25031 | 674 | set_bit(NFP_PORT_CHANGED, &port->flags); |
eb488c26 | 675 | nfp_err(cpp, "Error refreshing port state table!\n"); |
90fdc561 JK |
676 | return -EIO; |
677 | } | |
172f638c | 678 | |
3d4ed1e7 | 679 | ret = nfp_net_eth_port_update(cpp, port, eth_table); |
172f638c | 680 | |
90fdc561 | 681 | kfree(eth_table); |
172f638c | 682 | |
3d4ed1e7 | 683 | return ret; |
172f638c JK |
684 | } |
685 | ||
63461a02 JK |
686 | /* |
687 | * PCI device functions | |
688 | */ | |
689 | int nfp_net_pci_probe(struct nfp_pf *pf) | |
690 | { | |
63461a02 | 691 | struct nfp_net_fw_version fw_ver; |
73e253f0 | 692 | u8 __iomem *ctrl_bar, *qc_bar; |
63461a02 JK |
693 | int stride; |
694 | int err; | |
695 | ||
d4e7f092 | 696 | INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_vnics); |
d12537df | 697 | |
63461a02 | 698 | /* Verify that the board has completed initialization */ |
9baa4885 | 699 | if (!nfp_is_ready(pf)) { |
63461a02 JK |
700 | nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); |
701 | return -EINVAL; | |
702 | } | |
703 | ||
d557ee6b JK |
704 | if (!pf->rtbl) { |
705 | nfp_err(pf->cpp, "No %s, giving up.\n", | |
706 | pf->fw_loaded ? "symbol table" : "firmware found"); | |
26a8985f | 707 | return -EINVAL; |
d557ee6b JK |
708 | } |
709 | ||
d4e7f092 JK |
710 | mutex_lock(&pf->lock); |
711 | pf->max_data_vnics = nfp_net_pf_get_num_ports(pf); | |
69394af5 JK |
712 | if ((int)pf->max_data_vnics < 0) { |
713 | err = pf->max_data_vnics; | |
714 | goto err_unlock; | |
715 | } | |
63461a02 | 716 | |
a5950182 SH |
717 | err = nfp_net_pci_map_mem(pf); |
718 | if (err) | |
d12537df | 719 | goto err_unlock; |
a5950182 SH |
720 | |
721 | ctrl_bar = nfp_cpp_area_iomem(pf->data_vnic_bar); | |
722 | qc_bar = nfp_cpp_area_iomem(pf->qc_area); | |
723 | if (!ctrl_bar || !qc_bar) { | |
724 | err = -EIO; | |
725 | goto err_unmap; | |
d12537df | 726 | } |
63461a02 JK |
727 | |
728 | nfp_net_get_fw_version(&fw_ver, ctrl_bar); | |
729 | if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { | |
730 | nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n", | |
731 | fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); | |
732 | err = -EINVAL; | |
a5950182 | 733 | goto err_unmap; |
63461a02 JK |
734 | } |
735 | ||
736 | /* Determine stride */ | |
737 | if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1)) { | |
738 | stride = 2; | |
739 | nfp_warn(pf->cpp, "OBSOLETE Firmware detected - VF isolation not available\n"); | |
740 | } else { | |
741 | switch (fw_ver.major) { | |
f9380629 | 742 | case 1 ... 5: |
63461a02 JK |
743 | stride = 4; |
744 | break; | |
745 | default: | |
746 | nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n", | |
747 | fw_ver.resv, fw_ver.class, | |
748 | fw_ver.major, fw_ver.minor); | |
749 | err = -EINVAL; | |
a5950182 | 750 | goto err_unmap; |
63461a02 JK |
751 | } |
752 | } | |
753 | ||
02082701 | 754 | err = nfp_net_pf_app_init(pf, qc_bar, stride); |
7ac9ebd5 | 755 | if (err) |
a5950182 | 756 | goto err_unmap; |
7ac9ebd5 | 757 | |
63461a02 JK |
758 | pf->ddir = nfp_net_debugfs_device_add(pf->pdev); |
759 | ||
6d4b0d8e JK |
760 | /* Allocate the vnics and do basic init */ |
761 | err = nfp_net_pf_alloc_vnics(pf, ctrl_bar, qc_bar, stride); | |
63461a02 JK |
762 | if (err) |
763 | goto err_clean_ddir; | |
764 | ||
6d4b0d8e JK |
765 | err = nfp_net_pf_alloc_irqs(pf); |
766 | if (err) | |
767 | goto err_free_vnics; | |
768 | ||
02082701 | 769 | err = nfp_net_pf_app_start(pf); |
6d4b0d8e JK |
770 | if (err) |
771 | goto err_free_irqs; | |
772 | ||
02082701 JK |
773 | err = nfp_net_pf_init_vnics(pf); |
774 | if (err) | |
775 | goto err_stop_app; | |
776 | ||
d4e7f092 | 777 | mutex_unlock(&pf->lock); |
d12537df | 778 | |
63461a02 JK |
779 | return 0; |
780 | ||
02082701 JK |
781 | err_stop_app: |
782 | nfp_net_pf_app_stop(pf); | |
6d4b0d8e JK |
783 | err_free_irqs: |
784 | nfp_net_pf_free_irqs(pf); | |
785 | err_free_vnics: | |
786 | nfp_net_pf_free_vnics(pf); | |
63461a02 JK |
787 | err_clean_ddir: |
788 | nfp_net_debugfs_dir_clean(&pf->ddir); | |
7ac9ebd5 | 789 | nfp_net_pf_app_clean(pf); |
a5950182 SH |
790 | err_unmap: |
791 | nfp_net_pci_unmap_mem(pf); | |
d12537df | 792 | err_unlock: |
d4e7f092 | 793 | mutex_unlock(&pf->lock); |
ab832b8d | 794 | cancel_work_sync(&pf->port_refresh_work); |
63461a02 JK |
795 | return err; |
796 | } | |
797 | ||
798 | void nfp_net_pci_remove(struct nfp_pf *pf) | |
799 | { | |
800 | struct nfp_net *nn; | |
801 | ||
d4e7f092 JK |
802 | mutex_lock(&pf->lock); |
803 | if (list_empty(&pf->vnics)) | |
d12537df JK |
804 | goto out; |
805 | ||
71f8a116 | 806 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
02082701 JK |
807 | if (nfp_net_is_data_vnic(nn)) |
808 | nfp_net_pf_clean_vnic(pf, nn); | |
63461a02 | 809 | |
d4e7f092 | 810 | nfp_net_pf_free_vnics(pf); |
63461a02 | 811 | |
172f638c | 812 | nfp_net_pci_remove_finish(pf); |
d12537df | 813 | out: |
d4e7f092 | 814 | mutex_unlock(&pf->lock); |
172f638c JK |
815 | |
816 | cancel_work_sync(&pf->port_refresh_work); | |
63461a02 | 817 | } |