nfp_net_main.c 19.3 KB
Newer Older
Jakub Kicinski's avatar
Jakub Kicinski committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/*
 * Copyright (C) 2015-2017 Netronome Systems, Inc.
 *
 * This software is dual licensed under the GNU General License Version 2,
 * June 1991 as shown in the file COPYING in the top-level directory of this
 * source tree or the BSD 2-Clause License provided below.  You have the
 * option to license this software under the complete terms of either license.
 *
 * The BSD 2-Clause License:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      1. Redistributions of source code must retain the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer.
 *
 *      2. Redistributions in binary form must reproduce the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer in the documentation and/or other materials
 *         provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * nfp_net_main.c
 * Netronome network device driver: Main entry point
 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
 *          Alejandro Lucero <alejandro.lucero@netronome.com>
 *          Jason McMullan <jason.mcmullan@netronome.com>
 *          Rolf Neugebauer <rolf.neugebauer@netronome.com>
 */

#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/init.h>
46
#include <linux/lockdep.h>
Jakub Kicinski's avatar
Jakub Kicinski committed
47 48 49 50
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/msi.h>
#include <linux/random.h>
51
#include <linux/rtnetlink.h>
Jakub Kicinski's avatar
Jakub Kicinski committed
52 53 54 55

#include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h"
56
#include "nfpcore/nfp_nsp.h"
Jakub Kicinski's avatar
Jakub Kicinski committed
57
#include "nfpcore/nfp6000_pcie.h"
58
#include "nfp_app.h"
Jakub Kicinski's avatar
Jakub Kicinski committed
59 60 61
#include "nfp_net_ctrl.h"
#include "nfp_net.h"
#include "nfp_main.h"
Jakub Kicinski's avatar
Jakub Kicinski committed
62
#include "nfp_port.h"
Jakub Kicinski's avatar
Jakub Kicinski committed
63 64 65

#define NFP_PF_CSR_SLICE_SIZE	(32 * 1024)

66
static int nfp_is_ready(struct nfp_pf *pf)
Jakub Kicinski's avatar
Jakub Kicinski committed
67 68 69 70 71
{
	const char *cp;
	long state;
	int err;

72
	cp = nfp_hwinfo_lookup(pf->hwinfo, "board.state");
Jakub Kicinski's avatar
Jakub Kicinski committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
	if (!cp)
		return 0;

	err = kstrtol(cp, 0, &state);
	if (err < 0)
		return 0;

	return state == 15;
}

/**
 * nfp_net_map_area() - Help function to map an area
 * @cpp:    NFP CPP handler
 * @name:   Name for the area
 * @target: CPP target
 * @addr:   CPP address
 * @size:   Size of the area
 * @area:   Area handle (returned).
 *
 * This function is primarily to simplify the code in the main probe
 * function. To undo the effect of this functions call
 * @nfp_cpp_area_release_free(*area);
 *
 * Return: Pointer to memory mapped area or ERR_PTR
 */
static u8 __iomem *nfp_net_map_area(struct nfp_cpp *cpp,
				    const char *name, int isl, int target,
				    unsigned long long addr, unsigned long size,
				    struct nfp_cpp_area **area)
{
	u8 __iomem *res;
	u32 dest;
	int err;

	dest = NFP_CPP_ISLAND_ID(target, NFP_CPP_ACTION_RW, 0, isl);

	*area = nfp_cpp_area_alloc_with_name(cpp, dest, name, addr, size);
	if (!*area) {
		err = -EIO;
		goto err_area;
	}

	err = nfp_cpp_area_acquire(*area);
	if (err < 0)
		goto err_acquire;

	res = nfp_cpp_area_iomem(*area);
	if (!res) {
		err = -EIO;
		goto err_map;
	}

	return res;

err_map:
	nfp_cpp_area_release(*area);
err_acquire:
	nfp_cpp_area_free(*area);
err_area:
	return (u8 __iomem *)ERR_PTR(err);
}

135 136
/**
 * nfp_net_get_mac_addr() - Get the MAC address.
137
 * @pf:       NFP PF handle
138 139 140 141 142 143
 * @nn:       NFP Network structure
 * @id:	      NFP port id
 *
 * First try to get the MAC address from NSP ETH table. If that
 * fails try HWInfo.  As a last resort generate a random address.
 */
