From fa186b09e346e0b7f2504232731bc9e4dee0c600 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:31 +0000
Subject: [PATCH 14/19] net: dsa: mxl862xx: add devlink flash_update and
 info_get

Implement runtime firmware upgrade via "devlink dev flash" and version
reporting via "devlink dev info":

  devlink dev info mdio_bus/<bus>/<addr>
  devlink dev flash mdio_bus/<bus>/<addr> file <firmware.bin>

The driver sends SYS_MISC_FW_UPDATE to enter MCUboot rescue mode,
transfers the signed image over the SB PDI bulk-transfer protocol
(clause-22 SMDIO), waits for the switch to reboot, then schedules
device_reprobe() for a clean remove()+probe() cycle.

Before the transfer begins the driver closes all conduit interfaces
and marks every netdev (user and conduit) not-present via
netif_device_detach() so that userspace cannot bring ports back up
during the ~15 minute flash process. Progress is reported through
devlink status notifications. Once the FW_UPDATE command has been
sent the switch is in MCUboot mode and normal operation can only be
restored by a reprobe, so the driver always schedules one regardless
of transfer outcome.

The reprobe work item is dynamically allocated (following the iwlwifi
pattern) because device_reprobe() triggers remove() which frees the
devm-managed priv while the work is still executing.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/net/dsa/mxl862xx/Makefile        |   2 +-
 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h  |   1 +
 drivers/net/dsa/mxl862xx/mxl862xx-fw.c   | 434 +++++++++++++++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-fw.h   |  15 +
 drivers/net/dsa/mxl862xx/mxl862xx-host.c |   7 +
 drivers/net/dsa/mxl862xx/mxl862xx.c      |   4 +
 drivers/net/dsa/mxl862xx/mxl862xx.h      |   2 +
 7 files changed, 464 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.c
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.h

--- a/drivers/net/dsa/mxl862xx/Makefile
+++ b/drivers/net/dsa/mxl862xx/Makefile
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
-mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o
+mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o mxl862xx-fw.o
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -83,6 +83,7 @@
 #define INT_GPHY_READ			(GPY_GPY2XX_MAGIC + 0x1)
 #define INT_GPHY_WRITE			(GPY_GPY2XX_MAGIC + 0x2)
 
