]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
generic: 6.12: sync MxL862xx driver with upstream Linux
authorDaniel Golle <daniel@makrotopia.org>
Wed, 22 Apr 2026 02:56:43 +0000 (03:56 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Thu, 23 Apr 2026 12:23:39 +0000 (13:23 +0100)
Swap pending with accepted patches, rebase remaining pending patches
on top of new upstream.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
30 files changed:
target/linux/generic/backport-6.12/771-v7.1-net-dsa-mxl862xx-cancel-pending-work-on-probe-error.patch [moved from target/linux/generic/pending-6.12/760-00-net-dsa-mxl862xx-cancel-pending-work-on-probe-error.patch with 72% similarity]
target/linux/generic/backport-6.12/772-v7.1-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch [moved from target/linux/generic/pending-6.12/760-01-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch with 65% similarity]
target/linux/generic/backport-6.12/773-v7.1-net-dsa-add-bridge-member-iteration-macro.patch [moved from target/linux/generic/pending-6.12/760-02-net-dsa-add-bridge-member-iteration-macro.patch with 80% similarity]
target/linux/generic/backport-6.12/774-v7.1-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch [moved from target/linux/generic/pending-6.12/760-03-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch with 71% similarity]
target/linux/generic/backport-6.12/775-v7.1-net-dsa-mxl862xx-implement-bridge-offloading.patch [moved from target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-implement-bridge-offloading.patch with 87% similarity]
target/linux/generic/backport-6.12/776-v7.1-net-dsa-mxl862xx-reject-DSA_PORT_TYPE_DSA.patch [new file with mode: 0644]
target/linux/generic/backport-6.12/777-v7.1-net-dsa-mxl862xx-don-t-skip-early-bridge-port-config.patch [new file with mode: 0644]
target/linux/generic/backport-6.12/778-v7.1-net-dsa-mxl862xx-implement-VLAN-functionality.patch [moved from target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch with 82% similarity]
target/linux/generic/backport-6.12/779-v7.1-net-dsa-mxl862xx-add-ethtool-statistics-support.patch [moved from target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-ethtool-statistics-support.patch with 83% similarity]
target/linux/generic/backport-6.12/780-v7.1-net-dsa-mxl862xx-implement-.get_stats64.patch [moved from target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-implement-.get_stats64.patch with 80% similarity]
target/linux/generic/pending-6.12/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch [moved from target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch with 89% similarity]
target/linux/generic/pending-6.12/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch [moved from target/linux/generic/pending-6.12/760-09-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch with 95% similarity]
target/linux/generic/pending-6.12/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch [moved from target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch with 94% similarity]
target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch [moved from target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch with 99% similarity]
target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch [moved from target/linux/generic/pending-6.12/760-12-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch with 96% similarity]
target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch [moved from target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch with 96% similarity]
target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch [moved from target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-trap-link-local-frames-to-the-CPU-p.patch with 78% similarity]
target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch [moved from target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch with 81% similarity]
target/linux/generic/pending-6.12/760-09-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch [moved from target/linux/generic/pending-6.12/760-16-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch with 83% similarity]
target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-add-link-aggregation-support.patch [moved from target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-add-link-aggregation-support.patch with 90% similarity]
target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-mirror-port.patch [moved from target/linux/generic/pending-6.12/760-18-net-dsa-mxl862xx-add-support-for-mirror-port.patch with 89% similarity]
target/linux/generic/pending-6.12/760-12-net-dsa-wire-flash_update-devlink-callback-to-driver.patch [moved from target/linux/generic/pending-6.12/760-19-net-dsa-wire-flash_update-devlink-callback-to-driver.patch with 93% similarity]
target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch [moved from target/linux/generic/pending-6.12/760-20-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch with 94% similarity]
target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch [moved from target/linux/generic/pending-6.12/760-21-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch with 98% similarity]
target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-implement-port-MTU-configuration.patch [moved from target/linux/generic/pending-6.12/760-22-net-dsa-mxl862xx-implement-port-MTU-configuration.patch with 83% similarity]
target/linux/generic/pending-6.12/760-16-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch [moved from target/linux/generic/pending-6.12/760-23-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch with 55% similarity]
target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch [moved from target/linux/generic/pending-6.12/760-24-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch with 70% similarity]
target/linux/generic/pending-6.12/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch [moved from target/linux/generic/pending-6.12/760-25-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch with 87% similarity]
target/linux/generic/pending-6.12/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch [moved from target/linux/generic/pending-6.12/760-26-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch with 100% similarity]
target/linux/generic/pending-6.12/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch [new file with mode: 0644]

similarity index 72%
rename from target/linux/generic/pending-6.12/760-00-net-dsa-mxl862xx-cancel-pending-work-on-probe-error.patch
rename to target/linux/generic/backport-6.12/771-v7.1-net-dsa-mxl862xx-cancel-pending-work-on-probe-error.patch
index d215cbbb8d7f283c2439f9dc21c3b25382f6883d..ad42878eb7136b40d61e71a461cb0ff92a219378 100644 (file)
@@ -1,13 +1,16 @@
-From 3fd163f5bb88de426ca9847549f94b4296170ef0 Mon Sep 17 00:00:00 2001
+From e9abf1da0af3f787a03b249945e5ca726c1b8013 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 30 Mar 2026 23:40:53 +0100
+Date: Mon, 30 Mar 2026 23:52:09 +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)"
+Fixes: a319d0c8c8ce ("net: dsa: mxl862xx: add CRC for MDIO communication")
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/3fd163f5bb88de426ca9847549f94b4296170ef0.1774911025.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
  drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ++++++-
  1 file changed, 6 insertions(+), 1 deletion(-)
similarity index 65%
rename from target/linux/generic/pending-6.12/760-01-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch
rename to target/linux/generic/backport-6.12/772-v7.1-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch
index c203174a8a6f7caf26c9011ecfeacbae8cc25772..c0390aae9665c2e825e79cae215675a71f38e165 100644 (file)
@@ -1,7 +1,7 @@
-From cd698f1ae94c16499e2714b31dd6048e6f9f068d Mon Sep 17 00:00:00 2001
+From b0a79590d10847f190ed377d2664377d7068191d Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 25 Mar 2026 17:54:11 +0000
-Subject: [PATCH 01/26] net: dsa: move dsa_bridge_ports() helper to dsa.h
+Date: Wed, 1 Apr 2026 14:34:30 +0100
+Subject: [PATCH 1/4] 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.
@@ -10,9 +10,11 @@ Move the helper as static inline function into dsa.h, so other driver
 can make use of it as well.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/4f8bbfce3e4e3a02064fc4dc366263136c6e0383.1775049897.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
- include/net/dsa.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
+ include/net/dsa.h        | 13 +++++++++++++
2 files changed, 13 insertions(+), 13 deletions(-)
 
 --- a/include/net/dsa.h
 +++ b/include/net/dsa.h
similarity index 80%
rename from target/linux/generic/pending-6.12/760-02-net-dsa-add-bridge-member-iteration-macro.patch
rename to target/linux/generic/backport-6.12/773-v7.1-net-dsa-add-bridge-member-iteration-macro.patch
index 67dc948461c78372aab3a93c99db121a747a8d9d..26bd74ce1e1e16dbd412cafb5c4bb793770efb56 100644 (file)
@@ -1,7 +1,7 @@
-From c161533e1605a7282563c139323a3913890fdb72 Mon Sep 17 00:00:00 2001
+From f259e08494c47c614ce7b6d3079d1e0d3f30ae66 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 25 Mar 2026 17:54:41 +0000
-Subject: [PATCH 02/26] net: dsa: add bridge member iteration macro
+Date: Wed, 1 Apr 2026 14:34:42 +0100
+Subject: [PATCH 2/4] 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
@@ -15,6 +15,8 @@ directly, and use it for the existing dsa_bridge_ports() inline
 helper.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/e7136aaa26773f39e805a00fe4ecf13cd2b83fc0.1775049897.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
  include/net/dsa.h | 9 ++++++---
  1 file changed, 6 insertions(+), 3 deletions(-)
similarity index 71%
rename from target/linux/generic/pending-6.12/760-03-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch
rename to target/linux/generic/backport-6.12/774-v7.1-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch
index e47f4e44c8bbae33ee4c7ec1743c19c248d81d34..574a396c8266c3ce9730cc7e939a41d023d195f3 100644 (file)
@@ -1,7 +1,7 @@
-From 753efe27a9afee52c4ad42098a9b9278366d63cc Mon Sep 17 00:00:00 2001
+From 4250ff1640ea1ede99bfe02ca949acbcc6c0927f Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 25 Mar 2026 17:54:52 +0000
-Subject: [PATCH 03/26] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark()
+Date: Wed, 1 Apr 2026 14:34:52 +0100
+Subject: [PATCH 3/4] 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
@@ -11,6 +11,8 @@ Link-local frames are directly trapped to the CPU port only, so don't
 set dsa_default_offload_fwd_mark() on those.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/e1161c90894ddc519c57dc0224b3a0f6bfa1d2d6.1775049897.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
  net/dsa/tag_mxl862xx.c | 3 +++
  1 file changed, 3 insertions(+)
similarity index 87%
rename from target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-implement-bridge-offloading.patch
rename to target/linux/generic/backport-6.12/775-v7.1-net-dsa-mxl862xx-implement-bridge-offloading.patch
index a545e7067724eb1d03b9d947abebce5b666ba781..9b41f35c1ab6a8339b690e562c6cb4f52090db5f 100644 (file)
@@ -1,7 +1,7 @@
-From ce0664ff8f75c3ab01101c3f0f8569924d948775 Mon Sep 17 00:00:00 2001
+From 340bdf984613c4a9241d678915e513824f5a9b19 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 25 Mar 2026 17:55:08 +0000
-Subject: [PATCH 04/26] net: dsa: mxl862xx: implement bridge offloading
+Date: Wed, 1 Apr 2026 14:35:01 +0100
+Subject: [PATCH 4/4] 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
@@ -24,9 +24,17 @@ dsa_switch_for_each_bridge_member().
 
 As there are now more users of the BRIDGEPORT_CONFIG_SET API and the
 state of each port is cached locally, introduce a helper function
-mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which is
-then used to replace the direct calls to the API in
-mxl862xx_setup_cpu_bridge() and mxl862xx_add_single_port_bridge().
+mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which
+applies the cached per-port state to hardware. For standalone user
+ports (dp->bridge == NULL), it additionally resets the port to
+single-port bridge state: CPU-only portmap, learning and flooding
+disabled. The CPU port path sets its state explicitly before calling
+this helper and is therefore not affected by the reset.
+
+Note that MASK_VLAN_BASED_MAC_LEARNING is intentionally absent from
+the firmware write mask. After mxl862xx_reset(), the firmware
+initialises all VLAN-based MAC learning fields to 0 (disabled), so
+SVL is the active mode by default without having to set it explicitly.
 
 Note that there is no convenient way to control flooding on per-port
 level, so the driver is using a 0-rate QoS meter setup as a stopper in
@@ -36,12 +44,14 @@ registers -- without that at least one 64-byte packet could still
 pass before the meter would change from 'yellow' into 'red' state.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/dd079180e2098e5f9626fcd149b9bad9a1b5a1b2.1775049897.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 +++++++-
  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |  20 +-
- drivers/net/dsa/mxl862xx/mxl862xx.c     | 743 ++++++++++++++++++++++--
- drivers/net/dsa/mxl862xx/mxl862xx.h     | 133 +++++
- 4 files changed, 1076 insertions(+), 45 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 736 ++++++++++++++++++++++--
+ drivers/net/dsa/mxl862xx/mxl862xx.h     |  99 ++++
+ 4 files changed, 1022 insertions(+), 58 deletions(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -384,7 +394,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
                                                       int port,
                                                       enum dsa_tag_protocol m)
-@@ -168,6 +182,213 @@ static int mxl862xx_setup_mdio(struct ds
+@@ -168,6 +182,199 @@ static int mxl862xx_setup_mdio(struct ds
        return ret;
  }
  
@@ -479,11 +489,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      struct dsa_port *dp = dsa_to_port(ds, port);
 +      struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_port *p = &priv->ports[port];
-+      u16 bridge_id = dp->bridge ?
-+              priv->bridges[dp->bridge->num] : p->fid;
++      struct dsa_port *member_dp;
++      u16 bridge_id;
 +      bool enable;
 +      int i, idx;
 +
++      if (!p->setup_done)
++              return 0;
++
++      if (dsa_port_is_cpu(dp)) {
++              dsa_switch_for_each_user_port(member_dp, ds) {
++                      if (member_dp->cpu_dp->index != port)
++                              continue;
++                      mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                                  member_dp->index);
++              }
++      } else if (dp->bridge) {
++              dsa_switch_for_each_bridge_member(member_dp, ds,
++                                                dp->bridge->dev) {
++                      if (member_dp->index == port)
++                              continue;
++                      mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                                  member_dp->index);
++              }
++              mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                          dp->cpu_dp->index);
++      } else {
++              mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                          dp->cpu_dp->index);
++              p->flood_block = 0;
++              p->learning = false;
++      }
++
++      bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
++
 +      br_port_cfg.bridge_port_id = cpu_to_le16(port);
 +      br_port_cfg.bridge_id = cpu_to_le16(bridge_id);
 +      br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
@@ -492,8 +531,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                                     MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER);
 +      br_port_cfg.src_mac_learning_disable = !p->learning;
 +
-+      mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
-+
 +      for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
 +              idx = mxl862xx_flood_meters[i];
 +              enable = !!(p->flood_block & BIT(idx));
@@ -510,24 +547,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
 +                                      const struct dsa_bridge *bridge)
 +{
-+      struct mxl862xx_priv *priv = ds->priv;
-+      struct dsa_port *dp, *member_dp;
-+      int port, err, ret = 0;
++      struct dsa_port *dp;
++      int ret = 0, err;
 +
 +      dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
-+              port = dp->index;
-+
-+              bitmap_zero(priv->ports[port].portmap,
-+                          MXL862XX_MAX_BRIDGE_PORTS);
-+
-+              dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) {
-+                      if (member_dp->index != port)
-+                              __set_bit(member_dp->index,
-+                                        priv->ports[port].portmap);
-+              }
-+              __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
-+
-+              err = mxl862xx_set_bridge_port(ds, port);
++              err = mxl862xx_set_bridge_port(ds, dp->index);
 +              if (err)
 +                      ret = err;
 +      }
@@ -535,7 +559,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return ret;
 +}
 +
-+static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
++static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
 +{
 +      struct mxl862xx_bridge_alloc br_alloc = {};
 +      int ret;
@@ -544,8 +568,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (ret)
 +              return ret;
 +
-+      *bridge_id = le16_to_cpu(br_alloc.bridge_id);
-+      return 0;
++      return le16_to_cpu(br_alloc.bridge_id);
 +}
 +
 +static void mxl862xx_free_bridge(struct dsa_switch *ds,
@@ -567,38 +590,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +
 +      priv->bridges[bridge->num] = 0;
 +}
-+
-+static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
-+{
-+      struct dsa_port *dp = dsa_to_port(ds, port);
-+      struct mxl862xx_priv *priv = ds->priv;
-+      int ret;
-+
-+      ret = mxl862xx_allocate_bridge(priv, &priv->ports[port].fid);
-+      if (ret) {
-+              dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
-+              return ret;
-+      }
-+
-+      priv->ports[port].learning = false;
-+      bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
-+      __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
-+
-+      ret = mxl862xx_set_bridge_port(ds, port);
-+      if (ret)
-+              return ret;
-+
-+      /* Standalone ports should not flood unknown unicast or multicast
-+       * towards the CPU by default; only broadcast is needed initially.
-+       */
-+      return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
-+                                       false, false, true);
-+}
 +
  static int mxl862xx_setup(struct dsa_switch *ds)
  {
        struct mxl862xx_priv *priv = ds->priv;
-@@ -181,6 +402,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -181,6 +388,10 @@ static int mxl862xx_setup(struct dsa_swi
        if (ret)
                return ret;
  
@@ -609,66 +605,49 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return mxl862xx_setup_mdio(ds);
  }
  
-@@ -260,66 +485,87 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -260,99 +471,137 @@ static int mxl862xx_configure_sp_tag_pro
  
  static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
  {
 -      struct mxl862xx_bridge_port_config br_port_cfg = {};
        struct mxl862xx_priv *priv = ds->priv;
 -      u16 bridge_port_map = 0;
-       struct dsa_port *dp;
+-      struct dsa_port *dp;
  
 -      /* CPU port bridge setup */
 -      br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
 -                                     MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
 -                                     MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
--
++      priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
++      priv->ports[port].learning = true;
 -      br_port_cfg.bridge_port_id = cpu_to_le16(port);
 -      br_port_cfg.src_mac_learning_disable = false;
 -      br_port_cfg.vlan_src_mac_vid_enable = true;
 -      br_port_cfg.vlan_dst_mac_vid_enable = true;
-+      priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
-+      priv->ports[port].learning = true;
-       /* include all assigned user ports in the CPU portmap */
-+      bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
-       dsa_switch_for_each_user_port(dp, ds) {
-               /* it's safe to rely on cpu_dp being valid for user ports */
-               if (dp->cpu_dp->index != port)
-                       continue;
--              bridge_port_map |= BIT(dp->index);
-+              __set_bit(dp->index, priv->ports[port].portmap);
-       }
--      br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
--      return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
 +      return mxl862xx_set_bridge_port(ds, port);
- }
++}
  
--static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
+-      /* include all assigned user ports in the CPU portmap */
+-      dsa_switch_for_each_user_port(dp, ds) {
+-              /* it's safe to rely on cpu_dp being valid for user ports */
+-              if (dp->cpu_dp->index != port)
+-                      continue;
 +static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
 +                                   const struct dsa_bridge bridge,
 +                                   bool *tx_fwd_offload,
 +                                   struct netlink_ext_ack *extack)
- {
--      struct mxl862xx_bridge_port_config br_port_cfg = {};
--      struct dsa_port *dp = dsa_to_port(ds, port);
--      struct mxl862xx_bridge_alloc br_alloc = {};
++{
 +      struct mxl862xx_priv *priv = ds->priv;
-+      u16 fw_id;
-       int ret;
++      int ret;
  
--      ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
--      if (ret) {
--              dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
--              return ret;
+-              bridge_port_map |= BIT(dp->index);
 +      if (!priv->bridges[bridge.num]) {
-+              ret = mxl862xx_allocate_bridge(priv, &fw_id);
-+              if (ret)
++              ret = mxl862xx_allocate_bridge(priv);
++              if (ret < 0)
 +                      return ret;
 +
-+              priv->bridges[bridge.num] = fw_id;
++              priv->bridges[bridge.num] = ret;
 +
 +              /* Free bridge here on error, DSA rollback won't. */
 +              ret = mxl862xx_sync_bridge_members(ds, &bridge);
@@ -679,6 +658,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +
 +              return 0;
        }
+-      br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map);
+-      return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
++      return mxl862xx_sync_bridge_members(ds, &bridge);
+ }
+-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
++static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
++                                     const struct dsa_bridge bridge)
+ {
+-      struct mxl862xx_bridge_port_config br_port_cfg = {};
+-      struct dsa_port *dp = dsa_to_port(ds, port);
+-      struct mxl862xx_bridge_alloc br_alloc = {};
+-      int ret;
++      int err;
+-      ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc);
+-      if (ret) {
+-              dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
+-              return ret;
+-      }
++      err = mxl862xx_sync_bridge_members(ds, &bridge);
++      if (err)
++              dev_err(ds->dev,
++                      "failed to sync bridge members after port %d left: %pe\n",
++                      port, ERR_PTR(err));
  
 -      br_port_cfg.bridge_id = br_alloc.bridge_id;
 -      br_port_cfg.bridge_port_id = cpu_to_le16(port);
@@ -691,30 +696,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 -      br_port_cfg.vlan_dst_mac_vid_enable = false;
 -      /* As this function is only called for user ports it is safe to rely on
 -       * cpu_dp being valid
-+      return mxl862xx_sync_bridge_members(ds, &bridge);
-+}
-+
-+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
-+                                     const struct dsa_bridge bridge)
-+{
-+      struct dsa_port *dp = dsa_to_port(ds, port);
-+      struct mxl862xx_priv *priv = ds->priv;
-+      struct mxl862xx_port *p = &priv->ports[port];
-+      int err;
-+
-+      err = mxl862xx_sync_bridge_members(ds, &bridge);
-+      if (err)
-+              dev_err(ds->dev,
-+                      "failed to sync bridge members after port %d left: %pe\n",
-+                      port, ERR_PTR(err));
-+
 +      /* Revert leaving port, omitted by the sync above, to its
 +       * single-port bridge
         */
 -      br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index));
-+      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,
@@ -732,12 +717,54 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        struct dsa_port *dp = dsa_to_port(ds, port);
        bool is_cpu_port = dsa_port_is_cpu(dp);
        int ret;