144
void
145
nfp_net_get_mac_addr(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id)
Jakub Kicinski's avatar
Jakub Kicinski committed
146
{
Jakub Kicinski's avatar
Jakub Kicinski committed
147
	struct nfp_eth_table_port *eth_port;
148
	struct nfp_net_dp *dp = &nn->dp;
Jakub Kicinski's avatar
Jakub Kicinski committed
149 150 151 152
	u8 mac_addr[ETH_ALEN];
	const char *mac_str;
	char name[32];

Jakub Kicinski's avatar
Jakub Kicinski committed
153 154 155 156
	eth_port = __nfp_port_get_eth_port(nn->port);
	if (eth_port) {
		ether_addr_copy(dp->netdev->dev_addr, eth_port->mac_addr);
		ether_addr_copy(dp->netdev->perm_addr, eth_port->mac_addr);
157 158 159
		return;
	}

Jakub Kicinski's avatar
Jakub Kicinski committed
160 161
	snprintf(name, sizeof(name), "eth%d.mac", id);

162
	mac_str = nfp_hwinfo_lookup(pf->hwinfo, name);
Jakub Kicinski's avatar
Jakub Kicinski committed
163
	if (!mac_str) {
164 165
		dev_warn(dp->dev, "Can't lookup MAC address. Generate\n");
		eth_hw_addr_random(dp->netdev);
Jakub Kicinski's avatar
Jakub Kicinski committed
166 167 168 169 170 171
		return;
	}

	if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
		   &mac_addr[0], &mac_addr[1], &mac_addr[2],
		   &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
172
		dev_warn(dp->dev,
Jakub Kicinski's avatar
Jakub Kicinski committed
173
			 "Can't parse MAC address (%s). Generate.\n", mac_str);
174
		eth_hw_addr_random(dp->netdev);
Jakub Kicinski's avatar
Jakub Kicinski committed
175 176 177
		return;
	}

178 179
	ether_addr_copy(dp->netdev->dev_addr, mac_addr);
	ether_addr_copy(dp->netdev->perm_addr, mac_addr);
Jakub Kicinski's avatar
Jakub Kicinski committed
180 181
}

182
struct nfp_eth_table_port *
183
nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int id)
Jakub Kicinski's avatar
Jakub Kicinski committed
184 185 186
{
	int i;

187 188 189
	for (i = 0; eth_tbl && i < eth_tbl->count; i++)
		if (eth_tbl->ports[i].eth_index == id)
			return &eth_tbl->ports[i];
190

191
	return NULL;
Jakub Kicinski's avatar
Jakub Kicinski committed
192 193
}

194 195 196
static int
nfp_net_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
			       unsigned int default_val)
Jakub Kicinski's avatar
Jakub Kicinski committed
197 198 199 200 201
{
	char name[256];
	int err = 0;
	u64 val;

202
	snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp));
Jakub Kicinski's avatar
Jakub Kicinski committed
203

204
	val = nfp_rtsym_read_le(pf->rtbl, name, &err);
Jakub Kicinski's avatar
Jakub Kicinski committed
205
	if (err) {
206 207 208 209
		if (err == -ENOENT)
			return default_val;
		nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
		return err;
Jakub Kicinski's avatar
Jakub Kicinski committed
210 211 212 213 214
	}

	return val;
}

215 216 217 218 219
static int nfp_net_pf_get_num_ports(struct nfp_pf *pf)
{
	return nfp_net_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1);
}

220 221 222 223 224 225
static int nfp_net_pf_get_app_id(struct nfp_pf *pf)
{
	return nfp_net_pf_rtsym_read_optional(pf, "_pf%u_net_app_id",
					      NFP_APP_CORE_NIC);
}

226 227 228
static u8 __iomem *
nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
		     unsigned int min_size, struct nfp_cpp_area **area)
