lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 10 Feb 2023 18:33:18 +0530
From:   Harsh Jain <h.jain@....com>
To:     <davem@...emloft.net>, <edumazet@...gle.com>, <kuba@...nel.org>,
        <pabeni@...hat.com>, <thomas.lendacky@....com>,
        <Raju.Rangoju@....com>, <Shyam-sundar.S-k@....com>,
        <harshjain.prof@...il.com>, <abhijit.gangurde@....com>,
        <puneet.gupta@....com>, <nikhil.agarwal@....com>,
        <tarak.reddy@....com>, <netdev@...r.kernel.org>
CC:     Harsh Jain <h.jain@....com>
Subject: [PATCH  3/6] net: ethernet: efct: Add devlink support

Add devlink support.

Signed-off-by: Abhijit Gangurde<abhijit.gangurde@....com>
Signed-off-by: Puneet Gupta <puneet.gupta@....com>
Signed-off-by: Nikhil Agarwal<nikhil.agarwal@....com>
Signed-off-by: Tarak Reddy<tarak.reddy@....com>
Signed-off-by: Harsh Jain <h.jain@....com>
---
 drivers/net/ethernet/amd/efct/efct_devlink.c | 829 +++++++++++++++++++
 drivers/net/ethernet/amd/efct/efct_devlink.h |  21 +
 drivers/net/ethernet/amd/efct/efct_driver.h  |   3 +
 drivers/net/ethernet/amd/efct/efct_netdev.c  |   1 +
 drivers/net/ethernet/amd/efct/efct_pci.c     |  24 +-
 drivers/net/ethernet/amd/efct/efct_reflash.c | 564 +++++++++++++
 drivers/net/ethernet/amd/efct/efct_reflash.h |  16 +
 7 files changed, 1457 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/amd/efct/efct_devlink.c
 create mode 100644 drivers/net/ethernet/amd/efct/efct_devlink.h
 create mode 100644 drivers/net/ethernet/amd/efct/efct_reflash.c
 create mode 100644 drivers/net/ethernet/amd/efct/efct_reflash.h

