--- /dev/null
+From 3fd163f5bb88de426ca9847549f94b4296170ef0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 30 Mar 2026 23:40:53 +0100
+Subject: [PATCH] net: dsa: mxl862xx: cancel pending work on probe error
+
+Call mxl862xx_host_shutdown() in case dsa_register_switch() returns
+an error, so any still pending crc_err_work get canceled.
+
+Fixes: a319d0c8c8ced ("net: dsa: mxl862xx: add CRC for MDIO communication)"
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
+@@ -407,6 +407,7 @@ static int mxl862xx_probe(struct mdio_de
+ struct device *dev = &mdiodev->dev;
+ struct mxl862xx_priv *priv;
+ struct dsa_switch *ds;
++ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -428,7 +429,11 @@ static int mxl862xx_probe(struct mdio_de
+
+ dev_set_drvdata(dev, ds);
+
+- return dsa_register_switch(ds);
++ err = dsa_register_switch(ds);
++ if (err)
++ mxl862xx_host_shutdown(priv);
++
++ return err;
+ }
+
+ static void mxl862xx_remove(struct mdio_device *mdiodev)
-From de6dd19a3edd1dc6400fecf77610e438441a02ac Mon Sep 17 00:00:00 2001
+From cd698f1ae94c16499e2714b31dd6048e6f9f068d Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:11 +0000
-Subject: [PATCH 10/35] net: dsa: move dsa_bridge_ports() helper to dsa.h
+Subject: [PATCH 01/26] net: dsa: move dsa_bridge_ports() helper to dsa.h
The yt921x driver contains a helper to create a bitmap of ports
which are members of a bridge.
-From 880cde7abf58cb1316382ae7f59aac93c313e8fe Mon Sep 17 00:00:00 2001
+From c161533e1605a7282563c139323a3913890fdb72 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:41 +0000
-Subject: [PATCH 11/35] net: dsa: add bridge member iteration macro
+Subject: [PATCH 02/26] net: dsa: add bridge member iteration macro
Drivers that offload bridges need to iterate over the ports that are
members of a given bridge, for example to rebuild per-port forwarding
-From 149bb02d5bf031a1eb85f91377f54913de3a08ff Mon Sep 17 00:00:00 2001
+From 753efe27a9afee52c4ad42098a9b9278366d63cc Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:52 +0000
-Subject: [PATCH 12/35] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
+Subject: [PATCH 03/26] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
The MxL862xx offloads bridge forwarding in hardware, so set
dsa_default_offload_fwd_mark() to avoid duplicate forwarding of
-From 5acdee6df2fbd4a9b02045694227f25cb1d4e5e0 Mon Sep 17 00:00:00 2001
+From ce0664ff8f75c3ab01101c3f0f8569924d948775 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:55:08 +0000
-Subject: [PATCH 13/35] net: dsa: mxl862xx: implement bridge offloading
+Subject: [PATCH 04/26] net: dsa: mxl862xx: implement bridge offloading
Implement joining and leaving bridges as well as add, delete and dump
operations on isolated FDBs, port MDB membership management, and
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++-
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 20 +-
- drivers/net/dsa/mxl862xx/mxl862xx.c | 752 ++++++++++++++++++++++--
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 743 ++++++++++++++++++++++--
drivers/net/dsa/mxl862xx/mxl862xx.h | 133 +++++
- 4 files changed, 1087 insertions(+), 43 deletions(-)
+ 4 files changed, 1076 insertions(+), 45 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol m)
-@@ -168,6 +182,225 @@ static int mxl862xx_setup_mdio(struct ds
+@@ -168,6 +182,213 @@ static int mxl862xx_setup_mdio(struct ds
return ret;
}
+ return ret;
+}
+
-+/**
-+ * mxl862xx_allocate_bridge - Allocate a firmware bridge instance
-+ * @priv: driver private data
-+ * @bridge_id: output -- firmware bridge ID assigned by the firmware
-+ *
-+ * Newly allocated bridges default to flooding all traffic classes
-+ * (unknown unicast, multicast, broadcast). Callers that need
-+ * different forwarding behavior must call mxl862xx_bridge_config_fwd()
-+ * after allocation.
-+ *
-+ * Return: 0 on success, negative errno on failure.
-+ */
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
+{
+ struct mxl862xx_bridge_alloc br_alloc = {};
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -181,6 +414,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -181,6 +402,10 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
return mxl862xx_setup_mdio(ds);
}
-@@ -260,66 +497,87 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -260,66 +485,87 @@ static int mxl862xx_configure_sp_tag_pro
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
bool is_cpu_port = dsa_port_is_cpu(dp);
int ret;
-@@ -352,7 +610,31 @@ static int mxl862xx_port_setup(struct ds
+@@ -352,7 +598,31 @@ static int mxl862xx_port_setup(struct ds
return mxl862xx_setup_cpu_bridge(ds, port);
/* setup single-port bridge for user ports */
}
static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
-@@ -365,14 +647,385 @@ static void mxl862xx_phylink_get_caps(st
+@@ -365,14 +635,383 @@ static void mxl862xx_phylink_get_caps(st
config->supported_interfaces);
}
+ mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
+
+ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
-+ /* No ports left — remove the entry entirely */
++ /* No ports left -- remove the entry entirely */
+ rparam.fid = cpu_to_le16(fid);
+ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
+ ether_addr_copy(rparam.mac, mdb->addr);
+/* Deferred work handler for host flood configuration.
+ *
+ * port_set_host_flood is called from atomic context (under
-+ * netif_addr_lock), so firmware calls must be deferred. The worker
++ * netif_addr_lock), so firmware calls must be deferred. The worker
+ * acquires rtnl_lock() to serialize with DSA callbacks that access the
+ * same driver state.
+ */
+ mc = p->host_flood_mc;
+
+ /* The hardware controls unknown-unicast/multicast forwarding per FID
-+ * (bridge), not per source port. For bridged ports all members share
++ * (bridge), not per source port. For bridged ports all members share
+ * one FID, so we cannot selectively suppress flooding to the CPU for
-+ * one source port while allowing it for another. Silently ignore the
++ * one source port while allowing it for another. Silently ignore the
+ * request -- the excess flooding towards the CPU is harmless.
+ */
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
+ struct mxl862xx_priv *priv = ds->priv;
+ unsigned long old_block = priv->ports[port].flood_block;
+ unsigned long block = old_block;
-+ bool need_update = false;
+ int ret;
+
+ if (flags.mask & BR_FLOOD) {
+ if (flags.mask & BR_LEARNING)
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
+
-+ need_update = (block != old_block) || (flags.mask & BR_LEARNING);
-+ if (need_update) {
++ if ((block != old_block) || (flags.mask & BR_LEARNING)) {
+ priv->ports[port].flood_block = block;
+ ret = mxl862xx_set_bridge_port(ds, port);
+ if (ret)
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -407,6 +1060,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -407,7 +1046,7 @@ static int mxl862xx_probe(struct mdio_de
struct device *dev = &mdiodev->dev;
struct mxl862xx_priv *priv;
struct dsa_switch *ds;
-+ int i;
+- int err;
++ int err, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
-@@ -424,8 +1078,17 @@ static int mxl862xx_probe(struct mdio_de
+@@ -425,14 +1064,25 @@ static int mxl862xx_probe(struct mdio_de
ds->ops = &mxl862xx_switch_ops;
ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
ds->num_ports = MXL862XX_MAX_PORTS;
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -435,6 +1098,7 @@ static void mxl862xx_remove(struct mdio_
+ err = dsa_register_switch(ds);
+- if (err)
++ if (err) {
+ mxl862xx_host_shutdown(priv);
+-
++ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
++ cancel_work_sync(&priv->ports[i].host_flood_work);
++ }
+ return err;
+ }
+
+@@ -440,6 +1090,7 @@ static void mxl862xx_remove(struct mdio_
{
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
struct mxl862xx_priv *priv;
if (!ds)
return;
-@@ -444,12 +1108,21 @@ static void mxl862xx_remove(struct mdio_
+@@ -449,12 +1100,21 @@ static void mxl862xx_remove(struct mdio_
dsa_unregister_switch(ds);
mxl862xx_host_shutdown(priv);
+
-+ /* Cancel any pending host flood work. dsa_unregister_switch()
++ /* Cancel any pending host flood work. dsa_unregister_switch()
+ * has already called port_teardown (which sets setup_done=false),
-+ * but a worker could still be blocked on rtnl_lock(). Since we
++ * but a worker could still be blocked on rtnl_lock(). Since we
+ * are now outside RTNL, cancel_work_sync() will not deadlock.
+ */
+ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
if (!ds)
return;
-@@ -460,6 +1133,9 @@ static void mxl862xx_shutdown(struct mdi
+@@ -465,6 +1125,9 @@ static void mxl862xx_shutdown(struct mdi
mxl862xx_host_shutdown(priv);
-From 7286ac4f850339aac37dd52633f4a70816b621a8 Mon Sep 17 00:00:00 2001
+From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 10 Mar 2026 02:36:00 +0000
-Subject: [PATCH 14/35] net: dsa: mxl862xx: implement VLAN functionality
+Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
Add VLAN support using both the Extended VLAN (EVLAN) engine and the
VLAN Filter (VF) engine in a hybrid architecture that allows a higher
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
- drivers/net/dsa/mxl862xx/mxl862xx.c | 960 +++++++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx.h | 110 ++-
- 4 files changed, 1386 insertions(+), 25 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
+ 4 files changed, 1344 insertions(+), 16 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
-@@ -329,13 +472,131 @@ static int mxl862xx_sync_bridge_members(
+@@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
return ret;
}
+ return 0;
+}
+
-+/**
-+ * mxl862xx_vf_init - Initialize per-port VF block software state
-+ * @vf: VLAN Filter block to initialize
-+ * @size: block size (entries per port)
-+ */
+static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
+{
+ vf->allocated = false;
+ INIT_LIST_HEAD(&vf->vids);
+}
+
-+/**
-+ * mxl862xx_vf_block_alloc - Allocate a VLAN Filter block from firmware
-+ * @priv: driver private data
-+ * @size: number of entries to allocate
-+ * @block_id: output -- block ID assigned by firmware
-+ *
-+ * Allocates a contiguous VLAN Filter block and configures it to discard
-+ * unmatched tagged frames (VID membership enforcement).
-+ */
+static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
+ u16 size, u16 *block_id)
+{
+ return 0;
+}
+
-+/**
-+ * mxl862xx_vf_entry_discard - Write a DISCARD entry to plug an unused slot
-+ * @priv: driver private data
-+ * @block_id: HW VLAN Filter block ID
-+ * @index: entry index within the block
-+ *
-+ * Unwritten VLAN Filter entries default to VID=0 / ALLOW which would
-+ * leak VID 0 traffic. This writes a DISCARD entry to plug the slot.
-+ */
+static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv,
+ u16 block_id, u16 index)
+{
+ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
+}
+
-+/**
-+ * mxl862xx_vf_alloc - Allocate the port's VF HW block
-+ * @priv: driver private data
-+ * @vf: VLAN Filter block (must have been initialized via mxl862xx_vf_init)
-+ *
-+ * Allocates the block and writes a DISCARD sentinel at index 0 so that
-+ * when active_count is 0, the single-entry scan window blocks VID-0
-+ * (which would otherwise match the zeroed-out default and be allowed).
-+ * Called once per port from port_setup.
-+ */
+static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv,
+ struct mxl862xx_vf_block *vf)
+{
+ return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
+}
+
- /**
- * mxl862xx_allocate_bridge - Allocate a firmware bridge instance
- * @priv: driver private data
- * @bridge_id: output -- firmware bridge ID assigned by the firmware
- *
- * Newly allocated bridges default to flooding all traffic classes
-- * (unknown unicast, multicast, broadcast). Callers that need
-+ * (unknown unicast, multicast, broadcast). Callers that need
- * different forwarding behavior must call mxl862xx_bridge_config_fwd()
- * after allocation.
- *
-@@ -404,6 +665,9 @@ static int mxl862xx_add_single_port_brid
+ static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
+ {
+ struct mxl862xx_bridge_alloc br_alloc = {};
+@@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
int ret;
ret = mxl862xx_reset(priv);
-@@ -414,6 +678,50 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
ret = mxl862xx_setup_drop_meter(ds);
if (ret)
return ret;
-@@ -495,27 +803,616 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
}
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -565,6 +1462,22 @@ static void mxl862xx_port_bridge_leave(s
+@@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
__set_bit(dp->cpu_dp->index, p->portmap);
p->flood_block = 0;
err = mxl862xx_set_bridge_port(ds, port);
if (err)
dev_err(ds->dev,
-@@ -614,6 +1527,28 @@ static int mxl862xx_port_setup(struct ds
+@@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
priv->ports[port].setup_done = true;
return 0;
-@@ -808,7 +1743,7 @@ static int mxl862xx_port_mdb_del(struct
- mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
-
- if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
-- /* No ports left — remove the entry entirely */
-+ /* No ports left -- remove the entry entirely */
- rparam.fid = cpu_to_le16(fid);
- rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
- ether_addr_copy(rparam.mac, mdb->addr);
-@@ -899,7 +1834,7 @@ static void mxl862xx_port_stp_state_set(
- /* Deferred work handler for host flood configuration.
- *
- * port_set_host_flood is called from atomic context (under
-- * netif_addr_lock), so firmware calls must be deferred. The worker
-+ * netif_addr_lock), so firmware calls must be deferred. The worker
- * acquires rtnl_lock() to serialize with DSA callbacks that access the
- * same driver state.
- */
-@@ -924,9 +1859,9 @@ static void mxl862xx_host_flood_work_fn(
- mc = p->host_flood_mc;
-
- /* The hardware controls unknown-unicast/multicast forwarding per FID
-- * (bridge), not per source port. For bridged ports all members share
-+ * (bridge), not per source port. For bridged ports all members share
- * one FID, so we cannot selectively suppress flooding to the CPU for
-- * one source port while allowing it for another. Silently ignore the
-+ * one source port while allowing it for another. Silently ignore the
- * request -- the excess flooding towards the CPU is harmless.
- */
- if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
-@@ -1026,6 +1961,9 @@ static const struct dsa_switch_ops mxl86
+@@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -1111,7 +2049,7 @@ static void mxl862xx_remove(struct mdio_
-
- /* Cancel any pending host flood work. dsa_unregister_switch()
- * has already called port_teardown (which sets setup_done=false),
-- * but a worker could still be blocked on rtnl_lock(). Since we
-+ * but a worker could still be blocked on rtnl_lock(). Since we
- * are now outside RTNL, cancel_work_sync() will not deadlock.
- */
- for (i = 0; i < MXL862XX_MAX_PORTS; i++)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -13,6 +13,8 @@ struct mxl862xx_priv;
/* Number of __le16 words in a firmware portmap (128-bit bitmap). */
#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
-@@ -86,12 +88,72 @@ static inline bool mxl862xx_fw_portmap_i
+@@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
}
/**
* struct mxl862xx_port - per-port state tracked by the driver
* @priv: back-pointer to switch private data; needed by
* deferred work handlers to access ds and priv
-- * @fid: firmware FID for the permanent single-port bridge;
-- * kept alive for the lifetime of the port so traffic is
-- * never forwarded while the port is unbridged
-+ * @fid: firmware FID for the permanent single-port bridge; kept
-+ * alive for the lifetime of the port so traffic is never
-+ * forwarded while the port is unbridged
- * @portmap: bitmap of switch port indices that share the current
- * bridge with this port
- * @flood_block: bitmask of firmware meter indices that are currently
@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
* @setup_done: set at end of port_setup, cleared at start of
* port_teardown; guards deferred work against
-From 03b583e774835f771dd7c3c265be5903f008e8e5 Mon Sep 17 00:00:00 2001
+From 0067d79d10becfc5779fb50d5c0ac152cc5dc303 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:57:33 +0000
-Subject: [PATCH 15/35] net: dsa: mxl862xx: add ethtool statistics support
+Subject: [PATCH 06/26] net: dsa: mxl862xx: add ethtool statistics support
The MxL862xx firmware exposes per-port RMON counters through the
RMON_PORT_GET command, covering standard IEEE 802.3 MAC statistics
#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6))
#define MXL862XX_SDMA_PCTRL_EN BIT(0)
-@@ -1940,6 +1998,110 @@ static int mxl862xx_port_bridge_flags(st
+@@ -1893,6 +1951,110 @@ static int mxl862xx_port_bridge_flags(st
return 0;
}
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.setup = mxl862xx_setup,
-@@ -1964,6 +2126,12 @@ static const struct dsa_switch_ops mxl86
+@@ -1917,6 +2079,12 @@ static const struct dsa_switch_ops mxl86
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
-From 8b66d20f7e5226f4854a39cfb9f25a0591a5bb83 Mon Sep 17 00:00:00 2001
+From bab5a69e3872a693069e430a1fa0d2825ea83b4f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 04:14:38 +0000
-Subject: [PATCH 16/35] net: dsa: mxl862xx: implement .get_stats64
+Subject: [PATCH 07/26] net: dsa: mxl862xx: implement .get_stats64
Poll free-running firmware RMON counters every 2 seconds and accumulate
deltas into 64-bit per-port statistics. 32-bit packet counters wrap
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 167 ++++++++++++++++++++++++++++
- drivers/net/dsa/mxl862xx/mxl862xx.h | 51 +++++++++
- 2 files changed, 218 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx-host.c | 8 +-
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 174 +++++++++++++++++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 63 +++++++-
+ 3 files changed, 238 insertions(+), 7 deletions(-)
+--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
+@@ -48,7 +48,7 @@ static void mxl862xx_crc_err_work_fn(str
+ dev_close(dp->conduit);
+ rtnl_unlock();
+
+- clear_bit(0, &priv->crc_err);
++ clear_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags);
+ }
+
+ /* Firmware CRC error codes (outside normal Zephyr errno range). */
+@@ -247,7 +247,7 @@ static int mxl862xx_issue_cmd(struct mxl
+
+ ret = mxl862xx_crc6_verify(ctrl_enc, len_enc, &fw_result);
+ if (ret) {
+- if (!test_and_set_bit(0, &priv->crc_err))
++ if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ return -EIO;
+ }
+@@ -314,7 +314,7 @@ static int mxl862xx_send_cmd(struct mxl8
+ if (ret < 0) {
+ if ((ret == MXL862XX_FW_CRC6_ERR ||
+ ret == MXL862XX_FW_CRC16_ERR) &&
+- !test_and_set_bit(0, &priv->crc_err))
++ !test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ if (!quiet)
+ dev_err(&priv->mdiodev->dev,
+@@ -458,7 +458,7 @@ int mxl862xx_api_wrap(struct mxl862xx_pr
+ }
+
+ if (crc16(0xffff, (const u8 *)data, size) != crc) {
+- if (!test_and_set_bit(0, &priv->crc_err))
++ if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ ret = -EIO;
+ goto out;
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -30,6 +30,12 @@
struct mxl862xx_mib_desc {
unsigned int size;
unsigned int offset;
-@@ -784,6 +790,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -739,6 +745,9 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
return mxl862xx_setup_mdio(ds);
}
-@@ -2102,6 +2111,156 @@ static void mxl862xx_get_pause_stats(str
+@@ -2055,6 +2064,158 @@ static void mxl862xx_get_pause_stats(str
pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
}
+ dsa_switch_for_each_available_port(dp, ds)
+ mxl862xx_stats_poll(ds, dp->index);
+
-+ schedule_delayed_work(&priv->stats_work,
-+ MXL862XX_STATS_POLL_INTERVAL);
++ if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags))
++ schedule_delayed_work(&priv->stats_work,
++ MXL862XX_STATS_POLL_INTERVAL);
+}
+
+static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,
+ spin_unlock_bh(&priv->ports[port].stats_lock);
+
+ /* Trigger a fresh poll so the next read sees up-to-date counters.
-+ * No-op if the work is already pending or running.
++ * No-op if the work is already pending, running, or teardown started.
+ */
-+ schedule_delayed_work(&priv->stats_work, 0);
++ if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags))
++ schedule_delayed_work(&priv->stats_work, 0);
+}
+
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.setup = mxl862xx_setup,
-@@ -2132,6 +2291,7 @@ static const struct dsa_switch_ops mxl86
+@@ -2085,6 +2246,7 @@ static const struct dsa_switch_ops mxl86
.get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
.get_pause_stats = mxl862xx_get_pause_stats,
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -2193,8 +2353,11 @@ static int mxl862xx_probe(struct mdio_de
+@@ -2146,16 +2308,22 @@ static int mxl862xx_probe(struct mdio_de
priv->ports[i].priv = priv;
INIT_WORK(&priv->ports[i].host_flood_work,
mxl862xx_host_flood_work_fn);
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -2213,6 +2376,8 @@ static void mxl862xx_remove(struct mdio_
+ err = dsa_register_switch(ds);
+ if (err) {
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
++ cancel_delayed_work_sync(&priv->stats_work);
+ mxl862xx_host_shutdown(priv);
+ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
+ cancel_work_sync(&priv->ports[i].host_flood_work);
+ }
++
+ return err;
+ }
- dsa_unregister_switch(ds);
+@@ -2170,6 +2338,9 @@ static void mxl862xx_remove(struct mdio_
+
+ priv = ds->priv;
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+
- mxl862xx_host_shutdown(priv);
+ dsa_unregister_switch(ds);
- /* Cancel any pending host flood work. dsa_unregister_switch()
-@@ -2237,6 +2402,8 @@ static void mxl862xx_shutdown(struct mdi
+ mxl862xx_host_shutdown(priv);
+@@ -2196,6 +2367,9 @@ static void mxl862xx_shutdown(struct mdi
dsa_switch_shutdown(ds);
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+
mxl862xx_host_shutdown(priv);
*/
struct mxl862xx_port {
struct mxl862xx_priv *priv;
-@@ -195,6 +240,9 @@ struct mxl862xx_port {
+@@ -195,16 +240,25 @@ struct mxl862xx_port {
bool host_flood_uc;
bool host_flood_mc;
struct work_struct host_flood_work;
+ spinlock_t stats_lock;
};
++/* Bit indices for struct mxl862xx_priv::flags */
++#define MXL862XX_FLAG_CRC_ERR 0
++#define MXL862XX_FLAG_WORK_STOPPED 1
++
/**
-@@ -216,6 +264,8 @@ struct mxl862xx_port {
+ * struct mxl862xx_priv - driver private data for an MxL862xx switch
+ * @ds: pointer to the DSA switch instance
+ * @mdiodev: MDIO device used to communicate with the switch firmware
+ * @crc_err_work: deferred work for shutting down all ports on MDIO CRC
+ * errors
+- * @crc_err: set atomically before CRC-triggered shutdown, cleared
+- * after
++ * @flags: atomic status flags; %MXL862XX_FLAG_CRC_ERR is set
++ * before CRC-triggered shutdown and cleared after;
++ * %MXL862XX_FLAG_WORK_STOPPED is set before cancelling
++ * stats_work to prevent rescheduling during teardown
+ * @drop_meter: index of the single shared zero-rate firmware meter
+ * used to unconditionally drop traffic (used to block
+ * flooding)
+@@ -216,18 +270,21 @@ struct mxl862xx_port {
* @evlan_ingress_size: per-port ingress Extended VLAN block size
* @evlan_egress_size: per-port egress Extended VLAN block size
* @vf_block_size: per-port VLAN Filter block size
*/
struct mxl862xx_priv {
struct dsa_switch *ds;
-@@ -228,6 +278,7 @@ struct mxl862xx_priv {
+ struct mdio_device *mdiodev;
+ struct work_struct crc_err_work;
+- unsigned long crc_err;
++ unsigned long flags;
+ u16 drop_meter;
+ struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
+ u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
u16 evlan_egress_size;
u16 vf_block_size;
-From fecfbea928cd762b19ff17aa16fb1ab143d73db1 Mon Sep 17 00:00:00 2001
+From da12469e73282da814163125153f381823e33f20 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 17:56:35 +0000
-Subject: [PATCH 17/35] net: dsa: mxl862xx: store firmware version for feature
+Subject: [PATCH 08/26] net: dsa: mxl862xx: store firmware version for feature
gating
Query the firmware version at init (already done in wait_ready),
#include <linux/mdio.h>
#include <linux/workqueue.h>
#include <net/dsa.h>
-@@ -246,6 +247,38 @@ struct mxl862xx_port {
+@@ -245,6 +246,38 @@ struct mxl862xx_port {
+ spinlock_t stats_lock;
};
- /**
++/**
+ * union mxl862xx_fw_version - firmware version for comparison and display
+ * @major: firmware major version
+ * @minor: firmware minor version
+#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \
+ ((priv)->fw_version.raw >= MXL862XX_FW_VER(maj, min, rev))
+
-+/**
- * struct mxl862xx_priv - driver private data for an MxL862xx switch
- * @ds: pointer to the DSA switch instance
- * @mdiodev: MDIO device used to communicate with the switch firmware
-@@ -256,6 +289,8 @@ struct mxl862xx_port {
+ /* Bit indices for struct mxl862xx_priv::flags */
+ #define MXL862XX_FLAG_CRC_ERR 0
+ #define MXL862XX_FLAG_WORK_STOPPED 1
+@@ -262,6 +295,8 @@ struct mxl862xx_port {
* @drop_meter: index of the single shared zero-rate firmware meter
* used to unconditionally drop traffic (used to block
* flooding)
* @ports: per-port state, indexed by switch port number
* @bridges: maps DSA bridge number to firmware bridge ID;
* zero means no firmware bridge allocated for that
-@@ -273,6 +308,7 @@ struct mxl862xx_priv {
+@@ -279,6 +314,7 @@ struct mxl862xx_priv {
struct work_struct crc_err_work;
- unsigned long crc_err;
+ unsigned long flags;
u16 drop_meter;
+ union mxl862xx_fw_version fw_version;
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
-From 3cb224514226928df80e43ca2280c7dca654bdfe Mon Sep 17 00:00:00 2001
+From f7606470d398e4091e1bc405bf2125dc5fc99919 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 21:39:30 +0000
-Subject: [PATCH 18/35] net: dsa: mxl862xx: move phylink stubs to
+Subject: [PATCH 09/26] net: dsa: mxl862xx: move phylink stubs to
mxl862xx-phylink.c
Move the phylink MAC operations and get_caps callback from mxl862xx.c
#define MXL862XX_API_WRITE(dev, cmd, data) \
mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false)
-@@ -1642,16 +1643,6 @@ static void mxl862xx_port_teardown(struc
+@@ -1597,16 +1598,6 @@ static void mxl862xx_port_teardown(struc
priv->ports[port].setup_done = false;
}
static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -2297,33 +2288,6 @@ static const struct dsa_switch_ops mxl86
+@@ -2252,33 +2243,6 @@ static const struct dsa_switch_ops mxl86
.get_stats64 = mxl862xx_get_stats64,
};
-From de41d438c4e90876449715a307dd03fa37338742 Mon Sep 17 00:00:00 2001
+From e583eeeb907f0abeef2082162293a5d63b9fd6fa Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 26 Mar 2026 01:50:00 +0000
-Subject: [PATCH 19/35] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
+Subject: [PATCH 10/26] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
Move the MXL862XX_API_WRITE, MXL862XX_API_READ and
MXL862XX_API_READ_QUIET convenience macros from mxl862xx.c to
-From 88f46eb32d1aed296af2005c3ed8f23a6eea64c3 Mon Sep 17 00:00:00 2001
+From e25f0886853607e4a6d1157ae84e43e8224cf3b7 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:57:44 +0000
-Subject: [PATCH 20/35] net: dsa: mxl862xx: add support for SerDes ports
+Subject: [PATCH 11/26] net: dsa: mxl862xx: add support for SerDes ports
The MxL862xx has two XPCS/SerDes interfaces (XPCS0 for ports 9-12,
XPCS1 for ports 13-16). Each can operate in various single-lane
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -729,7 +729,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -684,7 +684,7 @@ static int mxl862xx_setup(struct dsa_swi
int n_user_ports = 0, max_vlans;
int ingress_finals, vid_rules;
struct dsa_port *dp;
ret = mxl862xx_reset(priv);
if (ret)
-@@ -739,6 +739,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -694,6 +694,9 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
* union mxl862xx_fw_version - firmware version for comparison and display
* @major: firmware major version
* @minor: firmware minor version
-@@ -291,6 +307,8 @@ union mxl862xx_fw_version {
+@@ -297,6 +313,8 @@ union mxl862xx_fw_version {
* flooding)
* @fw_version: cached firmware version, populated at probe and
* compared with MXL862XX_FW_VER_MIN()
* @ports: per-port state, indexed by switch port number
* @bridges: maps DSA bridge number to firmware bridge ID;
* zero means no firmware bridge allocated for that
-@@ -309,6 +327,7 @@ struct mxl862xx_priv {
- unsigned long crc_err;
+@@ -315,6 +333,7 @@ struct mxl862xx_priv {
+ unsigned long flags;
u16 drop_meter;
union mxl862xx_fw_version fw_version;
+ struct mxl862xx_pcs serdes_ports[8];
-From d40565e2e00fc2c8f04b9c571fcbea2f146db844 Mon Sep 17 00:00:00 2001
+From 24d752291784e30d7329bed15744bbbc6a3e2485 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:14:33 +0000
-Subject: [PATCH 21/35] net: dsa: mxl862xx: add SerDes ethtool statistics
+Subject: [PATCH 12/26] net: dsa: mxl862xx: add SerDes ethtool statistics
Expose SerDes equalization and signal detect parameters as ethtool
statistics on ports 9-16 (XPCS-backed ports). Uses the XPCS EQ_GET
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -2007,6 +2007,8 @@ static void mxl862xx_get_strings(struct
+@@ -1960,6 +1960,8 @@ static void mxl862xx_get_strings(struct
for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++)
ethtool_puts(&data, mxl862xx_mib[i].name);
}
static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
-@@ -2014,7 +2016,7 @@ static int mxl862xx_get_sset_count(struc
+@@ -1967,7 +1969,7 @@ static int mxl862xx_get_sset_count(struc
if (sset != ETH_SS_STATS)
return 0;
}
static int mxl862xx_read_rmon(struct dsa_switch *ds, int port,
-@@ -2050,6 +2052,8 @@ static void mxl862xx_get_ethtool_stats(s
+@@ -2003,6 +2005,8 @@ static void mxl862xx_get_ethtool_stats(s
else
*data++ = le64_to_cpu(*(__le64 *)field);
}
-From 54dd5fabc543f8538202367a863eb0e9161bacab Mon Sep 17 00:00:00 2001
+From ee227a5e4c74f599cc1b34578b32214d5873ad2f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:15:32 +0000
-Subject: [PATCH 22/35] net: dsa: mxl862xx: add SerDes self-test via PRBS and
+Subject: [PATCH 13/26] net: dsa: mxl862xx: add SerDes self-test via PRBS and
BERT
Implement the dsa_switch_ops.self_test callback for SerDes ports
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -2286,6 +2286,7 @@ static const struct dsa_switch_ops mxl86
+@@ -2241,6 +2241,7 @@ static const struct dsa_switch_ops mxl86
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
.get_pause_stats = mxl862xx_get_pause_stats,
.get_stats64 = mxl862xx_get_stats64,
-From dd62e68cd0bd29934c3efbce687d5e103cc4b331 Mon Sep 17 00:00:00 2001
+From 43eb3eed250ea4e7e83371fcbf2bfb8d626eade6 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:13 +0000
-Subject: [PATCH 23/35] net: dsa: mxl862xx: trap link-local frames to the CPU
+Subject: [PATCH 14/26] net: dsa: mxl862xx: trap link-local frames to the CPU
port
Install per-CTP PCE rules on each user port that trap IEEE 802.1D
static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
{
struct mxl862xx_bridge_port_config br_port_cfg = {};
-@@ -1594,6 +1658,11 @@ static int mxl862xx_port_setup(struct ds
+@@ -1549,6 +1613,11 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
-From 3bba25f7ba35e3bca8230bd37ffb612944dbf301 Mon Sep 17 00:00:00 2001
+From e18f5b235d8df21209c73f4f0bbc00cc3a1973ba Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:21 +0000
-Subject: [PATCH 24/35] net: dsa: mxl862xx: warn about old firmware default PCE
+Subject: [PATCH 15/26] net: dsa: mxl862xx: warn about old firmware default PCE
rules
Firmware versions older than 1.0.80 install global PCE rules at
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -854,6 +854,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -809,6 +809,10 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
-From 1687c5632dfd80461b12425b943e30555faa3dd4 Mon Sep 17 00:00:00 2001
+From 04929904d3a7d824593587e52e3ed21d6f0f109a Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:58:04 +0000
-Subject: [PATCH 25/35] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
+Subject: [PATCH 16/26] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
The MxL862xx native 8-byte special tag (SpTag) requires firmware
support on the switch CPU and is not compatible with all SoC Ethernet
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 221 +++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 1626 ++++++++++++++++++++---
- drivers/net/dsa/mxl862xx/mxl862xx.h | 21 +-
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 13 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 7 +
net/dsa/Makefile | 1 +
net/dsa/tag_mxl862xx_8021q.c | 59 +
- 9 files changed, 1738 insertions(+), 202 deletions(-)
+ 9 files changed, 1736 insertions(+), 196 deletions(-)
create mode 100644 net/dsa/tag_mxl862xx_8021q.c
--- a/drivers/net/dsa/mxl862xx/Kconfig
err = mxl862xx_set_bridge_port(ds, port);
if (err)
-@@ -762,7 +909,6 @@ static void mxl862xx_free_bridge(struct
+@@ -717,7 +864,6 @@ static void mxl862xx_free_bridge(struct
static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
{
struct mxl862xx_priv *priv = ds->priv;
int ret;
-@@ -774,15 +920,27 @@ static int mxl862xx_add_single_port_brid
+@@ -729,15 +875,27 @@ static int mxl862xx_add_single_port_brid
priv->ports[port].learning = false;
bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
- /* Standalone ports should not flood unknown unicast or multicast
- * towards the CPU by default; only broadcast is needed initially.
-- */
+ /* In tag_8021q mode the TX path goes through the bridge engine
+ * (CTP ingress EVLAN reassigns to a virtual bridge port which
+ * then forwards via the bridge). With learning disabled on
+ * In native SpTag mode, TX bypasses the bridge engine entirely
+ * (the special tag selects the egress port directly), so flood
+ * control only affects CPU-bound traffic and can be restrictive.
-+ */
+ */
+ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q)
+ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
+ true, true, true);
return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
false, false, true);
}
-@@ -790,10 +948,12 @@ static int mxl862xx_add_single_port_brid
+@@ -745,10 +903,12 @@ static int mxl862xx_add_single_port_brid
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
ret = mxl862xx_reset(priv);
if (ret)
-@@ -806,7 +966,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -761,7 +921,7 @@ static int mxl862xx_setup(struct dsa_swi
for (i = 0; i < 8; i++)
mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9);
* With VLAN Filter handling VID membership checks:
* Ingress: only final catchall rules (PVID insertion, 802.1Q
* accept, non-8021Q TPID handling, discard).
-@@ -814,40 +974,67 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -769,40 +929,67 @@ static int mxl862xx_setup(struct dsa_swi
* ingress EVLAN rules are needed. (7 entries.)
* Egress: 2 rules per VID that needs tag stripping (untagged VIDs).
* No egress final catchalls -- VLAN Filter does the discard.
}
ret = mxl862xx_setup_drop_meter(ds);
-@@ -858,6 +1045,68 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -813,6 +1000,68 @@ static int mxl862xx_setup(struct dsa_swi
dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules "
"that interfere with DSA operation, please update\n");
schedule_delayed_work(&priv->stats_work,
MXL862XX_STATS_POLL_INTERVAL);
-@@ -939,6 +1188,52 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -894,6 +1143,52 @@ static int mxl862xx_configure_sp_tag_pro
}
/**
* mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
* @priv: driver private data
* @block_id: HW Extended VLAN block ID
-@@ -947,6 +1242,7 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -902,6 +1197,7 @@ static int mxl862xx_configure_sp_tag_pro
* @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
* @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
* @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
*
* Translates a compact rule descriptor into a full firmware
* mxl862xx_extendedvlan_config struct and writes it via the API.
-@@ -954,7 +1250,8 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -909,7 +1205,8 @@ static int mxl862xx_configure_sp_tag_pro
static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
u16 block_id, u16 entry_index,
const struct mxl862xx_evlan_rule_desc *desc,
{
struct mxl862xx_extendedvlan_config cfg = {};
struct mxl862xx_extendedvlan_filter_vlan *fv;
-@@ -1044,6 +1341,31 @@ static int mxl862xx_evlan_write_rule(str
+@@ -999,6 +1296,31 @@ static int mxl862xx_evlan_write_rule(str
cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
}
break;
}
return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
-@@ -1104,7 +1426,7 @@ static int mxl862xx_evlan_write_final_ru
+@@ -1059,7 +1381,7 @@ static int mxl862xx_evlan_write_final_ru
for (i = 0; i < n_rules; i++) {
ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
start_idx + i, &rules[i],
if (ret)
return ret;
}
-@@ -1273,6 +1595,27 @@ static int mxl862xx_vf_del_vid(struct mx
+@@ -1228,6 +1550,27 @@ static int mxl862xx_vf_del_vid(struct mx
}
/**
* mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
* @priv: driver private data
* @port: port number
-@@ -1323,8 +1666,8 @@ static int mxl862xx_evlan_program_egress
+@@ -1278,8 +1621,8 @@ static int mxl862xx_evlan_program_egress
const struct mxl862xx_evlan_rule_desc *vid_rules;
struct mxl862xx_vf_vid *vfv;
u16 old_active = blk->n_active;
if (p->vlan_filtering) {
vid_rules = vid_accept_standard;
-@@ -1341,13 +1684,23 @@ static int mxl862xx_evlan_program_egress
+@@ -1296,13 +1639,23 @@ static int mxl862xx_evlan_program_egress
if (p->vlan_filtering && !vfv->untagged)
continue;
if (ret)
return ret;
-@@ -1356,7 +1709,29 @@ static int mxl862xx_evlan_program_egress
+@@ -1311,7 +1664,29 @@ static int mxl862xx_evlan_program_egress
idx++, &vid_rules[1],
vfv->vid,
vfv->untagged,
if (ret)
return ret;
}
-@@ -1368,8 +1743,7 @@ static int mxl862xx_evlan_program_egress
+@@ -1323,8 +1698,7 @@ static int mxl862xx_evlan_program_egress
*/
for (i = idx; i < old_active; i++) {
ret = mxl862xx_evlan_deactivate_entry(priv,
if (ret)
return ret;
}
-@@ -1393,13 +1767,16 @@ static int mxl862xx_port_vlan_filtering(
+@@ -1348,13 +1722,16 @@ static int mxl862xx_port_vlan_filtering(
/* Reprogram Extended VLAN rules if filtering mode changed */
if (changed) {
ret = mxl862xx_evlan_program_ingress(priv, port);
if (ret)
-@@ -1536,18 +1913,19 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1491,18 +1868,19 @@ static int mxl862xx_setup_cpu_bridge(str
/* include all assigned user ports in the CPU portmap */
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -1580,7 +1958,6 @@ static int mxl862xx_port_bridge_join(str
+@@ -1535,7 +1913,6 @@ static int mxl862xx_port_bridge_join(str
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge)
{
struct mxl862xx_priv *priv = ds->priv;
struct mxl862xx_port *p = &priv->ports[port];
int err;
-@@ -1595,34 +1972,587 @@ static void mxl862xx_port_bridge_leave(s
+@@ -1550,34 +1927,587 @@ static void mxl862xx_port_bridge_leave(s
* single-port bridge
*/
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
- /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
- * them. The firmware tracks a usage count per block and rejects
- * FREE while the count is non-zero.
+- *
+- * For EVLAN: setting in_use=false makes set_bridge_port send
+- * enable=false, which decrements the firmware refcount.
+ /* Reset VLAN state for standalone mode. Ingress EVLAN is not
+ * needed outside a VLAN-aware bridge. Egress EVLAN is
+ * reprogrammed below -- in tag_8021q mode it gets the
+ * management VID strip catchalls, in SpTag mode it is cleared.
*
-- * For EVLAN: setting in_use=false makes set_bridge_port send
-- * enable=false, which decrements the firmware refcount.
-- *
- * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
- * cleared it) and sends vlan_filter_enable=0, which decrements
- * the firmware VF refcount.
static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -1642,55 +2572,30 @@ static int mxl862xx_port_setup(struct ds
+@@ -1597,55 +2527,30 @@ static int mxl862xx_port_setup(struct ds
dsa_port_is_dsa(dp))
return 0;
return 0;
}
-@@ -1712,7 +2617,7 @@ static void mxl862xx_port_teardown(struc
+@@ -1667,7 +2572,7 @@ static void mxl862xx_port_teardown(struc
priv->ports[port].setup_done = false;
}
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -1730,23 +2635,244 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -1685,23 +2590,244 @@ static int mxl862xx_get_fid(struct dsa_s
}
}
+ if (dsa_is_cpu_port(ds, port) && priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q &&
+ db.type == DSA_DB_PORT) {
+ bp_cpu = priv->ports[db.dp->index].bridge_port_cpu;
-+
+
+- param.port_id = cpu_to_le32(port);
+ if (bp_cpu)
+ return bp_cpu;
+ }
+{
+ struct mxl862xx_mac_table_add param = {};
+ struct mxl862xx_priv *priv = ds->priv;
-
-- param.port_id = cpu_to_le32(port);
++
+ param.port_id = cpu_to_le32(port_id);
param.static_entry = true;
param.fid = cpu_to_le16(fid);
if (ret)
dev_err(ds->dev, "failed to add FDB entry on port %d\n", port);
-@@ -1756,18 +2882,25 @@ static int mxl862xx_port_fdb_add(struct
+@@ -1711,18 +2837,25 @@ static int mxl862xx_port_fdb_add(struct
static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, const struct dsa_db db)
{
if (ret)
dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port);
-@@ -1806,88 +2939,147 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -1761,88 +2894,147 @@ static int mxl862xx_port_fdb_dump(struct
return 0;
}
- int fid = mxl862xx_get_fid(ds, db), ret;
struct mxl862xx_priv *priv = ds->priv;
+ int fid, ret;
-+
+
+ /* tag_8021q host MDB for bridged ports: clear all VBP bits */
+ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) &&
+ db.type == DSA_DB_BRIDGE) {
+ return mxl862xx_mac_del_host_bridge(ds, mdb->addr,
+ mdb->vid, &db.bridge);
+ }
-
++
+ fid = mxl862xx_get_fid(ds, db);
if (fid < 0)
return fid;
return ret;
}
-@@ -1975,7 +3167,9 @@ static void mxl862xx_host_flood_work_fn(
+@@ -1930,7 +3122,9 @@ static void mxl862xx_host_flood_work_fn(
struct mxl862xx_priv *priv = p->priv;
struct dsa_switch *ds = priv->ds;
int port = p - priv->ports;
rtnl_lock();
-@@ -1988,14 +3182,31 @@ static void mxl862xx_host_flood_work_fn(
+@@ -1943,14 +3137,35 @@ static void mxl862xx_host_flood_work_fn(
uc = p->host_flood_uc;
mc = p->host_flood_mc;
+ port, ERR_PTR(ret));
+ }
+ } else {
-+ /* SpTag mode: per-FID forwarding, only works for
-+ * standalone ports (each has its own FID).
++ /* The hardware controls unknown-unicast/multicast forwarding
++ * per FID (bridge), not per source port. For bridged ports all
++ * members share one FID, so we cannot selectively suppress
++ * flooding to the CPU for one source port while allowing it
++ * for another. Silently ignore the request -- the excess
++ * flooding towards the CPU is harmless.
+ */
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
+ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
rtnl_unlock();
}
-@@ -2330,7 +3541,9 @@ static void mxl862xx_get_stats64(struct
+@@ -2285,7 +3500,9 @@ static void mxl862xx_get_stats64(struct
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.port_setup = mxl862xx_port_setup,
.port_teardown = mxl862xx_port_teardown,
.phylink_get_caps = mxl862xx_phylink_get_caps,
-@@ -2352,6 +3565,8 @@ static const struct dsa_switch_ops mxl86
+@@ -2307,6 +3524,8 @@ static const struct dsa_switch_ops mxl86
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
.get_strings = mxl862xx_get_strings,
.get_sset_count = mxl862xx_get_sset_count,
.get_ethtool_stats = mxl862xx_get_ethtool_stats,
-@@ -2399,6 +3614,8 @@ static int mxl862xx_probe(struct mdio_de
+@@ -2354,6 +3573,8 @@ static int mxl862xx_probe(struct mdio_de
INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn);
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -2415,16 +3632,29 @@ static void mxl862xx_remove(struct mdio_
-
- priv = ds->priv;
+ err = dsa_register_switch(ds);
+@@ -2382,6 +3603,19 @@ static void mxl862xx_remove(struct mdio_
+ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+ /* Tear down tag_8021q under RTNL before dsa_unregister_switch().
+ * dsa_tag_8021q_unregister() calls vlan_vid_del() which needs
+
dsa_unregister_switch(ds);
- cancel_delayed_work_sync(&priv->stats_work);
-
mxl862xx_host_shutdown(priv);
-
-- /* Cancel any pending host flood work. dsa_unregister_switch()
-+ /* Cancel any pending host flood work. dsa_unregister_switch()
- * has already called port_teardown (which sets setup_done=false),
- * but a worker could still be blocked on rtnl_lock(). Since we
-- * are now outside RTNL, cancel_work_sync() will not deadlock.
-+ * are now outside RTNL, cancel_work_sync() won't deadlock.
- */
- for (i = 0; i < MXL862XX_MAX_PORTS; i++)
- cancel_work_sync(&priv->ports[i].host_flood_work);
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -8,8 +8,6 @@
- #include <linux/workqueue.h>
- #include <net/dsa.h>
-
--struct mxl862xx_priv;
--
- #define MXL862XX_MAX_PORTS 17
- #define MXL862XX_DEFAULT_BRIDGE 0
- #define MXL862XX_MAX_BRIDGES 48
-@@ -20,6 +18,8 @@ struct mxl862xx_priv;
- /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
- #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
-
-+struct mxl862xx_priv;
-+
- /**
- * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware
- * portmap (__le16[8])
@@ -210,6 +210,9 @@ struct mxl862xx_port_stats {
* @vf: per-port VLAN Filter block state
* @ingress_evlan: ingress extended VLAN block state
/* Hardware stats accumulation */
struct mxl862xx_port_stats stats;
spinlock_t stats_lock;
-@@ -302,6 +311,7 @@ union mxl862xx_fw_version {
- * errors
- * @crc_err: set atomically before CRC-triggered shutdown, cleared
- * after
+@@ -308,6 +317,7 @@ union mxl862xx_fw_version {
+ * before CRC-triggered shutdown and cleared after;
+ * %MXL862XX_FLAG_WORK_STOPPED is set before cancelling
+ * stats_work to prevent rescheduling during teardown
+ * @tag_proto: active DSA tag protocol (native or 8021q)
* @drop_meter: index of the single shared zero-rate firmware meter
* used to unconditionally drop traffic (used to block
* flooding)
-@@ -310,12 +320,13 @@ union mxl862xx_fw_version {
- * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of
- * 10G_QXGMII
- * @ports: per-port state, indexed by switch port number
-+ * @evlan_ingress_size: per-port ingress Extended VLAN block size
-+ * @evlan_egress_size: per-port egress Extended VLAN block size
-+ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
- * @bridges: maps DSA bridge number to firmware bridge ID;
- * zero means no firmware bridge allocated for that
- * DSA bridge number. Indexed by dsa_bridge.num
+@@ -322,6 +332,7 @@ union mxl862xx_fw_version {
* (0 .. ds->max_num_bridges).
-- * @evlan_ingress_size: per-port ingress Extended VLAN block size
-- * @evlan_egress_size: per-port egress Extended VLAN block size
+ * @evlan_ingress_size: per-port ingress Extended VLAN block size
+ * @evlan_egress_size: per-port egress Extended VLAN block size
++ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
* @vf_block_size: per-port VLAN Filter block size
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
-@@ -325,6 +336,7 @@ struct mxl862xx_priv {
+@@ -331,6 +342,7 @@ struct mxl862xx_priv {
struct mdio_device *mdiodev;
struct work_struct crc_err_work;
- unsigned long crc_err;
+ unsigned long flags;
+ enum dsa_tag_protocol tag_proto;
u16 drop_meter;
union mxl862xx_fw_version fw_version;
struct mxl862xx_pcs serdes_ports[8];
-@@ -332,6 +344,7 @@ struct mxl862xx_priv {
+@@ -338,6 +350,7 @@ struct mxl862xx_priv {
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
u16 evlan_egress_size;
};
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
-@@ -56,6 +56,8 @@ struct tc_action;
+@@ -56,6 +56,7 @@ struct tc_action;
#define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28
#define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29
#define DSA_TAG_PROTO_MXL862_VALUE 30
+#define DSA_TAG_PROTO_MXL862_8021Q_VALUE 31
-+
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
-@@ -89,6 +91,7 @@ enum dsa_tag_protocol {
+@@ -89,6 +90,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE,
DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
-From 31359e8b7673e656d0591a9eb5014b45911383ae Mon Sep 17 00:00:00 2001
+From 4e1d854199c166f617b93b7542e863e6a8ad2ccb Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 03:44:41 +0000
-Subject: [PATCH 26/35] net: dsa: mxl862xx: add link aggregation support
+Subject: [PATCH 17/26] net: dsa: mxl862xx: add link aggregation support
Implement LAG offloading via the firmware's trunking engine. A
dedicated firmware bridge port is allocated per LAG and remains
return ret;
}
-@@ -1926,6 +2018,408 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1881,6 +1973,408 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -1952,7 +2446,18 @@ static int mxl862xx_port_bridge_join(str
+@@ -1907,7 +2401,18 @@ static int mxl862xx_port_bridge_join(str
return 0;
}
}
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
-@@ -2011,6 +2516,17 @@ static void mxl862xx_port_bridge_leave(s
+@@ -1966,6 +2471,17 @@ static void mxl862xx_port_bridge_leave(s
"failed to update CPU VBP for port %d: %pe\n", port,
ERR_PTR(err));
if (!dsa_bridge_ports(ds, bridge.dev))
mxl862xx_free_bridge(ds, &bridge);
}
-@@ -2636,18 +3152,17 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -2591,18 +3107,17 @@ static int mxl862xx_get_fid(struct dsa_s
}
/**
*/
static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port,
const struct dsa_db db)
-@@ -2663,7 +3178,7 @@ static int mxl862xx_fdb_bridge_port(stru
+@@ -2618,7 +3133,7 @@ static int mxl862xx_fdb_bridge_port(stru
return bp_cpu;
}
}
/**
-@@ -2907,11 +3422,43 @@ static int mxl862xx_port_fdb_del(struct
+@@ -2862,11 +3377,43 @@ static int mxl862xx_port_fdb_del(struct
return ret;
}
u32 entry_port_id;
int ret;
-@@ -2925,7 +3472,7 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -2880,7 +3427,7 @@ static int mxl862xx_port_fdb_dump(struct
entry_port_id = le32_to_cpu(param.port_id);
ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID,
le16_to_cpu(param.tci)),
param.static_entry, data);
-@@ -3562,6 +4109,11 @@ static const struct dsa_switch_ops mxl86
+@@ -3521,6 +4068,11 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
-@@ -3602,6 +4154,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -3561,6 +4113,7 @@ static int mxl862xx_probe(struct mdio_de
ds->num_ports = MXL862XX_MAX_PORTS;
ds->fdb_isolation = true;
ds->max_num_bridges = MXL862XX_MAX_BRIDGES;
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -14,6 +14,19 @@
+@@ -16,6 +16,19 @@ struct mxl862xx_priv;
#define MXL862XX_MAX_BRIDGE_PORTS 128
#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024
#define MXL862XX_TOTAL_VF_ENTRIES 1024
/* Hardware stats accumulation */
struct mxl862xx_port_stats stats;
spinlock_t stats_lock;
-@@ -328,6 +351,15 @@ union mxl862xx_fw_version {
- * DSA bridge number. Indexed by dsa_bridge.num
- * (0 .. ds->max_num_bridges).
+@@ -334,6 +357,15 @@ union mxl862xx_fw_version {
+ * @evlan_egress_size: per-port egress Extended VLAN block size
+ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
* @vf_block_size: per-port VLAN Filter block size
+ * @lag_bridge_ports: maps DSA LAG ID to firmware bridge port ID;
+ * zero means no bridge port allocated for that LAG.
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
*/
-@@ -346,6 +378,8 @@ struct mxl862xx_priv {
+@@ -352,6 +384,8 @@ struct mxl862xx_priv {
u16 evlan_egress_size;
u16 cpu_evlan_ingress_size;
u16 vf_block_size;
-From fbfa1b0649c578e0d43e3a61617b53a9a722efad Mon Sep 17 00:00:00 2001
+From 5528f38c3d709417625eb7f36628be31727a8221 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 12:05:29 +0000
-Subject: [PATCH 27/35] net: dsa: mxl862xx: add support for mirror port
+Subject: [PATCH 18/26] net: dsa: mxl862xx: add support for mirror port
The MxL862xx hardware supports a single monitor port which can be
configured to mirror any other port's ingress and/or egress traffic.
#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -1129,6 +1129,8 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -1084,6 +1084,8 @@ static int mxl862xx_setup(struct dsa_swi
(n_user_ports + n_cpu_ports);
}
ret = mxl862xx_setup_drop_meter(ds);
if (ret)
return ret;
-@@ -2018,6 +2020,120 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1973,6 +1975,120 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
/**
* mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member)
* @ds: DSA switch
-@@ -4109,6 +4225,8 @@ static const struct dsa_switch_ops mxl86
+@@ -4068,6 +4184,8 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
/* LAG state */
struct dsa_lag *lag;
bool lag_tx_enabled;
-@@ -360,6 +365,8 @@ union mxl862xx_fw_version {
+@@ -366,6 +371,8 @@ union mxl862xx_fw_version {
* @trunk_hash: current global hash field bitmask (6 bits,
* MXL862XX_TRUNK_HASH_*); union of all active LAGs'
* hash requirements
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
*/
-@@ -380,6 +387,7 @@ struct mxl862xx_priv {
+@@ -386,6 +393,7 @@ struct mxl862xx_priv {
u16 vf_block_size;
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
-From 67f82834819b71417b58dc1293c20f71b990264f Mon Sep 17 00:00:00 2001
+From 4059d35a5bbf1901b2e0eb7126369cd713cacfce Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:08 +0000
-Subject: [PATCH 28/35] net: dsa: wire flash_update devlink callback to drivers
+Subject: [PATCH 19/26] net: dsa: wire flash_update devlink callback to drivers
Add a devlink_flash_update callback to dsa_switch_ops so that DSA
drivers can support devlink dev flash without open-coding the devlink
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
-@@ -1186,6 +1186,9 @@ struct dsa_switch_ops {
+@@ -1185,6 +1185,9 @@ struct dsa_switch_ops {
int (*devlink_info_get)(struct dsa_switch *ds,
struct devlink_info_req *req,
struct netlink_ext_ack *extack);
-From 1a87b829ef3280d646dc480f7b261d9e32896899 Mon Sep 17 00:00:00 2001
+From 0145151dc68aa318d8addb6fe7f12c0967f951da Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:17 +0000
-Subject: [PATCH 29/35] net: dsa: mxl862xx: add SMDIO clause-22 register access
+Subject: [PATCH 20/26] net: dsa: mxl862xx: add SMDIO clause-22 register access
Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22
SMDIO register access. MCUboot rescue mode only exposes clause-22
-From b7e8f8fd4493b255f0f01fe790a73ad61b5e8ce8 Mon Sep 17 00:00:00 2001
+From bdbca48510e3e96ed9210f20fa4244dd6df5d44a Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:31 +0000
-Subject: [PATCH 30/35] net: dsa: mxl862xx: add devlink flash_update and
+Subject: [PATCH 21/26] net: dsa: mxl862xx: add devlink flash_update and
info_get
Implement runtime firmware upgrade via "devlink dev flash" and version
#include "mxl862xx-host.h"
#include "mxl862xx-phylink.h"
-@@ -4245,6 +4246,9 @@ static const struct dsa_switch_ops mxl86
+@@ -4204,6 +4205,9 @@ static const struct dsa_switch_ops mxl86
.get_pause_stats = mxl862xx_get_pause_stats,
.get_stats64 = mxl862xx_get_stats64,
.self_test = mxl862xx_serdes_self_test,
static int mxl862xx_probe(struct mdio_device *mdiodev)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -388,6 +388,8 @@ struct mxl862xx_priv {
+@@ -394,6 +394,8 @@ struct mxl862xx_priv {
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
int mirror_dest;
-From 2cb9aeb3a8d7ebac20331e0a533dcfbd73fa4237 Mon Sep 17 00:00:00 2001
+From 8deb5be9638f7eb3009ed3eb619eedadee1df523 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 23:42:18 +0000
-Subject: [PATCH 31/35] net: dsa: mxl862xx: implement port MTU configuration
+Subject: [PATCH 22/26] net: dsa: mxl862xx: implement port MTU configuration
The firmware exposes a global max_packet_len register via
MXL862XX_COMMON_CFGSET. Since this is switch-wide rather than
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
-@@ -3768,6 +3769,53 @@ static int mxl862xx_set_ageing_time(stru
+@@ -3723,6 +3724,53 @@ static int mxl862xx_set_ageing_time(stru
return ret;
}
static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
-@@ -4215,6 +4263,8 @@ static const struct dsa_switch_ops mxl86
+@@ -4174,6 +4222,8 @@ static const struct dsa_switch_ops mxl86
.port_disable = mxl862xx_port_disable,
.port_fast_age = mxl862xx_port_fast_age,
.set_ageing_time = mxl862xx_set_ageing_time,
-From d55ca68eb0d20a66c32d531b0a454871b486c1b1 Mon Sep 17 00:00:00 2001
+From 13a4c918cd9ded7207f38033511ab13f7aff9bd2 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 01:47:19 +0000
-Subject: [PATCH 32/35] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
+Subject: [PATCH 23/26] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
Implement hairpin mode by including the port's own bridge port ID in
its forwarding portmap. When hairpin is enabled, bridged frames whose
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 30 ++++++++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 29 +++++++++++++++++++++++++++--
drivers/net/dsa/mxl862xx/mxl862xx.h | 6 ++++++
- 2 files changed, 35 insertions(+), 1 deletion(-)
+ 2 files changed, 33 insertions(+), 2 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
err = mxl862xx_set_bridge_port(ds, port);
if (err)
ret = err;
-@@ -3939,7 +3948,7 @@ static int mxl862xx_port_pre_bridge_flag
+@@ -3898,7 +3907,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
return -EINVAL;
return 0;
-@@ -3954,6 +3963,7 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3912,6 +3921,7 @@ static int mxl862xx_port_bridge_flags(st
+ unsigned long old_block = priv->ports[port].flood_block;
unsigned long block = old_block;
- bool need_update = false;
int ret;
+ u16 bp;
if (flags.mask & BR_FLOOD) {
if (flags.val & BR_FLOOD)
-@@ -3988,6 +3998,24 @@ static int mxl862xx_port_bridge_flags(st
- ret = mxl862xx_set_bridge_port(ds, port);
- if (ret)
- return ret;
-+ }
-+
+@@ -3940,7 +3950,22 @@ static int mxl862xx_port_bridge_flags(st
+ if (flags.mask & BR_LEARNING)
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
+
+- if ((block != old_block) || (flags.mask & BR_LEARNING)) {
+ if (flags.mask & BR_HAIRPIN_MODE) {
+ bp = mxl862xx_lag_bridge_port(priv, port);
+ priv->ports[port].hairpin = !!(flags.val & BR_HAIRPIN_MODE);
+ __set_bit(bp, priv->ports[port].portmap);
+ else
+ __clear_bit(bp, priv->ports[port].portmap);
++ }
+
-+ ret = mxl862xx_set_bridge_port(ds, port);
-+ if (ret)
-+ return ret;
- }
-
- return 0;
++ if ((block != old_block) ||
++ (flags.mask & (BR_LEARNING | BR_HAIRPIN_MODE))) {
+ priv->ports[port].flood_block = block;
+ ret = mxl862xx_set_bridge_port(ds, port);
+ if (ret)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -241,6 +241,10 @@ struct mxl862xx_port_stats {
-From 74b6654ba74eb142340de4c51b97c0221cfcae37 Mon Sep 17 00:00:00 2001
+From d49d1f8bee29269def7593f980d0e08bfb5c3ef8 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 01:51:33 +0000
-Subject: [PATCH 33/35] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
+Subject: [PATCH 24/26] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
Implement port isolation by excluding isolated ports from each other's
forwarding portmaps in sync_bridge_members. Non-isolated ports can
if (member != port) {
bp = mxl862xx_lag_bridge_port(priv,
member);
-@@ -3948,7 +3956,7 @@ static int mxl862xx_port_pre_bridge_flag
+@@ -3907,7 +3915,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
return -EINVAL;
return 0;
-@@ -3962,6 +3970,7 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3920,6 +3928,7 @@ static int mxl862xx_port_bridge_flags(st
+ struct mxl862xx_priv *priv = ds->priv;
unsigned long old_block = priv->ports[port].flood_block;
unsigned long block = old_block;
- bool need_update = false;
+ struct dsa_port *dp;
int ret;
u16 bp;
-@@ -4018,6 +4027,21 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3972,6 +3981,21 @@ static int mxl862xx_port_bridge_flags(st
return ret;
}
-From 0902a6790750714445c75a66d60f1bc4897126ce Mon Sep 17 00:00:00 2001
+From c2fb7f0df63ac994850f766e7f2eb50c6c5ef2cf Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:17:49 +0000
-Subject: [PATCH 34/35] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
+Subject: [PATCH 25/26] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
workaround for old firmware
Re-introduce the mxl862xx_disable_fw_global_rules() function that
/* Per-CTP offset used for the link-local trap rule. Each port's CTP
* flow-table block is pre-allocated by the firmware during init (44
-@@ -1154,9 +1191,11 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -1109,9 +1146,11 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
-From 0ac876d5b952218ab79ea0a0815cf6fd1290b1d0 Mon Sep 17 00:00:00 2001
+From f0548f842b9ff31f19452a2fc72a16f937d86008 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:19:56 +0000
-Subject: [PATCH 35/35] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
+Subject: [PATCH 26/26] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
fallback for old firmware
Re-introduce the SYS_MISC_SFP_SET-based PCS implementation as a
--- /dev/null
+From 3fd163f5bb88de426ca9847549f94b4296170ef0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 30 Mar 2026 23:40:53 +0100
+Subject: [PATCH] net: dsa: mxl862xx: cancel pending work on probe error
+
+Call mxl862xx_host_shutdown() in case dsa_register_switch() returns
+an error, so any still pending crc_err_work get canceled.
+
+Fixes: a319d0c8c8ced ("net: dsa: mxl862xx: add CRC for MDIO communication)"
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
+@@ -407,6 +407,7 @@ static int mxl862xx_probe(struct mdio_de
+ struct device *dev = &mdiodev->dev;
+ struct mxl862xx_priv *priv;
+ struct dsa_switch *ds;
++ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -428,7 +429,11 @@ static int mxl862xx_probe(struct mdio_de
+
+ dev_set_drvdata(dev, ds);
+
+- return dsa_register_switch(ds);
++ err = dsa_register_switch(ds);
++ if (err)
++ mxl862xx_host_shutdown(priv);
++
++ return err;
+ }
+
+ static void mxl862xx_remove(struct mdio_device *mdiodev)
-From de6dd19a3edd1dc6400fecf77610e438441a02ac Mon Sep 17 00:00:00 2001
+From cd698f1ae94c16499e2714b31dd6048e6f9f068d Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:11 +0000
-Subject: [PATCH 10/35] net: dsa: move dsa_bridge_ports() helper to dsa.h
+Subject: [PATCH 01/26] net: dsa: move dsa_bridge_ports() helper to dsa.h
The yt921x driver contains a helper to create a bitmap of ports
which are members of a bridge.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
include/net/dsa.h | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
+ 2 files changed, 13 insertions(+), 13 deletions(-)
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
-From 880cde7abf58cb1316382ae7f59aac93c313e8fe Mon Sep 17 00:00:00 2001
+From c161533e1605a7282563c139323a3913890fdb72 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:41 +0000
-Subject: [PATCH 11/35] net: dsa: add bridge member iteration macro
+Subject: [PATCH 02/26] net: dsa: add bridge member iteration macro
Drivers that offload bridges need to iterate over the ports that are
members of a given bridge, for example to rebuild per-port forwarding
-From 149bb02d5bf031a1eb85f91377f54913de3a08ff Mon Sep 17 00:00:00 2001
+From 753efe27a9afee52c4ad42098a9b9278366d63cc Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:54:52 +0000
-Subject: [PATCH 12/35] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
+Subject: [PATCH 03/26] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
The MxL862xx offloads bridge forwarding in hardware, so set
dsa_default_offload_fwd_mark() to avoid duplicate forwarding of
-From 5acdee6df2fbd4a9b02045694227f25cb1d4e5e0 Mon Sep 17 00:00:00 2001
+From ce0664ff8f75c3ab01101c3f0f8569924d948775 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 17:55:08 +0000
-Subject: [PATCH 13/35] net: dsa: mxl862xx: implement bridge offloading
+Subject: [PATCH 04/26] net: dsa: mxl862xx: implement bridge offloading
Implement joining and leaving bridges as well as add, delete and dump
operations on isolated FDBs, port MDB membership management, and
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++-
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 20 +-
- drivers/net/dsa/mxl862xx/mxl862xx.c | 752 ++++++++++++++++++++++--
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 743 ++++++++++++++++++++++--
drivers/net/dsa/mxl862xx/mxl862xx.h | 133 +++++
- 4 files changed, 1087 insertions(+), 43 deletions(-)
+ 4 files changed, 1076 insertions(+), 45 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol m)
-@@ -168,6 +182,225 @@ static int mxl862xx_setup_mdio(struct ds
+@@ -168,6 +182,213 @@ static int mxl862xx_setup_mdio(struct ds
return ret;
}
+ return ret;
+}
+
-+/**
-+ * mxl862xx_allocate_bridge - Allocate a firmware bridge instance
-+ * @priv: driver private data
-+ * @bridge_id: output -- firmware bridge ID assigned by the firmware
-+ *
-+ * Newly allocated bridges default to flooding all traffic classes
-+ * (unknown unicast, multicast, broadcast). Callers that need
-+ * different forwarding behavior must call mxl862xx_bridge_config_fwd()
-+ * after allocation.
-+ *
-+ * Return: 0 on success, negative errno on failure.
-+ */
+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
+{
+ struct mxl862xx_bridge_alloc br_alloc = {};
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -181,6 +414,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -181,6 +402,10 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
return mxl862xx_setup_mdio(ds);
}
-@@ -260,66 +497,87 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -260,66 +485,87 @@ static int mxl862xx_configure_sp_tag_pro
static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
{
struct dsa_port *dp = dsa_to_port(ds, port);
bool is_cpu_port = dsa_port_is_cpu(dp);
int ret;
-@@ -352,7 +610,31 @@ static int mxl862xx_port_setup(struct ds
+@@ -352,7 +598,31 @@ static int mxl862xx_port_setup(struct ds
return mxl862xx_setup_cpu_bridge(ds, port);
/* setup single-port bridge for user ports */
}
static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
-@@ -365,14 +647,385 @@ static void mxl862xx_phylink_get_caps(st
+@@ -365,14 +635,383 @@ static void mxl862xx_phylink_get_caps(st
config->supported_interfaces);
}
+ mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
+
+ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
-+ /* No ports left — remove the entry entirely */
++ /* No ports left -- remove the entry entirely */
+ rparam.fid = cpu_to_le16(fid);
+ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
+ ether_addr_copy(rparam.mac, mdb->addr);
+/* Deferred work handler for host flood configuration.
+ *
+ * port_set_host_flood is called from atomic context (under
-+ * netif_addr_lock), so firmware calls must be deferred. The worker
++ * netif_addr_lock), so firmware calls must be deferred. The worker
+ * acquires rtnl_lock() to serialize with DSA callbacks that access the
+ * same driver state.
+ */
+ mc = p->host_flood_mc;
+
+ /* The hardware controls unknown-unicast/multicast forwarding per FID
-+ * (bridge), not per source port. For bridged ports all members share
++ * (bridge), not per source port. For bridged ports all members share
+ * one FID, so we cannot selectively suppress flooding to the CPU for
-+ * one source port while allowing it for another. Silently ignore the
++ * one source port while allowing it for another. Silently ignore the
+ * request -- the excess flooding towards the CPU is harmless.
+ */
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
+ struct mxl862xx_priv *priv = ds->priv;
+ unsigned long old_block = priv->ports[port].flood_block;
+ unsigned long block = old_block;
-+ bool need_update = false;
+ int ret;
+
+ if (flags.mask & BR_FLOOD) {
+ if (flags.mask & BR_LEARNING)
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
+
-+ need_update = (block != old_block) || (flags.mask & BR_LEARNING);
-+ if (need_update) {
++ if ((block != old_block) || (flags.mask & BR_LEARNING)) {
+ priv->ports[port].flood_block = block;
+ ret = mxl862xx_set_bridge_port(ds, port);
+ if (ret)
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -407,6 +1060,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -407,7 +1046,7 @@ static int mxl862xx_probe(struct mdio_de
struct device *dev = &mdiodev->dev;
struct mxl862xx_priv *priv;
struct dsa_switch *ds;
-+ int i;
+- int err;
++ int err, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
-@@ -424,8 +1078,17 @@ static int mxl862xx_probe(struct mdio_de
+@@ -425,14 +1064,25 @@ static int mxl862xx_probe(struct mdio_de
ds->ops = &mxl862xx_switch_ops;
ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
ds->num_ports = MXL862XX_MAX_PORTS;
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -435,6 +1098,7 @@ static void mxl862xx_remove(struct mdio_
+ err = dsa_register_switch(ds);
+- if (err)
++ if (err) {
+ mxl862xx_host_shutdown(priv);
+-
++ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
++ cancel_work_sync(&priv->ports[i].host_flood_work);
++ }
+ return err;
+ }
+
+@@ -440,6 +1090,7 @@ static void mxl862xx_remove(struct mdio_
{
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
struct mxl862xx_priv *priv;
if (!ds)
return;
-@@ -444,12 +1108,21 @@ static void mxl862xx_remove(struct mdio_
+@@ -449,12 +1100,21 @@ static void mxl862xx_remove(struct mdio_
dsa_unregister_switch(ds);
mxl862xx_host_shutdown(priv);
+
-+ /* Cancel any pending host flood work. dsa_unregister_switch()
++ /* Cancel any pending host flood work. dsa_unregister_switch()
+ * has already called port_teardown (which sets setup_done=false),
-+ * but a worker could still be blocked on rtnl_lock(). Since we
++ * but a worker could still be blocked on rtnl_lock(). Since we
+ * are now outside RTNL, cancel_work_sync() will not deadlock.
+ */
+ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
if (!ds)
return;
-@@ -460,6 +1133,9 @@ static void mxl862xx_shutdown(struct mdi
+@@ -465,6 +1125,9 @@ static void mxl862xx_shutdown(struct mdi
mxl862xx_host_shutdown(priv);
-From 7286ac4f850339aac37dd52633f4a70816b621a8 Mon Sep 17 00:00:00 2001
+From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 10 Mar 2026 02:36:00 +0000
-Subject: [PATCH 14/35] net: dsa: mxl862xx: implement VLAN functionality
+Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
Add VLAN support using both the Extended VLAN (EVLAN) engine and the
VLAN Filter (VF) engine in a hybrid architecture that allows a higher
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
- drivers/net/dsa/mxl862xx/mxl862xx.c | 960 +++++++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx.h | 110 ++-
- 4 files changed, 1386 insertions(+), 25 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
+ 4 files changed, 1344 insertions(+), 16 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
-@@ -329,13 +472,131 @@ static int mxl862xx_sync_bridge_members(
+@@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
return ret;
}
+ return 0;
+}
+
-+/**
-+ * mxl862xx_vf_init - Initialize per-port VF block software state
-+ * @vf: VLAN Filter block to initialize
-+ * @size: block size (entries per port)
-+ */
+static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
+{
+ vf->allocated = false;
+ INIT_LIST_HEAD(&vf->vids);
+}
+
-+/**
-+ * mxl862xx_vf_block_alloc - Allocate a VLAN Filter block from firmware
-+ * @priv: driver private data
-+ * @size: number of entries to allocate
-+ * @block_id: output -- block ID assigned by firmware
-+ *
-+ * Allocates a contiguous VLAN Filter block and configures it to discard
-+ * unmatched tagged frames (VID membership enforcement).
-+ */
+static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
+ u16 size, u16 *block_id)
+{
+ return 0;
+}
+
-+/**
-+ * mxl862xx_vf_entry_discard - Write a DISCARD entry to plug an unused slot
-+ * @priv: driver private data
-+ * @block_id: HW VLAN Filter block ID
-+ * @index: entry index within the block
-+ *
-+ * Unwritten VLAN Filter entries default to VID=0 / ALLOW which would
-+ * leak VID 0 traffic. This writes a DISCARD entry to plug the slot.
-+ */
+static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv,
+ u16 block_id, u16 index)
+{
+ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
+}
+
-+/**
-+ * mxl862xx_vf_alloc - Allocate the port's VF HW block
-+ * @priv: driver private data
-+ * @vf: VLAN Filter block (must have been initialized via mxl862xx_vf_init)
-+ *
-+ * Allocates the block and writes a DISCARD sentinel at index 0 so that
-+ * when active_count is 0, the single-entry scan window blocks VID-0
-+ * (which would otherwise match the zeroed-out default and be allowed).
-+ * Called once per port from port_setup.
-+ */
+static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv,
+ struct mxl862xx_vf_block *vf)
+{
+ return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
+}
+
- /**
- * mxl862xx_allocate_bridge - Allocate a firmware bridge instance
- * @priv: driver private data
- * @bridge_id: output -- firmware bridge ID assigned by the firmware
- *
- * Newly allocated bridges default to flooding all traffic classes
-- * (unknown unicast, multicast, broadcast). Callers that need
-+ * (unknown unicast, multicast, broadcast). Callers that need
- * different forwarding behavior must call mxl862xx_bridge_config_fwd()
- * after allocation.
- *
-@@ -404,6 +665,9 @@ static int mxl862xx_add_single_port_brid
+ static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
+ {
+ struct mxl862xx_bridge_alloc br_alloc = {};
+@@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
int ret;
ret = mxl862xx_reset(priv);
-@@ -414,6 +678,50 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
ret = mxl862xx_setup_drop_meter(ds);
if (ret)
return ret;
-@@ -495,27 +803,616 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
}
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -565,6 +1462,22 @@ static void mxl862xx_port_bridge_leave(s
+@@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
__set_bit(dp->cpu_dp->index, p->portmap);
p->flood_block = 0;
err = mxl862xx_set_bridge_port(ds, port);
if (err)
dev_err(ds->dev,
-@@ -614,6 +1527,28 @@ static int mxl862xx_port_setup(struct ds
+@@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
priv->ports[port].setup_done = true;
return 0;
-@@ -808,7 +1743,7 @@ static int mxl862xx_port_mdb_del(struct
- mxl862xx_fw_portmap_clear_bit(qparam.port_map, port);
-
- if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) {
-- /* No ports left — remove the entry entirely */
-+ /* No ports left -- remove the entry entirely */
- rparam.fid = cpu_to_le16(fid);
- rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
- ether_addr_copy(rparam.mac, mdb->addr);
-@@ -899,7 +1834,7 @@ static void mxl862xx_port_stp_state_set(
- /* Deferred work handler for host flood configuration.
- *
- * port_set_host_flood is called from atomic context (under
-- * netif_addr_lock), so firmware calls must be deferred. The worker
-+ * netif_addr_lock), so firmware calls must be deferred. The worker
- * acquires rtnl_lock() to serialize with DSA callbacks that access the
- * same driver state.
- */
-@@ -924,9 +1859,9 @@ static void mxl862xx_host_flood_work_fn(
- mc = p->host_flood_mc;
-
- /* The hardware controls unknown-unicast/multicast forwarding per FID
-- * (bridge), not per source port. For bridged ports all members share
-+ * (bridge), not per source port. For bridged ports all members share
- * one FID, so we cannot selectively suppress flooding to the CPU for
-- * one source port while allowing it for another. Silently ignore the
-+ * one source port while allowing it for another. Silently ignore the
- * request -- the excess flooding towards the CPU is harmless.
- */
- if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
-@@ -1026,6 +1961,9 @@ static const struct dsa_switch_ops mxl86
+@@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -1111,7 +2049,7 @@ static void mxl862xx_remove(struct mdio_
-
- /* Cancel any pending host flood work. dsa_unregister_switch()
- * has already called port_teardown (which sets setup_done=false),
-- * but a worker could still be blocked on rtnl_lock(). Since we
-+ * but a worker could still be blocked on rtnl_lock(). Since we
- * are now outside RTNL, cancel_work_sync() will not deadlock.
- */
- for (i = 0; i < MXL862XX_MAX_PORTS; i++)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -13,6 +13,8 @@ struct mxl862xx_priv;
/* Number of __le16 words in a firmware portmap (128-bit bitmap). */
#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
-@@ -86,12 +88,72 @@ static inline bool mxl862xx_fw_portmap_i
+@@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
}
/**
* struct mxl862xx_port - per-port state tracked by the driver
* @priv: back-pointer to switch private data; needed by
* deferred work handlers to access ds and priv
-- * @fid: firmware FID for the permanent single-port bridge;
-- * kept alive for the lifetime of the port so traffic is
-- * never forwarded while the port is unbridged
-+ * @fid: firmware FID for the permanent single-port bridge; kept
-+ * alive for the lifetime of the port so traffic is never
-+ * forwarded while the port is unbridged
- * @portmap: bitmap of switch port indices that share the current
- * bridge with this port
- * @flood_block: bitmask of firmware meter indices that are currently
@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
* @setup_done: set at end of port_setup, cleared at start of
* port_teardown; guards deferred work against
-From 03b583e774835f771dd7c3c265be5903f008e8e5 Mon Sep 17 00:00:00 2001
+From 0067d79d10becfc5779fb50d5c0ac152cc5dc303 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:57:33 +0000
-Subject: [PATCH 15/35] net: dsa: mxl862xx: add ethtool statistics support
+Subject: [PATCH 06/26] net: dsa: mxl862xx: add ethtool statistics support
The MxL862xx firmware exposes per-port RMON counters through the
RMON_PORT_GET command, covering standard IEEE 802.3 MAC statistics
#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6))
#define MXL862XX_SDMA_PCTRL_EN BIT(0)
-@@ -1940,6 +1998,110 @@ static int mxl862xx_port_bridge_flags(st
+@@ -1893,6 +1951,110 @@ static int mxl862xx_port_bridge_flags(st
return 0;
}
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.setup = mxl862xx_setup,
-@@ -1964,6 +2126,12 @@ static const struct dsa_switch_ops mxl86
+@@ -1917,6 +2079,12 @@ static const struct dsa_switch_ops mxl86
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
-From 8b66d20f7e5226f4854a39cfb9f25a0591a5bb83 Mon Sep 17 00:00:00 2001
+From bab5a69e3872a693069e430a1fa0d2825ea83b4f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 04:14:38 +0000
-Subject: [PATCH 16/35] net: dsa: mxl862xx: implement .get_stats64
+Subject: [PATCH 07/26] net: dsa: mxl862xx: implement .get_stats64
Poll free-running firmware RMON counters every 2 seconds and accumulate
deltas into 64-bit per-port statistics. 32-bit packet counters wrap
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 167 ++++++++++++++++++++++++++++
- drivers/net/dsa/mxl862xx/mxl862xx.h | 51 +++++++++
- 2 files changed, 218 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx-host.c | 8 +-
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 174 +++++++++++++++++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 63 +++++++-
+ 3 files changed, 238 insertions(+), 7 deletions(-)
+--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
+@@ -48,7 +48,7 @@ static void mxl862xx_crc_err_work_fn(str
+ dev_close(dp->conduit);
+ rtnl_unlock();
+
+- clear_bit(0, &priv->crc_err);
++ clear_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags);
+ }
+
+ /* Firmware CRC error codes (outside normal Zephyr errno range). */
+@@ -247,7 +247,7 @@ static int mxl862xx_issue_cmd(struct mxl
+
+ ret = mxl862xx_crc6_verify(ctrl_enc, len_enc, &fw_result);
+ if (ret) {
+- if (!test_and_set_bit(0, &priv->crc_err))
++ if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ return -EIO;
+ }
+@@ -314,7 +314,7 @@ static int mxl862xx_send_cmd(struct mxl8
+ if (ret < 0) {
+ if ((ret == MXL862XX_FW_CRC6_ERR ||
+ ret == MXL862XX_FW_CRC16_ERR) &&
+- !test_and_set_bit(0, &priv->crc_err))
++ !test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ if (!quiet)
+ dev_err(&priv->mdiodev->dev,
+@@ -458,7 +458,7 @@ int mxl862xx_api_wrap(struct mxl862xx_pr
+ }
+
+ if (crc16(0xffff, (const u8 *)data, size) != crc) {
+- if (!test_and_set_bit(0, &priv->crc_err))
++ if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
+ schedule_work(&priv->crc_err_work);
+ ret = -EIO;
+ goto out;
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -30,6 +30,12 @@
struct mxl862xx_mib_desc {
unsigned int size;
unsigned int offset;
-@@ -784,6 +790,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -739,6 +745,9 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
return mxl862xx_setup_mdio(ds);
}
-@@ -2102,6 +2111,156 @@ static void mxl862xx_get_pause_stats(str
+@@ -2055,6 +2064,158 @@ static void mxl862xx_get_pause_stats(str
pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
}
+ dsa_switch_for_each_available_port(dp, ds)
+ mxl862xx_stats_poll(ds, dp->index);
+
-+ schedule_delayed_work(&priv->stats_work,
-+ MXL862XX_STATS_POLL_INTERVAL);
++ if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags))
++ schedule_delayed_work(&priv->stats_work,
++ MXL862XX_STATS_POLL_INTERVAL);
+}
+
+static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,
+ spin_unlock_bh(&priv->ports[port].stats_lock);
+
+ /* Trigger a fresh poll so the next read sees up-to-date counters.
-+ * No-op if the work is already pending or running.
++ * No-op if the work is already pending, running, or teardown started.
+ */
-+ schedule_delayed_work(&priv->stats_work, 0);
++ if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags))
++ schedule_delayed_work(&priv->stats_work, 0);
+}
+
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.setup = mxl862xx_setup,
-@@ -2132,6 +2291,7 @@ static const struct dsa_switch_ops mxl86
+@@ -2085,6 +2246,7 @@ static const struct dsa_switch_ops mxl86
.get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
.get_pause_stats = mxl862xx_get_pause_stats,
};
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -2193,8 +2353,11 @@ static int mxl862xx_probe(struct mdio_de
+@@ -2146,16 +2308,22 @@ static int mxl862xx_probe(struct mdio_de
priv->ports[i].priv = priv;
INIT_WORK(&priv->ports[i].host_flood_work,
mxl862xx_host_flood_work_fn);
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -2213,6 +2376,8 @@ static void mxl862xx_remove(struct mdio_
+ err = dsa_register_switch(ds);
+ if (err) {
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
++ cancel_delayed_work_sync(&priv->stats_work);
+ mxl862xx_host_shutdown(priv);
+ for (i = 0; i < MXL862XX_MAX_PORTS; i++)
+ cancel_work_sync(&priv->ports[i].host_flood_work);
+ }
++
+ return err;
+ }
- dsa_unregister_switch(ds);
+@@ -2170,6 +2338,9 @@ static void mxl862xx_remove(struct mdio_
+
+ priv = ds->priv;
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+
- mxl862xx_host_shutdown(priv);
+ dsa_unregister_switch(ds);
- /* Cancel any pending host flood work. dsa_unregister_switch()
-@@ -2237,6 +2402,8 @@ static void mxl862xx_shutdown(struct mdi
+ mxl862xx_host_shutdown(priv);
+@@ -2196,6 +2367,9 @@ static void mxl862xx_shutdown(struct mdi
dsa_switch_shutdown(ds);
++ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+
mxl862xx_host_shutdown(priv);
*/
struct mxl862xx_port {
struct mxl862xx_priv *priv;
-@@ -195,6 +240,9 @@ struct mxl862xx_port {
+@@ -195,16 +240,25 @@ struct mxl862xx_port {
bool host_flood_uc;
bool host_flood_mc;
struct work_struct host_flood_work;
+ spinlock_t stats_lock;
};
++/* Bit indices for struct mxl862xx_priv::flags */
++#define MXL862XX_FLAG_CRC_ERR 0
++#define MXL862XX_FLAG_WORK_STOPPED 1
++
/**
-@@ -216,6 +264,8 @@ struct mxl862xx_port {
+ * struct mxl862xx_priv - driver private data for an MxL862xx switch
+ * @ds: pointer to the DSA switch instance
+ * @mdiodev: MDIO device used to communicate with the switch firmware
+ * @crc_err_work: deferred work for shutting down all ports on MDIO CRC
+ * errors
+- * @crc_err: set atomically before CRC-triggered shutdown, cleared
+- * after
++ * @flags: atomic status flags; %MXL862XX_FLAG_CRC_ERR is set
++ * before CRC-triggered shutdown and cleared after;
++ * %MXL862XX_FLAG_WORK_STOPPED is set before cancelling
++ * stats_work to prevent rescheduling during teardown
+ * @drop_meter: index of the single shared zero-rate firmware meter
+ * used to unconditionally drop traffic (used to block
+ * flooding)
+@@ -216,18 +270,21 @@ struct mxl862xx_port {
* @evlan_ingress_size: per-port ingress Extended VLAN block size
* @evlan_egress_size: per-port egress Extended VLAN block size
* @vf_block_size: per-port VLAN Filter block size
*/
struct mxl862xx_priv {
struct dsa_switch *ds;
-@@ -228,6 +278,7 @@ struct mxl862xx_priv {
+ struct mdio_device *mdiodev;
+ struct work_struct crc_err_work;
+- unsigned long crc_err;
++ unsigned long flags;
+ u16 drop_meter;
+ struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
+ u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
u16 evlan_egress_size;
u16 vf_block_size;
-From fecfbea928cd762b19ff17aa16fb1ab143d73db1 Mon Sep 17 00:00:00 2001
+From da12469e73282da814163125153f381823e33f20 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 17:56:35 +0000
-Subject: [PATCH 17/35] net: dsa: mxl862xx: store firmware version for feature
+Subject: [PATCH 08/26] net: dsa: mxl862xx: store firmware version for feature
gating
Query the firmware version at init (already done in wait_ready),
#include <linux/mdio.h>
#include <linux/workqueue.h>
#include <net/dsa.h>
-@@ -246,6 +247,38 @@ struct mxl862xx_port {
+@@ -245,6 +246,38 @@ struct mxl862xx_port {
+ spinlock_t stats_lock;
};
- /**
++/**
+ * union mxl862xx_fw_version - firmware version for comparison and display
+ * @major: firmware major version
+ * @minor: firmware minor version
+#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \
+ ((priv)->fw_version.raw >= MXL862XX_FW_VER(maj, min, rev))
+
-+/**
- * struct mxl862xx_priv - driver private data for an MxL862xx switch
- * @ds: pointer to the DSA switch instance
- * @mdiodev: MDIO device used to communicate with the switch firmware
-@@ -256,6 +289,8 @@ struct mxl862xx_port {
+ /* Bit indices for struct mxl862xx_priv::flags */
+ #define MXL862XX_FLAG_CRC_ERR 0
+ #define MXL862XX_FLAG_WORK_STOPPED 1
+@@ -262,6 +295,8 @@ struct mxl862xx_port {
* @drop_meter: index of the single shared zero-rate firmware meter
* used to unconditionally drop traffic (used to block
* flooding)
* @ports: per-port state, indexed by switch port number
* @bridges: maps DSA bridge number to firmware bridge ID;
* zero means no firmware bridge allocated for that
-@@ -273,6 +308,7 @@ struct mxl862xx_priv {
+@@ -279,6 +314,7 @@ struct mxl862xx_priv {
struct work_struct crc_err_work;
- unsigned long crc_err;
+ unsigned long flags;
u16 drop_meter;
+ union mxl862xx_fw_version fw_version;
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
-From 3cb224514226928df80e43ca2280c7dca654bdfe Mon Sep 17 00:00:00 2001
+From f7606470d398e4091e1bc405bf2125dc5fc99919 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 21:39:30 +0000
-Subject: [PATCH 18/35] net: dsa: mxl862xx: move phylink stubs to
+Subject: [PATCH 09/26] net: dsa: mxl862xx: move phylink stubs to
mxl862xx-phylink.c
Move the phylink MAC operations and get_caps callback from mxl862xx.c
#define MXL862XX_API_WRITE(dev, cmd, data) \
mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false)
-@@ -1642,16 +1643,6 @@ static void mxl862xx_port_teardown(struc
+@@ -1597,16 +1598,6 @@ static void mxl862xx_port_teardown(struc
priv->ports[port].setup_done = false;
}
static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -2297,33 +2288,6 @@ static const struct dsa_switch_ops mxl86
+@@ -2252,33 +2243,6 @@ static const struct dsa_switch_ops mxl86
.get_stats64 = mxl862xx_get_stats64,
};
-From de41d438c4e90876449715a307dd03fa37338742 Mon Sep 17 00:00:00 2001
+From e583eeeb907f0abeef2082162293a5d63b9fd6fa Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 26 Mar 2026 01:50:00 +0000
-Subject: [PATCH 19/35] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
+Subject: [PATCH 10/26] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
Move the MXL862XX_API_WRITE, MXL862XX_API_READ and
MXL862XX_API_READ_QUIET convenience macros from mxl862xx.c to
-From 88f46eb32d1aed296af2005c3ed8f23a6eea64c3 Mon Sep 17 00:00:00 2001
+From e25f0886853607e4a6d1157ae84e43e8224cf3b7 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:57:44 +0000
-Subject: [PATCH 20/35] net: dsa: mxl862xx: add support for SerDes ports
+Subject: [PATCH 11/26] net: dsa: mxl862xx: add support for SerDes ports
The MxL862xx has two XPCS/SerDes interfaces (XPCS0 for ports 9-12,
XPCS1 for ports 13-16). Each can operate in various single-lane
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -729,7 +729,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -684,7 +684,7 @@ static int mxl862xx_setup(struct dsa_swi
int n_user_ports = 0, max_vlans;
int ingress_finals, vid_rules;
struct dsa_port *dp;
ret = mxl862xx_reset(priv);
if (ret)
-@@ -739,6 +739,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -694,6 +694,9 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
* union mxl862xx_fw_version - firmware version for comparison and display
* @major: firmware major version
* @minor: firmware minor version
-@@ -291,6 +307,8 @@ union mxl862xx_fw_version {
+@@ -297,6 +313,8 @@ union mxl862xx_fw_version {
* flooding)
* @fw_version: cached firmware version, populated at probe and
* compared with MXL862XX_FW_VER_MIN()
* @ports: per-port state, indexed by switch port number
* @bridges: maps DSA bridge number to firmware bridge ID;
* zero means no firmware bridge allocated for that
-@@ -309,6 +327,7 @@ struct mxl862xx_priv {
- unsigned long crc_err;
+@@ -315,6 +333,7 @@ struct mxl862xx_priv {
+ unsigned long flags;
u16 drop_meter;
union mxl862xx_fw_version fw_version;
+ struct mxl862xx_pcs serdes_ports[8];
-From d40565e2e00fc2c8f04b9c571fcbea2f146db844 Mon Sep 17 00:00:00 2001
+From 24d752291784e30d7329bed15744bbbc6a3e2485 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:14:33 +0000
-Subject: [PATCH 21/35] net: dsa: mxl862xx: add SerDes ethtool statistics
+Subject: [PATCH 12/26] net: dsa: mxl862xx: add SerDes ethtool statistics
Expose SerDes equalization and signal detect parameters as ethtool
statistics on ports 9-16 (XPCS-backed ports). Uses the XPCS EQ_GET
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -2007,6 +2007,8 @@ static void mxl862xx_get_strings(struct
+@@ -1960,6 +1960,8 @@ static void mxl862xx_get_strings(struct
for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++)
ethtool_puts(&data, mxl862xx_mib[i].name);
}
static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
-@@ -2014,7 +2016,7 @@ static int mxl862xx_get_sset_count(struc
+@@ -1967,7 +1969,7 @@ static int mxl862xx_get_sset_count(struc
if (sset != ETH_SS_STATS)
return 0;
}
static int mxl862xx_read_rmon(struct dsa_switch *ds, int port,
-@@ -2050,6 +2052,8 @@ static void mxl862xx_get_ethtool_stats(s
+@@ -2003,6 +2005,8 @@ static void mxl862xx_get_ethtool_stats(s
else
*data++ = le64_to_cpu(*(__le64 *)field);
}
-From 54dd5fabc543f8538202367a863eb0e9161bacab Mon Sep 17 00:00:00 2001
+From ee227a5e4c74f599cc1b34578b32214d5873ad2f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:15:32 +0000
-Subject: [PATCH 22/35] net: dsa: mxl862xx: add SerDes self-test via PRBS and
+Subject: [PATCH 13/26] net: dsa: mxl862xx: add SerDes self-test via PRBS and
BERT
Implement the dsa_switch_ops.self_test callback for SerDes ports
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -2286,6 +2286,7 @@ static const struct dsa_switch_ops mxl86
+@@ -2241,6 +2241,7 @@ static const struct dsa_switch_ops mxl86
.get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
.get_pause_stats = mxl862xx_get_pause_stats,
.get_stats64 = mxl862xx_get_stats64,
-From dd62e68cd0bd29934c3efbce687d5e103cc4b331 Mon Sep 17 00:00:00 2001
+From 43eb3eed250ea4e7e83371fcbf2bfb8d626eade6 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:13 +0000
-Subject: [PATCH 23/35] net: dsa: mxl862xx: trap link-local frames to the CPU
+Subject: [PATCH 14/26] net: dsa: mxl862xx: trap link-local frames to the CPU
port
Install per-CTP PCE rules on each user port that trap IEEE 802.1D
static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
{
struct mxl862xx_bridge_port_config br_port_cfg = {};
-@@ -1594,6 +1658,11 @@ static int mxl862xx_port_setup(struct ds
+@@ -1549,6 +1613,11 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
-From 3bba25f7ba35e3bca8230bd37ffb612944dbf301 Mon Sep 17 00:00:00 2001
+From e18f5b235d8df21209c73f4f0bbc00cc3a1973ba Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:21 +0000
-Subject: [PATCH 24/35] net: dsa: mxl862xx: warn about old firmware default PCE
+Subject: [PATCH 15/26] net: dsa: mxl862xx: warn about old firmware default PCE
rules
Firmware versions older than 1.0.80 install global PCE rules at
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -854,6 +854,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -809,6 +809,10 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
-From 1687c5632dfd80461b12425b943e30555faa3dd4 Mon Sep 17 00:00:00 2001
+From 04929904d3a7d824593587e52e3ed21d6f0f109a Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:58:04 +0000
-Subject: [PATCH 25/35] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
+Subject: [PATCH 16/26] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
The MxL862xx native 8-byte special tag (SpTag) requires firmware
support on the switch CPU and is not compatible with all SoC Ethernet
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 221 +++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 1626 ++++++++++++++++++++---
- drivers/net/dsa/mxl862xx/mxl862xx.h | 21 +-
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 13 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 7 +
net/dsa/Makefile | 1 +
net/dsa/tag_mxl862xx_8021q.c | 59 +
- 9 files changed, 1738 insertions(+), 202 deletions(-)
+ 9 files changed, 1736 insertions(+), 196 deletions(-)
create mode 100644 net/dsa/tag_mxl862xx_8021q.c
--- a/drivers/net/dsa/mxl862xx/Kconfig
err = mxl862xx_set_bridge_port(ds, port);
if (err)
-@@ -762,7 +909,6 @@ static void mxl862xx_free_bridge(struct
+@@ -717,7 +864,6 @@ static void mxl862xx_free_bridge(struct
static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
{
struct mxl862xx_priv *priv = ds->priv;
int ret;
-@@ -774,15 +920,27 @@ static int mxl862xx_add_single_port_brid
+@@ -729,15 +875,27 @@ static int mxl862xx_add_single_port_brid
priv->ports[port].learning = false;
bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
- /* Standalone ports should not flood unknown unicast or multicast
- * towards the CPU by default; only broadcast is needed initially.
-- */
+ /* In tag_8021q mode the TX path goes through the bridge engine
+ * (CTP ingress EVLAN reassigns to a virtual bridge port which
+ * then forwards via the bridge). With learning disabled on
+ * In native SpTag mode, TX bypasses the bridge engine entirely
+ * (the special tag selects the egress port directly), so flood
+ * control only affects CPU-bound traffic and can be restrictive.
-+ */
+ */
+ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q)
+ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
+ true, true, true);
return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
false, false, true);
}
-@@ -790,10 +948,12 @@ static int mxl862xx_add_single_port_brid
+@@ -745,10 +903,12 @@ static int mxl862xx_add_single_port_brid
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
ret = mxl862xx_reset(priv);
if (ret)
-@@ -806,7 +966,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -761,7 +921,7 @@ static int mxl862xx_setup(struct dsa_swi
for (i = 0; i < 8; i++)
mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9);
* With VLAN Filter handling VID membership checks:
* Ingress: only final catchall rules (PVID insertion, 802.1Q
* accept, non-8021Q TPID handling, discard).
-@@ -814,40 +974,67 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -769,40 +929,67 @@ static int mxl862xx_setup(struct dsa_swi
* ingress EVLAN rules are needed. (7 entries.)
* Egress: 2 rules per VID that needs tag stripping (untagged VIDs).
* No egress final catchalls -- VLAN Filter does the discard.
}
ret = mxl862xx_setup_drop_meter(ds);
-@@ -858,6 +1045,68 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -813,6 +1000,68 @@ static int mxl862xx_setup(struct dsa_swi
dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules "
"that interfere with DSA operation, please update\n");
schedule_delayed_work(&priv->stats_work,
MXL862XX_STATS_POLL_INTERVAL);
-@@ -939,6 +1188,52 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -894,6 +1143,52 @@ static int mxl862xx_configure_sp_tag_pro
}
/**
* mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
* @priv: driver private data
* @block_id: HW Extended VLAN block ID
-@@ -947,6 +1242,7 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -902,6 +1197,7 @@ static int mxl862xx_configure_sp_tag_pro
* @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
* @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
* @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
*
* Translates a compact rule descriptor into a full firmware
* mxl862xx_extendedvlan_config struct and writes it via the API.
-@@ -954,7 +1250,8 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -909,7 +1205,8 @@ static int mxl862xx_configure_sp_tag_pro
static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
u16 block_id, u16 entry_index,
const struct mxl862xx_evlan_rule_desc *desc,
{
struct mxl862xx_extendedvlan_config cfg = {};
struct mxl862xx_extendedvlan_filter_vlan *fv;
-@@ -1044,6 +1341,31 @@ static int mxl862xx_evlan_write_rule(str
+@@ -999,6 +1296,31 @@ static int mxl862xx_evlan_write_rule(str
cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
}
break;
}
return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
-@@ -1104,7 +1426,7 @@ static int mxl862xx_evlan_write_final_ru
+@@ -1059,7 +1381,7 @@ static int mxl862xx_evlan_write_final_ru
for (i = 0; i < n_rules; i++) {
ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
start_idx + i, &rules[i],
if (ret)
return ret;
}
-@@ -1273,6 +1595,27 @@ static int mxl862xx_vf_del_vid(struct mx
+@@ -1228,6 +1550,27 @@ static int mxl862xx_vf_del_vid(struct mx
}
/**
* mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
* @priv: driver private data
* @port: port number
-@@ -1323,8 +1666,8 @@ static int mxl862xx_evlan_program_egress
+@@ -1278,8 +1621,8 @@ static int mxl862xx_evlan_program_egress
const struct mxl862xx_evlan_rule_desc *vid_rules;
struct mxl862xx_vf_vid *vfv;
u16 old_active = blk->n_active;
if (p->vlan_filtering) {
vid_rules = vid_accept_standard;
-@@ -1341,13 +1684,23 @@ static int mxl862xx_evlan_program_egress
+@@ -1296,13 +1639,23 @@ static int mxl862xx_evlan_program_egress
if (p->vlan_filtering && !vfv->untagged)
continue;
if (ret)
return ret;
-@@ -1356,7 +1709,29 @@ static int mxl862xx_evlan_program_egress
+@@ -1311,7 +1664,29 @@ static int mxl862xx_evlan_program_egress
idx++, &vid_rules[1],
vfv->vid,
vfv->untagged,
if (ret)
return ret;
}
-@@ -1368,8 +1743,7 @@ static int mxl862xx_evlan_program_egress
+@@ -1323,8 +1698,7 @@ static int mxl862xx_evlan_program_egress
*/
for (i = idx; i < old_active; i++) {
ret = mxl862xx_evlan_deactivate_entry(priv,
if (ret)
return ret;
}
-@@ -1393,13 +1767,16 @@ static int mxl862xx_port_vlan_filtering(
+@@ -1348,13 +1722,16 @@ static int mxl862xx_port_vlan_filtering(
/* Reprogram Extended VLAN rules if filtering mode changed */
if (changed) {
ret = mxl862xx_evlan_program_ingress(priv, port);
if (ret)
-@@ -1536,18 +1913,19 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1491,18 +1868,19 @@ static int mxl862xx_setup_cpu_bridge(str
/* include all assigned user ports in the CPU portmap */
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -1580,7 +1958,6 @@ static int mxl862xx_port_bridge_join(str
+@@ -1535,7 +1913,6 @@ static int mxl862xx_port_bridge_join(str
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge)
{
struct mxl862xx_priv *priv = ds->priv;
struct mxl862xx_port *p = &priv->ports[port];
int err;
-@@ -1595,34 +1972,587 @@ static void mxl862xx_port_bridge_leave(s
+@@ -1550,34 +1927,587 @@ static void mxl862xx_port_bridge_leave(s
* single-port bridge
*/
bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
- /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
- * them. The firmware tracks a usage count per block and rejects
- * FREE while the count is non-zero.
+- *
+- * For EVLAN: setting in_use=false makes set_bridge_port send
+- * enable=false, which decrements the firmware refcount.
+ /* Reset VLAN state for standalone mode. Ingress EVLAN is not
+ * needed outside a VLAN-aware bridge. Egress EVLAN is
+ * reprogrammed below -- in tag_8021q mode it gets the
+ * management VID strip catchalls, in SpTag mode it is cleared.
*
-- * For EVLAN: setting in_use=false makes set_bridge_port send
-- * enable=false, which decrements the firmware refcount.
-- *
- * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
- * cleared it) and sends vlan_filter_enable=0, which decrements
- * the firmware VF refcount.
static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -1642,55 +2572,30 @@ static int mxl862xx_port_setup(struct ds
+@@ -1597,55 +2527,30 @@ static int mxl862xx_port_setup(struct ds
dsa_port_is_dsa(dp))
return 0;
return 0;
}
-@@ -1712,7 +2617,7 @@ static void mxl862xx_port_teardown(struc
+@@ -1667,7 +2572,7 @@ static void mxl862xx_port_teardown(struc
priv->ports[port].setup_done = false;
}
{
struct mxl862xx_priv *priv = ds->priv;
-@@ -1730,23 +2635,244 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -1685,23 +2590,244 @@ static int mxl862xx_get_fid(struct dsa_s
}
}
+ if (dsa_is_cpu_port(ds, port) && priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q &&
+ db.type == DSA_DB_PORT) {
+ bp_cpu = priv->ports[db.dp->index].bridge_port_cpu;
-+
+
+- param.port_id = cpu_to_le32(port);
+ if (bp_cpu)
+ return bp_cpu;
+ }
+{
+ struct mxl862xx_mac_table_add param = {};
+ struct mxl862xx_priv *priv = ds->priv;
-
-- param.port_id = cpu_to_le32(port);
++
+ param.port_id = cpu_to_le32(port_id);
param.static_entry = true;
param.fid = cpu_to_le16(fid);
if (ret)
dev_err(ds->dev, "failed to add FDB entry on port %d\n", port);
-@@ -1756,18 +2882,25 @@ static int mxl862xx_port_fdb_add(struct
+@@ -1711,18 +2837,25 @@ static int mxl862xx_port_fdb_add(struct
static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, const struct dsa_db db)
{
if (ret)
dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port);
-@@ -1806,88 +2939,147 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -1761,88 +2894,147 @@ static int mxl862xx_port_fdb_dump(struct
return 0;
}
- int fid = mxl862xx_get_fid(ds, db), ret;
struct mxl862xx_priv *priv = ds->priv;
+ int fid, ret;
-+
+
+ /* tag_8021q host MDB for bridged ports: clear all VBP bits */
+ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) &&
+ db.type == DSA_DB_BRIDGE) {
+ return mxl862xx_mac_del_host_bridge(ds, mdb->addr,
+ mdb->vid, &db.bridge);
+ }
-
++
+ fid = mxl862xx_get_fid(ds, db);
if (fid < 0)
return fid;
return ret;
}
-@@ -1975,7 +3167,9 @@ static void mxl862xx_host_flood_work_fn(
+@@ -1930,7 +3122,9 @@ static void mxl862xx_host_flood_work_fn(
struct mxl862xx_priv *priv = p->priv;
struct dsa_switch *ds = priv->ds;
int port = p - priv->ports;
rtnl_lock();
-@@ -1988,14 +3182,31 @@ static void mxl862xx_host_flood_work_fn(
+@@ -1943,14 +3137,35 @@ static void mxl862xx_host_flood_work_fn(
uc = p->host_flood_uc;
mc = p->host_flood_mc;
+ port, ERR_PTR(ret));
+ }
+ } else {
-+ /* SpTag mode: per-FID forwarding, only works for
-+ * standalone ports (each has its own FID).
++ /* The hardware controls unknown-unicast/multicast forwarding
++ * per FID (bridge), not per source port. For bridged ports all
++ * members share one FID, so we cannot selectively suppress
++ * flooding to the CPU for one source port while allowing it
++ * for another. Silently ignore the request -- the excess
++ * flooding towards the CPU is harmless.
+ */
+ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
+ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
rtnl_unlock();
}
-@@ -2330,7 +3541,9 @@ static void mxl862xx_get_stats64(struct
+@@ -2285,7 +3500,9 @@ static void mxl862xx_get_stats64(struct
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
.port_setup = mxl862xx_port_setup,
.port_teardown = mxl862xx_port_teardown,
.phylink_get_caps = mxl862xx_phylink_get_caps,
-@@ -2352,6 +3565,8 @@ static const struct dsa_switch_ops mxl86
+@@ -2307,6 +3524,8 @@ static const struct dsa_switch_ops mxl86
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
.get_strings = mxl862xx_get_strings,
.get_sset_count = mxl862xx_get_sset_count,
.get_ethtool_stats = mxl862xx_get_ethtool_stats,
-@@ -2399,6 +3614,8 @@ static int mxl862xx_probe(struct mdio_de
+@@ -2354,6 +3573,8 @@ static int mxl862xx_probe(struct mdio_de
INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn);
+
dev_set_drvdata(dev, ds);
- return dsa_register_switch(ds);
-@@ -2415,16 +3632,29 @@ static void mxl862xx_remove(struct mdio_
-
- priv = ds->priv;
+ err = dsa_register_switch(ds);
+@@ -2382,6 +3603,19 @@ static void mxl862xx_remove(struct mdio_
+ set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
+ cancel_delayed_work_sync(&priv->stats_work);
+ /* Tear down tag_8021q under RTNL before dsa_unregister_switch().
+ * dsa_tag_8021q_unregister() calls vlan_vid_del() which needs
+
dsa_unregister_switch(ds);
- cancel_delayed_work_sync(&priv->stats_work);
-
mxl862xx_host_shutdown(priv);
-
-- /* Cancel any pending host flood work. dsa_unregister_switch()
-+ /* Cancel any pending host flood work. dsa_unregister_switch()
- * has already called port_teardown (which sets setup_done=false),
- * but a worker could still be blocked on rtnl_lock(). Since we
-- * are now outside RTNL, cancel_work_sync() will not deadlock.
-+ * are now outside RTNL, cancel_work_sync() won't deadlock.
- */
- for (i = 0; i < MXL862XX_MAX_PORTS; i++)
- cancel_work_sync(&priv->ports[i].host_flood_work);
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -8,8 +8,6 @@
- #include <linux/workqueue.h>
- #include <net/dsa.h>
-
--struct mxl862xx_priv;
--
- #define MXL862XX_MAX_PORTS 17
- #define MXL862XX_DEFAULT_BRIDGE 0
- #define MXL862XX_MAX_BRIDGES 48
-@@ -20,6 +18,8 @@ struct mxl862xx_priv;
- /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
- #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
-
-+struct mxl862xx_priv;
-+
- /**
- * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware
- * portmap (__le16[8])
@@ -210,6 +210,9 @@ struct mxl862xx_port_stats {
* @vf: per-port VLAN Filter block state
* @ingress_evlan: ingress extended VLAN block state
/* Hardware stats accumulation */
struct mxl862xx_port_stats stats;
spinlock_t stats_lock;
-@@ -302,6 +311,7 @@ union mxl862xx_fw_version {
- * errors
- * @crc_err: set atomically before CRC-triggered shutdown, cleared
- * after
+@@ -308,6 +317,7 @@ union mxl862xx_fw_version {
+ * before CRC-triggered shutdown and cleared after;
+ * %MXL862XX_FLAG_WORK_STOPPED is set before cancelling
+ * stats_work to prevent rescheduling during teardown
+ * @tag_proto: active DSA tag protocol (native or 8021q)
* @drop_meter: index of the single shared zero-rate firmware meter
* used to unconditionally drop traffic (used to block
* flooding)
-@@ -310,12 +320,13 @@ union mxl862xx_fw_version {
- * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of
- * 10G_QXGMII
- * @ports: per-port state, indexed by switch port number
-+ * @evlan_ingress_size: per-port ingress Extended VLAN block size
-+ * @evlan_egress_size: per-port egress Extended VLAN block size
-+ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
- * @bridges: maps DSA bridge number to firmware bridge ID;
- * zero means no firmware bridge allocated for that
- * DSA bridge number. Indexed by dsa_bridge.num
+@@ -322,6 +332,7 @@ union mxl862xx_fw_version {
* (0 .. ds->max_num_bridges).
-- * @evlan_ingress_size: per-port ingress Extended VLAN block size
-- * @evlan_egress_size: per-port egress Extended VLAN block size
+ * @evlan_ingress_size: per-port ingress Extended VLAN block size
+ * @evlan_egress_size: per-port egress Extended VLAN block size
++ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
* @vf_block_size: per-port VLAN Filter block size
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
-@@ -325,6 +336,7 @@ struct mxl862xx_priv {
+@@ -331,6 +342,7 @@ struct mxl862xx_priv {
struct mdio_device *mdiodev;
struct work_struct crc_err_work;
- unsigned long crc_err;
+ unsigned long flags;
+ enum dsa_tag_protocol tag_proto;
u16 drop_meter;
union mxl862xx_fw_version fw_version;
struct mxl862xx_pcs serdes_ports[8];
-@@ -332,6 +344,7 @@ struct mxl862xx_priv {
+@@ -338,6 +350,7 @@ struct mxl862xx_priv {
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
u16 evlan_egress_size;
};
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
-@@ -56,6 +56,8 @@ struct tc_action;
+@@ -56,6 +56,7 @@ struct tc_action;
#define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28
#define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29
#define DSA_TAG_PROTO_MXL862_VALUE 30
+#define DSA_TAG_PROTO_MXL862_8021Q_VALUE 31
-+
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
-@@ -89,6 +91,7 @@ enum dsa_tag_protocol {
+@@ -89,6 +90,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE,
DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
-From 31359e8b7673e656d0591a9eb5014b45911383ae Mon Sep 17 00:00:00 2001
+From 4e1d854199c166f617b93b7542e863e6a8ad2ccb Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 03:44:41 +0000
-Subject: [PATCH 26/35] net: dsa: mxl862xx: add link aggregation support
+Subject: [PATCH 17/26] net: dsa: mxl862xx: add link aggregation support
Implement LAG offloading via the firmware's trunking engine. A
dedicated firmware bridge port is allocated per LAG and remains
return ret;
}
-@@ -1926,6 +2018,408 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1881,6 +1973,408 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
const struct dsa_bridge bridge,
bool *tx_fwd_offload,
-@@ -1952,7 +2446,18 @@ static int mxl862xx_port_bridge_join(str
+@@ -1907,7 +2401,18 @@ static int mxl862xx_port_bridge_join(str
return 0;
}
}
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
-@@ -2011,6 +2516,17 @@ static void mxl862xx_port_bridge_leave(s
+@@ -1966,6 +2471,17 @@ static void mxl862xx_port_bridge_leave(s
"failed to update CPU VBP for port %d: %pe\n", port,
ERR_PTR(err));
if (!dsa_bridge_ports(ds, bridge.dev))
mxl862xx_free_bridge(ds, &bridge);
}
-@@ -2636,18 +3152,17 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -2591,18 +3107,17 @@ static int mxl862xx_get_fid(struct dsa_s
}
/**
*/
static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port,
const struct dsa_db db)
-@@ -2663,7 +3178,7 @@ static int mxl862xx_fdb_bridge_port(stru
+@@ -2618,7 +3133,7 @@ static int mxl862xx_fdb_bridge_port(stru
return bp_cpu;
}
}
/**
-@@ -2907,11 +3422,43 @@ static int mxl862xx_port_fdb_del(struct
+@@ -2862,11 +3377,43 @@ static int mxl862xx_port_fdb_del(struct
return ret;
}
u32 entry_port_id;
int ret;
-@@ -2925,7 +3472,7 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -2880,7 +3427,7 @@ static int mxl862xx_port_fdb_dump(struct
entry_port_id = le32_to_cpu(param.port_id);
ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID,
le16_to_cpu(param.tci)),
param.static_entry, data);
-@@ -3562,6 +4109,11 @@ static const struct dsa_switch_ops mxl86
+@@ -3521,6 +4068,11 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
.port_vlan_filtering = mxl862xx_port_vlan_filtering,
.port_vlan_add = mxl862xx_port_vlan_add,
.port_vlan_del = mxl862xx_port_vlan_del,
-@@ -3602,6 +4154,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -3561,6 +4113,7 @@ static int mxl862xx_probe(struct mdio_de
ds->num_ports = MXL862XX_MAX_PORTS;
ds->fdb_isolation = true;
ds->max_num_bridges = MXL862XX_MAX_BRIDGES;
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -14,6 +14,19 @@
+@@ -16,6 +16,19 @@ struct mxl862xx_priv;
#define MXL862XX_MAX_BRIDGE_PORTS 128
#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024
#define MXL862XX_TOTAL_VF_ENTRIES 1024
/* Hardware stats accumulation */
struct mxl862xx_port_stats stats;
spinlock_t stats_lock;
-@@ -328,6 +351,15 @@ union mxl862xx_fw_version {
- * DSA bridge number. Indexed by dsa_bridge.num
- * (0 .. ds->max_num_bridges).
+@@ -334,6 +357,15 @@ union mxl862xx_fw_version {
+ * @evlan_egress_size: per-port egress Extended VLAN block size
+ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q)
* @vf_block_size: per-port VLAN Filter block size
+ * @lag_bridge_ports: maps DSA LAG ID to firmware bridge port ID;
+ * zero means no bridge port allocated for that LAG.
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
*/
-@@ -346,6 +378,8 @@ struct mxl862xx_priv {
+@@ -352,6 +384,8 @@ struct mxl862xx_priv {
u16 evlan_egress_size;
u16 cpu_evlan_ingress_size;
u16 vf_block_size;
-From fbfa1b0649c578e0d43e3a61617b53a9a722efad Mon Sep 17 00:00:00 2001
+From 5528f38c3d709417625eb7f36628be31727a8221 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 12:05:29 +0000
-Subject: [PATCH 27/35] net: dsa: mxl862xx: add support for mirror port
+Subject: [PATCH 18/26] net: dsa: mxl862xx: add support for mirror port
The MxL862xx hardware supports a single monitor port which can be
configured to mirror any other port's ingress and/or egress traffic.
#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -1129,6 +1129,8 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -1084,6 +1084,8 @@ static int mxl862xx_setup(struct dsa_swi
(n_user_ports + n_cpu_ports);
}
ret = mxl862xx_setup_drop_meter(ds);
if (ret)
return ret;
-@@ -2018,6 +2020,120 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1973,6 +1975,120 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
/**
* mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member)
* @ds: DSA switch
-@@ -4109,6 +4225,8 @@ static const struct dsa_switch_ops mxl86
+@@ -4068,6 +4184,8 @@ static const struct dsa_switch_ops mxl86
.port_fdb_dump = mxl862xx_port_fdb_dump,
.port_mdb_add = mxl862xx_port_mdb_add,
.port_mdb_del = mxl862xx_port_mdb_del,
/* LAG state */
struct dsa_lag *lag;
bool lag_tx_enabled;
-@@ -360,6 +365,8 @@ union mxl862xx_fw_version {
+@@ -366,6 +371,8 @@ union mxl862xx_fw_version {
* @trunk_hash: current global hash field bitmask (6 bits,
* MXL862XX_TRUNK_HASH_*); union of all active LAGs'
* hash requirements
* @stats_work: periodic work item that polls RMON hardware counters
* and accumulates them into 64-bit per-port stats
*/
-@@ -380,6 +387,7 @@ struct mxl862xx_priv {
+@@ -386,6 +393,7 @@ struct mxl862xx_priv {
u16 vf_block_size;
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
-From 67f82834819b71417b58dc1293c20f71b990264f Mon Sep 17 00:00:00 2001
+From 4059d35a5bbf1901b2e0eb7126369cd713cacfce Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:08 +0000
-Subject: [PATCH 28/35] net: dsa: wire flash_update devlink callback to drivers
+Subject: [PATCH 19/26] net: dsa: wire flash_update devlink callback to drivers
Add a devlink_flash_update callback to dsa_switch_ops so that DSA
drivers can support devlink dev flash without open-coding the devlink
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
-@@ -1172,6 +1172,9 @@ struct dsa_switch_ops {
+@@ -1171,6 +1171,9 @@ struct dsa_switch_ops {
int (*devlink_info_get)(struct dsa_switch *ds,
struct devlink_info_req *req,
struct netlink_ext_ack *extack);
-From 1a87b829ef3280d646dc480f7b261d9e32896899 Mon Sep 17 00:00:00 2001
+From 0145151dc68aa318d8addb6fe7f12c0967f951da Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:17 +0000
-Subject: [PATCH 29/35] net: dsa: mxl862xx: add SMDIO clause-22 register access
+Subject: [PATCH 20/26] net: dsa: mxl862xx: add SMDIO clause-22 register access
Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22
SMDIO register access. MCUboot rescue mode only exposes clause-22
-From b7e8f8fd4493b255f0f01fe790a73ad61b5e8ce8 Mon Sep 17 00:00:00 2001
+From bdbca48510e3e96ed9210f20fa4244dd6df5d44a Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:31 +0000
-Subject: [PATCH 30/35] net: dsa: mxl862xx: add devlink flash_update and
+Subject: [PATCH 21/26] net: dsa: mxl862xx: add devlink flash_update and
info_get
Implement runtime firmware upgrade via "devlink dev flash" and version
#include "mxl862xx-host.h"
#include "mxl862xx-phylink.h"
-@@ -4245,6 +4246,9 @@ static const struct dsa_switch_ops mxl86
+@@ -4204,6 +4205,9 @@ static const struct dsa_switch_ops mxl86
.get_pause_stats = mxl862xx_get_pause_stats,
.get_stats64 = mxl862xx_get_stats64,
.self_test = mxl862xx_serdes_self_test,
static int mxl862xx_probe(struct mdio_device *mdiodev)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -388,6 +388,8 @@ struct mxl862xx_priv {
+@@ -394,6 +394,8 @@ struct mxl862xx_priv {
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
int mirror_dest;
-From 2cb9aeb3a8d7ebac20331e0a533dcfbd73fa4237 Mon Sep 17 00:00:00 2001
+From 8deb5be9638f7eb3009ed3eb619eedadee1df523 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 23:42:18 +0000
-Subject: [PATCH 31/35] net: dsa: mxl862xx: implement port MTU configuration
+Subject: [PATCH 22/26] net: dsa: mxl862xx: implement port MTU configuration
The firmware exposes a global max_packet_len register via
MXL862XX_COMMON_CFGSET. Since this is switch-wide rather than
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
-@@ -3768,6 +3769,53 @@ static int mxl862xx_set_ageing_time(stru
+@@ -3723,6 +3724,53 @@ static int mxl862xx_set_ageing_time(stru
return ret;
}
static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
-@@ -4215,6 +4263,8 @@ static const struct dsa_switch_ops mxl86
+@@ -4174,6 +4222,8 @@ static const struct dsa_switch_ops mxl86
.port_disable = mxl862xx_port_disable,
.port_fast_age = mxl862xx_port_fast_age,
.set_ageing_time = mxl862xx_set_ageing_time,
-From d55ca68eb0d20a66c32d531b0a454871b486c1b1 Mon Sep 17 00:00:00 2001
+From 13a4c918cd9ded7207f38033511ab13f7aff9bd2 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 01:47:19 +0000
-Subject: [PATCH 32/35] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
+Subject: [PATCH 23/26] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
Implement hairpin mode by including the port's own bridge port ID in
its forwarding portmap. When hairpin is enabled, bridged frames whose
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 30 ++++++++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 29 +++++++++++++++++++++++++++--
drivers/net/dsa/mxl862xx/mxl862xx.h | 6 ++++++
- 2 files changed, 35 insertions(+), 1 deletion(-)
+ 2 files changed, 33 insertions(+), 2 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
err = mxl862xx_set_bridge_port(ds, port);
if (err)
ret = err;
-@@ -3939,7 +3948,7 @@ static int mxl862xx_port_pre_bridge_flag
+@@ -3898,7 +3907,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
return -EINVAL;
return 0;
-@@ -3954,6 +3963,7 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3912,6 +3921,7 @@ static int mxl862xx_port_bridge_flags(st
+ unsigned long old_block = priv->ports[port].flood_block;
unsigned long block = old_block;
- bool need_update = false;
int ret;
+ u16 bp;
if (flags.mask & BR_FLOOD) {
if (flags.val & BR_FLOOD)
-@@ -3988,6 +3998,24 @@ static int mxl862xx_port_bridge_flags(st
- ret = mxl862xx_set_bridge_port(ds, port);
- if (ret)
- return ret;
-+ }
-+
+@@ -3940,7 +3950,22 @@ static int mxl862xx_port_bridge_flags(st
+ if (flags.mask & BR_LEARNING)
+ priv->ports[port].learning = !!(flags.val & BR_LEARNING);
+
+- if ((block != old_block) || (flags.mask & BR_LEARNING)) {
+ if (flags.mask & BR_HAIRPIN_MODE) {
+ bp = mxl862xx_lag_bridge_port(priv, port);
+ priv->ports[port].hairpin = !!(flags.val & BR_HAIRPIN_MODE);
+ __set_bit(bp, priv->ports[port].portmap);
+ else
+ __clear_bit(bp, priv->ports[port].portmap);
++ }
+
-+ ret = mxl862xx_set_bridge_port(ds, port);
-+ if (ret)
-+ return ret;
- }
-
- return 0;
++ if ((block != old_block) ||
++ (flags.mask & (BR_LEARNING | BR_HAIRPIN_MODE))) {
+ priv->ports[port].flood_block = block;
+ ret = mxl862xx_set_bridge_port(ds, port);
+ if (ret)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -241,6 +241,10 @@ struct mxl862xx_port_stats {
-From 74b6654ba74eb142340de4c51b97c0221cfcae37 Mon Sep 17 00:00:00 2001
+From d49d1f8bee29269def7593f980d0e08bfb5c3ef8 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 01:51:33 +0000
-Subject: [PATCH 33/35] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
+Subject: [PATCH 24/26] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
Implement port isolation by excluding isolated ports from each other's
forwarding portmaps in sync_bridge_members. Non-isolated ports can
if (member != port) {
bp = mxl862xx_lag_bridge_port(priv,
member);
-@@ -3948,7 +3956,7 @@ static int mxl862xx_port_pre_bridge_flag
+@@ -3907,7 +3915,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
return -EINVAL;
return 0;
-@@ -3962,6 +3970,7 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3920,6 +3928,7 @@ static int mxl862xx_port_bridge_flags(st
+ struct mxl862xx_priv *priv = ds->priv;
unsigned long old_block = priv->ports[port].flood_block;
unsigned long block = old_block;
- bool need_update = false;
+ struct dsa_port *dp;
int ret;
u16 bp;
-@@ -4018,6 +4027,21 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3972,6 +3981,21 @@ static int mxl862xx_port_bridge_flags(st
return ret;
}
-From 0902a6790750714445c75a66d60f1bc4897126ce Mon Sep 17 00:00:00 2001
+From c2fb7f0df63ac994850f766e7f2eb50c6c5ef2cf Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:17:49 +0000
-Subject: [PATCH 34/35] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
+Subject: [PATCH 25/26] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
workaround for old firmware
Re-introduce the mxl862xx_disable_fw_global_rules() function that
/* Per-CTP offset used for the link-local trap rule. Each port's CTP
* flow-table block is pre-allocated by the firmware during init (44
-@@ -1154,9 +1191,11 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -1109,9 +1146,11 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
-From 0ac876d5b952218ab79ea0a0815cf6fd1290b1d0 Mon Sep 17 00:00:00 2001
+From f0548f842b9ff31f19452a2fc72a16f937d86008 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:19:56 +0000
-Subject: [PATCH 35/35] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
+Subject: [PATCH 26/26] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
fallback for old firmware
Re-introduce the SYS_MISC_SFP_SET-based PCS implementation as a