Jakub Kicinski's avatar
Jakub Kicinski committed
229
{
230
	const struct nfp_rtsym *sym;
Jakub Kicinski's avatar
Jakub Kicinski committed
231
	char pf_symbol[256];
232
	u8 __iomem *mem;
Jakub Kicinski's avatar
Jakub Kicinski committed
233

234
	snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt,
235
		 nfp_cppcore_pcie_unit(pf->cpp));
Jakub Kicinski's avatar
Jakub Kicinski committed
236

237
	sym = nfp_rtsym_lookup(pf->rtbl, pf_symbol);
238 239 240
	if (!sym) {
		nfp_err(pf->cpp, "Failed to find PF symbol %s\n", pf_symbol);
		return (u8 __iomem *)ERR_PTR(-ENOENT);
Jakub Kicinski's avatar
Jakub Kicinski committed
241 242
	}

243 244 245
	if (sym->size < min_size) {
		nfp_err(pf->cpp, "PF symbol %s too small\n", pf_symbol);
		return (u8 __iomem *)ERR_PTR(-EINVAL);
Jakub Kicinski's avatar
Jakub Kicinski committed
246 247
	}

248 249 250 251 252 253
	mem = nfp_net_map_area(pf->cpp, name, sym->domain, sym->target,
			       sym->addr, sym->size, area);
	if (IS_ERR(mem)) {
		nfp_err(pf->cpp, "Failed to map PF symbol %s: %ld\n",
			pf_symbol, PTR_ERR(mem));
		return mem;
Jakub Kicinski's avatar
Jakub Kicinski committed
254 255
	}

256
	return mem;
Jakub Kicinski's avatar
Jakub Kicinski committed
257 258
}

259 260
static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn)
{
Jakub Kicinski's avatar
Jakub Kicinski committed
261
	nfp_port_free(nn->port);
262 263 264 265 266
	list_del(&nn->vnic_list);
	pf->num_vnics--;
	nfp_net_free(nn);
}

267
static void nfp_net_pf_free_vnics(struct nfp_pf *pf)
Jakub Kicinski's avatar
Jakub Kicinski committed
268
{
269
	struct nfp_net *nn, *next;
Jakub Kicinski's avatar
Jakub Kicinski committed
270

271 272 273
	list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list)
		if (nfp_net_is_data_vnic(nn))
			nfp_net_pf_free_vnic(pf, nn);
Jakub Kicinski's avatar
Jakub Kicinski committed
274 275 276
}

static struct nfp_net *
277
nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev,
278
		      void __iomem *ctrl_bar, void __iomem *qc_bar,
279
		      int stride, unsigned int eth_id)
Jakub Kicinski's avatar
Jakub Kicinski committed
280
{
281
	u32 tx_base, rx_base, n_tx_rings, n_rx_rings;
Jakub Kicinski's avatar
Jakub Kicinski committed
282
	struct nfp_net *nn;
283
	int err;
Jakub Kicinski's avatar
Jakub Kicinski committed
284

285 286
	tx_base = readl(ctrl_bar + NFP_NET_CFG_START_TXQ);
	rx_base = readl(ctrl_bar + NFP_NET_CFG_START_RXQ);
Jakub Kicinski's avatar
Jakub Kicinski committed
287 288 289
	n_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS);
	n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS);

290
	/* Allocate and initialise the vNIC */
291
	nn = nfp_net_alloc(pf->pdev, needs_netdev, n_tx_rings, n_rx_rings);
Jakub Kicinski's avatar
Jakub Kicinski committed
292 293 294
	if (IS_ERR(nn))
		return nn;

295
	nn->app = pf->app;
296
	nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar);
297
	nn->dp.ctrl_bar = ctrl_bar;
298 299
	nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ;
	nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ;
300
	nn->dp.is_vf = 0;
Jakub Kicinski's avatar
Jakub Kicinski committed
301 302
	nn->stride_rx = stride;
	nn->stride_tx = stride;
Jakub Kicinski's avatar
Jakub Kicinski committed
303

304 305 306 307 308 309
	if (needs_netdev) {
		err = nfp_app_vnic_init(pf->app, nn, eth_id);
		if (err) {
			nfp_net_free(nn);
			return ERR_PTR(err);
		}
Jakub Kicinski's avatar
Jakub Kicinski committed
310
	}
