Commit d287532e authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'nfp-port-speed-and-eeprom-get-set-updates'

Simon Horman says:

====================
nfp: port speed and eeprom get/set updates

this short series is the initial updates for the NFP driver for the v6.1
Kernel. It covers two enhancements:

1. Patches 1/3 and 2/3:
   - Support cases where application firmware does not know port speeds
     a priori by relaying this information from the management firmware
     to the application firmware.

     This allows the existing mechanism, whereby the driver reports port
     speeds to user-space as provided by the application firmware, to work
     in this case.

2. Patch 2/3:
   - Add support for eeprom get and set command
====================

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


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents f97e971d e6686745
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ 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
@@ -114,6 +115,7 @@ struct nfp_pf {

	bool fw_loaded;
	bool unload_fw_on_remove;
	bool sp_indiff;

	struct nfp_net *ctrl_vnic;

@@ -190,4 +192,7 @@ int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,

int nfp_devlink_params_register(struct nfp_pf *pf);
void nfp_devlink_params_unregister(struct nfp_pf *pf);

unsigned int nfp_net_lr2speed(unsigned int linkrate);
unsigned int nfp_net_speed2lr(unsigned int speed);
#endif /* NFP_MAIN_H */
+6 −3
Original line number Diff line number Diff line
@@ -474,19 +474,22 @@ static void nfp_net_read_link_status(struct nfp_net *nn)
{
	unsigned long flags;
	bool link_up;
	u32 sts;
	u16 sts;

	spin_lock_irqsave(&nn->link_status_lock, flags);

	sts = nn_readl(nn, NFP_NET_CFG_STS);
	sts = nn_readw(nn, NFP_NET_CFG_STS);
	link_up = !!(sts & NFP_NET_CFG_STS_LINK);

	if (nn->link_up == link_up)
		goto out;

	nn->link_up = link_up;
	if (nn->port)
	if (nn->port) {
		set_bit(NFP_PORT_CHANGED, &nn->port->flags);
		if (nn->port->link_cb)
			nn->port->link_cb(nn->port);
	}

	if (nn->link_up) {
		netif_carrier_on(nn->dp.netdev);
+8 −0
Original line number Diff line number Diff line
@@ -148,6 +148,14 @@ 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;
+11 −0
Original line number Diff line number Diff line
@@ -193,6 +193,10 @@
#define   NFP_NET_CFG_STS_LINK_RATE_40G		  5
#define   NFP_NET_CFG_STS_LINK_RATE_50G		  6
#define   NFP_NET_CFG_STS_LINK_RATE_100G	  7
/* NSP Link rate is a 16-bit word. It's determined by NSP and
 * written to CFG BAR by NFP driver.
 */
#define NFP_NET_CFG_STS_NSP_LINK_RATE	0x0036
#define NFP_NET_CFG_CAP			0x0038
#define NFP_NET_CFG_MAX_TXRINGS		0x003c
#define NFP_NET_CFG_MAX_RXRINGS		0x0040
@@ -488,6 +492,10 @@
 * %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
@@ -501,6 +509,7 @@
#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;

@@ -515,6 +524,7 @@ 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;
@@ -527,6 +537,7 @@ 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,
+166 −21
Original line number Diff line number Diff line
@@ -273,20 +273,11 @@ static int
nfp_net_get_link_ksettings(struct net_device *netdev,
			   struct ethtool_link_ksettings *cmd)
{
	static const u32 ls_to_ethtool[] = {
		[NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED]	= 0,
		[NFP_NET_CFG_STS_LINK_RATE_UNKNOWN]	= SPEED_UNKNOWN,
		[NFP_NET_CFG_STS_LINK_RATE_1G]		= SPEED_1000,
		[NFP_NET_CFG_STS_LINK_RATE_10G]		= SPEED_10000,
		[NFP_NET_CFG_STS_LINK_RATE_25G]		= SPEED_25000,
		[NFP_NET_CFG_STS_LINK_RATE_40G]		= SPEED_40000,
		[NFP_NET_CFG_STS_LINK_RATE_50G]		= SPEED_50000,
		[NFP_NET_CFG_STS_LINK_RATE_100G]	= SPEED_100000,
	};
	struct nfp_eth_table_port *eth_port;
	struct nfp_port *port;
	struct nfp_net *nn;
	u32 sts, ls;
	unsigned int speed;
	u16 sts;

	/* Init to unknowns */
	ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
@@ -319,18 +310,15 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
		return -EOPNOTSUPP;
	nn = netdev_priv(netdev);

	sts = nn_readl(nn, NFP_NET_CFG_STS);

	ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts);
	if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED)
	sts = nn_readw(nn, NFP_NET_CFG_STS);
	speed = nfp_net_lr2speed(FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts));
	if (!speed)
		return -EOPNOTSUPP;

	if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN ||
	    ls >= ARRAY_SIZE(ls_to_ethtool))
		return 0;

	cmd->base.speed = ls_to_ethtool[ls];
	if (speed != SPEED_UNKNOWN) {
		cmd->base.speed = speed;
		cmd->base.duplex = DUPLEX_FULL;
	}

	return 0;
}
@@ -1676,6 +1664,160 @@ static int nfp_net_set_phys_id(struct net_device *netdev,
	return err;
}

