Commit 6fd5eeee authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'support-setting-lanes-via-ethtool'

Danielle Ratson says:

====================
Support setting lanes via ethtool

Some speeds can be achieved with different number of lanes. For example,
100Gbps can be achieved using two lanes of 50Gbps or four lanes of
25Gbps. This patchset adds a new selector that allows ethtool to
advertise link modes according to their number of lanes and also force a
specific number of lanes when autonegotiation is off.

Advertising all link modes with a speed of 100Gbps that use two lanes:

$ ethtool -s swp1 speed 100000 lanes 2 autoneg on

Forcing a speed of 100Gbps using four lanes:

$ ethtool -s swp1 speed 100000 lanes 4 autoneg off
====================

Link: https://lore.kernel.org/r/20210202180612.325099-1-danieller@nvidia.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 99b8202b f72e2f48
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -431,16 +431,17 @@ Request contents:
  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
  ``ETHTOOL_A_LINKMODES_LANES``               u32     lanes
  ==========================================  ======  ==========================

``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If
autonegotiation is on (either set now or kept from before), advertised modes
are not changed (no ``ETHTOOL_A_LINKMODES_OURS`` attribute) and at least one
of speed and duplex is specified, kernel adjusts advertised modes to all
supported modes matching speed, duplex or both (whatever is specified). This
autoselection is done on ethtool side with ioctl interface, netlink interface
is supposed to allow requesting changes without knowing what exactly kernel
supports.
of speed, duplex and lanes is specified, kernel adjusts advertised modes to all
supported modes matching speed, duplex, lanes or all (whatever is specified).
This autoselection is done on ethtool side with ioctl interface, netlink
interface is supposed to allow requesting changes without knowing what exactly
kernel supports.


LINKSTATE_GET
+7 −6
Original line number Diff line number Diff line
@@ -328,15 +328,16 @@ struct mlxsw_sp_port_type_speed_ops {
					 u32 ptys_eth_proto,
					 struct ethtool_link_ksettings *cmd);
	void (*from_ptys_link)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
			       u8 width, unsigned long *mode);
			       unsigned long *mode);
	u32 (*from_ptys_speed)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto);
	void (*from_ptys_speed_duplex)(struct mlxsw_sp *mlxsw_sp,
	void (*from_ptys_link_mode)(struct mlxsw_sp *mlxsw_sp,
				    bool carrier_ok, u32 ptys_eth_proto,
				    struct ethtool_link_ksettings *cmd);
	int (*ptys_max_speed)(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed);
	u32 (*to_ptys_advert_link)(struct mlxsw_sp *mlxsw_sp, u8 width,
	u32 (*to_ptys_advert_link)(struct mlxsw_sp *mlxsw_sp,
				   const struct ethtool_link_ksettings *cmd);
	u32 (*to_ptys_speed_lanes)(struct mlxsw_sp *mlxsw_sp, u8 width,
				   const struct ethtool_link_ksettings *cmd);
	u32 (*to_ptys_speed)(struct mlxsw_sp *mlxsw_sp, u8 width, u32 speed);
	void (*reg_ptys_eth_pack)(struct mlxsw_sp *mlxsw_sp, char *payload,
				  u8 local_port, u32 proto_admin, bool autoneg);
	void (*reg_ptys_eth_unpack)(struct mlxsw_sp *mlxsw_sp, char *payload,
+116 −80
Original line number Diff line number Diff line
@@ -858,7 +858,7 @@ static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)

static void
mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap,
				 u8 width, struct ethtool_link_ksettings *cmd)
				 struct ethtool_link_ksettings *cmd)
{
	const struct mlxsw_sp_port_type_speed_ops *ops;

@@ -869,13 +869,13 @@ mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap,
	ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);

	ops->from_ptys_supported_port(mlxsw_sp, eth_proto_cap, cmd);
	ops->from_ptys_link(mlxsw_sp, eth_proto_cap, width,
	ops->from_ptys_link(mlxsw_sp, eth_proto_cap,
			    cmd->link_modes.supported);
}