311 312 313

	pf->num_vnics++;
	list_add_tail(&nn->vnic_list, &pf->vnics);
Jakub Kicinski's avatar
Jakub Kicinski committed
314 315 316 317 318

	return nn;
}

static int
319
nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id)
Jakub Kicinski's avatar
Jakub Kicinski committed
320 321 322 323 324 325 326 327 328
{
	int err;

	/* Get ME clock frequency from ctrl BAR
	 * XXX for now frequency is hardcoded until we figure out how
	 * to get the value from nfp-hwinfo into ctrl bar
	 */
	nn->me_freq_mhz = 1200;

329
	err = nfp_net_init(nn);
Jakub Kicinski's avatar
Jakub Kicinski committed
330 331 332
	if (err)
		return err;

333
	nfp_net_debugfs_vnic_add(nn, pf->ddir, id);
Jakub Kicinski's avatar
Jakub Kicinski committed
334

335 336 337 338 339 340
	if (nn->port) {
		err = nfp_devlink_port_register(pf->app, nn->port);
		if (err)
			goto err_dfs_clean;
	}

Jakub Kicinski's avatar
Jakub Kicinski committed
341 342 343
	nfp_net_info(nn);

	return 0;
344 345 346 347 348

err_dfs_clean:
	nfp_net_debugfs_dir_clean(&nn->debugfs_dir);
	nfp_net_clean(nn);
	return err;
Jakub Kicinski's avatar
Jakub Kicinski committed
349 350 351
}

static int
352
nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar,
353
		       void __iomem *qc_bar, int stride)
Jakub Kicinski's avatar
Jakub Kicinski committed
354 355 356 357 358
{
	struct nfp_net *nn;
	unsigned int i;
	int err;

359
	for (i = 0; i < pf->max_data_vnics; i++) {
360
		nn = nfp_net_pf_alloc_vnic(pf, true, ctrl_bar, qc_bar,
361
					   stride, i);
362 363 364
		if (IS_ERR(nn)) {
			err = PTR_ERR(nn);
			goto err_free_prev;
Jakub Kicinski's avatar
Jakub Kicinski committed
365 366 367
		}

		ctrl_bar += NFP_PF_CSR_SLICE_SIZE;
368

369 370
		/* Kill the vNIC if app init marked it as invalid */
		if (nn->port && nn->port->type == NFP_PORT_INVALID) {
371 372 373
			nfp_net_pf_free_vnic(pf, nn);
			continue;
		}
Jakub Kicinski's avatar
Jakub Kicinski committed
374 375
	}

376
	if (list_empty(&pf->vnics))
377 378
		return -ENODEV;

Jakub Kicinski's avatar
Jakub Kicinski committed
379 380 381
	return 0;

err_free_prev:
382
	nfp_net_pf_free_vnics(pf);
Jakub Kicinski's avatar
Jakub Kicinski committed
383 384 385
	return err;
}

386 387
static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn)
{
388 389
	if (nn->port)
		nfp_devlink_port_unregister(nn->port);
390 391
	nfp_net_debugfs_dir_clean(&nn->debugfs_dir);
	nfp_net_clean(nn);
392
	nfp_app_vnic_clean(pf->app, nn);
393 394
}

395
static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf)
Jakub Kicinski's avatar
Jakub Kicinski committed
396
{
397
	unsigned int wanted_irqs, num_irqs, vnics_left, irqs_left;
Jakub Kicinski's avatar
Jakub Kicinski committed
398 399 400 401
	struct nfp_net *nn;

	/* Get MSI-X vectors */
	wanted_irqs = 0;
402
	list_for_each_entry(nn, &pf->vnics, vnic_list)
403
		wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs;
Jakub Kicinski's avatar
Jakub Kicinski committed
404 405
	pf->irq_entries = kcalloc(wanted_irqs, sizeof(*pf->irq_entries),
				  GFP_KERNEL);
406 407
	if (!pf->irq_entries)
		return -ENOMEM;
Jakub Kicinski's avatar
Jakub Kicinski committed
408 409

	num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries,
410
				      NFP_NET_MIN_VNIC_IRQS * pf->num_vnics,
Jakub Kicinski's avatar
Jakub Kicinski committed
411 412
				      wanted_irqs);
	if (!num_irqs) {
413 414 415
		nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors\n");
		kfree(pf->irq_entries);
		return -ENOMEM;
Jakub Kicinski's avatar
Jakub Kicinski committed
416 417
	}

