Commit 5fcc2cfc authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'nfp-support-fec-mode-reporting-and-auto-neg'

Simon Horman says:

====================
nfp: support FEC mode reporting and auto-neg

this series adds support for the following features to the nfp driver:

* Patch 1/5: Support active FEC mode
* Patch 2/5: Don't halt driver on non-fatal error when interacting with fw
* Patch 3/5: Treat port independence as a firmware rather than port property
* Patch 4/5: Support link auto negotiation
* Patch 5/5: Support restart of link auto negotiation
====================

Link: https://lore.kernel.org/r/20220929085832.622510-1-simon.horman@corigine.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents f77a9f3c 2820a400
Loading
Loading
Loading
Loading
+73 −1
Original line number Diff line number Diff line
@@ -691,6 +691,71 @@ static int nfp_pf_find_rtsyms(struct nfp_pf *pf)
	return 0;
}

int nfp_net_pf_get_app_id(struct nfp_pf *pf)
{
	return nfp_pf_rtsym_read_optional(pf, "_pf%u_net_app_id",
					  NFP_APP_CORE_NIC);
}

static u64 nfp_net_pf_get_app_cap(struct nfp_pf *pf)
{
	char name[32];
	int err = 0;
	u64 val;

	snprintf(name, sizeof(name), "_pf%u_net_app_cap", nfp_cppcore_pcie_unit(pf->cpp));

	val = nfp_rtsym_read_le(pf->rtbl, name, &err);
	if (err) {
		if (err != -ENOENT)
			nfp_err(pf->cpp, "Unable to read symbol %s\n", name);

		return 0;
	}

	return val;
}

static int nfp_pf_cfg_hwinfo(struct nfp_pf *pf, bool sp_indiff)
{
	struct nfp_nsp *nsp;
	char hwinfo[32];
	int err;

	nsp = nfp_nsp_open(pf->cpp);
	if (IS_ERR(nsp))
		return PTR_ERR(nsp);

	snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff);
	err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
	/* Not a fatal error, no need to return error to stop driver from loading */
	if (err) {
		nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err);
	} else {
		/* Need reinit eth_tbl since the eth table state may change
		 * after sp_indiff is configured.
		 */
		kfree(pf->eth_tbl);
		pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
	}

	nfp_nsp_close(nsp);
	return 0;
}

static int nfp_pf_nsp_cfg(struct nfp_pf *pf)
{
	bool sp_indiff = (nfp_net_pf_get_app_id(pf) == NFP_APP_FLOWER_NIC) ||
			 (nfp_net_pf_get_app_cap(pf) & NFP_NET_APP_CAP_SP_INDIFF);

	return nfp_pf_cfg_hwinfo(pf, sp_indiff);
}

static void nfp_pf_nsp_clean(struct nfp_pf *pf)
{
	nfp_pf_cfg_hwinfo(pf, false);
}

static int nfp_pci_probe(struct pci_dev *pdev,
			 const struct pci_device_id *pci_id)
{
@@ -791,10 +856,14 @@ static int nfp_pci_probe(struct pci_dev *pdev,
		goto err_fw_unload;
	}

	err = nfp_net_pci_probe(pf);
	err = nfp_pf_nsp_cfg(pf);
	if (err)
		goto err_fw_unload;

	err = nfp_net_pci_probe(pf);
	if (err)
		goto err_nsp_clean;

	err = nfp_hwmon_register(pf);
	if (err) {
		dev_err(&pdev->dev, "Failed to register hwmon info\n");
@@ -805,6 +874,8 @@ static int nfp_pci_probe(struct pci_dev *pdev,

err_net_remove:
	nfp_net_pci_remove(pf);
err_nsp_clean:
	nfp_pf_nsp_clean(pf);
err_fw_unload:
	kfree(pf->rtbl);
	nfp_mip_close(pf->mip);
@@ -844,6 +915,7 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw)

	nfp_net_pci_remove(pf);

	nfp_pf_nsp_clean(pf);
	vfree(pf->dumpspec);
	kfree(pf->rtbl);
	nfp_mip_close(pf->mip);
+1 −2
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ struct nfp_dumpspec {
 * @num_vfs:		Number of SR-IOV VFs enabled
 * @fw_loaded:		Is the firmware loaded?
 * @unload_fw_on_remove:Do we need to unload firmware on driver removal?
 * @sp_indiff:		Is the firmware indifferent to physical port speed?
 * @ctrl_vnic:		Pointer to the control vNIC if available
 * @mip:		MIP handle
 * @rtbl:		RTsym table
@@ -115,7 +114,6 @@ struct nfp_pf {

	bool fw_loaded;
	bool unload_fw_on_remove;
	bool sp_indiff;

	struct nfp_net *ctrl_vnic;

@@ -163,6 +161,7 @@ bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);

int nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
			       unsigned int default_val);
int nfp_net_pf_get_app_id(struct nfp_pf *pf);
u8 __iomem *
nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
		 unsigned int min_size, struct nfp_cpp_area **area);
+0 −8
Original line number Diff line number Diff line
@@ -148,14 +148,6 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
							  true))
				return -EINVAL;
			break;
		case NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
			if (length) {
				dev_err(dev, "Unexpected len of SP_INDIFF TLV:%u\n", length);
				return -EINVAL;
			}

			caps->sp_indiff = true;
			break;
		default:
			if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
				break;
+3 −7
Original line number Diff line number Diff line
@@ -14,6 +14,9 @@

#include <linux/types.h>

/* 64-bit per app capabilities */
#define NFP_NET_APP_CAP_SP_INDIFF	BIT_ULL(0) /* indifferent to port speed */