diff --git a/drivers/net/ethernet/amd/efct/efct_devlink.c b/drivers/net/ethernet/amd/efct/efct_devlink.c
new file mode 100644
index 000000000000..64217337f80a
--- /dev/null
+++ b/drivers/net/ethernet/amd/efct/efct_devlink.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0
+/****************************************************************************
+ * Driver for AMD/Xilinx network controllers and boards
+ * Copyright (C) 2021, Xilinx, Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/rtc.h>
+#include "efct_devlink.h"
+#include "mcdi.h"
+#include "mcdi_functions.h"
+#include "mcdi_pcol.h"
+#include "efct_reflash.h"
+#include "efct_netdev.h"
+
+/* Custom devlink-info version object names for details that do not map to the
+ * generic standardized names.
+ */
+#define EFCT_DEVLINK_INFO_VERSION_FW_MGMT_SUC	"fw.mgmt.suc"
+#define EFCT_DEVLINK_INFO_VERSION_FW_MGMT_CMC	"fw.mgmt.cmc"
+#define EFCT_DEVLINK_INFO_VERSION_FPGA_REV	"fpga.rev"
+#define EFCT_DEVLINK_INFO_VERSION_DATAPATH_HW	"fpga.app"
+#define EFCT_DEVLINK_INFO_VERSION_DATAPATH_FW	DEVLINK_INFO_VERSION_GENERIC_FW_APP
+#define EFCT_DEVLINK_INFO_VERSION_SOC_BOOT	"coproc.boot"
+#define EFCT_DEVLINK_INFO_VERSION_SOC_UBOOT	"coproc.uboot"
+#define EFCT_DEVLINK_INFO_VERSION_SOC_MAIN	"coproc.main"
+#define EFCT_DEVLINK_INFO_VERSION_SOC_RECOVERY	"coproc.recovery"
+#define EFCT_DEVLINK_INFO_VERSION_FW_EXPROM	"fw.exprom"
+#define EFCT_DEVLINK_INFO_VERSION_FW_UEFI	"fw.uefi"
+
+static int efct_devlink_info_user_cfg(struct efct_nic *efct,
+				      struct devlink_info_req *req)
+{
+	char *buf, *desc;
+	u16 version[4];
+	int offset;
+	int rc;
+
+	offset = 0;
+	buf = kmalloc(MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM_MCDI2 +
+		      EFCT_MAX_VERSION_INFO_LEN, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	desc = kmalloc(MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM_MCDI2, GFP_KERNEL);
+	if (!desc) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	rc = efct_mcdi_nvram_metadata(efct, NVRAM_PARTITION_TYPE_USER_CONFIG, NULL, version,
+				      desc, MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM_MCDI2);
+	if (rc)
+		goto out;
+	if (version[0] || version[1] || version[2] || version[3])
+		offset = snprintf(buf, (EFCT_MAX_VERSION_INFO_LEN +
+				  MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM_MCDI2),
+				  "%u.%u.%u.%u ", version[0], version[1], version[2], version[3]);
+
+	snprintf(buf + offset, (EFCT_MAX_VERSION_INFO_LEN +
+				MC_CMD_NVRAM_METADATA_OUT_DESCRIPTION_MAXNUM_MCDI2),
+		 "%s", desc);
+
+	devlink_info_version_stored_put(req, DEVLINK_INFO_VERSION_GENERIC_FW_PSID, buf);
+
+out:
+	kfree(buf);
+	kfree(desc);
+	return rc;
+}
+
+static int efct_devlink_info_nvram_partition(struct efct_nic *efct,
+					     struct devlink_info_req *req,
+					    u32 partition_type,
+					    const char *version_name)
+{
+	char buf[EFCT_MAX_VERSION_INFO_LEN];
+	u16 version[4];
+	int rc;
+
+	rc = efct_mcdi_nvram_metadata(efct, partition_type, NULL, version, NULL, 0);
+	if (rc)
+		return rc;
+
+	snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u", version[0],
+		 version[1], version[2], version[3]);
+	devlink_info_version_stored_put(req, version_name, buf);
+
+	return 0;
+}
+
+static void efct_devlink_info_stored_versions(struct efct_nic *efct,
+					      struct devlink_info_req *req)
+{
+	efct_devlink_info_nvram_partition(efct, req, NVRAM_PARTITION_TYPE_BUNDLE,
+					  DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID);
+	efct_devlink_info_nvram_partition(efct, req,
+					  NVRAM_PARTITION_TYPE_MC_FIRMWARE,
+					 DEVLINK_INFO_VERSION_GENERIC_FW_MGMT);
+	efct_devlink_info_nvram_partition(efct, req,
+					  NVRAM_PARTITION_TYPE_SUC_FIRMWARE,
+					 EFCT_DEVLINK_INFO_VERSION_FW_MGMT_SUC);
+	efct_devlink_info_nvram_partition(efct, req,
+					  NVRAM_PARTITION_TYPE_EXPANSION_ROM,
+					 EFCT_DEVLINK_INFO_VERSION_FW_EXPROM);
+	efct_devlink_info_nvram_partition(efct, req,
+					  NVRAM_PARTITION_TYPE_EXPANSION_UEFI,
+					 EFCT_DEVLINK_INFO_VERSION_FW_UEFI);
+	efct_devlink_info_user_cfg(efct, req);
+}
+
+#define EFCT_MAX_SERIALNUM_LEN	(ETH_ALEN * 2 + 1)
+
+static void efct_devlink_info_board_cfg(struct efct_nic *efct,
+					struct devlink_info_req *req)
+{
+	char sn[EFCT_MAX_SERIALNUM_LEN];
+	u8 maddr[ETH_ALEN];
+	u32 mac;
+	int rc;
+
+	mac = 0;
+	rc = efct_get_mac_address(efct, maddr);
+	if (rc)
+		return;
+	mac  = maddr[3];
+	mac = (mac << 8) | maddr[4];
+	mac = (mac << 8) | maddr[5];
+	/* Calculate base mac address of device*/
+	mac -= efct->port_num;
+	maddr[3] = (mac >> 16) & 0xff;
+	maddr[4] = (mac >> 8) & 0xff;
+	maddr[5] = mac & 0xff;
+
+	snprintf(sn, EFCT_MAX_SERIALNUM_LEN, "%pm", maddr);
+	devlink_info_serial_number_put(req, sn);
+}
+
+#define EFCT_VER_FLAG(_f)	\
+	(MC_CMD_GET_VERSION_V5_OUT_ ## _f ## _PRESENT_LBN)
+
+static void efct_devlink_info_running_versions(struct efct_nic *efct,
+					       struct devlink_info_req *req)
+{
+	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_VERSION_V5_OUT_LEN);
+	MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_VERSION_EXT_IN_LEN);
+	char buf[EFCT_MAX_VERSION_INFO_LEN];
+	struct rtc_time build_date;
+	size_t outlength, offset;
+	const __le32 *dwords;
+	const __le16 *words;
+	u32 flags, build_id;
+	const char *str;
+	u64 tstamp;
+	int rc;
+
+	rc = efct_mcdi_rpc(efct, MC_CMD_GET_VERSION, inbuf, sizeof(inbuf),
+			   outbuf, sizeof(outbuf), &outlength);
+	if (rc || outlength < MC_CMD_GET_VERSION_V5_OUT_LEN)
+		return;
+
+	/* Handle previous output */
+	words = (__le16 *)MCDI_PTR(outbuf,
+				       GET_VERSION_V5_OUT_VERSION);
+	offset = snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			  le16_to_cpu(words[0]),
+			  le16_to_cpu(words[1]),
+			  le16_to_cpu(words[2]),
+			  le16_to_cpu(words[3]));
+
+	devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
+
+	flags = MCDI_DWORD(outbuf, GET_VERSION_V5_OUT_FLAGS);
+	if (flags & BIT(EFCT_VER_FLAG(BOARD_EXT_INFO))) {
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%s",
+			 MCDI_PTR(outbuf, GET_VERSION_V5_OUT_BOARD_NAME));
+		devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, buf);
+		/* Favour full board version if present (in V5 or later) */
+		if (~flags & BIT(EFCT_VER_FLAG(BOARD_VERSION))) {
+			snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u",
+				 MCDI_DWORD(outbuf, GET_VERSION_V5_OUT_BOARD_REVISION));
+			devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,
+						       buf);
+		}
+
+		str = MCDI_PTR(outbuf, GET_VERSION_V5_OUT_BOARD_SERIAL);
+		if (str[0])
+			devlink_info_board_serial_number_put(req, str);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(FPGA_EXT_INFO))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf,
+						GET_VERSION_V5_OUT_FPGA_VERSION);
+		offset = snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u_%c%u",
+				  le32_to_cpu(dwords[0]),
+				  'A' + le32_to_cpu(dwords[1]),
+				  le32_to_cpu(dwords[2]));
+
+		str = MCDI_PTR(outbuf, GET_VERSION_V5_OUT_FPGA_EXTRA);
+		if (str[0])
+			snprintf(&buf[offset], EFCT_MAX_VERSION_INFO_LEN - offset,
+				 " (%s)", str);
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_FPGA_REV, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(CMC_EXT_INFO))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_CMCFW_VERSION);
+		offset = snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+				  le32_to_cpu(dwords[0]),
+				  le32_to_cpu(dwords[1]),
+				  le32_to_cpu(dwords[2]),
+				  le32_to_cpu(dwords[3]));
+
+		tstamp = MCDI_QWORD(outbuf, GET_VERSION_V5_OUT_CMCFW_BUILD_DATE);
+		if (tstamp) {
+			rtc_time64_to_tm(tstamp, &build_date);
+			snprintf(&buf[offset], EFCT_MAX_VERSION_INFO_LEN - offset,
+				 " (%ptRd)", &build_date);
+		}
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_FW_MGMT_CMC, buf);
+	}
+
+	words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_VERSION);
+	offset = snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			  le16_to_cpu(words[0]), le16_to_cpu(words[1]),
+			  le16_to_cpu(words[2]), le16_to_cpu(words[3]));
+	if (flags & BIT(EFCT_VER_FLAG(MCFW_EXT_INFO))) {
+		build_id = MCDI_DWORD(outbuf, GET_VERSION_V5_OUT_MCFW_BUILD_ID);
+		snprintf(&buf[offset], EFCT_MAX_VERSION_INFO_LEN - offset,
+			 " (%x) %s", build_id,
+			 MCDI_PTR(outbuf, GET_VERSION_V5_OUT_MCFW_BUILD_NAME));
+	}
+	devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
+
+	if (flags & BIT(EFCT_VER_FLAG(SUCFW_EXT_INFO))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf,	GET_VERSION_V5_OUT_SUCFW_VERSION);
+		tstamp = MCDI_QWORD(outbuf, GET_VERSION_V5_OUT_SUCFW_BUILD_DATE);
+		rtc_time64_to_tm(tstamp, &build_date);
+		build_id = MCDI_DWORD(outbuf, GET_VERSION_V5_OUT_SUCFW_CHIP_ID);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN,
+			 "%u.%u.%u.%u type %x (%ptRd)",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]), le32_to_cpu(dwords[3]),
+			 build_id, &build_date);
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_FW_MGMT_SUC, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(DATAPATH_HW_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf,	GET_VERSION_V5_OUT_DATAPATH_HW_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_DATAPATH_HW, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(DATAPATH_FW_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_DATAPATH_FW_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_DATAPATH_FW,
+						 buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(SOC_BOOT_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_SOC_BOOT_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_SOC_BOOT, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(SOC_UBOOT_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_SOC_UBOOT_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_SOC_UBOOT, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(SOC_MAIN_ROOTFS_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_SOC_MAIN_ROOTFS_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_SOC_MAIN, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(SOC_RECOVERY_BUILDROOT_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf,
+				GET_VERSION_V5_OUT_SOC_RECOVERY_BUILDROOT_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_SOC_RECOVERY, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(SUCFW_VERSION)) &&
+	    ~flags & BIT(EFCT_VER_FLAG(SUCFW_EXT_INFO))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_SUCFW_VERSION);
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+		devlink_info_version_running_put(req, EFCT_DEVLINK_INFO_VERSION_FW_MGMT_SUC, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(BOARD_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_BOARD_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, buf);
+	}
+
+	if (flags & BIT(EFCT_VER_FLAG(BUNDLE_VERSION))) {
+		dwords = (__le32 *)MCDI_PTR(outbuf, GET_VERSION_V5_OUT_BUNDLE_VERSION);
+
+		snprintf(buf, EFCT_MAX_VERSION_INFO_LEN, "%u.%u.%u.%u",
+			 le32_to_cpu(dwords[0]), le32_to_cpu(dwords[1]),
+			 le32_to_cpu(dwords[2]),
+			 le32_to_cpu(dwords[3]));
+
+		devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID,
+						 buf);
+	}
+}
+
+#undef EFCT_VER_FLAG
+
+static void efct_devlink_info_query_all(struct efct_nic *efct,
+					struct devlink_info_req *req)
+{
+	efct_devlink_info_board_cfg(efct, req);
+	efct_devlink_info_stored_versions(efct, req);
+	efct_devlink_info_running_versions(efct, req);
+}
+
+enum efct_devlink_param_id {
+	EFCT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+	EFCT_DEVLINK_PARAM_ID_CT_THRESH,
+	EFCT_DEVLINK_PARAM_ID_DIST_LAYOUT,
+	EFCT_DEVLINK_PARAM_ID_SEPARATED_CPU,
+	EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_LOW_THRESH,
+	EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_HIGH_THRESH,
+	EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_IRQS,
+	EFCT_DEVLINK_PARAM_ID_RX_MERGE_TIMEOUT_NS,
+	EFCT_DEVLINK_PARAM_ID_TX_MERGE_TIMEOUT_NS,
+};
+
+static int efct_irq_adapt_low_thresh_param_get(struct devlink *dl, u32 id,
+					       struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu32 = efct->evq[0].irq_adapt_low_thresh;
+
+	return 0;
+}
+
+static int efct_irq_adapt_low_thresh_param_set(struct devlink *dl, u32 id,
+					       struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		for (j = 0; j < efct->max_evq_count; j++)
+			efct->evq[j].irq_adapt_low_thresh = ctx->val.vu32;
+	}
+	return 0;
+}
+
+static int efct_irq_adapt_high_thresh_param_get(struct devlink *dl, u32 id,
+						struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu32 = efct->evq[0].irq_adapt_high_thresh;
+
+	return 0;
+}
+
+static int efct_irq_adapt_high_thresh_param_set(struct devlink *dl, u32 id,
+						struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		for (j = 0; j < efct->max_evq_count; j++)
+			efct->evq[j].irq_adapt_high_thresh = ctx->val.vu32;
+	}
+	return 0;
+}
+
+static int efct_irq_adapt_irqs_param_get(struct devlink *dl, u32 id,
+					 struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu32 = efct->evq[0].irq_adapt_irqs;
+
+	return 0;
+}
+
+static int efct_irq_adapt_irqs_param_set(struct devlink *dl, u32 id,
+					 struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		for (j = 0; j < efct->max_evq_count; j++)
+			efct->evq[j].irq_adapt_irqs = ctx->val.vu32;
+	}
+	return 0;
+}
+
+static int efct_rx_merge_timeout_get(struct devlink *dl, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu32 = efct->evq[0].rx_merge_timeout_ns;
+
+	return 0;
+}
+
+static int efct_rx_merge_timeout_set(struct devlink *dl, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		for (j = 0; j < efct->max_evq_count; j++)
+			efct->evq[j].rx_merge_timeout_ns = ctx->val.vu32;
+	}
+	return 0;
+}
+
+static int efct_rx_merge_timeout_validate(struct devlink *dl, u32 id, union devlink_param_value val,
+					  struct netlink_ext_ack *extack)
+{
+	if (val.vu32 > MAX_RX_MERGE_TIMEOUT_NS_VALUE) {
+		pr_info("Receive event merge timeout too large for X3, It shall be less than %u\n",
+			MAX_RX_MERGE_TIMEOUT_NS_VALUE);
+		return -EINVAL;
+	}
+
+	if (val.vu32 > 0 && (val.vu32 % EV_TIMER_GRANULARITY_NS != 0)) {
+		pr_info("Receive event merge timeout must be a multiple of %u\n",
+			EV_TIMER_GRANULARITY_NS);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int efct_tx_merge_timeout_get(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu32 = efct->evq[0].tx_merge_timeout_ns;
+
+	return 0;
+}
+
+static int efct_tx_merge_timeout_set(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		for (j = 0; j < efct->max_evq_count; j++)
+			efct->evq[j].tx_merge_timeout_ns = ctx->val.vu32;
+	}
+	return 0;
+}
+
+static int efct_tx_merge_timeout_validate(struct devlink *dl, u32 id, union devlink_param_value val,
+					  struct netlink_ext_ack *extack)
+{
+	if (val.vu32 > MAX_TX_MERGE_TIMEOUT_NS_VALUE) {
+		pr_info("Transmit event merge timeout too large for X3, It shall be less than %u\n",
+			MAX_TX_MERGE_TIMEOUT_NS_VALUE);
+		return -EINVAL;
+	}
+
+	if (val.vu32 > 0 && (val.vu32 % EV_TIMER_GRANULARITY_NS != 0)) {
+		pr_info("Transmit event merge timeout must be a multiple of %u\n",
+			EV_TIMER_GRANULARITY_NS);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int efct_ct_thresh_param_get(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	ctx->val.vu16 = efct->ct_thresh * 64;
+
+	return 0;
+}
+
+static int efct_ct_thresh_param_set(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i, j;
+
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		efct->ct_thresh = ctx->val.vu16 / 64;
+		for (j = 0; j < EFCT_MAX_CORE_TX_QUEUES; j++)
+			efct->txq[j].ct_thresh = efct->ct_thresh;
+	}
+	return 0;
+}
+
+static int efct_ct_thresh_param_validate(struct devlink *dl, u32 id, union devlink_param_value val,
+					 struct netlink_ext_ack *extack)
+{
+	if (((val.vu16 % 64) != 0) || val.vu16 > MAX_CT_THRESHOLD_VALUE) {
+		pr_info("Invalid CTPIO Threshold value passed. It shall be less than %d and a multiple of 64",
+			MAX_CT_THRESHOLD_VALUE);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int efct_dist_layout_param_get(struct devlink *dl, u32 id,
+				      struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+
+	ctx->val.vu8 = (*efct_dev)->dist_layout;
+
+	return 0;
+}
+
+static int efct_dist_layout_param_set(struct devlink *dl, u32 id,
+				      struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i;
+
+	if ((*efct_dev)->dist_layout == ctx->val.vu8)
+		return 0;
+
+	(*efct_dev)->dist_layout = ctx->val.vu8;
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		mutex_lock(&efct->state_lock);
+		if (efct->state == STATE_NET_UP)
+			efct_set_interrupt_affinity(efct);
+		mutex_unlock(&efct->state_lock);
+	}
+
+	return 0;
+}
+
+static int efct_dist_layout_param_validate(struct devlink *dl, u32 id,
+					   union devlink_param_value val,
+					  struct netlink_ext_ack *extack)
+{
+	if (val.vu8 != RX_LAYOUT_DISTRIBUTED && val.vu8 != RX_LAYOUT_SEPARATED) {
+		NL_SET_ERR_MSG_MOD(extack, "Invalid Layout value.Use 0-Distributed,1-Separated");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int efct_separated_cpu_param_get(struct devlink *dl, u32 id,
+					struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+
+	ctx->val.vu8 = (*efct_dev)->separated_rx_cpu;
+
+	return 0;
+}
+
+static int efct_separated_cpu_param_set(struct devlink *dl, u32 id,
+					struct devlink_param_gset_ctx *ctx)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+	struct efct_nic *efct;
+	int i;
+
+	if ((*efct_dev)->separated_rx_cpu == ctx->val.vu8)
+		return 0;
+
+	(*efct_dev)->separated_rx_cpu = ctx->val.vu8;
+	for (i = 0; i < (*efct_dev)->num_ports; i++) {
+		efct = (*efct_dev)->efct[i];
+		mutex_lock(&efct->state_lock);
+		if (efct->state == STATE_NET_UP)
+			efct_set_interrupt_affinity(efct);
+		mutex_unlock(&efct->state_lock);
+	}
+
+	return 0;
+}
+
+static int efct_separated_cpu_param_validate(struct devlink *dl, u32 id,
+					     union devlink_param_value val,
+					     struct netlink_ext_ack *extack)
+{
+	struct efct_device **efct_dev = devlink_priv(dl);
+
+	if ((*efct_dev)->dist_layout == RX_LAYOUT_DISTRIBUTED) {
+		NL_SET_ERR_MSG_MOD(extack, "Current layout is distributed. Use separated layout");
+		return -EINVAL;
+	}
+
+	if (val.vu8 > num_online_cpus()) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Invalid CPU value passed. Value exceeds current online CPUS");
+		pr_info("Invalid CPU value. Exceeds current online CPUS [%u]", num_online_cpus());
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct devlink_param efct_devlink_params[] = {
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_CT_THRESH,
+			     "ct_thresh", DEVLINK_PARAM_TYPE_U16,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_ct_thresh_param_get,
+			     efct_ct_thresh_param_set,
+			     efct_ct_thresh_param_validate),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_DIST_LAYOUT,
+			     "dist_layout", DEVLINK_PARAM_TYPE_U8,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_dist_layout_param_get,
+			     efct_dist_layout_param_set,
+			     efct_dist_layout_param_validate),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_SEPARATED_CPU,
+			     "separated_cpu", DEVLINK_PARAM_TYPE_U8,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_separated_cpu_param_get,
+			     efct_separated_cpu_param_set,
+			     efct_separated_cpu_param_validate),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_LOW_THRESH,
+			     "irq_adapt_low_thresh", DEVLINK_PARAM_TYPE_U32,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_irq_adapt_low_thresh_param_get,
+			     efct_irq_adapt_low_thresh_param_set,
+			     NULL),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_HIGH_THRESH,
+			     "irq_adapt_high_thresh", DEVLINK_PARAM_TYPE_U32,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_irq_adapt_high_thresh_param_get,
+			     efct_irq_adapt_high_thresh_param_set,
+			     NULL),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_IRQ_ADAPT_IRQS,
+			     "irq_adapt_irqs", DEVLINK_PARAM_TYPE_U32,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_irq_adapt_irqs_param_get,
+			     efct_irq_adapt_irqs_param_set,
+			     NULL),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_RX_MERGE_TIMEOUT_NS,
+			     "rx_merge_timeout", DEVLINK_PARAM_TYPE_U32,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_rx_merge_timeout_get,
+			     efct_rx_merge_timeout_set,
+			     efct_rx_merge_timeout_validate),
+	DEVLINK_PARAM_DRIVER(EFCT_DEVLINK_PARAM_ID_TX_MERGE_TIMEOUT_NS,
+			     "tx_merge_timeout", DEVLINK_PARAM_TYPE_U32,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     efct_tx_merge_timeout_get,
+			     efct_tx_merge_timeout_set,
+			     efct_tx_merge_timeout_validate),
+};
+
+static int efct_devlink_info_get(struct devlink *devlink,
+				 struct devlink_info_req *req,
+				struct netlink_ext_ack *extack)
+{
+	struct efct_device **efct_dev = devlink_priv(devlink);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	efct_devlink_info_query_all(efct, req);
+	return 0;
+}
+
+static int efct_devlink_flash_update(struct devlink *devlink,
+				     struct devlink_flash_update_params *params,
+				    struct netlink_ext_ack *extack)
+{
+	struct efct_device **efct_dev = devlink_priv(devlink);
+	struct efct_nic *efct = (*efct_dev)->efct[0];
+
+	return efct_reflash_flash_firmware(efct, params->fw);
+}
+
+static const struct devlink_ops efct_devlink_ops = {
+	.supported_flash_update_params	= 0,
+	.flash_update			= efct_devlink_flash_update,
+	.info_get			= efct_devlink_info_get,
+};
+
+static void efct_devlink_params_unregister(struct efct_device *efct_dev)
+{
+	if (efct_dev->devlink) {
+		devlink_params_unregister(efct_dev->devlink, efct_devlink_params,
+					  ARRAY_SIZE(efct_devlink_params));
+	}
+}
+
+void efct_fini_devlink(struct efct_device *efct_dev)
+{
+	if (efct_dev->devlink)
+		devlink_free(efct_dev->devlink);
+	efct_dev->devlink = NULL;
+}
+
+int efct_probe_devlink(struct efct_device *efct_dev)
+{
+	struct efct_device **devlink_private;
+
+	int rc = 0;
+
+	efct_dev->devlink = devlink_alloc(&efct_devlink_ops,
+					  sizeof(struct efct_device **),
+					  &efct_dev->pci_dev->dev);
+	if (!efct_dev->devlink)
+		return -ENOMEM;
+
+	devlink_private = devlink_priv(efct_dev->devlink);
+	*devlink_private = efct_dev;
+
+	return rc;
+}
+
+int efct_devlink_port_register(struct efct_nic *efct)
+{
+	struct devlink_port_attrs attrs = {};
+	int rc;
+
+	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
+	attrs.phys.port_number = efct->port_num;
+	devlink_port_attrs_set(&efct->dl_port, &attrs);
+	SET_NETDEV_DEVLINK_PORT(efct->net_dev, &efct->dl_port);
+	rc = devlink_port_register(efct->efct_dev->devlink, &efct->dl_port,
+				   efct->port_num);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int efct_devlink_params_register(struct efct_device *efct_dev)
+{
+	int rc;
+
+	rc = devlink_params_register(efct_dev->devlink, efct_devlink_params,
+				     ARRAY_SIZE(efct_devlink_params));
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+int efct_devlink_register(struct efct_device *efct_dev)
+{
+	int rc = 0;
+
+	rc = efct_devlink_params_register(efct_dev);
+	if (rc)
+		return rc;
+
+	devlink_register(efct_dev->devlink);
+
+	return rc;
+}
+
+void efct_devlink_unregister(struct efct_device *efct_dev)
+{
+	devlink_unregister(efct_dev->devlink);
+	efct_devlink_params_unregister(efct_dev);
+}
diff --git a/drivers/net/ethernet/amd/efct/efct_devlink.h b/drivers/net/ethernet/amd/efct/efct_devlink.h
new file mode 100644
index 000000000000..8e3720262820
--- /dev/null
+++ b/drivers/net/ethernet/amd/efct/efct_devlink.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0
+ ****************************************************************************
+ * Driver for AMD/Xilinx network controllers and boards
+ * Copyright (C) 2021, Xilinx, Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+#ifndef _EFCT_DEVLINK_H
+#define _EFCT_DEVLINK_H
+
+#include "efct_driver.h"
+#include <net/devlink.h>
+
+int efct_probe_devlink(struct efct_device *efct_dev);
+int efct_devlink_port_register(struct efct_nic *efct);
+void efct_fini_devlink(struct efct_device *efct_dev);
+int efct_devlink_register(struct efct_device *efct_dev);
+void efct_devlink_unregister(struct efct_device *efct_dev);
+
+struct devlink_port *efct_get_devlink_port(struct net_device *dev);
+
+#endif	/* _EFCT_DEVLINK_H */
diff --git a/drivers/net/ethernet/amd/efct/efct_driver.h b/drivers/net/ethernet/amd/efct/efct_driver.h
index a8d396ecee49..eb110895cb18 100644
--- a/drivers/net/ethernet/amd/efct/efct_driver.h
+++ b/drivers/net/ethernet/amd/efct/efct_driver.h
@@ -20,6 +20,7 @@
 #include <linux/netdevice.h>
 #include <linux/pci.h>
 
+#include <net/devlink.h>
 #include "efct_enum.h"
 #include "efct_bitfield.h"
 
@@ -585,6 +586,7 @@ struct efct_nic {
 	/* Mutex for serializing firmware reflash operations.*/
 	struct mutex reflash_mutex;
 	struct efct_mcdi_filter_table *filter_table;
+	struct devlink_port dl_port;
 };
 
 struct design_params {
@@ -644,6 +646,7 @@ struct efct_device {
 	resource_size_t bar_len;
 	bool mcdi_logging;
 	struct efct_nic *efct[MAX_PORTS];
+	void *devlink;
 	/*IRQ vectors per port*/
 	u16 vec_per_port;
 	struct msix_entry *xentries;
diff --git a/drivers/net/ethernet/amd/efct/efct_netdev.c b/drivers/net/ethernet/amd/efct/efct_netdev.c
index 23a776ab88a3..a7814a1b1386 100644
--- a/drivers/net/ethernet/amd/efct/efct_netdev.c
+++ b/drivers/net/ethernet/amd/efct/efct_netdev.c
@@ -11,6 +11,7 @@
 #include "efct_nic.h"
 #include "mcdi.h"
 #include "mcdi_port_common.h"
+#include "efct_devlink.h"
 
 static int efct_netdev_event(struct notifier_block *this,
 			     unsigned long event, void *ptr)
diff --git a/drivers/net/ethernet/amd/efct/efct_pci.c b/drivers/net/ethernet/amd/efct/efct_pci.c
index af76ae37e040..6933ad634411 100644
--- a/drivers/net/ethernet/amd/efct/efct_pci.c
+++ b/drivers/net/ethernet/amd/efct/efct_pci.c
@@ -14,6 +14,7 @@
 #include "efct_common.h"
 #include "efct_netdev.h"
 #include "efct_nic.h"
+#include "efct_devlink.h"
 
 /**************************************************************************
  *
@@ -664,6 +665,7 @@ static u32 efct_get_num_ports(struct efct_device *efct_dev)
 static void efct_free_dev(struct efct_nic *efct)
 {
 	efct_unregister_netdev(efct);
+	devlink_port_unregister(&efct->dl_port);
 	efct->type->remove(efct);
 	efct_unmap_membase(efct);
 	free_netdev(efct->net_dev);
@@ -907,6 +909,9 @@ static int efct_pci_probe
 	rc = efct_alloc_msix(efct_dev);
 	if (rc)
 		goto fail2;
+	rc = efct_probe_devlink(efct_dev);
+	if (rc)
+		goto free_msi;
 	efct_nic_check_pcie_link(efct_dev, EFCT_BW_PCIE_GEN3_X8, NULL, NULL);
 
 	for (i = 0; i < efct_dev->num_ports; i++) {
@@ -957,6 +962,12 @@ static int efct_pci_probe
 			pci_err(pci_dev, "Unable to probe net device, rc=%d\n", rc);
 			goto fail6;
 		}
+		/*Devlink port register*/
+		rc = efct_devlink_port_register(efct);
+		if (rc) {
+			pci_err(pci_dev, "Unable to register devlink port, rc=%d\n", rc);
+			goto fail6;
+		}
 		rc = efct_register_netdev(efct);
 		if (rc) {
 			pci_err(pci_dev, "Unable to register net device, rc=%d\n", rc);
@@ -965,9 +976,14 @@ static int efct_pci_probe
 		/*Populating IRQ numbers in aux resources*/
 		efct_assign_msix(efct, i);
 	}
-	rc = efct_init_stats_wq(efct_dev);
+	rc = efct_devlink_register(efct_dev);
 	if (rc)
 		goto freep0;
+	rc = efct_init_stats_wq(efct_dev);
+	if (rc) {
+		efct_devlink_unregister(efct_dev);
+		goto freep0;
+	}
 
 	pr_info("EFCT X3 NIC detected: device %04x:%04x subsys %04x:%04x\n",
 		pci_dev->vendor, pci_dev->device,
@@ -976,6 +992,7 @@ static int efct_pci_probe
 	return 0;
 
 fail7:
+	devlink_port_unregister(&efct->dl_port);
 fail6:
 	efct->type->remove(efct);
 fail5:
@@ -987,6 +1004,8 @@ static int efct_pci_probe
 freep0:
 	for (j = 0; j < i ; j++)
 		efct_free_dev(efct_dev->efct[j]);
+	efct_fini_devlink(efct_dev);
+free_msi:
 	efct_free_msix(efct_dev);
 fail2:
 #ifdef CONFIG_EFCT_MCDI_LOGGING
@@ -1015,14 +1034,17 @@ static void efct_pci_remove(struct pci_dev *pci_dev)
 		return;
 
 	efct_fini_stats_wq(efct_dev);
+	efct_devlink_unregister(efct_dev);
 	for (i = 0; i < efct_dev->num_ports; i++) {
 		efct_close_netdev(efct_dev->efct[i]);
 		efct_remove_netdev(efct_dev->efct[i]);
+		devlink_port_unregister(&efct_dev->efct[i]->dl_port);
 		efct_dev->efct[i]->type->remove(efct_dev->efct[i]);
 		efct_flush_reset_workqueue();
 		efct_unmap_membase(efct_dev->efct[i]);
 		free_netdev(efct_dev->efct[i]->net_dev);
 	}
+	efct_fini_devlink(efct_dev);
 	efct_free_msix(efct_dev);
 #ifdef CONFIG_EFCT_MCDI_LOGGING
 	efct_fini_mcdi_logging(efct_dev);
diff --git a/drivers/net/ethernet/amd/efct/efct_reflash.c b/drivers/net/ethernet/amd/efct/efct_reflash.c
new file mode 100644
index 000000000000..9061dce05d4e
--- /dev/null
+++ b/drivers/net/ethernet/amd/efct/efct_reflash.c
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0
+/****************************************************************************
+ * Driver for AMD/Xilinx network controllers and boards
+ * Copyright (C) 2021, Xilinx, Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+#include <linux/crc32.h>
+
+#include "mcdi.h"
+#include "efct_reflash.h"
+#include "efct_devlink.h"
+#include "mcdi_functions.h"
+
+/* Reflash firmware data header and trailer fields (see SF-121352-AN) */
+#define EFCT_REFLASH_HEADER_MAGIC_OFST 0
+#define EFCT_REFLASH_HEADER_MAGIC_LEN 4
+#define EFCT_REFLASH_HEADER_MAGIC_VALUE 0x106F1A5
+
+#define EFCT_REFLASH_HEADER_VERSION_OFST 4
+#define EFCT_REFLASH_HEADER_VERSION_LEN 4
+#define EFCT_REFLASH_HEADER_VERSION_VALUE 4
+
+#define EFCT_REFLASH_HEADER_FIRMWARE_TYPE_OFST 8
+#define EFCT_REFLASH_HEADER_FIRMWARE_TYPE_LEN 4
+#define EFCT_REFLASH_FIRMWARE_TYPE_BOOTROM 0x2
+#define EFCT_REFLASH_FIRMWARE_TYPE_BUNDLE 0xd
+
+#define EFCT_REFLASH_HEADER_FIRMWARE_SUBTYPE_OFST 12
+#define EFCT_REFLASH_HEADER_FIRMWARE_SUBTYPE_LEN 4
+
+#define EFCT_REFLASH_HEADER_PAYLOAD_SIZE_OFST 16
+#define EFCT_REFLASH_HEADER_PAYLOAD_SIZE_LEN 4
+
+#define EFCT_REFLASH_HEADER_LENGTH_OFST 20
+#define EFCT_REFLASH_HEADER_LENGTH_LEN 4
+
+#define EFCT_REFLASH_HEADER_MINLEN	\
+	(EFCT_REFLASH_HEADER_LENGTH_OFST + EFCT_REFLASH_HEADER_LENGTH_LEN)
+
+#define EFCT_REFLASH_TRAILER_CRC_OFST 0
+#define EFCT_REFLASH_TRAILER_CRC_LEN 4
+
+#define EFCT_REFLASH_TRAILER_LEN	\
+	(EFCT_REFLASH_TRAILER_CRC_OFST + EFCT_REFLASH_TRAILER_CRC_LEN)
+
+static bool efct_reflash_parse_reflash_header(const struct firmware *fw,
+					      size_t header_offset, u32 *type,
+					     u32 *subtype, const u8 **data,
+					     size_t *data_size)
+{
+	u32 magic, version, payload_size, header_len, trailer_offset;
+	const u8 *header, *trailer;
+	u32 expected_crc, crc;
+
+	if (fw->size < header_offset + EFCT_REFLASH_HEADER_MINLEN)
+		return false;
+
+	header = fw->data + header_offset;
+	magic = get_unaligned_le32(header + EFCT_REFLASH_HEADER_MAGIC_OFST);
+	if (magic != EFCT_REFLASH_HEADER_MAGIC_VALUE)
+		return false;
+
+	version = get_unaligned_le32(header + EFCT_REFLASH_HEADER_VERSION_OFST);
+	if (version != EFCT_REFLASH_HEADER_VERSION_VALUE)
+		return false;
+
+	payload_size = get_unaligned_le32(header + EFCT_REFLASH_HEADER_PAYLOAD_SIZE_OFST);
+	header_len = get_unaligned_le32(header + EFCT_REFLASH_HEADER_LENGTH_OFST);
+	trailer_offset = header_offset + header_len + payload_size;
+	if (fw->size < trailer_offset + EFCT_REFLASH_TRAILER_LEN)
+		return false;
+
+	trailer = fw->data + trailer_offset;
+	expected_crc = get_unaligned_le32(trailer + EFCT_REFLASH_TRAILER_CRC_OFST);
+	crc = crc32_le(0, header, header_len + payload_size);
+	if (crc != expected_crc)
+		return false;
+
+	*type = get_unaligned_le32(header + EFCT_REFLASH_HEADER_FIRMWARE_TYPE_OFST);
+	*subtype = get_unaligned_le32(header + EFCT_REFLASH_HEADER_FIRMWARE_SUBTYPE_OFST);
+	if (*type == EFCT_REFLASH_FIRMWARE_TYPE_BUNDLE) {
+		/* All the bundle data is written verbatim to NVRAM */
+		*data = fw->data;
+		*data_size = fw->size;
+	} else {
+		/* Other payload types strip the reflash header and trailer
+		 * from the data written to NVRAM
+		 */
+		*data = header + header_len;
+		*data_size = payload_size;
+	}
+
+	return true;
+}
+
+static int efct_reflash_partition_type(u32 type, u32 subtype,
+				       u32 *partition_type,
+				      u32 *partition_subtype)
+{
+	int rc = 0;
+
+	/* Map from FIRMWARE_TYPE to NVRAM_PARTITION_TYPE */
+	switch (type) {
+	case EFCT_REFLASH_FIRMWARE_TYPE_BOOTROM:
+		*partition_type = NVRAM_PARTITION_TYPE_EXPANSION_ROM;
+		*partition_subtype = subtype;
+		break;
+	case EFCT_REFLASH_FIRMWARE_TYPE_BUNDLE:
+		*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
+		*partition_subtype = subtype;
+		break;
+	default:
+		/* Not supported */
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+/* SmartNIC image header fields */
+#define EFCT_SNICIMAGE_HEADER_MAGIC_OFST 16
+#define EFCT_SNICIMAGE_HEADER_MAGIC_LEN 4
+#define EFCT_SNICIMAGE_HEADER_MAGIC_VALUE 0x541C057A
+
+#define EFCT_SNICIMAGE_HEADER_VERSION_OFST 20
+#define EFCT_SNICIMAGE_HEADER_VERSION_LEN 4
+#define EFCT_SNICIMAGE_HEADER_VERSION_VALUE 1
+
+#define EFCT_SNICIMAGE_HEADER_LENGTH_OFST 24
+#define EFCT_SNICIMAGE_HEADER_LENGTH_LEN 4
+
+#define EFCT_SNICIMAGE_HEADER_PARTITION_TYPE_OFST 36
+#define EFCT_SNICIMAGE_HEADER_PARTITION_TYPE_LEN 4
+
+#define EFCT_SNICIMAGE_HEADER_PARTITION_SUBTYPE_OFST 40
+#define EFCT_SNICIMAGE_HEADER_PARTITION_SUBTYPE_LEN 4
+
+#define EFCT_SNICIMAGE_HEADER_PAYLOAD_SIZE_OFST 60
+#define EFCT_SNICIMAGE_HEADER_PAYLOAD_SIZE_LEN 4
+
+#define EFCT_SNICIMAGE_HEADER_CRC_OFST 64
+#define EFCT_SNICIMAGE_HEADER_CRC_LEN 4
+
+#define EFCT_SNICIMAGE_HEADER_MINLEN 256
+
+static bool efct_reflash_parse_snic_header(const struct firmware *fw,
+					   size_t header_offset,
+					  u32 *partition_type,
+					  u32 *partition_subtype,
+					  const u8 **data, size_t *data_size)
+{
+	u32 magic, version, payload_size, header_len, expected_crc, crc;
+	const u8 *header;
+
+	if (fw->size < header_offset + EFCT_SNICIMAGE_HEADER_MINLEN)
+		return false;
+
+	header = fw->data + header_offset;
+	magic = get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_MAGIC_OFST);
+	if (magic != EFCT_SNICIMAGE_HEADER_MAGIC_VALUE)
+		return false;
+
+	version = get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_VERSION_OFST);
+	if (version != EFCT_SNICIMAGE_HEADER_VERSION_VALUE)
+		return false;
+
+	header_len = get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_LENGTH_OFST);
+	payload_size = get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_PAYLOAD_SIZE_OFST);
+	if (fw->size < header_len + payload_size)
+		return false;
+
+	expected_crc = get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_CRC_OFST);
+
+	/* Calculate CRC omitting the expected CRC field itself */
+	crc = crc32_le(~0, header, EFCT_SNICIMAGE_HEADER_CRC_OFST);
+	crc = ~crc32_le(crc,
+			header + EFCT_SNICIMAGE_HEADER_CRC_OFST +
+			EFCT_SNICIMAGE_HEADER_CRC_LEN,
+			header_len + payload_size - EFCT_SNICIMAGE_HEADER_CRC_OFST -
+			EFCT_SNICIMAGE_HEADER_CRC_LEN);
+	if (crc != expected_crc)
+		return false;
+
+	*partition_type =
+		get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_PARTITION_TYPE_OFST);
+	*partition_subtype =
+		get_unaligned_le32(header + EFCT_SNICIMAGE_HEADER_PARTITION_SUBTYPE_OFST);
+	*data = fw->data;
+	*data_size = fw->size;
+	return true;
+}
+
+/* SmartNIC bundle header fields (see SF-122606-TC) */
+#define EFCT_SNICBUNDLE_HEADER_MAGIC_OFST 0
+#define EFCT_SNICBUNDLE_HEADER_MAGIC_LEN 4
+#define EFCT_SNICBUNDLE_HEADER_MAGIC_VALUE 0xB1001001
+
+#define EFCT_SNICBUNDLE_HEADER_VERSION_OFST 4
+#define EFCT_SNICBUNDLE_HEADER_VERSION_LEN 4
+#define EFCT_SNICBUNDLE_HEADER_VERSION_VALUE 1
+
+#define EFCT_SNICBUNDLE_HEADER_BUNDLE_TYPE_OFST 8
+#define EFCT_SNICBUNDLE_HEADER_BUNDLE_TYPE_LEN 4
+
+#define EFCT_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_OFST 12
+#define EFCT_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_LEN 4
+
+#define EFCT_SNICBUNDLE_HEADER_LENGTH_OFST 20
+#define EFCT_SNICBUNDLE_HEADER_LENGTH_LEN 4
+
+#define EFCT_SNICBUNDLE_HEADER_CRC_OFST 224
+#define EFCT_SNICBUNDLE_HEADER_CRC_LEN 4
+
+#define EFCT_SNICBUNDLE_HEADER_LEN	\
+	(EFCT_SNICBUNDLE_HEADER_CRC_OFST + EFCT_SNICBUNDLE_HEADER_CRC_LEN)
+
+static bool efct_reflash_parse_snic_bundle_header(const struct firmware *fw,
+						  size_t header_offset,
+						 u32 *partition_type,
+						 u32 *partition_subtype,
+						 const u8 **data,
+						 size_t *data_size)
+{
+	u32 magic, version, bundle_type, header_len, expected_crc, crc;
+	const u8 *header;
+
+	if (fw->size < header_offset + EFCT_SNICBUNDLE_HEADER_LEN)
+		return false;
+
+	header = fw->data + header_offset;
+	magic = get_unaligned_le32(header + EFCT_SNICBUNDLE_HEADER_MAGIC_OFST);
+	if (magic != EFCT_SNICBUNDLE_HEADER_MAGIC_VALUE)
+		return false;
+
+	version = get_unaligned_le32(header + EFCT_SNICBUNDLE_HEADER_VERSION_OFST);
+	if (version != EFCT_SNICBUNDLE_HEADER_VERSION_VALUE)
+		return false;
+
+	bundle_type = get_unaligned_le32(header + EFCT_SNICBUNDLE_HEADER_BUNDLE_TYPE_OFST);
+	if (bundle_type != NVRAM_PARTITION_TYPE_BUNDLE)
+		return false;
+
+	header_len = get_unaligned_le32(header + EFCT_SNICBUNDLE_HEADER_LENGTH_OFST);
+	if (header_len != EFCT_SNICBUNDLE_HEADER_LEN)
+		return false;
+
+	expected_crc = get_unaligned_le32(header + EFCT_SNICBUNDLE_HEADER_CRC_OFST);
+	crc = ~crc32_le(~0, header, EFCT_SNICBUNDLE_HEADER_CRC_OFST);
+	if (crc != expected_crc)
+		return false;
+
+	*partition_type = NVRAM_PARTITION_TYPE_BUNDLE;
+	*partition_subtype = get_unaligned_le32(header +
+			     EFCT_SNICBUNDLE_HEADER_BUNDLE_SUBTYPE_OFST);
+	*data = fw->data;
+	*data_size = fw->size;
+	return true;
+}
+
+static int efct_reflash_parse_firmware_data(const struct firmware *fw,
+					    u32 *partition_type,
+					   u32 *partition_subtype,
+					   const u8 **data, size_t *data_size)
+{
+	size_t header_offset;
+	u32 type, subtype;
+
+	/* Try to find a valid firmware payload in the firmware data.  Some
+	 * packaging formats (such as CMS/PKCS#7 signed images) prepend a
+	 * header for which finding the size is a non-trivial task.
+	 *
+	 * The checks are intended to reject firmware data that is clearly not
+	 * in the expected format.  They do not need to be exhaustive as the
+	 * running firmware will perform its own comprehensive validity and
+	 * compatibility checks during the update procedure.
+	 *
+	 * Firmware packages may contain multiple reflash images, e.g. a
+	 * bundle containing one or more other images.  Only check the
+	 * outermost container by stopping after the first candidate image
+	 * found even it is for an unsupported partition type.
+	 */
+	for (header_offset = 0; header_offset < fw->size; header_offset++) {
+		if (efct_reflash_parse_snic_bundle_header(fw, header_offset,
+							  partition_type,
+							 partition_subtype,
+							 data, data_size))
+			return 0;
+
+		if (efct_reflash_parse_snic_header(fw, header_offset,
+						   partition_type,
+						  partition_subtype, data,
+						  data_size))
+			return 0;
+
+		if (efct_reflash_parse_reflash_header(fw, header_offset, &type,
+						      &subtype, data, data_size))
+			return efct_reflash_partition_type(type, subtype,
+							  partition_type,
+							  partition_subtype);
+	}
+
+	return -EINVAL;
+}
+
+/* Limit the number of status updates during the erase or write phases */
+#define EFCT_DEVLINK_STATUS_UPDATE_COUNT		50
+
+/* Expected timeout for the efct_mcdi_nvram_update_finish_polled() */
+#define EFCT_DEVLINK_UPDATE_FINISH_TIMEOUT	900
+
+/* Ideal erase chunk size.  This is a balance between minimising the number of
+ * MCDI requests to erase an entire partition whilst avoiding tripping the MCDI
+ * RPC timeout.
+ */
+#define EFCT_NVRAM_ERASE_IDEAL_CHUNK_SIZE	(64 * 1024)
+
+static int efct_reflash_erase_partition(struct efct_nic *efct,
+					struct devlink *devlink, u32 type,
+				       size_t partition_size,
+				       size_t align)
+{
+	size_t chunk, offset, next_update;
+	int rc;
+
+	/* Partitions that cannot be erased or do not require erase before
+	 * write are advertised with a erase alignment/sector size of zero.
+	 */
+	if (align == 0)
+		/* Nothing to do */
+		return 0;
+
+	if (partition_size % align)
+		return -EINVAL;
+
+	/* Erase the entire NVRAM partition a chunk at a time to avoid
+	 * potentially tripping the MCDI RPC timeout.
+	 */
+	if (align >= EFCT_NVRAM_ERASE_IDEAL_CHUNK_SIZE)
+		chunk = align;
+	else
+		chunk = rounddown(EFCT_NVRAM_ERASE_IDEAL_CHUNK_SIZE, align);
+
+	for (offset = 0, next_update = 0; offset < partition_size; offset += chunk) {
+		if (offset >= next_update) {
+			devlink_flash_update_status_notify(devlink, "Erasing",
+							   NULL, offset,
+							   partition_size);
+			next_update += partition_size / EFCT_DEVLINK_STATUS_UPDATE_COUNT;
+		}
+
+		chunk = min_t(size_t, partition_size - offset, chunk);
+		rc = efct_mcdi_nvram_erase(efct, type, offset, chunk);
+		if (rc) {
+			netif_err(efct, hw, efct->net_dev,
+				  "Erase failed for NVRAM partition %#x at %#zx-%#zx with error %d\n",
+				  type, offset, offset + chunk - 1, rc);
+			return rc;
+		}
+	}
+
+	devlink_flash_update_status_notify(devlink, "Erasing", NULL,
+					   partition_size, partition_size);
+
+	return 0;
+}
+
+static int efct_reflash_write_partition(struct efct_nic *efct,
+					struct devlink *devlink, u32 type,
+				       const u8 *data, size_t data_size,
+				       size_t align)
+{
+	size_t write_max, chunk, offset, next_update;
+	int rc;
+
+	if (align == 0)
+		return -EINVAL;
+
+	/* Write the NVRAM partition in chunks that are the largest multiple
+	 * of the partiion's required write alignment that will fit into the
+	 * MCDI NVRAM_WRITE RPC payload.
+	 */
+	if (efct->type->mcdi_max_ver < 2)
+		write_max = MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_LEN *
+			    MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_MAXNUM;
+	else
+		write_max = MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_LEN *
+			    MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_MAXNUM_MCDI2;
+	chunk = rounddown(write_max, align);
+
+	for (offset = 0, next_update = 0; offset + chunk <= data_size; offset += chunk) {
+		if (offset >= next_update) {
+			devlink_flash_update_status_notify(devlink, "Writing",
+							   NULL, offset,
+							   data_size);
+			next_update += data_size / EFCT_DEVLINK_STATUS_UPDATE_COUNT;
+		}
+
+		rc = efct_mcdi_nvram_write(efct, type, offset, data + offset, chunk);
+		if (rc) {
+			netif_err(efct, hw, efct->net_dev,
+				  "Write failed for NVRAM partition %#x at %#zx-%#zx with error %d\n",
+				  type, offset, offset + chunk - 1, rc);
+			return rc;
+		}
+	}
+
+	/* Round up left over data to satisfy write alignment */
+	if (offset < data_size) {
+		size_t remaining = data_size - offset;
+		u8 *buf;
+
+		if (offset >= next_update)
+			devlink_flash_update_status_notify(devlink, "Writing",
+							   NULL, offset,
+							   data_size);
+
+		chunk = roundup(remaining, align);
+		buf = kmalloc(chunk, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+
+		memcpy(buf, data + offset, remaining);
+		memset(buf + remaining, 0xFF, chunk - remaining);
+		rc = efct_mcdi_nvram_write(efct, type, offset, buf, chunk);
+		kfree(buf);
+		if (rc) {
+			netif_err(efct, hw, efct->net_dev,
+				  "Write failed for NVRAM partition %#x at %#zx-%#zx with error %d\n",
+				  type, offset, offset + chunk - 1, rc);
+			return rc;
+		}
+	}
+
+	devlink_flash_update_status_notify(devlink, "Writing", NULL, data_size,
+					   data_size);
+
+	return 0;
+}
+
+int efct_reflash_flash_firmware(struct efct_nic *efct, const struct firmware *fw)
+{
+	struct devlink *devlink = efct->efct_dev->devlink;
+	size_t data_size, size, erase_align, write_align;
+	u32 type, data_subtype, subtype;
+	const u8 *data;
+	bool protected;
+	int rc;
+
+	if (!efct_has_cap(efct, BUNDLE_UPDATE)) {
+		netif_err(efct, hw, efct->net_dev,
+			  "NVRAM bundle updates are not supported by the firmware\n");
+		return -EOPNOTSUPP;
+	}
+
+	mutex_lock(&efct->reflash_mutex);
+
+	devlink_flash_update_status_notify(devlink, "Checking update", NULL, 0, 0);
+
+	rc = efct_reflash_parse_firmware_data(fw, &type, &data_subtype, &data,
+					      &data_size);
+	if (rc) {
+		netif_err(efct, drv, efct->net_dev,
+			  "Firmware image validation check failed with error %d\n",
+			  rc);
+		goto out;
+	}
+
+	rc = efct_mcdi_nvram_metadata(efct, type, &subtype, NULL, NULL, 0);
+	if (rc) {
+		netif_err(efct, drv, efct->net_dev,
+			  "Metadata query for NVRAM partition %#x failed with error %d\n",
+			  type, rc);
+		goto out;
+	}
+
+	if (subtype != data_subtype) {
+		netif_err(efct, drv, efct->net_dev,
+			  "Firmware image is not appropriate for this adapter");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = efct_mcdi_nvram_info(efct, type, &size, &erase_align, &write_align,
+				  &protected);
+	if (rc) {
+		netif_err(efct, hw, efct->net_dev,
+			  "Info query for NVRAM partition %#x failed with error %d\n",
+			  type, rc);
+		goto out;
+	}
+
+	if (protected) {
+		netif_err(efct, drv, efct->net_dev,
+			  "NVRAM partition %#x is protected\n", type);
+		rc = -EPERM;
+		goto out;
+	}
+
+	if (write_align == 0) {
+		netif_err(efct, drv, efct->net_dev,
+			  "NVRAM partition %#x is not writable\n", type);
+		rc = -EACCES;
+		goto out;
+	}
+
+	if (erase_align != 0 && size % erase_align) {
+		netif_err(efct, drv, efct->net_dev,
+			  "NVRAM partition %#x has a bad partition table entry and therefore is not erasable\n",
+			  type);
+		rc = -EACCES;
+		goto out;
+	}
+
+	if (data_size > size) {
+		netif_err(efct, drv, efct->net_dev,
+			  "Firmware image is too big for NVRAM partition %#x\n",
+			  type);
+		rc = -EFBIG;
+		goto out;
+	}
+
+	devlink_flash_update_status_notify(devlink, "Starting update", NULL, 0, 0);
+
+	rc = efct_mcdi_nvram_update_start(efct, type);
+	if (rc) {
+		netif_err(efct, hw, efct->net_dev,
+			  "Update start request for NVRAM partition %#x failed with error %d\n",
+			  type, rc);
+		goto out;
+	}
+
+	rc = efct_reflash_erase_partition(efct, devlink, type, size, erase_align);
+	if (rc)
+		goto out_update_finish;
+
+	rc = efct_reflash_write_partition(efct, devlink, type, data, data_size,
+					  write_align);
+	if (rc)
+		goto out_update_finish;
+
+	devlink_flash_update_timeout_notify(devlink, "Finishing update", NULL,
+					    EFCT_DEVLINK_UPDATE_FINISH_TIMEOUT);
+
+out_update_finish:
+	if (rc)
+		/* Don't obscure the return code from an earlier failure */
+		(void)efct_mcdi_nvram_update_finish(efct, type,
+						    EFCT_UPDATE_FINISH_ABORT);
+	else
+		rc = efct_mcdi_nvram_update_finish_polled(efct, type);
+
+out:
+	if (!rc) {
+		devlink_flash_update_status_notify(devlink, "Update complete",
+						   NULL, 0, 0);
+	} else {
+		devlink_flash_update_status_notify(devlink, "Update failed",
+						   NULL, 0, 0);
+	}
+
+	mutex_unlock(&efct->reflash_mutex);
+
+	return rc;
+}
diff --git a/drivers/net/ethernet/amd/efct/efct_reflash.h b/drivers/net/ethernet/amd/efct/efct_reflash.h
new file mode 100644
index 000000000000..d48b6e7a0675
--- /dev/null
+++ b/drivers/net/ethernet/amd/efct/efct_reflash.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0
+ ****************************************************************************
+ * Driver for AMD/Xilinx network controllers and boards
+ * Copyright (C) 2021, Xilinx, Inc.
+ * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _EFCT_REFLASH_H
+#define _EFCT_REFLASH_H
+
+#include "efct_driver.h"
+#include <linux/firmware.h>
+
+int efct_reflash_flash_firmware(struct efct_nic *efct, const struct firmware *fw);
+
+#endif  /* _EFCT_REFLASH_H */
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