418
	/* Distribute IRQs to vNICs */
Jakub Kicinski's avatar
Jakub Kicinski committed
419
	irqs_left = num_irqs;
420 421
	vnics_left = pf->num_vnics;
	list_for_each_entry(nn, &pf->vnics, vnic_list) {
Jakub Kicinski's avatar
Jakub Kicinski committed
422 423
		unsigned int n;

424 425
		n = min(NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs,
			DIV_ROUND_UP(irqs_left, vnics_left));
Jakub Kicinski's avatar
Jakub Kicinski committed
426 427 428
		nfp_net_irqs_assign(nn, &pf->irq_entries[num_irqs - irqs_left],
				    n);
		irqs_left -= n;
429
		vnics_left--;
Jakub Kicinski's avatar
Jakub Kicinski committed
430 431
	}

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	return 0;
}

static void nfp_net_pf_free_irqs(struct nfp_pf *pf)
{
	nfp_net_irqs_disable(pf->pdev);
	kfree(pf->irq_entries);
}

static int nfp_net_pf_init_vnics(struct nfp_pf *pf)
{
	struct nfp_net *nn;
	unsigned int id;
	int err;

447
	/* Finish vNIC init and register */
Jakub Kicinski's avatar
Jakub Kicinski committed
448
	id = 0;
449
	list_for_each_entry(nn, &pf->vnics, vnic_list) {
450 451
		if (!nfp_net_is_data_vnic(nn))
			continue;
452
		err = nfp_net_pf_init_vnic(pf, nn, id);
Jakub Kicinski's avatar
Jakub Kicinski committed
453 454 455 456 457 458 459 460 461
		if (err)
			goto err_prev_deinit;

		id++;
	}

	return 0;

err_prev_deinit:
462
	list_for_each_entry_continue_reverse(nn, &pf->vnics, vnic_list)
463 464
		if (nfp_net_is_data_vnic(nn))
			nfp_net_pf_clean_vnic(pf, nn);
Jakub Kicinski's avatar
Jakub Kicinski committed
465 466 467
	return err;
}

468 469
static int
nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
470
{
471
	u8 __iomem *ctrl_bar;
472 473 474 475 476 477 478 479 480 481
	int err;

	pf->app = nfp_app_alloc(pf, nfp_net_pf_get_app_id(pf));
	if (IS_ERR(pf->app))
		return PTR_ERR(pf->app);

	err = nfp_app_init(pf->app);
	if (err)
		goto err_free;

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	if (!nfp_app_needs_ctrl_vnic(pf->app))
		return 0;

	ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%u_net_ctrl_bar",
					NFP_PF_CSR_SLICE_SIZE,
					&pf->ctrl_vnic_bar);
	if (IS_ERR(ctrl_bar)) {
		err = PTR_ERR(ctrl_bar);
		goto err_free;
	}

	pf->ctrl_vnic =	nfp_net_pf_alloc_vnic(pf, false, ctrl_bar, qc_bar,
					      stride, 0);
	if (IS_ERR(pf->ctrl_vnic)) {
		err = PTR_ERR(pf->ctrl_vnic);
		goto err_unmap;
	}

500
	return 0;
501

502 503
err_unmap:
	nfp_cpp_area_release_free(pf->ctrl_vnic_bar);
504 505 506
err_free:
	nfp_app_free(pf->app);
	return err;
507 508 509 510
}

static void nfp_net_pf_app_clean(struct nfp_pf *pf)
{
511 512 513 514
	if (pf->ctrl_vnic) {
		nfp_net_pf_free_vnic(pf, pf->ctrl_vnic);
		nfp_cpp_area_release_free(pf->ctrl_vnic_bar);
	}
515
	nfp_app_free(pf->app);
Simon Horman's avatar
Simon Horman committed
516
	pf->app = NULL;
517 518
}