/* Configuration BAR size.
 *
 * The configuration BAR is 8K in size, but due to
@@ -492,10 +495,6 @@
 * %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN:
 * Same as %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS, but crypto TLS does stream scan
 * RX sync, rather than kernel-assisted sync.
 *
 * %NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
 * Empty, indicate the firmware is indifferent to port speed. Then no need to
 * reload driver and firmware when port speed is changed.
 */
#define NFP_NET_CFG_TLV_TYPE_UNKNOWN		0
#define NFP_NET_CFG_TLV_TYPE_RESERVED		1
@@ -509,7 +508,6 @@
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS		11 /* see crypto/fw.h */
#define NFP_NET_CFG_TLV_TYPE_VNIC_STATS		12
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN	13
#define NFP_NET_CFG_TLV_TYPE_SP_INDIFF		14

struct device;

@@ -524,7 +522,6 @@ struct device;
 * @vnic_stats_off:	offset of vNIC stats area
 * @vnic_stats_cnt:	number of vNIC stats
 * @tls_resync_ss:	TLS resync will be performed via stream scan
 * @sp_indiff:		Firmware is indifferent to port speed
 */
struct nfp_net_tlv_caps {
	u32 me_freq_mhz;
@@ -537,7 +534,6 @@ struct nfp_net_tlv_caps {
	unsigned int vnic_stats_off;
	unsigned int vnic_stats_cnt;
	unsigned int tls_resync_ss:1;
	unsigned int sp_indiff:1;
};

int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
+56 −5
Original line number Diff line number Diff line
@@ -228,6 +228,37 @@ nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
	nfp_get_drvinfo(nn->app, nn->pdev, vnic_version, drvinfo);
}

static int
nfp_net_nway_reset(struct net_device *netdev)
{
	struct nfp_eth_table_port *eth_port;
	struct nfp_port *port;
	int err;

	port = nfp_port_from_netdev(netdev);
	eth_port = nfp_port_get_eth_port(port);
	if (!eth_port)
		return -EOPNOTSUPP;

	if (!netif_running(netdev))
		return 0;

	err = nfp_eth_set_configured(port->app->cpp, eth_port->index, false);
	if (err) {
		netdev_info(netdev, "Link down failed: %d\n", err);
		return err;
	}

	err = nfp_eth_set_configured(port->app->cpp, eth_port->index, true);
	if (err) {
		netdev_info(netdev, "Link up failed: %d\n", err);
		return err;
	}

	netdev_info(netdev, "Link reset succeeded\n");
	return 0;
}

static void
nfp_app_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
@@ -290,8 +321,13 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
	if (eth_port) {
		ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
		cmd->base.autoneg = eth_port->aneg != NFP_ANEG_DISABLED ?
			AUTONEG_ENABLE : AUTONEG_DISABLE;
		if (eth_port->supp_aneg) {
			ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
			if (eth_port->aneg == NFP_ANEG_AUTO) {
				ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
				cmd->base.autoneg = AUTONEG_ENABLE;
			}
		}
		nfp_net_set_fec_link_mode(eth_port, cmd);
	}

@@ -327,6 +363,7 @@ static int
nfp_net_set_link_ksettings(struct net_device *netdev,
			   const struct ethtool_link_ksettings *cmd)
{
	bool req_aneg = (cmd->base.autoneg == AUTONEG_ENABLE);
	struct nfp_eth_table_port *eth_port;
	struct nfp_port *port;
	struct nfp_nsp *nsp;
@@ -346,13 +383,25 @@ nfp_net_set_link_ksettings(struct net_device *netdev,
	if (IS_ERR(nsp))
		return PTR_ERR(nsp);

	err = __nfp_eth_set_aneg(nsp, cmd->base.autoneg == AUTONEG_ENABLE ?
				 NFP_ANEG_AUTO : NFP_ANEG_DISABLED);
	if (req_aneg && !eth_port->supp_aneg) {
		netdev_warn(netdev, "Autoneg is not supported.\n");
		err = -EOPNOTSUPP;
		goto err_bad_set;
	}

	err = __nfp_eth_set_aneg(nsp, req_aneg ? NFP_ANEG_AUTO : NFP_ANEG_DISABLED);
	if (err)
		goto err_bad_set;

	if (cmd->base.speed != SPEED_UNKNOWN) {
		u32 speed = cmd->base.speed / eth_port->lanes;

		if (req_aneg) {
			netdev_err(netdev, "Speed changing is not allowed when working on autoneg mode.\n");
			err = -EINVAL;
			goto err_bad_set;
		}

		err = __nfp_eth_set_speed(nsp, speed);
		if (err)
			goto err_bad_set;
@@ -996,7 +1045,7 @@ nfp_port_get_fecparam(struct net_device *netdev,
		return 0;

	param->fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec_modes_supported);
	param->active_fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec);
	param->active_fec = nfp_port_fec_nsp_to_ethtool(BIT(eth_port->act_fec));

	return 0;
}
@@ -1823,6 +1872,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
				     ETHTOOL_COALESCE_MAX_FRAMES |
				     ETHTOOL_COALESCE_USE_ADAPTIVE,
	.get_drvinfo		= nfp_net_get_drvinfo,
	.nway_reset             = nfp_net_nway_reset,
	.get_link		= ethtool_op_get_link,
	.get_ringparam		= nfp_net_get_ringparam,
	.set_ringparam		= nfp_net_set_ringparam,
@@ -1860,6 +1910,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {

const struct ethtool_ops nfp_port_ethtool_ops = {
	.get_drvinfo		= nfp_app_get_drvinfo,
	.nway_reset             = nfp_net_nway_reset,
	.get_link		= ethtool_op_get_link,
	.get_strings		= nfp_port_get_strings,
	.get_ethtool_stats	= nfp_port_get_stats,
Loading