162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020, Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/vmalloc.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "ice.h"
762306a36Sopenharmony_ci#include "ice_lib.h"
862306a36Sopenharmony_ci#include "ice_devlink.h"
962306a36Sopenharmony_ci#include "ice_eswitch.h"
1062306a36Sopenharmony_ci#include "ice_fw_update.h"
1162306a36Sopenharmony_ci#include "ice_dcb_lib.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic int ice_active_port_option = -1;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* context for devlink info version reporting */
1662306a36Sopenharmony_cistruct ice_info_ctx {
1762306a36Sopenharmony_ci	char buf[128];
1862306a36Sopenharmony_ci	struct ice_orom_info pending_orom;
1962306a36Sopenharmony_ci	struct ice_nvm_info pending_nvm;
2062306a36Sopenharmony_ci	struct ice_netlist_info pending_netlist;
2162306a36Sopenharmony_ci	struct ice_hw_dev_caps dev_caps;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* The following functions are used to format specific strings for various
2562306a36Sopenharmony_ci * devlink info versions. The ctx parameter is used to provide the storage
2662306a36Sopenharmony_ci * buffer, as well as any ancillary information calculated when the info
2762306a36Sopenharmony_ci * request was made.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * If a version does not exist, for example when attempting to get the
3062306a36Sopenharmony_ci * inactive version of flash when there is no pending update, the function
3162306a36Sopenharmony_ci * should leave the buffer in the ctx structure empty.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic void ice_info_get_dsn(struct ice_pf *pf, struct ice_info_ctx *ctx)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	u8 dsn[8];
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Copy the DSN into an array in Big Endian format */
3962306a36Sopenharmony_ci	put_unaligned_be64(pci_get_dsn(pf->pdev), dsn);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%8phD", dsn);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void ice_info_pba(struct ice_pf *pf, struct ice_info_ctx *ctx)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
4762306a36Sopenharmony_ci	int status;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	status = ice_read_pba_string(hw, (u8 *)ctx->buf, sizeof(ctx->buf));
5062306a36Sopenharmony_ci	if (status)
5162306a36Sopenharmony_ci		/* We failed to locate the PBA, so just skip this entry */
5262306a36Sopenharmony_ci		dev_dbg(ice_pf_to_dev(pf), "Failed to read Product Board Assembly string, status %d\n",
5362306a36Sopenharmony_ci			status);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void ice_info_fw_mgmt(struct ice_pf *pf, struct ice_info_ctx *ctx)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",
6162306a36Sopenharmony_ci		 hw->fw_maj_ver, hw->fw_min_ver, hw->fw_patch);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void ice_info_fw_api(struct ice_pf *pf, struct ice_info_ctx *ctx)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->api_maj_ver,
6962306a36Sopenharmony_ci		 hw->api_min_ver, hw->api_patch);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void ice_info_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", hw->fw_build);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void ice_info_orom_ver(struct ice_pf *pf, struct ice_info_ctx *ctx)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct ice_orom_info *orom = &pf->hw.flash.orom;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",
8462306a36Sopenharmony_ci		 orom->major, orom->build, orom->patch);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void
8862306a36Sopenharmony_ciice_info_pending_orom_ver(struct ice_pf __always_unused *pf,
8962306a36Sopenharmony_ci			  struct ice_info_ctx *ctx)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct ice_orom_info *orom = &ctx->pending_orom;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_orom)
9462306a36Sopenharmony_ci		snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",
9562306a36Sopenharmony_ci			 orom->major, orom->build, orom->patch);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void ice_info_nvm_ver(struct ice_pf *pf, struct ice_info_ctx *ctx)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct ice_nvm_info *nvm = &pf->hw.flash.nvm;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void
10662306a36Sopenharmony_ciice_info_pending_nvm_ver(struct ice_pf __always_unused *pf,
10762306a36Sopenharmony_ci			 struct ice_info_ctx *ctx)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct ice_nvm_info *nvm = &ctx->pending_nvm;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm)
11262306a36Sopenharmony_ci		snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x",
11362306a36Sopenharmony_ci			 nvm->major, nvm->minor);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void ice_info_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct ice_nvm_info *nvm = &pf->hw.flash.nvm;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void
12462306a36Sopenharmony_ciice_info_pending_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct ice_nvm_info *nvm = &ctx->pending_nvm;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm)
12962306a36Sopenharmony_ci		snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void ice_info_ddp_pkg_name(struct ice_pf *pf, struct ice_info_ctx *ctx)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%s", hw->active_pkg_name);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void
14062306a36Sopenharmony_ciice_info_ddp_pkg_version(struct ice_pf *pf, struct ice_info_ctx *ctx)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct ice_pkg_ver *pkg = &pf->hw.active_pkg_ver;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u.%u",
14562306a36Sopenharmony_ci		 pkg->major, pkg->minor, pkg->update, pkg->draft);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void
14962306a36Sopenharmony_ciice_info_ddp_pkg_bundle_id(struct ice_pf *pf, struct ice_info_ctx *ctx)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", pf->hw.active_track_id);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void ice_info_netlist_ver(struct ice_pf *pf, struct ice_info_ctx *ctx)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct ice_netlist_info *netlist = &pf->hw.flash.netlist;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* The netlist version fields are BCD formatted */
15962306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x",
16062306a36Sopenharmony_ci		 netlist->major, netlist->minor,
16162306a36Sopenharmony_ci		 netlist->type >> 16, netlist->type & 0xFFFF,
16262306a36Sopenharmony_ci		 netlist->rev, netlist->cust_ver);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void ice_info_netlist_build(struct ice_pf *pf, struct ice_info_ctx *ctx)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct ice_netlist_info *netlist = &pf->hw.flash.netlist;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void
17362306a36Sopenharmony_ciice_info_pending_netlist_ver(struct ice_pf __always_unused *pf,
17462306a36Sopenharmony_ci			     struct ice_info_ctx *ctx)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct ice_netlist_info *netlist = &ctx->pending_netlist;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* The netlist version fields are BCD formatted */
17962306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist)
18062306a36Sopenharmony_ci		snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x",
18162306a36Sopenharmony_ci			 netlist->major, netlist->minor,
18262306a36Sopenharmony_ci			 netlist->type >> 16, netlist->type & 0xFFFF,
18362306a36Sopenharmony_ci			 netlist->rev, netlist->cust_ver);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void
18762306a36Sopenharmony_ciice_info_pending_netlist_build(struct ice_pf __always_unused *pf,
18862306a36Sopenharmony_ci			       struct ice_info_ctx *ctx)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct ice_netlist_info *netlist = &ctx->pending_netlist;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist)
19362306a36Sopenharmony_ci		snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci#define fixed(key, getter) { ICE_VERSION_FIXED, key, getter, NULL }
19762306a36Sopenharmony_ci#define running(key, getter) { ICE_VERSION_RUNNING, key, getter, NULL }
19862306a36Sopenharmony_ci#define stored(key, getter, fallback) { ICE_VERSION_STORED, key, getter, fallback }
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/* The combined() macro inserts both the running entry as well as a stored
20162306a36Sopenharmony_ci * entry. The running entry will always report the version from the active
20262306a36Sopenharmony_ci * handler. The stored entry will first try the pending handler, and fallback
20362306a36Sopenharmony_ci * to the active handler if the pending function does not report a version.
20462306a36Sopenharmony_ci * The pending handler should check the status of a pending update for the
20562306a36Sopenharmony_ci * relevant flash component. It should only fill in the buffer in the case
20662306a36Sopenharmony_ci * where a valid pending version is available. This ensures that the related
20762306a36Sopenharmony_ci * stored and running versions remain in sync, and that stored versions are
20862306a36Sopenharmony_ci * correctly reported as expected.
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_ci#define combined(key, active, pending) \
21162306a36Sopenharmony_ci	running(key, active), \
21262306a36Sopenharmony_ci	stored(key, pending, active)
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cienum ice_version_type {
21562306a36Sopenharmony_ci	ICE_VERSION_FIXED,
21662306a36Sopenharmony_ci	ICE_VERSION_RUNNING,
21762306a36Sopenharmony_ci	ICE_VERSION_STORED,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic const struct ice_devlink_version {
22162306a36Sopenharmony_ci	enum ice_version_type type;
22262306a36Sopenharmony_ci	const char *key;
22362306a36Sopenharmony_ci	void (*getter)(struct ice_pf *pf, struct ice_info_ctx *ctx);
22462306a36Sopenharmony_ci	void (*fallback)(struct ice_pf *pf, struct ice_info_ctx *ctx);
22562306a36Sopenharmony_ci} ice_devlink_versions[] = {
22662306a36Sopenharmony_ci	fixed(DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, ice_info_pba),
22762306a36Sopenharmony_ci	running(DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, ice_info_fw_mgmt),
22862306a36Sopenharmony_ci	running("fw.mgmt.api", ice_info_fw_api),
22962306a36Sopenharmony_ci	running("fw.mgmt.build", ice_info_fw_build),
23062306a36Sopenharmony_ci	combined(DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, ice_info_orom_ver, ice_info_pending_orom_ver),
23162306a36Sopenharmony_ci	combined("fw.psid.api", ice_info_nvm_ver, ice_info_pending_nvm_ver),
23262306a36Sopenharmony_ci	combined(DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, ice_info_eetrack, ice_info_pending_eetrack),
23362306a36Sopenharmony_ci	running("fw.app.name", ice_info_ddp_pkg_name),
23462306a36Sopenharmony_ci	running(DEVLINK_INFO_VERSION_GENERIC_FW_APP, ice_info_ddp_pkg_version),
23562306a36Sopenharmony_ci	running("fw.app.bundle_id", ice_info_ddp_pkg_bundle_id),
23662306a36Sopenharmony_ci	combined("fw.netlist", ice_info_netlist_ver, ice_info_pending_netlist_ver),
23762306a36Sopenharmony_ci	combined("fw.netlist.build", ice_info_netlist_build, ice_info_pending_netlist_build),
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/**
24162306a36Sopenharmony_ci * ice_devlink_info_get - .info_get devlink handler
24262306a36Sopenharmony_ci * @devlink: devlink instance structure
24362306a36Sopenharmony_ci * @req: the devlink info request
24462306a36Sopenharmony_ci * @extack: extended netdev ack structure
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Callback for the devlink .info_get operation. Reports information about the
24762306a36Sopenharmony_ci * device.
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic int ice_devlink_info_get(struct devlink *devlink,
25262306a36Sopenharmony_ci				struct devlink_info_req *req,
25362306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
25662306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
25762306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
25862306a36Sopenharmony_ci	struct ice_info_ctx *ctx;
25962306a36Sopenharmony_ci	size_t i;
26062306a36Sopenharmony_ci	int err;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	err = ice_wait_for_reset(pf, 10 * HZ);
26362306a36Sopenharmony_ci	if (err) {
26462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Device is busy resetting");
26562306a36Sopenharmony_ci		return err;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
26962306a36Sopenharmony_ci	if (!ctx)
27062306a36Sopenharmony_ci		return -ENOMEM;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* discover capabilities first */
27362306a36Sopenharmony_ci	err = ice_discover_dev_caps(hw, &ctx->dev_caps);
27462306a36Sopenharmony_ci	if (err) {
27562306a36Sopenharmony_ci		dev_dbg(dev, "Failed to discover device capabilities, status %d aq_err %s\n",
27662306a36Sopenharmony_ci			err, ice_aq_str(hw->adminq.sq_last_status));
27762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unable to discover device capabilities");
27862306a36Sopenharmony_ci		goto out_free_ctx;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_orom) {
28262306a36Sopenharmony_ci		err = ice_get_inactive_orom_ver(hw, &ctx->pending_orom);
28362306a36Sopenharmony_ci		if (err) {
28462306a36Sopenharmony_ci			dev_dbg(dev, "Unable to read inactive Option ROM version data, status %d aq_err %s\n",
28562306a36Sopenharmony_ci				err, ice_aq_str(hw->adminq.sq_last_status));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci			/* disable display of pending Option ROM */
28862306a36Sopenharmony_ci			ctx->dev_caps.common_cap.nvm_update_pending_orom = false;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) {
29362306a36Sopenharmony_ci		err = ice_get_inactive_nvm_ver(hw, &ctx->pending_nvm);
29462306a36Sopenharmony_ci		if (err) {
29562306a36Sopenharmony_ci			dev_dbg(dev, "Unable to read inactive NVM version data, status %d aq_err %s\n",
29662306a36Sopenharmony_ci				err, ice_aq_str(hw->adminq.sq_last_status));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			/* disable display of pending Option ROM */
29962306a36Sopenharmony_ci			ctx->dev_caps.common_cap.nvm_update_pending_nvm = false;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) {
30462306a36Sopenharmony_ci		err = ice_get_inactive_netlist_ver(hw, &ctx->pending_netlist);
30562306a36Sopenharmony_ci		if (err) {
30662306a36Sopenharmony_ci			dev_dbg(dev, "Unable to read inactive Netlist version data, status %d aq_err %s\n",
30762306a36Sopenharmony_ci				err, ice_aq_str(hw->adminq.sq_last_status));
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci			/* disable display of pending Option ROM */
31062306a36Sopenharmony_ci			ctx->dev_caps.common_cap.nvm_update_pending_netlist = false;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ice_info_get_dsn(pf, ctx);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	err = devlink_info_serial_number_put(req, ctx->buf);
31762306a36Sopenharmony_ci	if (err) {
31862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unable to set serial number");
31962306a36Sopenharmony_ci		goto out_free_ctx;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ice_devlink_versions); i++) {
32362306a36Sopenharmony_ci		enum ice_version_type type = ice_devlink_versions[i].type;
32462306a36Sopenharmony_ci		const char *key = ice_devlink_versions[i].key;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		memset(ctx->buf, 0, sizeof(ctx->buf));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		ice_devlink_versions[i].getter(pf, ctx);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		/* If the default getter doesn't report a version, use the
33162306a36Sopenharmony_ci		 * fallback function. This is primarily useful in the case of
33262306a36Sopenharmony_ci		 * "stored" versions that want to report the same value as the
33362306a36Sopenharmony_ci		 * running version in the normal case of no pending update.
33462306a36Sopenharmony_ci		 */
33562306a36Sopenharmony_ci		if (ctx->buf[0] == '\0' && ice_devlink_versions[i].fallback)
33662306a36Sopenharmony_ci			ice_devlink_versions[i].fallback(pf, ctx);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		/* Do not report missing versions */
33962306a36Sopenharmony_ci		if (ctx->buf[0] == '\0')
34062306a36Sopenharmony_ci			continue;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		switch (type) {
34362306a36Sopenharmony_ci		case ICE_VERSION_FIXED:
34462306a36Sopenharmony_ci			err = devlink_info_version_fixed_put(req, key, ctx->buf);
34562306a36Sopenharmony_ci			if (err) {
34662306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Unable to set fixed version");
34762306a36Sopenharmony_ci				goto out_free_ctx;
34862306a36Sopenharmony_ci			}
34962306a36Sopenharmony_ci			break;
35062306a36Sopenharmony_ci		case ICE_VERSION_RUNNING:
35162306a36Sopenharmony_ci			err = devlink_info_version_running_put(req, key, ctx->buf);
35262306a36Sopenharmony_ci			if (err) {
35362306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Unable to set running version");
35462306a36Sopenharmony_ci				goto out_free_ctx;
35562306a36Sopenharmony_ci			}
35662306a36Sopenharmony_ci			break;
35762306a36Sopenharmony_ci		case ICE_VERSION_STORED:
35862306a36Sopenharmony_ci			err = devlink_info_version_stored_put(req, key, ctx->buf);
35962306a36Sopenharmony_ci			if (err) {
36062306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Unable to set stored version");
36162306a36Sopenharmony_ci				goto out_free_ctx;
36262306a36Sopenharmony_ci			}
36362306a36Sopenharmony_ci			break;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ciout_free_ctx:
36862306a36Sopenharmony_ci	kfree(ctx);
36962306a36Sopenharmony_ci	return err;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * ice_devlink_reload_empr_start - Start EMP reset to activate new firmware
37462306a36Sopenharmony_ci * @pf: pointer to the pf instance
37562306a36Sopenharmony_ci * @extack: netlink extended ACK structure
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * Allow user to activate new Embedded Management Processor firmware by
37862306a36Sopenharmony_ci * issuing device specific EMP reset. Called in response to
37962306a36Sopenharmony_ci * a DEVLINK_CMD_RELOAD with the DEVLINK_RELOAD_ACTION_FW_ACTIVATE.
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci * Note that teardown and rebuild of the driver state happens automatically as
38262306a36Sopenharmony_ci * part of an interrupt and watchdog task. This is because all physical
38362306a36Sopenharmony_ci * functions on the device must be able to reset when an EMP reset occurs from
38462306a36Sopenharmony_ci * any source.
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_cistatic int
38762306a36Sopenharmony_ciice_devlink_reload_empr_start(struct ice_pf *pf,
38862306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
39162306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
39262306a36Sopenharmony_ci	u8 pending;
39362306a36Sopenharmony_ci	int err;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	err = ice_get_pending_updates(pf, &pending, extack);
39662306a36Sopenharmony_ci	if (err)
39762306a36Sopenharmony_ci		return err;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* pending is a bitmask of which flash banks have a pending update,
40062306a36Sopenharmony_ci	 * including the main NVM bank, the Option ROM bank, and the netlist
40162306a36Sopenharmony_ci	 * bank. If any of these bits are set, then there is a pending update
40262306a36Sopenharmony_ci	 * waiting to be activated.
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci	if (!pending) {
40562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "No pending firmware update");
40662306a36Sopenharmony_ci		return -ECANCELED;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (pf->fw_emp_reset_disabled) {
41062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To activate firmware, a reboot or power cycle is needed");
41162306a36Sopenharmony_ci		return -ECANCELED;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	dev_dbg(dev, "Issuing device EMP reset to activate firmware\n");
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	err = ice_aq_nvm_update_empr(hw);
41762306a36Sopenharmony_ci	if (err) {
41862306a36Sopenharmony_ci		dev_err(dev, "Failed to trigger EMP device reset to reload firmware, err %d aq_err %s\n",
41962306a36Sopenharmony_ci			err, ice_aq_str(hw->adminq.sq_last_status));
42062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device reset to reload firmware");
42162306a36Sopenharmony_ci		return err;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/**
42862306a36Sopenharmony_ci * ice_devlink_reload_down - prepare for reload
42962306a36Sopenharmony_ci * @devlink: pointer to the devlink instance to reload
43062306a36Sopenharmony_ci * @netns_change: if true, the network namespace is changing
43162306a36Sopenharmony_ci * @action: the action to perform
43262306a36Sopenharmony_ci * @limit: limits on what reload should do, such as not resetting
43362306a36Sopenharmony_ci * @extack: netlink extended ACK structure
43462306a36Sopenharmony_ci */
43562306a36Sopenharmony_cistatic int
43662306a36Sopenharmony_ciice_devlink_reload_down(struct devlink *devlink, bool netns_change,
43762306a36Sopenharmony_ci			enum devlink_reload_action action,
43862306a36Sopenharmony_ci			enum devlink_reload_limit limit,
43962306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	switch (action) {
44462306a36Sopenharmony_ci	case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
44562306a36Sopenharmony_ci		if (ice_is_eswitch_mode_switchdev(pf)) {
44662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
44762306a36Sopenharmony_ci					   "Go to legacy mode before doing reinit\n");
44862306a36Sopenharmony_ci			return -EOPNOTSUPP;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		if (ice_is_adq_active(pf)) {
45162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
45262306a36Sopenharmony_ci					   "Turn off ADQ before doing reinit\n");
45362306a36Sopenharmony_ci			return -EOPNOTSUPP;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		if (ice_has_vfs(pf)) {
45662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
45762306a36Sopenharmony_ci					   "Remove all VFs before doing reinit\n");
45862306a36Sopenharmony_ci			return -EOPNOTSUPP;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci		ice_unload(pf);
46162306a36Sopenharmony_ci		return 0;
46262306a36Sopenharmony_ci	case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
46362306a36Sopenharmony_ci		return ice_devlink_reload_empr_start(pf, extack);
46462306a36Sopenharmony_ci	default:
46562306a36Sopenharmony_ci		WARN_ON(1);
46662306a36Sopenharmony_ci		return -EOPNOTSUPP;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci/**
47162306a36Sopenharmony_ci * ice_devlink_reload_empr_finish - Wait for EMP reset to finish
47262306a36Sopenharmony_ci * @pf: pointer to the pf instance
47362306a36Sopenharmony_ci * @extack: netlink extended ACK structure
47462306a36Sopenharmony_ci *
47562306a36Sopenharmony_ci * Wait for driver to finish rebuilding after EMP reset is completed. This
47662306a36Sopenharmony_ci * includes time to wait for both the actual device reset as well as the time
47762306a36Sopenharmony_ci * for the driver's rebuild to complete.
47862306a36Sopenharmony_ci */
47962306a36Sopenharmony_cistatic int
48062306a36Sopenharmony_ciice_devlink_reload_empr_finish(struct ice_pf *pf,
48162306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	int err;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	err = ice_wait_for_reset(pf, 60 * HZ);
48662306a36Sopenharmony_ci	if (err) {
48762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Device still resetting after 1 minute");
48862306a36Sopenharmony_ci		return err;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return 0;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/**
49562306a36Sopenharmony_ci * ice_devlink_port_opt_speed_str - convert speed to a string
49662306a36Sopenharmony_ci * @speed: speed value
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_cistatic const char *ice_devlink_port_opt_speed_str(u8 speed)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	switch (speed & ICE_AQC_PORT_OPT_MAX_LANE_M) {
50162306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_100M:
50262306a36Sopenharmony_ci		return "0.1";
50362306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_1G:
50462306a36Sopenharmony_ci		return "1";
50562306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_2500M:
50662306a36Sopenharmony_ci		return "2.5";
50762306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_5G:
50862306a36Sopenharmony_ci		return "5";
50962306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_10G:
51062306a36Sopenharmony_ci		return "10";
51162306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_25G:
51262306a36Sopenharmony_ci		return "25";
51362306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_50G:
51462306a36Sopenharmony_ci		return "50";
51562306a36Sopenharmony_ci	case ICE_AQC_PORT_OPT_MAX_LANE_100G:
51662306a36Sopenharmony_ci		return "100";
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return "-";
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci#define ICE_PORT_OPT_DESC_LEN	50
52362306a36Sopenharmony_ci/**
52462306a36Sopenharmony_ci * ice_devlink_port_options_print - Print available port split options
52562306a36Sopenharmony_ci * @pf: the PF to print split port options
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * Prints a table with available port split options and max port speeds
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic void ice_devlink_port_options_print(struct ice_pf *pf)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	u8 i, j, options_count, cnt, speed, pending_idx, active_idx;
53262306a36Sopenharmony_ci	struct ice_aqc_get_port_options_elem *options, *opt;
53362306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
53462306a36Sopenharmony_ci	bool active_valid, pending_valid;
53562306a36Sopenharmony_ci	char desc[ICE_PORT_OPT_DESC_LEN];
53662306a36Sopenharmony_ci	const char *str;
53762306a36Sopenharmony_ci	int status;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	options = kcalloc(ICE_AQC_PORT_OPT_MAX * ICE_MAX_PORT_PER_PCI_DEV,
54062306a36Sopenharmony_ci			  sizeof(*options), GFP_KERNEL);
54162306a36Sopenharmony_ci	if (!options)
54262306a36Sopenharmony_ci		return;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	for (i = 0; i < ICE_MAX_PORT_PER_PCI_DEV; i++) {
54562306a36Sopenharmony_ci		opt = options + i * ICE_AQC_PORT_OPT_MAX;
54662306a36Sopenharmony_ci		options_count = ICE_AQC_PORT_OPT_MAX;
54762306a36Sopenharmony_ci		active_valid = 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		status = ice_aq_get_port_options(&pf->hw, opt, &options_count,
55062306a36Sopenharmony_ci						 i, true, &active_idx,
55162306a36Sopenharmony_ci						 &active_valid, &pending_idx,
55262306a36Sopenharmony_ci						 &pending_valid);
55362306a36Sopenharmony_ci		if (status) {
55462306a36Sopenharmony_ci			dev_dbg(dev, "Couldn't read port option for port %d, err %d\n",
55562306a36Sopenharmony_ci				i, status);
55662306a36Sopenharmony_ci			goto err;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	dev_dbg(dev, "Available port split options and max port speeds (Gbps):\n");
56162306a36Sopenharmony_ci	dev_dbg(dev, "Status  Split      Quad 0          Quad 1\n");
56262306a36Sopenharmony_ci	dev_dbg(dev, "        count  L0  L1  L2  L3  L4  L5  L6  L7\n");
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	for (i = 0; i < options_count; i++) {
56562306a36Sopenharmony_ci		cnt = 0;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		if (i == ice_active_port_option)
56862306a36Sopenharmony_ci			str = "Active";
56962306a36Sopenharmony_ci		else if ((i == pending_idx) && pending_valid)
57062306a36Sopenharmony_ci			str = "Pending";
57162306a36Sopenharmony_ci		else
57262306a36Sopenharmony_ci			str = "";
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
57562306a36Sopenharmony_ci				"%-8s", str);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
57862306a36Sopenharmony_ci				"%-6u", options[i].pmd);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		for (j = 0; j < ICE_MAX_PORT_PER_PCI_DEV; ++j) {
58162306a36Sopenharmony_ci			speed = options[i + j * ICE_AQC_PORT_OPT_MAX].max_lane_speed;
58262306a36Sopenharmony_ci			str = ice_devlink_port_opt_speed_str(speed);
58362306a36Sopenharmony_ci			cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
58462306a36Sopenharmony_ci					"%3s ", str);
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		dev_dbg(dev, "%s\n", desc);
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cierr:
59162306a36Sopenharmony_ci	kfree(options);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/**
59562306a36Sopenharmony_ci * ice_devlink_aq_set_port_option - Send set port option admin queue command
59662306a36Sopenharmony_ci * @pf: the PF to print split port options
59762306a36Sopenharmony_ci * @option_idx: selected port option
59862306a36Sopenharmony_ci * @extack: extended netdev ack structure
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * Sends set port option admin queue command with selected port option and
60162306a36Sopenharmony_ci * calls NVM write activate.
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_cistatic int
60462306a36Sopenharmony_ciice_devlink_aq_set_port_option(struct ice_pf *pf, u8 option_idx,
60562306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
60862306a36Sopenharmony_ci	int status;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	status = ice_aq_set_port_option(&pf->hw, 0, true, option_idx);
61162306a36Sopenharmony_ci	if (status) {
61262306a36Sopenharmony_ci		dev_dbg(dev, "ice_aq_set_port_option, err %d aq_err %d\n",
61362306a36Sopenharmony_ci			status, pf->hw.adminq.sq_last_status);
61462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port split request failed");
61562306a36Sopenharmony_ci		return -EIO;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	status = ice_acquire_nvm(&pf->hw, ICE_RES_WRITE);
61962306a36Sopenharmony_ci	if (status) {
62062306a36Sopenharmony_ci		dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
62162306a36Sopenharmony_ci			status, pf->hw.adminq.sq_last_status);
62262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
62362306a36Sopenharmony_ci		return -EIO;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	status = ice_nvm_write_activate(&pf->hw, ICE_AQC_NVM_ACTIV_REQ_EMPR, NULL);
62762306a36Sopenharmony_ci	if (status) {
62862306a36Sopenharmony_ci		dev_dbg(dev, "ice_nvm_write_activate failed, err %d aq_err %d\n",
62962306a36Sopenharmony_ci			status, pf->hw.adminq.sq_last_status);
63062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port split request failed to save data");
63162306a36Sopenharmony_ci		ice_release_nvm(&pf->hw);
63262306a36Sopenharmony_ci		return -EIO;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	ice_release_nvm(&pf->hw);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Reboot required to finish port split");
63862306a36Sopenharmony_ci	return 0;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/**
64262306a36Sopenharmony_ci * ice_devlink_port_split - .port_split devlink handler
64362306a36Sopenharmony_ci * @devlink: devlink instance structure
64462306a36Sopenharmony_ci * @port: devlink port structure
64562306a36Sopenharmony_ci * @count: number of ports to split to
64662306a36Sopenharmony_ci * @extack: extended netdev ack structure
64762306a36Sopenharmony_ci *
64862306a36Sopenharmony_ci * Callback for the devlink .port_split operation.
64962306a36Sopenharmony_ci *
65062306a36Sopenharmony_ci * Unfortunately, the devlink expression of available options is limited
65162306a36Sopenharmony_ci * to just a number, so search for an FW port option which supports
65262306a36Sopenharmony_ci * the specified number. As there could be multiple FW port options with
65362306a36Sopenharmony_ci * the same port split count, allow switching between them. When the same
65462306a36Sopenharmony_ci * port split count request is issued again, switch to the next FW port
65562306a36Sopenharmony_ci * option with the same port split count.
65662306a36Sopenharmony_ci *
65762306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
65862306a36Sopenharmony_ci */
65962306a36Sopenharmony_cistatic int
66062306a36Sopenharmony_ciice_devlink_port_split(struct devlink *devlink, struct devlink_port *port,
66162306a36Sopenharmony_ci		       unsigned int count, struct netlink_ext_ack *extack)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
66462306a36Sopenharmony_ci	u8 i, j, active_idx, pending_idx, new_option;
66562306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
66662306a36Sopenharmony_ci	u8 option_count = ICE_AQC_PORT_OPT_MAX;
66762306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
66862306a36Sopenharmony_ci	bool active_valid, pending_valid;
66962306a36Sopenharmony_ci	int status;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	status = ice_aq_get_port_options(&pf->hw, options, &option_count,
67262306a36Sopenharmony_ci					 0, true, &active_idx, &active_valid,
67362306a36Sopenharmony_ci					 &pending_idx, &pending_valid);
67462306a36Sopenharmony_ci	if (status) {
67562306a36Sopenharmony_ci		dev_dbg(dev, "Couldn't read port split options, err = %d\n",
67662306a36Sopenharmony_ci			status);
67762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to get available port split options");
67862306a36Sopenharmony_ci		return -EIO;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	new_option = ICE_AQC_PORT_OPT_MAX;
68262306a36Sopenharmony_ci	active_idx = pending_valid ? pending_idx : active_idx;
68362306a36Sopenharmony_ci	for (i = 1; i <= option_count; i++) {
68462306a36Sopenharmony_ci		/* In order to allow switching between FW port options with
68562306a36Sopenharmony_ci		 * the same port split count, search for a new option starting
68662306a36Sopenharmony_ci		 * from the active/pending option (with array wrap around).
68762306a36Sopenharmony_ci		 */
68862306a36Sopenharmony_ci		j = (active_idx + i) % option_count;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		if (count == options[j].pmd) {
69162306a36Sopenharmony_ci			new_option = j;
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (new_option == active_idx) {
69762306a36Sopenharmony_ci		dev_dbg(dev, "request to split: count: %u is already set and there are no other options\n",
69862306a36Sopenharmony_ci			count);
69962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Requested split count is already set");
70062306a36Sopenharmony_ci		ice_devlink_port_options_print(pf);
70162306a36Sopenharmony_ci		return -EINVAL;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (new_option == ICE_AQC_PORT_OPT_MAX) {
70562306a36Sopenharmony_ci		dev_dbg(dev, "request to split: count: %u not found\n", count);
70662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port split requested unsupported port config");
70762306a36Sopenharmony_ci		ice_devlink_port_options_print(pf);
70862306a36Sopenharmony_ci		return -EINVAL;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	status = ice_devlink_aq_set_port_option(pf, new_option, extack);
71262306a36Sopenharmony_ci	if (status)
71362306a36Sopenharmony_ci		return status;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	ice_devlink_port_options_print(pf);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return 0;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci * ice_devlink_port_unsplit - .port_unsplit devlink handler
72262306a36Sopenharmony_ci * @devlink: devlink instance structure
72362306a36Sopenharmony_ci * @port: devlink port structure
72462306a36Sopenharmony_ci * @extack: extended netdev ack structure
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * Callback for the devlink .port_unsplit operation.
72762306a36Sopenharmony_ci * Calls ice_devlink_port_split with split count set to 1.
72862306a36Sopenharmony_ci * There could be no FW option available with split count 1.
72962306a36Sopenharmony_ci *
73062306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
73162306a36Sopenharmony_ci */
73262306a36Sopenharmony_cistatic int
73362306a36Sopenharmony_ciice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port,
73462306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	return ice_devlink_port_split(devlink, port, 1, extack);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/**
74062306a36Sopenharmony_ci * ice_tear_down_devlink_rate_tree - removes devlink-rate exported tree
74162306a36Sopenharmony_ci * @pf: pf struct
74262306a36Sopenharmony_ci *
74362306a36Sopenharmony_ci * This function tears down tree exported during VF's creation.
74462306a36Sopenharmony_ci */
74562306a36Sopenharmony_civoid ice_tear_down_devlink_rate_tree(struct ice_pf *pf)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct devlink *devlink;
74862306a36Sopenharmony_ci	struct ice_vf *vf;
74962306a36Sopenharmony_ci	unsigned int bkt;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	devlink = priv_to_devlink(pf);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	devl_lock(devlink);
75462306a36Sopenharmony_ci	mutex_lock(&pf->vfs.table_lock);
75562306a36Sopenharmony_ci	ice_for_each_vf(pf, bkt, vf) {
75662306a36Sopenharmony_ci		if (vf->devlink_port.devlink_rate)
75762306a36Sopenharmony_ci			devl_rate_leaf_destroy(&vf->devlink_port);
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci	mutex_unlock(&pf->vfs.table_lock);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	devl_rate_nodes_destroy(devlink);
76262306a36Sopenharmony_ci	devl_unlock(devlink);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/**
76662306a36Sopenharmony_ci * ice_enable_custom_tx - try to enable custom Tx feature
76762306a36Sopenharmony_ci * @pf: pf struct
76862306a36Sopenharmony_ci *
76962306a36Sopenharmony_ci * This function tries to enable custom Tx feature,
77062306a36Sopenharmony_ci * it's not possible to enable it, if DCB or ADQ is active.
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cistatic bool ice_enable_custom_tx(struct ice_pf *pf)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct ice_port_info *pi = ice_get_main_vsi(pf)->port_info;
77562306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (pi->is_custom_tx_enabled)
77862306a36Sopenharmony_ci		/* already enabled, return true */
77962306a36Sopenharmony_ci		return true;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (ice_is_adq_active(pf)) {
78262306a36Sopenharmony_ci		dev_err(dev, "ADQ active, can't modify Tx scheduler tree\n");
78362306a36Sopenharmony_ci		return false;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (ice_is_dcb_active(pf)) {
78762306a36Sopenharmony_ci		dev_err(dev, "DCB active, can't modify Tx scheduler tree\n");
78862306a36Sopenharmony_ci		return false;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	pi->is_custom_tx_enabled = true;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	return true;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci/**
79762306a36Sopenharmony_ci * ice_traverse_tx_tree - traverse Tx scheduler tree
79862306a36Sopenharmony_ci * @devlink: devlink struct
79962306a36Sopenharmony_ci * @node: current node, used for recursion
80062306a36Sopenharmony_ci * @tc_node: tc_node struct, that is treated as a root
80162306a36Sopenharmony_ci * @pf: pf struct
80262306a36Sopenharmony_ci *
80362306a36Sopenharmony_ci * This function traverses Tx scheduler tree and exports
80462306a36Sopenharmony_ci * entire structure to the devlink-rate.
80562306a36Sopenharmony_ci */
80662306a36Sopenharmony_cistatic void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node *node,
80762306a36Sopenharmony_ci				 struct ice_sched_node *tc_node, struct ice_pf *pf)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	struct devlink_rate *rate_node = NULL;
81062306a36Sopenharmony_ci	struct ice_vf *vf;
81162306a36Sopenharmony_ci	int i;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (node->parent == tc_node) {
81462306a36Sopenharmony_ci		/* create root node */
81562306a36Sopenharmony_ci		rate_node = devl_rate_node_create(devlink, node, node->name, NULL);
81662306a36Sopenharmony_ci	} else if (node->vsi_handle &&
81762306a36Sopenharmony_ci		   pf->vsi[node->vsi_handle]->vf) {
81862306a36Sopenharmony_ci		vf = pf->vsi[node->vsi_handle]->vf;
81962306a36Sopenharmony_ci		if (!vf->devlink_port.devlink_rate)
82062306a36Sopenharmony_ci			/* leaf nodes doesn't have children
82162306a36Sopenharmony_ci			 * so we don't set rate_node
82262306a36Sopenharmony_ci			 */
82362306a36Sopenharmony_ci			devl_rate_leaf_create(&vf->devlink_port, node,
82462306a36Sopenharmony_ci					      node->parent->rate_node);
82562306a36Sopenharmony_ci	} else if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF &&
82662306a36Sopenharmony_ci		   node->parent->rate_node) {
82762306a36Sopenharmony_ci		rate_node = devl_rate_node_create(devlink, node, node->name,
82862306a36Sopenharmony_ci						  node->parent->rate_node);
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (rate_node && !IS_ERR(rate_node))
83262306a36Sopenharmony_ci		node->rate_node = rate_node;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	for (i = 0; i < node->num_children; i++)
83562306a36Sopenharmony_ci		ice_traverse_tx_tree(devlink, node->children[i], tc_node, pf);
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci/**
83962306a36Sopenharmony_ci * ice_devlink_rate_init_tx_topology - export Tx scheduler tree to devlink rate
84062306a36Sopenharmony_ci * @devlink: devlink struct
84162306a36Sopenharmony_ci * @vsi: main vsi struct
84262306a36Sopenharmony_ci *
84362306a36Sopenharmony_ci * This function finds a root node, then calls ice_traverse_tx tree, which
84462306a36Sopenharmony_ci * traverses the tree and exports it's contents to devlink rate.
84562306a36Sopenharmony_ci */
84662306a36Sopenharmony_ciint ice_devlink_rate_init_tx_topology(struct devlink *devlink, struct ice_vsi *vsi)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct ice_port_info *pi = vsi->port_info;
84962306a36Sopenharmony_ci	struct ice_sched_node *tc_node;
85062306a36Sopenharmony_ci	struct ice_pf *pf = vsi->back;
85162306a36Sopenharmony_ci	int i;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	tc_node = pi->root->children[0];
85462306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
85562306a36Sopenharmony_ci	devl_lock(devlink);
85662306a36Sopenharmony_ci	for (i = 0; i < tc_node->num_children; i++)
85762306a36Sopenharmony_ci		ice_traverse_tx_tree(devlink, tc_node->children[i], tc_node, pf);
85862306a36Sopenharmony_ci	devl_unlock(devlink);
85962306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	return 0;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * ice_set_object_tx_share - sets node scheduling parameter
86662306a36Sopenharmony_ci * @pi: devlink struct instance
86762306a36Sopenharmony_ci * @node: node struct instance
86862306a36Sopenharmony_ci * @bw: bandwidth in bytes per second
86962306a36Sopenharmony_ci * @extack: extended netdev ack structure
87062306a36Sopenharmony_ci *
87162306a36Sopenharmony_ci * This function sets ICE_MIN_BW scheduling BW limit.
87262306a36Sopenharmony_ci */
87362306a36Sopenharmony_cistatic int ice_set_object_tx_share(struct ice_port_info *pi, struct ice_sched_node *node,
87462306a36Sopenharmony_ci				   u64 bw, struct netlink_ext_ack *extack)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	int status;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
87962306a36Sopenharmony_ci	/* converts bytes per second to kilo bits per second */
88062306a36Sopenharmony_ci	node->tx_share = div_u64(bw, 125);
88162306a36Sopenharmony_ci	status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW, node->tx_share);
88262306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (status)
88562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_share");
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	return status;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci/**
89162306a36Sopenharmony_ci * ice_set_object_tx_max - sets node scheduling parameter
89262306a36Sopenharmony_ci * @pi: devlink struct instance
89362306a36Sopenharmony_ci * @node: node struct instance
89462306a36Sopenharmony_ci * @bw: bandwidth in bytes per second
89562306a36Sopenharmony_ci * @extack: extended netdev ack structure
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * This function sets ICE_MAX_BW scheduling BW limit.
89862306a36Sopenharmony_ci */
89962306a36Sopenharmony_cistatic int ice_set_object_tx_max(struct ice_port_info *pi, struct ice_sched_node *node,
90062306a36Sopenharmony_ci				 u64 bw, struct netlink_ext_ack *extack)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	int status;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
90562306a36Sopenharmony_ci	/* converts bytes per second value to kilo bits per second */
90662306a36Sopenharmony_ci	node->tx_max = div_u64(bw, 125);
90762306a36Sopenharmony_ci	status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW, node->tx_max);
90862306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (status)
91162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_max");
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	return status;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/**
91762306a36Sopenharmony_ci * ice_set_object_tx_priority - sets node scheduling parameter
91862306a36Sopenharmony_ci * @pi: devlink struct instance
91962306a36Sopenharmony_ci * @node: node struct instance
92062306a36Sopenharmony_ci * @priority: value representing priority for strict priority arbitration
92162306a36Sopenharmony_ci * @extack: extended netdev ack structure
92262306a36Sopenharmony_ci *
92362306a36Sopenharmony_ci * This function sets priority of node among siblings.
92462306a36Sopenharmony_ci */
92562306a36Sopenharmony_cistatic int ice_set_object_tx_priority(struct ice_port_info *pi, struct ice_sched_node *node,
92662306a36Sopenharmony_ci				      u32 priority, struct netlink_ext_ack *extack)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	int status;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (priority >= 8) {
93162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Priority should be less than 8");
93262306a36Sopenharmony_ci		return -EINVAL;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
93662306a36Sopenharmony_ci	node->tx_priority = priority;
93762306a36Sopenharmony_ci	status = ice_sched_set_node_priority(pi, node, node->tx_priority);
93862306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (status)
94162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_priority");
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	return status;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci/**
94762306a36Sopenharmony_ci * ice_set_object_tx_weight - sets node scheduling parameter
94862306a36Sopenharmony_ci * @pi: devlink struct instance
94962306a36Sopenharmony_ci * @node: node struct instance
95062306a36Sopenharmony_ci * @weight: value represeting relative weight for WFQ arbitration
95162306a36Sopenharmony_ci * @extack: extended netdev ack structure
95262306a36Sopenharmony_ci *
95362306a36Sopenharmony_ci * This function sets node weight for WFQ algorithm.
95462306a36Sopenharmony_ci */
95562306a36Sopenharmony_cistatic int ice_set_object_tx_weight(struct ice_port_info *pi, struct ice_sched_node *node,
95662306a36Sopenharmony_ci				    u32 weight, struct netlink_ext_ack *extack)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	int status;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (weight > 200 || weight < 1) {
96162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Weight must be between 1 and 200");
96262306a36Sopenharmony_ci		return -EINVAL;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
96662306a36Sopenharmony_ci	node->tx_weight = weight;
96762306a36Sopenharmony_ci	status = ice_sched_set_node_weight(pi, node, node->tx_weight);
96862306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (status)
97162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can't set scheduling node tx_weight");
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	return status;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci/**
97762306a36Sopenharmony_ci * ice_get_pi_from_dev_rate - get port info from devlink_rate
97862306a36Sopenharmony_ci * @rate_node: devlink struct instance
97962306a36Sopenharmony_ci *
98062306a36Sopenharmony_ci * This function returns corresponding port_info struct of devlink_rate
98162306a36Sopenharmony_ci */
98262306a36Sopenharmony_cistatic struct ice_port_info *ice_get_pi_from_dev_rate(struct devlink_rate *rate_node)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(rate_node->devlink);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	return ice_get_main_vsi(pf)->port_info;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic int ice_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv,
99062306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct ice_sched_node *node;
99362306a36Sopenharmony_ci	struct ice_port_info *pi;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	pi = ice_get_pi_from_dev_rate(rate_node);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
99862306a36Sopenharmony_ci		return -EBUSY;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/* preallocate memory for ice_sched_node */
100162306a36Sopenharmony_ci	node = devm_kzalloc(ice_hw_to_dev(pi->hw), sizeof(*node), GFP_KERNEL);
100262306a36Sopenharmony_ci	*priv = node;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	return 0;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic int ice_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv,
100862306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct ice_sched_node *node, *tc_node;
101162306a36Sopenharmony_ci	struct ice_port_info *pi;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	pi = ice_get_pi_from_dev_rate(rate_node);
101462306a36Sopenharmony_ci	tc_node = pi->root->children[0];
101562306a36Sopenharmony_ci	node = priv;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	if (!rate_node->parent || !node || tc_node == node || !extack)
101862306a36Sopenharmony_ci		return 0;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
102162306a36Sopenharmony_ci		return -EBUSY;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* can't allow to delete a node with children */
102462306a36Sopenharmony_ci	if (node->num_children)
102562306a36Sopenharmony_ci		return -EINVAL;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
102862306a36Sopenharmony_ci	ice_free_sched_node(pi, node);
102962306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	return 0;
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_cistatic int ice_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv,
103562306a36Sopenharmony_ci					    u64 tx_max, struct netlink_ext_ack *extack)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink)))
104062306a36Sopenharmony_ci		return -EBUSY;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (!node)
104362306a36Sopenharmony_ci		return 0;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_leaf),
104662306a36Sopenharmony_ci				     node, tx_max, extack);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic int ice_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv,
105062306a36Sopenharmony_ci					      u64 tx_share, struct netlink_ext_ack *extack)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink)))
105562306a36Sopenharmony_ci		return -EBUSY;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (!node)
105862306a36Sopenharmony_ci		return 0;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_leaf), node,
106162306a36Sopenharmony_ci				       tx_share, extack);
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int ice_devlink_rate_leaf_tx_priority_set(struct devlink_rate *rate_leaf, void *priv,
106562306a36Sopenharmony_ci						 u32 tx_priority, struct netlink_ext_ack *extack)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink)))
107062306a36Sopenharmony_ci		return -EBUSY;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (!node)
107362306a36Sopenharmony_ci		return 0;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_leaf), node,
107662306a36Sopenharmony_ci					  tx_priority, extack);
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int ice_devlink_rate_leaf_tx_weight_set(struct devlink_rate *rate_leaf, void *priv,
108062306a36Sopenharmony_ci					       u32 tx_weight, struct netlink_ext_ack *extack)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_leaf->devlink)))
108562306a36Sopenharmony_ci		return -EBUSY;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (!node)
108862306a36Sopenharmony_ci		return 0;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_leaf), node,
109162306a36Sopenharmony_ci					tx_weight, extack);
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic int ice_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv,
109562306a36Sopenharmony_ci					    u64 tx_max, struct netlink_ext_ack *extack)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
110062306a36Sopenharmony_ci		return -EBUSY;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (!node)
110362306a36Sopenharmony_ci		return 0;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	return ice_set_object_tx_max(ice_get_pi_from_dev_rate(rate_node),
110662306a36Sopenharmony_ci				     node, tx_max, extack);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int ice_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv,
111062306a36Sopenharmony_ci					      u64 tx_share, struct netlink_ext_ack *extack)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
111562306a36Sopenharmony_ci		return -EBUSY;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (!node)
111862306a36Sopenharmony_ci		return 0;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	return ice_set_object_tx_share(ice_get_pi_from_dev_rate(rate_node),
112162306a36Sopenharmony_ci				       node, tx_share, extack);
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic int ice_devlink_rate_node_tx_priority_set(struct devlink_rate *rate_node, void *priv,
112562306a36Sopenharmony_ci						 u32 tx_priority, struct netlink_ext_ack *extack)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
113062306a36Sopenharmony_ci		return -EBUSY;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (!node)
113362306a36Sopenharmony_ci		return 0;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	return ice_set_object_tx_priority(ice_get_pi_from_dev_rate(rate_node),
113662306a36Sopenharmony_ci					  node, tx_priority, extack);
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int ice_devlink_rate_node_tx_weight_set(struct devlink_rate *rate_node, void *priv,
114062306a36Sopenharmony_ci					       u32 tx_weight, struct netlink_ext_ack *extack)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct ice_sched_node *node = priv;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(rate_node->devlink)))
114562306a36Sopenharmony_ci		return -EBUSY;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (!node)
114862306a36Sopenharmony_ci		return 0;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	return ice_set_object_tx_weight(ice_get_pi_from_dev_rate(rate_node),
115162306a36Sopenharmony_ci					node, tx_weight, extack);
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic int ice_devlink_set_parent(struct devlink_rate *devlink_rate,
115562306a36Sopenharmony_ci				  struct devlink_rate *parent,
115662306a36Sopenharmony_ci				  void *priv, void *parent_priv,
115762306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	struct ice_port_info *pi = ice_get_pi_from_dev_rate(devlink_rate);
116062306a36Sopenharmony_ci	struct ice_sched_node *tc_node, *node, *parent_node;
116162306a36Sopenharmony_ci	u16 num_nodes_added;
116262306a36Sopenharmony_ci	u32 first_node_teid;
116362306a36Sopenharmony_ci	u32 node_teid;
116462306a36Sopenharmony_ci	int status;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	tc_node = pi->root->children[0];
116762306a36Sopenharmony_ci	node = priv;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!extack)
117062306a36Sopenharmony_ci		return 0;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (!ice_enable_custom_tx(devlink_priv(devlink_rate->devlink)))
117362306a36Sopenharmony_ci		return -EBUSY;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (!parent) {
117662306a36Sopenharmony_ci		if (!node || tc_node == node || node->num_children)
117762306a36Sopenharmony_ci			return -EINVAL;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		mutex_lock(&pi->sched_lock);
118062306a36Sopenharmony_ci		ice_free_sched_node(pi, node);
118162306a36Sopenharmony_ci		mutex_unlock(&pi->sched_lock);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci		return 0;
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	parent_node = parent_priv;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* if the node doesn't exist, create it */
118962306a36Sopenharmony_ci	if (!node->parent) {
119062306a36Sopenharmony_ci		mutex_lock(&pi->sched_lock);
119162306a36Sopenharmony_ci		status = ice_sched_add_elems(pi, tc_node, parent_node,
119262306a36Sopenharmony_ci					     parent_node->tx_sched_layer + 1,
119362306a36Sopenharmony_ci					     1, &num_nodes_added, &first_node_teid,
119462306a36Sopenharmony_ci					     &node);
119562306a36Sopenharmony_ci		mutex_unlock(&pi->sched_lock);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci		if (status) {
119862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Can't add a new node");
119962306a36Sopenharmony_ci			return status;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		if (devlink_rate->tx_share)
120362306a36Sopenharmony_ci			ice_set_object_tx_share(pi, node, devlink_rate->tx_share, extack);
120462306a36Sopenharmony_ci		if (devlink_rate->tx_max)
120562306a36Sopenharmony_ci			ice_set_object_tx_max(pi, node, devlink_rate->tx_max, extack);
120662306a36Sopenharmony_ci		if (devlink_rate->tx_priority)
120762306a36Sopenharmony_ci			ice_set_object_tx_priority(pi, node, devlink_rate->tx_priority, extack);
120862306a36Sopenharmony_ci		if (devlink_rate->tx_weight)
120962306a36Sopenharmony_ci			ice_set_object_tx_weight(pi, node, devlink_rate->tx_weight, extack);
121062306a36Sopenharmony_ci	} else {
121162306a36Sopenharmony_ci		node_teid = le32_to_cpu(node->info.node_teid);
121262306a36Sopenharmony_ci		mutex_lock(&pi->sched_lock);
121362306a36Sopenharmony_ci		status = ice_sched_move_nodes(pi, parent_node, 1, &node_teid);
121462306a36Sopenharmony_ci		mutex_unlock(&pi->sched_lock);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		if (status)
121762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Can't move existing node to a new parent");
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	return status;
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci/**
122462306a36Sopenharmony_ci * ice_devlink_reload_up - do reload up after reinit
122562306a36Sopenharmony_ci * @devlink: pointer to the devlink instance reloading
122662306a36Sopenharmony_ci * @action: the action requested
122762306a36Sopenharmony_ci * @limit: limits imposed by userspace, such as not resetting
122862306a36Sopenharmony_ci * @actions_performed: on return, indicate what actions actually performed
122962306a36Sopenharmony_ci * @extack: netlink extended ACK structure
123062306a36Sopenharmony_ci */
123162306a36Sopenharmony_cistatic int
123262306a36Sopenharmony_ciice_devlink_reload_up(struct devlink *devlink,
123362306a36Sopenharmony_ci		      enum devlink_reload_action action,
123462306a36Sopenharmony_ci		      enum devlink_reload_limit limit,
123562306a36Sopenharmony_ci		      u32 *actions_performed,
123662306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	switch (action) {
124162306a36Sopenharmony_ci	case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
124262306a36Sopenharmony_ci		*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
124362306a36Sopenharmony_ci		return ice_load(pf);
124462306a36Sopenharmony_ci	case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
124562306a36Sopenharmony_ci		*actions_performed = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE);
124662306a36Sopenharmony_ci		return ice_devlink_reload_empr_finish(pf, extack);
124762306a36Sopenharmony_ci	default:
124862306a36Sopenharmony_ci		WARN_ON(1);
124962306a36Sopenharmony_ci		return -EOPNOTSUPP;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic const struct devlink_ops ice_devlink_ops = {
125462306a36Sopenharmony_ci	.supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
125562306a36Sopenharmony_ci	.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
125662306a36Sopenharmony_ci			  BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
125762306a36Sopenharmony_ci	.reload_down = ice_devlink_reload_down,
125862306a36Sopenharmony_ci	.reload_up = ice_devlink_reload_up,
125962306a36Sopenharmony_ci	.eswitch_mode_get = ice_eswitch_mode_get,
126062306a36Sopenharmony_ci	.eswitch_mode_set = ice_eswitch_mode_set,
126162306a36Sopenharmony_ci	.info_get = ice_devlink_info_get,
126262306a36Sopenharmony_ci	.flash_update = ice_devlink_flash_update,
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	.rate_node_new = ice_devlink_rate_node_new,
126562306a36Sopenharmony_ci	.rate_node_del = ice_devlink_rate_node_del,
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	.rate_leaf_tx_max_set = ice_devlink_rate_leaf_tx_max_set,
126862306a36Sopenharmony_ci	.rate_leaf_tx_share_set = ice_devlink_rate_leaf_tx_share_set,
126962306a36Sopenharmony_ci	.rate_leaf_tx_priority_set = ice_devlink_rate_leaf_tx_priority_set,
127062306a36Sopenharmony_ci	.rate_leaf_tx_weight_set = ice_devlink_rate_leaf_tx_weight_set,
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	.rate_node_tx_max_set = ice_devlink_rate_node_tx_max_set,
127362306a36Sopenharmony_ci	.rate_node_tx_share_set = ice_devlink_rate_node_tx_share_set,
127462306a36Sopenharmony_ci	.rate_node_tx_priority_set = ice_devlink_rate_node_tx_priority_set,
127562306a36Sopenharmony_ci	.rate_node_tx_weight_set = ice_devlink_rate_node_tx_weight_set,
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	.rate_leaf_parent_set = ice_devlink_set_parent,
127862306a36Sopenharmony_ci	.rate_node_parent_set = ice_devlink_set_parent,
127962306a36Sopenharmony_ci};
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic int
128262306a36Sopenharmony_ciice_devlink_enable_roce_get(struct devlink *devlink, u32 id,
128362306a36Sopenharmony_ci			    struct devlink_param_gset_ctx *ctx)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	ctx->val.vbool = pf->rdma_mode & IIDC_RDMA_PROTOCOL_ROCEV2 ? true : false;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return 0;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic int
129362306a36Sopenharmony_ciice_devlink_enable_roce_set(struct devlink *devlink, u32 id,
129462306a36Sopenharmony_ci			    struct devlink_param_gset_ctx *ctx)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
129762306a36Sopenharmony_ci	bool roce_ena = ctx->val.vbool;
129862306a36Sopenharmony_ci	int ret;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	if (!roce_ena) {
130162306a36Sopenharmony_ci		ice_unplug_aux_dev(pf);
130262306a36Sopenharmony_ci		pf->rdma_mode &= ~IIDC_RDMA_PROTOCOL_ROCEV2;
130362306a36Sopenharmony_ci		return 0;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	pf->rdma_mode |= IIDC_RDMA_PROTOCOL_ROCEV2;
130762306a36Sopenharmony_ci	ret = ice_plug_aux_dev(pf);
130862306a36Sopenharmony_ci	if (ret)
130962306a36Sopenharmony_ci		pf->rdma_mode &= ~IIDC_RDMA_PROTOCOL_ROCEV2;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	return ret;
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic int
131562306a36Sopenharmony_ciice_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
131662306a36Sopenharmony_ci				 union devlink_param_value val,
131762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (!test_bit(ICE_FLAG_RDMA_ENA, pf->flags))
132262306a36Sopenharmony_ci		return -EOPNOTSUPP;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (pf->rdma_mode & IIDC_RDMA_PROTOCOL_IWARP) {
132562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "iWARP is currently enabled. This device cannot enable iWARP and RoCEv2 simultaneously");
132662306a36Sopenharmony_ci		return -EOPNOTSUPP;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	return 0;
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic int
133362306a36Sopenharmony_ciice_devlink_enable_iw_get(struct devlink *devlink, u32 id,
133462306a36Sopenharmony_ci			  struct devlink_param_gset_ctx *ctx)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	ctx->val.vbool = pf->rdma_mode & IIDC_RDMA_PROTOCOL_IWARP;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	return 0;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic int
134462306a36Sopenharmony_ciice_devlink_enable_iw_set(struct devlink *devlink, u32 id,
134562306a36Sopenharmony_ci			  struct devlink_param_gset_ctx *ctx)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
134862306a36Sopenharmony_ci	bool iw_ena = ctx->val.vbool;
134962306a36Sopenharmony_ci	int ret;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	if (!iw_ena) {
135262306a36Sopenharmony_ci		ice_unplug_aux_dev(pf);
135362306a36Sopenharmony_ci		pf->rdma_mode &= ~IIDC_RDMA_PROTOCOL_IWARP;
135462306a36Sopenharmony_ci		return 0;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	pf->rdma_mode |= IIDC_RDMA_PROTOCOL_IWARP;
135862306a36Sopenharmony_ci	ret = ice_plug_aux_dev(pf);
135962306a36Sopenharmony_ci	if (ret)
136062306a36Sopenharmony_ci		pf->rdma_mode &= ~IIDC_RDMA_PROTOCOL_IWARP;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	return ret;
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic int
136662306a36Sopenharmony_ciice_devlink_enable_iw_validate(struct devlink *devlink, u32 id,
136762306a36Sopenharmony_ci			       union devlink_param_value val,
136862306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	if (!test_bit(ICE_FLAG_RDMA_ENA, pf->flags))
137362306a36Sopenharmony_ci		return -EOPNOTSUPP;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	if (pf->rdma_mode & IIDC_RDMA_PROTOCOL_ROCEV2) {
137662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "RoCEv2 is currently enabled. This device cannot enable iWARP and RoCEv2 simultaneously");
137762306a36Sopenharmony_ci		return -EOPNOTSUPP;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	return 0;
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic const struct devlink_param ice_devlink_params[] = {
138462306a36Sopenharmony_ci	DEVLINK_PARAM_GENERIC(ENABLE_ROCE, BIT(DEVLINK_PARAM_CMODE_RUNTIME),
138562306a36Sopenharmony_ci			      ice_devlink_enable_roce_get,
138662306a36Sopenharmony_ci			      ice_devlink_enable_roce_set,
138762306a36Sopenharmony_ci			      ice_devlink_enable_roce_validate),
138862306a36Sopenharmony_ci	DEVLINK_PARAM_GENERIC(ENABLE_IWARP, BIT(DEVLINK_PARAM_CMODE_RUNTIME),
138962306a36Sopenharmony_ci			      ice_devlink_enable_iw_get,
139062306a36Sopenharmony_ci			      ice_devlink_enable_iw_set,
139162306a36Sopenharmony_ci			      ice_devlink_enable_iw_validate),
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci};
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cistatic void ice_devlink_free(void *devlink_ptr)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	devlink_free((struct devlink *)devlink_ptr);
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci/**
140162306a36Sopenharmony_ci * ice_allocate_pf - Allocate devlink and return PF structure pointer
140262306a36Sopenharmony_ci * @dev: the device to allocate for
140362306a36Sopenharmony_ci *
140462306a36Sopenharmony_ci * Allocate a devlink instance for this device and return the private area as
140562306a36Sopenharmony_ci * the PF structure. The devlink memory is kept track of through devres by
140662306a36Sopenharmony_ci * adding an action to remove it when unwinding.
140762306a36Sopenharmony_ci */
140862306a36Sopenharmony_cistruct ice_pf *ice_allocate_pf(struct device *dev)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct devlink *devlink;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	devlink = devlink_alloc(&ice_devlink_ops, sizeof(struct ice_pf), dev);
141362306a36Sopenharmony_ci	if (!devlink)
141462306a36Sopenharmony_ci		return NULL;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	/* Add an action to teardown the devlink when unwinding the driver */
141762306a36Sopenharmony_ci	if (devm_add_action_or_reset(dev, ice_devlink_free, devlink))
141862306a36Sopenharmony_ci		return NULL;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	return devlink_priv(devlink);
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci/**
142462306a36Sopenharmony_ci * ice_devlink_register - Register devlink interface for this PF
142562306a36Sopenharmony_ci * @pf: the PF to register the devlink for.
142662306a36Sopenharmony_ci *
142762306a36Sopenharmony_ci * Register the devlink instance associated with this physical function.
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
143062306a36Sopenharmony_ci */
143162306a36Sopenharmony_civoid ice_devlink_register(struct ice_pf *pf)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(pf);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	devlink_register(devlink);
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci/**
143962306a36Sopenharmony_ci * ice_devlink_unregister - Unregister devlink resources for this PF.
144062306a36Sopenharmony_ci * @pf: the PF structure to cleanup
144162306a36Sopenharmony_ci *
144262306a36Sopenharmony_ci * Releases resources used by devlink and cleans up associated memory.
144362306a36Sopenharmony_ci */
144462306a36Sopenharmony_civoid ice_devlink_unregister(struct ice_pf *pf)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	devlink_unregister(priv_to_devlink(pf));
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci/**
145062306a36Sopenharmony_ci * ice_devlink_set_switch_id - Set unique switch id based on pci dsn
145162306a36Sopenharmony_ci * @pf: the PF to create a devlink port for
145262306a36Sopenharmony_ci * @ppid: struct with switch id information
145362306a36Sopenharmony_ci */
145462306a36Sopenharmony_cistatic void
145562306a36Sopenharmony_ciice_devlink_set_switch_id(struct ice_pf *pf, struct netdev_phys_item_id *ppid)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct pci_dev *pdev = pf->pdev;
145862306a36Sopenharmony_ci	u64 id;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	id = pci_get_dsn(pdev);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	ppid->id_len = sizeof(id);
146362306a36Sopenharmony_ci	put_unaligned_be64(id, &ppid->id);
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ciint ice_devlink_register_params(struct ice_pf *pf)
146762306a36Sopenharmony_ci{
146862306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(pf);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	return devlink_params_register(devlink, ice_devlink_params,
147162306a36Sopenharmony_ci				       ARRAY_SIZE(ice_devlink_params));
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_civoid ice_devlink_unregister_params(struct ice_pf *pf)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	devlink_params_unregister(priv_to_devlink(pf), ice_devlink_params,
147762306a36Sopenharmony_ci				  ARRAY_SIZE(ice_devlink_params));
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci/**
148162306a36Sopenharmony_ci * ice_devlink_set_port_split_options - Set port split options
148262306a36Sopenharmony_ci * @pf: the PF to set port split options
148362306a36Sopenharmony_ci * @attrs: devlink attributes
148462306a36Sopenharmony_ci *
148562306a36Sopenharmony_ci * Sets devlink port split options based on available FW port options
148662306a36Sopenharmony_ci */
148762306a36Sopenharmony_cistatic void
148862306a36Sopenharmony_ciice_devlink_set_port_split_options(struct ice_pf *pf,
148962306a36Sopenharmony_ci				   struct devlink_port_attrs *attrs)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
149262306a36Sopenharmony_ci	u8 i, active_idx, pending_idx, option_count = ICE_AQC_PORT_OPT_MAX;
149362306a36Sopenharmony_ci	bool active_valid, pending_valid;
149462306a36Sopenharmony_ci	int status;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	status = ice_aq_get_port_options(&pf->hw, options, &option_count,
149762306a36Sopenharmony_ci					 0, true, &active_idx, &active_valid,
149862306a36Sopenharmony_ci					 &pending_idx, &pending_valid);
149962306a36Sopenharmony_ci	if (status) {
150062306a36Sopenharmony_ci		dev_dbg(ice_pf_to_dev(pf), "Couldn't read port split options, err = %d\n",
150162306a36Sopenharmony_ci			status);
150262306a36Sopenharmony_ci		return;
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	/* find the biggest available port split count */
150662306a36Sopenharmony_ci	for (i = 0; i < option_count; i++)
150762306a36Sopenharmony_ci		attrs->lanes = max_t(int, attrs->lanes, options[i].pmd);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	attrs->splittable = attrs->lanes ? 1 : 0;
151062306a36Sopenharmony_ci	ice_active_port_option = active_idx;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic const struct devlink_port_ops ice_devlink_port_ops = {
151462306a36Sopenharmony_ci	.port_split = ice_devlink_port_split,
151562306a36Sopenharmony_ci	.port_unsplit = ice_devlink_port_unsplit,
151662306a36Sopenharmony_ci};
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci/**
151962306a36Sopenharmony_ci * ice_devlink_create_pf_port - Create a devlink port for this PF
152062306a36Sopenharmony_ci * @pf: the PF to create a devlink port for
152162306a36Sopenharmony_ci *
152262306a36Sopenharmony_ci * Create and register a devlink_port for this PF.
152362306a36Sopenharmony_ci *
152462306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
152562306a36Sopenharmony_ci */
152662306a36Sopenharmony_ciint ice_devlink_create_pf_port(struct ice_pf *pf)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct devlink_port_attrs attrs = {};
152962306a36Sopenharmony_ci	struct devlink_port *devlink_port;
153062306a36Sopenharmony_ci	struct devlink *devlink;
153162306a36Sopenharmony_ci	struct ice_vsi *vsi;
153262306a36Sopenharmony_ci	struct device *dev;
153362306a36Sopenharmony_ci	int err;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	dev = ice_pf_to_dev(pf);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	devlink_port = &pf->devlink_port;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	vsi = ice_get_main_vsi(pf);
154062306a36Sopenharmony_ci	if (!vsi)
154162306a36Sopenharmony_ci		return -EIO;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
154462306a36Sopenharmony_ci	attrs.phys.port_number = pf->hw.bus.func;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/* As FW supports only port split options for whole device,
154762306a36Sopenharmony_ci	 * set port split options only for first PF.
154862306a36Sopenharmony_ci	 */
154962306a36Sopenharmony_ci	if (pf->hw.pf_id == 0)
155062306a36Sopenharmony_ci		ice_devlink_set_port_split_options(pf, &attrs);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ice_devlink_set_switch_id(pf, &attrs.switch_id);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	devlink_port_attrs_set(devlink_port, &attrs);
155562306a36Sopenharmony_ci	devlink = priv_to_devlink(pf);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	err = devlink_port_register_with_ops(devlink, devlink_port, vsi->idx,
155862306a36Sopenharmony_ci					     &ice_devlink_port_ops);
155962306a36Sopenharmony_ci	if (err) {
156062306a36Sopenharmony_ci		dev_err(dev, "Failed to create devlink port for PF %d, error %d\n",
156162306a36Sopenharmony_ci			pf->hw.pf_id, err);
156262306a36Sopenharmony_ci		return err;
156362306a36Sopenharmony_ci	}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	return 0;
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci/**
156962306a36Sopenharmony_ci * ice_devlink_destroy_pf_port - Destroy the devlink_port for this PF
157062306a36Sopenharmony_ci * @pf: the PF to cleanup
157162306a36Sopenharmony_ci *
157262306a36Sopenharmony_ci * Unregisters the devlink_port structure associated with this PF.
157362306a36Sopenharmony_ci */
157462306a36Sopenharmony_civoid ice_devlink_destroy_pf_port(struct ice_pf *pf)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	devlink_port_unregister(&pf->devlink_port);
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci/**
158062306a36Sopenharmony_ci * ice_devlink_create_vf_port - Create a devlink port for this VF
158162306a36Sopenharmony_ci * @vf: the VF to create a port for
158262306a36Sopenharmony_ci *
158362306a36Sopenharmony_ci * Create and register a devlink_port for this VF.
158462306a36Sopenharmony_ci *
158562306a36Sopenharmony_ci * Return: zero on success or an error code on failure.
158662306a36Sopenharmony_ci */
158762306a36Sopenharmony_ciint ice_devlink_create_vf_port(struct ice_vf *vf)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	struct devlink_port_attrs attrs = {};
159062306a36Sopenharmony_ci	struct devlink_port *devlink_port;
159162306a36Sopenharmony_ci	struct devlink *devlink;
159262306a36Sopenharmony_ci	struct ice_vsi *vsi;
159362306a36Sopenharmony_ci	struct device *dev;
159462306a36Sopenharmony_ci	struct ice_pf *pf;
159562306a36Sopenharmony_ci	int err;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	pf = vf->pf;
159862306a36Sopenharmony_ci	dev = ice_pf_to_dev(pf);
159962306a36Sopenharmony_ci	devlink_port = &vf->devlink_port;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	vsi = ice_get_vf_vsi(vf);
160262306a36Sopenharmony_ci	if (!vsi)
160362306a36Sopenharmony_ci		return -EINVAL;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF;
160662306a36Sopenharmony_ci	attrs.pci_vf.pf = pf->hw.bus.func;
160762306a36Sopenharmony_ci	attrs.pci_vf.vf = vf->vf_id;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	ice_devlink_set_switch_id(pf, &attrs.switch_id);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	devlink_port_attrs_set(devlink_port, &attrs);
161262306a36Sopenharmony_ci	devlink = priv_to_devlink(pf);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	err = devlink_port_register(devlink, devlink_port, vsi->idx);
161562306a36Sopenharmony_ci	if (err) {
161662306a36Sopenharmony_ci		dev_err(dev, "Failed to create devlink port for VF %d, error %d\n",
161762306a36Sopenharmony_ci			vf->vf_id, err);
161862306a36Sopenharmony_ci		return err;
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return 0;
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci/**
162562306a36Sopenharmony_ci * ice_devlink_destroy_vf_port - Destroy the devlink_port for this VF
162662306a36Sopenharmony_ci * @vf: the VF to cleanup
162762306a36Sopenharmony_ci *
162862306a36Sopenharmony_ci * Unregisters the devlink_port structure associated with this VF.
162962306a36Sopenharmony_ci */
163062306a36Sopenharmony_civoid ice_devlink_destroy_vf_port(struct ice_vf *vf)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	devl_rate_leaf_destroy(&vf->devlink_port);
163362306a36Sopenharmony_ci	devlink_port_unregister(&vf->devlink_port);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci#define ICE_DEVLINK_READ_BLK_SIZE (1024 * 1024)
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cistatic const struct devlink_region_ops ice_nvm_region_ops;
163962306a36Sopenharmony_cistatic const struct devlink_region_ops ice_sram_region_ops;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci/**
164262306a36Sopenharmony_ci * ice_devlink_nvm_snapshot - Capture a snapshot of the NVM flash contents
164362306a36Sopenharmony_ci * @devlink: the devlink instance
164462306a36Sopenharmony_ci * @ops: the devlink region to snapshot
164562306a36Sopenharmony_ci * @extack: extended ACK response structure
164662306a36Sopenharmony_ci * @data: on exit points to snapshot data buffer
164762306a36Sopenharmony_ci *
164862306a36Sopenharmony_ci * This function is called in response to a DEVLINK_CMD_REGION_NEW for either
164962306a36Sopenharmony_ci * the nvm-flash or shadow-ram region.
165062306a36Sopenharmony_ci *
165162306a36Sopenharmony_ci * It captures a snapshot of the NVM or Shadow RAM flash contents. This
165262306a36Sopenharmony_ci * snapshot can then later be viewed via the DEVLINK_CMD_REGION_READ netlink
165362306a36Sopenharmony_ci * interface.
165462306a36Sopenharmony_ci *
165562306a36Sopenharmony_ci * @returns zero on success, and updates the data pointer. Returns a non-zero
165662306a36Sopenharmony_ci * error code on failure.
165762306a36Sopenharmony_ci */
165862306a36Sopenharmony_cistatic int ice_devlink_nvm_snapshot(struct devlink *devlink,
165962306a36Sopenharmony_ci				    const struct devlink_region_ops *ops,
166062306a36Sopenharmony_ci				    struct netlink_ext_ack *extack, u8 **data)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
166362306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
166462306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
166562306a36Sopenharmony_ci	bool read_shadow_ram;
166662306a36Sopenharmony_ci	u8 *nvm_data, *tmp, i;
166762306a36Sopenharmony_ci	u32 nvm_size, left;
166862306a36Sopenharmony_ci	s8 num_blks;
166962306a36Sopenharmony_ci	int status;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (ops == &ice_nvm_region_ops) {
167262306a36Sopenharmony_ci		read_shadow_ram = false;
167362306a36Sopenharmony_ci		nvm_size = hw->flash.flash_size;
167462306a36Sopenharmony_ci	} else if (ops == &ice_sram_region_ops) {
167562306a36Sopenharmony_ci		read_shadow_ram = true;
167662306a36Sopenharmony_ci		nvm_size = hw->flash.sr_words * 2u;
167762306a36Sopenharmony_ci	} else {
167862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function");
167962306a36Sopenharmony_ci		return -EOPNOTSUPP;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	nvm_data = vzalloc(nvm_size);
168362306a36Sopenharmony_ci	if (!nvm_data)
168462306a36Sopenharmony_ci		return -ENOMEM;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	num_blks = DIV_ROUND_UP(nvm_size, ICE_DEVLINK_READ_BLK_SIZE);
168762306a36Sopenharmony_ci	tmp = nvm_data;
168862306a36Sopenharmony_ci	left = nvm_size;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* Some systems take longer to read the NVM than others which causes the
169162306a36Sopenharmony_ci	 * FW to reclaim the NVM lock before the entire NVM has been read. Fix
169262306a36Sopenharmony_ci	 * this by breaking the reads of the NVM into smaller chunks that will
169362306a36Sopenharmony_ci	 * probably not take as long. This has some overhead since we are
169462306a36Sopenharmony_ci	 * increasing the number of AQ commands, but it should always work
169562306a36Sopenharmony_ci	 */
169662306a36Sopenharmony_ci	for (i = 0; i < num_blks; i++) {
169762306a36Sopenharmony_ci		u32 read_sz = min_t(u32, ICE_DEVLINK_READ_BLK_SIZE, left);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci		status = ice_acquire_nvm(hw, ICE_RES_READ);
170062306a36Sopenharmony_ci		if (status) {
170162306a36Sopenharmony_ci			dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
170262306a36Sopenharmony_ci				status, hw->adminq.sq_last_status);
170362306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
170462306a36Sopenharmony_ci			vfree(nvm_data);
170562306a36Sopenharmony_ci			return -EIO;
170662306a36Sopenharmony_ci		}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		status = ice_read_flat_nvm(hw, i * ICE_DEVLINK_READ_BLK_SIZE,
170962306a36Sopenharmony_ci					   &read_sz, tmp, read_shadow_ram);
171062306a36Sopenharmony_ci		if (status) {
171162306a36Sopenharmony_ci			dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
171262306a36Sopenharmony_ci				read_sz, status, hw->adminq.sq_last_status);
171362306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
171462306a36Sopenharmony_ci			ice_release_nvm(hw);
171562306a36Sopenharmony_ci			vfree(nvm_data);
171662306a36Sopenharmony_ci			return -EIO;
171762306a36Sopenharmony_ci		}
171862306a36Sopenharmony_ci		ice_release_nvm(hw);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		tmp += read_sz;
172162306a36Sopenharmony_ci		left -= read_sz;
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	*data = nvm_data;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	return 0;
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci/**
173062306a36Sopenharmony_ci * ice_devlink_nvm_read - Read a portion of NVM flash contents
173162306a36Sopenharmony_ci * @devlink: the devlink instance
173262306a36Sopenharmony_ci * @ops: the devlink region to snapshot
173362306a36Sopenharmony_ci * @extack: extended ACK response structure
173462306a36Sopenharmony_ci * @offset: the offset to start at
173562306a36Sopenharmony_ci * @size: the amount to read
173662306a36Sopenharmony_ci * @data: the data buffer to read into
173762306a36Sopenharmony_ci *
173862306a36Sopenharmony_ci * This function is called in response to DEVLINK_CMD_REGION_READ to directly
173962306a36Sopenharmony_ci * read a section of the NVM contents.
174062306a36Sopenharmony_ci *
174162306a36Sopenharmony_ci * It reads from either the nvm-flash or shadow-ram region contents.
174262306a36Sopenharmony_ci *
174362306a36Sopenharmony_ci * @returns zero on success, and updates the data pointer. Returns a non-zero
174462306a36Sopenharmony_ci * error code on failure.
174562306a36Sopenharmony_ci */
174662306a36Sopenharmony_cistatic int ice_devlink_nvm_read(struct devlink *devlink,
174762306a36Sopenharmony_ci				const struct devlink_region_ops *ops,
174862306a36Sopenharmony_ci				struct netlink_ext_ack *extack,
174962306a36Sopenharmony_ci				u64 offset, u32 size, u8 *data)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
175262306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
175362306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
175462306a36Sopenharmony_ci	bool read_shadow_ram;
175562306a36Sopenharmony_ci	u64 nvm_size;
175662306a36Sopenharmony_ci	int status;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (ops == &ice_nvm_region_ops) {
175962306a36Sopenharmony_ci		read_shadow_ram = false;
176062306a36Sopenharmony_ci		nvm_size = hw->flash.flash_size;
176162306a36Sopenharmony_ci	} else if (ops == &ice_sram_region_ops) {
176262306a36Sopenharmony_ci		read_shadow_ram = true;
176362306a36Sopenharmony_ci		nvm_size = hw->flash.sr_words * 2u;
176462306a36Sopenharmony_ci	} else {
176562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function");
176662306a36Sopenharmony_ci		return -EOPNOTSUPP;
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	if (offset + size >= nvm_size) {
177062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot read beyond the region size");
177162306a36Sopenharmony_ci		return -ERANGE;
177262306a36Sopenharmony_ci	}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	status = ice_acquire_nvm(hw, ICE_RES_READ);
177562306a36Sopenharmony_ci	if (status) {
177662306a36Sopenharmony_ci		dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
177762306a36Sopenharmony_ci			status, hw->adminq.sq_last_status);
177862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
177962306a36Sopenharmony_ci		return -EIO;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	status = ice_read_flat_nvm(hw, (u32)offset, &size, data,
178362306a36Sopenharmony_ci				   read_shadow_ram);
178462306a36Sopenharmony_ci	if (status) {
178562306a36Sopenharmony_ci		dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
178662306a36Sopenharmony_ci			size, status, hw->adminq.sq_last_status);
178762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
178862306a36Sopenharmony_ci		ice_release_nvm(hw);
178962306a36Sopenharmony_ci		return -EIO;
179062306a36Sopenharmony_ci	}
179162306a36Sopenharmony_ci	ice_release_nvm(hw);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	return 0;
179462306a36Sopenharmony_ci}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci/**
179762306a36Sopenharmony_ci * ice_devlink_devcaps_snapshot - Capture snapshot of device capabilities
179862306a36Sopenharmony_ci * @devlink: the devlink instance
179962306a36Sopenharmony_ci * @ops: the devlink region being snapshotted
180062306a36Sopenharmony_ci * @extack: extended ACK response structure
180162306a36Sopenharmony_ci * @data: on exit points to snapshot data buffer
180262306a36Sopenharmony_ci *
180362306a36Sopenharmony_ci * This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for
180462306a36Sopenharmony_ci * the device-caps devlink region. It captures a snapshot of the device
180562306a36Sopenharmony_ci * capabilities reported by firmware.
180662306a36Sopenharmony_ci *
180762306a36Sopenharmony_ci * @returns zero on success, and updates the data pointer. Returns a non-zero
180862306a36Sopenharmony_ci * error code on failure.
180962306a36Sopenharmony_ci */
181062306a36Sopenharmony_cistatic int
181162306a36Sopenharmony_ciice_devlink_devcaps_snapshot(struct devlink *devlink,
181262306a36Sopenharmony_ci			     const struct devlink_region_ops *ops,
181362306a36Sopenharmony_ci			     struct netlink_ext_ack *extack, u8 **data)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	struct ice_pf *pf = devlink_priv(devlink);
181662306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
181762306a36Sopenharmony_ci	struct ice_hw *hw = &pf->hw;
181862306a36Sopenharmony_ci	void *devcaps;
181962306a36Sopenharmony_ci	int status;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	devcaps = vzalloc(ICE_AQ_MAX_BUF_LEN);
182262306a36Sopenharmony_ci	if (!devcaps)
182362306a36Sopenharmony_ci		return -ENOMEM;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	status = ice_aq_list_caps(hw, devcaps, ICE_AQ_MAX_BUF_LEN, NULL,
182662306a36Sopenharmony_ci				  ice_aqc_opc_list_dev_caps, NULL);
182762306a36Sopenharmony_ci	if (status) {
182862306a36Sopenharmony_ci		dev_dbg(dev, "ice_aq_list_caps: failed to read device capabilities, err %d aq_err %d\n",
182962306a36Sopenharmony_ci			status, hw->adminq.sq_last_status);
183062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to read device capabilities");
183162306a36Sopenharmony_ci		vfree(devcaps);
183262306a36Sopenharmony_ci		return status;
183362306a36Sopenharmony_ci	}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	*data = (u8 *)devcaps;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return 0;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic const struct devlink_region_ops ice_nvm_region_ops = {
184162306a36Sopenharmony_ci	.name = "nvm-flash",
184262306a36Sopenharmony_ci	.destructor = vfree,
184362306a36Sopenharmony_ci	.snapshot = ice_devlink_nvm_snapshot,
184462306a36Sopenharmony_ci	.read = ice_devlink_nvm_read,
184562306a36Sopenharmony_ci};
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic const struct devlink_region_ops ice_sram_region_ops = {
184862306a36Sopenharmony_ci	.name = "shadow-ram",
184962306a36Sopenharmony_ci	.destructor = vfree,
185062306a36Sopenharmony_ci	.snapshot = ice_devlink_nvm_snapshot,
185162306a36Sopenharmony_ci	.read = ice_devlink_nvm_read,
185262306a36Sopenharmony_ci};
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic const struct devlink_region_ops ice_devcaps_region_ops = {
185562306a36Sopenharmony_ci	.name = "device-caps",
185662306a36Sopenharmony_ci	.destructor = vfree,
185762306a36Sopenharmony_ci	.snapshot = ice_devlink_devcaps_snapshot,
185862306a36Sopenharmony_ci};
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci/**
186162306a36Sopenharmony_ci * ice_devlink_init_regions - Initialize devlink regions
186262306a36Sopenharmony_ci * @pf: the PF device structure
186362306a36Sopenharmony_ci *
186462306a36Sopenharmony_ci * Create devlink regions used to enable access to dump the contents of the
186562306a36Sopenharmony_ci * flash memory on the device.
186662306a36Sopenharmony_ci */
186762306a36Sopenharmony_civoid ice_devlink_init_regions(struct ice_pf *pf)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(pf);
187062306a36Sopenharmony_ci	struct device *dev = ice_pf_to_dev(pf);
187162306a36Sopenharmony_ci	u64 nvm_size, sram_size;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	nvm_size = pf->hw.flash.flash_size;
187462306a36Sopenharmony_ci	pf->nvm_region = devlink_region_create(devlink, &ice_nvm_region_ops, 1,
187562306a36Sopenharmony_ci					       nvm_size);
187662306a36Sopenharmony_ci	if (IS_ERR(pf->nvm_region)) {
187762306a36Sopenharmony_ci		dev_err(dev, "failed to create NVM devlink region, err %ld\n",
187862306a36Sopenharmony_ci			PTR_ERR(pf->nvm_region));
187962306a36Sopenharmony_ci		pf->nvm_region = NULL;
188062306a36Sopenharmony_ci	}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	sram_size = pf->hw.flash.sr_words * 2u;
188362306a36Sopenharmony_ci	pf->sram_region = devlink_region_create(devlink, &ice_sram_region_ops,
188462306a36Sopenharmony_ci						1, sram_size);
188562306a36Sopenharmony_ci	if (IS_ERR(pf->sram_region)) {
188662306a36Sopenharmony_ci		dev_err(dev, "failed to create shadow-ram devlink region, err %ld\n",
188762306a36Sopenharmony_ci			PTR_ERR(pf->sram_region));
188862306a36Sopenharmony_ci		pf->sram_region = NULL;
188962306a36Sopenharmony_ci	}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	pf->devcaps_region = devlink_region_create(devlink,
189262306a36Sopenharmony_ci						   &ice_devcaps_region_ops, 10,
189362306a36Sopenharmony_ci						   ICE_AQ_MAX_BUF_LEN);
189462306a36Sopenharmony_ci	if (IS_ERR(pf->devcaps_region)) {
189562306a36Sopenharmony_ci		dev_err(dev, "failed to create device-caps devlink region, err %ld\n",
189662306a36Sopenharmony_ci			PTR_ERR(pf->devcaps_region));
189762306a36Sopenharmony_ci		pf->devcaps_region = NULL;
189862306a36Sopenharmony_ci	}
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci/**
190262306a36Sopenharmony_ci * ice_devlink_destroy_regions - Destroy devlink regions
190362306a36Sopenharmony_ci * @pf: the PF device structure
190462306a36Sopenharmony_ci *
190562306a36Sopenharmony_ci * Remove previously created regions for this PF.
190662306a36Sopenharmony_ci */
190762306a36Sopenharmony_civoid ice_devlink_destroy_regions(struct ice_pf *pf)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	if (pf->nvm_region)
191062306a36Sopenharmony_ci		devlink_region_destroy(pf->nvm_region);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (pf->sram_region)
191362306a36Sopenharmony_ci		devlink_region_destroy(pf->sram_region);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (pf->devcaps_region)
191662306a36Sopenharmony_ci		devlink_region_destroy(pf->devcaps_region);
191762306a36Sopenharmony_ci}
1918