Commit 77baa37a authored by David S. Miller's avatar David S. Miller
Browse files


Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2022-08-24 (ice)

This series contains updates to ice driver only.

Marcin adds support for TC parsing on TTL and ToS fields.

Anatolli adds support for devlink port split command to allow
configuration of various port configurations.

Jake allows for passing and writing an additional NVM write activate
field by expanding current cmd_flag.

Ani makes PHY debug output more readable.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d73ffc08 f8c74ca6
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -139,6 +139,42 @@ EMP firmware image.
The driver does not currently support reloading the driver via
``DEVLINK_RELOAD_ACTION_DRIVER_REINIT``.

Port split
==========

The ``ice`` driver supports port splitting only for port 0, as the FW has
a predefined set of available port split options for the whole device.

A system reboot is required for port split to be applied.

The following command will select the port split option with 4 ports:

.. code:: shell

    $ devlink port split pci/0000:16:00.0/0 count 4

The list of all available port options will be printed to dynamic debug after
each ``split`` and ``unsplit`` command. The first option is the default.

.. code:: shell

    ice 0000:16:00.0: Available port split options and max port speeds (Gbps):
    ice 0000:16:00.0: Status  Split      Quad 0          Quad 1
    ice 0000:16:00.0:         count  L0  L1  L2  L3  L4  L5  L6  L7
    ice 0000:16:00.0: Active  2     100   -   -   - 100   -   -   -
    ice 0000:16:00.0:         2      50   -  50   -   -   -   -   -
    ice 0000:16:00.0: Pending 4      25  25  25  25   -   -   -   -
    ice 0000:16:00.0:         4      25  25   -   -  25  25   -   -
    ice 0000:16:00.0:         8      10  10  10  10  10  10  10  10
    ice 0000:16:00.0:         1     100   -   -   -   -   -   -   -

There could be multiple FW port options with the same port split count. When
the same port split count request is issued again, the next FW port option with
the same port split count will be selected.

``devlink port unsplit`` will select the option with a split count of 1. If
there is no FW option available with split count 1, you will receive an error.

Regions
=======

+60 −0
Original line number Diff line number Diff line
@@ -1423,6 +1423,56 @@ struct ice_aqc_set_port_id_led {
	u8 rsvd[13];
};

/* Get Port Options (indirect, 0x06EA) */
struct ice_aqc_get_port_options {
	u8 lport_num;
	u8 lport_num_valid;
	u8 port_options_count;
#define ICE_AQC_PORT_OPT_COUNT_M	GENMASK(3, 0)
#define ICE_AQC_PORT_OPT_MAX		16

	u8 innermost_phy_index;
	u8 port_options;
#define ICE_AQC_PORT_OPT_ACTIVE_M	GENMASK(3, 0)
#define ICE_AQC_PORT_OPT_VALID		BIT(7)

	u8 pending_port_option_status;
#define ICE_AQC_PENDING_PORT_OPT_IDX_M	GENMASK(3, 0)
#define ICE_AQC_PENDING_PORT_OPT_VALID	BIT(7)

	u8 rsvd[2];
	__le32 addr_high;
	__le32 addr_low;
};

struct ice_aqc_get_port_options_elem {
	u8 pmd;
#define ICE_AQC_PORT_OPT_PMD_COUNT_M	GENMASK(3, 0)

	u8 max_lane_speed;
#define ICE_AQC_PORT_OPT_MAX_LANE_M	GENMASK(3, 0)
#define ICE_AQC_PORT_OPT_MAX_LANE_100M	0
#define ICE_AQC_PORT_OPT_MAX_LANE_1G	1
#define ICE_AQC_PORT_OPT_MAX_LANE_2500M	2
#define ICE_AQC_PORT_OPT_MAX_LANE_5G	3
#define ICE_AQC_PORT_OPT_MAX_LANE_10G	4
#define ICE_AQC_PORT_OPT_MAX_LANE_25G	5
#define ICE_AQC_PORT_OPT_MAX_LANE_50G	6
#define ICE_AQC_PORT_OPT_MAX_LANE_100G	7