static void
mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
				 u32 eth_proto_admin, bool autoneg, u8 width,
				 u32 eth_proto_admin, bool autoneg,
				 struct ethtool_link_ksettings *cmd)
{
	const struct mlxsw_sp_port_type_speed_ops *ops;
@@ -886,7 +886,7 @@ mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
		return;

	ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
	ops->from_ptys_link(mlxsw_sp, eth_proto_admin, width,
	ops->from_ptys_link(mlxsw_sp, eth_proto_admin,
			    cmd->link_modes.advertising);
}

@@ -960,15 +960,13 @@ static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
	ops = mlxsw_sp->port_type_speed_ops;
	autoneg = mlxsw_sp_port->link.autoneg;

	mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap,
					 mlxsw_sp_port->mapping.width, cmd);
	mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap, cmd);

	mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg,
					 mlxsw_sp_port->mapping.width, cmd);
	mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg, cmd);

	cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
	cmd->base.port = mlxsw_sp_port_connector_port(connector_type);
	ops->from_ptys_speed_duplex(mlxsw_sp, netif_carrier_ok(dev),
	ops->from_ptys_link_mode(mlxsw_sp, netif_carrier_ok(dev),
				 eth_proto_oper, cmd);

	return 0;
@@ -997,14 +995,13 @@ mlxsw_sp_port_set_link_ksettings(struct net_device *dev,

	autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
	eth_proto_new = autoneg ?
		ops->to_ptys_advert_link(mlxsw_sp, mlxsw_sp_port->mapping.width,
					 cmd) :
		ops->to_ptys_speed(mlxsw_sp, mlxsw_sp_port->mapping.width,
				   cmd->base.speed);
		ops->to_ptys_advert_link(mlxsw_sp, cmd) :
		ops->to_ptys_speed_lanes(mlxsw_sp, mlxsw_sp_port->mapping.width,
					 cmd);

	eth_proto_new = eth_proto_new & eth_proto_cap;
	if (!eth_proto_new) {
		netdev_err(dev, "No supported speed requested\n");
		netdev_err(dev, "No supported speed or lanes requested\n");
		return -EINVAL;
	}

@@ -1063,6 +1060,7 @@ mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info)
}

const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
	.cap_link_lanes_supported	= true,
	.get_drvinfo			= mlxsw_sp_port_get_drvinfo,
	.get_link			= ethtool_op_get_link,
	.get_link_ext_state		= mlxsw_sp_port_get_link_ext_state,
@@ -1198,7 +1196,7 @@ mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,

static void
mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
			 u8 width, unsigned long *mode)
			 unsigned long *mode)
{
	int i;

@@ -1223,19 +1221,21 @@ mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
}

static void
mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
mlxsw_sp1_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
			      u32 ptys_eth_proto,
			      struct ethtool_link_ksettings *cmd)
{
	cmd->base.speed = SPEED_UNKNOWN;
	cmd->base.duplex = DUPLEX_UNKNOWN;
	int i;

	cmd->link_mode = -1;

	if (!carrier_ok)
		return;

	cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
	if (cmd->base.speed != SPEED_UNKNOWN)
		cmd->base.duplex = DUPLEX_FULL;
	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
		if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
			cmd->link_mode = mlxsw_sp1_port_link_mode[i].mask_ethtool;
	}
}

static int mlxsw_sp1_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed)
@@ -1260,7 +1260,7 @@ static int mlxsw_sp1_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_
}

static u32
mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp,
			      const struct ethtool_link_ksettings *cmd)
{
	u32 ptys_proto = 0;
@@ -1274,14 +1274,17 @@ mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
	return ptys_proto;
}

static u32 mlxsw_sp1_to_ptys_speed(struct mlxsw_sp *mlxsw_sp, u8 width,
				   u32 speed)
static u32 mlxsw_sp1_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width,
					 const struct ethtool_link_ksettings *cmd)
{
	u32 ptys_proto = 0;
	int i;

	if (cmd->lanes > width)
		return ptys_proto;

	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
		if (speed == mlxsw_sp1_port_link_mode[i].speed)
		if (cmd->base.speed == mlxsw_sp1_port_link_mode[i].speed)
			ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
	}
	return ptys_proto;