519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
static int nfp_net_pf_app_start_ctrl(struct nfp_pf *pf)
{
	int err;

	if (!pf->ctrl_vnic)
		return 0;
	err = nfp_net_pf_init_vnic(pf, pf->ctrl_vnic, 0);
	if (err)
		return err;

	err = nfp_ctrl_open(pf->ctrl_vnic);
	if (err)
		goto err_clean_ctrl;

	return 0;

err_clean_ctrl:
	nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic);
	return err;
}

static void nfp_net_pf_app_stop_ctrl(struct nfp_pf *pf)
{
	if (!pf->ctrl_vnic)
		return;
	nfp_ctrl_close(pf->ctrl_vnic);
	nfp_net_pf_clean_vnic(pf, pf->ctrl_vnic);
}

static int nfp_net_pf_app_start(struct nfp_pf *pf)
{
	int err;

	err = nfp_net_pf_app_start_ctrl(pf);
	if (err)
		return err;

	err = nfp_app_start(pf->app, pf->ctrl_vnic);
	if (err)
		goto err_ctrl_stop;

	return 0;

err_ctrl_stop:
	nfp_net_pf_app_stop_ctrl(pf);
	return err;
}

static void nfp_net_pf_app_stop(struct nfp_pf *pf)
{
	nfp_app_stop(pf->app);
	nfp_net_pf_app_stop_ctrl(pf);
}

573 574
static void nfp_net_pci_remove_finish(struct nfp_pf *pf)
{
575 576
	nfp_net_pf_app_stop(pf);
	/* stop app first, to avoid double free of ctrl vNIC's ddir */
577 578
	nfp_net_debugfs_dir_clean(&pf->ddir);

579
	nfp_net_pf_free_irqs(pf);
580

581 582
	nfp_net_pf_app_clean(pf);

583
	nfp_cpp_area_release_free(pf->qc_area);
584
	nfp_cpp_area_release_free(pf->data_vnic_bar);
585 586
}

587 588 589 590 591 592 593 594 595 596
static int
nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
			struct nfp_eth_table *eth_table)
{
	struct nfp_eth_table_port *eth_port;

	ASSERT_RTNL();

	eth_port = nfp_net_find_port(eth_table, port->eth_id);
	if (!eth_port) {
597
		set_bit(NFP_PORT_CHANGED, &port->flags);
598 599 600 601 602 603 604 605 606 607 608 609 610 611
		nfp_warn(cpp, "Warning: port #%d not present after reconfig\n",
			 port->eth_id);
		return -EIO;
	}
	if (eth_port->override_changed) {
		nfp_warn(cpp, "Port #%d config changed, unregistering. Reboot required before port will be operational again.\n", port->eth_id);
		port->type = NFP_PORT_INVALID;
	}

	memcpy(port->eth_port, eth_port, sizeof(*eth_port));

	return 0;
}

612
int nfp_net_refresh_port_table_sync(struct nfp_pf *pf)
613
{
614
	struct nfp_eth_table *eth_table;
615
	struct nfp_net *nn, *next;
616
	struct nfp_port *port;
617

618
	lockdep_assert_held(&pf->lock);
619 620

	/* Check for nfp_net_pci_remove() racing against us */
621
	if (list_empty(&pf->vnics))
622
		return 0;
623

624 625
	/* Update state of all ports */
	rtnl_lock();
626 627
	list_for_each_entry(port, &pf->ports, port_list)
		clear_bit(NFP_PORT_CHANGED, &port->flags);
628 629 630

	eth_table = nfp_eth_read_ports(pf->cpp);
	if (!eth_table) {
631 632 633
		list_for_each_entry(port, &pf->ports, port_list)
			if (__nfp_port_get_eth_port(port))
				set_bit(NFP_PORT_CHANGED, &port->flags);
634
		rtnl_unlock();
635
		nfp_err(pf->cpp, "Error refreshing port config!\n");
636
		return -EIO;
637 638
	}

639 640 641
	list_for_each_entry(port, &pf->ports, port_list)
		if (__nfp_port_get_eth_port(port))
			nfp_net_eth_port_update(pf->cpp, port, eth_table);
642 643
	rtnl_unlock();

644
	kfree(eth_table);
645

646
	/* Shoot off the ports which became invalid */
647
	list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) {
Jakub Kicinski's avatar
Jakub Kicinski committed
648
		if (!nn->port || nn->port->type != NFP_PORT_INVALID)
649 650
			continue;

651
		nfp_net_pf_clean_vnic(pf, nn);
652
		nfp_net_pf_free_vnic(pf, nn);
653 654
	}