	u8 global_scid[2];
	u8 phy_scid[2];
	u8 pf2port_cid[2];
};

/* Set Port Option (direct, 0x06EB) */
struct ice_aqc_set_port_option {
	u8 lport_num;
	u8 lport_num_valid;
	u8 selected_port_option;
	u8 rsvd[13];
};

/* Set/Get GPIO (direct, 0x06EC/0x06ED) */
struct ice_aqc_gpio {
	__le16 gpio_ctrl_handle;
@@ -1489,6 +1539,12 @@ struct ice_aqc_nvm {
#define ICE_AQC_NVM_PERST_FLAG		1
#define ICE_AQC_NVM_EMPR_FLAG		2
#define ICE_AQC_NVM_EMPR_ENA		BIT(0) /* Write Activate reply only */
	/* For Write Activate, several flags are sent as part of a separate
	 * flags2 field using a separate byte. For simplicity of the software
	 * interface, we pass the flags as a 16 bit value so these flags are
	 * all offset by 8 bits
	 */
#define ICE_AQC_NVM_ACTIV_REQ_EMPR	BIT(8) /* NVM Write Activate only */
	__le16 module_typeid;
	__le16 length;
#define ICE_AQC_NVM_ERASE_LEN	0xFFFF
@@ -2082,6 +2138,8 @@ struct ice_aq_desc {
		struct ice_aqc_gpio read_write_gpio;
		struct ice_aqc_sff_eeprom read_write_sff_param;
		struct ice_aqc_set_port_id_led set_port_id_led;
		struct ice_aqc_get_port_options get_port_options;
		struct ice_aqc_set_port_option set_port_option;
		struct ice_aqc_get_sw_cfg get_sw_conf;
		struct ice_aqc_set_port_params set_port_params;
		struct ice_aqc_sw_rules sw_rules;
@@ -2243,6 +2301,8 @@ enum ice_adminq_opc {
	ice_aqc_opc_read_i2c				= 0x06E2,
	ice_aqc_opc_write_i2c				= 0x06E3,
	ice_aqc_opc_set_port_id_led			= 0x06E9,
	ice_aqc_opc_get_port_options			= 0x06EA,
	ice_aqc_opc_set_port_option			= 0x06EB,
	ice_aqc_opc_set_gpio				= 0x06EC,
	ice_aqc_opc_get_gpio				= 0x06ED,
	ice_aqc_opc_sff_eeprom				= 0x06EE,
+255 −18
Original line number Diff line number Diff line
@@ -8,6 +8,108 @@

#define ICE_PF_RESET_WAIT_COUNT	300

static const char * const ice_link_mode_str_low[] = {
	[0] = "100BASE_TX",
	[1] = "100M_SGMII",
	[2] = "1000BASE_T",
	[3] = "1000BASE_SX",
	[4] = "1000BASE_LX",
	[5] = "1000BASE_KX",
	[6] = "1G_SGMII",
	[7] = "2500BASE_T",
	[8] = "2500BASE_X",
	[9] = "2500BASE_KX",
	[10] = "5GBASE_T",
	[11] = "5GBASE_KR",
	[12] = "10GBASE_T",
	[13] = "10G_SFI_DA",
	[14] = "10GBASE_SR",
	[15] = "10GBASE_LR",
	[16] = "10GBASE_KR_CR1",
	[17] = "10G_SFI_AOC_ACC",
	[18] = "10G_SFI_C2C",
	[19] = "25GBASE_T",
	[20] = "25GBASE_CR",
	[21] = "25GBASE_CR_S",
	[22] = "25GBASE_CR1",
	[23] = "25GBASE_SR",
	[24] = "25GBASE_LR",
	[25] = "25GBASE_KR",
	[26] = "25GBASE_KR_S",
	[27] = "25GBASE_KR1",
	[28] = "25G_AUI_AOC_ACC",
	[29] = "25G_AUI_C2C",
	[30] = "40GBASE_CR4",
	[31] = "40GBASE_SR4",
	[32] = "40GBASE_LR4",
	[33] = "40GBASE_KR4",
	[34] = "40G_XLAUI_AOC_ACC",
	[35] = "40G_XLAUI",
	[36] = "50GBASE_CR2",
	[37] = "50GBASE_SR2",
	[38] = "50GBASE_LR2",
	[39] = "50GBASE_KR2",
	[40] = "50G_LAUI2_AOC_ACC",
	[41] = "50G_LAUI2",
	[42] = "50G_AUI2_AOC_ACC",
	[43] = "50G_AUI2",
	[44] = "50GBASE_CP",
	[45] = "50GBASE_SR",
	[46] = "50GBASE_FR",
	[47] = "50GBASE_LR",
	[48] = "50GBASE_KR_PAM4",
	[49] = "50G_AUI1_AOC_ACC",
	[50] = "50G_AUI1",
	[51] = "100GBASE_CR4",
	[52] = "100GBASE_SR4",
	[53] = "100GBASE_LR4",
	[54] = "100GBASE_KR4",
	[55] = "100G_CAUI4_AOC_ACC",
	[56] = "100G_CAUI4",
	[57] = "100G_AUI4_AOC_ACC",
	[58] = "100G_AUI4",
	[59] = "100GBASE_CR_PAM4",
	[60] = "100GBASE_KR_PAM4",
	[61] = "100GBASE_CP2",
	[62] = "100GBASE_SR2",
	[63] = "100GBASE_DR",
};

static const char * const ice_link_mode_str_high[] = {
	[0] = "100GBASE_KR2_PAM4",
	[1] = "100G_CAUI2_AOC_ACC",
	[2] = "100G_CAUI2",
	[3] = "100G_AUI2_AOC_ACC",
	[4] = "100G_AUI2",
};

/**
 * ice_dump_phy_type - helper function to dump phy_type
 * @hw: pointer to the HW structure
 * @low: 64 bit value for phy_type_low
 * @high: 64 bit value for phy_type_high
 * @prefix: prefix string to differentiate multiple dumps
 */
static void
ice_dump_phy_type(struct ice_hw *hw, u64 low, u64 high, const char *prefix)
{
	ice_debug(hw, ICE_DBG_PHY, "%s: phy_type_low: 0x%016llx\n", prefix, low);

	for (u32 i = 0; i < BITS_PER_TYPE(typeof(low)); i++) {
		if (low & BIT_ULL(i))
			ice_debug(hw, ICE_DBG_PHY, "%s:   bit(%d): %s\n",
				  prefix, i, ice_link_mode_str_low[i]);
	}

	ice_debug(hw, ICE_DBG_PHY, "%s: phy_type_high: 0x%016llx\n", prefix, high);

	for (u32 i = 0; i < BITS_PER_TYPE(typeof(high)); i++) {
		if (high & BIT_ULL(i))
			ice_debug(hw, ICE_DBG_PHY, "%s:   bit(%d): %s\n",
				  prefix, i, ice_link_mode_str_high[i]);
	}
}

/**
 * ice_set_mac_type - Sets MAC type
 * @hw: pointer to the HW structure
@@ -183,6 +285,7 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
	struct ice_aqc_get_phy_caps *cmd;
	u16 pcaps_size = sizeof(*pcaps);
	struct ice_aq_desc desc;
	const char *prefix;
	struct ice_hw *hw;
	int status;

@@ -204,29 +307,48 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
	cmd->param0 |= cpu_to_le16(report_mode);
	status = ice_aq_send_cmd(hw, &desc, pcaps, pcaps_size, cd);

	ice_debug(hw, ICE_DBG_LINK, "get phy caps - report_mode = 0x%x\n",
		  report_mode);
	ice_debug(hw, ICE_DBG_LINK, "	phy_type_low = 0x%llx\n",
		  (unsigned long long)le64_to_cpu(pcaps->phy_type_low));
	ice_debug(hw, ICE_DBG_LINK, "	phy_type_high = 0x%llx\n",
		  (unsigned long long)le64_to_cpu(pcaps->phy_type_high));
	ice_debug(hw, ICE_DBG_LINK, "	caps = 0x%x\n", pcaps->caps);
	ice_debug(hw, ICE_DBG_LINK, "	low_power_ctrl_an = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "get phy caps dump\n");

	switch (report_mode) {
	case ICE_AQC_REPORT_TOPO_CAP_MEDIA:
		prefix = "phy_caps_media";
		break;
	case ICE_AQC_REPORT_TOPO_CAP_NO_MEDIA:
		prefix = "phy_caps_no_media";
		break;
	case ICE_AQC_REPORT_ACTIVE_CFG:
		prefix = "phy_caps_active";
		break;
	case ICE_AQC_REPORT_DFLT_CFG:
		prefix = "phy_caps_default";
		break;
	default:
		prefix = "phy_caps_invalid";
	}

	ice_dump_phy_type(hw, le64_to_cpu(pcaps->phy_type_low),
			  le64_to_cpu(pcaps->phy_type_high), prefix);

	ice_debug(hw, ICE_DBG_LINK, "%s: report_mode = 0x%x\n",
		  prefix, report_mode);
	ice_debug(hw, ICE_DBG_LINK, "%s: caps = 0x%x\n", prefix, pcaps->caps);
	ice_debug(hw, ICE_DBG_LINK, "%s: low_power_ctrl_an = 0x%x\n", prefix,
		  pcaps->low_power_ctrl_an);
	ice_debug(hw, ICE_DBG_LINK, "	eee_cap = 0x%x\n", pcaps->eee_cap);
	ice_debug(hw, ICE_DBG_LINK, "	eeer_value = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "%s: eee_cap = 0x%x\n", prefix,
		  pcaps->eee_cap);
	ice_debug(hw, ICE_DBG_LINK, "%s: eeer_value = 0x%x\n", prefix,
		  pcaps->eeer_value);
	ice_debug(hw, ICE_DBG_LINK, "	link_fec_options = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "%s: link_fec_options = 0x%x\n", prefix,
		  pcaps->link_fec_options);
	ice_debug(hw, ICE_DBG_LINK, "	module_compliance_enforcement = 0x%x\n",
		  pcaps->module_compliance_enforcement);
	ice_debug(hw, ICE_DBG_LINK, "   extended_compliance_code = 0x%x\n",
		  pcaps->extended_compliance_code);
	ice_debug(hw, ICE_DBG_LINK, "   module_type[0] = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "%s: module_compliance_enforcement = 0x%x\n",
		  prefix, pcaps->module_compliance_enforcement);
	ice_debug(hw, ICE_DBG_LINK, "%s: extended_compliance_code = 0x%x\n",
		  prefix, pcaps->extended_compliance_code);
	ice_debug(hw, ICE_DBG_LINK, "%s: module_type[0] = 0x%x\n", prefix,
		  pcaps->module_type[0]);
	ice_debug(hw, ICE_DBG_LINK, "   module_type[1] = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "%s: module_type[1] = 0x%x\n", prefix,
		  pcaps->module_type[1]);
	ice_debug(hw, ICE_DBG_LINK, "   module_type[2] = 0x%x\n",
	ice_debug(hw, ICE_DBG_LINK, "%s: module_type[2] = 0x%x\n", prefix,
		  pcaps->module_type[2]);

	if (!status && report_mode == ICE_AQC_REPORT_TOPO_CAP_MEDIA) {
@@ -3554,6 +3676,121 @@ ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode,
	return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
}

/**
 * ice_aq_get_port_options
 * @hw: pointer to the HW struct
 * @options: buffer for the resultant port options
 * @option_count: input - size of the buffer in port options structures,
 *                output - number of returned port options
 * @lport: logical port to call the command with (optional)
 * @lport_valid: when false, FW uses port owned by the PF instead of lport,
 *               when PF owns more than 1 port it must be true
 * @active_option_idx: index of active port option in returned buffer
 * @active_option_valid: active option in returned buffer is valid
 * @pending_option_idx: index of pending port option in returned buffer
 * @pending_option_valid: pending option in returned buffer is valid
 *
 * Calls Get Port Options AQC (0x06ea) and verifies result.
 */
int
ice_aq_get_port_options(struct ice_hw *hw,
			struct ice_aqc_get_port_options_elem *options,
			u8 *option_count, u8 lport, bool lport_valid,
			u8 *active_option_idx, bool *active_option_valid,
			u8 *pending_option_idx, bool *pending_option_valid)
{
	struct ice_aqc_get_port_options *cmd;
	struct ice_aq_desc desc;
	int status;
	u8 i;

	/* options buffer shall be able to hold max returned options */
	if (*option_count < ICE_AQC_PORT_OPT_COUNT_M)
		return -EINVAL;

	cmd = &desc.params.get_port_options;
	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_port_options);

	if (lport_valid)
		cmd->lport_num = lport;
	cmd->lport_num_valid = lport_valid;

	status = ice_aq_send_cmd(hw, &desc, options,
				 *option_count * sizeof(*options), NULL);
	if (status)
		return status;

	/* verify direct FW response & set output parameters */
	*option_count = FIELD_GET(ICE_AQC_PORT_OPT_COUNT_M,
				  cmd->port_options_count);
	ice_debug(hw, ICE_DBG_PHY, "options: %x\n", *option_count);
	*active_option_valid = FIELD_GET(ICE_AQC_PORT_OPT_VALID,
					 cmd->port_options);
	if (*active_option_valid) {
		*active_option_idx = FIELD_GET(ICE_AQC_PORT_OPT_ACTIVE_M,
					       cmd->port_options);
		if (*active_option_idx > (*option_count - 1))
			return -EIO;
		ice_debug(hw, ICE_DBG_PHY, "active idx: %x\n",
			  *active_option_idx);
	}

	*pending_option_valid = FIELD_GET(ICE_AQC_PENDING_PORT_OPT_VALID,
					  cmd->pending_port_option_status);
	if (*pending_option_valid) {
		*pending_option_idx = FIELD_GET(ICE_AQC_PENDING_PORT_OPT_IDX_M,
						cmd->pending_port_option_status);
		if (*pending_option_idx > (*option_count - 1))
			return -EIO;
		ice_debug(hw, ICE_DBG_PHY, "pending idx: %x\n",
			  *pending_option_idx);
	}

	/* mask output options fields */
	for (i = 0; i < *option_count; i++) {
		options[i].pmd = FIELD_GET(ICE_AQC_PORT_OPT_PMD_COUNT_M,
					   options[i].pmd);
		options[i].max_lane_speed = FIELD_GET(ICE_AQC_PORT_OPT_MAX_LANE_M,
						      options[i].max_lane_speed);
		ice_debug(hw, ICE_DBG_PHY, "pmds: %x max speed: %x\n",
			  options[i].pmd, options[i].max_lane_speed);
	}

	return 0;
}

/**
 * ice_aq_set_port_option
 * @hw: pointer to the HW struct
 * @lport: logical port to call the command with
 * @lport_valid: when false, FW uses port owned by the PF instead of lport,
 *               when PF owns more than 1 port it must be true
 * @new_option: new port option to be written
 *
 * Calls Set Port Options AQC (0x06eb).
 */
int
ice_aq_set_port_option(struct ice_hw *hw, u8 lport, u8 lport_valid,
		       u8 new_option)
{
	struct ice_aqc_set_port_option *cmd;
	struct ice_aq_desc desc;

	if (new_option > ICE_AQC_PORT_OPT_COUNT_M)
		return -EINVAL;

	cmd = &desc.params.set_port_option;
	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_port_option);

	if (lport_valid)
		cmd->lport_num = lport;

	cmd->lport_num_valid = lport_valid;
	cmd->selected_port_option = new_option;

	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
}

/**
 * ice_aq_sff_eeprom
 * @hw: pointer to the HW struct
+9 −0
Original line number Diff line number Diff line
@@ -151,6 +151,15 @@ int
ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode,
		       struct ice_sq_cd *cd);
int
ice_aq_get_port_options(struct ice_hw *hw,
			struct ice_aqc_get_port_options_elem *options,
			u8 *option_count, u8 lport, bool lport_valid,
			u8 *active_option_idx, bool *active_option_valid,
			u8 *pending_option_idx, bool *pending_option_valid);
int
ice_aq_set_port_option(struct ice_hw *hw, u8 lport, u8 lport_valid,
		       u8 new_option);
int
ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr,
		  u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length,
		  bool write, struct ice_sq_cd *cd);
+288 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@
#include "ice_eswitch.h"
#include "ice_fw_update.h"

static int ice_active_port_option = -1;

/* context for devlink info version reporting */
struct ice_info_ctx {
	char buf[128];
@@ -466,12 +468,259 @@ ice_devlink_reload_empr_finish(struct devlink *devlink,
	return 0;
}

/**
 * ice_devlink_port_opt_speed_str - convert speed to a string
 * @speed: speed value
 */
static const char *ice_devlink_port_opt_speed_str(u8 speed)
{
	switch (speed & ICE_AQC_PORT_OPT_MAX_LANE_M) {
	case ICE_AQC_PORT_OPT_MAX_LANE_100M:
		return "0.1";
	case ICE_AQC_PORT_OPT_MAX_LANE_1G:
		return "1";
	case ICE_AQC_PORT_OPT_MAX_LANE_2500M:
		return "2.5";
	case ICE_AQC_PORT_OPT_MAX_LANE_5G:
		return "5";
	case ICE_AQC_PORT_OPT_MAX_LANE_10G:
		return "10";
	case ICE_AQC_PORT_OPT_MAX_LANE_25G:
		return "25";
	case ICE_AQC_PORT_OPT_MAX_LANE_50G:
		return "50";
	case ICE_AQC_PORT_OPT_MAX_LANE_100G:
		return "100";
	}

	return "-";
}

#define ICE_PORT_OPT_DESC_LEN	50
/**
 * ice_devlink_port_options_print - Print available port split options
 * @pf: the PF to print split port options
 *
 * Prints a table with available port split options and max port speeds
 */
static void ice_devlink_port_options_print(struct ice_pf *pf)
{
	u8 i, j, options_count, cnt, speed, pending_idx, active_idx;
	struct ice_aqc_get_port_options_elem *options, *opt;
	struct device *dev = ice_pf_to_dev(pf);
	bool active_valid, pending_valid;
	char desc[ICE_PORT_OPT_DESC_LEN];
	const char *str;
	int status;

	options = kcalloc(ICE_AQC_PORT_OPT_MAX * ICE_MAX_PORT_PER_PCI_DEV,
			  sizeof(*options), GFP_KERNEL);
	if (!options)
		return;

	for (i = 0; i < ICE_MAX_PORT_PER_PCI_DEV; i++) {
		opt = options + i * ICE_AQC_PORT_OPT_MAX;
		options_count = ICE_AQC_PORT_OPT_MAX;
		active_valid = 0;

		status = ice_aq_get_port_options(&pf->hw, opt, &options_count,
						 i, true, &active_idx,
						 &active_valid, &pending_idx,
						 &pending_valid);
		if (status) {
			dev_dbg(dev, "Couldn't read port option for port %d, err %d\n",
				i, status);
			goto err;
		}
	}

	dev_dbg(dev, "Available port split options and max port speeds (Gbps):\n");
	dev_dbg(dev, "Status  Split      Quad 0          Quad 1\n");
	dev_dbg(dev, "        count  L0  L1  L2  L3  L4  L5  L6  L7\n");

	for (i = 0; i < options_count; i++) {
		cnt = 0;

		if (i == ice_active_port_option)
			str = "Active";
		else if ((i == pending_idx) && pending_valid)
			str = "Pending";
		else
			str = "";

		cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
				"%-8s", str);

		cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
				"%-6u", options[i].pmd);

		for (j = 0; j < ICE_MAX_PORT_PER_PCI_DEV; ++j) {
			speed = options[i + j * ICE_AQC_PORT_OPT_MAX].max_lane_speed;
			str = ice_devlink_port_opt_speed_str(speed);
			cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
					"%3s ", str);
		}

		dev_dbg(dev, "%s\n", desc);
	}

err:
	kfree(options);
}