-@@ -352,7 +598,31 @@ static int mxl862xx_port_setup(struct ds
+-      /* disable port and flush MAC entries */
+       ret = mxl862xx_port_state(ds, port, false);
+       if (ret)
+               return ret;
+       mxl862xx_port_fast_age(ds, port);
+-      /* skip setup for unused and DSA ports */
+       if (dsa_port_is_unused(dp) ||
+           dsa_port_is_dsa(dp))
+               return 0;
+-      /* configure tag protocol */
+       ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
+       if (ret)
+               return ret;
+-      /* assign CTP port IDs */
+       ret = mxl862xx_configure_ctp_port(ds, port, port,
+                                         is_cpu_port ? 32 - port : 1);
+       if (ret)
+               return ret;
+       if (is_cpu_port)
+-              /* assign user ports to CPU port bridge */
                return mxl862xx_setup_cpu_bridge(ds, port);
  
-       /* setup single-port bridge for user ports */
+-      /* setup single-port bridge for user ports */
 -      return mxl862xx_add_single_port_bridge(ds, port);
-+      ret = mxl862xx_add_single_port_bridge(ds, port);
++      /* setup single-port bridge for user ports.
++       * If this fails, the FID is leaked -- but the port then transitions
++       * to unused, and the FID pool is sized to tolerate this.
++       */
++      ret = mxl862xx_allocate_bridge(priv);
++      if (ret < 0) {
++              dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
++              return ret;
++      }
++      priv->ports[port].fid = ret;
++      /* Standalone ports should not flood unknown unicast or multicast
++       * towards the CPU by default; only broadcast is needed initially.
++       */
++      ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
++                                       false, false, true);
++      if (ret)
++              return ret;
++      ret = mxl862xx_set_bridge_port(ds, port);
 +      if (ret)
 +              return ret;
 +
@@ -765,7 +792,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
-@@ -365,14 +635,383 @@ static void mxl862xx_phylink_get_caps(st
+@@ -365,14 +614,371 @@ static void mxl862xx_phylink_get_caps(st
                  config->supported_interfaces);
  }
  
@@ -876,7 +903,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (fid < 0)
 +              return fid;
 +
-+      /* Look up existing entry by {MAC, FID, TCI} */
 +      ether_addr_copy(qparam.mac, mdb->addr);
 +      qparam.fid = cpu_to_le16(fid);
 +      qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
@@ -892,7 +918,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      aparam.static_entry = true;
 +      aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG);
 +
-+      /* Merge with existing portmap if entry already exists */
 +      if (qparam.found)
 +              memcpy(aparam.port_map, qparam.port_map,
 +                     sizeof(aparam.port_map));
@@ -915,7 +940,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (fid < 0)
 +              return fid;
 +
-+      /* Look up existing entry */
 +      qparam.fid = cpu_to_le16(fid);
 +      qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
 +      ether_addr_copy(qparam.mac, mdb->addr);
@@ -930,7 +954,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      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 */
 +              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);
@@ -1007,8 +1030,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +       * LEARNING or FORWARDING state (per 802.1D defaults).
 +       * Re-apply the driver's intended learning and metering config so that
 +       * standalone ports keep learning disabled.
-+       * This is likely to get fixed in future firmware releases, however,
-+       * the additional API call even then doesn't hurt much.
 +       */
 +      ret = mxl862xx_set_bridge_port(ds, port);
 +      if (ret)
@@ -1031,8 +1052,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                                             host_flood_work);
 +      struct mxl862xx_priv *priv = p->priv;
 +      struct dsa_switch *ds = priv->ds;
-+      int port = p - priv->ports;
-+      bool uc, mc;
 +
 +      rtnl_lock();
 +
@@ -1042,17 +1061,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +              return;
 +      }
 +
-+      uc = p->host_flood_uc;
-+      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
-+       * 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.
++      /* Always write to the standalone FID. When standalone it takes effect
++       * immediately; when bridged the port uses the shared bridge FID so the
++       * write is a no-op for current forwarding, but the state is preserved
++       * in hardware and is ready once the port returns to standalone.
 +       */
-+      if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
-+              mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
++      mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc,
++                                 p->host_flood_mc, true);
 +
 +      rtnl_unlock();
 +}
@@ -1115,7 +1130,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (flags.mask & BR_LEARNING)
 +              priv->ports[port].learning = !!(flags.val & BR_LEARNING);
 +
-+      if ((block != old_block) || (flags.mask & BR_LEARNING)) {
++      if (block != old_block || (flags.mask & BR_LEARNING)) {
 +              priv->ports[port].flood_block = block;
 +              ret = mxl862xx_set_bridge_port(ds, port);
 +              if (ret)
@@ -1149,7 +1164,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  };
  
  static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -407,7 +1046,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -407,7 +1013,7 @@ static int mxl862xx_probe(struct mdio_de
        struct device *dev = &mdiodev->dev;
        struct mxl862xx_priv *priv;
        struct dsa_switch *ds;
@@ -1158,7 +1173,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
-@@ -425,14 +1064,25 @@ static int mxl862xx_probe(struct mdio_de
+@@ -425,14 +1031,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;
@@ -1186,7 +1201,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return err;
  }
  
-@@ -440,6 +1090,7 @@ static void mxl862xx_remove(struct mdio_
+@@ -440,6 +1057,7 @@ static void mxl862xx_remove(struct mdio_
  {
        struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
        struct mxl862xx_priv *priv;
@@ -1194,7 +1209,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        if (!ds)
                return;
-@@ -449,12 +1100,21 @@ static void mxl862xx_remove(struct mdio_
+@@ -449,12 +1067,21 @@ static void mxl862xx_remove(struct mdio_
        dsa_unregister_switch(ds);
  
        mxl862xx_host_shutdown(priv);
@@ -1216,7 +1231,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        if (!ds)
                return;
-@@ -465,6 +1125,9 @@ static void mxl862xx_shutdown(struct mdi
+@@ -465,6 +1092,9 @@ static void mxl862xx_shutdown(struct mdi
  
        mxl862xx_host_shutdown(priv);
  
@@ -1228,7 +1243,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -4,15 +4,148 @@
+@@ -4,15 +4,114 @@
  #define __MXL862XX_H
  
  #include <linux/mdio.h>
@@ -1246,37 +1261,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +#define MXL862XX_FW_PORTMAP_WORDS     (MXL862XX_MAX_BRIDGE_PORTS / 16)
 +
 +/**
-+ * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware
-+ *                                   portmap (__le16[8])
-+ * @dst: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
-+ * @src: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
-+ */
-+static inline void
-+mxl862xx_fw_portmap_from_bitmap(__le16 *dst, const unsigned long *src)
-+{
-+      int i;
-+
-+      for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
-+              dst[i] = cpu_to_le16(bitmap_read(src, i * 16, 16));
-+}
-+
-+/**
-+ * mxl862xx_fw_portmap_to_bitmap - convert a firmware portmap (__le16[8]) to
-+ *                                 a kernel bitmap
-+ * @dst: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits
-+ * @src: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
-+ */
-+static inline void
-+mxl862xx_fw_portmap_to_bitmap(unsigned long *dst, const __le16 *src)
-+{
-+      int i;
-+
-+      bitmap_zero(dst, MXL862XX_MAX_BRIDGE_PORTS);
-+      for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++)
-+              bitmap_write(dst, le16_to_cpu(src[i]), i * 16, 16);
-+}
-+
-+/**
 + * mxl862xx_fw_portmap_set_bit - set a single port bit in a firmware portmap
 + * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries)
 + * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1)
@@ -1320,8 +1304,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * @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
 + *                       rate-limiting flood traffic on this port (zero-rate
 + *                       meters used to block flooding)
@@ -1343,7 +1325,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +struct mxl862xx_port {
 +      struct mxl862xx_priv *priv;
 +      u16 fid;
-+      DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
 +      unsigned long flood_block;
 +      bool learning;
 +      bool setup_done;
diff --git a/target/linux/generic/backport-6.12/776-v7.1-net-dsa-mxl862xx-reject-DSA_PORT_TYPE_DSA.patch b/target/linux/generic/backport-6.12/776-v7.1-net-dsa-mxl862xx-reject-DSA_PORT_TYPE_DSA.patch
new file mode 100644 (file)
index 0000000..0550b57
--- /dev/null
@@ -0,0 +1,50 @@
+From 3a4056ec7ec8f71ae9722f86d3cfbc4589deeac4 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 7 Apr 2026 18:30:27 +0100
+Subject: [PATCH 1/2] net: dsa: mxl862xx: reject DSA_PORT_TYPE_DSA
+
+DSA links aren't supported by the mxl862xx driver.
+
+Instead of returning early from .port_setup when called for
+DSA_PORT_TYPE_DSA ports rather return -EOPNOTSUPP and show an error
+message.
+
+The desired side-effect is that the framework will switch the port to
+DSA_PORT_TYPE_UNUSED, so we can stop caring about DSA_PORT_TYPE_DSA in
+all other places.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/b686f3a22d8a6e7d470e7aa98da811a996a229b9.1775581804.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
+@@ -544,10 +544,14 @@ static int mxl862xx_port_setup(struct ds
+       mxl862xx_port_fast_age(ds, port);
+-      if (dsa_port_is_unused(dp) ||
+-          dsa_port_is_dsa(dp))
++      if (dsa_port_is_unused(dp))
+               return 0;
++      if (dsa_port_is_dsa(dp)) {
++              dev_err(ds->dev, "port %d: DSA links not supported\n", port);
++              return -EOPNOTSUPP;
++      }
++
+       ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
+       if (ret)
+               return ret;
+@@ -591,7 +595,7 @@ static void mxl862xx_port_teardown(struc
+       struct mxl862xx_priv *priv = ds->priv;
+       struct dsa_port *dp = dsa_to_port(ds, port);
+-      if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp))
++      if (dsa_port_is_unused(dp))
+               return;
+       /* Prevent deferred host_flood_work from acting on stale state.
diff --git a/target/linux/generic/backport-6.12/777-v7.1-net-dsa-mxl862xx-don-t-skip-early-bridge-port-config.patch b/target/linux/generic/backport-6.12/777-v7.1-net-dsa-mxl862xx-don-t-skip-early-bridge-port-config.patch
new file mode 100644 (file)
index 0000000..5020526
--- /dev/null
@@ -0,0 +1,38 @@
+From 71934b9e6f36b1786bd969c0e1d2de8f9bd65f0f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 7 Apr 2026 18:30:35 +0100
+Subject: [PATCH 2/2] net: dsa: mxl862xx: don't skip early bridge port
+ configuration
+
+mxl862xx_bridge_port_set() is currently guarded by the
+mxl8622_port->setup_done flag, as the early call to
+mxl862xx_bridge_port_set() from mxl862xx_port_stp_state_set() would
+otherwise cause a NULL-pointer dereference on unused ports which don't
+have dp->cpu_dp despite not being a CPU port.
+
+Using the setup_done flag (which is never set for unused ports),
+however, also prevents mxl862xx_bridge_port_set() from configuring
+user ports' single-port bridges early, which was unintended.
+
+Fix this by returning early from mxl862xx_bridge_port_set() in case
+dsa_port_is_unused().
+
+Fixes: 340bdf984613c ("net: dsa: mxl862xx: implement bridge offloading")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/15962aac29ebe0a6eb77565451acff880c41ef33.1775581804.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
+@@ -278,7 +278,7 @@ static int mxl862xx_set_bridge_port(stru
+       bool enable;
+       int i, idx;
+-      if (!p->setup_done)
++      if (dsa_port_is_unused(dp))
+               return 0;
+       if (dsa_port_is_cpu(dp)) {
similarity index 82%
rename from target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch
rename to target/linux/generic/backport-6.12/778-v7.1-net-dsa-mxl862xx-implement-VLAN-functionality.patch
index c9e1769d98e396d38105cb80ba23bb1a6b2b0ee6..5708dec4148467f9744ad0a2c44d64474768031a 100644 (file)
@@ -1,7 +1,7 @@
-From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
+From d587f9b6dcc98c1e8aeb5c189a7bfac60d6d29ac Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 10 Mar 2026 02:36:00 +0000
-Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
+Date: Tue, 7 Apr 2026 18:31:01 +0100
+Subject: [PATCH] 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
@@ -16,8 +16,8 @@ egress rules at all, so they consume only a VF entry.
 Both engines draw from shared 1024-entry hardware pools. The VF pool
 is divided equally among user ports for VID membership, while the
 EVLAN pool is partitioned into small fixed-size ingress blocks (7
-entries of catchall rules per port) and variable-size egress blocks
-for tag stripping.
+entries of catchall rules per port) and fixed-size egress blocks for
+tag stripping.
 
 With 5 user ports this yields up to 204 VIDs per port (limited by VF),
 of which up to 98 can be untagged (limited by EVLAN egress budget).
@@ -34,12 +34,14 @@ rather than worst-case pre-allocation, or by sharing EVLAN egress and
 VLAN Filter blocks across ports with identical VID sets.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/9be29637675342b109a85fa08f5378800d9f7b78.1775581804.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.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     | 915 +++++++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx.h     | 10++-
- 4 files changed, 1344 insertions(+), 16 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 781 +++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.h     | 103 +++-
+ 4 files changed, 1211 insertions(+), 14 deletions(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -101,14 +103,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +/**
 + * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
-+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag
-+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag
++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY: Copy from inner tag
++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY: Copy from outer tag
 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
 + */
 +enum mxl862xx_extended_vlan_treatment_priority {
 +      MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
-+      MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
-+      MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
++      MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY = 1,
++      MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY = 2,
 +      MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
 +};
 +
@@ -409,16 +411,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define MXL862XX_STP_PORTCFGSET               (MXL862XX_STP_MAGIC + 0x2)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[]
+@@ -50,6 +50,85 @@ static const int mxl862xx_flood_meters[]
        MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
  };
  
 +enum mxl862xx_evlan_action {
 +      EVLAN_ACCEPT,                   /* pass-through, no tag removal */
 +      EVLAN_STRIP_IF_UNTAGGED,        /* remove 1 tag if entry's untagged flag set */
-+      EVLAN_DISCARD,                  /* discard upstream */
 +      EVLAN_PVID_OR_DISCARD,          /* insert PVID tag or discard if no PVID */
-+      EVLAN_PVID_OR_PASS,             /* insert PVID tag or pass-through */
 +      EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
 +};
 +
@@ -457,7 +457,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * regardless of TPID, so without the ACCEPT guard, it would also
 + * catch standard 802.1Q VID>0 frames and corrupt them. With the
 + * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
-+ * pass through untouched; only non-8021Q TPID frames fall through
++ * pass through untouched; only non-8021Q TPID frames pass through
 + * to the NO_FILTER catchalls.
 + */
 +static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
@@ -486,27 +486,26 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +};
 +
 +/*
-+ * VID-specific accept rules for VLAN-unaware egress.
-+ * The HW sees the MxL tag as outer, real VLAN tag as inner.
-+ * match on inner VID with outer=NO_FILTER.
++ * Egress tag-stripping rules for VLAN-unaware mode (2 per untagged VID).
++ * The HW sees the MxL tag as outer; the real VLAN tag, if any, is inner.
 + */
 +static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
-+      { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
-+      { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE,  true, EVLAN_STRIP_IF_UNTAGGED },
++      { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true,  EVLAN_STRIP_IF_UNTAGGED },
++      { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE,  false, EVLAN_STRIP_IF_UNTAGGED },
 +};
 +
  static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
                                                       int port,
                                                       enum dsa_tag_protocol m)
-@@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru
+@@ -275,6 +354,7 @@ static int mxl862xx_set_bridge_port(stru
        struct mxl862xx_port *p = &priv->ports[port];
-       u16 bridge_id = dp->bridge ?
-               priv->bridges[dp->bridge->num] : p->fid;
+       struct dsa_port *member_dp;
+       u16 bridge_id;
 +      u16 vf_scan;
        bool enable;
        int i, idx;
  
-@@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru
+@@ -312,9 +392,69 @@ static int mxl862xx_set_bridge_port(stru
        br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
                                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
                                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
@@ -574,23 +573,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
 +      br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
 +
-       mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
        for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
-@@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
+               idx = mxl862xx_flood_meters[i];
+               enable = !!(p->flood_block & BIT(idx));
+@@ -343,6 +483,72 @@ static int mxl862xx_sync_bridge_members(
        return ret;
  }
  
-+static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk,
-+                                    u16 size)
-+{
-+      blk->allocated = false;
-+      blk->in_use = false;
-+      blk->block_id = 0;
-+      blk->block_size = size;
-+      blk->n_active = 0;
-+}
-+
 +static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
 +                                    struct mxl862xx_evlan_block *blk)
 +{
@@ -609,15 +598,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return 0;
 +}
 +
-+static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
-+{
-+      vf->allocated = false;
-+      vf->block_id = 0;
-+      vf->block_size = size;
-+      vf->active_count = 0;
-+      INIT_LIST_HEAD(&vf->vids);
-+}
-+
 +static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
 +                                 u16 size, u16 *block_id)
 +{
@@ -666,10 +646,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
 +}
 +
- static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
+ static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
  {
        struct mxl862xx_bridge_alloc br_alloc = {};
-@@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
+@@ -378,6 +584,9 @@ static void mxl862xx_free_bridge(struct
  static int mxl862xx_setup(struct dsa_switch *ds)
  {
        struct mxl862xx_priv *priv = ds->priv;
@@ -679,7 +659,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        int ret;
  
        ret = mxl862xx_reset(priv);
-@@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -388,6 +597,50 @@ static int mxl862xx_setup(struct dsa_swi
        if (ret)
                return ret;
  
@@ -695,7 +675,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +       *            through without EVLAN processing.
 +       *
 +       * Total EVLAN budget:
-+       *   n_user_ports * (ingress + egress) â‰¤ 1024.
++       *   n_user_ports * (ingress + egress) <= 1024.
 +       * Ingress blocks are small (7 entries), so almost all capacity
 +       * goes to egress VID rules.
 +       */
@@ -730,23 +710,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        ret = mxl862xx_setup_drop_meter(ds);
        if (ret)
                return ret;
-@@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -469,12 +722,509 @@ static int mxl862xx_configure_sp_tag_pro
        return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
  }
  
-+/**
-+ * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
-+ * @priv: driver private data
-+ * @block_id: HW Extended VLAN block ID
-+ * @entry_index: entry index within the block
-+ * @desc: rule descriptor (filter type + action)
-+ * @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.
-+ */
 +static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
 +                                   u16 block_id, u16 entry_index,
 +                                   const struct mxl862xx_evlan_rule_desc *desc,
@@ -788,11 +755,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                      MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
 +              break;
 +
-+      case EVLAN_DISCARD:
-+              cfg.treatment.remove_tag =
-+                      cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
-+              break;
-+
 +      case EVLAN_PVID_OR_DISCARD:
 +              if (pvid) {
 +                      cfg.treatment.remove_tag =
@@ -809,22 +771,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +              }
 +              break;
 +
-+      case EVLAN_PVID_OR_PASS:
-+              if (pvid) {
-+                      cfg.treatment.remove_tag =
-+                              cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
-+                      cfg.treatment.add_outer_vlan = 1;
-+                      cfg.treatment.outer_vlan.vid_mode =
-+                              cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
-+                      cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
-+                      cfg.treatment.outer_vlan.tpid =
-+                              cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
-+              } else {
-+                      cfg.treatment.remove_tag =
-+                              cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
-+              }
-+              break;
-+
 +      case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
 +              if (pvid) {
 +                      cfg.treatment.remove_tag =
@@ -845,15 +791,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
 +}
 +
-+/**
-+ * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op
-+ * @priv: driver private data
-+ * @block_id: HW Extended VLAN block ID
-+ * @entry_index: entry index within the block
-+ *
-+ * Writes a zeroed-out config to the firmware, which deactivates the
-+ * rule (making it transparent / no-op).
-+ */
 +static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
 +                                         u16 block_id, u16 entry_index)
 +{
@@ -876,19 +813,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
 +}
 +
-+/**
-+ * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block
-+ * @priv: driver private data
-+ * @blk: Extended VLAN block (already allocated)
-+ * @rules: array of rule descriptors for the final rules
-+ * @n_rules: number of final rules
-+ * @pvid: port VLAN ID (for PVID insertion rules)
-+ *
-+ * Writes final catchall rules starting at block_size - n_rules. With
-+ * VLAN Filter handling VID membership, only the ingress block uses
-+ * finals, and the block is sized to exactly fit them (no VID entries),
-+ * so the rules fill the entire block.
-+ */
 +static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
 +                                          struct mxl862xx_evlan_block *blk,
 +                                          const struct mxl862xx_evlan_rule_desc *rules,
@@ -908,15 +832,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return 0;
 +}
 +
-+/**
-+ * mxl862xx_vf_entry_set - Write a single VLAN Filter entry
-+ * @priv: driver private data
-+ * @block_id: HW VLAN Filter block ID
-+ * @index: entry index within the block
-+ * @vid: VLAN ID to allow
-+ *
-+ * Writes an ALLOW entry (discard_matched=false) for the given VID.
-+ */
 +static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
 +                               u16 block_id, u16 index, u16 vid)
 +{
@@ -931,13 +846,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
 +}
 +
-+/**
-+ * mxl862xx_vf_find_vid - Find a VID entry in a VF block
-+ * @vf: VLAN Filter block to search
-+ * @vid: VLAN ID to find
-+ */
-+static struct mxl862xx_vf_vid *
-+mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid)
++static struct mxl862xx_vf_vid *mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf,
++                                                  u16 vid)
 +{
 +      struct mxl862xx_vf_vid *ve;
 +
@@ -948,17 +858,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return NULL;
 +}
 +
-+/**
-+ * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block
-+ * @priv: driver private data
-+ * @vf: VLAN Filter block
-+ * @vid: VLAN ID to add
-+ * @untagged: whether this VID should strip tags on egress
-+ *
-+ * Idempotent. Writes an ALLOW entry at active_count and increments
-+ * active_count. If the VID already exists, only the untagged flag
-+ * is updated. The HW block must be allocated before calling this.
-+ */
 +static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
 +                             struct mxl862xx_vf_block *vf,
 +                             u16 vid, bool untagged)
@@ -995,17 +894,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return 0;
 +}
 +