@@ -1321,10 +1324,10 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = {
	.from_ptys_supported_port	= mlxsw_sp1_from_ptys_supported_port,
	.from_ptys_link			= mlxsw_sp1_from_ptys_link,
	.from_ptys_speed		= mlxsw_sp1_from_ptys_speed,
	.from_ptys_speed_duplex		= mlxsw_sp1_from_ptys_speed_duplex,
	.from_ptys_link_mode		= mlxsw_sp1_from_ptys_link_mode,
	.ptys_max_speed			= mlxsw_sp1_ptys_max_speed,
	.to_ptys_advert_link		= mlxsw_sp1_to_ptys_advert_link,
	.to_ptys_speed			= mlxsw_sp1_to_ptys_speed,
	.to_ptys_speed_lanes		= mlxsw_sp1_to_ptys_speed_lanes,
	.reg_ptys_eth_pack		= mlxsw_sp1_reg_ptys_eth_pack,
	.reg_ptys_eth_unpack		= mlxsw_sp1_reg_ptys_eth_unpack,
	.ptys_proto_cap_masked_get	= mlxsw_sp1_ptys_proto_cap_masked_get,
@@ -1486,7 +1489,8 @@ struct mlxsw_sp2_port_link_mode {
	int m_ethtool_len;
	u32 mask;
	u32 speed;
	u8 mask_width;
	u32 width;
	u8 mask_sup_width;
};

static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = {
@@ -1494,105 +1498,117 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = {
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_SGMII_100M,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_sgmii_100m,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
				  MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_100,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_1000BASE_X_SGMII,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_1000base_x_sgmii,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
				  MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_1000,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_5GBASE_R,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_5gbase_r,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
				  MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_5000,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_XFI_XAUI_1_10G,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
				  MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_10000,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_XLAUI_4_XLPPI_4_40G,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_40000,
		.width		= 4,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_25GAUI_1_25GBASE_CR_KR,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
				  MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_25000,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_2_LAUI_2_50GBASE_CR2_KR2,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_2X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_2X |
				  MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_50000,
		.width		= 2,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_1_LAUI_1_50GBASE_CR_KR,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X,
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_1X,
		.speed		= SPEED_50000,
		.width		= 1,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_CAUI_4_100GBASE_CR4_KR4,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_100000,
		.width		= 4,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_2X,
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_2X,
		.speed		= SPEED_100000,
		.width		= 2,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_200GAUI_4_200GBASE_CR4_KR4,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
				  MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_200000,
		.width		= 4,
	},
	{
		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_400GAUI_8,
		.mask_ethtool	= mlxsw_sp2_mask_ethtool_400gaui_8,
		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN,
		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_8X,
		.mask_sup_width	= MLXSW_SP_PORT_MASK_WIDTH_8X,
		.speed		= SPEED_400000,
		.width		= 8,
	},
};

@@ -1619,14 +1635,12 @@ mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,

static void
mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
			 u8 width, unsigned long *mode)
			 unsigned long *mode)
{
	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
	int i;

	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
		if ((ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) &&
		    (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
		if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
			mlxsw_sp2_set_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
						  mode);
	}
@@ -1646,19 +1660,24 @@ mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
}

static void
mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
mlxsw_sp2_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
			      u32 ptys_eth_proto,
			      struct ethtool_link_ksettings *cmd)
{
	cmd->base.speed = SPEED_UNKNOWN;
	cmd->base.duplex = DUPLEX_UNKNOWN;
	struct mlxsw_sp2_port_link_mode link;
	int i;

	cmd->link_mode = -1;

	if (!carrier_ok)
		return;

	cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
	if (cmd->base.speed != SPEED_UNKNOWN)
		cmd->base.duplex = DUPLEX_FULL;
	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
		if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) {
			link = mlxsw_sp2_port_link_mode[i];
			cmd->link_mode = link.mask_ethtool[1];
		}
	}
}

static int mlxsw_sp2_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed)
@@ -1698,33 +1717,50 @@ mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
}