#define NFP_EEPROM_LEN ETH_ALEN

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

	port = nfp_port_from_netdev(netdev);
	eth_port = __nfp_port_get_eth_port(port);
	if (!eth_port)
		return 0;

	return NFP_EEPROM_LEN;
}

static int
nfp_net_get_nsp_hwindex(struct net_device *netdev,
			struct nfp_nsp **nspptr,
			u32 *index)
{
	struct nfp_eth_table_port *eth_port;
	struct nfp_port *port;
	struct nfp_nsp *nsp;
	int err;

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

	nsp = nfp_nsp_open(port->app->cpp);
	if (IS_ERR(nsp)) {
		err = PTR_ERR(nsp);
		netdev_err(netdev, "Failed to access the NSP: %d\n", err);
		return err;
	}

	if (!nfp_nsp_has_hwinfo_lookup(nsp)) {
		netdev_err(netdev, "NSP doesn't support PF MAC generation\n");
		nfp_nsp_close(nsp);
		return -EOPNOTSUPP;
	}

	*nspptr = nsp;
	*index = eth_port->eth_index;

	return 0;
}

static int
nfp_net_get_port_mac_by_hwinfo(struct net_device *netdev,
			       u8 *mac_addr)
{
	char hwinfo[32] = {};
	struct nfp_nsp *nsp;
	u32 index;
	int err;

	err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
	if (err)
		return err;

	snprintf(hwinfo, sizeof(hwinfo), "eth%u.mac", index);
	err = nfp_nsp_hwinfo_lookup(nsp, hwinfo, sizeof(hwinfo));
	nfp_nsp_close(nsp);
	if (err) {
		netdev_err(netdev, "Reading persistent MAC address failed: %d\n",
			   err);
		return -EOPNOTSUPP;
	}

	if (sscanf(hwinfo, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
		   &mac_addr[0], &mac_addr[1], &mac_addr[2],
		   &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
		netdev_err(netdev, "Can't parse persistent MAC address (%s)\n",
			   hwinfo);
		return -EOPNOTSUPP;
	}

	return 0;
}

static int
nfp_net_set_port_mac_by_hwinfo(struct net_device *netdev,
			       u8 *mac_addr)
{
	char hwinfo[32] = {};
	struct nfp_nsp *nsp;
	u32 index;
	int err;

	err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
	if (err)
		return err;

	snprintf(hwinfo, sizeof(hwinfo),
		 "eth%u.mac=%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
		 index, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
		 mac_addr[4], mac_addr[5]);

	err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
	nfp_nsp_close(nsp);
	if (err) {
		netdev_err(netdev, "HWinfo set failed: %d, hwinfo: %s\n",
			   err, hwinfo);
		return -EOPNOTSUPP;
	}

	return 0;
}

static int
nfp_net_get_eeprom(struct net_device *netdev,
		   struct ethtool_eeprom *eeprom, u8 *bytes)
{
	struct nfp_net *nn = netdev_priv(netdev);
	u8 buf[NFP_EEPROM_LEN] = {};

	if (eeprom->len == 0)
		return -EINVAL;

	if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
		return -EOPNOTSUPP;

	eeprom->magic = nn->pdev->vendor | (nn->pdev->device << 16);
	memcpy(bytes, buf + eeprom->offset, eeprom->len);

	return 0;
}

static int
nfp_net_set_eeprom(struct net_device *netdev,
		   struct ethtool_eeprom *eeprom, u8 *bytes)
{
	struct nfp_net *nn = netdev_priv(netdev);
	u8 buf[NFP_EEPROM_LEN] = {};

	if (eeprom->len == 0)
		return -EINVAL;

	if (eeprom->magic != (nn->pdev->vendor | nn->pdev->device << 16))
		return -EINVAL;

	if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
		return -EOPNOTSUPP;

	memcpy(buf + eeprom->offset, bytes, eeprom->len);
	if (nfp_net_set_port_mac_by_hwinfo(netdev, buf))
		return -EOPNOTSUPP;

	return 0;
}

static const struct ethtool_ops nfp_net_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
				     ETHTOOL_COALESCE_MAX_FRAMES |
@@ -1699,6 +1841,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
	.set_dump		= nfp_app_set_dump,
	.get_dump_flag		= nfp_app_get_dump_flag,
	.get_dump_data		= nfp_app_get_dump_data,
	.get_eeprom_len         = nfp_net_get_eeprom_len,
	.get_eeprom             = nfp_net_get_eeprom,
	.set_eeprom             = nfp_net_set_eeprom,
	.get_module_info	= nfp_port_get_module_info,
	.get_module_eeprom	= nfp_port_get_module_eeprom,
	.get_coalesce           = nfp_net_get_coalesce,
Loading