-+/**
-+ * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block
-+ * @priv: driver private data
-+ * @vf: VLAN Filter block
-+ * @vid: VLAN ID to remove
-+ *
-+ * Swap-compacts: the last active entry is moved into the gap,
-+ * active_count is decremented, and the old last slot is plugged
-+ * with DISCARD. When active_count drops to 0, a DISCARD sentinel
-+ * is restored at index 0.
-+ */
 +static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
 +                             struct mxl862xx_vf_block *vf, u16 vid)
 +{
@@ -1035,12 +923,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                      return ret;
 +      } else if (gap < last) {
 +              /* Swap: move the last ALLOW entry into the gap */
-+              last_ve = NULL;
 +              list_for_each_entry(last_ve, &vf->vids, list)
 +                      if (last_ve->index == last)
 +                              break;
 +
-+              if (WARN_ON(!last_ve || last_ve->index != last))
++              if (WARN_ON(list_entry_is_head(last_ve, &vf->vids, list)))
 +                      return -EINVAL;
 +
 +              ret = mxl862xx_vf_entry_set(priv, vf->block_id,
@@ -1049,16 +936,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                      return ret;
 +
 +              last_ve->index = gap;
-+
-+              /* Plug the old last slot with DISCARD */
-+              ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
-+              if (ret)
-+                      return ret;
-+      } else {
-+              /* Deleting the last entry -- just plug it */
-+              ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
-+              if (ret)
-+                      return ret;
 +      }
 +
 +      list_del(&ve->list);
@@ -1068,20 +945,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return 0;
 +}
 +
-+/**
-+ * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
-+ * @priv: driver private data
-+ * @port: port number
-+ *
-+ * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
-+ * untagged/priority-tagged frames, passes through standard 802.1Q
-+ * tagged frames for VF membership checking, and treats non-8021Q TPID
-+ * frames as untagged. The block is sized to exactly fit the 7 catchall
-+ * rules and is rewritten whenever PVID changes.
-+ *
-+ * In VLAN-unaware mode the firmware passes frames through unchanged when
-+ * no ingress block is assigned, so nothing is programmed.
-+ */
 +static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
 +{
 +      struct mxl862xx_port *p = &priv->ports[port];
@@ -1099,19 +962,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                                              p->pvid);
 +}
 +
