// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024, Intel Corporation. */
#include <linux/vmalloc.h>
#include "ice.h"
#include "devlink.h"
#include "port.h"
#include "ice_lib.h"
#include "ice_fltr.h"
static int ice_active_port_option = -1;
/**
* 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_40G:
return "40";
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