generic: 6.18: update MxL862xx DSA switch driver

Update driver to be ready for the upcoming firmware release.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
Daniel Golle
2026-05-21 18:19:05 +01:00
parent 5b69e6a4a6
commit 028dc3f57a
22 changed files with 1336 additions and 1449 deletions
@@ -1,4 +1,4 @@
From 60a23e663e0c607ae4ed871aaa24d257051ad557 Mon Sep 17 00:00:00 2001
From 1ad83956d550a252fffc473a191dbe155c4a383e Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 17:56:35 +0000
Subject: [PATCH 01/19] net: dsa: mxl862xx: store firmware version for feature
@@ -8,15 +8,15 @@ Query the firmware version at init (already done in wait_ready),
cache it in priv->fw_version, and provide MXL862XX_FW_VER_MIN()
for version-gated code paths throughout the driver.
The union mxl862xx_fw_version lays out major/minor/revision so
that the u32 raw field compares with natural version ordering on
both big- and little-endian machines.
MXL862XX_FW_VER() packs major/minor/revision into a u32 with
bitwise shifts so that versions compare with natural ordering,
independent of host endianness.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx.c | 3 +++
drivers/net/dsa/mxl862xx/mxl862xx.h | 36 +++++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
drivers/net/dsa/mxl862xx/mxl862xx.h | 23 +++++++++++++++++++++++
2 files changed, 26 insertions(+)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -40,46 +40,33 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#include <linux/mdio.h>
#include <linux/workqueue.h>
#include <net/dsa.h>
@@ -241,6 +242,38 @@ struct mxl862xx_port {
@@ -241,6 +242,25 @@ struct mxl862xx_port {
spinlock_t stats_lock; /* protects stats accumulators */
};
+/**
+ * union mxl862xx_fw_version - firmware version for comparison and display
+ * struct mxl862xx_fw_version - firmware version for comparison and display
+ * @major: firmware major version
+ * @minor: firmware minor version
+ * @revision: firmware revision number
+ * @raw: combined u32 for direct >= comparison (major most significant)
+ *
+ * The struct layout places major in the most-significant byte of the
+ * u32 on both big- and little-endian machines, so raw values compare
+ * with the natural major > minor > revision ordering.
+ */
+union mxl862xx_fw_version {
+ struct {
+#if defined(__BIG_ENDIAN)
+ u8 major;
+ u8 minor;
+ u16 revision;
+#elif defined(__LITTLE_ENDIAN)
+ u16 revision;
+ u8 minor;
+ u8 major;
+#endif
+ };
+ u32 raw;
+struct mxl862xx_fw_version {
+ u8 major;
+ u8 minor;
+ u16 revision;
+};
+
+#define MXL862XX_FW_VER(maj, min, rev) \
+ ((union mxl862xx_fw_version){ .major = (maj), .minor = (min), \
+ .revision = (rev) }).raw
+ (((u32)(maj) << 24) | ((u32)(min) << 16) | (rev))
+#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \
+ ((priv)->fw_version.raw >= MXL862XX_FW_VER(maj, min, rev))
+ (MXL862XX_FW_VER((priv)->fw_version.major, (priv)->fw_version.minor, \
+ (priv)->fw_version.revision) >= \
+ MXL862XX_FW_VER(maj, min, rev))
+
/* Bit indices for struct mxl862xx_priv::flags */
#define MXL862XX_FLAG_CRC_ERR 0
#define MXL862XX_FLAG_WORK_STOPPED 1
@@ -258,6 +291,8 @@ struct mxl862xx_port {
@@ -258,6 +278,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,11 +75,11 @@ 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
@@ -275,6 +310,7 @@ struct mxl862xx_priv {
@@ -275,6 +297,7 @@ struct mxl862xx_priv {
struct work_struct crc_err_work;
unsigned long flags;
u16 drop_meter;
+ union mxl862xx_fw_version fw_version;
+ struct mxl862xx_fw_version fw_version;
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
@@ -1,4 +1,4 @@
From cefa0447dc95a4ddd5093f7b8cf35e654870283f Mon Sep 17 00:00:00 2001
From eaf472bfc89692cd3cc5e0298c90791f4c1c3244 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 21:39:30 +0000
Subject: [PATCH 02/19] net: dsa: mxl862xx: move phylink stubs to
@@ -1,4 +1,4 @@
From 3c1d77006daca1df20d612850535bc6050e266ee Mon Sep 17 00:00:00 2001
From 483be884b2fdee28ac70458067c45a1369939144 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 26 Mar 2026 01:50:00 +0000
Subject: [PATCH 03/19] net: dsa: mxl862xx: move API macros to mxl862xx-host.h
@@ -1,32 +1,54 @@
From 3659914c43a587a1ca6418867834831aa518ac35 Mon Sep 17 00:00:00 2001
From 74044a2d2e62fcc3ee2b6cf22742fa94609c529e Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:14:33 +0000
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
and SIGNAL_DETECT firmware commands to read TX/RX equalization
coefficients, DFE taps, and link-level signal status.
Expose SerDes equalization parameters as ethtool statistics on
ports 9-16 (XPCS-backed ports). Uses the XPCS EQ_GET firmware
command to read TX/RX equalization coefficients and DFE taps.
The 19 additional stats (serdes_tx_*, serdes_rx_*, serdes_pma_link,
serdes_link_fault, serdes_in_reset) are appended after the standard
RMON counters and gated on firmware >= 1.0.80.
The 15 additional stats (serdes_tx_*, serdes_rx_*) are appended
after the standard RMON counters and gated on firmware >= 1.0.84.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 88 +++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 +
drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 93 +++++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 92 +++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 +
drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 97 +++++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 3 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 6 +-
5 files changed, 191 insertions(+), 1 deletion(-)
5 files changed, 198 insertions(+), 1 deletion(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -1812,4 +1812,92 @@ struct mxl862xx_xpcs_reset_cfg {
@@ -1668,4 +1668,96 @@ struct mxl862xx_xpcs_pcs_link_up {
__le16 result;
} __packed;
+/**
+ * struct mxl862xx_xpcs_loopback_cfg - loopback control
+ * @port_id: XPCS port index
+ * @mode: loopback mode. See &enum mxl862xx_xpcs_loopback_mode
+ * @result: firmware result
+ */
+struct mxl862xx_xpcs_loopback_cfg {
+ u8 port_id;
+ u8 mode; /* enum mxl862xx_xpcs_loopback_mode */
+ __le16 result;
+} __packed;
+
+/**
+ * struct mxl862xx_xpcs_reset_cfg - XPCS reset
+ * @port_id: XPCS port index
+ * @reset_type: reset type. See &enum mxl862xx_xpcs_reset_type
+ * @result: firmware result
+ */
+struct mxl862xx_xpcs_reset_cfg {
+ u8 port_id;
+ u8 reset_type; /* enum mxl862xx_xpcs_reset_type */
+ __le16 result;
+} __packed;
+
+/**
+ * struct mxl862xx_xpcs_eq_item - single equalization parameter
+ * @value: current initial value
@@ -94,42 +116,21 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ struct mxl862xx_xpcs_tx_eq_info tx;
+ struct mxl862xx_xpcs_rx_eq_info rx;
+} __packed;
+
+/**
+ * struct mxl862xx_xpcs_signal_detect - signal detect status
+ * @port_id: XPCS port index (0 or 1)
+ * @rx_signal: RX signal detected
+ * @pma_link: PMA link up
+ * @link_fault: PCS link fault
+ * @in_reset: XPCS in reset
+ * @result: firmware result
+ */
+struct mxl862xx_xpcs_signal_detect {
+ u8 port_id:2;
+ u8 rx_signal:1;
+ u8 pma_link:1;
+ u8 link_fault:1;
+ u8 in_reset:1;
+ u8 __rsv:2;
+ u8 __pad;
+ __le16 result;
+} __packed;
+
#endif /* __MXL862XX_API_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -83,6 +83,8 @@
#define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7)
@@ -79,6 +79,7 @@
#define MXL862XX_XPCS_PCS_LINK_UP (MXL862XX_XPCS_MAGIC + 0x7)
#define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8)
#define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9)
+#define MXL862XX_XPCS_EQ_GET (MXL862XX_XPCS_MAGIC + 0xc)
+#define MXL862XX_XPCS_SIGNAL_DETECT (MXL862XX_XPCS_MAGIC + 0xd)
#define MMD_API_MAXIMUM_ID 0x7fff
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
@@ -456,3 +456,96 @@ const struct phylink_mac_ops mxl862xx_ph
@@ -414,3 +414,100 @@ const struct phylink_mac_ops mxl862xx_ph
.mac_link_up = mxl862xx_phylink_mac_link_up,
.mac_select_pcs = mxl862xx_phylink_mac_select_pcs,
};
@@ -152,18 +153,19 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ "serdes_rx_dfe_bypass",
+ "serdes_rx_adapt_mode",
+ "serdes_rx_adapt_sel",
+ "serdes_rx_signal",
+ "serdes_pma_link",
+ "serdes_link_fault",
+ "serdes_in_reset",
+};
+
+static bool mxl862xx_port_has_serdes_stats(struct dsa_switch *ds, int port)
+{
+ struct mxl862xx_priv *priv = ds->priv;
+
+ return port >= 9 && port <= 16 &&
+ MXL862XX_FW_VER_MIN(priv, 1, 0, 80);
+ /* Firmware reads EQ/signal status per XPCS, not per lane. Expose
+ * the stats only on the slot-0 port of each XPCS (9 and 13) so
+ * users don't see four duplicate copies labelled as if they were
+ * independent per-port readings.
+ */
+ return (port == 9 || port == 13) &&
+ MXL862XX_FW_VER_MIN(priv, 1, 0, 84);
+}
+
+int mxl862xx_serdes_stats_count(struct dsa_switch *ds, int port)
@@ -185,50 +187,53 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ ethtool_puts(&data, mxl862xx_serdes_stats[i]);
+}
+
+static u64 mxl862xx_eq_effective(const struct mxl862xx_xpcs_eq_item *it)
+{
+ return it->ovrd_en ? it->ovrd : it->value;
+}
+
+void mxl862xx_serdes_get_stats(struct dsa_switch *ds, int port, u64 *data)
+{
+ struct mxl862xx_xpcs_eq_get eq = {
+ .port_id = mxl862xx_xpcs_port_id(port),
+ .port_id = MXL862XX_SERDES_PORT_ID(port),
+ };
+ struct mxl862xx_xpcs_signal_detect sig = {};
+ int ret;
+
+ if (!mxl862xx_port_has_serdes_stats(ds, port))
+ return;
+
+ sig.port_id = mxl862xx_xpcs_port_id(port);
+
+ if (!MXL862XX_API_READ(ds->priv, MXL862XX_XPCS_EQ_GET, eq)) {
+ *data++ = eq.tx.main.value;
+ *data++ = eq.tx.pre.value;
+ *data++ = eq.tx.post.value;
+ *data++ = eq.tx.iboost_lvl.value;
+ *data++ = eq.tx.vboost_lvl.value;
+ *data++ = eq.tx.vboost_en.value;
+ *data++ = eq.rx.att_lvl.value;
+ *data++ = eq.rx.vga1_gain.value;
+ *data++ = eq.rx.vga2_gain.value;
+ *data++ = eq.rx.ctle_boost.value;
+ *data++ = eq.rx.ctle_pole.value;
+ *data++ = eq.rx.dfe_tap1.value;
+ *data++ = eq.rx.dfe_bypass.value;
+ *data++ = eq.rx.adapt_mode.value;
+ *data++ = eq.rx.adapt_sel.value;
+ } else {
+ data += 15;
+ ret = MXL862XX_API_READ(ds->priv, MXL862XX_XPCS_EQ_GET, eq);
+ if (ret) {
+ dev_err(ds->dev,
+ "port %d: XPCS EQ_GET failed: %d\n", port, ret);
+ return;
+ }
+ if ((s16)le16_to_cpu(eq.result) < 0) {
+ dev_err(ds->dev,
+ "port %d: XPCS EQ_GET firmware result: %d\n",
+ port, (s16)le16_to_cpu(eq.result));
+ return;
+ }
+
+ if (!MXL862XX_API_READ(ds->priv, MXL862XX_XPCS_SIGNAL_DETECT, sig)) {
+ *data++ = sig.rx_signal;
+ *data++ = sig.pma_link;
+ *data++ = sig.link_fault;
+ *data++ = sig.in_reset;
+ } else {
+ data += 4;
+ }
+ *data++ = mxl862xx_eq_effective(&eq.tx.main);
+ *data++ = mxl862xx_eq_effective(&eq.tx.pre);
+ *data++ = mxl862xx_eq_effective(&eq.tx.post);
+ *data++ = mxl862xx_eq_effective(&eq.tx.iboost_lvl);
+ *data++ = mxl862xx_eq_effective(&eq.tx.vboost_lvl);
+ *data++ = mxl862xx_eq_effective(&eq.tx.vboost_en);
+ *data++ = mxl862xx_eq_effective(&eq.rx.att_lvl);
+ *data++ = mxl862xx_eq_effective(&eq.rx.vga1_gain);
+ *data++ = mxl862xx_eq_effective(&eq.rx.vga2_gain);
+ *data++ = mxl862xx_eq_effective(&eq.rx.ctle_boost);
+ *data++ = mxl862xx_eq_effective(&eq.rx.ctle_pole);
+ *data++ = mxl862xx_eq_effective(&eq.rx.dfe_tap1);
+ *data++ = mxl862xx_eq_effective(&eq.rx.dfe_bypass);
+ *data++ = mxl862xx_eq_effective(&eq.rx.adapt_mode);
+ *data++ = mxl862xx_eq_effective(&eq.rx.adapt_sel);
+}
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
@@ -12,5 +12,8 @@ void mxl862xx_phylink_get_caps(struct ds
@@ -17,5 +17,8 @@ void mxl862xx_phylink_get_caps(struct ds
struct phylink_config *config);
void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs,
int port);
@@ -239,7 +244,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
@@ -1775,6 +1775,8 @@ static void mxl862xx_get_strings(struct
@@ -1776,6 +1776,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 +253,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
@@ -1782,7 +1784,7 @@ static int mxl862xx_get_sset_count(struc
@@ -1783,7 +1785,7 @@ static int mxl862xx_get_sset_count(struc
if (sset != ETH_SS_STATS)
return 0;
@@ -257,7 +262,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
static int mxl862xx_read_rmon(struct dsa_switch *ds, int port,
@@ -1818,6 +1820,8 @@ static void mxl862xx_get_ethtool_stats(s
@@ -1819,6 +1821,8 @@ static void mxl862xx_get_ethtool_stats(s
else
*data++ = le64_to_cpu(*(__le64 *)field);
}
@@ -1,208 +0,0 @@
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 06/19] net: dsa: mxl862xx: add SerDes self-test via PRBS and
BERT
Implement the dsa_switch_ops.self_test callback for SerDes ports
(9-16). Two loopback tests are run:
1. PCS-level PRBS31: enables TX/RX PRBS31 pattern at the PCS layer,
waits 100ms, then reads the error counter.
2. SerDes-level BERT PRBS31: enables TX/RX BERT with PRBS31 pattern
at the SerDes layer, waits 100ms, then reads the error counter.
Both tests clean up (disable pattern generators) regardless of outcome.
Gated on firmware >= 1.0.80 via mxl862xx_port_has_serdes_stats().
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 45 +++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 +
drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 85 +++++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 3 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 1 +
5 files changed, 136 insertions(+)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -1900,4 +1900,49 @@ struct mxl862xx_xpcs_signal_detect {
__le16 result;
} __packed;
+/**
+ * struct mxl862xx_xpcs_prbs_cfg - PCS-level PRBS31 test pattern
+ * @port_id: XPCS port index (0 or 1)
+ * @tx_en: TX PRBS31 enable
+ * @rx_en: RX PRBS31 enable
+ * @read_err: read error count
+ * @rx_err_cnt: RX PRBS31 error count (valid when read_err=1)
+ * @result: firmware result
+ */
+struct mxl862xx_xpcs_prbs_cfg {
+ u8 port_id:2;
+ u8 tx_en:1;
+ u8 rx_en:1;
+ u8 read_err:1;
+ u8 __rsv:3;
+ u8 __pad;
+ __le16 rx_err_cnt;
+ __le16 result;
+} __packed;
+
+/**
+ * struct mxl862xx_xpcs_bert_cfg - SerDes-level BERT test pattern
+ * @port_id: XPCS port index (0 or 1)
+ * @tx_en: TX BERT enable
+ * @rx_en: RX BERT enable
+ * @read_err: read RX error count
+ * @clear_err: clear RX error counter
+ * @insert_err: insert one TX error
+ * @pattern: PRBS pattern type (1-7; 0 = disable)
+ * @rx_err_cnt: RX BERT error count (valid when read_err=1)
+ * @result: firmware result
+ */
+struct mxl862xx_xpcs_bert_cfg {
+ u8 port_id:2;
+ u8 tx_en:1;
+ u8 rx_en:1;
+ u8 read_err:1;
+ u8 clear_err:1;
+ u8 insert_err:1;
+ u8 __rsv:1;
+ u8 pattern;
+ __le16 rx_err_cnt;
+ __le16 result;
+} __packed;
+
#endif /* __MXL862XX_API_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -83,6 +83,8 @@
#define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7)
#define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8)
#define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9)
+#define MXL862XX_XPCS_PRBS_CFG (MXL862XX_XPCS_MAGIC + 0xa)
+#define MXL862XX_XPCS_BERT_CFG (MXL862XX_XPCS_MAGIC + 0xb)
#define MXL862XX_XPCS_EQ_GET (MXL862XX_XPCS_MAGIC + 0xc)
#define MXL862XX_XPCS_SIGNAL_DETECT (MXL862XX_XPCS_MAGIC + 0xd)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
@@ -549,3 +549,88 @@ void mxl862xx_serdes_get_stats(struct ds
data += 4;
}
}
+
+void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port,
+ struct ethtool_test *etest, u64 *data)
+{
+ struct mxl862xx_xpcs_prbs_cfg prbs = {};
+ struct mxl862xx_xpcs_bert_cfg bert = {};
+ struct mxl862xx_priv *priv = ds->priv;
+ int xpcs_id = mxl862xx_xpcs_port_id(port);
+ int i = 0;
+ int ret;
+
+ if (!mxl862xx_port_has_serdes_stats(ds, port))
+ return;
+
+ /* Test 1: PCS PRBS31 */
+ prbs.port_id = xpcs_id;
+ prbs.tx_en = 1;
+ prbs.rx_en = 1;
+ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PRBS_CFG, prbs);
+ if (ret) {
+ data[i++] = 1;
+ goto skip_prbs;
+ }
+
+ msleep(100);
+
+ memset(&prbs, 0, sizeof(prbs));
+ prbs.port_id = xpcs_id;
+ prbs.read_err = 1;
+ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PRBS_CFG, prbs);
+
+ /* Disable PRBS */
+ memset(&prbs, 0, sizeof(prbs));
+ prbs.port_id = xpcs_id;
+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PRBS_CFG, prbs);
+
+ if (ret) {
+ data[i++] = 1;
+ } else {
+ data[i] = le16_to_cpu(prbs.rx_err_cnt) ? 1 : 0;
+ if (data[i])
+ etest->flags |= ETH_TEST_FL_FAILED;
+ i++;
+ }
+
+skip_prbs:
+ /* Test 2: SerDes BERT PRBS31 -- clear error counter first */
+ bert.port_id = xpcs_id;
+ bert.clear_err = 1;
+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert);
+
+ /* Enable BERT with PRBS31 pattern */
+ memset(&bert, 0, sizeof(bert));
+ bert.port_id = xpcs_id;
+ bert.tx_en = 1;
+ bert.rx_en = 1;
+ bert.pattern = 6; /* PRBS31 */
+ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert);
+ if (ret) {
+ data[i++] = 1;
+ return;
+ }
+
+ msleep(100);
+
+ /* Read error count */
+ memset(&bert, 0, sizeof(bert));
+ bert.port_id = xpcs_id;
+ bert.read_err = 1;
+ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_BERT_CFG, bert);
+
+ /* Disable BERT */
+ memset(&bert, 0, sizeof(bert));
+ bert.port_id = xpcs_id;
+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert);
+
+ if (ret) {
+ data[i++] = 1;
+ } else {
+ data[i] = le16_to_cpu(bert.rx_err_cnt) ? 1 : 0;
+ if (data[i])
+ etest->flags |= ETH_TEST_FL_FAILED;
+ i++;
+ }
+}
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
@@ -3,6 +3,7 @@
#ifndef __MXL862XX_PHYLINK_H
#define __MXL862XX_PHYLINK_H
+#include <linux/ethtool.h>
#include <linux/phylink.h>
#include "mxl862xx.h"
@@ -15,5 +16,7 @@ void mxl862xx_setup_pcs(struct mxl862xx_
int mxl862xx_serdes_stats_count(struct dsa_switch *ds, int port);
void mxl862xx_serdes_get_strings(struct dsa_switch *ds, int port, u8 *data);
void mxl862xx_serdes_get_stats(struct dsa_switch *ds, int port, u64 *data);
+void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port,
+ struct ethtool_test *etest, u64 *data);
#endif /* __MXL862XX_PHYLINK_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -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,
};
static int mxl862xx_probe(struct mdio_device *mdiodev)
@@ -1,7 +1,7 @@
From e6defbd42db6e64c2bb203f82b7d7f8c0691a052 Mon Sep 17 00:00:00 2001
From 50a84327282120a51af12da91214ed9e06f585cb Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:13 +0000
Subject: [PATCH 07/19] net: dsa: mxl862xx: trap link-local and multicast
Subject: [PATCH 06/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
@@ -19,10 +19,21 @@ 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)
Rules are written through the firmware's logical-index API,
GSW_TFLOW_PCERULELOGICWRITE. The firmware translates the logical
index in @rule->pattern.index into a physical position within the
per-CTP block selected by @rule->region and @rule->logicalportid,
and grows the hardware block size on demand as rules are inserted.
A single dispatch helper, mxl862xx_pce_rule_write(), centralises the
firmware command so the trap callers do not need to know which API
variant is used. Four logical indices are consumed in each port's
per-CTP block (index 0 is reserved by the firmware for its
flow-control marking rule):
index 1 -- IEEE 802.1D link-local (dst 01:80:c2:00:00:0x)
index 2 -- IPv4 IGMP (IP protocol 2, all versions)
index 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
index 4 -- ICMPv6 type 143 (MLDv2 Listener Report)
Add the PCE rule firmware API structures, command definitions, and
the rule block allocation interface.
@@ -30,15 +41,15 @@ the rule block allocation interface.
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 | 186 +++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 6 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 198 +++++++
drivers/net/dsa/mxl862xx/mxl862xx.h | 8 +
4 files changed, 883 insertions(+)
4 files changed, 896 insertions(+)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -1420,6 +1420,690 @@ struct mxl862xx_port_link_cfg {
u8 lpi;
@@ -1184,6 +1184,690 @@ struct mxl862xx_ctp_port_assignment {
__le16 bridge_port_id;
} __packed;
+/* PCE (Packet Classification Engine) rule structures.
@@ -694,7 +705,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * @action: Forwarding action (portmap, cross-state, etc.)
+ *
+ * This structure is passed to the firmware via the MDIO data
+ * buffer using the %MXL862XX_TFLOW_PCERULEWRITE command.
+ * buffer using the %MXL862XX_TFLOW_PCERULELOGICWRITE command.
+ * The binary layout must exactly match the firmware's
+ * GSW_PCE_rule_t (466 bytes, packed, little-endian scalars).
+ */
@@ -738,13 +749,14 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#define MXL862XX_BRDG_MAGIC 0x300
#define MXL862XX_BRDGPORT_MAGIC 0x400
#define MXL862XX_CTP_MAGIC 0x500
@@ -31,6 +32,10 @@
@@ -30,6 +31,11 @@
#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa)
#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
+#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
+#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4)
+#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5)
+#define MXL862XX_TFLOW_PCERULELOGICWRITE \
+ (MXL862XX_TFLOW_MAGIC + 0xa)
+
#define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1)
#define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2)
@@ -771,21 +783,35 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return 0;
not_ready_yet:
@@ -381,6 +384,158 @@ static int mxl862xx_setup_drop_meter(str
@@ -381,6 +384,170 @@ static int mxl862xx_setup_drop_meter(str
return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
}
+
+/* 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.
+/* Per-CTP logical indices for protocol trap rules.
+ *
+ * The firmware pre-allocates a per-CTP PCE block for every port during
+ * init and reserves logical index 0 for its flow-control marking rule.
+ * Logical indices 1-4 are used here for link-local and multicast
+ * snooping traps; the firmware grows the underlying hardware block on
+ * demand as PCERULELOGICWRITE inserts new entries.
+ */
+#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
+#define MXL862XX_LINK_LOCAL_CTP_INDEX 1
+#define MXL862XX_IGMP_CTP_INDEX 2
+#define MXL862XX_MLDV1_CTP_INDEX 3
+#define MXL862XX_MLDV2_CTP_INDEX 4
+
+/* Install (or overwrite) a PCE rule via the firmware's logical-index
+ * API. The firmware translates the logical index in @rule->pattern.index
+ * to a physical position within the block selected by @rule->region and
+ * @rule->logicalportid, and expands the hardware block size as needed.
+ */
+static int mxl862xx_pce_rule_write(struct mxl862xx_priv *priv,
+ struct mxl862xx_pce_rule *rule)
+{
+ return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULELOGICWRITE,
+ *rule);
+}
+
+/* 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
@@ -826,12 +852,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * The firmware does not install this rule by default because its own
+ * STP module is not used when DSA manages STP.
+ *
+ * The rule is written into the port's per-CTP flow table at offset 1.
+ * The firmware already allocates a 44-entry block for every CTP during
+ * init (8 entries exposed initially, expandable), so no dynamic
+ * allocation via PCERULEALLOC is needed. Using region=CTP causes the
+ * firmware to translate the CTP-relative offset into an absolute
+ * hardware index.
+ * The rule is written into the port's per-CTP flow table at logical
+ * index 1 using PCERULELOGICWRITE, which selects the per-CTP block via
+ * @region and @logicalportid and grows the block on demand.
+ *
+ * Cross-state is enabled so that link-local frames reach the CPU even
+ * when the bridge port is in BLOCKING or LEARNING state.
@@ -844,7 +867,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ rule.logicalportid = port;
+ rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP);
+
+ rule.pattern.index = cpu_to_le16(MXL862XX_LINK_LOCAL_CTP_OFFSET);
+ rule.pattern.index = cpu_to_le16(MXL862XX_LINK_LOCAL_CTP_INDEX);
+ rule.pattern.enable = 1;
+ rule.pattern.mac_dst_enable = 1;
+ memcpy(rule.pattern.mac_dst, eth_reserved_addr_base, ETH_ALEN);
@@ -852,7 +875,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+ mxl862xx_fill_cpu_trap_action(ds, port, &rule);
+
+ return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
+ return mxl862xx_pce_rule_write(priv, &rule);
+}
+
+/* Install PCE rules that trap IGMP and MLD frames to the CPU port for
@@ -862,10 +885,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * 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)
+ * Three rules are installed per port at logical indices in the per-CTP
+ * block:
+ * index 2 -- IPv4 IGMP (IP protocol 2, all versions)
+ * index 3 -- ICMPv6 types 130-132 (MLDv1 query, report, done)
+ * index 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)
@@ -883,12 +907,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ 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.index = cpu_to_le16(MXL862XX_IGMP_CTP_INDEX);
+ rule.pattern.enable = 1;
+ rule.pattern.protocol = IPPROTO_IGMP;
+ rule.pattern.protocol_enable = 1;
+
+ ret = MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
+ ret = mxl862xx_pce_rule_write(priv, &rule);
+ if (ret)
+ return ret;
+
@@ -896,7 +920,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * 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.index = cpu_to_le16(MXL862XX_MLDV1_CTP_INDEX);
+ rule.pattern.enable = 1;
+ rule.pattern.protocol = IPPROTO_ICMPV6;
+ rule.pattern.protocol_enable = 1;
@@ -907,7 +931,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ 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);
+ ret = mxl862xx_pce_rule_write(priv, &rule);
+ if (ret)
+ return ret;
+
@@ -915,7 +939,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * 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.index = cpu_to_le16(MXL862XX_MLDV2_CTP_INDEX);
+ rule.pattern.enable = 1;
+ rule.pattern.protocol = IPPROTO_ICMPV6;
+ rule.pattern.protocol_enable = 1;
@@ -924,13 +948,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ 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);
+ return mxl862xx_pce_rule_write(priv, &rule);
+}
+
static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
{
struct mxl862xx_bridge_port_config br_port_cfg = {};
@@ -683,6 +838,28 @@ static int mxl862xx_setup(struct dsa_swi
@@ -684,6 +851,28 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
@@ -959,7 +983,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
schedule_delayed_work(&priv->stats_work,
MXL862XX_STATS_POLL_INTERVAL);
@@ -1382,6 +1559,15 @@ static int mxl862xx_port_setup(struct ds
@@ -1383,6 +1572,15 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
@@ -977,7 +1001,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
@@ -319,6 +319,13 @@ union mxl862xx_fw_version {
@@ -317,6 +317,13 @@ struct 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
@@ -991,7 +1015,7 @@ 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
*/
@@ -335,6 +342,7 @@ struct mxl862xx_priv {
@@ -334,6 +341,7 @@ struct mxl862xx_priv {
u16 evlan_ingress_size;
u16 evlan_egress_size;
u16 vf_block_size;
@@ -1,7 +1,7 @@
From 264838ee8ee3ad611a84df96d889429f1ded2148 Mon Sep 17 00:00:00 2001
From 6126dff8c28e71091c23fd0cc2f55b1a50e117a6 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:51:21 +0000
Subject: [PATCH 08/19] net: dsa: mxl862xx: warn about old firmware default PCE
Subject: [PATCH 07/19] net: dsa: mxl862xx: warn about old firmware default PCE
rules
Firmware versions older than 1.0.80 install global PCE rules at
@@ -19,7 +19,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -860,6 +860,10 @@ static int mxl862xx_setup(struct dsa_swi
@@ -873,6 +873,10 @@ static int mxl862xx_setup(struct dsa_swi
true, true, true);
if (ret)
return ret;
@@ -1,7 +1,7 @@
From d7ed9b681298d206d81e9cf3c93558d6e912ae88 Mon Sep 17 00:00:00 2001
From 69a0946ef48325b85f4a331152d6340f39083053 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:58:04 +0000
Subject: [PATCH 09/20] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx
Subject: [PATCH 08/19] 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 | 1678 ++++++++++++++++++++---
drivers/net/dsa/mxl862xx/mxl862xx.c | 1677 ++++++++++++++++++++---
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 | 65 +
9 files changed, 1803 insertions(+), 186 deletions(-)
9 files changed, 1804 insertions(+), 184 deletions(-)
create mode 100644 net/dsa/tag_mxl862xx_8021q.c
--- a/drivers/net/dsa/mxl862xx/Kconfig
@@ -43,10 +43,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
These switches have two 10GE SerDes interfaces, one typically
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -1185,6 +1185,227 @@ struct mxl862xx_ctp_port_assignment {
@@ -1184,6 +1184,227 @@ struct mxl862xx_ctp_port_assignment {
__le16 bridge_port_id;
} __packed;
/**
+/**
+ * enum mxl862xx_ctp_port_config_mask - CTP Port Configuration Mask
+ * @MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID:
+ * Mask for bridge_port_id.
@@ -267,10 +268,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ u8 egress_mirror_enable;
+} __packed;
+
+/**
* enum mxl862xx_port_duplex - Ethernet port duplex status
* @MXL862XX_DUPLEX_FULL: Port operates in full-duplex mode
* @MXL862XX_DUPLEX_HALF: Port operates in half-duplex mode
/* PCE (Packet Classification Engine) rule structures.
*
* Binary layout must exactly match the firmware's GSW_PCE_rule_t
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -47,12 +47,14 @@
@@ -387,9 +387,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
/* PHY access via firmware relay */
@@ -396,6 +459,78 @@ static int mxl862xx_setup_drop_meter(str
#define MXL862XX_MLDV1_CTP_OFFSET 3
#define MXL862XX_MLDV2_CTP_OFFSET 4
@@ -410,6 +473,78 @@ static int mxl862xx_pce_rule_write(struc
*rule);
}
+/**
+ * mxl862xx_cpu_bridge_port_id - Get the bridge port ID for CPU-side forwarding
@@ -466,7 +466,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
/* 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
@@ -418,24 +553,36 @@ static int mxl862xx_pce_rule_write(struc
* 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,
@@ -505,17 +505,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
rule->action.cross_state_action =
cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS);
@@ -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)
{
@@ -552,11 +696,17 @@ static int mxl862xx_set_bridge_port(stru
@@ -564,11 +711,17 @@ static int mxl862xx_set_bridge_port(stru
return 0;
if (dsa_port_is_cpu(dp)) {
@@ -538,7 +528,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
} else if (dp->bridge) {
dsa_switch_for_each_bridge_member(member_dp, ds,
@@ -567,10 +717,10 @@ static int mxl862xx_set_bridge_port(stru
@@ -579,10 +732,10 @@ static int mxl862xx_set_bridge_port(stru
member_dp->index);
}
mxl862xx_fw_portmap_set_bit(br_port_cfg.bridge_port_map,
@@ -551,7 +541,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
p->flood_block = 0;
p->learning = false;
}
@@ -774,10 +924,12 @@ static void mxl862xx_free_bridge(struct
@@ -786,10 +939,12 @@ static void mxl862xx_free_bridge(struct
static int mxl862xx_setup(struct dsa_switch *ds)
{
struct mxl862xx_priv *priv = ds->priv;
@@ -566,16 +556,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
ret = mxl862xx_reset(priv);
if (ret)
@@ -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);
@@ -803,7 +958,7 @@ static int mxl862xx_setup(struct dsa_swi
mxl862xx_setup_pcs(priv, &priv->serdes_ports[i],
i + MXL862XX_FIRST_SERDES_PORT);
- /* Calculate Extended VLAN block sizes.
+ /* Calculate Extended VLAN and VLAN Filter block sizes.
* With VLAN Filter handling VID membership checks:
* Ingress: only final catchall rules (PVID insertion, 802.1Q
* accept, non-8021Q TPID handling, discard).
@@ -798,46 +950,151 @@ static int mxl862xx_setup(struct dsa_swi
@@ -811,46 +966,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.
@@ -739,7 +729,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
/* 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
@@ -873,10 +1133,6 @@ static int mxl862xx_setup(struct dsa_swi
true, true, true);
if (ret)
return ret;
@@ -750,7 +740,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
schedule_delayed_work(&priv->stats_work,
MXL862XX_STATS_POLL_INTERVAL);
@@ -944,10 +1197,71 @@ static int mxl862xx_configure_sp_tag_pro
@@ -957,10 +1213,71 @@ static int mxl862xx_configure_sp_tag_pro
return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
}
@@ -823,7 +813,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
{
struct mxl862xx_extendedvlan_config cfg = {};
struct mxl862xx_extendedvlan_filter_vlan *fv;
@@ -1016,6 +1330,31 @@ static int mxl862xx_evlan_write_rule(str
@@ -1029,6 +1346,31 @@ static int mxl862xx_evlan_write_rule(str
cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
}
break;
@@ -855,7 +845,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
@@ -1054,7 +1393,7 @@ static int mxl862xx_evlan_write_final_ru
@@ -1067,7 +1409,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],
@@ -864,7 +854,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (ret)
return ret;
}
@@ -1175,6 +1514,41 @@ static int mxl862xx_vf_del_vid(struct mx
@@ -1188,6 +1530,41 @@ static int mxl862xx_vf_del_vid(struct mx
return 0;
}
@@ -906,7 +896,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
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
@@ -1212,8 +1589,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;
@@ -916,7 +906,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (p->vlan_filtering) {
vid_rules = vid_accept_standard;
@@ -1214,13 +1588,23 @@ static int mxl862xx_evlan_program_egress
@@ -1227,13 +1604,23 @@ static int mxl862xx_evlan_program_egress
if (!vfv->untagged)
continue;
@@ -941,7 +931,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (ret)
return ret;
@@ -1229,7 +1613,29 @@ static int mxl862xx_evlan_program_egress
@@ -1242,7 +1629,29 @@ static int mxl862xx_evlan_program_egress
idx++, &vid_rules[1],
vfv->vid,
vfv->untagged,
@@ -972,7 +962,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (ret)
return ret;
}
@@ -1241,8 +1647,7 @@ static int mxl862xx_evlan_program_egress
@@ -1254,8 +1663,7 @@ static int mxl862xx_evlan_program_egress
*/
for (i = idx; i < old_active; i++) {
ret = mxl862xx_evlan_deactivate_entry(priv,
@@ -982,7 +972,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (ret)
return ret;
}
@@ -1267,13 +1672,16 @@ static int mxl862xx_port_vlan_filtering(
@@ -1280,13 +1688,16 @@ static int mxl862xx_port_vlan_filtering(
p->vlan_filtering = vlan_filtering;
if (changed) {
@@ -1004,7 +994,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
ret = mxl862xx_evlan_program_ingress(priv, port);
if (ret)
@@ -1493,22 +1901,575 @@ static void mxl862xx_port_bridge_leave(s
@@ -1506,22 +1917,575 @@ static void mxl862xx_port_bridge_leave(s
port, ERR_PTR(err));
/* Revert leaving port, omitted by the sync above, to its
@@ -1582,7 +1572,7 @@ 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;
@@ -1530,33 +2491,52 @@ static int mxl862xx_port_setup(struct ds
@@ -1543,33 +2507,52 @@ static int mxl862xx_port_setup(struct ds
return -EOPNOTSUPP;
}
@@ -1651,7 +1641,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (ret)
return ret;
ret = mxl862xx_set_bridge_port(ds, port);
@@ -1572,24 +2552,7 @@ static int mxl862xx_port_setup(struct ds
@@ -1585,24 +2568,7 @@ static int mxl862xx_port_setup(struct ds
if (ret)
return ret;
@@ -1676,7 +1666,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return 0;
}
@@ -1611,7 +2574,7 @@ static void mxl862xx_port_teardown(struc
@@ -1624,7 +2590,7 @@ static void mxl862xx_port_teardown(struc
priv->ports[port].setup_done = false;
}
@@ -1685,7 +1675,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
{
struct mxl862xx_priv *priv = ds->priv;
@@ -1629,23 +2592,247 @@ static int mxl862xx_get_fid(struct dsa_s
@@ -1642,23 +2608,247 @@ static int mxl862xx_get_fid(struct dsa_s
}
}
@@ -1718,12 +1708,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;
+}
+
@@ -1941,7 +1931,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);
@@ -1655,18 +2842,25 @@ static int mxl862xx_port_fdb_add(struct
@@ -1668,18 +2858,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)
{
@@ -1950,7 +1940,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) {
@@ -1960,7 +1950,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;
@@ -1974,7 +1964,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);
@@ -1705,84 +2899,153 @@ static int mxl862xx_port_fdb_dump(struct
@@ -1718,84 +2915,153 @@ static int mxl862xx_port_fdb_dump(struct
return 0;
}
@@ -2075,7 +2065,8 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
- sizeof(aparam.port_map));
+ return ret;
+}
+
- mxl862xx_fw_portmap_set_bit(aparam.port_map, port);
+/**
+ * mxl862xx_mac_del_host_bridge - Remove every user-port VBP bit from a host
+ * FDB/MDB entry, dropping the entry when its
@@ -2099,14 +2090,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ struct dsa_port *dp;
+ u16 vbp;
- mxl862xx_fw_portmap_set_bit(aparam.port_map, port);
- return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam);
+ 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);
+
+ return mxl862xx_mac_portmap_del(priv, addr, fid, vid, del_map);
}
@@ -2120,7 +2110,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
- int fid = mxl862xx_get_fid(ds, db), ret;
struct mxl862xx_priv *priv = ds->priv;
+ int fid, ret;
+
+ /* tag_8021q host MDB for bridged ports: clear all VBP bits */
+ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) &&
+ db.type == DSA_DB_BRIDGE) {
@@ -2130,7 +2120,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ return mxl862xx_mac_del_host_bridge(ds, mdb->addr,
+ mdb->vid, &db.bridge);
+ }
+
+ fid = mxl862xx_get_fid(ds, db);
if (fid < 0)
return fid;
@@ -2175,7 +2165,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return ret;
}
@@ -1867,6 +3130,9 @@ static void mxl862xx_host_flood_work_fn(
@@ -1880,6 +3146,9 @@ static void mxl862xx_host_flood_work_fn(
host_flood_work);
struct mxl862xx_priv *priv = p->priv;
struct dsa_switch *ds = priv->ds;
@@ -2185,7 +2175,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
rtnl_lock();
@@ -1876,13 +3142,34 @@ static void mxl862xx_host_flood_work_fn(
@@ -1889,13 +3158,34 @@ static void mxl862xx_host_flood_work_fn(
return;
}
@@ -2227,7 +2217,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
rtnl_unlock();
}
@@ -2248,7 +3535,9 @@ static void mxl862xx_get_stats64(struct
@@ -2261,7 +3551,9 @@ static void mxl862xx_get_stats64(struct
static const struct dsa_switch_ops mxl862xx_switch_ops = {
.get_tag_protocol = mxl862xx_get_tag_protocol,
@@ -2237,7 +2227,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,
@@ -2270,6 +3559,8 @@ static const struct dsa_switch_ops mxl86
@@ -2283,6 +3575,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,
@@ -2246,7 +2236,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,
@@ -2318,6 +3609,8 @@ static int mxl862xx_probe(struct mdio_de
@@ -2330,6 +3624,8 @@ static int mxl862xx_probe(struct mdio_de
INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn);
@@ -2255,7 +2245,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
dev_set_drvdata(dev, ds);
err = dsa_register_switch(ds);
@@ -2346,6 +3639,19 @@ static void mxl862xx_remove(struct mdio_
@@ -2358,6 +3654,19 @@ static void mxl862xx_remove(struct mdio_
set_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags);
cancel_delayed_work_sync(&priv->stats_work);
@@ -2277,7 +2267,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
@@ -209,6 +209,9 @@ struct mxl862xx_port_stats {
@@ -212,6 +212,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
@@ -2287,7 +2277,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
@@ -223,6 +226,7 @@ struct mxl862xx_port_stats {
@@ -226,6 +229,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
@@ -2295,7 +2285,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
*/
struct mxl862xx_port {
struct mxl862xx_priv *priv;
@@ -235,9 +239,13 @@ struct mxl862xx_port {
@@ -238,9 +242,13 @@ struct mxl862xx_port {
struct mxl862xx_vf_block vf;
struct mxl862xx_evlan_block ingress_evlan;
struct mxl862xx_evlan_block egress_evlan;
@@ -2309,7 +2299,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
struct mxl862xx_port_stats stats;
spinlock_t stats_lock; /* protects stats accumulators */
};
@@ -304,6 +312,7 @@ union mxl862xx_fw_version {
@@ -298,6 +306,7 @@ struct 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
@@ -2317,7 +2307,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)
@@ -318,6 +327,7 @@ union mxl862xx_fw_version {
@@ -316,6 +325,7 @@ struct 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
@@ -2325,15 +2315,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
* @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
@@ -334,6 +344,7 @@ struct mxl862xx_priv {
@@ -332,6 +342,7 @@ struct mxl862xx_priv {
struct mdio_device *mdiodev;
struct work_struct crc_err_work;
unsigned long flags;
+ enum dsa_tag_protocol tag_proto;
u16 drop_meter;
union mxl862xx_fw_version fw_version;
struct mxl862xx_fw_version fw_version;
struct mxl862xx_pcs serdes_ports[8];
@@ -341,6 +352,7 @@ struct mxl862xx_priv {
@@ -340,6 +351,7 @@ struct mxl862xx_priv {
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
u16 evlan_egress_size;
@@ -1,7 +1,7 @@
From a32f6a9c09b90e767a03ddac34d061545a0cf15e Mon Sep 17 00:00:00 2001
From 4220567bd18b2174c74e90d22f3bf47422c1fdf6 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 03:44:41 +0000
Subject: [PATCH 10/20] net: dsa: mxl862xx: add link aggregation support
Subject: [PATCH 09/19] 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
@@ -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
@@ -680,7 +680,42 @@ static int mxl862xx_setup_snooping_traps
return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, rule);
@@ -695,7 +695,42 @@ static int mxl862xx_setup_snooping_traps
return mxl862xx_pce_rule_write(priv, &rule);
}
-static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
@@ -134,7 +134,7 @@ 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);
@@ -713,8 +748,12 @@ static int mxl862xx_set_bridge_port(stru
@@ -728,8 +763,12 @@ static int mxl862xx_set_bridge_port(stru
dp->bridge->dev) {
if (member_dp->index == port)
continue;
@@ -149,7 +149,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
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
@@ -742,7 +781,7 @@ static int mxl862xx_set_bridge_port(stru
bridge_id = dp->bridge ? priv->bridges[dp->bridge->num] : p->fid;
@@ -158,7 +158,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
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 |
@@ -808,11 +847,38 @@ static int mxl862xx_set_bridge_port(stru
@@ -823,11 +862,38 @@ static int mxl862xx_set_bridge_port(stru
br_port_cfg);
}
@@ -198,7 +198,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) {
err = mxl862xx_set_bridge_port(ds, dp->index);
@@ -820,6 +886,25 @@ static int mxl862xx_sync_bridge_members(
@@ -835,6 +901,25 @@ static int mxl862xx_sync_bridge_members(
ret = err;
}
@@ -224,7 +224,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return ret;
}
@@ -1859,6 +1944,408 @@ static int mxl862xx_setup_cpu_bridge(str
@@ -1875,6 +1960,408 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
@@ -633,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,
@@ -1884,7 +2371,18 @@ static int mxl862xx_port_bridge_join(str
@@ -1900,7 +2387,18 @@ static int mxl862xx_port_bridge_join(str
return 0;
}
@@ -653,7 +653,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
@@ -1935,6 +2433,17 @@ static void mxl862xx_port_bridge_leave(s
@@ -1951,6 +2449,17 @@ static void mxl862xx_port_bridge_leave(s
"failed to update CPU VBP for port %d: %pe\n", port,
ERR_PTR(err));
@@ -671,7 +671,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
if (!dsa_bridge_ports(ds, bridge.dev))
mxl862xx_free_bridge(ds, &bridge);
}
@@ -2593,18 +3102,17 @@ static int mxl862xx_get_fid(struct dsa_s
@@ -2609,18 +3118,17 @@ static int mxl862xx_get_fid(struct dsa_s
}
/**
@@ -697,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)
@@ -2620,7 +3128,7 @@ static int mxl862xx_fdb_bridge_port(stru
@@ -2636,7 +3144,7 @@ static int mxl862xx_fdb_bridge_port(stru
return bp_cpu;
}
@@ -706,7 +706,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
/**
@@ -2867,11 +3375,43 @@ static int mxl862xx_port_fdb_del(struct
@@ -2883,11 +3391,43 @@ static int mxl862xx_port_fdb_del(struct
return ret;
}
@@ -750,7 +750,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
u32 entry_port_id;
int ret;
@@ -2885,7 +3425,7 @@ static int mxl862xx_port_fdb_dump(struct
@@ -2901,7 +3441,7 @@ static int mxl862xx_port_fdb_dump(struct
entry_port_id = le32_to_cpu(param.port_id);
@@ -759,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);
@@ -3556,6 +4096,11 @@ static const struct dsa_switch_ops mxl86
@@ -3572,6 +4112,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,
@@ -771,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,
@@ -3597,6 +4142,7 @@ static int mxl862xx_probe(struct mdio_de
@@ -3612,6 +4157,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;
@@ -781,7 +781,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -16,6 +16,19 @@ struct mxl862xx_priv;
@@ -19,6 +19,19 @@ struct mxl862xx_priv;
#define MXL862XX_MAX_BRIDGE_PORTS 128
#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024
#define MXL862XX_TOTAL_VF_ENTRIES 1024
@@ -801,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)
@@ -227,6 +240,12 @@ struct mxl862xx_port_stats {
@@ -230,6 +243,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
@@ -814,7 +814,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
*/
struct mxl862xx_port {
struct mxl862xx_priv *priv;
@@ -246,6 +265,9 @@ struct mxl862xx_port {
@@ -249,6 +268,9 @@ struct mxl862xx_port {
struct work_struct host_flood_work;
u16 tag_8021q_vid;
struct mxl862xx_evlan_block cpu_egress_evlan;
@@ -824,7 +824,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
struct mxl862xx_port_stats stats;
spinlock_t stats_lock; /* protects stats accumulators */
};
@@ -336,6 +358,15 @@ union mxl862xx_fw_version {
@@ -334,6 +356,15 @@ struct 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.
@@ -840,7 +840,7 @@ 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
*/
@@ -355,6 +386,8 @@ struct mxl862xx_priv {
@@ -354,6 +385,8 @@ struct mxl862xx_priv {
u16 cpu_evlan_ingress_size;
u16 vf_block_size;
u16 cpu_trap_fid;
@@ -1,7 +1,7 @@
From d919c2f8da9dbd4dda57ceebb5c8b103805b58d1 Mon Sep 17 00:00:00 2001
From 8f7296099da5eaedfe5108a6fe93296bcc0c4b45 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 12:05:29 +0000
Subject: [PATCH 11/19] net: dsa: mxl862xx: add support for mirror port
Subject: [PATCH 10/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.
@@ -40,17 +40,17 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
* @age_timer: Custom MAC table aging timer in seconds
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -30,6 +30,7 @@
#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7)
@@ -29,6 +29,7 @@
#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa)
+#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xe)
#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -1100,6 +1100,8 @@ static int mxl862xx_setup(struct dsa_swi
@@ -1116,6 +1116,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;
@@ -3167,6 +3169,120 @@ static int mxl862xx_fdb_del_per_fid(stru
return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param);
@@ -1960,6 +1962,120 @@ static int mxl862xx_setup_cpu_bridge(str
return mxl862xx_set_bridge_port(ds, port);
}
+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_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
* mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member)
* @ds: DSA switch
@@ -4112,6 +4228,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
@@ -240,6 +240,8 @@ struct mxl862xx_port_stats {
@@ -243,6 +243,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,7 +200,7 @@ 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
@@ -265,6 +267,8 @@ struct mxl862xx_port {
@@ -268,6 +270,8 @@ struct mxl862xx_port {
struct work_struct host_flood_work;
u16 tag_8021q_vid;
struct mxl862xx_evlan_block cpu_egress_evlan;
@@ -209,7 +209,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
struct dsa_lag *lag;
bool lag_tx_enabled;
u8 lag_hash_bits;
@@ -367,6 +371,8 @@ union mxl862xx_fw_version {
@@ -365,6 +369,8 @@ struct mxl862xx_fw_version {
* @trunk_hash: current global hash field bitmask (6 bits,
* MXL862XX_TRUNK_HASH_*); union of all active LAGs'
* hash requirements
@@ -218,7 +218,7 @@ 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
*/
@@ -388,6 +394,7 @@ struct mxl862xx_priv {
@@ -387,6 +393,7 @@ struct mxl862xx_priv {
u16 cpu_trap_fid;
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
@@ -1,7 +1,7 @@
From 64269d9d809962a0f7e68e9b618d81e561e3eb6f Mon Sep 17 00:00:00 2001
From d11e0b07317e04f347de1b5053bb6720d72fd111 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:08 +0000
Subject: [PATCH 12/19] net: dsa: wire flash_update devlink callback to drivers
Subject: [PATCH 11/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
@@ -1,7 +1,7 @@
From fed4225f75b6fe6898e48f472cbbee0aaa046760 Mon Sep 17 00:00:00 2001
From 4dd558c7422fcd2b5c76994928fe3a65e0059fb4 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:17 +0000
Subject: [PATCH 13/19] net: dsa: mxl862xx: add SMDIO clause-22 register access
Subject: [PATCH 12/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
@@ -1,7 +1,7 @@
From fa186b09e346e0b7f2504232731bc9e4dee0c600 Mon Sep 17 00:00:00 2001
From 099e70920b16f5da14d34bc00ac58a5ca95c1e33 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 16:30:31 +0000
Subject: [PATCH 14/19] net: dsa: mxl862xx: add devlink flash_update and
Subject: [PATCH 13/19] net: dsa: mxl862xx: add devlink flash_update and
info_get
Implement runtime firmware upgrade via "devlink dev flash" and version
@@ -35,9 +35,9 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
drivers/net/dsa/mxl862xx/mxl862xx-fw.c | 434 +++++++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-fw.h | 15 +
drivers/net/dsa/mxl862xx/mxl862xx-host.c | 7 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 4 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 3 +
drivers/net/dsa/mxl862xx/mxl862xx.h | 2 +
7 files changed, 464 insertions(+), 1 deletion(-)
7 files changed, 463 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.c
create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.h
@@ -57,7 +57,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1)
#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2)
#define MXL862XX_XPCS_MAGIC 0x1a00
#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1)
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c
@@ -0,0 +1,434 @@
@@ -546,19 +546,18 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#include "mxl862xx-host.h"
#include "mxl862xx-phylink.h"
@@ -4233,6 +4234,9 @@ static const struct dsa_switch_ops mxl86
@@ -4248,6 +4249,8 @@ 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,
+ .devlink_info_get = mxl862xx_devlink_info_get,
+ .devlink_flash_update = mxl862xx_devlink_flash_update,
+
};
static int mxl862xx_probe(struct mdio_device *mdiodev)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -395,6 +395,8 @@ struct mxl862xx_priv {
@@ -394,6 +394,8 @@ struct mxl862xx_priv {
u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1];
u8 trunk_hash;
int mirror_dest;
@@ -1,7 +1,7 @@
From 05bc981690d6ef03143a094f28714aa0171ab571 Mon Sep 17 00:00:00 2001
From d27d60f42f5188d6cfcb771d2188633f1031646f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 1 Apr 2026 13:43:08 +0100
Subject: [PATCH 15/19] net: dsa: mxl862xx: implement port MTU configuration
Subject: [PATCH 14/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
@@ -25,7 +25,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
@@ -3727,6 +3728,53 @@ static int mxl862xx_set_ageing_time(stru
@@ -3743,6 +3744,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)
{
@@ -4202,6 +4250,8 @@ static const struct dsa_switch_ops mxl86
@@ -4218,6 +4266,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
@@ -248,6 +248,8 @@ struct mxl862xx_port_stats {
@@ -251,6 +251,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,7 +99,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
*/
struct mxl862xx_port {
struct mxl862xx_priv *priv;
@@ -272,6 +274,7 @@ struct mxl862xx_port {
@@ -275,6 +277,7 @@ struct mxl862xx_port {
struct dsa_lag *lag;
bool lag_tx_enabled;
u8 lag_hash_bits;
@@ -1,7 +1,7 @@
From b723f7484006aadbaa51e16b870f3c98390605b1 Mon Sep 17 00:00:00 2001
From ba48edffc80f3dd676a9e8cf63aa4778a4ed0f0d Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 1 Apr 2026 13:43:12 +0100
Subject: [PATCH 16/19] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag
Subject: [PATCH 15/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
@@ -24,7 +24,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -759,6 +759,10 @@ static int __mxl862xx_set_bridge_port(st
@@ -774,6 +774,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));
@@ -35,7 +35,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
} 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
@@ -3911,7 +3915,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
@@ -44,7 +44,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return -EINVAL;
return 0;
@@ -3937,7 +3941,11 @@ static int mxl862xx_port_bridge_flags(st
@@ -3953,7 +3957,11 @@ static int mxl862xx_port_bridge_flags(st
if (flags.mask & BR_LEARNING)
priv->ports[port].learning = !!(flags.val & BR_LEARNING);
@@ -59,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
@@ -240,6 +240,10 @@ struct mxl862xx_port_stats {
@@ -243,6 +243,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
@@ -70,7 +70,7 @@ 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;
@@ -269,6 +273,7 @@ struct mxl862xx_port {
@@ -272,6 +276,7 @@ struct mxl862xx_port {
struct work_struct host_flood_work;
u16 tag_8021q_vid;
struct mxl862xx_evlan_block cpu_egress_evlan;
@@ -1,7 +1,7 @@
From d8f20ba50ce0f93f34a41a9b833be76a5d1d2714 Mon Sep 17 00:00:00 2001
From 45fcd9669da62b27823004480c07854c54574a1a Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Mar 2026 01:51:33 +0000
Subject: [PATCH 17/19] net: dsa: mxl862xx: support BR_ISOLATED bridge flag
Subject: [PATCH 16/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
@@ -19,7 +19,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -752,6 +752,8 @@ static int __mxl862xx_set_bridge_port(st
@@ -767,6 +767,8 @@ static int __mxl862xx_set_bridge_port(st
continue;
if (!mxl862xx_is_lag_master(priv, member_dp->index))
continue;
@@ -28,7 +28,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
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
@@ -3915,7 +3917,7 @@ static int mxl862xx_port_pre_bridge_flag
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD |
@@ -37,7 +37,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
return -EINVAL;
return 0;
@@ -3912,6 +3914,7 @@ static int mxl862xx_port_bridge_flags(st
@@ -3928,6 +3930,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;
@@ -45,7 +45,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
int ret;
if (flags.mask & BR_FLOOD) {
@@ -3952,6 +3955,21 @@ static int mxl862xx_port_bridge_flags(st
@@ -3968,6 +3971,21 @@ static int mxl862xx_port_bridge_flags(st
return ret;
}
@@ -69,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
@@ -213,6 +213,9 @@ struct mxl862xx_port_stats {
@@ -216,6 +216,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)
@@ -79,7 +79,7 @@ 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
@@ -259,6 +262,7 @@ struct mxl862xx_port {
@@ -262,6 +265,7 @@ struct mxl862xx_port {
struct mxl862xx_priv *priv;
u16 fid;
unsigned long flood_block;
@@ -0,0 +1,134 @@
From 8120b9b88ec47ee41b8350e3e4341839f5fdc28e Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:17:49 +0000
Subject: [PATCH 17/19] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE
workaround for old firmware
Re-introduce the legacy PCE rule paths needed for firmware versions
older than 1.0.83.
Firmware >= 1.0.83 exposes the new GSW_TFLOW_PCERULELOGICWRITE API
which selects the per-CTP block via @region/@logicalportid and grows
the hardware block on demand. Older firmware lacks that command and
exposes only the legacy GSW_TFLOW_PCERULEWRITE call, where the rule
index is a direct offset into a fixed-size pre-allocated CTP block.
Add MXL862XX_TFLOW_PCERULEWRITE back to mxl862xx-cmd.h and make
mxl862xx_pce_rule_write() pick the right command at runtime based on
the cached firmware version.
In addition, firmware older than 1.0.80 still installs global PCE
rules at boot that redirect link-local frames (BPDUs, LLDP, LACP) to
port 0 (the on-chip microcontroller) instead of the DSA CPU port.
With port 0 disabled under DSA these rules silently drop matching
traffic. Re-introduce mxl862xx_disable_fw_global_rules() to clear
them out during setup. The upstream submission replaced that call
with a dev_warn() since firmware >= 1.0.80 no longer installs the
problematic rules.
This commit is for downstream use only and must not be submitted
upstream.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 +
drivers/net/dsa/mxl862xx/mxl862xx.c | 61 +++++++++++++++++++++----
2 files changed, 53 insertions(+), 9 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -32,6 +32,7 @@
#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xe)
#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
+#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4)
#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5)
#define MXL862XX_TFLOW_PCERULELOGICWRITE \
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -449,6 +449,43 @@ static int mxl862xx_setup_drop_meter(str
return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
}
+/* Disable firmware global PCE rules that trap various protocols to the
+ * on-die microcontroller (port 0) via PORTMAP_CPU. Under DSA, these
+ * frames must either reach the host CPU via per-port rules (link-local)
+ * or through the normal bridge forwarding path (ARP broadcast), so the
+ * global firmware rules are not needed. With the microcontroller port
+ * disabled they would silently drop matching traffic.
+ *
+ * Global rules have lower indices than CTP rules, hence higher priority
+ * in the PCE pipeline -- they must be explicitly disabled or they will
+ * shadow the per-CTP traps.
+ *
+ * Indices from gsw_flow_index.h:
+ * 1 -- BPDU (STP/RSTP, dst 01:80:c2:00:00:00)
+ * 3 -- LLDP (EtherType 0x88cc)
+ * 4 -- OAM/LACP (EtherType 0x8809)
+ * 6 -- System MAC (dst 02:e0:92:00:00:01, vendor management MAC)
+ * 7 -- ARP Request (broadcast + EtherType 0x0806 + TPA 192.0.2.1)
+ */
+static int mxl862xx_disable_fw_global_rules(struct dsa_switch *ds)
+{
+ static const u16 indices[] = { 1, 3, 4, 6, 7 };
+ struct mxl862xx_pce_rule rule;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(indices); i++) {
+ memset(&rule, 0, sizeof(rule));
+ rule.pattern.index = cpu_to_le16(indices[i]);
+ /* pattern.enable == 0 -> rule is disabled */
+
+ ret = MXL862XX_API_WRITE(ds->priv,
+ MXL862XX_TFLOW_PCERULEWRITE, rule);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
/* Per-CTP logical indices for protocol trap rules.
*
@@ -463,16 +500,20 @@ static int mxl862xx_setup_drop_meter(str
#define MXL862XX_MLDV1_CTP_INDEX 3
#define MXL862XX_MLDV2_CTP_INDEX 4
-/* Install (or overwrite) a PCE rule via the firmware's logical-index
- * API. The firmware translates the logical index in @rule->pattern.index
- * to a physical position within the block selected by @rule->region and
- * @rule->logicalportid, and expands the hardware block size as needed.
+/* Install (or overwrite) a PCE rule. On firmware >= 1.0.83 use the
+ * logical-index API (PCERULELOGICWRITE), which grows the per-CTP block
+ * on demand. On older firmware that API does not exist, so fall back
+ * to the legacy PCERULEWRITE call which uses the rule index as a
+ * direct offset into a fixed-size pre-allocated CTP block.
*/
static int mxl862xx_pce_rule_write(struct mxl862xx_priv *priv,
struct mxl862xx_pce_rule *rule)
{
- return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULELOGICWRITE,
- *rule);
+ u16 cmd = MXL862XX_FW_VER_MIN(priv, 1, 0, 83) ?
+ MXL862XX_TFLOW_PCERULELOGICWRITE :
+ MXL862XX_TFLOW_PCERULEWRITE;
+
+ return MXL862XX_API_WRITE(priv, cmd, *rule);
}
/**
@@ -1130,9 +1171,11 @@ static int mxl862xx_setup(struct dsa_swi
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");
+ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) {
+ ret = mxl862xx_disable_fw_global_rules(ds);
+ if (ret)
+ return ret;
+ }
/* Pre-allocate firmware resources for all ports. The DSA core
* calls change_tag_protocol() between setup() and port_setup(),
@@ -0,0 +1,491 @@
From 494a1eb62219d9ccb97a60e15ea59e7de2f5d9bc Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:19:56 +0000
Subject: [PATCH 18/19] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
fallback for old firmware
Re-introduce the SYS_MISC_SFP_SET-based PCS implementation as a
fallback for firmware versions older than 1.0.84 which lack the
XPCS API. mxl862xx_setup_pcs() selects between the XPCS ops and
legacy SFP ops based on firmware version.
This commit is for downstream use only and must not be submitted
upstream.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 244 ++++++++++++++++++++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 3 +
drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 155 ++++++++++++-
drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 3 +
4 files changed, 401 insertions(+), 4 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -2164,6 +2164,18 @@ struct mxl862xx_sys_fw_image_version {
} __packed;
/**
+ * enum mxl862xx_port_duplex - Ethernet port duplex status
+ * @MXL862XX_DUPLEX_FULL: Port operates in full-duplex mode
+ * @MXL862XX_DUPLEX_HALF: Port operates in half-duplex mode
+ * @MXL862XX_DUPLEX_AUTO: Port operates in Auto mode
+ */
+enum mxl862xx_port_duplex {
+ MXL862XX_DUPLEX_FULL = 0,
+ MXL862XX_DUPLEX_HALF,
+ MXL862XX_DUPLEX_AUTO,
+};
+
+/**
* enum mxl862xx_port_type - Port Type
* @MXL862XX_LOGICAL_PORT: Logical Port
* @MXL862XX_PHYSICAL_PORT: Physical Port
@@ -2178,6 +2190,238 @@ enum mxl862xx_port_type {
};
/**
+ * enum mxl862xx_port_enable - port enable type selection.
+ * @MXL862XX_PORT_DISABLE: the port is disabled in both directions
+ * @MXL862XX_PORT_ENABLE_RXTX: the port is enabled in both directions
+ * @MXL862XX_PORT_ENABLE_RX: the port is enabled in the receive direction
+ * @MXL862XX_PORT_ENABLE_TX: the port is enabled in the transmit direction
+ */
+enum mxl862xx_port_enable {
+ MXL862XX_PORT_DISABLE = 0,
+ MXL862XX_PORT_ENABLE_RXTX,
+ MXL862XX_PORT_ENABLE_RX,
+ MXL862XX_PORT_ENABLE_TX,
+};
+
+/**
+ * enum mxl862xx_port_flow - ethernet flow control status
+ * @MXL862XX_FLOW_AUTO: automatic flow control
+ * @MXL862XX_FLOW_RX: receive flow control only
+ * @MXL862XX_FLOW_TX: transmit flow control only
+ * @MXL862XX_FLOW_RXTX: receive and transmit flow control
+ * @MXL862XX_FLOW_OFF: no flow control
+ */
+enum mxl862xx_port_flow {
+ MXL862XX_FLOW_AUTO = 0,
+ MXL862XX_FLOW_RX,
+ MXL862XX_FLOW_TX,
+ MXL862XX_FLOW_RXTX,
+ MXL862XX_FLOW_OFF,
+};
+
+/**
+ * enum mxl862xx_port_monitor - port mirror options
+ * @MXL862XX_PORT_MONITOR_NONE: mirroring is disabled
+ * @MXL862XX_PORT_MONITOR_RX: ingress packets are mirrored
+ * @MXL862XX_PORT_MONITOR_TX: egress packets are mirrored
+ * @MXL862XX_PORT_MONITOR_RXTX: ingress and egress packets are mirrored
+ * @MXL862XX_PORT_MONITOR_VLAN_UNKNOWN: mirroring of 'unknown VLAN violation' frames
+ * @MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP: mirroring of 'VLAN ingress or egress membership
+ * violation' frames
+ * @MXL862XX_PORT_MONITOR_PORT_STATE: mirroring of 'port state violation' frames
+ * @MXL862XX_PORT_MONITOR_LEARNING_LIMIT: mirroring of 'MAC learning limit violation' frames
+ * @MXL862XX_PORT_MONITOR_PORT_LOCK: mirroring of 'port lock violation' frames
+ */
+enum mxl862xx_port_monitor {
+ MXL862XX_PORT_MONITOR_NONE = 0,
+ MXL862XX_PORT_MONITOR_RX,
+ MXL862XX_PORT_MONITOR_TX,
+ MXL862XX_PORT_MONITOR_RXTX,
+ MXL862XX_PORT_MONITOR_VLAN_UNKNOWN,
+ MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP = 16,
+ MXL862XX_PORT_MONITOR_PORT_STATE = 32,
+ MXL862XX_PORT_MONITOR_LEARNING_LIMIT = 64,
+ MXL862XX_PORT_MONITOR_PORT_LOCK = 128,
+};
+
+/**
+ * enum mxl862xx_if_rmon_mode - interface RMON counter mode
+ * @MXL862XX_IF_RMON_FID: FID based RMON counters
+ * @MXL862XX_IF_RMON_SUBID: sub-interface ID based
+ * @MXL862XX_IF_RMON_FLOWID_LSB: flow ID based (bits 3:0)
+ * @MXL862XX_IF_RMON_FLOWID_MSB: flow ID based (bits 7:4)
+ */
+enum mxl862xx_if_rmon_mode {
+ MXL862XX_IF_RMON_FID = 0,
+ MXL862XX_IF_RMON_SUBID,
+ MXL862XX_IF_RMON_FLOWID_LSB,
+ MXL862XX_IF_RMON_FLOWID_MSB,
+};
+
+/**
+ * struct mxl862xx_port_cfg - Port Configuration Parameters
+ * @port_type: See &enum mxl862xx_port_type
+ * @port_id: Ethernet Port number (zero-based counting)
+ * @enable: See &enum mxl862xx_port_enable
+ * @unicast_unknown_drop: Drop unknown unicast packets
+ * @multicast_unknown_drop: Drop unknown multicast packets
+ * @reserved_packet_drop: Drop reserved packet types
+ * @broadcast_drop: Drop broadcast packets
+ * @aging: Enables MAC address table aging.
+ * @learning: MAC address table learning
+ * @learning_mac_port_lock: Automatic MAC address table learning locking on the port
+ * @learning_limit: Automatic MAC address table learning limitation on this port
+ * @mac_spoofing_detection: MAC spoofing detection. Identifies ingress packets that carry
+ * a MAC source address which was previously learned on a different ingress port
+ * @flow_ctrl: See &enum mxl862xx_port_flow
+ * @port_monitor: See &enum mxl862xx_port_monitor
+ * @if_counters: Assign Interface RMON Counters for this Port
+ * @if_count_start_idx: Interface RMON Counters Start Index
+ * @if_rmonmode: See &enum mxl862xx_if_rmon_mode
+ */
+struct mxl862xx_port_cfg {
+ __le32 port_type; /* enum mxl862xx_port_type */
+ __le16 port_id;
+ __le32 enable; /* enum mxl862xx_port_enable */
+ u8 unicast_unknown_drop;
+ u8 multicast_unknown_drop;
+ u8 reserved_packet_drop;
+ u8 broadcast_drop;
+ u8 aging;
+ u8 learning;
+ u8 learning_mac_port_lock;
+ __le16 learning_limit;
+ u8 mac_spoofing_detection;
+ __le32 flow_ctrl; /* enum mxl862xx_port_flow */
+ __le32 port_monitor; /* enum mxl862xx_port_monitor */
+ u8 if_counters;
+ __le32 if_count_start_idx;
+ __le32 if_rmonmode; /* enum mxl862xx_if_rmon_mode */
+} __packed;
+
+/**
+ * enum mxl862xx_port_speed - Ethernet port speed mode
+ * @MXL862XX_PORT_SPEED_10: 10 Mbit/s
+ * @MXL862XX_PORT_SPEED_100: 100 Mbit/s
+ * @MXL862XX_PORT_SPEED_200: 200 Mbit/s
+ * @MXL862XX_PORT_SPEED_1000: 1000 Mbit/s
+ * @MXL862XX_PORT_SPEED_2500: 2.5 Gbit/s
+ * @MXL862XX_PORT_SPEED_5000: 5 Gbit/s
+ * @MXL862XX_PORT_SPEED_10000: 10 Gbit/s
+ * @MXL862XX_PORT_SPEED_AUTO: Auto speed for XGMAC
+ */
+enum mxl862xx_port_speed {
+ MXL862XX_PORT_SPEED_10 = 0,
+ MXL862XX_PORT_SPEED_100,
+ MXL862XX_PORT_SPEED_200,
+ MXL862XX_PORT_SPEED_1000,
+ MXL862XX_PORT_SPEED_2500,
+ MXL862XX_PORT_SPEED_5000,
+ MXL862XX_PORT_SPEED_10000,
+ MXL862XX_PORT_SPEED_AUTO,
+};
+
+/**
+ * enum mxl862xx_port_link - Force the MAC and PHY link modus
+ * @MXL862XX_PORT_LINK_UP: Link up
+ * @MXL862XX_PORT_LINK_DOWN: Link down
+ * @MXL862XX_PORT_LINK_AUTO: Link Auto
+ */
+enum mxl862xx_port_link {
+ MXL862XX_PORT_LINK_UP = 0,
+ MXL862XX_PORT_LINK_DOWN,
+ MXL862XX_PORT_LINK_AUTO,
+};
+
+/**
+ * enum mxl862xx_mii_mode - Ethernet port interface mode
+ * @MXL862XX_PORT_HW_MII: Normal PHY interface
+ * @MXL862XX_PORT_HW_RMII: Reduced MII interface in normal mode
+ * @MXL862XX_PORT_HW_GMII: GMII or MII, depending upon the speed
+ * @MXL862XX_PORT_HW_RGMII: RGMII mode
+ * @MXL862XX_PORT_HW_XGMII: XGMII mode
+ */
+enum mxl862xx_mii_mode {
+ MXL862XX_PORT_HW_MII = 0,
+ MXL862XX_PORT_HW_RMII,
+ MXL862XX_PORT_HW_GMII,
+ MXL862XX_PORT_HW_RGMII,
+ MXL862XX_PORT_HW_XGMII,
+};
+
+/**
+ * enum mxl862xx_mii_type - Ethernet port configuration for PHY or MAC mode
+ * @MXL862XX_PORT_MAC: The Ethernet port is configured to work in MAC mode
+ * @MXL862XX_PORT_PHY: The Ethernet port is configured to work in PHY mode
+ */
+enum mxl862xx_mii_type {
+ MXL862XX_PORT_MAC = 0,
+ MXL862XX_PORT_PHY,
+};
+
+/**
+ * enum mxl862xx_clk_mode - Ethernet port clock source configuration
+ * @MXL862XX_PORT_CLK_NA: Clock Mode not applicable
+ * @MXL862XX_PORT_CLK_MASTER: Clock Master Mode.
+ * @MXL862XX_PORT_CLK_SLAVE: Clock Slave Mode.
+ */
+enum mxl862xx_clk_mode {
+ MXL862XX_PORT_CLK_NA = 0,
+ MXL862XX_PORT_CLK_MASTER,
+ MXL862XX_PORT_CLK_SLAVE,
+};
+
+/**
+ * struct mxl862xx_port_link_cfg - Ethernet port link, speed status and flow control status
+ * @port_id: Ethernet Port number
+ * @duplex_force: Force Port Duplex Mode
+ * @duplex: See &enum mxl862xx_port_duplex
+ * @speed_force: Force Link Speed
+ * @speed: See &enum mxl862xx_port_speed
+ * @link_force: Force Link
+ * @link: See &enum mxl862xx_port_link
+ * @mii_mode: See &enum mxl862xx_mii_mode
+ * @mii_type: See &enum mxl862xx_mii_type
+ * @clk_mode: See &enum mxl862xx_clk_mode
+ * @lpi: 'Low Power Idle' Support for 'Energy Efficient Ethernet'
+ */
+struct mxl862xx_port_link_cfg {
+ __le16 port_id;
+ u8 duplex_force;
+ __le32 duplex; /* enum mxl862xx_port_duplex */
+ u8 speed_force;
+ __le32 speed; /* enum mxl862xx_port_speed */
+ u8 link_force;
+ __le32 link; /* enum mxl862xx_port_link */
+ __le32 mii_mode; /* enum mxl862xx_mii_mode */
+ __le32 mii_type; /* enum mxl862xx_mii_type */
+ __le32 clk_mode; /* enum mxl862xx_clk_mode */
+ u8 lpi;
+} __packed;
+
+/**
+ * struct mxl862xx_sys_sfp_cfg - legacy SFP/SerDes port configuration
+ * @port_id: port id (0 or 1)
+ * @option: config options (0 - SFP mode/speed/link-status, 1 - flow control)
+ * @mode: SFP mode (0 - auto, 1 - fix, 2 - disable)
+ * @speed: select speed when mode is 1
+ * @link: get link state
+ * @fc_en: flow control (0 - disable, 1 - enable)
+ */
+struct mxl862xx_sys_sfp_cfg {
+ u8 port_id:4;
+ u8 option:4;
+ union {
+ struct {
+ u8 mode;
+ u8 speed;
+ u8 link;
+ };
+ u8 fc_en;
+ };
+} __packed;
+
+/**
* enum mxl862xx_rmon_port_type - RMON counter table type
* @MXL862XX_RMON_CTP_PORT_RX: CTP RX counters
* @MXL862XX_RMON_CTP_PORT_TX: CTP TX counters
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -27,6 +27,8 @@
#define SYS_MISC_MAGIC 0x1900
#define MXL862XX_XPCS_MAGIC 0x1a00
+#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5)
+#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7)
#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa)
#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xe)
@@ -86,6 +88,7 @@
#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1)
#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2)
+#define SYS_MISC_SFP_SET (SYS_MISC_MAGIC + 0xe)
#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1)
#define MXL862XX_XPCS_PCS_GET_STATE (MXL862XX_XPCS_MAGIC + 0x2)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
@@ -62,6 +62,153 @@ static struct mxl862xx_pcs *pcs_to_mxl86
return container_of(pcs, struct mxl862xx_pcs, pcs);
}
+/* Legacy SFP-based PCS implementation for firmware < 1.0.84 */
+static int mxl862xx_legacy_pcs_config(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
+ struct mxl862xx_priv *priv = mpcs->priv;
+ struct mxl862xx_sys_sfp_cfg ser_intf = {
+ .option = 0,
+ .mode = 1,
+ };
+
+ if (mpcs->slot != 0)
+ return 0;
+
+ ser_intf.port_id = mpcs->serdes_id;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ ser_intf.speed = 8;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ ser_intf.speed = (neg_mode & PHYLINK_PCS_NEG_INBAND) ? 1 : 7;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ ser_intf.speed = 4;
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ ser_intf.speed = 2;
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ ser_intf.speed = 3;
+ break;
+ default:
+ dev_err(priv->ds->dev, "unsupported interface: %s\n",
+ phy_modes(interface));
+ return -EINVAL;
+ }
+
+ return MXL862XX_API_WRITE(priv, SYS_MISC_SFP_SET, ser_intf);
+}
+
+static void mxl862xx_legacy_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
+ struct mxl862xx_priv *priv = mpcs->priv;
+ struct mxl862xx_port_link_cfg port_link_cfg = {
+ .port_id = cpu_to_le16(MXL862XX_PCS_PORT(mpcs)),
+ };
+ struct mxl862xx_port_cfg port_cfg = {
+ .port_id = cpu_to_le16(MXL862XX_PCS_PORT(mpcs)),
+ };
+ int ret;
+
+ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTLINKCFGGET,
+ port_link_cfg);
+ if (ret)
+ return;
+
+ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTCFGGET, port_cfg);
+ if (ret)
+ return;
+
+ state->link = (le32_to_cpu(port_link_cfg.link) == MXL862XX_PORT_LINK_UP);
+ state->an_complete = state->link;
+
+ switch (le32_to_cpu(port_link_cfg.speed)) {
+ case MXL862XX_PORT_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case MXL862XX_PORT_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case MXL862XX_PORT_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case MXL862XX_PORT_SPEED_2500:
+ state->speed = SPEED_2500;
+ break;
+ case MXL862XX_PORT_SPEED_5000:
+ state->speed = SPEED_5000;
+ break;
+ case MXL862XX_PORT_SPEED_10000:
+ state->speed = SPEED_10000;
+ break;
+ default:
+ state->speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ switch (le32_to_cpu(port_link_cfg.duplex)) {
+ case MXL862XX_DUPLEX_HALF:
+ state->duplex = DUPLEX_HALF;
+ break;
+ case MXL862XX_DUPLEX_FULL:
+ state->duplex = DUPLEX_FULL;
+ break;
+ default:
+ state->duplex = DUPLEX_UNKNOWN;
+ break;
+ }
+
+ state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
+ switch (le32_to_cpu(port_cfg.flow_ctrl)) {
+ case MXL862XX_FLOW_RXTX:
+ state->pause |= MLO_PAUSE_TXRX_MASK;
+ break;
+ case MXL862XX_FLOW_TX:
+ state->pause |= MLO_PAUSE_TX;
+ break;
+ case MXL862XX_FLOW_RX:
+ state->pause |= MLO_PAUSE_RX;
+ break;
+ case MXL862XX_FLOW_OFF:
+ default:
+ break;
+ }
+}
+
+static unsigned int
+mxl862xx_legacy_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ return LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return LINK_INBAND_DISABLE;
+ default:
+ return 0;
+ }
+}
+
+static const struct phylink_pcs_ops mxl862xx_legacy_pcs_ops = {
+ .pcs_get_state = mxl862xx_legacy_pcs_get_state,
+ .pcs_config = mxl862xx_legacy_pcs_config,
+ .pcs_inband_caps = mxl862xx_legacy_pcs_inband_caps,
+};
+
static int mxl862xx_xpcs_if_mode(phy_interface_t interface)
{
switch (interface) {
@@ -352,7 +499,10 @@ void mxl862xx_setup_pcs(struct mxl862xx_
pcs->slot = MXL862XX_SERDES_SLOT(port);
pcs->interface = PHY_INTERFACE_MODE_NA;
- pcs->pcs.ops = &mxl862xx_pcs_ops;
+ if (MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
+ pcs->pcs.ops = &mxl862xx_pcs_ops;
+ else
+ pcs->pcs.ops = &mxl862xx_legacy_pcs_ops;
pcs->pcs.poll = true;
__set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces);
@@ -376,9 +526,6 @@ mxl862xx_phylink_mac_select_pcs(struct p
struct mxl862xx_priv *priv = dp->ds->priv;
int port = dp->index;
- if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
- return NULL;
-
switch (port) {
case 9 ... 16:
return &priv->serdes_ports[port - 9].pcs;
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
@@ -11,6 +11,9 @@
(((port) - MXL862XX_FIRST_SERDES_PORT) % MXL862XX_SERDES_SLOTS)
#define MXL862XX_SERDES_PORT_ID(port) \
(((port) - MXL862XX_FIRST_SERDES_PORT) / MXL862XX_SERDES_SLOTS)
+#define MXL862XX_PCS_PORT(mpcs) \
+ (MXL862XX_FIRST_SERDES_PORT + \
+ (mpcs)->serdes_id * MXL862XX_SERDES_SLOTS + (mpcs)->slot)
extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops;
void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
@@ -1,81 +0,0 @@
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 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
disables firmware default global PCE rules for firmware versions
older than 1.0.80. The upstream submission replaced this with a
dev_warn() since firmware >= 1.0.80 no longer installs these rules,
but downstream deployments may still run older firmware.
This commit is for downstream use only and must not be submitted
upstream.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx.c | 45 +++++++++++++++++++++++++++--
1 file changed, 42 insertions(+), 3 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -449,6 +449,43 @@ static int mxl862xx_setup_drop_meter(str
return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg);
}
+/* Disable firmware global PCE rules that trap various protocols to the
+ * on-die microcontroller (port 0) via PORTMAP_CPU. Under DSA, these
+ * frames must either reach the host CPU via per-port rules (link-local)
+ * or through the normal bridge forwarding path (ARP broadcast), so the
+ * global firmware rules are not needed. With the microcontroller port
+ * disabled they would silently drop matching traffic.
+ *
+ * Global rules have lower indices than CTP rules, hence higher priority
+ * in the PCE pipeline -- they must be explicitly disabled or they will
+ * shadow the per-CTP traps.
+ *
+ * Indices from gsw_flow_index.h:
+ * 1 -- BPDU (STP/RSTP, dst 01:80:c2:00:00:00)
+ * 3 -- LLDP (EtherType 0x88cc)
+ * 4 -- OAM/LACP (EtherType 0x8809)
+ * 6 -- System MAC (dst 02:e0:92:00:00:01, vendor management MAC)
+ * 7 -- ARP Request (broadcast + EtherType 0x0806 + TPA 192.0.2.1)
+ */
+static int mxl862xx_disable_fw_global_rules(struct dsa_switch *ds)
+{
+ static const u16 indices[] = { 1, 3, 4, 6, 7 };
+ struct mxl862xx_pce_rule rule;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(indices); i++) {
+ memset(&rule, 0, sizeof(rule));
+ rule.pattern.index = cpu_to_le16(indices[i]);
+ /* pattern.enable == 0 -> rule is disabled */
+
+ ret = MXL862XX_API_WRITE(ds->priv,
+ MXL862XX_TFLOW_PCERULEWRITE, rule);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
/* 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;
- 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");
+ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) {
+ ret = mxl862xx_disable_fw_global_rules(ds);
+ if (ret)
+ return ret;
+ }
/* Pre-allocate firmware resources for all ports. The DSA core
* calls change_tag_protocol() between setup() and port_setup(),
@@ -1,7 +1,7 @@
From df0c747216063041ba0d786b01f9b1e2aba5316a Mon Sep 17 00:00:00 2001
From 332dd61f50b096256f889ecb4d730d385e58aec2 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
Subject: [PATCH 19/19] 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
@@ -1,252 +0,0 @@
From 20be48bbcc89f5ca91e9d6adadeda9bd29d75c53 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 24 Mar 2026 18:19:56 +0000
Subject: [PATCH 19/19] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API
fallback for old firmware
Re-introduce the SYS_MISC_SFP_SET-based PCS implementation as a
fallback for firmware versions older than 1.0.80 which lack the
XPCS API. mxl862xx_setup_pcs() selects between the XPCS ops and
legacy SFP ops based on firmware version.
This commit is for downstream use only and must not be submitted
upstream.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mxl862xx/mxl862xx-api.h | 22 +++
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 +
drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 160 +++++++++++++++++++-
3 files changed, 178 insertions(+), 5 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -2400,6 +2400,28 @@ struct mxl862xx_sys_fw_image_version {
} __packed;
/**
+ * struct mxl862xx_sys_sfp_cfg - legacy SFP/SerDes port configuration
+ * @port_id: port id (0 or 1)
+ * @option: config options (0 - SFP mode/speed/link-status, 1 - flow control)
+ * @mode: SFP mode (0 - auto, 1 - fix, 2 - disable)
+ * @speed: select speed when mode is 1
+ * @link: get link state
+ * @fc_en: flow control (0 - disable, 1 - enable)
+ */
+struct mxl862xx_sys_sfp_cfg {
+ u8 port_id:4;
+ u8 option:4;
+ union {
+ struct {
+ u8 mode;
+ u8 speed;
+ u8 link;
+ };
+ u8 fc_en;
+ };
+} __packed;
+
+/**
* enum mxl862xx_rmon_port_type - RMON counter table type
* @MXL862XX_RMON_CTP_PORT_RX: CTP RX counters
* @MXL862XX_RMON_CTP_PORT_TX: CTP TX counters
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -85,6 +85,7 @@
#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1)
#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2)
+#define SYS_MISC_SFP_SET (SYS_MISC_MAGIC + 0xe)
#define MXL862XX_XPCS_MAGIC 0x1a00
#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
@@ -52,6 +52,156 @@ static struct mxl862xx_pcs *pcs_to_mxl86
return container_of(pcs, struct mxl862xx_pcs, pcs);
}
+/* Legacy SFP-based PCS implementation for firmware < 1.0.80 */
+static int mxl862xx_legacy_pcs_config(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv;
+ int port = pcs_to_mxl862xx_pcs(pcs)->port;
+ struct mxl862xx_sys_sfp_cfg ser_intf = {
+ .option = 0,
+ .mode = 1,
+ };
+
+ if (port != 9 && port != 13)
+ return 0;
+
+ if (port == 9)
+ ser_intf.port_id = 0;
+ else
+ ser_intf.port_id = 1;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ ser_intf.speed = 8;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ ser_intf.speed = (neg_mode & PHYLINK_PCS_NEG_INBAND) ? 1 : 7;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ ser_intf.speed = 4;
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ ser_intf.speed = 2;
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ ser_intf.speed = 3;
+ break;
+ default:
+ dev_err(priv->ds->dev, "unsupported interface: %s\n",
+ phy_modes(interface));
+ return -EINVAL;
+ }
+
+ return MXL862XX_API_WRITE(priv, SYS_MISC_SFP_SET, ser_intf);
+}
+
+static void mxl862xx_legacy_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv;
+ int port = pcs_to_mxl862xx_pcs(pcs)->port;
+ struct mxl862xx_port_link_cfg port_link_cfg = {
+ .port_id = port,
+ };
+ struct mxl862xx_port_cfg port_cfg = {
+ .port_id = port,
+ };
+ int ret;
+
+ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTLINKCFGGET,
+ port_link_cfg);
+ if (ret)
+ return;
+
+ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTCFGGET, port_cfg);
+ if (ret)
+ return;
+
+ state->link = (port_link_cfg.link == MXL862XX_PORT_LINK_UP);
+ state->an_complete = state->link;
+
+ switch (port_link_cfg.speed) {
+ case MXL862XX_PORT_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case MXL862XX_PORT_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case MXL862XX_PORT_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case MXL862XX_PORT_SPEED_2500:
+ state->speed = SPEED_2500;
+ break;
+ case MXL862XX_PORT_SPEED_5000:
+ state->speed = SPEED_5000;
+ break;
+ case MXL862XX_PORT_SPEED_10000:
+ state->speed = SPEED_10000;
+ break;
+ default:
+ state->speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ switch (port_link_cfg.duplex) {
+ case MXL862XX_DUPLEX_HALF:
+ state->duplex = DUPLEX_HALF;
+ break;
+ case MXL862XX_DUPLEX_FULL:
+ state->duplex = DUPLEX_FULL;
+ break;
+ default:
+ state->duplex = DUPLEX_UNKNOWN;
+ break;
+ }
+
+ state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
+ switch (port_cfg.flow_ctrl) {
+ case MXL862XX_FLOW_RXTX:
+ state->pause |= MLO_PAUSE_TXRX_MASK;
+ break;
+ case MXL862XX_FLOW_TX:
+ state->pause |= MLO_PAUSE_TX;
+ break;
+ case MXL862XX_FLOW_RX:
+ state->pause |= MLO_PAUSE_RX;
+ break;
+ case MXL862XX_FLOW_OFF:
+ default:
+ break;
+ }
+}
+
+static unsigned int
+mxl862xx_legacy_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ return LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return LINK_INBAND_DISABLE;
+ default:
+ return 0;
+ }
+}
+
+static const struct phylink_pcs_ops mxl862xx_legacy_pcs_ops = {
+ .pcs_get_state = mxl862xx_legacy_pcs_get_state,
+ .pcs_config = mxl862xx_legacy_pcs_config,
+ .pcs_inband_caps = mxl862xx_legacy_pcs_inband_caps,
+};
+
static int mxl862xx_xpcs_port_id(int port)
{
return port >= 13;
@@ -390,7 +540,10 @@ void mxl862xx_setup_pcs(struct mxl862xx_
pcs->priv = priv;
pcs->port = port;
- pcs->pcs.ops = &mxl862xx_pcs_ops;
+ if (MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
+ pcs->pcs.ops = &mxl862xx_pcs_ops;
+ else
+ pcs->pcs.ops = &mxl862xx_legacy_pcs_ops;
pcs->pcs.poll = true;
/* Sub-ports only support QSGMII (quad mode with dedicated
@@ -418,9 +571,6 @@ mxl862xx_phylink_mac_select_pcs(struct p
struct mxl862xx_priv *priv = dp->ds->priv;
int port = dp->index;
- if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
- return NULL;
-
switch (port) {
case 9 ... 16:
return &priv->serdes_ports[port - 9].pcs;
@@ -551,7 +701,7 @@ void mxl862xx_serdes_get_stats(struct ds
}
void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port,
- struct ethtool_test *etest, u64 *data)
+ struct ethtool_test *etest, u64 *data)
{
struct mxl862xx_xpcs_prbs_cfg prbs = {};
struct mxl862xx_xpcs_bert_cfg bert = {};