+#define SYS_MISC_FW_UPDATE		(SYS_MISC_MAGIC + 0x1)
 #define SYS_MISC_FW_VERSION		(SYS_MISC_MAGIC + 0x2)
 
 #define MXL862XX_XPCS_MAGIC		0x1a00
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Firmware flash and devlink support for MaxLinear MxL862xx
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ *
+ * Usage:
+ *   # Query running firmware version:
+ *   devlink dev info mdio_bus/<bus>/<addr>
+ *
+ *   # Flash new firmware (all ports are taken down automatically):
+ *   devlink dev flash mdio_bus/<bus>/<addr> file <firmware.bin>
+ *
+ * The flash process takes approximately 15 minutes. Progress is
+ * reported via devlink status notifications. After a successful (or
+ * failed) flash the driver reprobes the device automatically.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/dsa.h>
+
+#include "mxl862xx.h"
+#include "mxl862xx-api.h"
+#include "mxl862xx-cmd.h"
+#include "mxl862xx-fw.h"
+#include "mxl862xx-host.h"
+
+/* SB PDI registers (clause-22 SMDIO address space) */
+#define MXL862XX_SB_PDI_CTRL		0xe100
+#define MXL862XX_SB_PDI_ADDR		0xe101
+#define MXL862XX_SB_PDI_DATA		0xe102
+#define MXL862XX_SB_PDI_STAT		0xe103
+
+/* SB PDI CTRL modes */
+#define MXL862XX_SB_PDI_CTRL_RST	0x00
+#define MXL862XX_SB_PDI_CTRL_WR	0x02
+
+/* SB PDI handshake magic */
+#define MXL862XX_SB_PDI_READY		0xc55c
+#define MXL862XX_SB_PDI_START		0xf48f
+#define MXL862XX_SB_PDI_END		0x3cc3
+
+/* Firmware transfer geometry */
+#define MXL862XX_FW_HDR_SIZE		20
+#define MXL862XX_FW_BANK_HALF		16384	/* words per half-bank */
+#define MXL862XX_FW_BANK_SLICE		32760	/* words per full slice */
+#define MXL862XX_FW_SB1_ADDR		0x7800	/* SB1 word address */
+
+/* Timeouts */
+#define MXL862XX_FW_READY_TIMEOUT_MS	30000
+#define MXL862XX_FW_ACK_TIMEOUT_MS	5000
+#define MXL862XX_FW_ERASE_TIMEOUT_MS	300000	/* flash erase is very slow */
+#define MXL862XX_FW_WRITE_TIMEOUT_MS	120000	/* per-slice program timeout */
+#define MXL862XX_FW_REBOOT_DELAY_MS	5000
+
+static void mxl862xx_sb_pdi_reset(struct mxl862xx_priv *priv)
+{
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+			     MXL862XX_SB_PDI_CTRL_RST);
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_ADDR,
+			     MXL862XX_SB_PDI_CTRL_RST);
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA,
+			     MXL862XX_SB_PDI_CTRL_RST);
+}
+
+static int mxl862xx_sb_pdi_poll_stat(struct mxl862xx_priv *priv, u16 expected,
+				     unsigned long timeout_ms)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	int ret;
+
+	do {
+		ret = mxl862xx_smdio_read(priv, MXL862XX_SB_PDI_STAT);
+		if (ret < 0)
+			return ret;
+		if ((u16)ret == expected)
+			return 0;
+		usleep_range(10000, 11000);
+	} while (time_before(jiffies, timeout));
+
+	return -ETIMEDOUT;
+}
+
+/* Reprobe work -- dynamically allocated so it survives remove().
+ * device_reprobe() -> remove() frees priv (devm) while work is executing,
+ * so the work struct must not live in mxl862xx_priv.
+ */
+struct mxl862xx_reprobe {
+	struct device *dev;
+	struct delayed_work dwork;
+};
+
+static void mxl862xx_reprobe_work_fn(struct work_struct *work)
+{
+	struct mxl862xx_reprobe *reprobe =
+		container_of(work, struct mxl862xx_reprobe, dwork.work);
+
+	if (device_reprobe(reprobe->dev))
+		dev_err(reprobe->dev, "reprobe failed\n");
+	put_device(reprobe->dev);
+	kfree(reprobe);
+	module_put(THIS_MODULE);
+}
+
+/* MCUboot firmware image header (20 bytes) */
+struct mxl862xx_fw_hdr {
+	__le32 image_type;
+	__le32 image_size_1;
+	__le32 image_checksum_1;
+	__le32 image_size_2;
+	__le32 image_checksum_2;
+} __packed;
+
+static int mxl862xx_flash_firmware(struct mxl862xx_priv *priv,
+				   const struct firmware *fw,
+				   struct devlink *dl)
+{
+	const struct mxl862xx_fw_hdr *hdr;
+	u32 word_idx = 0, data_written = 0, idx = 0;
+	unsigned long next_notify = 0;
+	const u8 *payload;
+	u32 payload_size;
+	u16 word, fdata;
+	int ret, i;
+	u32 crc;
+
+	if (fw->size < MXL862XX_FW_HDR_SIZE)
+		return -EINVAL;
+
+	hdr = (const struct mxl862xx_fw_hdr *)fw->data;
+	payload = fw->data + MXL862XX_FW_HDR_SIZE;
+	payload_size = le32_to_cpu(hdr->image_size_1) +
+		       le32_to_cpu(hdr->image_size_2);
+
+	if (payload_size > fw->size - MXL862XX_FW_HDR_SIZE) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: firmware file too small for declared size\n");
+		return -EINVAL;
+	}
+
+	/* Validate CRC-32 of both image slots before touching hardware */
+	if (le32_to_cpu(hdr->image_size_1)) {
+		crc = ~crc32_le(~0U, payload,
+				le32_to_cpu(hdr->image_size_1));
+		if (crc != le32_to_cpu(hdr->image_checksum_1)) {
+			dev_err(&priv->mdiodev->dev,
+				"flash: image 1 CRC mismatch (got %08x, expected %08x)\n",
+				crc, le32_to_cpu(hdr->image_checksum_1));
+			return -EINVAL;
+		}
+	}
+
+	if (le32_to_cpu(hdr->image_size_2)) {
+		crc = ~crc32_le(~0U,
+				payload + le32_to_cpu(hdr->image_size_1),
+				le32_to_cpu(hdr->image_size_2));
+		if (crc != le32_to_cpu(hdr->image_checksum_2)) {
+			dev_err(&priv->mdiodev->dev,
+				"flash: image 2 CRC mismatch (got %08x, expected %08x)\n",
+				crc, le32_to_cpu(hdr->image_checksum_2));
+			return -EINVAL;
+		}
+	}
+
+	/* Step 1: Tell firmware to enter MCUboot rescue mode.
+	 * The FW_UPDATE command takes no payload (size 0).
+	 */
+	ret = mxl862xx_api_wrap(priv, SYS_MISC_FW_UPDATE, NULL, 0,
+				false, false);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: FW_UPDATE command failed: %pe\n",
+			ERR_PTR(ret));
+		return ret;
+	}
+
+	/* From this point on, the switch is in MCUboot rescue mode.
+	 * Any failure must go through the end_magic label to tell
+	 * MCUboot to reboot rather than leaving it stuck waiting.
+	 */
+
+	/* Step 2: Reset PDI and wait for bootloader ready */
+	devlink_flash_update_status_notify(dl, "Waiting for bootloader",
+					   NULL, 0, 0);
+	mxl862xx_sb_pdi_reset(priv);
+	ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_SB_PDI_READY,
+					MXL862XX_FW_READY_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: bootloader not ready: %pe\n", ERR_PTR(ret));
+		goto end_magic;
+	}
+
+	/* Step 3: Start handshake */
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT,
+			     MXL862XX_SB_PDI_START);
+	ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_SB_PDI_START + 1,
+					MXL862XX_FW_ACK_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: start handshake failed: %pe\n", ERR_PTR(ret));
+		goto end_magic;
+	}
+
+	/* Step 4: Transfer 20-byte header using auto-increment write mode */
+	devlink_flash_update_status_notify(dl, "Erasing flash", NULL, 0, 0);
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+			     MXL862XX_SB_PDI_CTRL_WR);
+	for (i = 0; i < MXL862XX_FW_HDR_SIZE / 2; i++) {
+		word = fw->data[i * 2] |
+		       ((u16)fw->data[i * 2 + 1] << 8);
+		mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA, word);
+	}
+	mxl862xx_sb_pdi_reset(priv);
+
+	/* Write header byte count to STAT to trigger erase */
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT,
+			     MXL862XX_FW_HDR_SIZE);
+
+	/* Wait for header ACK (header_size + 1) */
+	ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_FW_HDR_SIZE + 1,
+					MXL862XX_FW_ACK_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: header ACK failed: %pe\n", ERR_PTR(ret));
+		goto end_magic;
+	}
+
+	/* Step 5: Wait for erase to complete (STAT goes to 0) */
+	ret = mxl862xx_sb_pdi_poll_stat(priv, 0,
+					MXL862XX_FW_ERASE_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: erase timeout: %pe\n", ERR_PTR(ret));
+		goto end_magic;
+	}
+
+	/* Step 6: Transfer payload using dual-bank auto-increment writes */
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+			     MXL862XX_SB_PDI_CTRL_WR);
+
+	while (idx < payload_size) {
+		if (idx + 1 < payload_size) {
+			fdata = payload[idx] |
+				((u16)payload[idx + 1] << 8);
+			idx += 2;
+			data_written += 2;
+		} else {
+			fdata = payload[idx];
+			idx++;
+			data_written++;
+		}
+
+		mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA, fdata);
+		word_idx++;
+
+		/* Last byte(s): flush final partial slice */
+		if (idx >= payload_size) {
+			mxl862xx_sb_pdi_reset(priv);
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT,
+					     data_written);
+			break;
+		}
+
+		/* Half-bank boundary: switch to SB1 address */
+		if (word_idx == MXL862XX_FW_BANK_HALF) {
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+					     MXL862XX_SB_PDI_CTRL_RST);
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_ADDR,
+					     MXL862XX_FW_SB1_ADDR);
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+					     MXL862XX_SB_PDI_CTRL_WR);
+		} else if (word_idx >= MXL862XX_FW_BANK_SLICE) {
+			/* Full slice: flush and wait for program */
+			mxl862xx_sb_pdi_reset(priv);
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT,
+					     data_written);
+			word_idx = 0;
+			data_written = 0;
+
+			ret = mxl862xx_sb_pdi_poll_stat(
+				priv, 0, MXL862XX_FW_WRITE_TIMEOUT_MS);
+			if (ret) {
+				dev_err(&priv->mdiodev->dev,
+					"flash: write timeout at %u/%u: %pe\n",
+					idx, payload_size, ERR_PTR(ret));
+				goto end_magic;
+			}
+			mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL,
+					     MXL862XX_SB_PDI_CTRL_WR);
+
+			if (time_after(jiffies, next_notify)) {
+				devlink_flash_update_status_notify(
+					dl, "Flashing", NULL,
+					idx, payload_size);
+				next_notify = jiffies +
+					      msecs_to_jiffies(500);
+			}
+		}
+	}
+
+	/* Wait for final slice to be programmed */
+	ret = mxl862xx_sb_pdi_poll_stat(priv, 0,
+					MXL862XX_FW_WRITE_TIMEOUT_MS);
+	if (ret) {
+		dev_err(&priv->mdiodev->dev,
+			"flash: final write timeout: %pe\n", ERR_PTR(ret));
+		goto end_magic;
+	}
+
+	devlink_flash_update_status_notify(dl, "Flashing", NULL,
+					   payload_size, payload_size);
+
+end_magic:
+	/* Always send end magic so MCUboot reboots instead of sitting
+	 * idle. The hardware reset during reprobe recovers the switch
+	 * regardless of whether the transfer succeeded or failed.
+	 */
+	mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT,
+			     MXL862XX_SB_PDI_END);
+	msleep(MXL862XX_FW_REBOOT_DELAY_MS);
+
+	return ret;
+}
+
+int mxl862xx_devlink_info_get(struct dsa_switch *ds,
+			      struct devlink_info_req *req,
+			      struct netlink_ext_ack *extack)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	char ver_str[32];
+
+	snprintf(ver_str, sizeof(ver_str), "%u.%u.%u",
+		 priv->fw_version.major, priv->fw_version.minor,
+		 priv->fw_version.revision);
+
+	return devlink_info_version_running_put(req, "fw", ver_str);
+}
+
+int mxl862xx_devlink_flash_update(struct dsa_switch *ds,
+				  struct devlink_flash_update_params *params,
+				  struct netlink_ext_ack *extack)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_sys_fw_image_version ver = {};
+	struct mxl862xx_reprobe *reprobe;
+	struct dsa_port *dp;
+	int ret, i;
+
+	if (params->component) {
+		NL_SET_ERR_MSG_MOD(extack, "component is not supported");
+		return -EOPNOTSUPP;
+	}
+
+	dev_info(ds->dev, "flash: running firmware %u.%u.%u\n",
+		 priv->fw_version.major, priv->fw_version.minor,
+		 priv->fw_version.revision);
+
+	/* Close all user and CPU ports while the firmware is still
+	 * alive. dev_close() on user ports triggers multicast group
+	 * leave and host MDB/FDB removal on the CPU port through the
+	 * normal DSA callbacks so the core's tracking lists are
+	 * drained before we enter MCUboot. Then mark user ports
+	 * not-present so userspace cannot bring them back up during
+	 * the (slow) flash process. The conduit is only closed, not
+	 * detached -- it is owned by the Ethernet MAC driver and
+	 * dev_open() during reprobe must be able to bring it back.
+	 */
+	rtnl_lock();
+	dsa_switch_for_each_user_port(dp, ds) {
+		if (dp->user) {
+			dev_close(dp->user);
+			netif_device_detach(dp->user);
+		}
+	}
+	dsa_switch_for_each_cpu_port(dp, ds)
+		dev_close(dp->conduit);
+	rtnl_unlock();
+
+	/* Block all firmware API commands while the switch is being
+	 * reflashed. The conduit is intentionally kept open -- it is
+	 * owned by the Ethernet MAC driver and would not recover on
+	 * reprobe if we closed it here.
+	 */
+	priv->block_host = true;
+
+	/* Stop stats polling and pending host-flood work */
+	cancel_delayed_work_sync(&priv->stats_work);
+	for (i = 0; i < ds->num_ports; i++)
+		cancel_work_sync(&priv->ports[i].host_flood_work);
+
+	ret = mxl862xx_flash_firmware(priv, params->fw, ds->devlink);
+	if (ret)
+		NL_SET_ERR_MSG_MOD(extack, "firmware transfer failed");
+
+	if (!ret) {
+		/* Read new firmware version (switch just rebooted).
+		 * Temporarily lift the block for this single query.
+		 */
+		priv->block_host = false;
+		memset(&ver, 0, sizeof(ver));
+		if (!MXL862XX_API_READ_QUIET(priv, SYS_MISC_FW_VERSION, ver)
+		    && ver.iv_major)
+			dev_info(ds->dev, "flash: new firmware %u.%u.%u\n",
+				 ver.iv_major, ver.iv_minor,
+				 le16_to_cpu(ver.iv_revision));
+	}
+
+	/* Silently discard all API commands during the teardown that
+	 * reprobe triggers -- the switch firmware has been reset and
+	 * has no knowledge of the old configuration.
+	 */
+	priv->skip_teardown = true;
+
+	reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
+	if (!reprobe)
+		return ret;
+
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(reprobe);
+		return ret;
+	}
+
+	reprobe->dev = get_device(ds->dev);
+	INIT_DELAYED_WORK(&reprobe->dwork, mxl862xx_reprobe_work_fn);
+	schedule_delayed_work(&reprobe->dwork, msecs_to_jiffies(500));
+
+	return ret;
+}
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __MXL862XX_FW_H
+#define __MXL862XX_FW_H
+
+#include <net/dsa.h>
+
+int mxl862xx_devlink_info_get(struct dsa_switch *ds,
+			      struct devlink_info_req *req,
+			      struct netlink_ext_ack *extack);
+int mxl862xx_devlink_flash_update(struct dsa_switch *ds,
+				  struct devlink_flash_update_params *params,
+				  struct netlink_ext_ack *extack);
+
+#endif /* __MXL862XX_FW_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
@@ -14,6 +14,7 @@
 #include <linux/limits.h>
 #include <net/dsa.h>
 #include "mxl862xx.h"