655
	if (list_empty(&pf->vnics))
656
		nfp_net_pci_remove_finish(pf);
657 658 659 660 661 662 663 664 665 666 667

	return 0;
}

static void nfp_net_refresh_vnics(struct work_struct *work)
{
	struct nfp_pf *pf = container_of(work, struct nfp_pf,
					 port_refresh_work);

	mutex_lock(&pf->lock);
	nfp_net_refresh_port_table_sync(pf);
668
	mutex_unlock(&pf->lock);
669 670
}

Jakub Kicinski's avatar
Jakub Kicinski committed
671
void nfp_net_refresh_port_table(struct nfp_port *port)
672
{
Jakub Kicinski's avatar
Jakub Kicinski committed
673
	struct nfp_pf *pf = port->app->pf;
674

675 676
	set_bit(NFP_PORT_CHANGED, &port->flags);

677 678
	schedule_work(&pf->port_refresh_work);
}
679

Jakub Kicinski's avatar
Jakub Kicinski committed
680
int nfp_net_refresh_eth_port(struct nfp_port *port)
681
{
Jakub Kicinski's avatar
Jakub Kicinski committed
682
	struct nfp_cpp *cpp = port->app->cpp;
683
	struct nfp_eth_table *eth_table;
684
	int ret;
685

686 687
	clear_bit(NFP_PORT_CHANGED, &port->flags);

Jakub Kicinski's avatar
Jakub Kicinski committed
688
	eth_table = nfp_eth_read_ports(cpp);
689
	if (!eth_table) {
690
		set_bit(NFP_PORT_CHANGED, &port->flags);
Jakub Kicinski's avatar
Jakub Kicinski committed
691
		nfp_err(cpp, "Error refreshing port state table!\n");
692 693
		return -EIO;
	}
694

695
	ret = nfp_net_eth_port_update(cpp, port, eth_table);
696

697
	kfree(eth_table);
698

699
	return ret;
700 701
}

Jakub Kicinski's avatar
Jakub Kicinski committed
702 703 704 705 706 707
/*
 * PCI device functions
 */
int nfp_net_pci_probe(struct nfp_pf *pf)
{
	struct nfp_net_fw_version fw_ver;
708 709
	u8 __iomem *ctrl_bar, *qc_bar;
	u32 ctrl_bar_sz;
Jakub Kicinski's avatar
Jakub Kicinski committed
710 711 712
	int stride;
	int err;

713
	INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_vnics);
714

Jakub Kicinski's avatar
Jakub Kicinski committed
715
	/* Verify that the board has completed initialization */
716
	if (!nfp_is_ready(pf)) {
Jakub Kicinski's avatar
Jakub Kicinski committed
717 718 719 720
		nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n");
		return -EINVAL;
	}

721 722
	mutex_lock(&pf->lock);
	pf->max_data_vnics = nfp_net_pf_get_num_ports(pf);
723 724 725 726
	if ((int)pf->max_data_vnics < 0) {
		err = pf->max_data_vnics;
		goto err_unlock;
	}
Jakub Kicinski's avatar
Jakub Kicinski committed
727

728 729 730 731 732 733 734
	ctrl_bar_sz = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE;
	ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0",
					ctrl_bar_sz, &pf->data_vnic_bar);
	if (IS_ERR(ctrl_bar)) {
		err = PTR_ERR(ctrl_bar);
		if (!pf->fw_loaded && err == -ENOENT)
			err = -EPROBE_DEFER;
735 736
		goto err_unlock;
	}
