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