/**
 * ice_devlink_aq_set_port_option - Send set port option admin queue command
 * @pf: the PF to print split port options
 * @option_idx: selected port option
 * @extack: extended netdev ack structure
 *
 * Sends set port option admin queue command with selected port option and
 * calls NVM write activate.
 */
static int
ice_devlink_aq_set_port_option(struct ice_pf *pf, u8 option_idx,
			       struct netlink_ext_ack *extack)
{
	struct device *dev = ice_pf_to_dev(pf);
	int status;

	status = ice_aq_set_port_option(&pf->hw, 0, true, option_idx);
	if (status) {
		dev_dbg(dev, "ice_aq_set_port_option, err %d aq_err %d\n",
			status, pf->hw.adminq.sq_last_status);
		NL_SET_ERR_MSG_MOD(extack, "Port split request failed");
		return -EIO;
	}

	status = ice_acquire_nvm(&pf->hw, ICE_RES_WRITE);
	if (status) {
		dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
			status, pf->hw.adminq.sq_last_status);
		NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
		return -EIO;
	}

	status = ice_nvm_write_activate(&pf->hw, ICE_AQC_NVM_ACTIV_REQ_EMPR, NULL);
	if (status) {
		dev_dbg(dev, "ice_nvm_write_activate failed, err %d aq_err %d\n",
			status, pf->hw.adminq.sq_last_status);
		NL_SET_ERR_MSG_MOD(extack, "Port split request failed to save data");
		ice_release_nvm(&pf->hw);
		return -EIO;
	}

	ice_release_nvm(&pf->hw);

	NL_SET_ERR_MSG_MOD(extack, "Reboot required to finish port split");
	return 0;
}