static u32
mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp,
			      const struct ethtool_link_ksettings *cmd)
{
	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
	u32 ptys_proto = 0;
	int i;

	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
		if ((mask_width & mlxsw_sp2_port_link_mode[i].mask_width) &&
		    mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
		if (mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
					       cmd->link_modes.advertising))
			ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
	}
	return ptys_proto;
}

static u32 mlxsw_sp2_to_ptys_speed(struct mlxsw_sp *mlxsw_sp,
				   u8 width, u32 speed)
static u32 mlxsw_sp2_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width,
					 const struct ethtool_link_ksettings *cmd)
{
	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
	struct mlxsw_sp2_port_link_mode link_mode;
	u32 ptys_proto = 0;
	int i;

	if (cmd->lanes > width)
		return ptys_proto;

	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
		if ((speed == mlxsw_sp2_port_link_mode[i].speed) &&
		    (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
			ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
		if (cmd->base.speed == mlxsw_sp2_port_link_mode[i].speed) {
			link_mode = mlxsw_sp2_port_link_mode[i];

			if (!cmd->lanes) {
				/* If number of lanes was not set by user space,
				 * choose the link mode that supports the width
				 * of the port.
				 */
				if (mask_width & link_mode.mask_sup_width)
					ptys_proto |= link_mode.mask;
			} else if (cmd->lanes == link_mode.width) {
				/* Else if the number of lanes was set, choose
				 * the link mode that its actual width equals to
				 * it.
				 */
				ptys_proto |= link_mode.mask;
			}
		}
	}
	return ptys_proto;
}
@@ -1764,10 +1800,10 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = {
	.from_ptys_supported_port	= mlxsw_sp2_from_ptys_supported_port,
	.from_ptys_link			= mlxsw_sp2_from_ptys_link,
	.from_ptys_speed		= mlxsw_sp2_from_ptys_speed,
	.from_ptys_speed_duplex		= mlxsw_sp2_from_ptys_speed_duplex,
	.from_ptys_link_mode		= mlxsw_sp2_from_ptys_link_mode,
	.ptys_max_speed			= mlxsw_sp2_ptys_max_speed,
	.to_ptys_advert_link		= mlxsw_sp2_to_ptys_advert_link,
	.to_ptys_speed			= mlxsw_sp2_to_ptys_speed,
	.to_ptys_speed_lanes		= mlxsw_sp2_to_ptys_speed_lanes,
	.reg_ptys_eth_pack		= mlxsw_sp2_reg_ptys_eth_pack,
	.reg_ptys_eth_unpack		= mlxsw_sp2_reg_ptys_eth_unpack,
	.ptys_proto_cap_masked_get	= mlxsw_sp2_ptys_proto_cap_masked_get,
+5 −0
Original line number Diff line number Diff line
@@ -128,6 +128,8 @@ struct ethtool_link_ksettings {
		__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
		__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
	} link_modes;
	u32	lanes;
	enum ethtool_link_mode_bit_indices link_mode;
};

/**
@@ -265,6 +267,8 @@ struct ethtool_pause_stats {

/**
 * struct ethtool_ops - optional netdev operations
 * @cap_link_lanes_supported: indicates if the driver supports lanes
 *	parameter.
 * @supported_coalesce_params: supported types of interrupt coalescing.
 * @get_drvinfo: Report driver/device information.  Should only set the
 *	@driver, @version, @fw_version and @bus_info fields.  If not
@@ -420,6 +424,7 @@ struct ethtool_pause_stats {
 * of the generic netdev features interface.
 */
struct ethtool_ops {
	u32     cap_link_lanes_supported:1;
	u32	supported_coalesce_params;
	void	(*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
	int	(*get_regs_len)(struct net_device *);
+1 −0
Original line number Diff line number Diff line
@@ -227,6 +227,7 @@ enum {
	ETHTOOL_A_LINKMODES_DUPLEX,		/* u8 */
	ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,	/* u8 */
	ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE,	/* u8 */
	ETHTOOL_A_LINKMODES_LANES,		/* u32 */

	/* add new constants above here */
	__ETHTOOL_A_LINKMODES_CNT,
Loading