-+/**
-+ * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules
-+ * @priv: driver private data
-+ * @port: port number
-+ *
-+ * Walks the port's VF VID list and writes 2 EVLAN rules per VID that
-+ * needs egress tag stripping. In VLAN-aware mode only untagged VIDs
-+ * need rules (tagged VIDs pass through EVLAN untouched). In unaware
-+ * mode every VID gets rules.
-+ *
-+ * Entries are packed starting at index 0, and the scan window
-+ * (n_active) is narrowed so stale entries beyond it are never matched.
-+ */
 +static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
 +{
 +      struct mxl862xx_port *p = &priv->ports[port];
@@ -1131,10 +981,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      }
 +
 +      list_for_each_entry(vfv, &p->vf.vids, list) {
-+              /* In VLAN-aware mode tagged-only VIDs need no EVLAN
-+               * rules -- VLAN Filter handles membership.
-+               */
-+              if (p->vlan_filtering && !vfv->untagged)
++              if (!vfv->untagged)
 +                      continue;
 +
 +              if (idx + n_vid > blk->block_size)
@@ -1182,12 +1029,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +{
 +      struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_port *p = &priv->ports[port];
++      bool old_vlan_filtering = p->vlan_filtering;
++      bool old_in_use = p->ingress_evlan.in_use;
 +      bool changed = (p->vlan_filtering != vlan_filtering);
 +      int ret;
 +
 +      p->vlan_filtering = vlan_filtering;
 +
-+      /* Reprogram Extended VLAN rules if filtering mode changed */
 +      if (changed) {
 +              /* When leaving VLAN-aware mode, release the ingress HW
 +               * block. The firmware passes frames through unchanged
@@ -1199,17 +1047,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +
 +              ret = mxl862xx_evlan_program_ingress(priv, port);
 +              if (ret)
-+                      return ret;
++                      goto err_restore;
 +
 +              ret = mxl862xx_evlan_program_egress(priv, port);
 +              if (ret)
-+                      return ret;
++                      goto err_restore;
 +      }
 +
-+      /* Push VLAN-based MAC learning flags and (possibly newly
-+       * allocated) ingress block to hardware.
-+       */
 +      return mxl862xx_set_bridge_port(ds, port);
++
++      /* No HW rollback -- restoring SW state is sufficient for a correct retry. */
++err_restore:
++      p->vlan_filtering = old_vlan_filtering;
++      p->ingress_evlan.in_use = old_in_use;
++      return ret;
 +}
 +
 +static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
@@ -1255,21 +1106,32 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (pvid_changed) {
 +              ret = mxl862xx_evlan_program_ingress(priv, port);
 +              if (ret)
-+                      goto err_pvid;
++                      goto err_rollback;
 +      }
 +
 +      /* Reprogram egress tag-stripping rules (walks VF VID list) */
 +      ret = mxl862xx_evlan_program_egress(priv, port);
 +      if (ret)
-+              goto err_pvid;
++              goto err_rollback;
 +
 +      /* Apply VLAN block IDs and MAC learning flags to bridge port */
 +      ret = mxl862xx_set_bridge_port(ds, port);
 +      if (ret)
-+              goto err_pvid;
++              goto err_rollback;
 +
 +      return 0;
 +
++err_rollback:
++      /* Best-effort: undo VF add and restore consistent hardware state.
++       * A retry of port_vlan_add will converge since vf_add_vid is
++       * idempotent.
++       */
++      p->pvid = old_pvid;
++      mxl862xx_vf_del_vid(priv, &p->vf, vid);
++      mxl862xx_evlan_program_ingress(priv, port);
++      mxl862xx_evlan_program_egress(priv, port);
++      mxl862xx_set_bridge_port(ds, port);
++      return ret;
 +err_pvid:
 +      p->pvid = old_pvid;
 +      return ret;
@@ -1280,13 +1142,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +{
 +      struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_port *p = &priv->ports[port];
-+      u16 vid = vlan->vid;
++      struct mxl862xx_vf_vid *ve;
 +      bool pvid_changed = false;
++      u16 vid = vlan->vid;
++      bool old_untagged;
++      u16 old_pvid;
 +      int ret;
 +
 +      if (dsa_is_cpu_port(ds, port))
 +              return 0;
 +
++      ve = mxl862xx_vf_find_vid(&p->vf, vid);
++      if (!ve)
++              return 0;
++      old_untagged = ve->untagged;
++      old_pvid = p->pvid;
++
 +      /* Clear PVID if we're deleting it */
 +      if (p->pvid == vid) {
 +              p->pvid = 0;
@@ -1299,28 +1170,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +       */
 +      ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
 +      if (ret)
-+              return ret;
++              goto err_pvid;
 +
 +      /* Reprogram egress tag-stripping rules (VID is now gone) */
 +      ret = mxl862xx_evlan_program_egress(priv, port);
 +      if (ret)
-+              return ret;
++              goto err_rollback;
 +
 +      /* If PVID changed, reprogram ingress finals */
 +      if (pvid_changed) {
 +              ret = mxl862xx_evlan_program_ingress(priv, port);
 +              if (ret)
-+                      return ret;
++                      goto err_rollback;
 +      }
 +
-+      return mxl862xx_set_bridge_port(ds, port);
++      ret = mxl862xx_set_bridge_port(ds, port);
++      if (ret)
++              goto err_rollback;
++
++      return 0;
++
++err_rollback:
++      /* Best-effort: re-add the VID and restore consistent hardware
++       * state. A retry of port_vlan_del will converge.
++       */
++      p->pvid = old_pvid;
++      mxl862xx_vf_add_vid(priv, &p->vf, vid, old_untagged);
++      mxl862xx_evlan_program_egress(priv, port);
++      mxl862xx_evlan_program_ingress(priv, port);
++      mxl862xx_set_bridge_port(ds, port);
++      return ret;
++err_pvid:
++      p->pvid = old_pvid;
++      return ret;
 +}
 +
  static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
  {
        struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_port *p = &priv->ports[port];
-       struct dsa_port *dp;
  
 -      priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
 -      priv->ports[port].learning = true;
@@ -1332,41 +1220,21 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +       * assignment need to be configured.
 +       */
  
-       /* include all assigned user ports in the CPU portmap */
--      bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
-+      bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
-       dsa_switch_for_each_user_port(dp, ds) {
-               /* it's safe to rely on cpu_dp being valid for user ports */
-               if (dp->cpu_dp->index != port)
-                       continue;
--              __set_bit(dp->index, priv->ports[port].portmap);
-+              __set_bit(dp->index, p->portmap);
-       }
        return mxl862xx_set_bridge_port(ds, port);
  }
+@@ -510,6 +1260,8 @@ 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;
  
-+
- static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
-                                    const struct dsa_bridge bridge,
-                                    bool *tx_fwd_offload,
-@@ -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;
-+
-+      /* 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.
-+       *
-+       * 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.
-+       */
+       err = mxl862xx_sync_bridge_members(ds, &bridge);
+@@ -521,6 +1273,10 @@ static void mxl862xx_port_bridge_leave(s
+       /* Revert leaving port, omitted by the sync above, to its
+        * single-port bridge
+        */
 +      p->pvid = 0;
 +      p->ingress_evlan.in_use = false;
 +      p->egress_evlan.in_use = false;
@@ -1374,28 +1242,22 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        err = mxl862xx_set_bridge_port(ds, port);
        if (err)
                dev_err(ds->dev,
-@@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
+@@ -585,6 +1341,22 @@ static int mxl862xx_port_setup(struct ds
        if (ret)
                return ret;
  
-+      /* Initialize and pre-allocate per-port EVLAN and VF blocks for
-+       * user ports. CPU ports do not use EVLAN or VF -- frames pass
-+       * through without processing. Pre-allocation avoids firmware
-+       * EVLAN table fragmentation and simplifies control flow.
-+       */
-+      mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
-+                                priv->evlan_ingress_size);
++      priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
 +      ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
 +      if (ret)
 +              return ret;
 +
-+      mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
-+                                priv->evlan_egress_size);
++      priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size;
 +      ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
 +      if (ret)
 +              return ret;
 +
-+      mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
++      priv->ports[port].vf.block_size = priv->vf_block_size;
++      INIT_LIST_HEAD(&priv->ports[port].vf.vids);
 +      ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
 +      if (ret)
 +              return ret;
@@ -1403,7 +1265,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        priv->ports[port].setup_done = true;
  
        return 0;
-@@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
+@@ -983,6 +1755,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,
@@ -1424,7 +1286,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
  /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
  #define MXL862XX_FW_PORTMAP_WORDS     (MXL862XX_MAX_BRIDGE_PORTS / 16)
-@@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
+@@ -55,6 +57,66 @@ static inline bool mxl862xx_fw_portmap_i
  }
  
  /**
@@ -1491,7 +1353,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * 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
-@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
+@@ -68,6 +130,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
   *                       acting on torn-down state
@@ -1503,11 +1365,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @host_flood_uc:       desired host unicast flood state (true = flood);
   *                       updated atomically by port_set_host_flood, consumed
   *                       by the deferred host_flood_work
-@@ -119,6 +186,12 @@ struct mxl862xx_port {
+@@ -85,6 +152,11 @@ struct mxl862xx_port {
        unsigned long flood_block;
        bool learning;
        bool setup_done;
-+      /* VLAN state */
 +      u16 pvid;
 +      bool vlan_filtering;
 +      struct mxl862xx_vf_block vf;
@@ -1516,7 +1377,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        bool host_flood_uc;
        bool host_flood_mc;
        struct work_struct host_flood_work;
-@@ -126,17 +199,23 @@ struct mxl862xx_port {
+@@ -92,17 +164,23 @@ struct mxl862xx_port {
  
  /**
   * struct mxl862xx_priv - driver private data for an MxL862xx switch
@@ -1551,7 +1412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  struct mxl862xx_priv {
        struct dsa_switch *ds;
-@@ -146,6 +225,9 @@ struct mxl862xx_priv {
+@@ -112,6 +190,9 @@ struct mxl862xx_priv {
        u16 drop_meter;
        struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
        u16 bridges[MXL862XX_MAX_BRIDGES + 1];
similarity index 83%
rename from target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-ethtool-statistics-support.patch
rename to target/linux/generic/backport-6.12/779-v7.1-net-dsa-mxl862xx-add-ethtool-statistics-support.patch
index 9788fb40213e96e5c015ceb23f64b10b380cbe8a..e733713bd3b070bb34fa60aa932b958f838e6da1 100644 (file)
@@ -1,7 +1,7 @@
-From 0067d79d10becfc5779fb50d5c0ac152cc5dc303 Mon Sep 17 00:00:00 2001
+From e6295d124644b14a12b55edf5d3e89cf86a4a2ce Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 22 Mar 2026 00:57:33 +0000
-Subject: [PATCH 06/26] net: dsa: mxl862xx: add ethtool statistics support
+Date: Sun, 12 Apr 2026 01:01:57 +0100
+Subject: [PATCH 1/2] 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
@@ -16,11 +16,13 @@ legacy ethtool -S support. Implement .get_eth_mac_stats,
 IEEE 802.3 statistics interface.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/480be14d5ed51f3db7b1681b298044dbf8e87494.1775951347.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 ++++++++++++++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 +++++++++++++++++++
  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |   3 +
- drivers/net/dsa/mxl862xx/mxl862xx.c     | 168 ++++++++++++++++++++++++
- 3 files changed, 313 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 173 ++++++++++++++++++++++++
+ 3 files changed, 318 insertions(+)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -120,7 +122,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * @tx_good_bytes: Transmitted good byte count (64-bit)
 + */
 +struct mxl862xx_rmon_port_cnt {
-+      enum mxl862xx_port_type port_type;
++      __le32 port_type; /* enum mxl862xx_port_type */
 +      __le16 port_id;
 +      __le16 sub_if_id_group;
 +      u8 pce_bypass;
@@ -192,7 +194,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define MXL862XX_MAC_TABLEENTRYQUERY  (MXL862XX_SWMAC_MAGIC + 0x4)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -30,6 +30,64 @@
+@@ -30,6 +30,38 @@
  #define MXL862XX_API_READ_QUIET(dev, cmd, data) \
        mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true)
  
@@ -209,60 +211,34 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      .offset = offsetof(struct mxl862xx_rmon_port_cnt, _element)     \
 +}
 +
++/* Hardware-specific counters not covered by any standardized stats callback. */
 +static const struct mxl862xx_mib_desc mxl862xx_mib[] = {
-+      MIB_DESC(1, "TxGoodPkts", tx_good_pkts),
-+      MIB_DESC(1, "TxUnicastPkts", tx_unicast_pkts),
-+      MIB_DESC(1, "TxBroadcastPkts", tx_broadcast_pkts),
-+      MIB_DESC(1, "TxMulticastPkts", tx_multicast_pkts),
-+      MIB_DESC(1, "Tx64BytePkts", tx64byte_pkts),
-+      MIB_DESC(1, "Tx127BytePkts", tx127byte_pkts),
-+      MIB_DESC(1, "Tx255BytePkts", tx255byte_pkts),
-+      MIB_DESC(1, "Tx511BytePkts", tx511byte_pkts),
-+      MIB_DESC(1, "Tx1023BytePkts", tx1023byte_pkts),
-+      MIB_DESC(1, "TxMaxBytePkts", tx_max_byte_pkts),
-+      MIB_DESC(1, "TxDroppedPkts", tx_dropped_pkts),
 +      MIB_DESC(1, "TxAcmDroppedPkts", tx_acm_dropped_pkts),
-+      MIB_DESC(2, "TxGoodBytes", tx_good_bytes),
-+      MIB_DESC(1, "TxSingleCollCount", tx_single_coll_count),
-+      MIB_DESC(1, "TxMultCollCount", tx_mult_coll_count),
-+      MIB_DESC(1, "TxLateCollCount", tx_late_coll_count),
-+      MIB_DESC(1, "TxExcessCollCount", tx_excess_coll_count),
-+      MIB_DESC(1, "TxCollCount", tx_coll_count),
-+      MIB_DESC(1, "TxPauseCount", tx_pause_count),
-+      MIB_DESC(1, "RxGoodPkts", rx_good_pkts),
-+      MIB_DESC(1, "RxUnicastPkts", rx_unicast_pkts),
-+      MIB_DESC(1, "RxBroadcastPkts", rx_broadcast_pkts),
-+      MIB_DESC(1, "RxMulticastPkts", rx_multicast_pkts),
-+      MIB_DESC(1, "RxFCSErrorPkts", rx_fcserror_pkts),
-+      MIB_DESC(1, "RxUnderSizeGoodPkts", rx_under_size_good_pkts),
-+      MIB_DESC(1, "RxOversizeGoodPkts", rx_oversize_good_pkts),
-+      MIB_DESC(1, "RxUnderSizeErrorPkts", rx_under_size_error_pkts),
-+      MIB_DESC(1, "RxOversizeErrorPkts", rx_oversize_error_pkts),
 +      MIB_DESC(1, "RxFilteredPkts", rx_filtered_pkts),
-+      MIB_DESC(1, "Rx64BytePkts", rx64byte_pkts),
-+      MIB_DESC(1, "Rx127BytePkts", rx127byte_pkts),
-+      MIB_DESC(1, "Rx255BytePkts", rx255byte_pkts),
-+      MIB_DESC(1, "Rx511BytePkts", rx511byte_pkts),
-+      MIB_DESC(1, "Rx1023BytePkts", rx1023byte_pkts),
-+      MIB_DESC(1, "RxMaxBytePkts", rx_max_byte_pkts),
-+      MIB_DESC(1, "RxDroppedPkts", rx_dropped_pkts),
 +      MIB_DESC(1, "RxExtendedVlanDiscardPkts", rx_extended_vlan_discard_pkts),
 +      MIB_DESC(1, "MtuExceedDiscardPkts", mtu_exceed_discard_pkts),
-+      MIB_DESC(2, "RxGoodBytes", rx_good_bytes),
 +      MIB_DESC(2, "RxBadBytes", rx_bad_bytes),
-+      MIB_DESC(1, "RxGoodPausePkts", rx_good_pause_pkts),
-+      MIB_DESC(1, "RxAlignErrorPkts", rx_align_error_pkts),
++};
++
++static const struct ethtool_rmon_hist_range mxl862xx_rmon_ranges[] = {
++      { 0, 64 },
++      { 65, 127 },
++      { 128, 255 },
++      { 256, 511 },
++      { 512, 1023 },
++      { 1024, 10240 },
++      {}
 +};
 +
  #define MXL862XX_SDMA_PCTRLP(p)               (0xbc0 + ((p) * 0x6))
  #define MXL862XX_SDMA_PCTRL_EN                BIT(0)
  
-@@ -1893,6 +1951,110 @@ static int mxl862xx_port_bridge_flags(st
+@@ -1734,6 +1766,140 @@ static int mxl862xx_port_bridge_flags(st
        return 0;
  }
  
 +static void mxl862xx_get_strings(struct dsa_switch *ds, int port,
-+                              u32 stringset, u8 *data)
++                               u32 stringset, u8 *data)
 +{
 +      int i;
 +
@@ -285,7 +261,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                            struct mxl862xx_rmon_port_cnt *cnt)
 +{
 +      memset(cnt, 0, sizeof(*cnt));
-+      cnt->port_type = MXL862XX_CTP_PORT;
++      cnt->port_type = cpu_to_le32(MXL862XX_CTP_PORT);
 +      cnt->port_id = cpu_to_le16(port);
 +
 +      return MXL862XX_API_READ(ds->priv, MXL862XX_RMON_PORT_GET, *cnt);
@@ -365,10 +341,40 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
 +}
 +
++static void mxl862xx_get_rmon_stats(struct dsa_switch *ds, int port,
++                                  struct ethtool_rmon_stats *rmon_stats,
++                                  const struct ethtool_rmon_hist_range **ranges)
++{
++      struct mxl862xx_rmon_port_cnt cnt;
++
++      if (mxl862xx_read_rmon(ds, port, &cnt))
++              return;
++
++      rmon_stats->undersize_pkts = le32_to_cpu(cnt.rx_under_size_good_pkts);
++      rmon_stats->oversize_pkts = le32_to_cpu(cnt.rx_oversize_good_pkts);
++      rmon_stats->fragments = le32_to_cpu(cnt.rx_under_size_error_pkts);
++      rmon_stats->jabbers = le32_to_cpu(cnt.rx_oversize_error_pkts);
++
++      rmon_stats->hist[0] = le32_to_cpu(cnt.rx64byte_pkts);
++      rmon_stats->hist[1] = le32_to_cpu(cnt.rx127byte_pkts);
++      rmon_stats->hist[2] = le32_to_cpu(cnt.rx255byte_pkts);
++      rmon_stats->hist[3] = le32_to_cpu(cnt.rx511byte_pkts);
++      rmon_stats->hist[4] = le32_to_cpu(cnt.rx1023byte_pkts);
++      rmon_stats->hist[5] = le32_to_cpu(cnt.rx_max_byte_pkts);
++
++      rmon_stats->hist_tx[0] = le32_to_cpu(cnt.tx64byte_pkts);
++      rmon_stats->hist_tx[1] = le32_to_cpu(cnt.tx127byte_pkts);
++      rmon_stats->hist_tx[2] = le32_to_cpu(cnt.tx255byte_pkts);
++      rmon_stats->hist_tx[3] = le32_to_cpu(cnt.tx511byte_pkts);
++      rmon_stats->hist_tx[4] = le32_to_cpu(cnt.tx1023byte_pkts);
++      rmon_stats->hist_tx[5] = le32_to_cpu(cnt.tx_max_byte_pkts);
++
++      *ranges = mxl862xx_rmon_ranges;
++}
  static const struct dsa_switch_ops mxl862xx_switch_ops = {
        .get_tag_protocol = mxl862xx_get_tag_protocol,
        .setup = mxl862xx_setup,
-@@ -1917,6 +2079,12 @@ static const struct dsa_switch_ops mxl86
+@@ -1758,6 +1924,13 @@ 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,
@@ -378,6 +384,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      .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,
++      .get_rmon_stats = mxl862xx_get_rmon_stats,
  };
  
  static void mxl862xx_phylink_mac_config(struct phylink_config *config,
similarity index 80%
rename from target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-implement-.get_stats64.patch
rename to target/linux/generic/backport-6.12/780-v7.1-net-dsa-mxl862xx-implement-.get_stats64.patch
index 55b81a7023e4ac84bc4a08d96ea2164503103a11..b985f9ecbc0f254f04bb1a661b8df94e3d6db0ae 100644 (file)
@@ -1,20 +1,22 @@
-From bab5a69e3872a693069e430a1fa0d2825ea83b4f Mon Sep 17 00:00:00 2001
+From a21d33a5265f0b31d935a8b9b2b6faefb5185911 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 24 Mar 2026 04:14:38 +0000
-Subject: [PATCH 07/26] net: dsa: mxl862xx: implement .get_stats64
+Date: Sun, 12 Apr 2026 01:02:05 +0100
+Subject: [PATCH 2/2] 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
-in ~880s at 2.5 Gbps line rate; the 2s polling interval provides a
-comfortable margin. The .get_stats64 callback forces a fresh poll so
-that counters are always up to date when queried.
+in ~220s at 10 Gbps line rate with minimum-size frames; the 2s polling
+interval provides a comfortable margin. The .get_stats64 callback
+forces a fresh poll so that counters are always up to date when queried.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/fa38548ba05866879e8912721edc91947ce4ff12.1775951347.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 ---
  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(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c      | 175 +++++++++++++++++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx.h      |  94 +++++++++++-
+ 3 files changed, 270 insertions(+), 7 deletions(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
@@ -69,7 +71,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  struct mxl862xx_mib_desc {
        unsigned int size;
        unsigned int offset;
-@@ -739,6 +745,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -677,6 +683,9 @@ static int mxl862xx_setup(struct dsa_swi
        if (ret)
                return ret;
  
@@ -79,10 +81,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return mxl862xx_setup_mdio(ds);
  }
  
-@@ -2055,6 +2064,158 @@ static void mxl862xx_get_pause_stats(str
-       pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts);
- }
+@@ -1900,6 +1909,159 @@ static void mxl862xx_get_rmon_stats(stru
  
+       *ranges = mxl862xx_rmon_ranges;
+ }
++
 +/* Compute the delta between two 32-bit free-running counter snapshots,
 + * handling a single wrap-around correctly via unsigned subtraction.
 + */
@@ -238,15 +241,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static const struct dsa_switch_ops mxl862xx_switch_ops = {
        .get_tag_protocol = mxl862xx_get_tag_protocol,
        .setup = mxl862xx_setup,
-@@ -2085,6 +2246,7 @@ static const struct dsa_switch_ops mxl86
-       .get_eth_mac_stats = mxl862xx_get_eth_mac_stats,
+@@ -1931,6 +2093,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_rmon_stats = mxl862xx_get_rmon_stats,
 +      .get_stats64 = mxl862xx_get_stats64,
  };
  
  static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -2146,16 +2308,22 @@ static int mxl862xx_probe(struct mdio_de
+@@ -1992,16 +2155,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);
@@ -269,7 +272,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return err;
  }
  
-@@ -2170,6 +2338,9 @@ static void mxl862xx_remove(struct mdio_
+@@ -2016,6 +2185,9 @@ static void mxl862xx_remove(struct mdio_
  
        priv = ds->priv;
  
@@ -279,7 +282,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        dsa_unregister_switch(ds);
  
        mxl862xx_host_shutdown(priv);
-@@ -2196,6 +2367,9 @@ static void mxl862xx_shutdown(struct mdi
+@@ -2042,6 +2214,9 @@ static void mxl862xx_shutdown(struct mdi
  
        dsa_switch_shutdown(ds);
  
@@ -291,11 +294,45 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        for (i = 0; i < MXL862XX_MAX_PORTS; i++)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -148,6 +148,47 @@ struct mxl862xx_evlan_block {
+@@ -117,6 +117,79 @@ struct mxl862xx_evlan_block {
  };
  
  /**
 + * struct mxl862xx_port_stats - 64-bit accumulated hardware port statistics
++ * @rx_packets: total received packets
++ * @tx_packets: total transmitted packets
++ * @rx_bytes: total received bytes
++ * @tx_bytes: total transmitted bytes
++ * @rx_errors: total receive errors
++ * @tx_errors: total transmit errors
++ * @rx_dropped: total received packets dropped
++ * @tx_dropped: total transmitted packets dropped
++ * @multicast: total received multicast packets
++ * @collisions: total transmit collisions
++ * @rx_length_errors: received length errors (undersize + oversize)
++ * @rx_crc_errors: received FCS errors
++ * @rx_frame_errors: received alignment errors
++ * @prev_rx_good_pkts: previous snapshot of rx good packet counter
++ * @prev_tx_good_pkts: previous snapshot of tx good packet counter
++ * @prev_rx_good_bytes: previous snapshot of rx good byte counter
++ * @prev_tx_good_bytes: previous snapshot of tx good byte counter
++ * @prev_rx_fcserror_pkts: previous snapshot of rx FCS error counter
++ * @prev_rx_under_size_error_pkts: previous snapshot of rx undersize
++ *                                 error counter
++ * @prev_rx_oversize_error_pkts: previous snapshot of rx oversize
++ *                               error counter
++ * @prev_rx_align_error_pkts: previous snapshot of rx alignment
++ *                            error counter
++ * @prev_tx_dropped_pkts: previous snapshot of tx dropped counter
++ * @prev_rx_dropped_pkts: previous snapshot of rx dropped counter
++ * @prev_rx_evlan_discard_pkts: previous snapshot of extended VLAN
++ *                              discard counter
++ * @prev_mtu_exceed_discard_pkts: previous snapshot of MTU exceed
++ *                                discard counter
++ * @prev_tx_acm_dropped_pkts: previous snapshot of tx ACM dropped
++ *                            counter
++ * @prev_rx_multicast_pkts: previous snapshot of rx multicast counter
++ * @prev_tx_coll_count: previous snapshot of tx collision counter
 + *
 + * The firmware RMON counters are 32-bit free-running (64-bit for byte
 + * counters). This structure holds 64-bit accumulators alongside the
@@ -303,7 +340,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * handling 32-bit wrap correctly via unsigned subtraction.
 + */
 +struct mxl862xx_port_stats {
-+      /* 64-bit accumulators */
 +      u64 rx_packets;
 +      u64 tx_packets;
 +      u64 rx_bytes;
@@ -317,7 +353,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      u64 rx_length_errors;
 +      u64 rx_crc_errors;
 +      u64 rx_frame_errors;
-+      /* Previous raw RMON values for delta computation */
 +      u32 prev_rx_good_pkts;
 +      u32 prev_tx_good_pkts;
 +      u64 prev_rx_good_bytes;
@@ -339,7 +374,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * 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
-@@ -178,6 +219,10 @@ struct mxl862xx_evlan_block {
+@@ -145,6 +218,10 @@ struct mxl862xx_evlan_block {
   *                       The worker acquires rtnl_lock() to serialize with
   *                       DSA callbacks and checks @setup_done to avoid
   *                       acting on torn-down ports.
@@ -350,13 +385,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  struct mxl862xx_port {
        struct mxl862xx_priv *priv;
-@@ -195,16 +240,25 @@ struct mxl862xx_port {
+@@ -160,16 +237,24 @@ struct mxl862xx_port {
        bool host_flood_uc;
        bool host_flood_mc;
        struct work_struct host_flood_work;
-+      /* Hardware stats accumulation */
 +      struct mxl862xx_port_stats stats;
-+      spinlock_t stats_lock;
++      spinlock_t stats_lock; /* protects stats accumulators */
  };
  
 +/* Bit indices for struct mxl862xx_priv::flags */
@@ -378,7 +412,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @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 {
+@@ -181,18 +266,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
similarity index 89%
rename from target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch
rename to target/linux/generic/pending-6.12/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch
index 03aeb8c9b7baa13f2129b31b6b4ac2468590ddf2..1559511d9810d4915d273cfd3c131d296a3fc8b3 100644 (file)
@@ -1,7 +1,7 @@
-From da12469e73282da814163125153f381823e33f20 Mon Sep 17 00:00:00 2001
+From 60a23e663e0c607ae4ed871aaa24d257051ad557 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 17:56:35 +0000
-Subject: [PATCH 08/26] net: dsa: mxl862xx: store firmware version for feature
+Subject: [PATCH 01/19] net: dsa: mxl862xx: store firmware version for feature
  gating
 
 Query the firmware version at init (already done in wait_ready),
@@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -286,6 +286,9 @@ static int mxl862xx_wait_ready(struct ds
+@@ -257,6 +257,9 @@ static int mxl862xx_wait_ready(struct ds
                         ver.iv_major, ver.iv_minor,
                         le16_to_cpu(ver.iv_revision),
                         le32_to_cpu(ver.iv_build_num));
@@ -40,8 +40,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #include <linux/mdio.h>
  #include <linux/workqueue.h>
  #include <net/dsa.h>
-@@ -245,6 +246,38 @@ struct mxl862xx_port {
-       spinlock_t stats_lock;
+@@ -241,6 +242,38 @@ struct mxl862xx_port {
+       spinlock_t stats_lock; /* protects stats accumulators */
  };
  
 +/**
@@ -79,7 +79,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  /* 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 {
+@@ -258,6 +291,8 @@ struct mxl862xx_port {
   * @drop_meter:         index of the single shared zero-rate firmware meter
   *                      used to unconditionally drop traffic (used to block
   *                      flooding)
@@ -88,7 +88,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @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
-@@ -279,6 +314,7 @@ struct mxl862xx_priv {
+@@ -275,6 +310,7 @@ struct mxl862xx_priv {
        struct work_struct crc_err_work;
        unsigned long flags;
        u16 drop_meter;
similarity index 95%
rename from target/linux/generic/pending-6.12/760-09-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch
rename to target/linux/generic/pending-6.12/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch
index 2ce4d1fb0e96b0dc9660862567d1c64169590bf0..6d36ad48933545b1d5d469ab20c9ded60afdebf9 100644 (file)
@@ -1,7 +1,7 @@
-From f7606470d398e4091e1bc405bf2125dc5fc99919 Mon Sep 17 00:00:00 2001
+From cefa0447dc95a4ddd5093f7b8cf35e654870283f Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Wed, 25 Mar 2026 21:39:30 +0000
-Subject: [PATCH 09/26] net: dsa: mxl862xx: move phylink stubs to
+Subject: [PATCH 02/19] net: dsa: mxl862xx: move phylink stubs to
  mxl862xx-phylink.c
 
 Move the phylink MAC operations and get_caps callback from mxl862xx.c
@@ -110,7 +110,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
  #define MXL862XX_API_WRITE(dev, cmd, data) \
        mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false)
-@@ -1597,16 +1598,6 @@ static void mxl862xx_port_teardown(struc
+@@ -1424,16 +1425,6 @@ static void mxl862xx_port_teardown(struc
        priv->ports[port].setup_done = false;
  }
  
@@ -127,7 +127,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db)
  {
        struct mxl862xx_priv *priv = ds->priv;
-@@ -2252,33 +2243,6 @@ static const struct dsa_switch_ops mxl86
+@@ -2099,33 +2090,6 @@ static const struct dsa_switch_ops mxl86
        .get_stats64 = mxl862xx_get_stats64,
  };
  
similarity index 94%
rename from target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch
rename to target/linux/generic/pending-6.12/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch
index 7ac42cb3a663af2a7be2adb346c62d31db2b245e..50af43a01948391d5b70ab303668a5ad7b1ef9b0 100644 (file)
@@ -1,7 +1,7 @@
-From e583eeeb907f0abeef2082162293a5d63b9fd6fa Mon Sep 17 00:00:00 2001
+From 3c1d77006daca1df20d612850535bc6050e266ee Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Thu, 26 Mar 2026 01:50:00 +0000
-Subject: [PATCH 10/26] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
+Subject: [PATCH 03/19] 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
similarity index 99%
rename from target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch
rename to target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch
index 6fe50aeb909a69161912ef65c7af386ce84b1e1c..38d6729997b10321bcaece0a1505275051db1214 100644 (file)
@@ -990,7 +990,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #endif /* __MXL862XX_PHYLINK_H */
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -684,7 +684,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -622,7 +622,7 @@ static int mxl862xx_setup(struct dsa_swi
        int n_user_ports = 0, max_vlans;
        int ingress_finals, vid_rules;
        struct dsa_port *dp;
@@ -999,7 +999,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        ret = mxl862xx_reset(priv);
        if (ret)
-@@ -694,6 +694,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -632,6 +632,9 @@ static int mxl862xx_setup(struct dsa_swi
        if (ret)
                return ret;
  
@@ -1011,7 +1011,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
         *   Ingress: only final catchall rules (PVID insertion, 802.1Q
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -247,6 +247,22 @@ struct mxl862xx_port {
+@@ -243,6 +243,22 @@ struct mxl862xx_port {
  };
  
  /**
@@ -1034,7 +1034,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * union mxl862xx_fw_version - firmware version for comparison and display
   * @major: firmware major version
   * @minor: firmware minor version
-@@ -297,6 +313,8 @@ union mxl862xx_fw_version {
+@@ -293,6 +309,8 @@ union mxl862xx_fw_version {
   *                      flooding)
   * @fw_version:         cached firmware version, populated at probe and
   *                      compared with MXL862XX_FW_VER_MIN()
@@ -1043,7 +1043,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @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
-@@ -315,6 +333,7 @@ struct mxl862xx_priv {
+@@ -311,6 +329,7 @@ struct mxl862xx_priv {
        unsigned long flags;
        u16 drop_meter;
        union mxl862xx_fw_version fw_version;
similarity index 96%
rename from target/linux/generic/pending-6.12/760-12-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch
rename to target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch
index 749a1ee2a66aa31ced0bba810e9e7f1f32689e30..1afbc17f15b0c224f292c10d3bf98beb564bf285 100644 (file)
@@ -1,7 +1,7 @@
-From 24d752291784e30d7329bed15744bbbc6a3e2485 Mon Sep 17 00:00:00 2001
+From 3659914c43a587a1ca6418867834831aa518ac35 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 18:14:33 +0000
-Subject: [PATCH 12/26] net: dsa: mxl862xx: add SerDes ethtool statistics
+Subject: [PATCH 05/19] 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
@@ -239,7 +239,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #endif /* __MXL862XX_PHYLINK_H */
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -1960,6 +1960,8 @@ static void mxl862xx_get_strings(struct
+@@ -1775,6 +1775,8 @@ static void mxl862xx_get_strings(struct
  
        for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++)
                ethtool_puts(&data, mxl862xx_mib[i].name);
@@ -248,7 +248,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
-@@ -1967,7 +1969,7 @@ static int mxl862xx_get_sset_count(struc
+@@ -1782,7 +1784,7 @@ static int mxl862xx_get_sset_count(struc
        if (sset != ETH_SS_STATS)
                return 0;
  
@@ -257,7 +257,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  static int mxl862xx_read_rmon(struct dsa_switch *ds, int port,
-@@ -2003,6 +2005,8 @@ static void mxl862xx_get_ethtool_stats(s
+@@ -1818,6 +1820,8 @@ static void mxl862xx_get_ethtool_stats(s
                else
                        *data++ = le64_to_cpu(*(__le64 *)field);
        }
similarity index 96%
rename from target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch
rename to target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch
index baba8311c68a26503df4c278d3e37c82b9665e74..1fdcff672eb1100a637240ee61ef9e0c9bb5eaf1 100644 (file)
@@ -1,7 +1,7 @@
-From ee227a5e4c74f599cc1b34578b32214d5873ad2f Mon Sep 17 00:00:00 2001
+From ce66c0be462c8500dfc483395e68be4326ebf296 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 18:15:32 +0000
-Subject: [PATCH 13/26] net: dsa: mxl862xx: add SerDes self-test via PRBS and
+Subject: [PATCH 06/19] net: dsa: mxl862xx: add SerDes self-test via PRBS and
  BERT
 
 Implement the dsa_switch_ops.self_test callback for SerDes ports
@@ -198,9 +198,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #endif /* __MXL862XX_PHYLINK_H */
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -2241,6 +2241,7 @@ static const struct dsa_switch_ops mxl86
-       .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats,
+@@ -2088,6 +2088,7 @@ static const struct dsa_switch_ops mxl86
        .get_pause_stats = mxl862xx_get_pause_stats,
+       .get_rmon_stats = mxl862xx_get_rmon_stats,
        .get_stats64 = mxl862xx_get_stats64,
 +      .self_test = mxl862xx_serdes_self_test,
  };
similarity index 78%
rename from target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-trap-link-local-frames-to-the-CPU-p.patch
rename to target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch
index 56d361231070b87900849be14c4898469a823ff6..935a82d168f9b483529e06c213e52e194063ea04 100644 (file)
@@ -1,14 +1,28 @@
-From 43eb3eed250ea4e7e83371fcbf2bfb8d626eade6 Mon Sep 17 00:00:00 2001
+From e6defbd42db6e64c2bb203f82b7d7f8c0691a052 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 18:51:13 +0000
-Subject: [PATCH 14/26] net: dsa: mxl862xx: trap link-local frames to the CPU
- port
+Subject: [PATCH 07/19] net: dsa: mxl862xx: trap link-local and multicast
+ snooping frames to CPU
 
 Install per-CTP PCE rules on each user port that trap IEEE 802.1D
-link-local frames (01:80:c2:00:00:0x) to the CPU port via an
-explicit forwarding portmap with cross-state enabled, ensuring the
-frames reach the host even when the bridge port is in BLOCKING or
-LEARNING state.
+link-local frames (01:80:c2:00:00:0x) and IP multicast snooping
+frames (IGMP, MLDv1, MLDv2) to the CPU port.
+
+All trap rules share a common action helper,
+mxl862xx_fill_cpu_trap_action(), which sets PORTMAP_ALTERNATIVE to
+redirect frames to the CPU and enables cross-state forwarding so
+that frames reach the host even when the bridge port is in BLOCKING
+or LEARNING state.
+
+A dedicated bridge FID (cpu_trap_fid) is allocated during setup with
+all flood modes enabled. Each trap rule points the bridge engine at
+this FID via bFidEnable so that IGMP and MLD frames are never
+silently dropped by the ingress port's private flood policy.
+
+Three multicast snooping rules are installed per port:
+  offset 2 -- IPv4 IGMP (IP protocol 2, all versions)
+  offset 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
+  offset 4 -- ICMPv6 type 143 (MLDv2 Listener Report)
 
 Add the PCE rule firmware API structures, command definitions, and
 the rule block allocation interface.
@@ -17,8 +31,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
  drivers/net/dsa/mxl862xx/mxl862xx-api.h | 684 ++++++++++++++++++++++++
  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |   5 +
- drivers/net/dsa/mxl862xx/mxl862xx.c     |  69 +++
- 3 files changed, 758 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 186 +++++++
+ drivers/net/dsa/mxl862xx/mxl862xx.h     |   8 +
+ 4 files changed, 883 insertions(+)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -736,7 +751,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define MXL862XX_BRIDGE_CONFIGGET     (MXL862XX_BRDG_MAGIC + 0x3)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -280,9 +280,11 @@ static int mxl862xx_wait_ready(struct ds
+@@ -10,6 +10,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/delay.h>
+ #include <linux/etherdevice.h>
++#include <linux/icmpv6.h>
+ #include <linux/if_bridge.h>
+ #include <linux/module.h>
+ #include <linux/of_device.h>
+@@ -251,9 +252,11 @@ static int mxl862xx_wait_ready(struct ds
                         ver.iv_major, ver.iv_minor,
                         le16_to_cpu(ver.iv_revision),
                         le32_to_cpu(ver.iv_build_num));
@@ -748,17 +771,54 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                return 0;
  
  not_ready_yet:
-@@ -410,6 +412,68 @@ static int mxl862xx_setup_drop_meter(str
+@@ -381,6 +384,158 @@ static int mxl862xx_setup_drop_meter(str
        return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
  }
  
 +
-+/* 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
-+ * entries per port on a 10-port SKU, of which offset 0 is reserved
-+ * for flow-control marking). Offset 1 is the first unused slot.
++/* Per-CTP offsets for protocol trap rules. Each port's CTP flow-table
++ * block is pre-allocated by the firmware during init (44 entries per
++ * port on a 10-port SKU, of which offset 0 is reserved for flow-control
++ * marking). Offsets 1-4 are used for link-local and multicast snooping
++ * traps; all others remain free.
 + */
 +#define MXL862XX_LINK_LOCAL_CTP_OFFSET                1
++#define MXL862XX_IGMP_CTP_OFFSET              2
++#define MXL862XX_MLDV1_CTP_OFFSET             3
++#define MXL862XX_MLDV2_CTP_OFFSET             4
++
++/* Fill the action fields of a PCE rule that traps ingress frames to
++ * the CPU port. Used by both the link-local trap and the multicast
++ * snooping traps. The caller must already have set the rule header
++ * (logicalportid, subifidgroup, region) and the pattern fields.
++ *
++ * PORTMAP_ALTERNATIVE redirects the frame to the CPU port but does
++ * not by itself bypass downstream flood gates. In SpTag mode the
++ * ingress port's private FID may have forward_unknown_multicast=false,
++ * which silently drops IGMP/MLD before they reach the CPU.
++ * Setting bFidEnable to cpu_trap_fid (a dedicated bridge with all
++ * flood modes enabled) overrides the FID used by the bridge engine,
++ * so the frame is never classified as blocked unknown MC regardless
++ * of the ingress port's standalone flood policy.
++ *
++ * Cross-state is enabled so trapped frames bypass STP port state.
++ */
++static void mxl862xx_fill_cpu_trap_action(struct dsa_switch *ds, int port,
++                                         struct mxl862xx_pce_rule *rule)
++{
++      struct mxl862xx_priv *priv = ds->priv;
++      int cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++
++      rule->action.port_map_action =
++              cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE);
++      mxl862xx_fw_portmap_set_bit(rule->action.forward_port_map, cpu_port);
++
++      rule->action.cross_state_action =
++              cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
++
++      rule->action.fid_enable = 1;
++      rule->action.fid = priv->cpu_trap_fid;
++}
 +
 +/* Install a PCE rule that traps IEEE 802.1D link-local frames
 + * (01:80:c2:00:00:0x) to the CPU port for a single user port,
@@ -778,54 +838,164 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + */
 +static int mxl862xx_setup_link_local_trap(struct dsa_switch *ds, int port)
 +{
-+      DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
-+      struct dsa_port *dp = dsa_to_port(ds, port);
++      struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_pce_rule rule = {};
-+      int cpu_port = dp->cpu_dp->index;
-+      int i;
 +
-+      /* Address this port's CTP flow-table block */
 +      rule.logicalportid = port;
-+      rule.subifidgroup = 0;
 +      rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP);
 +
-+      /* Pattern: link-local MAC on this specific ingress port */
 +      rule.pattern.index = cpu_to_le16(MXL862XX_LINK_LOCAL_CTP_OFFSET);
 +      rule.pattern.enable = 1;
 +      rule.pattern.mac_dst_enable = 1;
 +      memcpy(rule.pattern.mac_dst, eth_reserved_addr_base, ETH_ALEN);
 +      rule.pattern.mac_dst_mask = cpu_to_le16(0x0001);
 +
-+      /* Action: forward to the CPU port via explicit portmap */
-+      rule.action.port_map_action =
-+              cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE);
++      mxl862xx_fill_cpu_trap_action(ds, port, &rule);
 +
-+      bitmap_zero(portmap, MXL862XX_MAX_BRIDGE_PORTS);
-+      __set_bit(cpu_port, portmap);
-+      for (i = 0; i < ARRAY_SIZE(rule.action.forward_port_map); i++)
-+              rule.action.forward_port_map[i] =
-+                      cpu_to_le16(bitmap_read(portmap, i * 16, 16));
++      return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
++}
 +
-+      /* Bypass STP port state */
-+      rule.action.cross_state_action =
-+              cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
++/* Install PCE rules that trap IGMP and MLD frames to the CPU port for
++ * a single user port. PORTMAP_ALTERNATIVE overrides the bridge
++ * forwarding portmap to the CPU port. bFidEnable points the bridge
++ * engine at cpu_trap_fid (all flood modes enabled) so the frames are
++ * never classified as blocked unknown MC regardless of the ingress
++ * port's standalone flood policy.
++ *
++ * Three rules are installed per port:
++ *   offset 2 -- IPv4 IGMP (IP protocol 2, all versions)
++ *   offset 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
++ *   offset 4 -- ICMPv6 type 143 (MLDv2 Listener Report)
++ *
++ * The MLDv1 rule uses range mode on the first two bytes after the IP
++ * header (ICMPv6 type + code): lower bound 0x8200 (type 130, code 0)
++ * to upper bound 0x84ff (type 132, code 255). The MLDv2 rule uses
++ * nibble mask 0x3 to match type 143 with any code byte.
++ */
++static int mxl862xx_setup_snooping_traps(struct dsa_switch *ds, int port)
++{
++      struct mxl862xx_priv *priv = ds->priv;
++      struct mxl862xx_pce_rule rule = {};
++      int ret;
++
++      rule.logicalportid = port;
++      rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP);
++      mxl862xx_fill_cpu_trap_action(ds, port, &rule);
++
++      /* IGMP: IPv4 protocol 2, all versions */
++      rule.pattern.index = cpu_to_le16(MXL862XX_IGMP_CTP_OFFSET);
++      rule.pattern.enable = 1;
++      rule.pattern.protocol = IPPROTO_IGMP;
++      rule.pattern.protocol_enable = 1;
 +
-+      return MXL862XX_API_WRITE(ds->priv, MXL862XX_TFLOW_PCERULEWRITE,
-+                                rule);
++      ret = MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
++      if (ret)
++              return ret;
++
++      /* MLDv1: ICMPv6 types 130 (query), 131 (report), 132 (done).
++       * Range mode covers all three types with any code value.
++       */
++      memset(&rule.pattern, 0, sizeof(rule.pattern));
++      rule.pattern.index = cpu_to_le16(MXL862XX_MLDV1_CTP_OFFSET);
++      rule.pattern.enable = 1;
++      rule.pattern.protocol = IPPROTO_ICMPV6;
++      rule.pattern.protocol_enable = 1;
++      rule.pattern.app_data_msb =
++              cpu_to_le16((u16)ICMPV6_MGM_QUERY << 8);
++      rule.pattern.app_mask_range_msb =
++              cpu_to_le16(((u16)ICMPV6_MGM_REDUCTION << 8) | 0xff);
++      rule.pattern.app_data_msb_enable = 1;
++      rule.pattern.app_mask_range_msb_select = 1; /* range mode */
++
++      ret = MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
++      if (ret)
++              return ret;
++
++      /* MLDv2: ICMPv6 type 143 (Listener Report v2), any code byte.
++       * Nibble mask 0x3 masks nibbles 0-1 (lower byte = code field).
++       */
++      memset(&rule.pattern, 0, sizeof(rule.pattern));
++      rule.pattern.index = cpu_to_le16(MXL862XX_MLDV2_CTP_OFFSET);
++      rule.pattern.enable = 1;
++      rule.pattern.protocol = IPPROTO_ICMPV6;
++      rule.pattern.protocol_enable = 1;
++      rule.pattern.app_data_msb = cpu_to_le16((u16)ICMPV6_MLD2_REPORT << 8);
++      rule.pattern.app_mask_range_msb = cpu_to_le16(0x0003);
++      rule.pattern.app_data_msb_enable = 1;
++      /* app_mask_range_msb_select = 0: nibble mask mode (default) */
++
++      return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
 +}
 +
  static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
  {
        struct mxl862xx_bridge_port_config br_port_cfg = {};
-@@ -1549,6 +1613,11 @@ static int mxl862xx_port_setup(struct ds
+@@ -683,6 +838,28 @@ static int mxl862xx_setup(struct dsa_swi
+       if (ret)
+               return ret;
++
++      /* Allocate a dedicated PCE snooping FID with all flood modes enabled.
++       * Per-port PCE trap rules (link-local, IGMP, MLD) set bFidEnable to
++       * this FID so that the bridge engine uses it for its flood-permission
++       * check instead of the ingress port's private FID (which has
++       * mc_flood=false to restrict unknown MC from reaching the CPU in the
++       * normal path). The hardware PCE FID action field is 6 bits wide, so
++       * the allocated ID must be in range 0..63.
++       */
++      ret = mxl862xx_allocate_bridge(priv);
++      if (ret < 0)
++              return ret;
++
++      if (WARN_ON_ONCE(ret > 0x3F))
++              return -ERANGE;
++
++      priv->cpu_trap_fid = ret;
++
++      ret = mxl862xx_bridge_config_fwd(ds, priv->cpu_trap_fid,
++                                       true, true, true);
++      if (ret)
++              return ret;
+       schedule_delayed_work(&priv->stats_work,
+                             MXL862XX_STATS_POLL_INTERVAL);
+@@ -1382,6 +1559,15 @@ static int mxl862xx_port_setup(struct ds
        if (ret)
                return ret;
  
-+      /* install link-local trap for this user port */
++      /* install link-local and multicast snooping traps */
 +      ret = mxl862xx_setup_link_local_trap(ds, port);
 +      if (ret)
 +              return ret;
 +
-       /* Initialize and pre-allocate per-port EVLAN and VF blocks for
-        * user ports. CPU ports do not use EVLAN or VF -- frames pass
-        * through without processing. Pre-allocation avoids firmware
++      ret = mxl862xx_setup_snooping_traps(ds, port);
++      if (ret)
++              return ret;
++
+       priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
+       ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
+       if (ret)
+--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
+@@ -319,6 +319,13 @@ union mxl862xx_fw_version {
+  * @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
++ * @cpu_trap_fid:       firmware bridge FID allocated for PCE-trapped frames;
++ *                      configured with uc/mc/bc flood all enabled so that
++ *                      IGMP, MLD, and link-local frames always reach the CPU
++ *                      regardless of the ingress port's private FID flood
++ *                      policy. Set once in setup() and referenced by
++ *                      fill_cpu_trap_action() via bFidEnable. The PCE FID
++ *                      action field is 6 bits, so this value must be <= 63.
+  * @stats_work:         periodic work item that polls RMON hardware counters
+  *                      and accumulates them into 64-bit per-port stats
+  */
+@@ -335,6 +342,7 @@ struct mxl862xx_priv {
+       u16 evlan_ingress_size;
+       u16 evlan_egress_size;
+       u16 vf_block_size;
++      u16 cpu_trap_fid;
+       struct delayed_work stats_work;
+ };
similarity index 81%
rename from target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch
rename to target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch
index d590f54f47f6ef856b5808d3b9154fe059b89b3b..48ffab50ec86e10e37c6118458e40ca6312b8ef8 100644 (file)
@@ -1,7 +1,7 @@
-From e18f5b235d8df21209c73f4f0bbc00cc3a1973ba Mon Sep 17 00:00:00 2001
+From 264838ee8ee3ad611a84df96d889429f1ded2148 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 18:51:21 +0000
-Subject: [PATCH 15/26] net: dsa: mxl862xx: warn about old firmware default PCE
+Subject: [PATCH 08/19] net: dsa: mxl862xx: warn about old firmware default PCE
  rules
 
 Firmware versions older than 1.0.80 install global PCE rules at
@@ -19,14 +19,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -809,6 +809,10 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -860,6 +860,10 @@ static int mxl862xx_setup(struct dsa_swi
+                                        true, true, true);
        if (ret)
                return ret;
++
 +      if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
 +              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);
  
similarity index 83%
rename from target/linux/generic/pending-6.12/760-16-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch
rename to target/linux/generic/pending-6.12/760-09-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch
index 9b7a719db0badace623d9f0a9008a3daf31baac1..6cebfacd98dd13326c67644b131096e968a4f1f8 100644 (file)
@@ -1,7 +1,7 @@
-From 04929904d3a7d824593587e52e3ed21d6f0f109a Mon Sep 17 00:00:00 2001
+From d7ed9b681298d206d81e9cf3c93558d6e912ae88 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Sun, 22 Mar 2026 00:58:04 +0000
-Subject: [PATCH 16/26] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
+Subject: [PATCH 09/20] 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
@@ -22,13 +22,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  drivers/net/dsa/mxl862xx/Kconfig        |    1 +
  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     |   13 +
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 1678 ++++++++++++++++++++---
+ drivers/net/dsa/mxl862xx/mxl862xx.h     |   12 +
  include/net/dsa.h                       |    2 +
  net/dsa/Kconfig                         |    7 +
  net/dsa/Makefile                        |    1 +
- net/dsa/tag_mxl862xx_8021q.c            |   59 +
- 9 files changed, 1736 insertions(+), 196 deletions(-)
+ net/dsa/tag_mxl862xx_8021q.c            |   65 +
+ 9 files changed, 1803 insertions(+), 186 deletions(-)
  create mode 100644 net/dsa/tag_mxl862xx_8021q.c
 
 --- a/drivers/net/dsa/mxl862xx/Kconfig
@@ -290,7 +290,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define MXL862XX_MAC_TABLEENTRYQUERY  (MXL862XX_SWMAC_MAGIC + 0x4)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -16,6 +16,7 @@
+@@ -17,6 +17,7 @@
  #include <linux/of_mdio.h>
  #include <linux/phy.h>
  #include <linux/phylink.h>
@@ -298,9 +298,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #include <net/dsa.h>
  
  #include "mxl862xx.h"
-@@ -115,6 +116,9 @@ enum mxl862xx_evlan_action {
+@@ -88,6 +89,9 @@ enum mxl862xx_evlan_action {
+       EVLAN_STRIP_IF_UNTAGGED,        /* remove 1 tag if entry's untagged flag set */
        EVLAN_PVID_OR_DISCARD,          /* insert PVID tag or discard if no PVID */
-       EVLAN_PVID_OR_PASS,             /* insert PVID tag or pass-through */
        EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
 +      EVLAN_INSERT_OUTER,             /* insert outer tag with mgmt_vid */
 +      EVLAN_STRIP1,                   /* strip 1 tag unconditionally */
@@ -308,7 +308,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  };
  
  struct mxl862xx_evlan_rule_desc {
-@@ -124,6 +128,7 @@ struct mxl862xx_evlan_rule_desc {
+@@ -97,6 +101,7 @@ struct mxl862xx_evlan_rule_desc {
        u8 inner_tpid;          /* enum mxl862xx_extended_vlan_filter_tpid */
        bool match_vid;         /* true: match on VID from the vid parameter */
        u8 action;              /* enum mxl862xx_evlan_action */
@@ -316,8 +316,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  };
  
  /* Shorthand constants for readability */
-@@ -190,11 +195,69 @@ static const struct mxl862xx_evlan_rule_
-       { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE,  true, EVLAN_STRIP_IF_UNTAGGED },
+@@ -162,11 +167,69 @@ static const struct mxl862xx_evlan_rule_
+       { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE,  false, EVLAN_STRIP_IF_UNTAGGED },
  };
  
 +/*
@@ -387,9 +387,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  /* PHY access via firmware relay */
-@@ -420,6 +483,78 @@ static int mxl862xx_setup_drop_meter(str
-  */
- #define MXL862XX_LINK_LOCAL_CTP_OFFSET                1
+@@ -396,6 +459,78 @@ static int mxl862xx_setup_drop_meter(str
+ #define MXL862XX_MLDV1_CTP_OFFSET             3
+ #define MXL862XX_MLDV2_CTP_OFFSET             4
  
 +/**
 + * mxl862xx_cpu_bridge_port_id - Get the bridge port ID for CPU-side forwarding
@@ -463,94 +463,95 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp);
 +}
 +
- /* Install a PCE rule that traps IEEE 802.1D link-local frames
-  * (01:80:c2:00:00:0x) to the CPU port for a single user port,
-  * preventing the hardware bridge from flooding them to other ports.
-@@ -440,10 +575,14 @@ static int mxl862xx_setup_link_local_tra
+ /* Fill the action fields of a PCE rule that traps ingress frames to
+  * the CPU port. Used by both the link-local trap and the multicast
+  * snooping traps. The caller must already have set the rule header
+@@ -404,24 +539,36 @@ static int mxl862xx_setup_drop_meter(str
+  * PORTMAP_ALTERNATIVE redirects the frame to the CPU port but does
+  * not by itself bypass downstream flood gates. In SpTag mode the
+  * ingress port's private FID may have forward_unknown_multicast=false,
+- * which silently drops IGMP/MLD before they reach the CPU.
++ * which silently drops IGMP/MLD before they reach the CPU. In
++ * tag_8021q mode the VBP egress sub-meters can have the same effect.
+  * Setting bFidEnable to cpu_trap_fid (a dedicated bridge with all
+  * flood modes enabled) overrides the FID used by the bridge engine,
+  * so the frame is never classified as blocked unknown MC regardless
+  * of the ingress port's standalone flood policy.
+  *
+- * Cross-state is enabled so trapped frames bypass STP port state.
++ * In tag_8021q mode the VBP egress EVLAN block is also attached so
++ * that the management VID is inserted before the frame reaches the
++ * CPU. Cross-state is enabled so trapped frames bypass STP port
++ * state.
+  */
+ static void mxl862xx_fill_cpu_trap_action(struct dsa_switch *ds, int port,
+                                          struct mxl862xx_pce_rule *rule)
  {
-       DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
-       struct dsa_port *dp = dsa_to_port(ds, port);
-+      struct mxl862xx_priv *priv = ds->priv;
-       struct mxl862xx_pce_rule rule = {};
-       int cpu_port = dp->cpu_dp->index;
-+      struct mxl862xx_port *p;
-       int i;
+       struct mxl862xx_priv *priv = ds->priv;
++      struct mxl862xx_port *p = &priv->ports[port];
+       int cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
  
-+      p = &priv->ports[port];
-+
-       /* Address this port's CTP flow-table block */
-       rule.logicalportid = port;
-       rule.subifidgroup = 0;
-@@ -466,11 +605,18 @@ static int mxl862xx_setup_link_local_tra
-               rule.action.forward_port_map[i] =
-                       cpu_to_le16(bitmap_read(portmap, i * 16, 16));
+       rule->action.port_map_action =
+               cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE);
+       mxl862xx_fw_portmap_set_bit(rule->action.forward_port_map, cpu_port);
  
 +      if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q &&
 +          p->cpu_egress_evlan.in_use) {
-+              rule.action.extended_vlan_enable = 1;
-+              rule.action.extended_vlan_block_id =
++              rule->action.extended_vlan_enable = 1;
++              rule->action.extended_vlan_block_id =
 +                      cpu_to_le16(p->cpu_egress_evlan.block_id);
 +      }
 +
-       /* Bypass STP port state */
-       rule.action.cross_state_action =
+       rule->action.cross_state_action =
                cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
  
--      return MXL862XX_API_WRITE(ds->priv, MXL862XX_TFLOW_PCERULEWRITE,
-+      return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE,
-                                 rule);
- }
-@@ -587,7 +733,8 @@ static int mxl862xx_sync_bridge_members(
-                               __set_bit(member_dp->index,
-                                         priv->ports[port].portmap);
-               }
--              __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
-+              __set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
-+                        priv->ports[port].portmap);
-               err = mxl862xx_set_bridge_port(ds, port);
-               if (err)
-@@ -717,7 +864,6 @@ static void mxl862xx_free_bridge(struct
- static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port)
+@@ -441,9 +588,6 @@ static void mxl862xx_fill_cpu_trap_actio
+  * allocation via PCERULEALLOC is needed. Using region=CTP causes the
+  * firmware to translate the CTP-relative offset into an absolute
+  * hardware index.
+- *
+- * Cross-state is enabled so that link-local frames reach the CPU even
+- * when the bridge port is in BLOCKING or LEARNING state.
+  */
+ static int mxl862xx_setup_link_local_trap(struct dsa_switch *ds, int port)
  {
--      struct dsa_port *dp = dsa_to_port(ds, port);
-       struct mxl862xx_priv *priv = ds->priv;
-       int ret;
-@@ -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);
--      __set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
-+      __set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
-+                priv->ports[port].portmap);
-       ret = mxl862xx_set_bridge_port(ds, port);
-       if (ret)
-               return ret;
+@@ -552,11 +696,17 @@ static int mxl862xx_set_bridge_port(stru
+               return 0;
  
--      /* 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
-+       * standalone ports, unknown unicast must be flooded so that
-+       * frames from the host can reach the user port.
-+       *
-+       * 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);
- }
-@@ -745,10 +903,12 @@ static int mxl862xx_add_single_port_brid
+       if (dsa_port_is_cpu(dp)) {
+-              dsa_switch_for_each_user_port(member_dp, ds) {
+-                      if (member_dp->cpu_dp->index != port)
+-                              continue;
+-                      mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+-                                                  member_dp->index);
++              /* In tag_8021q mode the CPU TX path uses per-user-port virtual
++               * bridge ports; leave the physical CPU bridge port map empty to
++               * prevent FID 0 flooding back to user ports.
++               */
++              if (priv->tag_proto != DSA_TAG_PROTO_MXL862_8021Q) {
++                      dsa_switch_for_each_user_port(member_dp, ds) {
++                              if (member_dp->cpu_dp->index != port)
++                                      continue;
++                              mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                                          member_dp->index);
++                      }
+               }
+       } else if (dp->bridge) {
+               dsa_switch_for_each_bridge_member(member_dp, ds,
+@@ -567,10 +717,10 @@ static int mxl862xx_set_bridge_port(stru
+                                                   member_dp->index);
+               }
+               mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+-                                          dp->cpu_dp->index);
++                                          mxl862xx_cpu_bridge_port_id(ds, port));
+       } else {
+               mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+-                                          dp->cpu_dp->index);
++                                          mxl862xx_cpu_bridge_port_id(ds, port));
+               p->flood_block = 0;
+               p->learning = false;
+       }
+@@ -774,10 +924,12 @@ static void mxl862xx_free_bridge(struct
  static int mxl862xx_setup(struct dsa_switch *ds)
  {
        struct mxl862xx_priv *priv = ds->priv;
@@ -565,7 +566,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        ret = mxl862xx_reset(priv);
        if (ret)
-@@ -761,7 +921,7 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -790,7 +942,7 @@ static int mxl862xx_setup(struct dsa_swi
        for (i = 0; i < 8; i++)
                mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9);
  
@@ -574,7 +575,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
         * With VLAN Filter handling VID membership checks:
         *   Ingress: only final catchall rules (PVID insertion, 802.1Q
         *            accept, non-8021Q TPID handling, discard).
-@@ -769,40 +929,67 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -798,46 +950,151 @@ 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.
@@ -589,11 +590,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +       *   VF:    CPU port needs its own VF block for management VIDs.
         *
         * Total EVLAN budget:
--       *   n_user_ports * (ingress + egress) â‰¤ 1024.
--       * Ingress blocks are small (7 entries), so almost all capacity
--       * goes to egress VID rules.
+-       *   n_user_ports * (ingress + egress) <= 1024.
 +       *   n_user_ports * (ingress + egress + cpu_egress + cpu_ingress_share)
 +       *   <= 1024.
+        * Ingress blocks are small (7 entries), so almost all capacity
+        * goes to egress VID rules.
 +       * Total VF budget:
 +       *   (n_user_ports + n_cpu_ports) * vf_block_size <= 1024.
         */
@@ -656,16 +657,19 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        }
  
        ret = mxl862xx_setup_drop_meter(ds);
-@@ -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");
+       if (ret)
+               return ret;
  
++      if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
++              dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules "
++                       "that interfere with DSA operation, please update\n");
++
 +      /* Pre-allocate firmware resources for all ports. The DSA core
 +       * calls change_tag_protocol() between setup() and port_setup(),
 +       * and in tag_8021q mode that triggers dsa_tag_8021q_register()
 +       * which fires tag_8021q_vlan_add callbacks that need EVLAN and
 +       * VF blocks. complete_tag_8021q_setup() also needs per-port
-+       * FIDs from add_single_port_bridge().
++       * FIDs allocated before port_setup() runs.
 +       *
 +       * Per-port configuration (SpTag, CTP, portmaps, link-local
 +       * traps) is deferred to port_setup().
@@ -673,10 +677,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      dsa_switch_for_each_cpu_port(dp, ds) {
 +              port = dp->index;
 +
-+              mxl862xx_vf_init(&priv->ports[port].vf,
-+                               priv->vf_block_size);
-+              mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
-+                                        priv->cpu_evlan_ingress_size);
++              priv->ports[port].vf.block_size = priv->vf_block_size;
++              INIT_LIST_HEAD(&priv->ports[port].vf.vids);
++              priv->ports[port].ingress_evlan.block_size =
++                      priv->cpu_evlan_ingress_size;
 +              ret = mxl862xx_evlan_block_alloc(priv,
 +                                               &priv->ports[port].ingress_evlan);
 +              if (ret)
@@ -690,45 +694,67 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      dsa_switch_for_each_user_port(dp, ds) {
 +              port = dp->index;
 +
-+              mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
-+                                        priv->evlan_ingress_size);
++              priv->ports[port].ingress_evlan.block_size =
++                      priv->evlan_ingress_size;
 +              ret = mxl862xx_evlan_block_alloc(priv,
 +                                               &priv->ports[port].ingress_evlan);
 +              if (ret)
 +                      return ret;
 +
-+              mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
-+                                        priv->evlan_egress_size);
++              priv->ports[port].egress_evlan.block_size =
++                      priv->evlan_egress_size;
 +              ret = mxl862xx_evlan_block_alloc(priv,
 +                                               &priv->ports[port].egress_evlan);
 +              if (ret)
 +                      return ret;
 +
-+              mxl862xx_vf_init(&priv->ports[port].vf,
-+                               priv->vf_block_size);
++              priv->ports[port].vf.block_size = priv->vf_block_size;
++              INIT_LIST_HEAD(&priv->ports[port].vf.vids);
 +              ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
 +              if (ret)
 +                      return ret;
 +
-+              mxl862xx_evlan_block_init(&priv->ports[port].cpu_egress_evlan,
-+                                        ARRAY_SIZE(cpu_egress_tag_8021q));
++              priv->ports[port].cpu_egress_evlan.block_size =
++                      ARRAY_SIZE(cpu_egress_tag_8021q);
 +              ret = mxl862xx_evlan_block_alloc(priv,
 +                                               &priv->ports[port].cpu_egress_evlan);
 +              if (ret)
 +                      return ret;
 +
-+              ret = mxl862xx_add_single_port_bridge(ds, port);
++              ret = mxl862xx_allocate_bridge(priv);
++              if (ret < 0)
++                      return ret;
++              priv->ports[port].fid = ret;
++
++              /* Initialize flood forwarding for the private FID.
++               * change_tag_protocol() runs between setup() and port_setup();
++               * ports must be in a clean standalone state before that window.
++               */
++              ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
++                                               false, false, true);
 +              if (ret)
 +                      return ret;
 +      }
 +
+       /* Allocate a dedicated PCE snooping FID with all flood modes enabled.
+        * Per-port PCE trap rules (link-local, IGMP, MLD) set bFidEnable to
+@@ -860,10 +1117,6 @@ static int mxl862xx_setup(struct dsa_swi
+                                        true, true, true);
+       if (ret)
+               return ret;
+-
+-      if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
+-              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);
  
-@@ -894,6 +1143,52 @@ static int mxl862xx_configure_sp_tag_pro
+@@ -944,10 +1197,71 @@ static int mxl862xx_configure_sp_tag_pro
+       return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
  }
  
- /**
++/**
 + * mxl862xx_set_cpu_vbp - Push CPU-side virtual bridge port config to firmware
 + * @ds: DSA switch
 + * @port: user port index whose VBP to configure
@@ -775,18 +801,19 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +}
 +
 +/**
-  * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
-  * @priv: driver private data
-  * @block_id: HW Extended VLAN block ID
-@@ -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)
++ * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
++ * @priv: driver private data
++ * @block_id: HW Extended VLAN block ID
++ * @entry_index: entry index within the block
++ * @desc: rule descriptor (filter type + action)
++ * @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)
 + * @mgmt_vid: tag_8021q management VID for outer tag insertion (0 = unused)
-  *
-  * Translates a compact rule descriptor into a full firmware
-  * mxl862xx_extendedvlan_config struct and writes it via the API.
-@@ -909,7 +1205,8 @@ static int mxl862xx_configure_sp_tag_pro
++ *
++ * Translates a compact rule descriptor into a full firmware
++ * mxl862xx_extendedvlan_config struct and writes it via the API.
++ */
  static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
                                     u16 block_id, u16 entry_index,
                                     const struct mxl862xx_evlan_rule_desc *desc,
@@ -796,7 +823,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  {
        struct mxl862xx_extendedvlan_config cfg = {};
        struct mxl862xx_extendedvlan_filter_vlan *fv;
-@@ -999,6 +1296,31 @@ static int mxl862xx_evlan_write_rule(str
+@@ -1016,6 +1330,31 @@ static int mxl862xx_evlan_write_rule(str
                                cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
                }
                break;
@@ -828,7 +855,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        }
  
        return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
-@@ -1059,7 +1381,7 @@ static int mxl862xx_evlan_write_final_ru
+@@ -1054,7 +1393,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],
@@ -837,10 +864,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                if (ret)
                        return ret;
        }
-@@ -1228,6 +1550,27 @@ static int mxl862xx_vf_del_vid(struct mx
+@@ -1175,6 +1514,41 @@ static int mxl862xx_vf_del_vid(struct mx
+       return 0;
  }
  
- /**
++/**
 + * mxl862xx_vf_clear_vids - Remove all VID entries without freeing the HW block
 + * @priv: driver private data
 + * @vf: VLAN Filter block
@@ -862,10 +890,23 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +}
 +
 +/**
-  * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
-  * @priv: driver private data
-  * @port: port number
-@@ -1278,8 +1621,8 @@ static int mxl862xx_evlan_program_egress
++ * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
++ * @priv: driver private data
++ * @port: port number
++ *
++ * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
++ * untagged/priority-tagged frames, passes through standard 802.1Q
++ * tagged frames for VF membership checking, and treats non-8021Q TPID
++ * frames as untagged. The block is sized to exactly fit the 7 catchall
++ * rules and is rewritten whenever PVID changes.
++ *
++ * In VLAN-unaware mode the firmware passes frames through unchanged when
++ * no ingress block is assigned, so nothing is programmed.
++ */
+ static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
+ {
+       struct mxl862xx_port *p = &priv->ports[port];
+@@ -1199,8 +1573,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;
@@ -875,8 +916,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        if (p->vlan_filtering) {
                vid_rules = vid_accept_standard;
-@@ -1296,13 +1639,23 @@ static int mxl862xx_evlan_program_egress
-               if (p->vlan_filtering && !vfv->untagged)
+@@ -1214,13 +1588,23 @@ static int mxl862xx_evlan_program_egress
+               if (!vfv->untagged)
                        continue;
  
 +              /* Skip the tag_8021q management VID -- it must NOT get
@@ -900,7 +941,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                if (ret)
                        return ret;
  
-@@ -1311,7 +1664,29 @@ static int mxl862xx_evlan_program_egress
+@@ -1229,7 +1613,29 @@ static int mxl862xx_evlan_program_egress
                                                        idx++, &vid_rules[1],
                                                        vfv->vid,
                                                        vfv->untagged,
@@ -931,7 +972,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                        if (ret)
                                return ret;
                }
-@@ -1323,8 +1698,7 @@ static int mxl862xx_evlan_program_egress
+@@ -1241,8 +1647,7 @@ static int mxl862xx_evlan_program_egress
         */
        for (i = idx; i < old_active; i++) {
                ret = mxl862xx_evlan_deactivate_entry(priv,
@@ -941,9 +982,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                if (ret)
                        return ret;
        }
-@@ -1348,13 +1722,16 @@ static int mxl862xx_port_vlan_filtering(
+@@ -1267,13 +1672,16 @@ static int mxl862xx_port_vlan_filtering(
+       p->vlan_filtering = vlan_filtering;
  
-       /* Reprogram Extended VLAN rules if filtering mode changed */
        if (changed) {
 -              /* When leaving VLAN-aware mode, release the ingress HW
 -               * block. The firmware passes frames through unchanged
@@ -963,63 +1004,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
                ret = mxl862xx_evlan_program_ingress(priv, port);
                if (ret)
-@@ -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);
--      dsa_switch_for_each_user_port(dp, ds) {
--              /* it's safe to rely on cpu_dp being valid for user ports */
--              if (dp->cpu_dp->index != port)
--                      continue;
-+      if (priv->tag_proto != DSA_TAG_PROTO_MXL862_8021Q) {
-+              dsa_switch_for_each_user_port(dp, ds) {
-+                      /* it's safe to rely on cpu_dp being valid for user ports */
-+                      if (dp->cpu_dp->index != port)
-+                              continue;
--              __set_bit(dp->index, p->portmap);
-+                      __set_bit(dp->index, p->portmap);
-+              }
-       }
-       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,
-@@ -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 dsa_port *dp = dsa_to_port(ds, port);
-       struct mxl862xx_priv *priv = ds->priv;
-       struct mxl862xx_port *p = &priv->ports[port];
-       int err;
-@@ -1550,34 +1927,587 @@ static void mxl862xx_port_bridge_leave(s
-        * single-port bridge
-        */
-       bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
--      __set_bit(dp->cpu_dp->index, p->portmap);
-+      __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), p->portmap);
-       p->flood_block = 0;
-+      p->host_flood_block = 0;
--      /* 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 VF: set_bridge_port sees dp->bridge == NULL (DSA already
--       * cleared it) and sends vlan_filter_enable=0, which decrements
--       * the firmware VF refcount.
+@@ -1493,22 +1901,575 @@ static void mxl862xx_port_bridge_leave(s
+                       port, ERR_PTR(err));
+       /* Revert leaving port, omitted by the sync above, to its
+-       * single-port bridge
++       * single-port bridge state. Egress EVLAN is reprogrammed below
++       * -- in tag_8021q mode it gets management VID strip catchalls,
++       * in SpTag mode it is cleared.
++       *
 +       * Do NOT clear the VF VID list here. Bridge VLANs are already
 +       * removed by port_vlan_del during the switchdev replay in
 +       * dsa_port_pre_bridge_leave. The remaining VIDs (e.g. the
@@ -1343,34 +1336,21 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +}
 +
 +/**
-+ * mxl862xx_refresh_cpu_targets - Update portmaps and traps for new CPU target
++ * mxl862xx_refresh_cpu_targets - Update bridge ports and traps for new CPU target
 + * @ds: DSA switch
 + *
 + * After switching between SpTag and tag_8021q, the CPU-side target in
-+ * each user port's portmap changes (physical CPU port vs. virtual
-+ * bridge port). This rebuilds every user port's portmap with the
-+ * correct CPU target and reinstalls the link-local PCE trap.
++ * each user port's bridge port map changes (physical CPU port vs. virtual
++ * bridge port). Reinstalls bridge port config and link-local PCE traps.
 + */
 +static int mxl862xx_refresh_cpu_targets(struct dsa_switch *ds)
 +{
 +      struct mxl862xx_priv *priv = ds->priv;
-+      struct dsa_port *dp, *member_dp;
-+      struct mxl862xx_port *p;
++      struct dsa_port *dp;
 +      int ret, port;
 +
 +      dsa_switch_for_each_user_port(dp, ds) {
 +              port = dp->index;
-+              p = &priv->ports[port];
-+
-+              bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
-+              if (dp->bridge) {
-+
-+                      dsa_switch_for_each_bridge_member(member_dp, ds, dp->bridge->dev) {
-+                              if (member_dp->index != port)
-+                                      __set_bit(member_dp->index, p->portmap);
-+                      }
-+              }
-+              __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), p->portmap);
 +
 +              /* Reprogram user port egress EVLAN to add or remove the
 +               * tag_8021q management VID strip catchalls.
@@ -1386,6 +1366,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +              ret = mxl862xx_setup_link_local_trap(ds, port);
 +              if (ret)
 +                      return ret;
++
++              ret = mxl862xx_setup_snooping_traps(ds, port);
++              if (ret)
++                      return ret;
 +      }
 +
 +      return 0;
@@ -1516,7 +1500,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +
 +                              mxl862xx_bridge_config_fwd(ds,
 +                                                        priv->ports[port].fid,
-+                                                        false, false, true);
++                                                        priv->ports[port].host_flood_uc,
++                                                        priv->ports[port].host_flood_mc,
++                                                        true);
 +                      }
 +              }
 +              dsa_switch_for_each_cpu_port(dp, ds) {
@@ -1596,11 +1582,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
  {
        struct mxl862xx_priv *priv = ds->priv;
-@@ -1597,55 +2527,30 @@ static int mxl862xx_port_setup(struct ds
-           dsa_port_is_dsa(dp))
-               return 0;
+@@ -1530,33 +2491,52 @@ static int mxl862xx_port_setup(struct ds
+               return -EOPNOTSUPP;
+       }
  
--      /* configure tag protocol */
 -      ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
 +      /* configure tag protocol: SpTag for native, disable for 8021q */
 +      ret = mxl862xx_configure_sp_tag_proto(ds, port,
@@ -1609,7 +1594,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (ret)
                return ret;
  
-       /* assign CTP port IDs */
        ret = mxl862xx_configure_ctp_port(ds, port, port,
 -                                        is_cpu_port ? 32 - port : 1);
 +                                        (is_cpu_port &&
@@ -1619,37 +1603,70 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                return ret;
  
        if (is_cpu_port)
--              /* assign user ports to CPU port bridge */
                return mxl862xx_setup_cpu_bridge(ds, port);
  
--      /* setup single-port bridge for user ports */
--      ret = mxl862xx_add_single_port_bridge(ds, port);
--      if (ret)
+-      /* setup single-port bridge for user ports.
+-       * If this fails, the FID is leaked -- but the port then transitions
+-       * to unused, and the FID pool is sized to tolerate this.
+-       */
+-      ret = mxl862xx_allocate_bridge(priv);
+-      if (ret < 0) {
+-              dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port);
 -              return ret;
--
-       /* install link-local trap for this user port */
-       ret = mxl862xx_setup_link_local_trap(ds, port);
++      /* The FID and initial bridge port config were set up in setup()
++       * before change_tag_protocol() runs. Reconfigure here now that
++       * the per-port CTP and SpTag settings are in place.
++       *
++       * 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
++       * standalone ports, unknown unicast must be flooded so that
++       * frames from the host can reach the user port.
++       *
++       * In 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.
++       * Block unknown UC/MC on the VBP egress meters in tag_8021q
++       * mode so frames to unknown destinations are not forwarded to
++       * the host. The DSA core re-enables selectively via
++       * port_set_host_flood when needed (e.g. promisc mode).
++       */
++      if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q) {
++              ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
++                                               true, true, true);
++              priv->ports[port].host_flood_block =
++                      BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC) |
++                      BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP) |
++                      BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP);
++      } else {
++              ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
++                                               false, false, true);
+       }
+-      priv->ports[port].fid = ret;
+-      /* Standalone ports should not flood unknown unicast or multicast
+-       * towards the CPU by default; only broadcast is needed initially.
+-       */
+-      ret = mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid,
+-                                       false, false, true);
+       if (ret)
+               return ret;
+       ret = mxl862xx_set_bridge_port(ds, port);
+@@ -1572,24 +2552,7 @@ static int mxl862xx_port_setup(struct ds
        if (ret)
                return ret;
  
--      /* Initialize and pre-allocate per-port EVLAN and VF blocks for
--       * user ports. CPU ports do not use EVLAN or VF -- frames pass
--       * through without processing. Pre-allocation avoids firmware
--       * EVLAN table fragmentation and simplifies control flow.
--       */
--      mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
--                                priv->evlan_ingress_size);
+-      priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
 -      ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
 -      if (ret)
 -              return ret;
 -
--      mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
--                                priv->evlan_egress_size);
+-      priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size;
 -      ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
 -      if (ret)
 -              return ret;
 -
--      mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
+-      priv->ports[port].vf.block_size = priv->vf_block_size;
+-      INIT_LIST_HEAD(&priv->ports[port].vf.vids);
 -      ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
 -      if (ret)
 -              return ret;
@@ -1659,7 +1676,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return 0;
  }
  
-@@ -1667,7 +2572,7 @@ static void mxl862xx_port_teardown(struc
+@@ -1611,7 +2574,7 @@ static void mxl862xx_port_teardown(struc
        priv->ports[port].setup_done = false;
  }
  
@@ -1668,7 +1685,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  {
        struct mxl862xx_priv *priv = ds->priv;
  
-@@ -1685,23 +2590,244 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -1629,23 +2592,247 @@ static int mxl862xx_get_fid(struct dsa_s
        }
  }
  
@@ -1701,12 +1718,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      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;
 +      }
-+
+-      param.port_id = cpu_to_le32(port);
 +      return port;
 +}
 +
@@ -1751,7 +1768,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * mxl862xx_mac_portmap_add - Set port bits in a MAC table entry's portmap
 + * @priv: driver private data
 + * @addr: MAC address
-+ * @fid: firmware FID
++ * @fid: FID
 + * @vid: VLAN ID
 + * @add_map: firmware-format portmap of bits to set
 + *
@@ -1796,7 +1813,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * mxl862xx_mac_portmap_del - Clear port bits from a MAC table entry's portmap
 + * @priv: driver private data
 + * @addr: MAC address
-+ * @fid: firmware FID
++ * @fid: FID
 + * @vid: VLAN ID
 + * @del_map: firmware-format portmap of bits to clear
 + *
@@ -1852,15 +1869,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * @ds: DSA switch
 + * @addr: MAC address
 + * @vid: VLAN ID
-+ * @bridge: bridge whose members' VBPs to include
++ * @bridge: bridge the entry is scoped to (used to pick the FID)
 + *
-+ * In tag_8021q mode, host FDB/MDB entries in a shared bridge FID must use
-+ * portmap mode targeting ALL bridge members' virtual bridge ports (VBPs).
-+ * The firmware ANDs the entry's portmap with each ingress port's
-+ * bridge_port_map, which contains only that port's own VBP. This
-+ * selects the correct VBP per ingress port, ensuring frames exit
-+ * through the right egress EVLAN (which inserts the per-port management
-+ * VID that identifies the source port to DSA on the CPU side).
++ * The entry's portmap lists every user port's VBP unconditionally. The
++ * bridging engine ANDs it with the ingress bridge port's bridge_port_map,
++ * which already encodes the current bridge membership, so only the
++ * ingress port's own VBP survives the intersection -- that selects the
++ * correct egress EVLAN (inserting the per-port management VID that
++ * identifies the source port to DSA on the CPU side) without any need
++ * to track bridge membership changes in this MAC entry.
 + */
 +static int mxl862xx_mac_add_host_bridge(struct dsa_switch *ds,
 +                                      const unsigned char *addr, u16 vid,
@@ -1869,11 +1886,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      __le16 add_map[MXL862XX_FW_PORTMAP_WORDS] = {};
 +      struct mxl862xx_priv *priv = ds->priv;
 +      u16 fid = priv->bridges[bridge->num];
-+      struct dsa_port *member_dp;
++      struct dsa_port *dp;
++      u16 vbp;
 +
-+      dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev)
-+              mxl862xx_fw_portmap_set_bit(add_map,
-+                                          priv->ports[member_dp->index].bridge_port_cpu);
++      dsa_switch_for_each_user_port(dp, ds) {
++              vbp = priv->ports[dp->index].bridge_port_cpu;
++              if (vbp)
++                      mxl862xx_fw_portmap_set_bit(add_map, vbp);
++      }
 +
 +      return mxl862xx_mac_portmap_add(priv, addr, fid, vid, add_map);
 +}
@@ -1921,7 +1941,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (ret)
                dev_err(ds->dev, "failed to add FDB entry on port %d\n", port);
  
-@@ -1711,18 +2837,25 @@ static int mxl862xx_port_fdb_add(struct
+@@ -1655,18 +2842,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)
  {
@@ -1930,7 +1950,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        struct mxl862xx_priv *priv = ds->priv;
 +      struct dsa_port *target_dp;
 +      int fid, ret;
++
 +      /* Mirror of the standalone->bridge FID path in fdb_add */
 +      if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) &&
 +          db.type == DSA_DB_PORT && vid > 0) {
@@ -1940,7 +1960,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                      mxl862xx_fdb_del_per_fid(ds, addr, vid,
 +                                               priv->bridges[target_dp->bridge->num]);
 +      }
-+
 +      fid = mxl862xx_get_fid(ds, db);
        if (fid < 0)
                return fid;
@@ -1954,7 +1974,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (ret)
                dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port);
  
-@@ -1761,88 +2894,147 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -1705,84 +2899,153 @@ static int mxl862xx_port_fdb_dump(struct
        return 0;
  }
  
@@ -1962,7 +1982,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * mxl862xx_mdb_add_to_fid - Add a port bit to an MDB entry in one FID
 + * @ds: DSA switch
 + * @mdb: multicast group address and VID
-+ * @fid: firmware FID to operate on
++ * @fid: FID to operate on
 + * @port_bit: port index to set in the portmap
 + * @vid: VLAN ID for the MAC table entry
 + */
@@ -1982,7 +2002,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 + * mxl862xx_mdb_del_from_fid - Remove a port bit from an MDB entry in one FID
 + * @ds: DSA switch
 + * @mdb: multicast group address
-+ * @fid: firmware FID to operate on
++ * @fid: FID to operate on
 + * @port_bit: port index to clear from the portmap
 + * @vid: VLAN ID for the MAC table entry (0 for SVL/tag_8021q mode)
 + */
@@ -2021,7 +2041,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (fid < 0)
                return fid;
  
--      /* Look up existing entry by {MAC, FID, TCI} */
 -      ether_addr_copy(qparam.mac, mdb->addr);
 -      qparam.fid = cpu_to_le16(fid);
 -      qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
@@ -2051,23 +2070,24 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +              ret = mxl862xx_mdb_add_to_fid(ds, mdb, fid, db.dp->index,
 +                                             mdb->vid);
  
--      /* Merge with existing portmap if entry already exists */
 -      if (qparam.found)
 -              memcpy(aparam.port_map, qparam.port_map,
 -                     sizeof(aparam.port_map));
 +      return ret;
 +}
--      mxl862xx_fw_portmap_set_bit(aparam.port_map, port);
++
 +/**
-+ * mxl862xx_mac_del_host_bridge - Remove VBP bits from a host FDB/MDB entry
++ * mxl862xx_mac_del_host_bridge - Remove every user-port VBP bit from a host
++ *                                FDB/MDB entry, dropping the entry when its
++ *                                portmap becomes empty
 + * @ds: DSA switch
 + * @addr: MAC address
 + * @vid: VLAN ID
-+ * @bridge: bridge whose members' VBPs to clear
++ * @bridge: bridge the entry is scoped to (used to pick the FID)
 + *
-+ * Clears all bridge member VBP bits from the portmap. If the portmap
-+ * becomes empty (no user-port bits remain), removes the entry entirely.
++ * Counterpart of mxl862xx_mac_add_host_bridge(): the add path sets every
++ * user port's VBP in the portmap, so the delete path clears the same
++ * set of bits.
 + */
 +static int mxl862xx_mac_del_host_bridge(struct dsa_switch *ds,
 +                                      const unsigned char *addr, u16 vid,
@@ -2076,13 +2096,17 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      __le16 del_map[MXL862XX_FW_PORTMAP_WORDS] = {};
 +      struct mxl862xx_priv *priv = ds->priv;
 +      u16 fid = priv->bridges[bridge->num];
-+      struct dsa_port *member_dp;
++      struct dsa_port *dp;
++      u16 vbp;
+-      mxl862xx_fw_portmap_set_bit(aparam.port_map, port);
++      dsa_switch_for_each_user_port(dp, ds) {
++              vbp = priv->ports[dp->index].bridge_port_cpu;
++              if (vbp)
++                      mxl862xx_fw_portmap_set_bit(del_map, vbp);
++      }
  
 -      return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam);
-+      dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev)
-+              mxl862xx_fw_portmap_set_bit(del_map,
-+                                          priv->ports[member_dp->index].bridge_port_cpu);
-+
 +      return mxl862xx_mac_portmap_del(priv, addr, fid, vid, del_map);
  }
  
@@ -2111,7 +2135,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (fid < 0)
                return fid;
  
--      /* Look up existing entry */
 -      qparam.fid = cpu_to_le16(fid);
 -      qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid));
 -      ether_addr_copy(qparam.mac, mdb->addr);
@@ -2129,7 +2152,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 -      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 */
 -              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);
@@ -2153,34 +2175,33 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
        return ret;
  }
-@@ -1930,7 +3122,9 @@ static void mxl862xx_host_flood_work_fn(
+@@ -1867,6 +3130,9 @@ static void mxl862xx_host_flood_work_fn(
+                                              host_flood_work);
        struct mxl862xx_priv *priv = p->priv;
        struct dsa_switch *ds = priv->ds;
-       int port = p - priv->ports;
++      int port = p - priv->ports;
 +      unsigned long block;
-       bool uc, mc;
 +      int ret;
  
        rtnl_lock();
  
-@@ -1943,14 +3137,35 @@ static void mxl862xx_host_flood_work_fn(
-       uc = p->host_flood_uc;
-       mc = p->host_flood_mc;
+@@ -1876,13 +3142,34 @@ static void mxl862xx_host_flood_work_fn(
+               return;
+       }
  
--      /* 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.
+-      /* Always write to the standalone FID. When standalone it takes effect
+-       * immediately; when bridged the port uses the shared bridge FID so the
+-       * write is a no-op for current forwarding, but the state is preserved
+-       * in hardware and is ready once the port returns to standalone.
 -       */
--      if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
--              mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
+-      mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc,
+-                                 p->host_flood_mc, true);
 +      if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q) {
 +              block = 0;
 +
-+              if (!uc)
++              if (!p->host_flood_uc)
 +                      block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC);
-+              if (!mc) {
++              if (!p->host_flood_mc) {
 +                      block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP);
 +                      block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP);
 +              }
@@ -2194,20 +2215,19 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +                                      port, ERR_PTR(ret));
 +              }
 +      } else {
-+              /* 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.
++              /* Always write to the standalone FID. When standalone it takes
++               * effect immediately; when bridged the port uses the shared
++               * bridge FID so the write is a no-op for current forwarding,
++               * but the state is preserved in hardware and is ready once the
++               * port returns to standalone.
 +               */
-+              if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port)))
-+                      mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true);
++              mxl862xx_bridge_config_fwd(ds, p->fid, p->host_flood_uc,
++                                         p->host_flood_mc, true);
 +      }
  
        rtnl_unlock();
  }