/**
 * ice_devlink_port_split - .port_split devlink handler
 * @devlink: devlink instance structure
 * @port: devlink port structure
 * @count: number of ports to split to
 * @extack: extended netdev ack structure
 *
 * Callback for the devlink .port_split operation.
 *
 * Unfortunately, the devlink expression of available options is limited
 * to just a number, so search for an FW port option which supports
 * the specified number. As there could be multiple FW port options with
 * the same port split count, allow switching between them. When the same
 * port split count request is issued again, switch to the next FW port
 * option with the same port split count.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_split(struct devlink *devlink, struct devlink_port *port,
		       unsigned int count, struct netlink_ext_ack *extack)
{
	struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
	u8 i, j, active_idx, pending_idx, new_option;
	struct ice_pf *pf = devlink_priv(devlink);
	u8 option_count = ICE_AQC_PORT_OPT_MAX;
	struct device *dev = ice_pf_to_dev(pf);
	bool active_valid, pending_valid;
	int status;

	status = ice_aq_get_port_options(&pf->hw, options, &option_count,
					 0, true, &active_idx, &active_valid,
					 &pending_idx, &pending_valid);
	if (status) {
		dev_dbg(dev, "Couldn't read port split options, err = %d\n",
			status);
		NL_SET_ERR_MSG_MOD(extack, "Failed to get available port split options");
		return -EIO;
	}

	new_option = ICE_AQC_PORT_OPT_MAX;
	active_idx = pending_valid ? pending_idx : active_idx;
	for (i = 1; i <= option_count; i++) {
		/* In order to allow switching between FW port options with
		 * the same port split count, search for a new option starting
		 * from the active/pending option (with array wrap around).
		 */
		j = (active_idx + i) % option_count;

		if (count == options[j].pmd) {
			new_option = j;
			break;
		}
	}

	if (new_option == active_idx) {
		dev_dbg(dev, "request to split: count: %u is already set and there are no other options\n",
			count);
		NL_SET_ERR_MSG_MOD(extack, "Requested split count is already set");
		ice_devlink_port_options_print(pf);
		return -EINVAL;
	}

	if (new_option == ICE_AQC_PORT_OPT_MAX) {
		dev_dbg(dev, "request to split: count: %u not found\n", count);
		NL_SET_ERR_MSG_MOD(extack, "Port split requested unsupported port config");
		ice_devlink_port_options_print(pf);
		return -EINVAL;
	}

	status = ice_devlink_aq_set_port_option(pf, new_option, extack);
	if (status)
		return status;

	ice_devlink_port_options_print(pf);

	return 0;
}

