558 lines · 15.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright (c) 2025, Intel Corporation. */ #include "ixgbe.h"#include "devlink.h"#include "ixgbe_fw_update.h" struct ixgbe_info_ctx {	char buf[128];	struct ixgbe_orom_info pending_orom;	struct ixgbe_nvm_info pending_nvm;	struct ixgbe_netlist_info pending_netlist;	struct ixgbe_hw_dev_caps dev_caps;}; enum ixgbe_devlink_version_type {	IXGBE_DL_VERSION_RUNNING,	IXGBE_DL_VERSION_STORED}; static void ixgbe_info_get_dsn(struct ixgbe_adapter *adapter,			       struct ixgbe_info_ctx *ctx){	u8 dsn[8]; 	/* Copy the DSN into an array in Big Endian format */	put_unaligned_be64(pci_get_dsn(adapter->pdev), dsn); 	snprintf(ctx->buf, sizeof(ctx->buf), "%8phD", dsn);} static void ixgbe_info_orom_ver(struct ixgbe_adapter *adapter,				struct ixgbe_info_ctx *ctx,				enum ixgbe_devlink_version_type type){	struct ixgbe_hw *hw = &adapter->hw;	struct ixgbe_nvm_version nvm_ver; 	ctx->buf[0] = '\0'; 	if (hw->mac.type == ixgbe_mac_e610) {		struct ixgbe_orom_info *orom = &adapter->hw.flash.orom; 		if (type == IXGBE_DL_VERSION_STORED &&		    ctx->dev_caps.common_cap.nvm_update_pending_orom)			orom = &ctx->pending_orom; 		snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",			 orom->major, orom->build, orom->patch);		return;	} 	ixgbe_get_oem_prod_version(hw, &nvm_ver);	if (nvm_ver.oem_valid) {		snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x",			 nvm_ver.oem_major, nvm_ver.oem_minor,			 nvm_ver.oem_release); 		return;	} 	ixgbe_get_orom_version(hw, &nvm_ver);	if (nvm_ver.or_valid)		snprintf(ctx->buf, sizeof(ctx->buf), "%d.%d.%d",			 nvm_ver.or_major, nvm_ver.or_build, nvm_ver.or_patch);} static void ixgbe_info_eetrack(struct ixgbe_adapter *adapter,			       struct ixgbe_info_ctx *ctx,			       enum ixgbe_devlink_version_type type){	struct ixgbe_hw *hw = &adapter->hw;	struct ixgbe_nvm_version nvm_ver; 	if (hw->mac.type == ixgbe_mac_e610) {		u32 eetrack = hw->flash.nvm.eetrack; 		if (type == IXGBE_DL_VERSION_STORED &&		    ctx->dev_caps.common_cap.nvm_update_pending_nvm)			eetrack = ctx->pending_nvm.eetrack; 		snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", eetrack);		return;	} 	ixgbe_get_oem_prod_version(hw, &nvm_ver); 	/* No ETRACK version for OEM */	if (nvm_ver.oem_valid) {		ctx->buf[0] = '\0';		return;	} 	ixgbe_get_etk_id(hw, &nvm_ver);	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm_ver.etk_id);} static void ixgbe_info_fw_api(struct ixgbe_adapter *adapter,			      struct ixgbe_info_ctx *ctx){	struct ixgbe_hw *hw = &adapter->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",		 hw->api_maj_ver, hw->api_min_ver, hw->api_patch);} static void ixgbe_info_fw_build(struct ixgbe_adapter *adapter,				struct ixgbe_info_ctx *ctx){	struct ixgbe_hw *hw = &adapter->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", hw->fw_build);} static void ixgbe_info_fw_srev(struct ixgbe_adapter *adapter,			       struct ixgbe_info_ctx *ctx,			       enum ixgbe_devlink_version_type type){	struct ixgbe_nvm_info *nvm = &adapter->hw.flash.nvm; 	if (type == IXGBE_DL_VERSION_STORED &&	    ctx->dev_caps.common_cap.nvm_update_pending_nvm)		nvm = &ctx->pending_nvm; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u", nvm->srev);} static void ixgbe_info_orom_srev(struct ixgbe_adapter *adapter,				 struct ixgbe_info_ctx *ctx,				 enum ixgbe_devlink_version_type type){	struct ixgbe_orom_info *orom = &adapter->hw.flash.orom; 	if (type == IXGBE_DL_VERSION_STORED &&	    ctx->dev_caps.common_cap.nvm_update_pending_orom)		orom = &ctx->pending_orom; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u", orom->srev);} static void ixgbe_info_nvm_ver(struct ixgbe_adapter *adapter,			       struct ixgbe_info_ctx *ctx,			       enum ixgbe_devlink_version_type type){	struct ixgbe_nvm_info *nvm = &adapter->hw.flash.nvm; 	if (type == IXGBE_DL_VERSION_STORED &&	    ctx->dev_caps.common_cap.nvm_update_pending_nvm)		nvm = &ctx->pending_nvm; 	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor);} static void ixgbe_info_netlist_ver(struct ixgbe_adapter *adapter,				   struct ixgbe_info_ctx *ctx,				   enum ixgbe_devlink_version_type type){	struct ixgbe_netlist_info *netlist = &adapter->hw.flash.netlist; 	if (type == IXGBE_DL_VERSION_STORED &&	    ctx->dev_caps.common_cap.nvm_update_pending_netlist)		netlist = &ctx->pending_netlist; 	/* The netlist version fields are BCD formatted */	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x",		 netlist->major, netlist->minor,		 netlist->type >> 16, netlist->type & 0xFFFF,		 netlist->rev, netlist->cust_ver);} static void ixgbe_info_netlist_build(struct ixgbe_adapter *adapter,				     struct ixgbe_info_ctx *ctx,				     enum ixgbe_devlink_version_type type){	struct ixgbe_netlist_info *netlist = &adapter->hw.flash.netlist; 	if (type == IXGBE_DL_VERSION_STORED &&	    ctx->dev_caps.common_cap.nvm_update_pending_netlist)		netlist = &ctx->pending_netlist; 	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);} static int ixgbe_set_ctx_dev_caps(struct ixgbe_hw *hw,				  struct ixgbe_info_ctx *ctx,				  struct netlink_ext_ack *extack){	bool *pending_orom, *pending_nvm, *pending_netlist;	int err; 	err = ixgbe_discover_dev_caps(hw, &ctx->dev_caps);	if (err) {		NL_SET_ERR_MSG_MOD(extack,				   "Unable to discover device capabilities");		return err;	} 	pending_orom = &ctx->dev_caps.common_cap.nvm_update_pending_orom;	pending_nvm = &ctx->dev_caps.common_cap.nvm_update_pending_nvm;	pending_netlist = &ctx->dev_caps.common_cap.nvm_update_pending_netlist; 	if (*pending_orom) {		err = ixgbe_get_inactive_orom_ver(hw, &ctx->pending_orom);		if (err)			*pending_orom = false;	} 	if (*pending_nvm) {		err = ixgbe_get_inactive_nvm_ver(hw, &ctx->pending_nvm);		if (err)			*pending_nvm = false;	} 	if (*pending_netlist) {		err = ixgbe_get_inactive_netlist_ver(hw, &ctx->pending_netlist);		if (err)			*pending_netlist = false;	} 	return 0;} static int ixgbe_devlink_info_get_e610(struct ixgbe_adapter *adapter,				       struct devlink_info_req *req,				       struct ixgbe_info_ctx *ctx){	int err; 	ixgbe_info_fw_api(adapter, ctx);	err = devlink_info_version_running_put(req,					       DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API,					       ctx->buf);	if (err)		return err; 	ixgbe_info_fw_build(adapter, ctx);	err = devlink_info_version_running_put(req, "fw.mgmt.build", ctx->buf);	if (err)		return err; 	ixgbe_info_fw_srev(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req, "fw.mgmt.srev", ctx->buf);	if (err)		return err; 	ixgbe_info_orom_srev(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req, "fw.undi.srev", ctx->buf);	if (err)		return err; 	ixgbe_info_nvm_ver(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req, "fw.psid.api", ctx->buf);	if (err)		return err; 	ixgbe_info_netlist_ver(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req, "fw.netlist", ctx->buf);	if (err)		return err; 	ixgbe_info_netlist_build(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	return devlink_info_version_running_put(req, "fw.netlist.build",						ctx->buf);} static intixgbe_devlink_pending_info_get_e610(struct ixgbe_adapter *adapter,				    struct devlink_info_req *req,				    struct ixgbe_info_ctx *ctx){	int err; 	ixgbe_info_orom_ver(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req,					      DEVLINK_INFO_VERSION_GENERIC_FW_UNDI,					      ctx->buf);	if (err)		return err; 	ixgbe_info_eetrack(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req,					      DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID,					      ctx->buf);	if (err)		return err; 	ixgbe_info_fw_srev(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req, "fw.mgmt.srev", ctx->buf);	if (err)		return err; 	ixgbe_info_orom_srev(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req, "fw.undi.srev", ctx->buf);	if (err)		return err; 	ixgbe_info_nvm_ver(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req, "fw.psid.api", ctx->buf);	if (err)		return err; 	ixgbe_info_netlist_ver(adapter, ctx, IXGBE_DL_VERSION_STORED);	err = devlink_info_version_stored_put(req, "fw.netlist", ctx->buf);	if (err)		return err; 	ixgbe_info_netlist_build(adapter, ctx, IXGBE_DL_VERSION_STORED);	return devlink_info_version_stored_put(req, "fw.netlist.build",					       ctx->buf);} static int ixgbe_devlink_info_get(struct devlink *devlink,				  struct devlink_info_req *req,				  struct netlink_ext_ack *extack){	struct ixgbe_adapter *adapter = devlink_priv(devlink);	struct ixgbe_hw *hw = &adapter->hw;	struct ixgbe_info_ctx *ctx;	int err; 	ctx = kmalloc_obj(*ctx);	if (!ctx)		return -ENOMEM; 	if (hw->mac.type == ixgbe_mac_e610)		ixgbe_refresh_fw_version(adapter); 	ixgbe_info_get_dsn(adapter, ctx);	err = devlink_info_serial_number_put(req, ctx->buf);	if (err)		goto free_ctx; 	err = hw->eeprom.ops.read_pba_string(hw, ctx->buf, sizeof(ctx->buf));	if (err)		goto free_ctx; 	err = devlink_info_version_fixed_put(req,					     DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,					     ctx->buf);	if (err)		goto free_ctx; 	ixgbe_info_orom_ver(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req,					       DEVLINK_INFO_VERSION_GENERIC_FW_UNDI,					       ctx->buf);	if (err)		goto free_ctx; 	ixgbe_info_eetrack(adapter, ctx, IXGBE_DL_VERSION_RUNNING);	err = devlink_info_version_running_put(req,					       DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID,					       ctx->buf);	if (err || hw->mac.type != ixgbe_mac_e610)		goto free_ctx; 	err = ixgbe_set_ctx_dev_caps(hw, ctx, extack);	if (err)		goto free_ctx; 	err = ixgbe_devlink_info_get_e610(adapter, req, ctx);	if (err)		goto free_ctx; 	err = ixgbe_devlink_pending_info_get_e610(adapter, req, ctx);free_ctx:	kfree(ctx);	return err;} /** * ixgbe_devlink_reload_empr_start - Start EMP reset to activate new firmware * @devlink: pointer to the devlink instance to reload * @netns_change: if true, the network namespace is changing * @action: the action to perform. Must be DEVLINK_RELOAD_ACTION_FW_ACTIVATE * @limit: limits on what reload should do, such as not resetting * @extack: netlink extended ACK structure * * Allow user to activate new Embedded Management Processor firmware by * issuing device specific EMP reset. Called in response to * a DEVLINK_CMD_RELOAD with the DEVLINK_RELOAD_ACTION_FW_ACTIVATE. * * Note that teardown and rebuild of the driver state happens automatically as * part of an interrupt and watchdog task. This is because all physical * functions on the device must be able to reset when an EMP reset occurs from * any source. * * Return: the exit code of the operation. */static int ixgbe_devlink_reload_empr_start(struct devlink *devlink,					   bool netns_change,					   enum devlink_reload_action action,					   enum devlink_reload_limit limit,					   struct netlink_ext_ack *extack){	struct ixgbe_adapter *adapter = devlink_priv(devlink);	struct ixgbe_hw *hw = &adapter->hw;	u8 pending;	int err; 	if (hw->mac.type != ixgbe_mac_e610)		return -EOPNOTSUPP; 	err = ixgbe_get_pending_updates(adapter, &pending, extack);	if (err)		return err; 	/* Pending is a bitmask of which flash banks have a pending update,	 * including the main NVM bank, the Option ROM bank, and the netlist	 * bank. If any of these bits are set, then there is a pending update	 * waiting to be activated.	 */	if (!pending) {		NL_SET_ERR_MSG_MOD(extack, "No pending firmware update");		return -ECANCELED;	} 	if (adapter->fw_emp_reset_disabled) {		NL_SET_ERR_MSG_MOD(extack,				   "EMP reset is not available. To activate firmware, a reboot or power cycle is needed");		return -ECANCELED;	} 	err = ixgbe_aci_nvm_update_empr(hw);	if (err)		NL_SET_ERR_MSG_MOD(extack,				   "Failed to trigger EMP device reset to reload firmware"); 	return err;} /*Wait for 10 sec with 0.5 sec tic. EMPR takes no less than half of a sec */#define IXGBE_DEVLINK_RELOAD_TIMEOUT_SEC	20 /** * ixgbe_devlink_reload_empr_finish - finishes EMP reset * @devlink: pointer to the devlink instance * @action: the action to perform. * @limit: limits on what reload should do * @actions_performed: actions performed * @extack: netlink extended ACK structure * * Wait for new NVM to be loaded during EMP reset. * * Return: -ETIME when timer is exceeded, 0 on success. */static int ixgbe_devlink_reload_empr_finish(struct devlink *devlink,					    enum devlink_reload_action action,					    enum devlink_reload_limit limit,					    u32 *actions_performed,					    struct netlink_ext_ack *extack){	struct ixgbe_adapter *adapter = devlink_priv(devlink);	struct ixgbe_hw *hw = &adapter->hw;	int i = 0;	u32 fwsm; 	do {		/* Just right away after triggering EMP reset the FWSM register		 * may be not cleared yet, so begin the loop with the delay		 * in order to not check the not updated register.		 */		mdelay(500); 		fwsm = IXGBE_READ_REG(hw, IXGBE_FWSM(hw)); 		if (i++ >= IXGBE_DEVLINK_RELOAD_TIMEOUT_SEC)			return -ETIME; 	} while (!(fwsm & IXGBE_FWSM_FW_VAL_BIT)); 	*actions_performed = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE); 	adapter->flags2 &= ~(IXGBE_FLAG2_API_MISMATCH |			     IXGBE_FLAG2_FW_ROLLBACK); 	return ixgbe_refresh_fw_version(adapter);} static const struct devlink_ops ixgbe_devlink_ops = {	.info_get = ixgbe_devlink_info_get,	.supported_flash_update_params =		DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,	.flash_update = ixgbe_flash_pldm_image,	.reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),	.reload_down = ixgbe_devlink_reload_empr_start,	.reload_up = ixgbe_devlink_reload_empr_finish,}; /** * ixgbe_allocate_devlink - Allocate devlink instance * @dev: device to allocate devlink for * * Allocate a devlink instance for this physical function. * * Return: pointer to the device adapter structure on success, * ERR_PTR(-ENOMEM) when allocation failed. */struct ixgbe_adapter *ixgbe_allocate_devlink(struct device *dev){

Lines 1–500 of 558.