-@@ -2285,7 +3500,9 @@ static void mxl862xx_get_stats64(struct
+@@ -2248,7 +3535,9 @@ static void mxl862xx_get_stats64(struct
  
  static const struct dsa_switch_ops mxl862xx_switch_ops = {
        .get_tag_protocol = mxl862xx_get_tag_protocol,
@@ -2217,7 +2237,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        .port_setup = mxl862xx_port_setup,
        .port_teardown = mxl862xx_port_teardown,
        .phylink_get_caps = mxl862xx_phylink_get_caps,
-@@ -2307,6 +3524,8 @@ static const struct dsa_switch_ops mxl86
+@@ -2270,6 +3559,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,
@@ -2226,7 +2246,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        .get_strings = mxl862xx_get_strings,
        .get_sset_count = mxl862xx_get_sset_count,
        .get_ethtool_stats = mxl862xx_get_ethtool_stats,
-@@ -2354,6 +3573,8 @@ static int mxl862xx_probe(struct mdio_de
+@@ -2318,6 +3609,8 @@ static int mxl862xx_probe(struct mdio_de
  
        INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn);
  
@@ -2235,7 +2255,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        dev_set_drvdata(dev, ds);
  
        err = dsa_register_switch(ds);
-@@ -2382,6 +3603,19 @@ static void mxl862xx_remove(struct mdio_
+@@ -2346,6 +3639,19 @@ static void mxl862xx_remove(struct mdio_
        set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
        cancel_delayed_work_sync(&priv->stats_work);
  
@@ -2257,7 +2277,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        mxl862xx_host_shutdown(priv);
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -210,6 +210,9 @@ struct mxl862xx_port_stats {
+@@ -209,6 +209,9 @@ struct mxl862xx_port_stats {
   * @vf:                  per-port VLAN Filter block state
   * @ingress_evlan:       ingress extended VLAN block state
   * @egress_evlan:        egress extended VLAN block state
@@ -2267,7 +2287,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @host_flood_uc:       desired host unicast flood state (true = flood);
   *                       updated atomically by port_set_host_flood, consumed
   *                       by the deferred host_flood_work
-@@ -224,6 +227,7 @@ struct mxl862xx_port_stats {
+@@ -223,6 +226,7 @@ struct mxl862xx_port_stats {
   *                       periodically by the stats polling work
   * @stats_lock:          protects accumulator reads in .get_stats64 against
   *                       concurrent updates from the polling work
@@ -2275,11 +2295,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  struct mxl862xx_port {
        struct mxl862xx_priv *priv;
-@@ -238,9 +242,14 @@ struct mxl862xx_port {
+@@ -235,9 +239,13 @@ struct mxl862xx_port {
        struct mxl862xx_vf_block vf;
        struct mxl862xx_evlan_block ingress_evlan;
        struct mxl862xx_evlan_block egress_evlan;
-+      /* tag_8021q state */
 +      u16 bridge_port_cpu;
 +      unsigned long host_flood_block;
        bool host_flood_uc;
@@ -2287,10 +2306,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        struct work_struct host_flood_work;
 +      u16 tag_8021q_vid;
 +      struct mxl862xx_evlan_block cpu_egress_evlan;
-       /* Hardware stats accumulation */
        struct mxl862xx_port_stats stats;
-       spinlock_t stats_lock;
-@@ -308,6 +317,7 @@ union mxl862xx_fw_version {
+       spinlock_t stats_lock; /* protects stats accumulators */
+ };
+@@ -304,6 +312,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
@@ -2298,15 +2317,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @drop_meter:         index of the single shared zero-rate firmware meter
   *                      used to unconditionally drop traffic (used to block
   *                      flooding)
-@@ -322,6 +332,7 @@ union mxl862xx_fw_version {
+@@ -318,6 +327,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
 + * @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
-@@ -331,6 +342,7 @@ struct mxl862xx_priv {
+  * @cpu_trap_fid:       firmware bridge FID allocated for PCE-trapped frames;
+  *                      configured with uc/mc/bc flood all enabled so that
+@@ -334,6 +344,7 @@ struct mxl862xx_priv {
        struct mdio_device *mdiodev;
        struct work_struct crc_err_work;
        unsigned long flags;
@@ -2314,14 +2333,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        u16 drop_meter;
        union mxl862xx_fw_version fw_version;
        struct mxl862xx_pcs serdes_ports[8];
-@@ -338,6 +350,7 @@ struct mxl862xx_priv {
+@@ -341,6 +352,7 @@ struct mxl862xx_priv {
        u16 bridges[MXL862XX_MAX_BRIDGES + 1];
        u16 evlan_ingress_size;
        u16 evlan_egress_size;
 +      u16 cpu_evlan_ingress_size;
        u16 vf_block_size;
+       u16 cpu_trap_fid;
        struct delayed_work stats_work;
- };
 --- a/include/net/dsa.h
 +++ b/include/net/dsa.h
 @@ -56,6 +56,7 @@ struct tc_action;
@@ -2368,7 +2387,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
 --- /dev/null
 +++ b/net/dsa/tag_mxl862xx_8021q.c
-@@ -0,0 +1,59 @@
+@@ -0,0 +1,65 @@
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +/*
 + * DSA 802.1Q-based tag driver for MaxLinear MxL862xx switches
@@ -2401,13 +2420,19 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +static struct sk_buff *mxl862_8021q_rcv(struct sk_buff *skb,
 +                                      struct net_device *netdev)
 +{
-+      int src_port = -1, switch_id = -1;
++      int src_port = -1, switch_id = -1, vbid = -1;
++      int vid = -1;
 +
-+      dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL);
++      dsa_8021q_rcv(skb, &src_port, &switch_id, &vbid, &vid);
 +
-+      skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port);
-+      if (!skb->dev)
++      skb->dev = dsa_tag_8021q_find_user(netdev, src_port, switch_id,
++                                          vid, vbid);
++      if (!skb->dev) {
++              net_warn_ratelimited("%s: dropped frame: src_port=%d switch_id=%d vid=%d vbid=%d\n",
++                                   netdev->name, src_port, switch_id,
++                                   vid, vbid);
 +              return NULL;
++      }
 +
 +      dsa_default_offload_fwd_mark(skb);
 +
similarity index 90%
rename from target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-add-link-aggregation-support.patch
rename to target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-add-link-aggregation-support.patch
index 0e4074254b25a25f81dba4d6e78ca77991a3861b..3b50fae970e5a0d2d42086792de31a49b095b65a 100644 (file)
@@ -1,7 +1,7 @@
-From 4e1d854199c166f617b93b7542e863e6a8ad2ccb Mon Sep 17 00:00:00 2001
+From a32f6a9c09b90e767a03ddac34d061545a0cf15e Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 03:44:41 +0000
-Subject: [PATCH 17/26] net: dsa: mxl862xx: add link aggregation support
+Subject: [PATCH 10/20] 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
@@ -33,9 +33,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
  drivers/net/dsa/mxl862xx/mxl862xx-api.h |  22 +
  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |   4 +
- drivers/net/dsa/mxl862xx/mxl862xx.c     | 587 +++++++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx.h     |  34 ++
- 4 files changed, 630 insertions(+), 17 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c     | 578 +++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.h     |  33 ++
+ 4 files changed, 621 insertions(+), 16 deletions(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -90,8 +90,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define INT_GPHY_READ                 (GPY_GPY2XX_MAGIC + 0x1)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -620,7 +620,42 @@ static int mxl862xx_setup_link_local_tra
-                                 rule);
+@@ -680,7 +680,42 @@ static int mxl862xx_setup_snooping_traps
+       return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
  }
  
 -static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
@@ -134,16 +134,31 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  {
        struct mxl862xx_bridge_port_config br_port_cfg = {};
        struct dsa_port *dp = dsa_to_port(ds, port);
-@@ -632,7 +667,7 @@ static int mxl862xx_set_bridge_port(stru
-       bool enable;
-       int i, idx;
+@@ -713,8 +748,12 @@ static int mxl862xx_set_bridge_port(stru
+                                                 dp->bridge->dev) {
+                       if (member_dp->index == port)
+                               continue;
+-                      mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+-                                                  member_dp->index);
++                      if (!mxl862xx_is_lag_master(priv, member_dp->index))
++                              continue;
++                      mxl862xx_fw_portmap_set_bit(
++                              br_port_cfg.bridge_port_map,
++                              mxl862xx_lag_bridge_port(priv,
++                                                       member_dp->index));
+               }
+               mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+                                           mxl862xx_cpu_bridge_port_id(ds, port));
+@@ -727,7 +766,7 @@ static int mxl862xx_set_bridge_port(stru
+       bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
  
 -      br_port_cfg.bridge_port_id = cpu_to_le16(port);
 +      br_port_cfg.bridge_port_id = cpu_to_le16(bp_id);
        br_port_cfg.bridge_id = cpu_to_le16(bridge_id);
        br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
                                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
-@@ -715,12 +750,38 @@ static int mxl862xx_set_bridge_port(stru
+@@ -808,11 +847,38 @@ static int mxl862xx_set_bridge_port(stru
                                  br_port_cfg);
  }
  
@@ -174,41 +189,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
                                        const struct dsa_bridge *bridge)
  {
-       struct mxl862xx_priv *priv = ds->priv;
-       struct dsa_port *dp, *member_dp;
--      int port, err, ret = 0;
++      struct mxl862xx_priv *priv = ds->priv;
 +      struct mxl862xx_port *p;
-+      int port, member, err, ret = 0;
-+      u16 lag_bp, bp;
+       struct dsa_port *dp;
+-      int ret = 0, err;
++      u16 lag_bp;
++      int err, ret = 0;
  
        dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
-               port = dp->index;
-@@ -729,9 +790,21 @@ static int mxl862xx_sync_bridge_members(
-                           MXL862XX_MAX_BRIDGE_PORTS);
-               dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) {
--                      if (member_dp->index != port)
--                              __set_bit(member_dp->index,
--                                        priv->ports[port].portmap);
-+                      member = member_dp->index;
-+
-+                      /* For LAG members, only include the LAG's
-+                       * dedicated bridge port in the portmap.
-+                       * Non-master members are skipped to avoid
-+                       * duplicates (they share the same LAG bridge
-+                       * port).
-+                       */
-+                      if (!mxl862xx_is_lag_master(priv, member))
-+                              continue;
-+                      if (member != port) {
-+                              bp = mxl862xx_lag_bridge_port(priv,
-+                                                           member);
-+                              __set_bit(bp, priv->ports[port].portmap);
-+                      }
-               }
-               __set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
-                         priv->ports[port].portmap);
-@@ -741,6 +814,25 @@ static int mxl862xx_sync_bridge_members(
+               err = mxl862xx_set_bridge_port(ds, dp->index);
+@@ -820,6 +886,25 @@ static int mxl862xx_sync_bridge_members(
                        ret = err;
        }
  
@@ -234,7 +224,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        return ret;
  }
  
-@@ -1881,6 +1973,408 @@ static int mxl862xx_setup_cpu_bridge(str
+@@ -1859,6 +1944,408 @@ static int mxl862xx_setup_cpu_bridge(str
        return mxl862xx_set_bridge_port(ds, port);
  }
  
@@ -643,7 +633,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
                                     const struct dsa_bridge bridge,
                                     bool *tx_fwd_offload,
-@@ -1907,7 +2401,18 @@ static int mxl862xx_port_bridge_join(str
+@@ -1884,7 +2371,18 @@ static int mxl862xx_port_bridge_join(str
                return 0;
        }
  
@@ -663,7 +653,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
-@@ -1966,6 +2471,17 @@ static void mxl862xx_port_bridge_leave(s
+@@ -1935,6 +2433,17 @@ static void mxl862xx_port_bridge_leave(s
                        "failed to update CPU VBP for port %d: %pe\n", port,
                        ERR_PTR(err));
  
@@ -681,7 +671,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (!dsa_bridge_ports(ds, bridge.dev))
                mxl862xx_free_bridge(ds, &bridge);
  }
-@@ -2591,18 +3107,17 @@ static int mxl862xx_get_fid(struct dsa_s
+@@ -2593,18 +3102,17 @@ static int mxl862xx_get_fid(struct dsa_s
  }
  
  /**
@@ -707,7 +697,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port,
                                    const struct dsa_db db)
-@@ -2618,7 +3133,7 @@ static int mxl862xx_fdb_bridge_port(stru
+@@ -2620,7 +3128,7 @@ static int mxl862xx_fdb_bridge_port(stru
                        return bp_cpu;
        }
  
@@ -716,7 +706,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  }
  
  /**
-@@ -2862,11 +3377,43 @@ static int mxl862xx_port_fdb_del(struct
+@@ -2867,11 +3375,43 @@ static int mxl862xx_port_fdb_del(struct
        return ret;
  }
  
@@ -760,7 +750,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        u32 entry_port_id;
        int ret;
  
-@@ -2880,7 +3427,7 @@ static int mxl862xx_port_fdb_dump(struct
+@@ -2885,7 +3425,7 @@ static int mxl862xx_port_fdb_dump(struct
  
                entry_port_id = le32_to_cpu(param.port_id);
  
@@ -769,7 +759,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                        ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID,
                                                      le16_to_cpu(param.tci)),
                                 param.static_entry, data);
-@@ -3521,6 +4068,11 @@ static const struct dsa_switch_ops mxl86
+@@ -3556,6 +4096,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,
@@ -781,7 +771,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        .port_vlan_filtering = mxl862xx_port_vlan_filtering,
        .port_vlan_add = mxl862xx_port_vlan_add,
        .port_vlan_del = mxl862xx_port_vlan_del,
-@@ -3561,6 +4113,7 @@ static int mxl862xx_probe(struct mdio_de
+@@ -3597,6 +4142,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;
@@ -811,7 +801,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
  /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
  #define MXL862XX_FW_PORTMAP_WORDS     (MXL862XX_MAX_BRIDGE_PORTS / 16)
-@@ -228,6 +241,12 @@ struct mxl862xx_port_stats {
+@@ -227,6 +240,12 @@ struct mxl862xx_port_stats {
   * @stats_lock:          protects accumulator reads in .get_stats64 against
   *                       concurrent updates from the polling work
   * @tag_8021q_vid:       currently assigned tag_8021q management VID
@@ -824,21 +814,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  struct mxl862xx_port {
        struct mxl862xx_priv *priv;
-@@ -250,6 +269,10 @@ struct mxl862xx_port {
+@@ -246,6 +265,9 @@ struct mxl862xx_port {
        struct work_struct host_flood_work;
        u16 tag_8021q_vid;
        struct mxl862xx_evlan_block cpu_egress_evlan;
-+      /* LAG state */
 +      struct dsa_lag *lag;
 +      bool lag_tx_enabled;
 +      u8 lag_hash_bits;
-       /* Hardware stats accumulation */
        struct mxl862xx_port_stats stats;
-       spinlock_t stats_lock;
-@@ -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
+       spinlock_t stats_lock; /* protects stats accumulators */
+ };
+@@ -336,6 +358,15 @@ union mxl862xx_fw_version {
+  *                      policy. Set once in setup() and referenced by
+  *                      fill_cpu_trap_action() via bFidEnable. The PCE FID
+  *                      action field is 6 bits, so this value must be <= 63.
 + * @lag_bridge_ports:   maps DSA LAG ID to firmware bridge port ID;
 + *                      zero means no bridge port allocated for that LAG.
 + *                      Indexed by lag->id (entry 0 is unused).
@@ -851,10 +840,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @stats_work:         periodic work item that polls RMON hardware counters
   *                      and accumulates them into 64-bit per-port stats
   */
-@@ -352,6 +384,8 @@ struct mxl862xx_priv {
-       u16 evlan_egress_size;
+@@ -355,6 +386,8 @@ struct mxl862xx_priv {
        u16 cpu_evlan_ingress_size;
        u16 vf_block_size;
+       u16 cpu_trap_fid;
 +      u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
 +      u8 trunk_hash;
        struct delayed_work stats_work;
similarity index 89%
rename from target/linux/generic/pending-6.12/760-18-net-dsa-mxl862xx-add-support-for-mirror-port.patch
rename to target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-mirror-port.patch
index cdcbaee869d8494544ab156abf412b3d37d7544c..045abd1bc22c7b0757bf09f605fda9193b03a16d 100644 (file)
@@ -1,7 +1,7 @@
-From 5528f38c3d709417625eb7f36628be31727a8221 Mon Sep 17 00:00:00 2001
+From d919c2f8da9dbd4dda57ceebb5c8b103805b58d1 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 12:05:29 +0000
-Subject: [PATCH 18/26] net: dsa: mxl862xx: add support for mirror port
+Subject: [PATCH 11/19] 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.
@@ -14,8 +14,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  drivers/net/dsa/mxl862xx/mxl862xx-api.h |  12 +++
  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |   1 +
  drivers/net/dsa/mxl862xx/mxl862xx.c     | 118 ++++++++++++++++++++++++
- drivers/net/dsa/mxl862xx/mxl862xx.h     |   8 ++
- 4 files changed, 139 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx.h     |   7 ++
+ 4 files changed, 138 insertions(+)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -50,7 +50,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define MXL862XX_TFLOW_PCERULEWRITE   (MXL862XX_TFLOW_MAGIC + 0x2)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -1084,6 +1084,8 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -1100,6 +1100,8 @@ static int mxl862xx_setup(struct dsa_swi
                                      (n_user_ports + n_cpu_ports);
        }
  
@@ -59,8 +59,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        ret = mxl862xx_setup_drop_meter(ds);
        if (ret)
                return ret;
-@@ -1973,6 +1975,120 @@ static int mxl862xx_setup_cpu_bridge(str
-       return mxl862xx_set_bridge_port(ds, port);
+@@ -3167,6 +3169,120 @@ static int mxl862xx_fdb_del_per_fid(stru
+       return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param);
  }
  
 +static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port,
@@ -178,9 +178,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +}
 +
  /**
-  * mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member)
-  * @ds: DSA switch
-@@ -4068,6 +4184,8 @@ static const struct dsa_switch_ops mxl86
+  * mxl862xx_mac_portmap_add - Set port bits in a MAC table entry's portmap
+  * @priv: driver private data
+@@ -4096,6 +4212,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,
@@ -191,7 +191,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        .port_lag_change = mxl862xx_port_lag_change,
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -241,6 +241,8 @@ struct mxl862xx_port_stats {
+@@ -240,6 +240,8 @@ struct mxl862xx_port_stats {
   * @stats_lock:          protects accumulator reads in .get_stats64 against
   *                       concurrent updates from the polling work
   * @tag_8021q_vid:       currently assigned tag_8021q management VID
@@ -200,17 +200,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @lag:                 non-NULL when port is member of a LAG group;
   *                       points to the DSA LAG structure
   * @lag_tx_enabled:      true when this port is active for TX in its LAG
-@@ -269,6 +271,9 @@ struct mxl862xx_port {
+@@ -265,6 +267,8 @@ struct mxl862xx_port {
        struct work_struct host_flood_work;
        u16 tag_8021q_vid;
        struct mxl862xx_evlan_block cpu_egress_evlan;
-+      /* Mirror state */
 +      bool ingress_mirror;
 +      bool egress_mirror;
-       /* LAG state */
        struct dsa_lag *lag;
        bool lag_tx_enabled;
-@@ -366,6 +371,8 @@ union mxl862xx_fw_version {
+       u8 lag_hash_bits;
+@@ -367,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
@@ -219,8 +218,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @stats_work:         periodic work item that polls RMON hardware counters
   *                      and accumulates them into 64-bit per-port stats
   */
-@@ -386,6 +393,7 @@ struct mxl862xx_priv {
-       u16 vf_block_size;
+@@ -388,6 +394,7 @@ struct mxl862xx_priv {
+       u16 cpu_trap_fid;
        u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
        u8 trunk_hash;
 +      int mirror_dest;
similarity index 93%
rename from target/linux/generic/pending-6.12/760-19-net-dsa-wire-flash_update-devlink-callback-to-driver.patch
rename to target/linux/generic/pending-6.12/760-12-net-dsa-wire-flash_update-devlink-callback-to-driver.patch
index 2adad2d11c1c4962db530fa21987a22f44e8ec2b..b15074991766457bc8082e115d361fd25569d082 100644 (file)
@@ -1,7 +1,7 @@
-From 4059d35a5bbf1901b2e0eb7126369cd713cacfce Mon Sep 17 00:00:00 2001
+From 64269d9d809962a0f7e68e9b618d81e561e3eb6f Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 16:30:08 +0000
-Subject: [PATCH 19/26] net: dsa: wire flash_update devlink callback to drivers
+Subject: [PATCH 12/19] 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
similarity index 94%
rename from target/linux/generic/pending-6.12/760-20-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch
rename to target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch
index 298614317562ea8e59af400f6c3c88e36e02a947..53a102904f60dd651b8456661693b091309bc607 100644 (file)
@@ -1,7 +1,7 @@
-From 0145151dc68aa318d8addb6fe7f12c0967f951da Mon Sep 17 00:00:00 2001
+From fed4225f75b6fe6898e48f472cbbee0aaa046760 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 16:30:17 +0000
-Subject: [PATCH 20/26] net: dsa: mxl862xx: add SMDIO clause-22 register access
+Subject: [PATCH 13/19] 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
similarity index 98%
rename from target/linux/generic/pending-6.12/760-21-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch
rename to target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch
index d731ec1b08392c046a9a2b17bbb3075598876963..9bfc096301623bc6d9a6e306ed707d8066367922 100644 (file)
@@ -1,7 +1,7 @@
-From bdbca48510e3e96ed9210f20fa4244dd6df5d44a Mon Sep 17 00:00:00 2001
+From fa186b09e346e0b7f2504232731bc9e4dee0c600 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 16:30:31 +0000
-Subject: [PATCH 21/26] net: dsa: mxl862xx: add devlink flash_update and
+Subject: [PATCH 14/19] net: dsa: mxl862xx: add devlink flash_update and
  info_get
 
 Implement runtime firmware upgrade via "devlink dev flash" and version
@@ -538,7 +538,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -22,6 +22,7 @@
+@@ -23,6 +23,7 @@
  #include "mxl862xx.h"
  #include "mxl862xx-api.h"
  #include "mxl862xx-cmd.h"
@@ -546,8 +546,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #include "mxl862xx-host.h"
  #include "mxl862xx-phylink.h"
  
-@@ -4204,6 +4205,9 @@ static const struct dsa_switch_ops mxl86
-       .get_pause_stats = mxl862xx_get_pause_stats,
+@@ -4233,6 +4234,9 @@ static const struct dsa_switch_ops mxl86
+       .get_rmon_stats = mxl862xx_get_rmon_stats,
        .get_stats64 = mxl862xx_get_stats64,
        .self_test = mxl862xx_serdes_self_test,
 +      .devlink_info_get = mxl862xx_devlink_info_get,
@@ -558,7 +558,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int mxl862xx_probe(struct mdio_device *mdiodev)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -394,6 +394,8 @@ struct mxl862xx_priv {
+@@ -395,6 +395,8 @@ struct mxl862xx_priv {
        u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
        u8 trunk_hash;
        int mirror_dest;
similarity index 83%
rename from target/linux/generic/pending-6.12/760-22-net-dsa-mxl862xx-implement-port-MTU-configuration.patch
rename to target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-implement-port-MTU-configuration.patch
index 1a22bba1f1cf5e4f5a5374711c1b4a2dbd434b62..0be764173cb4b2c59c3dee52052f3f4bebc66b89 100644 (file)
@@ -1,7 +1,7 @@
-From 8deb5be9638f7eb3009ed3eb619eedadee1df523 Mon Sep 17 00:00:00 2001
+From 05bc981690d6ef03143a094f28714aa0171ab571 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 24 Mar 2026 23:42:18 +0000
-Subject: [PATCH 22/26] net: dsa: mxl862xx: implement port MTU configuration
+Date: Wed, 1 Apr 2026 13:43:08 +0100
+Subject: [PATCH 15/19] 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
@@ -12,20 +12,20 @@ the effective maximum does not change.
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
  drivers/net/dsa/mxl862xx/mxl862xx.c | 50 +++++++++++++++++++++++++++++
- drivers/net/dsa/mxl862xx/mxl862xx.h |  4 +++
- 2 files changed, 54 insertions(+)
+ drivers/net/dsa/mxl862xx/mxl862xx.h |  ++
+ 2 files changed, 53 insertions(+)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -11,6 +11,7 @@
- #include <linux/delay.h>
+@@ -12,6 +12,7 @@
  #include <linux/etherdevice.h>
+ #include <linux/icmpv6.h>
  #include <linux/if_bridge.h>
 +#include <linux/if_vlan.h>
  #include <linux/module.h>
  #include <linux/of_device.h>
  #include <linux/of_mdio.h>
-@@ -3723,6 +3724,53 @@ static int mxl862xx_set_ageing_time(stru
+@@ -3727,6 +3728,53 @@ static int mxl862xx_set_ageing_time(stru
        return ret;
  }
  
@@ -79,7 +79,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
                                        u8 state)
  {
-@@ -4174,6 +4222,8 @@ static const struct dsa_switch_ops mxl86
+@@ -4202,6 +4250,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,
@@ -90,7 +90,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        .port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags,
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -249,6 +249,8 @@ struct mxl862xx_port_stats {
+@@ -248,6 +248,8 @@ struct mxl862xx_port_stats {
   * @lag_hash_bits:       hash field bitmask (MXL862XX_TRUNK_HASH_*) requested
   *                       when this port joined its LAG; used to recompute the
   *                       global trunk_hash when a LAG is destroyed
@@ -99,12 +99,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   */
  struct mxl862xx_port {
        struct mxl862xx_priv *priv;
-@@ -278,6 +280,8 @@ struct mxl862xx_port {
+@@ -272,6 +274,7 @@ struct mxl862xx_port {
        struct dsa_lag *lag;
        bool lag_tx_enabled;
        u8 lag_hash_bits;
-+      /* MTU */
 +      int mtu;
-       /* Hardware stats accumulation */
        struct mxl862xx_port_stats stats;
-       spinlock_t stats_lock;
+       spinlock_t stats_lock; /* protects stats accumulators */
+ };
similarity index 55%
rename from target/linux/generic/pending-6.12/760-23-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch
rename to target/linux/generic/pending-6.12/760-16-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch
index dfbf953754af0a31fae73719a7f6e78617ec683d..07a5f43a429ad901b5a1fafac9979af4c234187c 100644 (file)
@@ -1,7 +1,7 @@
-From 13a4c918cd9ded7207f38033511ab13f7aff9bd2 Mon Sep 17 00:00:00 2001
+From b723f7484006aadbaa51e16b870f3c98390605b1 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 25 Mar 2026 01:47:19 +0000
-Subject: [PATCH 23/26] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
+Date: Wed, 1 Apr 2026 13:43:12 +0100
+Subject: [PATCH 16/19] 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
@@ -18,29 +18,24 @@ bridge member rebuild since only the calling port is affected.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 29 +++++++++++++++++++++++++++--
- drivers/net/dsa/mxl862xx/mxl862xx.h |  6 ++++++
- 2 files changed, 33 insertions(+), 2 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 12 ++++++++++--
+ drivers/net/dsa/mxl862xx/mxl862xx.h |  +++++
+ 2 files changed, 15 insertions(+), 2 deletions(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -811,6 +811,15 @@ static int mxl862xx_sync_bridge_members(
-               __set_bit(mxl862xx_cpu_bridge_port_id(ds, port),
-                         priv->ports[port].portmap);
-+              /* Hairpin: include the port's own bridge port so bridged
-+               * frames can egress the ingress port.
-+               * For LAG ports this adds the LAG bridge port, which
-+               * propagates to the LAG BP in the second loop below.
-+               */
-+              if (priv->ports[port].hairpin)
-+                      __set_bit(mxl862xx_lag_bridge_port(priv, port),
-+                                priv->ports[port].portmap);
-+
-               err = mxl862xx_set_bridge_port(ds, port);
-               if (err)
-                       ret = err;
-@@ -3898,7 +3907,7 @@ static int mxl862xx_port_pre_bridge_flag
+@@ -759,6 +759,10 @@ static int __mxl862xx_set_bridge_port(st
+               }
+               mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+                                           mxl862xx_cpu_bridge_port_id(ds, port));
++              if (p->hairpin)
++                      mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
++                                                  mxl862xx_lag_bridge_port(priv,
++                                                                           port));
+       } else {
+               mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
+                                           mxl862xx_cpu_bridge_port_id(ds, port));
+@@ -3895,7 +3899,7 @@ static int mxl862xx_port_pre_bridge_flag
                                          struct netlink_ext_ack *extack)
  {
        if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
@@ -49,33 +44,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                return -EINVAL;
  
        return 0;
-@@ -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;
-       int ret;
-+      u16 bp;
-       if (flags.mask & BR_FLOOD) {
-               if (flags.val & BR_FLOOD)
-@@ -3940,7 +3950,22 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3937,7 +3941,11 @@ 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);
+-      if (block != old_block || (flags.mask & BR_LEARNING)) {
++      if (flags.mask & BR_HAIRPIN_MODE)
 +              priv->ports[port].hairpin = !!(flags.val & BR_HAIRPIN_MODE);
 +
-+              /* Hairpin adds/removes the port's own bridge port from its
-+               * cached portmap. Only this port is affected -- push the
-+               * updated portmap directly.
-+               */
-+              if (flags.val & BR_HAIRPIN_MODE)
-+                      __set_bit(bp, priv->ports[port].portmap);
-+              else
-+                      __clear_bit(bp, priv->ports[port].portmap);
-+      }
-+
 +      if ((block != old_block) ||
 +          (flags.mask & (BR_LEARNING | BR_HAIRPIN_MODE))) {
                priv->ports[port].flood_block = block;
@@ -83,7 +59,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                if (ret)
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -241,6 +241,10 @@ struct mxl862xx_port_stats {
+@@ -240,6 +240,10 @@ struct mxl862xx_port_stats {
   * @stats_lock:          protects accumulator reads in .get_stats64 against
   *                       concurrent updates from the polling work
   * @tag_8021q_vid:       currently assigned tag_8021q management VID
@@ -94,12 +70,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @ingress_mirror:      true when ingress mirroring is active on this port
   * @egress_mirror:       true when egress mirroring is active on this port
   * @lag:                 non-NULL when port is member of a LAG group;
-@@ -273,6 +277,8 @@ struct mxl862xx_port {
+@@ -269,6 +273,7 @@ struct mxl862xx_port {
        struct work_struct host_flood_work;
        u16 tag_8021q_vid;
        struct mxl862xx_evlan_block cpu_egress_evlan;
-+      /* Hairpin state */
 +      bool hairpin;
-       /* Mirror state */
        bool ingress_mirror;
        bool egress_mirror;
+       struct dsa_lag *lag;
similarity index 70%
rename from target/linux/generic/pending-6.12/760-24-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch
rename to target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch
index c67d95d5c51c439854fe90e1f298995d9f31163b..445384aed5ad4ff5aad24e3f48c1be1ca7b2018a 100644 (file)
@@ -1,7 +1,7 @@
-From d49d1f8bee29269def7593f980d0e08bfb5c3ef8 Mon Sep 17 00:00:00 2001
+From d8f20ba50ce0f93f34a41a9b833be76a5d1d2714 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Wed, 25 Mar 2026 01:51:33 +0000
-Subject: [PATCH 24/26] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
+Subject: [PATCH 17/19] 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
@@ -13,28 +13,22 @@ rebuilt via sync_bridge_members since multiple ports are affected.
 
 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
- drivers/net/dsa/mxl862xx/mxl862xx.c | 26 +++++++++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 2+++++++++++++++++++-
  drivers/net/dsa/mxl862xx/mxl862xx.h |  4 ++++
- 2 files changed, 29 insertions(+), 1 deletion(-)
+ 2 files changed, 23 insertions(+), 1 deletion(-)
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -802,6 +802,14 @@ static int mxl862xx_sync_bridge_members(
-                        */
-                       if (!mxl862xx_is_lag_master(priv, member))
+@@ -752,6 +752,8 @@ static int __mxl862xx_set_bridge_port(st
                                continue;
-+
-+                      /* Isolated ports cannot forward to each other.
-+                       * Non-isolated ports can reach everyone.
-+                       */
-+                      if (priv->ports[port].isolated &&
-+                          priv->ports[member].isolated)
+                       if (!mxl862xx_is_lag_master(priv, member_dp->index))
+                               continue;
++                      if (p->isolated && priv->ports[member_dp->index].isolated)
 +                              continue;
-+
-                       if (member != port) {
-                               bp = mxl862xx_lag_bridge_port(priv,
-                                                            member);
-@@ -3907,7 +3915,7 @@ static int mxl862xx_port_pre_bridge_flag
+                       mxl862xx_fw_portmap_set_bit(
+                               br_port_cfg.bridge_port_map,
+                               mxl862xx_lag_bridge_port(priv,
+@@ -3899,7 +3901,7 @@ static int mxl862xx_port_pre_bridge_flag
                                          struct netlink_ext_ack *extack)
  {
        if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
@@ -43,15 +37,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
                return -EINVAL;
  
        return 0;
-@@ -3920,6 +3928,7 @@ static int mxl862xx_port_bridge_flags(st
+@@ -3912,6 +3914,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;
 +      struct dsa_port *dp;
        int ret;
-       u16 bp;
  
-@@ -3972,6 +3981,21 @@ static int mxl862xx_port_bridge_flags(st
+       if (flags.mask & BR_FLOOD) {
+@@ -3952,6 +3955,21 @@ static int mxl862xx_port_bridge_flags(st
                        return ret;
        }
  
@@ -75,7 +69,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -214,6 +214,9 @@ struct mxl862xx_port_stats {
+@@ -213,6 +213,9 @@ struct mxl862xx_port_stats {
   * @flood_block:         bitmask of firmware meter indices that are currently
   *                       rate-limiting flood traffic on this port (zero-rate
   *                       meters used to block flooding)
@@ -85,11 +79,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
   * @learning:            true when address learning is enabled on this port
   * @setup_done:          set at end of port_setup, cleared at start of
   *                       port_teardown; guards deferred work against
-@@ -261,6 +264,7 @@ struct mxl862xx_port {
+@@ -259,6 +262,7 @@ struct mxl862xx_port {
+       struct mxl862xx_priv *priv;
        u16 fid;
-       DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS);
        unsigned long flood_block;
 +      bool isolated;
        bool learning;
        bool setup_done;
-       /* VLAN state */
+       u16 pvid;
similarity index 87%
rename from target/linux/generic/pending-6.12/760-25-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
rename to target/linux/generic/pending-6.12/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
index 6e5d25069d4ea851a85d80dd5a11f5191b36f4e1..bd2f15ca08d06390471f58199f97a17f63797649 100644 (file)
@@ -1,7 +1,7 @@
-From c2fb7f0df63ac994850f766e7f2eb50c6c5ef2cf Mon Sep 17 00:00:00 2001
+From 8856f7610167a3005ecef401c8528111d153b554 Mon Sep 17 00:00:00 2001
 From: Daniel Golle <daniel@makrotopia.org>
 Date: Tue, 24 Mar 2026 18:17:49 +0000
-Subject: [PATCH 25/26] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
+Subject: [PATCH 18/19] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
  workaround for old firmware
 
 Re-introduce the mxl862xx_disable_fw_global_rules() function that
@@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
 +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
-@@ -477,6 +477,43 @@ static int mxl862xx_setup_drop_meter(str
+@@ -449,6 +449,43 @@ static int mxl862xx_setup_drop_meter(str
        return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
  }
  
@@ -62,9 +62,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      return 0;
 +}
  
- /* 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
-@@ -1109,9 +1146,11 @@ static int mxl862xx_setup(struct dsa_swi
+ /* Per-CTP offsets for protocol trap rules. Each port's CTP flow-table
+  * block is pre-allocated by the firmware during init (44 entries per
+@@ -1114,9 +1151,11 @@ static int mxl862xx_setup(struct dsa_swi
        if (ret)
                return ret;
  
diff --git a/target/linux/generic/pending-6.12/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch b/target/linux/generic/pending-6.12/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch
new file mode 100644 (file)
index 0000000..d0ad2eb
--- /dev/null
@@ -0,0 +1,25 @@
+From df0c747216063041ba0d786b01f9b1e2aba5316a Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 22 Apr 2026 15:15:52 +0100
+Subject: [PATCH] DO NOT SUBMIT: net: dsa: mxl862xx: increase CMD timeout
+
+Lift the command timeout by 10x from 500ms to 5s.
+This is done because older firmware can be extremely slow to respond
+and cause -ETIMEOUT during setup, or crash the PHY state machine.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/dsa/mxl862xx/mxl862xx-host.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c
++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
+@@ -207,7 +207,7 @@ static int mxl862xx_busy_wait(struct mxl
+       int val;
+       return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
+-                                !(val & CTRL_BUSY_MASK), 15, 500000);
++                                !(val & CTRL_BUSY_MASK), 50, 5000000);
+ }
+ /* Issue a firmware command with CRC-6 protection on the ctrl and len_ret