/**
 * ice_devlink_port_unsplit - .port_unsplit devlink handler
 * @devlink: devlink instance structure
 * @port: devlink port structure
 * @extack: extended netdev ack structure
 *
 * Callback for the devlink .port_unsplit operation.
 * Calls ice_devlink_port_split with split count set to 1.
 * There could be no FW option available with split count 1.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port,
			 struct netlink_ext_ack *extack)
{
	return ice_devlink_port_split(devlink, port, 1, extack);
}

static const struct devlink_ops ice_devlink_ops = {
	.supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
	.reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
	/* The ice driver currently does not support driver reinit */
	.reload_down = ice_devlink_reload_empr_start,
	.reload_up = ice_devlink_reload_empr_finish,
	.port_split = ice_devlink_port_split,
	.port_unsplit = ice_devlink_port_unsplit,
	.eswitch_mode_get = ice_eswitch_mode_get,
	.eswitch_mode_set = ice_eswitch_mode_set,
	.info_get = ice_devlink_info_get,
@@ -694,6 +943,39 @@ void ice_devlink_unregister_params(struct ice_pf *pf)
				  ARRAY_SIZE(ice_devlink_params));
}

/**
 * ice_devlink_set_port_split_options - Set port split options
 * @pf: the PF to set port split options
 * @attrs: devlink attributes
 *
 * Sets devlink port split options based on available FW port options
 */
