]>
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 | ||
02082701 JK |
459 | err = nfp_app_start(pf->app, pf->ctrl_vnic); |
460 | if (err) | |
461 | goto err_ctrl_stop; | |
462 | ||
e3f28473 JK |
463 | if (pf->num_vfs) { |
464 | err = nfp_app_sriov_enable(pf->app, pf->num_vfs); | |
465 | if (err) | |
466 | goto err_app_stop; | |
467 | } | |
468 | ||
02082701 JK |
469 | return 0; |
470 | ||
e3f28473 JK |
471 | err_app_stop: |
472 | nfp_app_stop(pf->app); | |
02082701 JK |
473 | err_ctrl_stop: |
474 | nfp_net_pf_app_stop_ctrl(pf); | |
475 | return err; | |
476 | } | |
477 | ||
478 | static void nfp_net_pf_app_stop(struct nfp_pf *pf) | |
479 | { | |
e3f28473 JK |
480 | if (pf->num_vfs) |
481 | nfp_app_sriov_disable(pf->app); | |
02082701 | 482 | nfp_app_stop(pf->app); |
02082701 JK |
483 | } |
484 | ||
a5950182 SH |
485 | static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) |
486 | { | |
487 | if (pf->vf_cfg_bar) | |
488 | nfp_cpp_area_release_free(pf->vf_cfg_bar); | |
489 | if (pf->mac_stats_bar) | |
490 | nfp_cpp_area_release_free(pf->mac_stats_bar); | |
491 | nfp_cpp_area_release_free(pf->qc_area); | |
492 | nfp_cpp_area_release_free(pf->data_vnic_bar); | |
493 | } | |
494 | ||
495 | static int nfp_net_pci_map_mem(struct nfp_pf *pf) | |
496 | { | |
a5950182 | 497 | u8 __iomem *mem; |
f8473024 | 498 | u32 min_size; |
a5950182 SH |
499 | int err; |
500 | ||
f8473024 | 501 | min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; |
a5950182 | 502 | mem = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0", |
f8473024 | 503 | min_size, &pf->data_vnic_bar); |
a5950182 SH |
504 | if (IS_ERR(mem)) { |
505 | nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); | |
d557ee6b | 506 | return PTR_ERR(mem); |
a5950182 SH |
507 | } |
508 | ||
f8473024 JK |
509 | min_size = NFP_MAC_STATS_SIZE * (pf->eth_tbl->max_index + 1); |
510 | pf->mac_stats_mem = nfp_rtsym_map(pf->rtbl, "_mac_stats", | |
511 | "net.macstats", min_size, | |
512 | &pf->mac_stats_bar); | |
a5950182 SH |
513 | if (IS_ERR(pf->mac_stats_mem)) { |
514 | if (PTR_ERR(pf->mac_stats_mem) != -ENOENT) { | |
515 | err = PTR_ERR(pf->mac_stats_mem); | |
516 | goto err_unmap_ctrl; | |
517 | } | |
518 | pf->mac_stats_mem = NULL; | |
519 | } | |
520 | ||
521 | pf->vf_cfg_mem = nfp_net_pf_map_rtsym(pf, "net.vfcfg", | |
522 | "_pf%d_net_vf_bar", | |
523 | NFP_NET_CFG_BAR_SZ * | |
524 | pf->limit_vfs, &pf->vf_cfg_bar); | |
525 | if (IS_ERR(pf->vf_cfg_mem)) { | |
526 | if (PTR_ERR(pf->vf_cfg_mem) != -ENOENT) { | |
527 | err = PTR_ERR(pf->vf_cfg_mem); | |
528 | goto err_unmap_mac_stats; | |
529 | } | |
530 | pf->vf_cfg_mem = NULL; | |
531 | } | |
532 | ||
064dc319 | 533 | mem = nfp_cpp_map_area(pf->cpp, "net.qc", 0, 0, |
a5950182 SH |
534 | NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ, |
535 | &pf->qc_area); | |
536 | if (IS_ERR(mem)) { | |
537 | nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); | |
538 | err = PTR_ERR(mem); | |
539 | goto err_unmap_vf_cfg; | |
540 | } | |
541 | ||
542 | return 0; | |
543 | ||
544 | err_unmap_vf_cfg: | |
545 | if (pf->vf_cfg_bar) | |
546 | nfp_cpp_area_release_free(pf->vf_cfg_bar); | |
547 | err_unmap_mac_stats: | |
548 | if (pf->mac_stats_bar) | |
549 | nfp_cpp_area_release_free(pf->mac_stats_bar); | |
550 | err_unmap_ctrl: | |
551 | nfp_cpp_area_release_free(pf->data_vnic_bar); | |
552 | return err; | |
553 | } | |
554 | ||
172f638c JK |
555 | static void nfp_net_pci_remove_finish(struct nfp_pf *pf) |
556 | { | |
326ce603 | 557 | nfp_net_pf_app_stop_ctrl(pf); |
02082701 | 558 | /* stop app first, to avoid double free of ctrl vNIC's ddir */ |
172f638c JK |
559 | nfp_net_debugfs_dir_clean(&pf->ddir); |
560 | ||
6d4b0d8e | 561 | nfp_net_pf_free_irqs(pf); |
7ac9ebd5 | 562 | nfp_net_pf_app_clean(pf); |
a5950182 | 563 | nfp_net_pci_unmap_mem(pf); |
172f638c JK |
564 | } |
565 | ||
3d4ed1e7 JK |
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_table, port->eth_id); | |
575 | if (!eth_port) { | |
46b25031 | 576 | set_bit(NFP_PORT_CHANGED, &port->flags); |
3d4ed1e7 JK |
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. Reboot 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 | ||
588 | return 0; | |
589 | } | |
590 | ||
ec8b1fbe | 591 | int nfp_net_refresh_port_table_sync(struct nfp_pf *pf) |
172f638c | 592 | { |
90fdc561 | 593 | struct nfp_eth_table *eth_table; |
172f638c | 594 | struct nfp_net *nn, *next; |
3eb3b74a | 595 | struct nfp_port *port; |
172f638c | 596 | |
ec8b1fbe | 597 | lockdep_assert_held(&pf->lock); |
172f638c JK |
598 | |
599 | /* Check for nfp_net_pci_remove() racing against us */ | |
d4e7f092 | 600 | if (list_empty(&pf->vnics)) |
ec8b1fbe | 601 | return 0; |
172f638c | 602 | |
6d4f8cba JK |
603 | /* Update state of all ports */ |
604 | rtnl_lock(); | |
3eb3b74a JK |
605 | list_for_each_entry(port, &pf->ports, port_list) |
606 | clear_bit(NFP_PORT_CHANGED, &port->flags); | |
90fdc561 JK |
607 | |
608 | eth_table = nfp_eth_read_ports(pf->cpp); | |
609 | if (!eth_table) { | |
46b25031 JK |
610 | list_for_each_entry(port, &pf->ports, port_list) |
611 | if (__nfp_port_get_eth_port(port)) | |
612 | set_bit(NFP_PORT_CHANGED, &port->flags); | |
6d4f8cba | 613 | rtnl_unlock(); |
90fdc561 | 614 | nfp_err(pf->cpp, "Error refreshing port config!\n"); |
ec8b1fbe | 615 | return -EIO; |
90fdc561 JK |
616 | } |
617 | ||
3eb3b74a JK |
618 | list_for_each_entry(port, &pf->ports, port_list) |
619 | if (__nfp_port_get_eth_port(port)) | |
620 | nfp_net_eth_port_update(pf->cpp, port, eth_table); | |
90fdc561 JK |
621 | rtnl_unlock(); |
622 | ||
3d4ed1e7 | 623 | kfree(eth_table); |
90fdc561 | 624 | |
6d4f8cba | 625 | /* Shoot off the ports which became invalid */ |
d4e7f092 | 626 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { |
eb488c26 | 627 | if (!nn->port || nn->port->type != NFP_PORT_INVALID) |
172f638c JK |
628 | continue; |
629 | ||
71f8a116 | 630 | nfp_net_pf_clean_vnic(pf, nn); |
9140b30d | 631 | nfp_net_pf_free_vnic(pf, nn); |
172f638c JK |
632 | } |
633 | ||
d4e7f092 | 634 | if (list_empty(&pf->vnics)) |
172f638c | 635 | nfp_net_pci_remove_finish(pf); |
ec8b1fbe JK |
636 | |
637 | return 0; | |
638 | } | |
639 | ||
640 | static void nfp_net_refresh_vnics(struct work_struct *work) | |
641 | { | |
642 | struct nfp_pf *pf = container_of(work, struct nfp_pf, | |
643 | port_refresh_work); | |
644 | ||
645 | mutex_lock(&pf->lock); | |
646 | nfp_net_refresh_port_table_sync(pf); | |
d4e7f092 | 647 | mutex_unlock(&pf->lock); |
172f638c JK |
648 | } |
649 | ||
eb488c26 | 650 | void nfp_net_refresh_port_table(struct nfp_port *port) |
172f638c | 651 | { |
eb488c26 | 652 | struct nfp_pf *pf = port->app->pf; |
172f638c | 653 | |
1f60a581 JK |
654 | set_bit(NFP_PORT_CHANGED, &port->flags); |
655 | ||
6d48ceb2 | 656 | queue_work(pf->wq, &pf->port_refresh_work); |
90fdc561 | 657 | } |
172f638c | 658 | |
eb488c26 | 659 | int nfp_net_refresh_eth_port(struct nfp_port *port) |
90fdc561 | 660 | { |
eb488c26 | 661 | struct nfp_cpp *cpp = port->app->cpp; |
90fdc561 | 662 | struct nfp_eth_table *eth_table; |
3d4ed1e7 | 663 | int ret; |
172f638c | 664 | |
6d4f8cba JK |
665 | clear_bit(NFP_PORT_CHANGED, &port->flags); |
666 | ||
eb488c26 | 667 | eth_table = nfp_eth_read_ports(cpp); |
90fdc561 | 668 | if (!eth_table) { |
46b25031 | 669 | set_bit(NFP_PORT_CHANGED, &port->flags); |
eb488c26 | 670 | nfp_err(cpp, "Error refreshing port state table!\n"); |
90fdc561 JK |
671 | return -EIO; |
672 | } | |
172f638c | 673 | |
3d4ed1e7 | 674 | ret = nfp_net_eth_port_update(cpp, port, eth_table); |
172f638c | 675 | |
90fdc561 | 676 | kfree(eth_table); |
172f638c | 677 | |
3d4ed1e7 | 678 | return ret; |
172f638c JK |
679 | } |
680 | ||
63461a02 JK |
681 | /* |
682 | * PCI device functions | |
683 | */ | |
684 | int nfp_net_pci_probe(struct nfp_pf *pf) | |
685 | { | |
63461a02 | 686 | struct nfp_net_fw_version fw_ver; |
73e253f0 | 687 | u8 __iomem *ctrl_bar, *qc_bar; |
326ce603 | 688 | struct nfp_net *nn; |
63461a02 JK |
689 | int stride; |
690 | int err; | |
691 | ||
d4e7f092 | 692 | INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_vnics); |
d12537df | 693 | |
63461a02 | 694 | /* Verify that the board has completed initialization */ |
9baa4885 | 695 | if (!nfp_is_ready(pf)) { |
63461a02 JK |
696 | nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); |
697 | return -EINVAL; | |
698 | } | |
699 | ||
d557ee6b JK |
700 | if (!pf->rtbl) { |
701 | nfp_err(pf->cpp, "No %s, giving up.\n", | |
702 | pf->fw_loaded ? "symbol table" : "firmware found"); | |
703 | return -EPROBE_DEFER; | |
704 | } | |
705 | ||
d4e7f092 JK |
706 | mutex_lock(&pf->lock); |
707 | pf->max_data_vnics = nfp_net_pf_get_num_ports(pf); | |
69394af5 JK |
708 | if ((int)pf->max_data_vnics < 0) { |
709 | err = pf->max_data_vnics; | |
710 | goto err_unlock; | |
711 | } | |
63461a02 | 712 | |
a5950182 SH |
713 | err = nfp_net_pci_map_mem(pf); |
714 | if (err) | |
d12537df | 715 | goto err_unlock; |
a5950182 SH |
716 | |
717 | ctrl_bar = nfp_cpp_area_iomem(pf->data_vnic_bar); | |
718 | qc_bar = nfp_cpp_area_iomem(pf->qc_area); | |
719 | if (!ctrl_bar || !qc_bar) { | |
720 | err = -EIO; | |
721 | goto err_unmap; | |
d12537df | 722 | } |
63461a02 JK |
723 | |
724 | nfp_net_get_fw_version(&fw_ver, ctrl_bar); | |
725 | if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { | |
726 | nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n", | |
727 | fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); | |
728 | err = -EINVAL; | |
a5950182 | 729 | goto err_unmap; |
63461a02 JK |
730 | } |
731 | ||
732 | /* Determine stride */ | |
733 | if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1)) { | |
734 | stride = 2; | |
735 | nfp_warn(pf->cpp, "OBSOLETE Firmware detected - VF isolation not available\n"); | |
736 | } else { | |
737 | switch (fw_ver.major) { | |
f9380629 | 738 | case 1 ... 5: |
63461a02 JK |
739 | stride = 4; |
740 | break; | |
741 | default: | |
742 | nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n", | |
743 | fw_ver.resv, fw_ver.class, | |
744 | fw_ver.major, fw_ver.minor); | |
745 | err = -EINVAL; | |
a5950182 | 746 | goto err_unmap; |
63461a02 JK |
747 | } |
748 | } | |
749 | ||
02082701 | 750 | err = nfp_net_pf_app_init(pf, qc_bar, stride); |
7ac9ebd5 | 751 | if (err) |
a5950182 | 752 | goto err_unmap; |
7ac9ebd5 | 753 | |
63461a02 JK |
754 | pf->ddir = nfp_net_debugfs_device_add(pf->pdev); |
755 | ||
6d4b0d8e JK |
756 | /* Allocate the vnics and do basic init */ |
757 | err = nfp_net_pf_alloc_vnics(pf, ctrl_bar, qc_bar, stride); | |
63461a02 JK |
758 | if (err) |
759 | goto err_clean_ddir; | |
760 | ||
6d4b0d8e JK |
761 | err = nfp_net_pf_alloc_irqs(pf); |
762 | if (err) | |
763 | goto err_free_vnics; | |
764 | ||
326ce603 | 765 | err = nfp_net_pf_app_start_ctrl(pf); |
6d4b0d8e JK |
766 | if (err) |
767 | goto err_free_irqs; | |
768 | ||
02082701 JK |
769 | err = nfp_net_pf_init_vnics(pf); |
770 | if (err) | |
771 | goto err_stop_app; | |
772 | ||
326ce603 JK |
773 | err = nfp_net_pf_app_start(pf); |
774 | if (err) | |
775 | goto err_clean_vnics; | |
776 | ||
d4e7f092 | 777 | mutex_unlock(&pf->lock); |
d12537df | 778 | |
63461a02 JK |
779 | return 0; |
780 | ||
326ce603 JK |
781 | err_clean_vnics: |
782 | list_for_each_entry(nn, &pf->vnics, vnic_list) | |
783 | if (nfp_net_is_data_vnic(nn)) | |
784 | nfp_net_pf_clean_vnic(pf, nn); | |
02082701 | 785 | err_stop_app: |
326ce603 | 786 | nfp_net_pf_app_stop_ctrl(pf); |
6d4b0d8e JK |
787 | err_free_irqs: |
788 | nfp_net_pf_free_irqs(pf); | |
789 | err_free_vnics: | |
790 | nfp_net_pf_free_vnics(pf); | |
63461a02 JK |
791 | err_clean_ddir: |
792 | nfp_net_debugfs_dir_clean(&pf->ddir); | |
7ac9ebd5 | 793 | nfp_net_pf_app_clean(pf); |
a5950182 SH |
794 | err_unmap: |
795 | nfp_net_pci_unmap_mem(pf); | |
d12537df | 796 | err_unlock: |
d4e7f092 | 797 | mutex_unlock(&pf->lock); |
ab832b8d | 798 | cancel_work_sync(&pf->port_refresh_work); |
63461a02 JK |
799 | return err; |
800 | } | |
801 | ||
802 | void nfp_net_pci_remove(struct nfp_pf *pf) | |
803 | { | |
804 | struct nfp_net *nn; | |
805 | ||
d4e7f092 JK |
806 | mutex_lock(&pf->lock); |
807 | if (list_empty(&pf->vnics)) | |
d12537df JK |
808 | goto out; |
809 | ||
326ce603 JK |
810 | nfp_net_pf_app_stop(pf); |
811 | ||
71f8a116 | 812 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
02082701 JK |
813 | if (nfp_net_is_data_vnic(nn)) |
814 | nfp_net_pf_clean_vnic(pf, nn); | |
63461a02 | 815 | |
d4e7f092 | 816 | nfp_net_pf_free_vnics(pf); |
63461a02 | 817 | |
172f638c | 818 | nfp_net_pci_remove_finish(pf); |
d12537df | 819 | out: |
d4e7f092 | 820 | mutex_unlock(&pf->lock); |
172f638c JK |
821 | |
822 | cancel_work_sync(&pf->port_refresh_work); | |
63461a02 | 823 | } |