+#include "mxl862xx-cmd.h"
 #include "mxl862xx-host.h"
 
 #define CTRL_BUSY_MASK			BIT(15)
@@ -334,6 +335,12 @@ int mxl862xx_api_wrap(struct mxl862xx_pr
 	int ret, cmd_ret;
 	u16 max, crc, i;
 
+	if (priv->skip_teardown)
+		return 0;
+
+	if (priv->block_host && cmd != SYS_MISC_FW_UPDATE)
+		return -EBUSY;
+
 	dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data);
 
 	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -23,6 +23,7 @@
 #include "mxl862xx.h"
 #include "mxl862xx-api.h"
 #include "mxl862xx-cmd.h"
+#include "mxl862xx-fw.h"
 #include "mxl862xx-host.h"
 #include "mxl862xx-phylink.h"
 
@@ -4233,6 +4234,9 @@ static const struct dsa_switch_ops mxl86
 	.get_rmon_stats = mxl862xx_get_rmon_stats,
 	.get_stats64 = mxl862xx_get_stats64,
 	.self_test = mxl862xx_serdes_self_test,
+	.devlink_info_get = mxl862xx_devlink_info_get,
+	.devlink_flash_update = mxl862xx_devlink_flash_update,
+
 };
 
 static int mxl862xx_probe(struct mdio_device *mdiodev)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -395,6 +395,8 @@ struct mxl862xx_priv {
 	u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
 	u8 trunk_hash;
 	int mirror_dest;
+	bool block_host;
+	bool skip_teardown;
 	struct delayed_work stats_work;
 };
 
