162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/*
562306a36Sopenharmony_ci * nfp_main.c
662306a36Sopenharmony_ci * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
762306a36Sopenharmony_ci *          Alejandro Lucero <alejandro.lucero@netronome.com>
862306a36Sopenharmony_ci *          Jason McMullan <jason.mcmullan@netronome.com>
962306a36Sopenharmony_ci *          Rolf Neugebauer <rolf.neugebauer@netronome.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/pci.h>
1662306a36Sopenharmony_ci#include <linux/firmware.h>
1762306a36Sopenharmony_ci#include <linux/vmalloc.h>
1862306a36Sopenharmony_ci#include <net/devlink.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "nfpcore/nfp.h"
2162306a36Sopenharmony_ci#include "nfpcore/nfp_cpp.h"
2262306a36Sopenharmony_ci#include "nfpcore/nfp_dev.h"
2362306a36Sopenharmony_ci#include "nfpcore/nfp_nffw.h"
2462306a36Sopenharmony_ci#include "nfpcore/nfp_nsp.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "nfpcore/nfp6000_pcie.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "nfp_abi.h"
2962306a36Sopenharmony_ci#include "nfp_app.h"
3062306a36Sopenharmony_ci#include "nfp_main.h"
3162306a36Sopenharmony_ci#include "nfp_net.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const char nfp_driver_name[] = "nfp";
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct pci_device_id nfp_pci_device_ids[] = {
3662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP3800,
3762306a36Sopenharmony_ci	  PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
3862306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP3800,
3962306a36Sopenharmony_ci	},
4062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP4000,
4162306a36Sopenharmony_ci	  PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
4262306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
4362306a36Sopenharmony_ci	},
4462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP5000,
4562306a36Sopenharmony_ci	  PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
4662306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
4762306a36Sopenharmony_ci	},
4862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP6000,
4962306a36Sopenharmony_ci	  PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
5062306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
5162306a36Sopenharmony_ci	},
5262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP3800,
5362306a36Sopenharmony_ci	  PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID,
5462306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP3800,
5562306a36Sopenharmony_ci	},
5662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP4000,
5762306a36Sopenharmony_ci	  PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID,
5862306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
5962306a36Sopenharmony_ci	},
6062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP5000,
6162306a36Sopenharmony_ci	  PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID,
6262306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP6000,
6562306a36Sopenharmony_ci	  PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID,
6662306a36Sopenharmony_ci	  PCI_ANY_ID, 0, NFP_DEV_NFP6000,
6762306a36Sopenharmony_ci	},
6862306a36Sopenharmony_ci	{ 0, } /* Required last entry. */
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nfp_pci_device_ids);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciint nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
7362306a36Sopenharmony_ci			       unsigned int default_val)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	char name[256];
7662306a36Sopenharmony_ci	int err = 0;
7762306a36Sopenharmony_ci	u64 val;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	val = nfp_rtsym_read_le(pf->rtbl, name, &err);
8262306a36Sopenharmony_ci	if (err) {
8362306a36Sopenharmony_ci		if (err == -ENOENT)
8462306a36Sopenharmony_ci			return default_val;
8562306a36Sopenharmony_ci		nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
8662306a36Sopenharmony_ci		return err;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return val;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciu8 __iomem *
9362306a36Sopenharmony_cinfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
9462306a36Sopenharmony_ci		 unsigned int min_size, struct nfp_cpp_area **area)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	char pf_symbol[256];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt,
9962306a36Sopenharmony_ci		 nfp_cppcore_pcie_unit(pf->cpp));
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return nfp_rtsym_map(pf->rtbl, pf_symbol, name, min_size, area);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* Callers should hold the devlink instance lock */
10562306a36Sopenharmony_ciint nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
10662306a36Sopenharmony_ci		 void *out_data, u64 out_length)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	unsigned long err_at;
10962306a36Sopenharmony_ci	u64 max_data_sz;
11062306a36Sopenharmony_ci	u32 val = 0;
11162306a36Sopenharmony_ci	int n, err;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!pf->mbox)
11462306a36Sopenharmony_ci		return -EOPNOTSUPP;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	max_data_sz = nfp_rtsym_size(pf->mbox) - NFP_MBOX_SYM_MIN_SIZE;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Check if cmd field is clear */
11962306a36Sopenharmony_ci	err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_CMD, &val);
12062306a36Sopenharmony_ci	if (err || val) {
12162306a36Sopenharmony_ci		nfp_warn(pf->cpp, "failed to issue command (%u): %u, err: %d\n",
12262306a36Sopenharmony_ci			 cmd, val, err);
12362306a36Sopenharmony_ci		return err ?: -EBUSY;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	in_length = min(in_length, max_data_sz);
12762306a36Sopenharmony_ci	n = nfp_rtsym_write(pf->cpp, pf->mbox, NFP_MBOX_DATA, in_data,
12862306a36Sopenharmony_ci			    in_length);
12962306a36Sopenharmony_ci	if (n != in_length)
13062306a36Sopenharmony_ci		return -EIO;
13162306a36Sopenharmony_ci	/* Write data_len and wipe reserved */
13262306a36Sopenharmony_ci	err = nfp_rtsym_writeq(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, in_length);
13362306a36Sopenharmony_ci	if (err)
13462306a36Sopenharmony_ci		return err;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Read back for ordering */
13762306a36Sopenharmony_ci	err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, &val);
13862306a36Sopenharmony_ci	if (err)
13962306a36Sopenharmony_ci		return err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Write cmd and wipe return value */
14262306a36Sopenharmony_ci	err = nfp_rtsym_writeq(pf->cpp, pf->mbox, NFP_MBOX_CMD, cmd);
14362306a36Sopenharmony_ci	if (err)
14462306a36Sopenharmony_ci		return err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	err_at = jiffies + 5 * HZ;
14762306a36Sopenharmony_ci	while (true) {
14862306a36Sopenharmony_ci		/* Wait for command to go to 0 (NFP_MBOX_NO_CMD) */
14962306a36Sopenharmony_ci		err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_CMD, &val);
15062306a36Sopenharmony_ci		if (err)
15162306a36Sopenharmony_ci			return err;
15262306a36Sopenharmony_ci		if (!val)
15362306a36Sopenharmony_ci			break;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		if (time_is_before_eq_jiffies(err_at))
15662306a36Sopenharmony_ci			return -ETIMEDOUT;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		msleep(5);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Copy output if any (could be error info, do it before reading ret) */
16262306a36Sopenharmony_ci	err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, &val);
16362306a36Sopenharmony_ci	if (err)
16462306a36Sopenharmony_ci		return err;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	out_length = min_t(u32, val, min(out_length, max_data_sz));
16762306a36Sopenharmony_ci	n = nfp_rtsym_read(pf->cpp, pf->mbox, NFP_MBOX_DATA,
16862306a36Sopenharmony_ci			   out_data, out_length);
16962306a36Sopenharmony_ci	if (n != out_length)
17062306a36Sopenharmony_ci		return -EIO;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Check if there is an error */
17362306a36Sopenharmony_ci	err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_RET, &val);
17462306a36Sopenharmony_ci	if (err)
17562306a36Sopenharmony_ci		return err;
17662306a36Sopenharmony_ci	if (val)
17762306a36Sopenharmony_ci		return -val;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return out_length;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic bool nfp_board_ready(struct nfp_pf *pf)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	const char *cp;
18562306a36Sopenharmony_ci	long state;
18662306a36Sopenharmony_ci	int err;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	cp = nfp_hwinfo_lookup(pf->hwinfo, "board.state");
18962306a36Sopenharmony_ci	if (!cp)
19062306a36Sopenharmony_ci		return false;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	err = kstrtol(cp, 0, &state);
19362306a36Sopenharmony_ci	if (err < 0)
19462306a36Sopenharmony_ci		return false;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return state == 15;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int nfp_pf_board_state_wait(struct nfp_pf *pf)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	const unsigned long wait_until = jiffies + 10 * HZ;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	while (!nfp_board_ready(pf)) {
20462306a36Sopenharmony_ci		if (time_is_before_eq_jiffies(wait_until)) {
20562306a36Sopenharmony_ci			nfp_err(pf->cpp, "NFP board initialization timeout\n");
20662306a36Sopenharmony_ci			return -EINVAL;
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		nfp_info(pf->cpp, "waiting for board initialization\n");
21062306a36Sopenharmony_ci		if (msleep_interruptible(500))
21162306a36Sopenharmony_ci			return -ERESTARTSYS;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		/* Refresh cached information */
21462306a36Sopenharmony_ci		kfree(pf->hwinfo);
21562306a36Sopenharmony_ci		pf->hwinfo = nfp_hwinfo_read(pf->cpp);
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	int err;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	pf->limit_vfs = nfp_rtsym_read_le(pf->rtbl, "nfd_vf_cfg_max_vfs", &err);
22662306a36Sopenharmony_ci	if (err) {
22762306a36Sopenharmony_ci		/* For backwards compatibility if symbol not found allow all */
22862306a36Sopenharmony_ci		pf->limit_vfs = ~0;
22962306a36Sopenharmony_ci		if (err == -ENOENT)
23062306a36Sopenharmony_ci			return 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		nfp_warn(pf->cpp, "Warning: VF limit read failed: %d\n", err);
23362306a36Sopenharmony_ci		return err;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	err = pci_sriov_set_totalvfs(pf->pdev, pf->limit_vfs);
23762306a36Sopenharmony_ci	if (err)
23862306a36Sopenharmony_ci		nfp_warn(pf->cpp, "Failed to set VF count in sysfs: %d\n", err);
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
24562306a36Sopenharmony_ci	struct nfp_pf *pf = pci_get_drvdata(pdev);
24662306a36Sopenharmony_ci	struct devlink *devlink;
24762306a36Sopenharmony_ci	int err;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (num_vfs > pf->limit_vfs) {
25062306a36Sopenharmony_ci		nfp_info(pf->cpp, "Firmware limits number of VFs to %u\n",
25162306a36Sopenharmony_ci			 pf->limit_vfs);
25262306a36Sopenharmony_ci		return -EINVAL;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	err = pci_enable_sriov(pdev, num_vfs);
25662306a36Sopenharmony_ci	if (err) {
25762306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Failed to enable PCI SR-IOV: %d\n", err);
25862306a36Sopenharmony_ci		return err;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	devlink = priv_to_devlink(pf);
26262306a36Sopenharmony_ci	devl_lock(devlink);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	err = nfp_app_sriov_enable(pf->app, num_vfs);
26562306a36Sopenharmony_ci	if (err) {
26662306a36Sopenharmony_ci		dev_warn(&pdev->dev,
26762306a36Sopenharmony_ci			 "App specific PCI SR-IOV configuration failed: %d\n",
26862306a36Sopenharmony_ci			 err);
26962306a36Sopenharmony_ci		goto err_sriov_disable;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	pf->num_vfs = num_vfs;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Created %d VFs.\n", pf->num_vfs);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	devl_unlock(devlink);
27762306a36Sopenharmony_ci	return num_vfs;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cierr_sriov_disable:
28062306a36Sopenharmony_ci	devl_unlock(devlink);
28162306a36Sopenharmony_ci	pci_disable_sriov(pdev);
28262306a36Sopenharmony_ci	return err;
28362306a36Sopenharmony_ci#endif
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int nfp_pcie_sriov_disable(struct pci_dev *pdev)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
29062306a36Sopenharmony_ci	struct nfp_pf *pf = pci_get_drvdata(pdev);
29162306a36Sopenharmony_ci	struct devlink *devlink;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	devlink = priv_to_devlink(pf);
29462306a36Sopenharmony_ci	devl_lock(devlink);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* If the VFs are assigned we cannot shut down SR-IOV without
29762306a36Sopenharmony_ci	 * causing issues, so just leave the hardware available but
29862306a36Sopenharmony_ci	 * disabled
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	if (pci_vfs_assigned(pdev)) {
30162306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Disabling while VFs assigned - VFs will not be deallocated\n");
30262306a36Sopenharmony_ci		devl_unlock(devlink);
30362306a36Sopenharmony_ci		return -EPERM;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	nfp_app_sriov_disable(pf->app);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pf->num_vfs = 0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	devl_unlock(devlink);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	pci_disable_sriov(pdev);
31362306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Removed VFs.\n");
31462306a36Sopenharmony_ci#endif
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	if (!pci_get_drvdata(pdev))
32162306a36Sopenharmony_ci		return -ENOENT;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (num_vfs == 0)
32462306a36Sopenharmony_ci		return nfp_pcie_sriov_disable(pdev);
32562306a36Sopenharmony_ci	else
32662306a36Sopenharmony_ci		return nfp_pcie_sriov_enable(pdev, num_vfs);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciint nfp_flash_update_common(struct nfp_pf *pf, const struct firmware *fw,
33062306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct device *dev = &pf->pdev->dev;
33362306a36Sopenharmony_ci	struct nfp_nsp *nsp;
33462306a36Sopenharmony_ci	int err;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	nsp = nfp_nsp_open(pf->cpp);
33762306a36Sopenharmony_ci	if (IS_ERR(nsp)) {
33862306a36Sopenharmony_ci		err = PTR_ERR(nsp);
33962306a36Sopenharmony_ci		if (extack)
34062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
34162306a36Sopenharmony_ci		else
34262306a36Sopenharmony_ci			dev_err(dev, "Failed to access the NSP: %d\n", err);
34362306a36Sopenharmony_ci		return err;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	err = nfp_nsp_write_flash(nsp, fw);
34762306a36Sopenharmony_ci	if (err < 0)
34862306a36Sopenharmony_ci		goto exit_close_nsp;
34962306a36Sopenharmony_ci	dev_info(dev, "Finished writing flash image\n");
35062306a36Sopenharmony_ci	err = 0;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciexit_close_nsp:
35362306a36Sopenharmony_ci	nfp_nsp_close(nsp);
35462306a36Sopenharmony_ci	return err;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const struct firmware *
35862306a36Sopenharmony_cinfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	const struct firmware *fw = NULL;
36162306a36Sopenharmony_ci	int err;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	err = request_firmware_direct(&fw, name, &pdev->dev);
36462306a36Sopenharmony_ci	nfp_info(pf->cpp, "  %s: %s\n",
36562306a36Sopenharmony_ci		 name, err ? "not found" : "found");
36662306a36Sopenharmony_ci	if (err)
36762306a36Sopenharmony_ci		return NULL;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return fw;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * nfp_net_fw_find() - Find the correct firmware image for netdev mode
37462306a36Sopenharmony_ci * @pdev:	PCI Device structure
37562306a36Sopenharmony_ci * @pf:		NFP PF Device structure
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * Return: firmware if found and requested successfully.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic const struct firmware *
38062306a36Sopenharmony_cinfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct nfp_eth_table_port *port;
38362306a36Sopenharmony_ci	const struct firmware *fw;
38462306a36Sopenharmony_ci	const char *fw_model;
38562306a36Sopenharmony_ci	char fw_name[256];
38662306a36Sopenharmony_ci	const u8 *serial;
38762306a36Sopenharmony_ci	u16 interface;
38862306a36Sopenharmony_ci	int spc, i, j;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	nfp_info(pf->cpp, "Looking for firmware file in order of priority:\n");
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* First try to find a firmware image specific for this device */
39362306a36Sopenharmony_ci	interface = nfp_cpp_interface(pf->cpp);
39462306a36Sopenharmony_ci	nfp_cpp_serial(pf->cpp, &serial);
39562306a36Sopenharmony_ci	sprintf(fw_name, "netronome/serial-%pMF-%02x-%02x.nffw",
39662306a36Sopenharmony_ci		serial, interface >> 8, interface & 0xff);
39762306a36Sopenharmony_ci	fw = nfp_net_fw_request(pdev, pf, fw_name);
39862306a36Sopenharmony_ci	if (fw)
39962306a36Sopenharmony_ci		return fw;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Then try the PCI name */
40262306a36Sopenharmony_ci	sprintf(fw_name, "netronome/pci-%s.nffw", pci_name(pdev));
40362306a36Sopenharmony_ci	fw = nfp_net_fw_request(pdev, pf, fw_name);
40462306a36Sopenharmony_ci	if (fw)
40562306a36Sopenharmony_ci		return fw;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Finally try the card type and media */
40862306a36Sopenharmony_ci	if (!pf->eth_tbl) {
40962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Error: can't identify media config\n");
41062306a36Sopenharmony_ci		return NULL;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	fw_model = nfp_hwinfo_lookup(pf->hwinfo, "nffw.partno");
41462306a36Sopenharmony_ci	if (!fw_model)
41562306a36Sopenharmony_ci		fw_model = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno");
41662306a36Sopenharmony_ci	if (!fw_model) {
41762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Error: can't read part number\n");
41862306a36Sopenharmony_ci		return NULL;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	spc = ARRAY_SIZE(fw_name);
42262306a36Sopenharmony_ci	spc -= snprintf(fw_name, spc, "netronome/nic_%s", fw_model);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	for (i = 0; spc > 0 && i < pf->eth_tbl->count; i += j) {
42562306a36Sopenharmony_ci		port = &pf->eth_tbl->ports[i];
42662306a36Sopenharmony_ci		j = 1;
42762306a36Sopenharmony_ci		while (i + j < pf->eth_tbl->count &&
42862306a36Sopenharmony_ci		       port->speed == port[j].speed)
42962306a36Sopenharmony_ci			j++;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		spc -= snprintf(&fw_name[ARRAY_SIZE(fw_name) - spc], spc,
43262306a36Sopenharmony_ci				"_%dx%d", j, port->speed / 1000);
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (spc <= 0)
43662306a36Sopenharmony_ci		return NULL;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	spc -= snprintf(&fw_name[ARRAY_SIZE(fw_name) - spc], spc, ".nffw");
43962306a36Sopenharmony_ci	if (spc <= 0)
44062306a36Sopenharmony_ci		return NULL;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return nfp_net_fw_request(pdev, pf, fw_name);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int
44662306a36Sopenharmony_cinfp_get_fw_policy_value(struct pci_dev *pdev, struct nfp_nsp *nsp,
44762306a36Sopenharmony_ci			const char *key, const char *default_val, int max_val,
44862306a36Sopenharmony_ci			int *value)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	char hwinfo[64];
45162306a36Sopenharmony_ci	long hi_val;
45262306a36Sopenharmony_ci	int err;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	snprintf(hwinfo, sizeof(hwinfo), key);
45562306a36Sopenharmony_ci	err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
45662306a36Sopenharmony_ci					     default_val);
45762306a36Sopenharmony_ci	if (err)
45862306a36Sopenharmony_ci		return err;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	err = kstrtol(hwinfo, 0, &hi_val);
46162306a36Sopenharmony_ci	if (err || hi_val < 0 || hi_val > max_val) {
46262306a36Sopenharmony_ci		dev_warn(&pdev->dev,
46362306a36Sopenharmony_ci			 "Invalid value '%s' from '%s', ignoring\n",
46462306a36Sopenharmony_ci			 hwinfo, key);
46562306a36Sopenharmony_ci		err = kstrtol(default_val, 0, &hi_val);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	*value = hi_val;
46962306a36Sopenharmony_ci	return err;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/**
47362306a36Sopenharmony_ci * nfp_fw_load() - Load the firmware image
47462306a36Sopenharmony_ci * @pdev:       PCI Device structure
47562306a36Sopenharmony_ci * @pf:		NFP PF Device structure
47662306a36Sopenharmony_ci * @nsp:	NFP SP handle
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * Return: -ERRNO, 0 for no firmware loaded, 1 for firmware loaded
47962306a36Sopenharmony_ci */
48062306a36Sopenharmony_cistatic int
48162306a36Sopenharmony_cinfp_fw_load(struct pci_dev *pdev, struct nfp_pf *pf, struct nfp_nsp *nsp)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	bool do_reset, fw_loaded = false;
48462306a36Sopenharmony_ci	const struct firmware *fw = NULL;
48562306a36Sopenharmony_ci	int err, reset, policy, ifcs = 0;
48662306a36Sopenharmony_ci	char *token, *ptr;
48762306a36Sopenharmony_ci	char hwinfo[64];
48862306a36Sopenharmony_ci	u16 interface;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	snprintf(hwinfo, sizeof(hwinfo), "abi_drv_load_ifc");
49162306a36Sopenharmony_ci	err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
49262306a36Sopenharmony_ci					     NFP_NSP_DRV_LOAD_IFC_DEFAULT);
49362306a36Sopenharmony_ci	if (err)
49462306a36Sopenharmony_ci		return err;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	interface = nfp_cpp_interface(pf->cpp);
49762306a36Sopenharmony_ci	ptr = hwinfo;
49862306a36Sopenharmony_ci	while ((token = strsep(&ptr, ","))) {
49962306a36Sopenharmony_ci		unsigned long interface_hi;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		err = kstrtoul(token, 0, &interface_hi);
50262306a36Sopenharmony_ci		if (err) {
50362306a36Sopenharmony_ci			dev_err(&pdev->dev,
50462306a36Sopenharmony_ci				"Failed to parse interface '%s': %d\n",
50562306a36Sopenharmony_ci				token, err);
50662306a36Sopenharmony_ci			return err;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		ifcs++;
51062306a36Sopenharmony_ci		if (interface == interface_hi)
51162306a36Sopenharmony_ci			break;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (!token) {
51562306a36Sopenharmony_ci		dev_info(&pdev->dev, "Firmware will be loaded by partner\n");
51662306a36Sopenharmony_ci		return 0;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	err = nfp_get_fw_policy_value(pdev, nsp, "abi_drv_reset",
52062306a36Sopenharmony_ci				      NFP_NSP_DRV_RESET_DEFAULT,
52162306a36Sopenharmony_ci				      NFP_NSP_DRV_RESET_NEVER, &reset);
52262306a36Sopenharmony_ci	if (err)
52362306a36Sopenharmony_ci		return err;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	err = nfp_get_fw_policy_value(pdev, nsp, "app_fw_from_flash",
52662306a36Sopenharmony_ci				      NFP_NSP_APP_FW_LOAD_DEFAULT,
52762306a36Sopenharmony_ci				      NFP_NSP_APP_FW_LOAD_PREF, &policy);
52862306a36Sopenharmony_ci	if (err)
52962306a36Sopenharmony_ci		return err;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	fw = nfp_net_fw_find(pdev, pf);
53262306a36Sopenharmony_ci	do_reset = reset == NFP_NSP_DRV_RESET_ALWAYS ||
53362306a36Sopenharmony_ci		   (fw && reset == NFP_NSP_DRV_RESET_DISK);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (do_reset) {
53662306a36Sopenharmony_ci		dev_info(&pdev->dev, "Soft-resetting the NFP\n");
53762306a36Sopenharmony_ci		err = nfp_nsp_device_soft_reset(nsp);
53862306a36Sopenharmony_ci		if (err < 0) {
53962306a36Sopenharmony_ci			dev_err(&pdev->dev,
54062306a36Sopenharmony_ci				"Failed to soft reset the NFP: %d\n", err);
54162306a36Sopenharmony_ci			goto exit_release_fw;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (fw && policy != NFP_NSP_APP_FW_LOAD_FLASH) {
54662306a36Sopenharmony_ci		if (nfp_nsp_has_fw_loaded(nsp) && nfp_nsp_fw_loaded(nsp))
54762306a36Sopenharmony_ci			goto exit_release_fw;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		err = nfp_nsp_load_fw(nsp, fw);
55062306a36Sopenharmony_ci		if (err < 0) {
55162306a36Sopenharmony_ci			dev_err(&pdev->dev, "FW loading failed: %d\n",
55262306a36Sopenharmony_ci				err);
55362306a36Sopenharmony_ci			goto exit_release_fw;
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci		dev_info(&pdev->dev, "Finished loading FW image\n");
55662306a36Sopenharmony_ci		fw_loaded = true;
55762306a36Sopenharmony_ci	} else if (policy != NFP_NSP_APP_FW_LOAD_DISK &&
55862306a36Sopenharmony_ci		   nfp_nsp_has_stored_fw_load(nsp)) {
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		/* Don't propagate this error to stick with legacy driver
56162306a36Sopenharmony_ci		 * behavior, failure will be detected later during init.
56262306a36Sopenharmony_ci		 */
56362306a36Sopenharmony_ci		if (!nfp_nsp_load_stored_fw(nsp))
56462306a36Sopenharmony_ci			dev_info(&pdev->dev, "Finished loading stored FW image\n");
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		/* Don't flag the fw_loaded in this case since other devices
56762306a36Sopenharmony_ci		 * may reuse the firmware when configured this way
56862306a36Sopenharmony_ci		 */
56962306a36Sopenharmony_ci	} else {
57062306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Didn't load firmware, please update flash or reconfigure card\n");
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ciexit_release_fw:
57462306a36Sopenharmony_ci	release_firmware(fw);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* We don't want to unload firmware when other devices may still be
57762306a36Sopenharmony_ci	 * dependent on it, which could be the case if there are multiple
57862306a36Sopenharmony_ci	 * devices that could load firmware.
57962306a36Sopenharmony_ci	 */
58062306a36Sopenharmony_ci	if (fw_loaded && ifcs == 1)
58162306a36Sopenharmony_ci		pf->unload_fw_on_remove = true;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return err < 0 ? err : fw_loaded;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void
58762306a36Sopenharmony_cinfp_nsp_init_ports(struct pci_dev *pdev, struct nfp_pf *pf,
58862306a36Sopenharmony_ci		   struct nfp_nsp *nsp)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	bool needs_reinit = false;
59162306a36Sopenharmony_ci	int i;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
59462306a36Sopenharmony_ci	if (!pf->eth_tbl)
59562306a36Sopenharmony_ci		return;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!nfp_nsp_has_mac_reinit(nsp))
59862306a36Sopenharmony_ci		return;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	for (i = 0; i < pf->eth_tbl->count; i++)
60162306a36Sopenharmony_ci		needs_reinit |= pf->eth_tbl->ports[i].override_changed;
60262306a36Sopenharmony_ci	if (!needs_reinit)
60362306a36Sopenharmony_ci		return;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	kfree(pf->eth_tbl);
60662306a36Sopenharmony_ci	if (nfp_nsp_mac_reinit(nsp))
60762306a36Sopenharmony_ci		dev_warn(&pdev->dev, "MAC reinit failed\n");
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct nfp_nsp *nsp;
61562306a36Sopenharmony_ci	int err;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	err = nfp_resource_wait(pf->cpp, NFP_RESOURCE_NSP, 30);
61862306a36Sopenharmony_ci	if (err)
61962306a36Sopenharmony_ci		return err;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	nsp = nfp_nsp_open(pf->cpp);
62262306a36Sopenharmony_ci	if (IS_ERR(nsp)) {
62362306a36Sopenharmony_ci		err = PTR_ERR(nsp);
62462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to access the NSP: %d\n", err);
62562306a36Sopenharmony_ci		return err;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	err = nfp_nsp_wait(nsp);
62962306a36Sopenharmony_ci	if (err < 0)
63062306a36Sopenharmony_ci		goto exit_close_nsp;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	nfp_nsp_init_ports(pdev, pf, nsp);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	pf->nspi = __nfp_nsp_identify(nsp);
63562306a36Sopenharmony_ci	if (pf->nspi)
63662306a36Sopenharmony_ci		dev_info(&pdev->dev, "BSP: %s\n", pf->nspi->version);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	err = nfp_fw_load(pdev, pf, nsp);
63962306a36Sopenharmony_ci	if (err < 0) {
64062306a36Sopenharmony_ci		kfree(pf->nspi);
64162306a36Sopenharmony_ci		kfree(pf->eth_tbl);
64262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to load FW\n");
64362306a36Sopenharmony_ci		goto exit_close_nsp;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	pf->fw_loaded = !!err;
64762306a36Sopenharmony_ci	err = 0;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ciexit_close_nsp:
65062306a36Sopenharmony_ci	nfp_nsp_close(nsp);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return err;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void nfp_fw_unload(struct nfp_pf *pf)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct nfp_nsp *nsp;
65862306a36Sopenharmony_ci	int err;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	nsp = nfp_nsp_open(pf->cpp);
66162306a36Sopenharmony_ci	if (IS_ERR(nsp)) {
66262306a36Sopenharmony_ci		nfp_err(pf->cpp, "Reset failed, can't open NSP\n");
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	err = nfp_nsp_device_soft_reset(nsp);
66762306a36Sopenharmony_ci	if (err < 0)
66862306a36Sopenharmony_ci		dev_warn(&pf->pdev->dev, "Couldn't unload firmware: %d\n", err);
66962306a36Sopenharmony_ci	else
67062306a36Sopenharmony_ci		dev_info(&pf->pdev->dev, "Firmware safely unloaded\n");
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	nfp_nsp_close(nsp);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int nfp_pf_find_rtsyms(struct nfp_pf *pf)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	char pf_symbol[256];
67862306a36Sopenharmony_ci	unsigned int pf_id;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	pf_id = nfp_cppcore_pcie_unit(pf->cpp);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* Optional per-PCI PF mailbox */
68362306a36Sopenharmony_ci	snprintf(pf_symbol, sizeof(pf_symbol), NFP_MBOX_SYM_NAME, pf_id);
68462306a36Sopenharmony_ci	pf->mbox = nfp_rtsym_lookup(pf->rtbl, pf_symbol);
68562306a36Sopenharmony_ci	if (pf->mbox && nfp_rtsym_size(pf->mbox) < NFP_MBOX_SYM_MIN_SIZE) {
68662306a36Sopenharmony_ci		nfp_err(pf->cpp, "PF mailbox symbol too small: %llu < %d\n",
68762306a36Sopenharmony_ci			nfp_rtsym_size(pf->mbox), NFP_MBOX_SYM_MIN_SIZE);
68862306a36Sopenharmony_ci		return -EINVAL;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return 0;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ciint nfp_net_pf_get_app_id(struct nfp_pf *pf)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	return nfp_pf_rtsym_read_optional(pf, "_pf%u_net_app_id",
69762306a36Sopenharmony_ci					  NFP_APP_CORE_NIC);
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic u64 nfp_net_pf_get_app_cap(struct nfp_pf *pf)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	char name[32];
70362306a36Sopenharmony_ci	int err = 0;
70462306a36Sopenharmony_ci	u64 val;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	snprintf(name, sizeof(name), "_pf%u_net_app_cap", nfp_cppcore_pcie_unit(pf->cpp));
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	val = nfp_rtsym_read_le(pf->rtbl, name, &err);
70962306a36Sopenharmony_ci	if (err) {
71062306a36Sopenharmony_ci		if (err != -ENOENT)
71162306a36Sopenharmony_ci			nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		return 0;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return val;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void nfp_pf_cfg_hwinfo(struct nfp_pf *pf)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct nfp_nsp *nsp;
72262306a36Sopenharmony_ci	char hwinfo[32];
72362306a36Sopenharmony_ci	bool sp_indiff;
72462306a36Sopenharmony_ci	int err;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	nsp = nfp_nsp_open(pf->cpp);
72762306a36Sopenharmony_ci	if (IS_ERR(nsp))
72862306a36Sopenharmony_ci		return;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!nfp_nsp_has_hwinfo_set(nsp))
73162306a36Sopenharmony_ci		goto end;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	sp_indiff = (nfp_net_pf_get_app_id(pf) == NFP_APP_FLOWER_NIC) ||
73462306a36Sopenharmony_ci		    (nfp_net_pf_get_app_cap(pf) & NFP_NET_APP_CAP_SP_INDIFF);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* No need to clean `sp_indiff` in driver, management firmware
73762306a36Sopenharmony_ci	 * will do it when application firmware is unloaded.
73862306a36Sopenharmony_ci	 */
73962306a36Sopenharmony_ci	snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff);
74062306a36Sopenharmony_ci	err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
74162306a36Sopenharmony_ci	/* Not a fatal error, no need to return error to stop driver from loading */
74262306a36Sopenharmony_ci	if (err) {
74362306a36Sopenharmony_ci		nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err);
74462306a36Sopenharmony_ci	} else {
74562306a36Sopenharmony_ci		/* Need reinit eth_tbl since the eth table state may change
74662306a36Sopenharmony_ci		 * after sp_indiff is configured.
74762306a36Sopenharmony_ci		 */
74862306a36Sopenharmony_ci		kfree(pf->eth_tbl);
74962306a36Sopenharmony_ci		pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ciend:
75362306a36Sopenharmony_ci	nfp_nsp_close(nsp);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int nfp_pci_probe(struct pci_dev *pdev,
75762306a36Sopenharmony_ci			 const struct pci_device_id *pci_id)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	const struct nfp_dev_info *dev_info;
76062306a36Sopenharmony_ci	struct devlink *devlink;
76162306a36Sopenharmony_ci	struct nfp_pf *pf;
76262306a36Sopenharmony_ci	int err;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if ((pdev->vendor == PCI_VENDOR_ID_NETRONOME ||
76562306a36Sopenharmony_ci	     pdev->vendor == PCI_VENDOR_ID_CORIGINE) &&
76662306a36Sopenharmony_ci	    (pdev->device == PCI_DEVICE_ID_NFP3800_VF ||
76762306a36Sopenharmony_ci	     pdev->device == PCI_DEVICE_ID_NFP6000_VF))
76862306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Binding NFP VF device to the NFP PF driver, the VF driver is called 'nfp_netvf'\n");
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	dev_info = &nfp_dev_info[pci_id->driver_data];
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	err = pci_enable_device(pdev);
77362306a36Sopenharmony_ci	if (err < 0)
77462306a36Sopenharmony_ci		return err;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	pci_set_master(pdev);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, dev_info->dma_mask);
77962306a36Sopenharmony_ci	if (err)
78062306a36Sopenharmony_ci		goto err_pci_disable;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	err = pci_request_regions(pdev, nfp_driver_name);
78362306a36Sopenharmony_ci	if (err < 0) {
78462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to reserve pci resources.\n");
78562306a36Sopenharmony_ci		goto err_pci_disable;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	devlink = devlink_alloc(&nfp_devlink_ops, sizeof(*pf), &pdev->dev);
78962306a36Sopenharmony_ci	if (!devlink) {
79062306a36Sopenharmony_ci		err = -ENOMEM;
79162306a36Sopenharmony_ci		goto err_rel_regions;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci	pf = devlink_priv(devlink);
79462306a36Sopenharmony_ci	INIT_LIST_HEAD(&pf->vnics);
79562306a36Sopenharmony_ci	INIT_LIST_HEAD(&pf->ports);
79662306a36Sopenharmony_ci	pci_set_drvdata(pdev, pf);
79762306a36Sopenharmony_ci	pf->pdev = pdev;
79862306a36Sopenharmony_ci	pf->dev_info = dev_info;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	pf->wq = alloc_workqueue("nfp-%s", 0, 2, pci_name(pdev));
80162306a36Sopenharmony_ci	if (!pf->wq) {
80262306a36Sopenharmony_ci		err = -ENOMEM;
80362306a36Sopenharmony_ci		goto err_pci_priv_unset;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	pf->cpp = nfp_cpp_from_nfp6000_pcie(pdev, dev_info);
80762306a36Sopenharmony_ci	if (IS_ERR(pf->cpp)) {
80862306a36Sopenharmony_ci		err = PTR_ERR(pf->cpp);
80962306a36Sopenharmony_ci		goto err_disable_msix;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	err = nfp_resource_table_init(pf->cpp);
81362306a36Sopenharmony_ci	if (err)
81462306a36Sopenharmony_ci		goto err_cpp_free;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	pf->hwinfo = nfp_hwinfo_read(pf->cpp);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	dev_info(&pdev->dev, "Assembly: %s%s%s-%s CPLD: %s\n",
81962306a36Sopenharmony_ci		 nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor"),
82062306a36Sopenharmony_ci		 nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno"),
82162306a36Sopenharmony_ci		 nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial"),
82262306a36Sopenharmony_ci		 nfp_hwinfo_lookup(pf->hwinfo, "assembly.revision"),
82362306a36Sopenharmony_ci		 nfp_hwinfo_lookup(pf->hwinfo, "cpld.version"));
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	err = nfp_pf_board_state_wait(pf);
82662306a36Sopenharmony_ci	if (err)
82762306a36Sopenharmony_ci		goto err_hwinfo_free;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	err = nfp_nsp_init(pdev, pf);
83062306a36Sopenharmony_ci	if (err)
83162306a36Sopenharmony_ci		goto err_hwinfo_free;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	pf->mip = nfp_mip_open(pf->cpp);
83462306a36Sopenharmony_ci	pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	err = nfp_pf_find_rtsyms(pf);
83762306a36Sopenharmony_ci	if (err)
83862306a36Sopenharmony_ci		goto err_fw_unload;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	pf->dump_flag = NFP_DUMP_NSP_DIAG;
84162306a36Sopenharmony_ci	pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	err = nfp_pcie_sriov_read_nfd_limit(pf);
84462306a36Sopenharmony_ci	if (err)
84562306a36Sopenharmony_ci		goto err_fw_unload;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	pf->num_vfs = pci_num_vf(pdev);
84862306a36Sopenharmony_ci	if (pf->num_vfs > pf->limit_vfs) {
84962306a36Sopenharmony_ci		dev_err(&pdev->dev,
85062306a36Sopenharmony_ci			"Error: %d VFs already enabled, but loaded FW can only support %d\n",
85162306a36Sopenharmony_ci			pf->num_vfs, pf->limit_vfs);
85262306a36Sopenharmony_ci		err = -EINVAL;
85362306a36Sopenharmony_ci		goto err_fw_unload;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	nfp_pf_cfg_hwinfo(pf);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	err = nfp_net_pci_probe(pf);
85962306a36Sopenharmony_ci	if (err)
86062306a36Sopenharmony_ci		goto err_fw_unload;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	err = nfp_hwmon_register(pf);
86362306a36Sopenharmony_ci	if (err) {
86462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register hwmon info\n");
86562306a36Sopenharmony_ci		goto err_net_remove;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return 0;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cierr_net_remove:
87162306a36Sopenharmony_ci	nfp_net_pci_remove(pf);
87262306a36Sopenharmony_cierr_fw_unload:
87362306a36Sopenharmony_ci	kfree(pf->rtbl);
87462306a36Sopenharmony_ci	nfp_mip_close(pf->mip);
87562306a36Sopenharmony_ci	if (pf->unload_fw_on_remove)
87662306a36Sopenharmony_ci		nfp_fw_unload(pf);
87762306a36Sopenharmony_ci	kfree(pf->eth_tbl);
87862306a36Sopenharmony_ci	kfree(pf->nspi);
87962306a36Sopenharmony_ci	vfree(pf->dumpspec);
88062306a36Sopenharmony_cierr_hwinfo_free:
88162306a36Sopenharmony_ci	kfree(pf->hwinfo);
88262306a36Sopenharmony_cierr_cpp_free:
88362306a36Sopenharmony_ci	nfp_cpp_free(pf->cpp);
88462306a36Sopenharmony_cierr_disable_msix:
88562306a36Sopenharmony_ci	destroy_workqueue(pf->wq);
88662306a36Sopenharmony_cierr_pci_priv_unset:
88762306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
88862306a36Sopenharmony_ci	devlink_free(devlink);
88962306a36Sopenharmony_cierr_rel_regions:
89062306a36Sopenharmony_ci	pci_release_regions(pdev);
89162306a36Sopenharmony_cierr_pci_disable:
89262306a36Sopenharmony_ci	pci_disable_device(pdev);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return err;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct nfp_pf *pf;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	pf = pci_get_drvdata(pdev);
90262306a36Sopenharmony_ci	if (!pf)
90362306a36Sopenharmony_ci		return;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	nfp_hwmon_unregister(pf);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	nfp_pcie_sriov_disable(pdev);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	nfp_net_pci_remove(pf);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	vfree(pf->dumpspec);
91262306a36Sopenharmony_ci	kfree(pf->rtbl);
91362306a36Sopenharmony_ci	nfp_mip_close(pf->mip);
91462306a36Sopenharmony_ci	if (unload_fw && pf->unload_fw_on_remove)
91562306a36Sopenharmony_ci		nfp_fw_unload(pf);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	destroy_workqueue(pf->wq);
91862306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
91962306a36Sopenharmony_ci	kfree(pf->hwinfo);
92062306a36Sopenharmony_ci	nfp_cpp_free(pf->cpp);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	kfree(pf->eth_tbl);
92362306a36Sopenharmony_ci	kfree(pf->nspi);
92462306a36Sopenharmony_ci	devlink_free(priv_to_devlink(pf));
92562306a36Sopenharmony_ci	pci_release_regions(pdev);
92662306a36Sopenharmony_ci	pci_disable_device(pdev);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic void nfp_pci_remove(struct pci_dev *pdev)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	__nfp_pci_shutdown(pdev, true);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic void nfp_pci_shutdown(struct pci_dev *pdev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	__nfp_pci_shutdown(pdev, false);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic struct pci_driver nfp_pci_driver = {
94062306a36Sopenharmony_ci	.name			= nfp_driver_name,
94162306a36Sopenharmony_ci	.id_table		= nfp_pci_device_ids,
94262306a36Sopenharmony_ci	.probe			= nfp_pci_probe,
94362306a36Sopenharmony_ci	.remove			= nfp_pci_remove,
94462306a36Sopenharmony_ci	.shutdown		= nfp_pci_shutdown,
94562306a36Sopenharmony_ci	.sriov_configure	= nfp_pcie_sriov_configure,
94662306a36Sopenharmony_ci};
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic int __init nfp_main_init(void)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	int err;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	pr_info("%s: NFP PCIe Driver, Copyright (C) 2014-2020 Netronome Systems\n",
95362306a36Sopenharmony_ci		nfp_driver_name);
95462306a36Sopenharmony_ci	pr_info("%s: NFP PCIe Driver, Copyright (C) 2021-2022 Corigine Inc.\n",
95562306a36Sopenharmony_ci		nfp_driver_name);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	nfp_net_debugfs_create();
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	err = pci_register_driver(&nfp_pci_driver);
96062306a36Sopenharmony_ci	if (err < 0)
96162306a36Sopenharmony_ci		goto err_destroy_debugfs;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	err = pci_register_driver(&nfp_netvf_pci_driver);
96462306a36Sopenharmony_ci	if (err)
96562306a36Sopenharmony_ci		goto err_unreg_pf;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return err;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cierr_unreg_pf:
97062306a36Sopenharmony_ci	pci_unregister_driver(&nfp_pci_driver);
97162306a36Sopenharmony_cierr_destroy_debugfs:
97262306a36Sopenharmony_ci	nfp_net_debugfs_destroy();
97362306a36Sopenharmony_ci	return err;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic void __exit nfp_main_exit(void)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	pci_unregister_driver(&nfp_netvf_pci_driver);
97962306a36Sopenharmony_ci	pci_unregister_driver(&nfp_pci_driver);
98062306a36Sopenharmony_ci	nfp_net_debugfs_destroy();
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cimodule_init(nfp_main_init);
98462306a36Sopenharmony_cimodule_exit(nfp_main_exit);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0058-0011_2x40.nffw");
98762306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0058-0012_2x40.nffw");
98862306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0081-0001_1x40.nffw");
98962306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0081-0001_4x10.nffw");
99062306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0096-0001_2x10.nffw");
99162306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0097-0001_2x40.nffw");
99262306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0097-0001_4x10_1x40.nffw");
99362306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0097-0001_8x10.nffw");
99462306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x10.nffw");
99562306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x25.nffw");
99662306a36Sopenharmony_ciMODULE_FIRMWARE("netronome/nic_AMDA0099-0001_1x10_1x25.nffw");
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ciMODULE_AUTHOR("Corigine, Inc. <oss-drivers@corigine.com>");
99962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
100062306a36Sopenharmony_ciMODULE_DESCRIPTION("The Network Flow Processor (NFP) driver.");
1001