static void
ice_devlink_set_port_split_options(struct ice_pf *pf,
				   struct devlink_port_attrs *attrs)
{
	struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
	u8 i, active_idx, pending_idx, option_count = ICE_AQC_PORT_OPT_MAX;
	bool active_valid, pending_valid;
	int status;

	status = ice_aq_get_port_options(&pf->hw, options, &option_count,
					 0, true, &active_idx, &active_valid,
					 &pending_idx, &pending_valid);
	if (status) {
		dev_dbg(ice_pf_to_dev(pf), "Couldn't read port split options, err = %d\n",
			status);
		return;
	}

	/* find the biggest available port split count */
	for (i = 0; i < option_count; i++)
		attrs->lanes = max_t(int, attrs->lanes, options[i].pmd);

	attrs->splittable = attrs->lanes ? 1 : 0;
	ice_active_port_option = active_idx;
}

/**
 * ice_devlink_create_pf_port - Create a devlink port for this PF
 * @pf: the PF to create a devlink port for
@@ -722,6 +1004,12 @@ int ice_devlink_create_pf_port(struct ice_pf *pf)
	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
	attrs.phys.port_number = pf->hw.bus.func;

	/* As FW supports only port split options for whole device,
	 * set port split options only for first PF.
	 */
	if (pf->hw.pf_id == 0)
		ice_devlink_set_port_split_options(pf, &attrs);

	ice_devlink_set_switch_id(pf, &attrs.switch_id);

	devlink_port_attrs_set(devlink_port, &attrs);
Loading