Jakub Kicinski's avatar
Jakub Kicinski committed
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751

	nfp_net_get_fw_version(&fw_ver, ctrl_bar);
	if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) {
		nfp_err(pf->cpp, "Unknown Firmware ABI %d.%d.%d.%d\n",
			fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor);
		err = -EINVAL;
		goto err_ctrl_unmap;
	}

	/* Determine stride */
	if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1)) {
		stride = 2;
		nfp_warn(pf->cpp, "OBSOLETE Firmware detected - VF isolation not available\n");
	} else {
		switch (fw_ver.major) {
752
		case 1 ... 5:
Jakub Kicinski's avatar
Jakub Kicinski committed
753 754 755 756 757 758 759 760 761 762 763
			stride = 4;
			break;
		default:
			nfp_err(pf->cpp, "Unsupported Firmware ABI %d.%d.%d.%d\n",
				fw_ver.resv, fw_ver.class,
				fw_ver.major, fw_ver.minor);
			err = -EINVAL;
			goto err_ctrl_unmap;
		}
	}

764 765 766 767 768 769 770
	/* Map queues */
	qc_bar = nfp_net_map_area(pf->cpp, "net.qc", 0, 0,
				  NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ,
				  &pf->qc_area);
	if (IS_ERR(qc_bar)) {
		nfp_err(pf->cpp, "Failed to map Queue Controller area.\n");
		err = PTR_ERR(qc_bar);
Jakub Kicinski's avatar
Jakub Kicinski committed
771 772 773
		goto err_ctrl_unmap;
	}

774
	err = nfp_net_pf_app_init(pf, qc_bar, stride);
775
	if (err)
776
		goto err_unmap_qc;
777

Jakub Kicinski's avatar
Jakub Kicinski committed
778 779
	pf->ddir = nfp_net_debugfs_device_add(pf->pdev);

780 781
	/* Allocate the vnics and do basic init */
	err = nfp_net_pf_alloc_vnics(pf, ctrl_bar, qc_bar, stride);
Jakub Kicinski's avatar
Jakub Kicinski committed
782 783 784
	if (err)
		goto err_clean_ddir;

785 786 787 788
	err = nfp_net_pf_alloc_irqs(pf);
	if (err)
		goto err_free_vnics;

789
	err = nfp_net_pf_app_start(pf);
790 791 792
	if (err)
		goto err_free_irqs;

793 794 795 796
	err = nfp_net_pf_init_vnics(pf);
	if (err)
		goto err_stop_app;

797
	mutex_unlock(&pf->lock);
798

Jakub Kicinski's avatar
Jakub Kicinski committed
799 800
	return 0;

801 802
err_stop_app:
	nfp_net_pf_app_stop(pf);
803 804 805 806
err_free_irqs:
	nfp_net_pf_free_irqs(pf);
err_free_vnics:
	nfp_net_pf_free_vnics(pf);
Jakub Kicinski's avatar
Jakub Kicinski committed
807 808
err_clean_ddir:
	nfp_net_debugfs_dir_clean(&pf->ddir);
809
	nfp_net_pf_app_clean(pf);
810 811
err_unmap_qc:
	nfp_cpp_area_release_free(pf->qc_area);
Jakub Kicinski's avatar
Jakub Kicinski committed
812
err_ctrl_unmap:
813
	nfp_cpp_area_release_free(pf->data_vnic_bar);
814
err_unlock:
815
	mutex_unlock(&pf->lock);
816
	cancel_work_sync(&pf->port_refresh_work);
Jakub Kicinski's avatar
Jakub Kicinski committed
817 818 819 820 821 822 823
	return err;
}

void nfp_net_pci_remove(struct nfp_pf *pf)
{
	struct nfp_net *nn;

824 825
	mutex_lock(&pf->lock);
	if (list_empty(&pf->vnics))
826 827
		goto out;

828
	list_for_each_entry(nn, &pf->vnics, vnic_list)
829 830
		if (nfp_net_is_data_vnic(nn))
			nfp_net_pf_clean_vnic(pf, nn);
Jakub Kicinski's avatar
Jakub Kicinski committed
831

832
	nfp_net_pf_free_vnics(pf);
Jakub Kicinski's avatar
Jakub Kicinski committed
833

834
	nfp_net_pci_remove_finish(pf);
835
out:
836
	mutex_unlock(&pf->lock);
837 838

	cancel_work_sync(&pf->port_refresh_work);
Jakub Kicinski's avatar
Jakub Kicinski committed
839
}