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 | ||
66 | static int nfp_is_ready(struct nfp_cpp *cpp) | |
67 | { | |
68 | const char *cp; | |
69 | long state; | |
70 | int err; | |
71 | ||
72 | cp = nfp_hwinfo_lookup(cpp, "board.state"); | |
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 | ||
83 | /** | |
84 | * nfp_net_map_area() - Help function to map an area | |
85 | * @cpp: NFP CPP handler | |
86 | * @name: Name for the area | |
87 | * @target: CPP target | |
88 | * @addr: CPP address | |
89 | * @size: Size of the area | |
90 | * @area: Area handle (returned). | |
91 | * | |
92 | * This function is primarily to simplify the code in the main probe | |
93 | * function. To undo the effect of this functions call | |
94 | * @nfp_cpp_area_release_free(*area); | |
95 | * | |
96 | * Return: Pointer to memory mapped area or ERR_PTR | |
97 | */ | |
98 | static u8 __iomem *nfp_net_map_area(struct nfp_cpp *cpp, | |
99 | const char *name, int isl, int target, | |
100 | unsigned long long addr, unsigned long size, | |
101 | struct nfp_cpp_area **area) | |
102 | { | |
103 | u8 __iomem *res; | |
104 | u32 dest; | |
105 | int err; | |
106 | ||
107 | dest = NFP_CPP_ISLAND_ID(target, NFP_CPP_ACTION_RW, 0, isl); | |
108 | ||
109 | *area = nfp_cpp_area_alloc_with_name(cpp, dest, name, addr, size); | |
110 | if (!*area) { | |
111 | err = -EIO; | |
112 | goto err_area; | |
113 | } | |
114 | ||
115 | err = nfp_cpp_area_acquire(*area); | |
116 | if (err < 0) | |
117 | goto err_acquire; | |
118 | ||
119 | res = nfp_cpp_area_iomem(*area); | |
120 | if (!res) { | |
121 | err = -EIO; | |
122 | goto err_map; | |
123 | } | |
124 | ||
125 | return res; | |
126 | ||
127 | err_map: | |
128 | nfp_cpp_area_release(*area); | |
129 | err_acquire: | |
130 | nfp_cpp_area_free(*area); | |
131 | err_area: | |
132 | return (u8 __iomem *)ERR_PTR(err); | |
133 | } | |
134 | ||
b9de0077 JK |
135 | /** |
136 | * nfp_net_get_mac_addr() - Get the MAC address. | |
137 | * @nn: NFP Network structure | |
138 | * @cpp: NFP CPP handle | |
139 | * @id: NFP port id | |
140 | * | |
141 | * First try to get the MAC address from NSP ETH table. If that | |
142 | * fails try HWInfo. As a last resort generate a random address. | |
143 | */ | |
8aa0cb00 | 144 | void |
b9de0077 | 145 | nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id) |
63461a02 | 146 | { |
eb488c26 | 147 | struct nfp_eth_table_port *eth_port; |
b9de0077 | 148 | struct nfp_net_dp *dp = &nn->dp; |
63461a02 JK |
149 | u8 mac_addr[ETH_ALEN]; |
150 | const char *mac_str; | |
151 | char name[32]; | |
152 | ||
eb488c26 JK |
153 | eth_port = __nfp_port_get_eth_port(nn->port); |
154 | if (eth_port) { | |
155 | ether_addr_copy(dp->netdev->dev_addr, eth_port->mac_addr); | |
156 | ether_addr_copy(dp->netdev->perm_addr, eth_port->mac_addr); | |
b9de0077 JK |
157 | return; |
158 | } | |
159 | ||
63461a02 JK |
160 | snprintf(name, sizeof(name), "eth%d.mac", id); |
161 | ||
162 | mac_str = nfp_hwinfo_lookup(cpp, name); | |
163 | if (!mac_str) { | |
79c12a75 JK |
164 | dev_warn(dp->dev, "Can't lookup MAC address. Generate\n"); |
165 | eth_hw_addr_random(dp->netdev); | |
63461a02 JK |
166 | return; |
167 | } | |
168 | ||
169 | if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | |
170 | &mac_addr[0], &mac_addr[1], &mac_addr[2], | |
171 | &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { | |
79c12a75 | 172 | dev_warn(dp->dev, |
63461a02 | 173 | "Can't parse MAC address (%s). Generate.\n", mac_str); |
79c12a75 | 174 | eth_hw_addr_random(dp->netdev); |
63461a02 JK |
175 | return; |
176 | } | |
177 | ||
79c12a75 JK |
178 | ether_addr_copy(dp->netdev->dev_addr, mac_addr); |
179 | ether_addr_copy(dp->netdev->perm_addr, mac_addr); | |
63461a02 JK |
180 | } |
181 | ||
8aa0cb00 | 182 | struct nfp_eth_table_port * |
90fdc561 | 183 | nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int id) |
63461a02 JK |
184 | { |
185 | int i; | |
186 | ||
90fdc561 JK |
187 | for (i = 0; eth_tbl && i < eth_tbl->count; i++) |
188 | if (eth_tbl->ports[i].eth_index == id) | |
189 | return ð_tbl->ports[i]; | |
47465aed | 190 | |
b9de0077 | 191 | return NULL; |
63461a02 JK |
192 | } |
193 | ||
69394af5 JK |
194 | static int |
195 | nfp_net_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format, | |
196 | unsigned int default_val) | |
63461a02 JK |
197 | { |
198 | char name[256]; | |
63461a02 JK |
199 | int err = 0; |
200 | u64 val; | |
201 | ||
69394af5 | 202 | snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp)); |
63461a02 JK |
203 | |
204 | val = nfp_rtsym_read_le(pf->cpp, name, &err); | |
63461a02 | 205 | if (err) { |
69394af5 JK |
206 | if (err == -ENOENT) |
207 | return default_val; | |
208 | nfp_err(pf->cpp, "Unable to read symbol %s\n", name); | |
209 | return err; | |
63461a02 JK |
210 | } |
211 | ||
212 | return val; | |
213 | } | |
214 | ||
69394af5 JK |
215 | static int nfp_net_pf_get_num_ports(struct nfp_pf *pf) |
216 | { | |
217 | return nfp_net_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1); | |
218 | } | |
219 | ||
8aa0cb00 JK |
220 | static int nfp_net_pf_get_app_id(struct nfp_pf *pf) |
221 | { | |
222 | return nfp_net_pf_rtsym_read_optional(pf, "_pf%u_net_app_id", | |
223 | NFP_APP_CORE_NIC); | |
224 | } | |
225 | ||
c24ca95f JK |
226 | static u8 __iomem * |
227 | nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt, | |
228 | unsigned int min_size, struct nfp_cpp_area **area) | |
63461a02 | 229 | { |
c24ca95f | 230 | const struct nfp_rtsym *sym; |
63461a02 | 231 | char pf_symbol[256]; |
c24ca95f | 232 | u8 __iomem *mem; |
63461a02 | 233 | |
c24ca95f | 234 | snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt, |
cd6f8db9 | 235 | nfp_cppcore_pcie_unit(pf->cpp)); |
63461a02 | 236 | |
c24ca95f JK |
237 | sym = nfp_rtsym_lookup(pf->cpp, pf_symbol); |
238 | if (!sym) { | |
239 | nfp_err(pf->cpp, "Failed to find PF symbol %s\n", pf_symbol); | |
240 | return (u8 __iomem *)ERR_PTR(-ENOENT); | |
63461a02 JK |
241 | } |
242 | ||
c24ca95f JK |
243 | if (sym->size < min_size) { |
244 | nfp_err(pf->cpp, "PF symbol %s too small\n", pf_symbol); | |
245 | return (u8 __iomem *)ERR_PTR(-EINVAL); | |
63461a02 JK |
246 | } |
247 | ||
c24ca95f JK |
248 | mem = nfp_net_map_area(pf->cpp, name, sym->domain, sym->target, |
249 | sym->addr, sym->size, area); | |
250 | if (IS_ERR(mem)) { | |
251 | nfp_err(pf->cpp, "Failed to map PF symbol %s: %ld\n", | |
252 | pf_symbol, PTR_ERR(mem)); | |
253 | return mem; | |
63461a02 JK |
254 | } |
255 | ||
c24ca95f | 256 | return mem; |
63461a02 JK |
257 | } |
258 | ||
9140b30d JK |
259 | static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
260 | { | |
eb488c26 | 261 | nfp_port_free(nn->port); |
9140b30d JK |
262 | list_del(&nn->vnic_list); |
263 | pf->num_vnics--; | |
264 | nfp_net_free(nn); | |
265 | } | |
266 | ||
d4e7f092 | 267 | static void nfp_net_pf_free_vnics(struct nfp_pf *pf) |
63461a02 | 268 | { |
02082701 | 269 | struct nfp_net *nn, *next; |
63461a02 | 270 | |
02082701 JK |
271 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) |
272 | if (nfp_net_is_data_vnic(nn)) | |
273 | nfp_net_pf_free_vnic(pf, nn); | |
63461a02 JK |
274 | } |
275 | ||
276 | static struct nfp_net * | |
a7b1ad08 | 277 | nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, |
73e253f0 | 278 | void __iomem *ctrl_bar, void __iomem *qc_bar, |
21537bc7 | 279 | int stride, unsigned int eth_id) |
63461a02 | 280 | { |
73e253f0 | 281 | u32 tx_base, rx_base, n_tx_rings, n_rx_rings; |
63461a02 | 282 | struct nfp_net *nn; |
8aa0cb00 | 283 | int err; |
63461a02 | 284 | |
73e253f0 JK |
285 | tx_base = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); |
286 | rx_base = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); | |
63461a02 JK |
287 | n_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS); |
288 | n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); | |
289 | ||
d4e7f092 | 290 | /* Allocate and initialise the vNIC */ |
a7b1ad08 | 291 | nn = nfp_net_alloc(pf->pdev, needs_netdev, n_tx_rings, n_rx_rings); |
63461a02 JK |
292 | if (IS_ERR(nn)) |
293 | return nn; | |
294 | ||
7ac9ebd5 | 295 | nn->app = pf->app; |
21537bc7 | 296 | nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar); |
d2b84397 | 297 | nn->dp.ctrl_bar = ctrl_bar; |
73e253f0 JK |
298 | nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ; |
299 | nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ; | |
79c12a75 | 300 | nn->dp.is_vf = 0; |
63461a02 JK |
301 | nn->stride_rx = stride; |
302 | nn->stride_tx = stride; | |
eb488c26 | 303 | |
02082701 JK |
304 | if (needs_netdev) { |
305 | err = nfp_app_vnic_init(pf->app, nn, eth_id); | |
306 | if (err) { | |
307 | nfp_net_free(nn); | |
308 | return ERR_PTR(err); | |
309 | } | |
eb488c26 | 310 | } |
d88b0a23 JK |
311 | |
312 | pf->num_vnics++; | |
313 | list_add_tail(&nn->vnic_list, &pf->vnics); | |
63461a02 JK |
314 | |
315 | return nn; | |
316 | } | |
317 | ||
318 | static int | |
d4e7f092 | 319 | nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) |
63461a02 JK |
320 | { |
321 | int err; | |
322 | ||
63461a02 JK |
323 | /* Get ME clock frequency from ctrl BAR |
324 | * XXX for now frequency is hardcoded until we figure out how | |
325 | * to get the value from nfp-hwinfo into ctrl bar | |
326 | */ | |
327 | nn->me_freq_mhz = 1200; | |
328 | ||
beba69ca | 329 | err = nfp_net_init(nn); |
63461a02 JK |
330 | if (err) |
331 | return err; | |
332 | ||
d4e7f092 | 333 | nfp_net_debugfs_vnic_add(nn, pf->ddir, id); |
63461a02 | 334 | |
53e7a08f JK |
335 | if (nn->port) { |
336 | err = nfp_devlink_port_register(pf->app, nn->port); | |
337 | if (err) | |
338 | goto err_dfs_clean; | |
339 | } | |
340 | ||
63461a02 JK |
341 | nfp_net_info(nn); |
342 | ||
343 | return 0; | |
53e7a08f JK |
344 | |
345 | err_dfs_clean: | |
346 | nfp_net_debugfs_dir_clean(&nn->debugfs_dir); | |
347 | nfp_net_clean(nn); | |
348 | return err; | |
63461a02 JK |
349 | } |
350 | ||
351 | static int | |
d4e7f092 | 352 | nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar, |
21537bc7 | 353 | void __iomem *qc_bar, int stride) |
63461a02 | 354 | { |
63461a02 JK |
355 | struct nfp_net *nn; |
356 | unsigned int i; | |
357 | int err; | |
358 | ||
d4e7f092 | 359 | for (i = 0; i < pf->max_data_vnics; i++) { |
73e253f0 | 360 | nn = nfp_net_pf_alloc_vnic(pf, true, ctrl_bar, qc_bar, |
21537bc7 | 361 | stride, i); |
d88b0a23 JK |
362 | if (IS_ERR(nn)) { |
363 | err = PTR_ERR(nn); | |
364 | goto err_free_prev; | |
63461a02 | 365 | } |
63461a02 JK |
366 | |
367 | ctrl_bar += NFP_PF_CSR_SLICE_SIZE; | |
d88b0a23 | 368 | |
8aa0cb00 JK |
369 | /* Kill the vNIC if app init marked it as invalid */ |
370 | if (nn->port && nn->port->type == NFP_PORT_INVALID) { | |
d88b0a23 JK |
371 | nfp_net_pf_free_vnic(pf, nn); |
372 | continue; | |
373 | } | |
63461a02 JK |
374 | } |
375 | ||
d4e7f092 | 376 | if (list_empty(&pf->vnics)) |
b9de0077 JK |
377 | return -ENODEV; |
378 | ||
63461a02 JK |
379 | return 0; |
380 | ||
381 | err_free_prev: | |
d4e7f092 | 382 | nfp_net_pf_free_vnics(pf); |
63461a02 JK |
383 | return err; |
384 | } | |
385 | ||
71f8a116 JK |
386 | static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) |
387 | { | |
53e7a08f JK |
388 | if (nn->port) |
389 | nfp_devlink_port_unregister(nn->port); | |
71f8a116 JK |
390 | nfp_net_debugfs_dir_clean(&nn->debugfs_dir); |
391 | nfp_net_clean(nn); | |
bb45e51c | 392 | nfp_app_vnic_clean(pf->app, nn); |
71f8a116 JK |
393 | } |
394 | ||
6d4b0d8e | 395 | static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf) |
63461a02 | 396 | { |
6d4b0d8e | 397 | unsigned int wanted_irqs, num_irqs, vnics_left, irqs_left; |
63461a02 | 398 | struct nfp_net *nn; |
63461a02 JK |
399 | |
400 | /* Get MSI-X vectors */ | |
401 | wanted_irqs = 0; | |
d4e7f092 | 402 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
79c12a75 | 403 | wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs; |
63461a02 JK |
404 | pf->irq_entries = kcalloc(wanted_irqs, sizeof(*pf->irq_entries), |
405 | GFP_KERNEL); | |
6d4b0d8e JK |
406 | if (!pf->irq_entries) |
407 | return -ENOMEM; | |
63461a02 JK |
408 | |
409 | num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries, | |
d4e7f092 | 410 | NFP_NET_MIN_VNIC_IRQS * pf->num_vnics, |
63461a02 JK |
411 | wanted_irqs); |
412 | if (!num_irqs) { | |
6d4b0d8e JK |
413 | nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors\n"); |
414 | kfree(pf->irq_entries); | |
415 | return -ENOMEM; | |
63461a02 JK |
416 | } |
417 | ||
d4e7f092 | 418 | /* Distribute IRQs to vNICs */ |
63461a02 | 419 | irqs_left = num_irqs; |
d4e7f092 JK |
420 | vnics_left = pf->num_vnics; |
421 | list_for_each_entry(nn, &pf->vnics, vnic_list) { | |
63461a02 JK |
422 | unsigned int n; |
423 | ||
2c7e41c0 JK |
424 | n = min(NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs, |
425 | DIV_ROUND_UP(irqs_left, vnics_left)); | |
63461a02 JK |
426 | nfp_net_irqs_assign(nn, &pf->irq_entries[num_irqs - irqs_left], |
427 | n); | |
428 | irqs_left -= n; | |
d4e7f092 | 429 | vnics_left--; |
63461a02 JK |
430 | } |
431 | ||
6d4b0d8e JK |
432 | return 0; |
433 | } | |
434 | ||
435 | static void nfp_net_pf_free_irqs(struct nfp_pf *pf) | |
436 | { | |
437 | nfp_net_irqs_disable(pf->pdev); | |
438 | kfree(pf->irq_entries); | |
439 | } | |
440 | ||
441 | static int nfp_net_pf_init_vnics(struct nfp_pf *pf) | |
442 | { | |
443 | struct nfp_net *nn; | |
444 | unsigned int id; | |
445 | int err; | |
446 | ||
d4e7f092 | 447 | /* Finish vNIC init and register */ |
63461a02 | 448 | id = 0; |
d4e7f092 | 449 | list_for_each_entry(nn, &pf->vnics, vnic_list) { |
02082701 JK |
450 | if (!nfp_net_is_data_vnic(nn)) |
451 | continue; | |
d4e7f092 | 452 | err = nfp_net_pf_init_vnic(pf, nn, id); |
63461a02 JK |
453 | if (err) |
454 | goto err_prev_deinit; | |
455 | ||
456 | id++; | |
457 | } | |
458 | ||
459 | return 0; | |
460 | ||
461 | err_prev_deinit: | |
71f8a116 | 462 | list_for_each_entry_continue_reverse(nn, &pf->vnics, vnic_list) |
02082701 JK |
463 | if (nfp_net_is_data_vnic(nn)) |
464 | nfp_net_pf_clean_vnic(pf, nn); | |
63461a02 JK |
465 | return err; |
466 | } | |
467 | ||
02082701 JK |
468 | static int |
469 | nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) | |
7ac9ebd5 | 470 | { |
02082701 | 471 | u8 __iomem *ctrl_bar; |
8aa0cb00 JK |
472 | int err; |
473 | ||
474 | pf->app = nfp_app_alloc(pf, nfp_net_pf_get_app_id(pf)); | |
475 | if (IS_ERR(pf->app)) | |
476 | return PTR_ERR(pf->app); | |
477 | ||
478 | err = nfp_app_init(pf->app); | |
479 | if (err) | |
480 | goto err_free; | |
481 | ||
02082701 JK |
482 | if (!nfp_app_needs_ctrl_vnic(pf->app)) |
483 | return 0; | |
484 | ||
485 | ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%u_net_ctrl_bar", | |
486 | NFP_PF_CSR_SLICE_SIZE, | |
487 | &pf->ctrl_vnic_bar); | |
488 | if (IS_ERR(ctrl_bar)) { | |
489 | err = PTR_ERR(ctrl_bar); | |
490 | goto err_free; | |
491 | } | |
492 | ||
493 | pf->ctrl_vnic = nfp_net_pf_alloc_vnic(pf, false, ctrl_bar, qc_bar, | |
494 | stride, 0); | |
495 | if (IS_ERR(pf->ctrl_vnic)) { | |
496 | err = PTR_ERR(pf->ctrl_vnic); | |
497 | goto err_unmap; | |
498 | } | |
499 | ||
8aa0cb00 | 500 | return 0; |
7ac9ebd5 | 501 | |
02082701 JK |
502 | err_unmap: |
503 | nfp_cpp_area_release_free(pf->ctrl_vnic_bar); | |
8aa0cb00 JK |
504 | err_free: |
505 | nfp_app_free(pf->app); | |
506 | return err; | |
7ac9ebd5 JK |
507 | } |
508 | ||
509 | static void nfp_net_pf_app_clean(struct nfp_pf *pf) | |
510 | { | |
02082701 JK |
511 | if (pf->ctrl_vnic) { |
512 | nfp_net_pf_free_vnic(pf, pf->ctrl_vnic); | |
513 | nfp_cpp_area_release_free(pf->ctrl_vnic_bar); | |
514 | } | |
7ac9ebd5 | 515 | nfp_app_free(pf->app); |
1851f93f | 516 | pf->app = NULL; |
7ac9ebd5 JK |
517 | } |
518 | ||
02082701 JK |
519 | static int nfp_net_pf_app_start_ctrl(struct nfp_pf *pf) |
520 | { | |
521 | int err; | |
522 | ||
523 | if (!pf->ctrl_vnic) | |
524 | return 0; | |
525 | err = nfp_net_pf_init_vnic(pf, pf->ctrl_vnic, 0); | |
526 | if (err) | |
527 | return err; | |
528 | ||
529 | err = nfp_ctrl_open(pf->ctrl_vnic); | |
530 | if (err) | |
531 | goto err_clean_ctrl; | |
532 | ||
533 | return 0; | |
534 | ||
535 | err_clean_ctrl: | |
536 | nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic); | |
537 | return err; | |
538 | } | |
539 | ||
540 | static void nfp_net_pf_app_stop_ctrl(struct nfp_pf *pf) | |
541 | { | |
542 | if (!pf->ctrl_vnic) | |
543 | return; | |
544 | nfp_ctrl_close(pf->ctrl_vnic); | |
545 | nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic); | |
546 | } | |
547 | ||
548 | static int nfp_net_pf_app_start(struct nfp_pf *pf) | |
549 | { | |
550 | int err; | |
551 | ||
552 | err = nfp_net_pf_app_start_ctrl(pf); | |
553 | if (err) | |
554 | return err; | |
555 | ||
556 | err = nfp_app_start(pf->app, pf->ctrl_vnic); | |
557 | if (err) | |
558 | goto err_ctrl_stop; | |
559 | ||
560 | return 0; | |
561 | ||
562 | err_ctrl_stop: | |
563 | nfp_net_pf_app_stop_ctrl(pf); | |
564 | return err; | |
565 | } | |
566 | ||
567 | static void nfp_net_pf_app_stop(struct nfp_pf *pf) | |
568 | { | |
569 | nfp_app_stop(pf->app); | |
570 | nfp_net_pf_app_stop_ctrl(pf); | |
571 | } | |
572 | ||
172f638c JK |
573 | static void nfp_net_pci_remove_finish(struct nfp_pf *pf) |
574 | { | |
02082701 JK |
575 | nfp_net_pf_app_stop(pf); |
576 | /* stop app first, to avoid double free of ctrl vNIC's ddir */ | |
172f638c JK |
577 | nfp_net_debugfs_dir_clean(&pf->ddir); |
578 | ||
6d4b0d8e | 579 | nfp_net_pf_free_irqs(pf); |
172f638c | 580 | |
7ac9ebd5 JK |
581 | nfp_net_pf_app_clean(pf); |
582 | ||
73e253f0 | 583 | nfp_cpp_area_release_free(pf->qc_area); |
d4e7f092 | 584 | nfp_cpp_area_release_free(pf->data_vnic_bar); |
172f638c JK |
585 | } |
586 | ||
3d4ed1e7 JK |
587 | static int |
588 | nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, | |
589 | struct nfp_eth_table *eth_table) | |
590 | { | |
591 | struct nfp_eth_table_port *eth_port; | |
592 | ||
593 | ASSERT_RTNL(); | |
594 | ||
595 | eth_port = nfp_net_find_port(eth_table, port->eth_id); | |
596 | if (!eth_port) { | |
46b25031 | 597 | set_bit(NFP_PORT_CHANGED, &port->flags); |
3d4ed1e7 JK |
598 | nfp_warn(cpp, "Warning: port #%d not present after reconfig\n", |
599 | port->eth_id); | |
600 | return -EIO; | |
601 | } | |
602 | if (eth_port->override_changed) { | |
603 | nfp_warn(cpp, "Port #%d config changed, unregistering. Reboot required before port will be operational again.\n", port->eth_id); | |
604 | port->type = NFP_PORT_INVALID; | |
605 | } | |
606 | ||
607 | memcpy(port->eth_port, eth_port, sizeof(*eth_port)); | |
608 | ||
609 | return 0; | |
610 | } | |
611 | ||
ec8b1fbe | 612 | int nfp_net_refresh_port_table_sync(struct nfp_pf *pf) |
172f638c | 613 | { |
90fdc561 | 614 | struct nfp_eth_table *eth_table; |
172f638c | 615 | struct nfp_net *nn, *next; |
3eb3b74a | 616 | struct nfp_port *port; |
172f638c | 617 | |
ec8b1fbe | 618 | lockdep_assert_held(&pf->lock); |
172f638c JK |
619 | |
620 | /* Check for nfp_net_pci_remove() racing against us */ | |
d4e7f092 | 621 | if (list_empty(&pf->vnics)) |
ec8b1fbe | 622 | return 0; |
172f638c | 623 | |
6d4f8cba JK |
624 | /* Update state of all ports */ |
625 | rtnl_lock(); | |
3eb3b74a JK |
626 | list_for_each_entry(port, &pf->ports, port_list) |
627 | clear_bit(NFP_PORT_CHANGED, &port->flags); | |
90fdc561 JK |
628 | |
629 | eth_table = nfp_eth_read_ports(pf->cpp); | |
630 | if (!eth_table) { | |
46b25031 JK |
631 | list_for_each_entry(port, &pf->ports, port_list) |
632 | if (__nfp_port_get_eth_port(port)) | |
633 | set_bit(NFP_PORT_CHANGED, &port->flags); | |
6d4f8cba | 634 | rtnl_unlock(); |
90fdc561 | 635 | nfp_err(pf->cpp, "Error refreshing port config!\n"); |
ec8b1fbe | 636 | return -EIO; |
90fdc561 JK |
637 | } |
638 | ||
3eb3b74a JK |
639 | list_for_each_entry(port, &pf->ports, port_list) |
640 | if (__nfp_port_get_eth_port(port)) | |
641 | nfp_net_eth_port_update(pf->cpp, port, eth_table); | |
90fdc561 JK |
642 | rtnl_unlock(); |
643 | ||
3d4ed1e7 | 644 | kfree(eth_table); |
90fdc561 | 645 | |
6d4f8cba | 646 | /* Shoot off the ports which became invalid */ |
d4e7f092 | 647 | list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { |
eb488c26 | 648 | if (!nn->port || nn->port->type != NFP_PORT_INVALID) |
172f638c JK |
649 | continue; |
650 | ||
71f8a116 | 651 | nfp_net_pf_clean_vnic(pf, nn); |
9140b30d | 652 | nfp_net_pf_free_vnic(pf, nn); |
172f638c JK |
653 | } |
654 | ||
d4e7f092 | 655 | if (list_empty(&pf->vnics)) |
172f638c | 656 | nfp_net_pci_remove_finish(pf); |
ec8b1fbe JK |
657 | |
658 | return 0; | |
659 | } | |
660 | ||
661 | static void nfp_net_refresh_vnics(struct work_struct *work) | |
662 | { | |
663 | struct nfp_pf *pf = container_of(work, struct nfp_pf, | |
664 | port_refresh_work); | |
665 | ||
666 | mutex_lock(&pf->lock); | |
667 | nfp_net_refresh_port_table_sync(pf); | |
d4e7f092 | 668 | mutex_unlock(&pf->lock); |
172f638c JK |
669 | } |
670 | ||
eb488c26 | 671 | void nfp_net_refresh_port_table(struct nfp_port *port) |
172f638c | 672 | { |
eb488c26 | 673 | struct nfp_pf *pf = port->app->pf; |
172f638c | 674 | |
1f60a581 JK |
675 | set_bit(NFP_PORT_CHANGED, &port->flags); |
676 | ||
90fdc561 JK |
677 | schedule_work(&pf->port_refresh_work); |
678 | } | |
172f638c | 679 | |
eb488c26 | 680 | int nfp_net_refresh_eth_port(struct nfp_port *port) |
90fdc561 | 681 | { |
eb488c26 | 682 | struct nfp_cpp *cpp = port->app->cpp; |
90fdc561 | 683 | struct nfp_eth_table *eth_table; |
3d4ed1e7 | 684 | int ret; |
172f638c | 685 | |
6d4f8cba JK |
686 | clear_bit(NFP_PORT_CHANGED, &port->flags); |
687 | ||
eb488c26 | 688 | eth_table = nfp_eth_read_ports(cpp); |
90fdc561 | 689 | if (!eth_table) { |
46b25031 | 690 | set_bit(NFP_PORT_CHANGED, &port->flags); |
eb488c26 | 691 | nfp_err(cpp, "Error refreshing port state table!\n"); |
90fdc561 JK |
692 | return -EIO; |
693 | } | |
172f638c | 694 | |
3d4ed1e7 | 695 | ret = nfp_net_eth_port_update(cpp, port, eth_table); |
172f638c | 696 | |
90fdc561 | 697 | kfree(eth_table); |
172f638c | 698 | |
3d4ed1e7 | 699 | return ret; |
172f638c JK |
700 | } |
701 | ||
63461a02 JK |
702 | /* |
703 | * PCI device functions | |
704 | */ | |
705 | int nfp_net_pci_probe(struct nfp_pf *pf) | |
706 | { | |
63461a02 | 707 | struct nfp_net_fw_version fw_ver; |
73e253f0 JK |
708 | u8 __iomem *ctrl_bar, *qc_bar; |
709 | u32 ctrl_bar_sz; | |
63461a02 JK |
710 | int stride; |
711 | int err; | |
712 | ||
d4e7f092 | 713 | INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_vnics); |
d12537df | 714 | |
63461a02 JK |
715 | /* Verify that the board has completed initialization */ |
716 | if (!nfp_is_ready(pf->cpp)) { | |
717 | nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); | |
718 | return -EINVAL; | |
719 | } | |
720 | ||
d4e7f092 JK |
721 | mutex_lock(&pf->lock); |
722 | pf->max_data_vnics = nfp_net_pf_get_num_ports(pf); | |
69394af5 JK |
723 | if ((int)pf->max_data_vnics < 0) { |
724 | err = pf->max_data_vnics; | |
725 | goto err_unlock; | |
726 | } | |
63461a02 | 727 | |
c24ca95f JK |
728 | ctrl_bar_sz = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; |
729 | ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0", | |
730 | ctrl_bar_sz, &pf->data_vnic_bar); | |
731 | if (IS_ERR(ctrl_bar)) { | |
732 | err = PTR_ERR(ctrl_bar); | |
733 | if (!pf->fw_loaded && err == -ENOENT) | |
734 | err = -EPROBE_DEFER; | |
d12537df JK |
735 | goto err_unlock; |
736 | } | |
63461a02 JK |
737 | |
738 | nfp_net_get_fw_version(&fw_ver, ctrl_bar); | |
739 | if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { | |
740 | nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n", | |
741 | fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); | |
742 | err = -EINVAL; | |
743 | goto err_ctrl_unmap; | |
744 | } | |
745 | ||
746 | /* Determine stride */ | |
747 | if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1)) { | |
748 | stride = 2; | |
749 | nfp_warn(pf->cpp, "OBSOLETE Firmware detected - VF isolation not available\n"); | |
750 | } else { | |
751 | switch (fw_ver.major) { | |
f9380629 | 752 | case 1 ... 5: |
63461a02 JK |
753 | stride = 4; |
754 | break; | |
755 | default: | |
756 | nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n", | |
757 | fw_ver.resv, fw_ver.class, | |
758 | fw_ver.major, fw_ver.minor); | |
759 | err = -EINVAL; | |
760 | goto err_ctrl_unmap; | |
761 | } | |
762 | } | |
763 | ||
73e253f0 JK |
764 | /* Map queues */ |
765 | qc_bar = nfp_net_map_area(pf->cpp, "net.qc", 0, 0, | |
766 | NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ, | |
767 | &pf->qc_area); | |
768 | if (IS_ERR(qc_bar)) { | |
769 | nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); | |
770 | err = PTR_ERR(qc_bar); | |
63461a02 JK |
771 | goto err_ctrl_unmap; |
772 | } | |
773 | ||
02082701 | 774 | err = nfp_net_pf_app_init(pf, qc_bar, stride); |
7ac9ebd5 | 775 | if (err) |
73e253f0 | 776 | goto err_unmap_qc; |
7ac9ebd5 | 777 | |
63461a02 JK |
778 | pf->ddir = nfp_net_debugfs_device_add(pf->pdev); |
779 | ||
6d4b0d8e JK |
780 | /* Allocate the vnics and do basic init */ |
781 | err = nfp_net_pf_alloc_vnics(pf, ctrl_bar, qc_bar, stride); | |
63461a02 JK |
782 | if (err) |
783 | goto err_clean_ddir; | |
784 | ||
6d4b0d8e JK |
785 | err = nfp_net_pf_alloc_irqs(pf); |
786 | if (err) | |
787 | goto err_free_vnics; | |
788 | ||
02082701 | 789 | err = nfp_net_pf_app_start(pf); |
6d4b0d8e JK |
790 | if (err) |
791 | goto err_free_irqs; | |
792 | ||
02082701 JK |
793 | err = nfp_net_pf_init_vnics(pf); |
794 | if (err) | |
795 | goto err_stop_app; | |
796 | ||
d4e7f092 | 797 | mutex_unlock(&pf->lock); |
d12537df | 798 | |
63461a02 JK |
799 | return 0; |
800 | ||
02082701 JK |
801 | err_stop_app: |
802 | nfp_net_pf_app_stop(pf); | |
6d4b0d8e JK |
803 | err_free_irqs: |
804 | nfp_net_pf_free_irqs(pf); | |
805 | err_free_vnics: | |
806 | nfp_net_pf_free_vnics(pf); | |
63461a02 JK |
807 | err_clean_ddir: |
808 | nfp_net_debugfs_dir_clean(&pf->ddir); | |
7ac9ebd5 | 809 | nfp_net_pf_app_clean(pf); |
73e253f0 JK |
810 | err_unmap_qc: |
811 | nfp_cpp_area_release_free(pf->qc_area); | |
63461a02 | 812 | err_ctrl_unmap: |
d4e7f092 | 813 | nfp_cpp_area_release_free(pf->data_vnic_bar); |
d12537df | 814 | err_unlock: |
d4e7f092 | 815 | mutex_unlock(&pf->lock); |
ab832b8d | 816 | cancel_work_sync(&pf->port_refresh_work); |
63461a02 JK |
817 | return err; |
818 | } | |
819 | ||
820 | void nfp_net_pci_remove(struct nfp_pf *pf) | |
821 | { | |
822 | struct nfp_net *nn; | |
823 | ||
d4e7f092 JK |
824 | mutex_lock(&pf->lock); |
825 | if (list_empty(&pf->vnics)) | |
d12537df JK |
826 | goto out; |
827 | ||
71f8a116 | 828 | list_for_each_entry(nn, &pf->vnics, vnic_list) |
02082701 JK |
829 | if (nfp_net_is_data_vnic(nn)) |
830 | nfp_net_pf_clean_vnic(pf, nn); | |
63461a02 | 831 | |
d4e7f092 | 832 | nfp_net_pf_free_vnics(pf); |
63461a02 | 833 | |
172f638c | 834 | nfp_net_pci_remove_finish(pf); |
d12537df | 835 | out: |
d4e7f092 | 836 | mutex_unlock(&pf->lock); |
172f638c JK |
837 | |
838 | cancel_work_sync(&pf->port_refresh_work); | |
63461a02 | 839 | } |