summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Golle2026-05-21 17:19:05 +0000
committerDaniel Golle2026-05-27 18:01:52 +0000
commit028dc3f57a6f9430181587a5af34d0cb7e9a442e (patch)
treecf5b3cec29cf24f260b2f7093463ff67f55cc5bf
parent5b69e6a4a6588734c8c929750004c771b149cb49 (diff)
downloadopenwrt-main.tar.gz
generic: 6.18: update MxL862xx DSA switch driverHEADmastermain
Update driver to be ready for the upcoming firmware release. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-rw-r--r--target/linux/generic/pending-6.18/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch51
-rw-r--r--target/linux/generic/pending-6.18/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch2
-rw-r--r--target/linux/generic/pending-6.18/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch2
-rw-r--r--target/linux/generic/pending-6.18/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch1002
-rw-r--r--target/linux/generic/pending-6.18/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch159
-rw-r--r--target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch208
-rw-r--r--target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch (renamed from target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch)116
-rw-r--r--target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch (renamed from target/linux/generic/pending-6.18/760-08-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch)6
-rw-r--r--target/linux/generic/pending-6.18/760-08-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch (renamed from target/linux/generic/pending-6.18/760-09-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch)136
-rw-r--r--target/linux/generic/pending-6.18/760-09-net-dsa-mxl862xx-add-link-aggregation-support.patch (renamed from target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-link-aggregation-support.patch)44
-rw-r--r--target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-support-for-mirror-port.patch (renamed from target/linux/generic/pending-6.18/760-11-net-dsa-mxl862xx-add-support-for-mirror-port.patch)30
-rw-r--r--target/linux/generic/pending-6.18/760-11-net-dsa-wire-flash_update-devlink-callback-to-driver.patch (renamed from target/linux/generic/pending-6.18/760-12-net-dsa-wire-flash_update-devlink-callback-to-driver.patch)4
-rw-r--r--target/linux/generic/pending-6.18/760-12-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch (renamed from target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch)4
-rw-r--r--target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch (renamed from target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch)17
-rw-r--r--target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-implement-port-MTU-configuration.patch (renamed from target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-implement-port-MTU-configuration.patch)12
-rw-r--r--target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch (renamed from target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch)14
-rw-r--r--target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch (renamed from target/linux/generic/pending-6.18/760-17-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch)16
-rw-r--r--target/linux/generic/pending-6.18/760-17-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch134
-rw-r--r--target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch491
-rw-r--r--target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch81
-rw-r--r--target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch (renamed from target/linux/generic/pending-6.18/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch)4
-rw-r--r--target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch252
22 files changed, 1336 insertions, 1449 deletions
diff --git a/target/linux/generic/pending-6.18/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch b/target/linux/generic/pending-6.18/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch
index 1559511d98..37ff40dbf2 100644
--- a/target/linux/generic/pending-6.18/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch
+++ b/target/linux/generic/pending-6.18/760-01-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch b/target/linux/generic/pending-6.18/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch
index 6d36ad4893..83d6ac5148 100644
--- a/target/linux/generic/pending-6.18/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch
+++ b/target/linux/generic/pending-6.18/760-02-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch
@@ -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
diff --git a/target/linux/generic/pending-6.18/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch b/target/linux/generic/pending-6.18/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch
index 50af43a019..2d95fb190f 100644
--- a/target/linux/generic/pending-6.18/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch
+++ b/target/linux/generic/pending-6.18/760-03-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch
@@ -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
diff --git a/target/linux/generic/pending-6.18/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch b/target/linux/generic/pending-6.18/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch
index bb94befddc..6b7599c925 100644
--- a/target/linux/generic/pending-6.18/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch
+++ b/target/linux/generic/pending-6.18/760-04-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch
@@ -1,307 +1,44 @@
-From 4b728cd74424738dbe35fcd6edb118a85a4ac83b Mon Sep 17 00:00:00 2001
+From 376f6f71bd1f291e0adf471f2753c89ed7eac9e6 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 22 Mar 2026 00:57:44 +0000
Subject: [PATCH 04/19] net: dsa: mxl862xx: add support for SerDes ports
The MxL862xx has two XPCS/SerDes interfaces (XPCS0 for ports 9-12,
-XPCS1 for ports 13-16). Each can operate in various single-lane
-modes (SGMII, 1000BASE-X, 2500BASE-X, 10GBASE-R, 10GBASE-KR,
-USXGMII) or as QSGMII providing four sub-ports.
+XPCS1 for ports 13-16). Each can operate in various single-lane modes
+(SGMII, 1000Base-X, 2500Base-X, 10GBase-R, 10GBase-KR, USXGMII) or as
+QSGMII or 10G_QXGMII providing four sub-ports per interface.
Implement phylink PCS operations using the firmware's XPCS API:
- - pcs_pre_config: power-sequence the SerDes (hard reset if already
- running, then PCS_ENABLE with the target interface mode), polling
- SIGNAL_DETECT until the XPCS exits reset.
- pcs_config: configure negotiation mode and CL37/SGMII advertising.
- - pcs_get_state: read link/speed/duplex/LPA from firmware and decode
- using phylink's standard CL37, SGMII, and USXGMII decoders, with
- firmware-resolved speed/duplex override for downshift detection.
+ - pcs_get_state: read link state and the link-partner ability word
+ from firmware and decode using phylink's standard CL37, SGMII, and
+ USXGMII decoders.
- pcs_an_restart: restart CL37 or CL73 auto-negotiation.
- pcs_link_up: force speed/duplex for SGMII.
- pcs_inband_caps: report per-mode in-band status capabilities.
-Register a PCS instance for each SerDes port and QSGMII sub-port
-during setup. Advertise the supported interface modes in
-phylink_get_caps based on port number.
+Register a PCS instance for each SerDes interface and
+QSGMII/10G_QXGMII sub-ports during setup. Advertise the supported
+interface modes in phylink_get_caps based on port number.
+
+Lacking support for expressing PHY-side role modes in Linux only the
+MAC-side of SGMII, QSGMII, USXGMII and 10G_QXGMII are implemented for
+now.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 474 +++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 13 +
- drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 411 ++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 2 +
- drivers/net/dsa/mxl862xx/mxl862xx.c | 5 +-
- drivers/net/dsa/mxl862xx/mxl862xx.h | 19 +
- 6 files changed, 907 insertions(+), 17 deletions(-)
+ drivers/net/dsa/mxl862xx/mxl862xx-api.h | 302 ++++++++++++++++
+ drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 9 +
+ drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 369 +++++++++++++++++++-
+ drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 7 +
+ drivers/net/dsa/mxl862xx/mxl862xx.c | 6 +-
+ drivers/net/dsa/mxl862xx/mxl862xx.h | 31 ++
+ 6 files changed, 721 insertions(+), 3 deletions(-)
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
-@@ -1185,6 +1185,242 @@ struct mxl862xx_ctp_port_assignment {
- } __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
-+ * @MXL862XX_CTP_PORT: Connectivity Termination Port (CTP)
-+ * @MXL862XX_BRIDGE_PORT: Bridge Port
-+ */
-+enum mxl862xx_port_type {
-+ MXL862XX_LOGICAL_PORT = 0,
-+ MXL862XX_PHYSICAL_PORT,
-+ MXL862XX_CTP_PORT,
-+ MXL862XX_BRIDGE_PORT,
-+};
-+
-+/**
-+ * 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. The port is configured to provide the clock as output signal
-+ * @MXL862XX_PORT_CLK_SLAVE: Clock Slave Mode. The port is configured to use the input clock signal
-+ */
-+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;
-+
-+/**
- * enum mxl862xx_stp_port_state - Spanning Tree Protocol port states
- * @MXL862XX_STP_PORT_STATE_FORWARD: Forwarding state
- * @MXL862XX_STP_PORT_STATE_DISABLE: Disabled/Discarding state
-@@ -1225,20 +1461,6 @@ struct mxl862xx_sys_fw_image_version {
- } __packed;
-
- /**
-- * enum mxl862xx_port_type - Port Type
-- * @MXL862XX_LOGICAL_PORT: Logical Port
-- * @MXL862XX_PHYSICAL_PORT: Physical Port
-- * @MXL862XX_CTP_PORT: Connectivity Termination Port (CTP)
-- * @MXL862XX_BRIDGE_PORT: Bridge Port
-- */
--enum mxl862xx_port_type {
-- MXL862XX_LOGICAL_PORT = 0,
-- MXL862XX_PHYSICAL_PORT,
-- MXL862XX_CTP_PORT,
-- MXL862XX_BRIDGE_PORT,
--};
--
--/**
- * 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
-@@ -1366,4 +1588,228 @@ struct mxl862xx_rmon_port_cnt {
+@@ -1366,4 +1366,306 @@ struct mxl862xx_rmon_port_cnt {
__le64 tx_good_bytes;
} __packed;
@@ -310,7 +47,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ * @MXL862XX_XPCS_IF_SGMII: SGMII
+ * @MXL862XX_XPCS_IF_1000BASEX: 1000BASE-X
+ * @MXL862XX_XPCS_IF_2500BASEX: 2500BASE-X
-+ * @MXL862XX_XPCS_IF_USXGMII: USXGMII
++ * @MXL862XX_XPCS_IF_USXGMII: USXGMII (single or quad)
+ * @MXL862XX_XPCS_IF_10GBASER: 10GBASE-R
+ * @MXL862XX_XPCS_IF_10GKR: 10GBASE-KR
+ * @MXL862XX_XPCS_IF_5GBASER: 5GBASE-R
@@ -340,26 +77,58 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+};
+
+/**
-+ * enum mxl862xx_xpcs_speed - PCS speed values
-+ * @MXL862XX_XPCS_SPEED_UNKNOWN: unknown speed
-+ * @MXL862XX_XPCS_SPEED_10: 10 Mbps
-+ * @MXL862XX_XPCS_SPEED_100: 100 Mbps
-+ * @MXL862XX_XPCS_SPEED_1000: 1000 Mbps
-+ * @MXL862XX_XPCS_SPEED_2500: 2500 Mbps
-+ * @MXL862XX_XPCS_SPEED_5000: 5000 Mbps
-+ * @MXL862XX_XPCS_SPEED_10000: 10000 Mbps
++ * enum mxl862xx_xpcs_role - PCS protocol role
++ * @MXL862XX_XPCS_ROLE_MAC: local end is MAC side (TX_CONFIG = 0)
++ * @MXL862XX_XPCS_ROLE_PHY: local end is PHY side (TX_CONFIG = 1)
++ *
++ * Selects the role the XPCS plays in protocols that have an asymmetric
++ * AN code word (Cisco SGMII / QSGMII / USXGMII). Drives
++ * VR_MII_AN_CTRL.TX_CONFIG: 0 means the local end receives the partner's
++ * AN word, 1 means it sources one. Ignored for symmetric protocols
++ * (1000BASE-X, 2500BASE-X, 10GBASE-R/KR).
++ */
++enum mxl862xx_xpcs_role {
++ MXL862XX_XPCS_ROLE_MAC = 0,
++ MXL862XX_XPCS_ROLE_PHY = 1,
++};
++
++/**
++ * enum mxl862xx_xpcs_usx_lane_mode - USXGMII lane mode
++ * @MXL862XX_XPCS_USX_SINGLE: single USXGMII lane
++ * @MXL862XX_XPCS_USX_QUAD: quad USXGMII (4 ports per lane)
+ */
-+enum mxl862xx_xpcs_speed {
-+ MXL862XX_XPCS_SPEED_UNKNOWN = 0,
-+ MXL862XX_XPCS_SPEED_10 = 10,
-+ MXL862XX_XPCS_SPEED_100 = 100,
-+ MXL862XX_XPCS_SPEED_1000 = 1000,
-+ MXL862XX_XPCS_SPEED_2500 = 2500,
-+ MXL862XX_XPCS_SPEED_5000 = 5000,
-+ MXL862XX_XPCS_SPEED_10000 = 10000,
++enum mxl862xx_xpcs_usx_lane_mode {
++ MXL862XX_XPCS_USX_SINGLE = 0,
++ MXL862XX_XPCS_USX_QUAD = 1,
+};
+
+/**
++ * union mxl862xx_xpcs_an_word - XPCS AN code word, tagged by interface mode
++ * @cl37: 16-bit base-page word exchanged over the CL37 hardware AN path
++ * (SR_MII_AN_ADV on write, SR_MII_LP_BABL on read). Carries the
++ * 802.3 CL37 base page for 1000BASE-X/2500BASE-X and the Cisco
++ * SGMII config word for SGMII/QSGMII.
++ * @usx: USXGMII 16-bit AN code word, MDIO_USXGMII_* layout
++ * @cl73: CL73 48-bit base page (10GBASE-KR), three 16-bit registers per
++ * 802.3 Annex 28C
++ * @cl73.adv1: CL73 SR_AN_ADV1 / SR_AN_LP_ABL1
++ * @cl73.adv2: CL73 SR_AN_ADV2 / SR_AN_LP_ABL2
++ * @cl73.adv3: CL73 SR_AN_ADV3 / SR_AN_LP_ABL3
++ *
++ * The host picks the right member based on the interface field of the
++ * surrounding struct (and, for the asymmetric protocols, on the role).
++ */
++union mxl862xx_xpcs_an_word {
++ __le16 cl37;
++ __le16 usx;
++ struct {
++ __le16 adv1;
++ __le16 adv2;
++ __le16 adv3;
++ } cl73;
++} __packed;
++
++/**
+ * enum mxl862xx_xpcs_duplex - PCS duplex mode
+ * @MXL862XX_XPCS_DUPLEX_HALF: half duplex
+ * @MXL862XX_XPCS_DUPLEX_FULL: full duplex
@@ -386,173 +155,215 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+};
+
+/**
-+ * enum mxl862xx_xpcs_reset_type - XPCS reset type
-+ * @MXL862XX_XPCS_RESET_VR: vendor-specific reset (fast)
-+ * @MXL862XX_XPCS_RESET_SOFT: PCS soft reset
-+ * @MXL862XX_XPCS_RESET_HARD: full hardware reset
-+ */
-+enum mxl862xx_xpcs_reset_type {
-+ MXL862XX_XPCS_RESET_VR = 0,
-+ MXL862XX_XPCS_RESET_SOFT = 1,
-+ MXL862XX_XPCS_RESET_HARD = 2,
-+};
-+
-+/**
-+ * struct mxl862xx_xpcs_pcs_cfg - PCS configuration
-+ * @port_id: XPCS port index (0 or 1)
-+ * @interface: interface mode (enum mxl862xx_xpcs_if_mode)
-+ * @neg_mode: negotiation mode (enum mxl862xx_xpcs_neg_mode)
-+ * @permit_pause: allow pause to MAC
-+ * @usx_lane_mode: USXGMII lane mode (0=single, 1=quad)
-+ * @phy_side: PHY side (1) or MAC side (0)
-+ * @advertising: CL37 advertisement word
-+ * @result: firmware result (>0 AN restart needed, 0 no change, <0 error)
++ * struct mxl862xx_xpcs_pcs_cfg - PCS configuration parameters
++ * @port_id: XPCS port index (0-3)
++ * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode
++ * @neg_mode: PCS negotiation mode. See &enum mxl862xx_xpcs_neg_mode
++ * @permit_pause: Allow pause to MAC
++ * @usx_lane_mode: USXGMII lane mode.
++ * See &enum mxl862xx_xpcs_usx_lane_mode
++ * @role: PCS protocol role. See &enum mxl862xx_xpcs_role
++ * @usx_subport: Sub-port (0-3) within the XPCS. Used by the firmware
++ * to set MAC pause per sub-port; ignored for the
++ * XPCS-wide bringup, which is idempotent across slots.
++ * @advertising: AN code word the local end transmits. The active union
++ * member is selected by @interface (and, for the asymmetric
++ * protocols, by @role). Ignored when the local end does
++ * not transmit an AN word (role=MAC for SGMII/QSGMII/
++ * USXGMII, 10GBASE-R, 5GBASE-R) or when @neg_mode is not
++ * INBAND_AN_ON. Pass all-zero to keep the firmware default
++ * advertisement.
++ * @result: Firmware result. >0 means the host must follow with an AN
++ * restart, 0 means no host follow-up is needed, <0 is an errno.
+ */
+struct mxl862xx_xpcs_pcs_cfg {
++#ifdef __LITTLE_ENDIAN_BITFIELD
++ u8 port_id:2;
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 neg_mode:2; /* enum mxl862xx_xpcs_neg_mode */
++ u8 permit_pause:1;
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++ u8 role:1; /* enum mxl862xx_xpcs_role */
++ u8 usx_subport:2;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
+ u8 port_id:2;
-+ u8 interface:6;
-+ u8 neg_mode:2;
++ u8 usx_subport:2;
++ u8 role:1; /* enum mxl862xx_xpcs_role */
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
+ u8 permit_pause:1;
-+ u8 usx_lane_mode:2;
-+ u8 phy_side:1;
-+ u8 __rsv:2;
-+ __le16 advertising;
++ u8 neg_mode:2; /* enum mxl862xx_xpcs_neg_mode */
++#else
++#error "Unknown bitfield endianness"
++#endif
++ union mxl862xx_xpcs_an_word advertising;
+ __le16 result;
+} __packed;
+
+/**
+ * struct mxl862xx_xpcs_pcs_state - PCS link state
-+ * @port_id: XPCS port index (0 or 1)
-+ * @interface: interface mode (enum mxl862xx_xpcs_if_mode)
-+ * @usx_lane_mode: USXGMII lane mode
-+ * @usx_subport: USXGMII sub-port index (0-3)
-+ * @link: link up
-+ * @an_complete: auto-negotiation complete
-+ * @duplex: duplex mode (enum mxl862xx_xpcs_duplex)
-+ * @pcs_fault: PCS fault detected
-+ * @pause: pause flags (bit 0 = symmetric, bit 1 = asymmetric)
-+ * @speed: link speed in Mbps (enum mxl862xx_xpcs_speed)
-+ * @lpa: raw link partner advertisement word
++ * @port_id: XPCS port index (0-3) (input)
++ * @interface: PCS interface mode (input).
++ * See &enum mxl862xx_xpcs_if_mode
++ * @usx_lane_mode: USX lane mode (input)
++ * @usx_subport: USX sub-port 0-3 (input)
++ * @link: Link up (1) / down (0) (output)
++ * @an_complete: Auto-negotiation complete (output)
++ * @duplex: Duplex mode (output). See &enum mxl862xx_xpcs_duplex
++ * @pcs_fault: PCS fault (output)
++ * @pause: Pause negotiation result, bit 0 symmetric, bit 1 asymmetric
++ * (output)
++ * @lp_eee_cap: Link partner supports EEE (output)
++ * @lp_eee_cs_cap: Link partner supports EEE clock-stop (output)
++ * @__rsv: reserved
++ * @__pad: padding
++ * @speed: Resolved speed in Mbit/s (output)
++ * @lpa: Link partner ability word (output). Same union as
++ * &union mxl862xx_xpcs_an_word; the host picks the member based on
++ * @interface.
+ */
+struct mxl862xx_xpcs_pcs_state {
++#ifdef __LITTLE_ENDIAN_BITFIELD
+ u8 port_id:2;
-+ u8 interface:6;
-+ u8 usx_lane_mode:2;
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
+ u8 usx_subport:2;
+ u8 link:1;
+ u8 an_complete:1;
-+ u8 duplex:1;
++ u8 duplex:1; /* enum mxl862xx_xpcs_duplex */
+ u8 pcs_fault:1;
+ u8 pause:2;
-+ u8 __rsv:6;
-+ u8 __pad;
-+ __le16 speed;
-+ __le16 lpa;
-+} __packed;
-+
-+/**
-+ * struct mxl862xx_xpcs_pcs_power - PCS enable/disable
-+ * @port_id: XPCS port index (0 or 1)
-+ * @interface: interface mode (enum mxl862xx_xpcs_if_mode)
-+ * @phy_side: PHY side (1) or MAC side (0)
-+ * @result: firmware result
-+ */
-+struct mxl862xx_xpcs_pcs_power {
-+ u8 port_id:2;
-+ u8 interface:6;
-+ u8 phy_side:1;
-+ u8 __rsv:7;
-+ __le16 result;
-+} __packed;
-+
-+/**
-+ * struct mxl862xx_xpcs_an_restart - AN restart parameters
-+ * @port_id: XPCS port index (0 or 1)
-+ * @interface: interface mode (enum mxl862xx_xpcs_if_mode)
-+ * @usx_lane_mode: USXGMII lane mode
-+ * @result: firmware result
-+ */
-+struct mxl862xx_xpcs_an_restart {
++ u8 lp_eee_cap:1;
++ u8 lp_eee_cs_cap:1;
++ u8 __rsv:4;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
+ u8 port_id:2;
-+ u8 interface:6;
-+ u8 usx_lane_mode:2;
-+ u8 __rsv:6;
-+ __le16 result;
-+} __packed;
-+
-+/**
-+ * struct mxl862xx_xpcs_an_disable - AN disable parameters
-+ * @port_id: XPCS port index
-+ * @result: firmware result
-+ */
-+struct mxl862xx_xpcs_an_disable {
-+ u8 port_id;
++ u8 pcs_fault:1;
++ u8 duplex:1; /* enum mxl862xx_xpcs_duplex */
++ u8 an_complete:1;
++ u8 link:1;
++ u8 usx_subport:2;
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++ u8 __rsv:4;
++ u8 lp_eee_cs_cap:1;
++ u8 lp_eee_cap:1;
++ u8 pause:2;
++#else
++#error "Unknown bitfield endianness"
++#endif
+ u8 __pad;
-+ __le16 result;
++ __le16 speed; /* Mbit/s */
++ union mxl862xx_xpcs_an_word lpa;
+} __packed;
+
+/**
-+ * struct mxl862xx_xpcs_force_speed - force PCS speed and duplex
++ * struct mxl862xx_xpcs_pcs_disable - PCS disable parameters
+ * @port_id: XPCS port index
-+ * @duplex: duplex mode (enum mxl862xx_xpcs_duplex)
-+ * @speed: speed in Mbps (enum mxl862xx_xpcs_speed)
-+ * @result: firmware result
++ * @__pad: padding
++ * @result: Firmware result. 0 on success, <0 on error.
++ *
++ * Asserts IDDQ + PHY + XPCS resets to power down the SERDES when the
++ * port is admin-down or no module is plugged in. The next PCS config
++ * implicitly powers it back up and reprograms the desired interface.
+ */
-+struct mxl862xx_xpcs_force_speed {
++struct mxl862xx_xpcs_pcs_disable {
+ u8 port_id;
-+ u8 duplex;
-+ __le16 speed;
++ u8 __pad;
+ __le16 result;
+} __packed;
+
+/**
-+ * struct mxl862xx_xpcs_loopback_cfg - loopback control
-+ * @port_id: XPCS port index
-+ * @mode: loopback mode (enum mxl862xx_xpcs_loopback_mode)
-+ * @result: firmware result
++ * struct mxl862xx_xpcs_an_restart - AN restart parameters
++ * @port_id: XPCS port index (0-3)
++ * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode
++ * @usx_lane_mode: USX lane mode
++ * @usx_subport: Sub-port (0-3) within the XPCS. Selects the lane
++ * whose AN is restarted for QSGMII and QUSXGMII;
++ * ignored by single-lane modes.
++ * @__rsv: reserved
++ * @result: Firmware result. 0 on success, <0 on error.
++ *
++ * Restarts auto-negotiation on a single sub-port of the XPCS. The
++ * SERDES must already be configured.
+ */
-+struct mxl862xx_xpcs_loopback_cfg {
-+ u8 port_id;
-+ u8 mode;
++struct mxl862xx_xpcs_an_restart {
++#ifdef __LITTLE_ENDIAN_BITFIELD
++ u8 port_id:2;
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++ u8 usx_subport:2;
++ u8 __rsv:4;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 port_id:2;
++ u8 __rsv:4;
++ u8 usx_subport:2;
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++#else
++#error "Unknown bitfield endianness"
++#endif
+ __le16 result;
+} __packed;
+
+/**
-+ * struct mxl862xx_xpcs_reset_cfg - XPCS reset
-+ * @port_id: XPCS port index
-+ * @reset_type: reset type (enum mxl862xx_xpcs_reset_type)
-+ * @result: firmware result
++ * struct mxl862xx_xpcs_pcs_link_up - PCS link-up parameters
++ * @port_id: XPCS port index (0-3)
++ * @interface: PCS interface mode. See &enum mxl862xx_xpcs_if_mode
++ * @duplex: Duplex mode. See &enum mxl862xx_xpcs_duplex
++ * @usx_lane_mode: USX lane mode (USXGMII only; ignored otherwise).
++ * See &enum mxl862xx_xpcs_usx_lane_mode
++ * @usx_subport: USX sub-port 0-3 (QUSXGMII only; ignored otherwise)
++ * @__rsv0: reserved
++ * @speed: Resolved speed in Mbit/s
++ * @result: Firmware result. 0 on success, <0 is errno.
++ *
++ * Called once per link-up event after the host has resolved the
++ * line-side speed/duplex (from the PHY's read_status, from a preceding
++ * PCS get-state, or from a fixed-link description).
+ */
-+struct mxl862xx_xpcs_reset_cfg {
-+ u8 port_id;
-+ u8 reset_type;
++struct mxl862xx_xpcs_pcs_link_up {
++#ifdef __LITTLE_ENDIAN_BITFIELD
++ u8 port_id:2;
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 duplex:1; /* enum mxl862xx_xpcs_duplex */
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++ u8 usx_subport:2;
++ u8 __rsv0:3;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ u8 interface:6; /* enum mxl862xx_xpcs_if_mode */
++ u8 port_id:2;
++ u8 __rsv0:3;
++ u8 usx_subport:2;
++ u8 usx_lane_mode:2; /* enum mxl862xx_xpcs_usx_lane_mode */
++ u8 duplex:1; /* enum mxl862xx_xpcs_duplex */
++#else
++#error "Unknown bitfield endianness"
++#endif
++ __le16 speed; /* Mbit/s */
+ __le16 result;
+} __packed;
+
#endif /* __MXL862XX_API_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
-@@ -25,6 +25,8 @@
+@@ -24,6 +24,7 @@
+ #define MXL862XX_SS_MAGIC 0x1600
#define GPY_GPY2XX_MAGIC 0x1800
#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_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
-@@ -71,6 +73,17 @@
+@@ -71,6 +72,14 @@
#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2)
-+#define MXL862XX_XPCS_MAGIC 0x1a00
+#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1)
+#define MXL862XX_XPCS_PCS_GET_STATE (MXL862XX_XPCS_MAGIC + 0x2)
-+#define MXL862XX_XPCS_PCS_ENABLE (MXL862XX_XPCS_MAGIC + 0x3)
+#define MXL862XX_XPCS_PCS_DISABLE (MXL862XX_XPCS_MAGIC + 0x4)
+#define MXL862XX_XPCS_AN_RESTART (MXL862XX_XPCS_MAGIC + 0x5)
-+#define MXL862XX_XPCS_AN_DISABLE (MXL862XX_XPCS_MAGIC + 0x6)
-+#define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7)
++#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)
+
@@ -561,11 +372,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#endif /* __MXL862XX_CMD_H */
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c
-@@ -7,10 +7,14 @@
+@@ -7,20 +7,384 @@
* Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
*/
-+#include <linux/iopoll.h>
++#include <linux/atomic.h>
#include <linux/phylink.h>
#include <net/dsa.h>
@@ -576,7 +387,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
#include "mxl862xx-phylink.h"
void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
-@@ -19,8 +23,410 @@ void mxl862xx_phylink_get_caps(struct ds
+ struct phylink_config *config)
+ {
++ struct mxl862xx_priv *priv = ds->priv;
++
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
MAC_100 | MAC_1000 | MAC_2500FD;
@@ -589,21 +403,29 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ break;
+ case 9:
+ case 13:
++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
++ break;
+ __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces);
-+ config->mac_capabilities |= MAC_10000FD | MAC_5000FD;
+ fallthrough;
+ case 10 ... 12:
+ case 14 ... 16:
++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
++ break;
+ __set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces);
++
+ break;
+ default:
+ break;
+ }
++
++ if (port == 9 || port == 13)
++ config->mac_capabilities |= MAC_10000FD | MAC_5000FD;
+}
+
+static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs)
@@ -611,11 +433,6 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ return container_of(pcs, struct mxl862xx_pcs, pcs);
+}
+
-+static int mxl862xx_xpcs_port_id(int port)
-+{
-+ return port >= 13;
-+}
-+
+static int mxl862xx_xpcs_if_mode(phy_interface_t interface)
+{
+ switch (interface) {
@@ -628,6 +445,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return MXL862XX_XPCS_IF_2500BASEX;
+ case PHY_INTERFACE_MODE_USXGMII:
++ case PHY_INTERFACE_MODE_10G_QXGMII:
+ return MXL862XX_XPCS_IF_USXGMII;
+ case PHY_INTERFACE_MODE_10GBASER:
+ return MXL862XX_XPCS_IF_10GBASER;
@@ -647,80 +465,50 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ return MXL862XX_XPCS_NEG_INBAND_AN_OFF;
+}
+
-+static struct mxl862xx_xpcs_signal_detect
-+mxl862xx_xpcs_signal_detect(struct mxl862xx_priv *priv, int port_id)
-+{
-+ struct mxl862xx_xpcs_signal_detect sd = { .port_id = port_id };
-+
-+ MXL862XX_API_READ(priv, MXL862XX_XPCS_SIGNAL_DETECT, sd);
-+
-+ return sd;
-+}
-+
-+static int mxl862xx_xpcs_poll_ready(struct mxl862xx_priv *priv, int port_id)
++static int mxl862xx_pcs_enable(struct phylink_pcs *pcs)
+{
-+ struct mxl862xx_xpcs_signal_detect sd;
-+ int ret;
++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
+
-+ ret = read_poll_timeout(mxl862xx_xpcs_signal_detect, sd,
-+ !sd.in_reset, 50000, 1000000,
-+ false, priv, port_id);
-+ if (ret)
-+ dev_warn(priv->ds->dev, "XPCS%d ready timeout\n", port_id);
++ /* Bringup is done idempotently by pcs_config; just account this
++ * sub-port so pcs_disable powers the shared XPCS down only after
++ * the last sub-port has been released.
++ */
++ atomic_inc(&mpcs->priv->serdes_refcount[mpcs->serdes_id]);
+
-+ return ret;
++ return 0;
+}
+
+static void mxl862xx_pcs_disable(struct phylink_pcs *pcs)
+{
+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
++ struct mxl862xx_xpcs_pcs_disable dis = {};
+ struct mxl862xx_priv *priv = mpcs->priv;
-+ int port = mpcs->port;
-+ struct mxl862xx_xpcs_pcs_power pwr = {};
+
-+ if (port != 9 && port != 13)
++ /* The SerDes is shared across QSGMII/QUSXGMII sub-ports; only
++ * power it down once the last active sub-port goes away.
++ */
++ if (!atomic_dec_and_test(&priv->serdes_refcount[mpcs->serdes_id]))
+ return;
+
-+ pwr.port_id = mxl862xx_xpcs_port_id(port);
++ dis.port_id = mpcs->serdes_id;
+
-+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, pwr);
-+ mpcs->enabled = false;
++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, dis);
+}
+
-+static void mxl862xx_pcs_pre_config(struct phylink_pcs *pcs,
-+ phy_interface_t interface)
++/* The XPCS firmware reports failures in the result field using its own
++ * libc errno values; ENOTSUP (134) in particular has no kernel errno.
++ * Translate the codes the firmware can actually return.
++ */
++static int mxl862xx_xpcs_errno(int result)
+{
-+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
-+ struct mxl862xx_priv *priv = mpcs->priv;
-+ int port = mpcs->port;
-+ struct mxl862xx_xpcs_pcs_power pwr = {};
-+ struct mxl862xx_xpcs_reset_cfg rst = {};
-+ int port_id, if_mode;
-+
-+ if (port != 9 && port != 13)
-+ return;
-+
-+ if_mode = mxl862xx_xpcs_if_mode(interface);
-+ if (if_mode < 0)
-+ return;
-+
-+ port_id = mxl862xx_xpcs_port_id(port);
-+
-+ /* Full reset only if PCS is already running (not after a clean disable,
-+ * which already asserts hardware reset via XPCS_PCS_DISABLE).
-+ */
-+ if (mpcs->enabled) {
-+ rst.port_id = port_id;
-+ rst.reset_type = MXL862XX_XPCS_RESET_HARD;
-+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_RESET, rst);
-+ mxl862xx_xpcs_poll_ready(priv, port_id);
++ switch (result) {
++ case -5: /* firmware -EIO */
++ return -EIO;
++ case -134: /* firmware -ENOTSUP */
++ return -EOPNOTSUPP;
++ default: /* firmware -EINVAL and anything unexpected */
++ return -EINVAL;
+ }
-+
-+ pwr.port_id = port_id;
-+ pwr.interface = if_mode;
-+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_ENABLE, pwr);
-+ mxl862xx_xpcs_poll_ready(priv, port_id);
-+ mpcs->enabled = true;
+}
+
+static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
@@ -730,13 +518,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+{
+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
+ struct mxl862xx_priv *priv = mpcs->priv;
-+ int port = mpcs->port;
+ struct mxl862xx_xpcs_pcs_cfg cfg = {};
+ int if_mode, ret;
++ u16 adv;
+
-+ /* Sub-interfaces are set up implicitly by the main interface */
-+ if (port != 9 && port != 13)
-+ return 0;
++ mpcs->interface = interface;
+
+ if_mode = mxl862xx_xpcs_if_mode(interface);
+ if (if_mode < 0) {
@@ -745,128 +531,99 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ return if_mode;
+ }
+
-+ mpcs->if_mode = if_mode;
-+
-+ cfg.port_id = mxl862xx_xpcs_port_id(port);
++ /* The XPCS bringup is per-instance and idempotent in the
++ * firmware: every QSGMII/QUSXGMII sub-port may call pcs_config
++ * and the firmware will skip the bringup if the requested mode
++ * matches the cached one, then update MAC pause for the
++ * sub-port indicated by @usx_subport.
++ */
++ cfg.port_id = mpcs->serdes_id;
++ cfg.usx_subport = mpcs->slot;
++ cfg.usx_lane_mode = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
++ MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
+ cfg.interface = if_mode;
+ cfg.neg_mode = mxl862xx_xpcs_neg_mode(neg_mode);
++ cfg.role = MXL862XX_XPCS_ROLE_MAC;
+ cfg.permit_pause = permit_pause_to_mac ? 1 : 0;
+
+ if (neg_mode & PHYLINK_PCS_NEG_INBAND) {
+ switch (interface) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
-+ cfg.advertising = cpu_to_le16(
-+ linkmode_adv_to_mii_adv_x(advertising,
-+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT));
++ adv = linkmode_adv_to_mii_adv_x(advertising,
++ ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
++ cfg.advertising.cl37 = cpu_to_le16(adv);
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
-+ cfg.advertising = cpu_to_le16(ADVERTISE_SGMII);
++ cfg.advertising.cl37 = cpu_to_le16(ADVERTISE_SGMII);
+ break;
+ default:
+ break;
+ }
+ }
+
-+ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_CONFIG, cfg);
++ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_CONFIG, cfg);
+ if (ret)
+ return ret;
+
-+ /* result > 0 means AN restart is needed */
-+ return le16_to_cpu(cfg.result) > 0 ? 1 : 0;
-+}
-+
-+static void mxl862xx_xpcs_decode_speed(u16 fw_speed,
-+ struct phylink_link_state *state)
-+{
-+ switch (fw_speed) {
-+ case MXL862XX_XPCS_SPEED_10:
-+ state->speed = SPEED_10;
-+ break;
-+ case MXL862XX_XPCS_SPEED_100:
-+ state->speed = SPEED_100;
-+ break;
-+ case MXL862XX_XPCS_SPEED_1000:
-+ state->speed = SPEED_1000;
-+ break;
-+ case MXL862XX_XPCS_SPEED_2500:
-+ state->speed = SPEED_2500;
-+ break;
-+ case MXL862XX_XPCS_SPEED_5000:
-+ state->speed = SPEED_5000;
-+ break;
-+ case MXL862XX_XPCS_SPEED_10000:
-+ state->speed = SPEED_10000;
-+ break;
-+ default:
-+ state->speed = SPEED_UNKNOWN;
-+ break;
-+ }
-+
-+ state->duplex = DUPLEX_FULL;
++ ret = (s16)le16_to_cpu(cfg.result);
++ if (ret < 0)
++ return mxl862xx_xpcs_errno(ret);
++ return ret > 0 ? 1 : 0;
+}
+
+static void mxl862xx_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_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
++ struct mxl862xx_priv *priv = mpcs->priv;
+ struct mxl862xx_xpcs_pcs_state st = {};
+ int if_mode, ret;
-+ u16 fw_speed, lpa, bmsr;
++ u16 bmsr;
+
+ if_mode = mxl862xx_xpcs_if_mode(state->interface);
+ if (if_mode < 0)
+ return;
+
-+ st.port_id = mxl862xx_xpcs_port_id(port);
++ st.port_id = mpcs->serdes_id;
+ st.interface = if_mode;
++ st.usx_subport = mpcs->slot;
++ st.usx_lane_mode = (state->interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
++ MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
+
+ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st);
+ if (ret)
+ return;
+
-+ fw_speed = le16_to_cpu(st.speed);
-+ lpa = le16_to_cpu(st.lpa);
-+
+ state->link = st.link && !st.pcs_fault;
-+ if (!state->link)
-+ return;
++ state->an_complete = st.an_complete;
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
-+ /* Synthesize BMSR from firmware state and use phylink's
-+ * standard CL37/SGMII decoders for LPA, pause, and speed.
-+ */
-+ bmsr = BMSR_LSTATUS;
-+ if (st.an_complete)
-+ bmsr |= BMSR_ANEGCOMPLETE;
-+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
-+
-+ /* Override speed/duplex with firmware's resolved values
-+ * for downshift detection.
-+ */
-+ mxl862xx_xpcs_decode_speed(fw_speed, state);
-+ state->duplex = st.duplex ? DUPLEX_FULL : DUPLEX_HALF;
++ bmsr = (state->link ? BMSR_LSTATUS : 0) |
++ (state->an_complete ? BMSR_ANEGCOMPLETE : 0);
++ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr,
++ le16_to_cpu(st.lpa.cl37));
+ break;
+
+ case PHY_INTERFACE_MODE_USXGMII:
-+ state->an_complete = st.an_complete;
-+ phylink_decode_usxgmii_word(state, lpa);
-+
-+ /* Override with firmware's resolved values */
-+ mxl862xx_xpcs_decode_speed(fw_speed, state);
-+ state->duplex = st.duplex ? DUPLEX_FULL : DUPLEX_HALF;
++ case PHY_INTERFACE_MODE_10G_QXGMII:
++ if (state->link)
++ phylink_decode_usxgmii_word(state,
++ le16_to_cpu(st.lpa.usx));
+ break;
+
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_10GKR:
-+ mxl862xx_xpcs_decode_speed(fw_speed, state);
++ if (state->link) {
++ state->speed = SPEED_10000;
++ state->duplex = DUPLEX_FULL;
++ }
+ break;
+
+ default:
@@ -879,14 +636,18 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+{
+ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
+ struct mxl862xx_priv *priv = mpcs->priv;
-+ int port = mpcs->port;
+ struct mxl862xx_xpcs_an_restart an = {};
++ int if_mode;
+
-+ if (port != 9 && port != 13)
++ if_mode = mxl862xx_xpcs_if_mode(mpcs->interface);
++ if (if_mode < 0)
+ return;
+
-+ an.port_id = mxl862xx_xpcs_port_id(port);
-+ an.interface = mpcs->if_mode;
++ an.port_id = mpcs->serdes_id;
++ an.interface = if_mode;
++ an.usx_subport = mpcs->slot;
++ an.usx_lane_mode = (mpcs->interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
++ MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
+
+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an);
+}
@@ -895,23 +656,33 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ phy_interface_t interface, int speed,
+ int duplex)
+{
-+ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv;
-+ int port = pcs_to_mxl862xx_pcs(pcs)->port;
-+ struct mxl862xx_xpcs_force_speed fs = {};
++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
++ struct mxl862xx_xpcs_pcs_link_up lu = {};
++ struct mxl862xx_priv *priv = mpcs->priv;
++ int if_mode;
+
-+ /* Only SGMII needs explicit speed forcing */
-+ if (interface != PHY_INTERFACE_MODE_SGMII)
++ /* With inband-AN enabled (role=MAC), the XPCS auto-resolves
++ * speed/duplex from the partner's AN word and the firmware
++ * short-circuits link_up. Skip the firmware round-trip, same
++ * as pcs-mtk-lynxi.
++ */
++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ return;
+
-+ if (port != 9 && port != 13)
++ if_mode = mxl862xx_xpcs_if_mode(interface);
++ if (if_mode < 0)
+ return;
+
-+ fs.port_id = mxl862xx_xpcs_port_id(port);
-+ fs.duplex = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL :
++ lu.port_id = mpcs->serdes_id;
++ lu.interface = if_mode;
++ lu.usx_subport = mpcs->slot;
++ lu.usx_lane_mode = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
++ MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
++ lu.duplex = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL :
+ MXL862XX_XPCS_DUPLEX_HALF;
-+ fs.speed = cpu_to_le16(speed);
++ lu.speed = cpu_to_le16(speed);
+
-+ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_FORCE_SPEED, fs);
++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_LINK_UP, lu);
+}
+
+static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs,
@@ -920,22 +691,23 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
-+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
-+ case PHY_INTERFACE_MODE_10GBASER:
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE |
-+ LINK_INBAND_BYPASS;
++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
++ case PHY_INTERFACE_MODE_USXGMII:
++ case PHY_INTERFACE_MODE_10G_QXGMII:
+ case PHY_INTERFACE_MODE_10GKR:
-+ return LINK_INBAND_ENABLE | LINK_INBAND_BYPASS;
++ return LINK_INBAND_ENABLE;
++ case PHY_INTERFACE_MODE_10GBASER:
++ return LINK_INBAND_DISABLE;
+ default:
+ return 0;
+ }
+}
+
+static const struct phylink_pcs_ops mxl862xx_pcs_ops = {
++ .pcs_enable = mxl862xx_pcs_enable,
+ .pcs_disable = mxl862xx_pcs_disable,
-+ .pcs_pre_config = mxl862xx_pcs_pre_config,
+ .pcs_config = mxl862xx_pcs_config,
+ .pcs_get_state = mxl862xx_pcs_get_state,
+ .pcs_an_restart = mxl862xx_pcs_an_restart,
@@ -947,18 +719,16 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ int port)
+{
+ pcs->priv = priv;
-+ pcs->port = port;
++ pcs->serdes_id = MXL862XX_SERDES_PORT_ID(port);
++ pcs->slot = MXL862XX_SERDES_SLOT(port);
++ pcs->interface = PHY_INTERFACE_MODE_NA;
+
+ pcs->pcs.ops = &mxl862xx_pcs_ops;
+ pcs->pcs.poll = true;
+
-+ /* Sub-ports only support QSGMII (quad mode with dedicated
-+ * PHY_INTERFACE_MODE). Single-lane USXGMII is supported on main
-+ * ports only; quad USXGMII is not yet supported as Linux lacks the
-+ * infrastructure to signal TDM mode before AN.
-+ */
+ __set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces);
-+ if (port != 9 && port != 13)
++ __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, pcs->pcs.supported_interfaces);
++ if (pcs->slot != 0)
+ return;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces);
@@ -977,7 +747,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ struct mxl862xx_priv *priv = dp->ds->priv;
+ int port = dp->index;
+
-+ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80))
++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
+ return NULL;
+
+ switch (port) {
@@ -989,7 +759,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
}
static void mxl862xx_phylink_mac_config(struct phylink_config *config,
-@@ -48,4 +454,5 @@ const struct phylink_mac_ops mxl862xx_ph
+@@ -48,4 +412,5 @@ const struct phylink_mac_ops mxl862xx_ph
.mac_config = mxl862xx_phylink_mac_config,
.mac_link_down = mxl862xx_phylink_mac_link_down,
.mac_link_up = mxl862xx_phylink_mac_link_up,
@@ -997,7 +767,15 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
};
--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h
-@@ -10,5 +10,7 @@
+@@ -7,8 +7,15 @@
+
+ #include "mxl862xx.h"
+
++#define MXL862XX_SERDES_SLOT(port) \
++ (((port) - MXL862XX_FIRST_SERDES_PORT) % MXL862XX_SERDES_SLOTS)
++#define MXL862XX_SERDES_PORT_ID(port) \
++ (((port) - MXL862XX_FIRST_SERDES_PORT) / MXL862XX_SERDES_SLOTS)
++
extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops;
void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config);
@@ -1016,55 +794,75 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
ret = mxl862xx_reset(priv);
if (ret)
-@@ -632,6 +632,9 @@ static int mxl862xx_setup(struct dsa_swi
+@@ -632,6 +632,10 @@ static int mxl862xx_setup(struct dsa_swi
if (ret)
return ret;
-+ for (i = 0; i < 8; i++)
-+ mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9);
++ for (i = 0; i < ARRAY_SIZE(priv->serdes_ports); i++)
++ mxl862xx_setup_pcs(priv, &priv->serdes_ports[i],
++ i + MXL862XX_FIRST_SERDES_PORT);
+
/* Calculate Extended VLAN block sizes.
* With VLAN Filter handling VID membership checks:
* Ingress: only final catchall rules (PVID insertion, 802.1Q
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
-@@ -243,6 +243,22 @@ struct mxl862xx_port {
+@@ -11,6 +11,9 @@
+ struct mxl862xx_priv;
+
+ #define MXL862XX_MAX_PORTS 17
++#define MXL862XX_FIRST_SERDES_PORT 9
++#define MXL862XX_SERDES_SLOTS 4
++
+ #define MXL862XX_DEFAULT_BRIDGE 0
+ #define MXL862XX_MAX_BRIDGES 48
+ #define MXL862XX_MAX_BRIDGE_PORTS 128
+@@ -243,6 +246,26 @@ struct mxl862xx_port {
};
/**
+ * struct mxl862xx_pcs - link SerDes interfaces to bridge ports
-+ * @pcs: &struct phylink_pcs instance
-+ * @priv: pointer to &struct mxl862xx_priv
-+ * @port: bridge port index
-+ * @if_mode: cached firmware interface mode (enum mxl862xx_xpcs_if_mode)
-+ * @enabled: true if the PCS/SerDes is currently powered up
++ * @pcs: &struct phylink_pcs instance
++ * @priv: pointer to &struct mxl862xx_priv
++ * @serdes_id: SerDes instance index (0 or 1)
++ * @slot: slot within the SerDes (0-3 for QSGMII/QUSXGMII, 0 otherwise)
++ * @interface: cached PHY interface, last value passed to pcs_config().
++ * %PHY_INTERFACE_MODE_NA before the first successful
++ * pcs_config(). Used by pcs_an_restart() to populate the
++ * firmware command and by pcs_disable() to skip the
++ * firmware power-down for shared (QSGMII/QUSXGMII) modes.
+ */
+struct mxl862xx_pcs {
+ struct phylink_pcs pcs;
+ struct mxl862xx_priv *priv;
-+ int port;
-+ int if_mode;
-+ bool enabled;
++ int serdes_id;
++ int slot;
++ phy_interface_t interface;
+};
+
+/**
- * 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
-@@ -293,6 +309,8 @@ union mxl862xx_fw_version {
+@@ -280,6 +303,12 @@ struct mxl862xx_fw_version {
* flooding)
* @fw_version: cached firmware version, populated at probe and
* compared with MXL862XX_FW_VER_MIN()
+ * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of
-+ * 10G_QXGMII
++ * 10G_QXGMII or QSGMII
++ * @serdes_refcount: per-XPCS count of sub-ports enabled by phylink;
++ * pcs_disable powers an XPCS down when the count
++ * reaches zero. atomic_t so concurrent sub-port
++ * enable/disable need no extra lock.
* @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
-@@ -311,6 +329,7 @@ struct mxl862xx_priv {
+@@ -298,6 +327,8 @@ struct mxl862xx_priv {
unsigned long flags;
u16 drop_meter;
- union mxl862xx_fw_version fw_version;
+ struct mxl862xx_fw_version fw_version;
+ struct mxl862xx_pcs serdes_ports[8];
++ atomic_t serdes_refcount[2];
struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
u16 bridges[MXL862XX_MAX_BRIDGES + 1];
u16 evlan_ingress_size;
diff --git a/target/linux/generic/pending-6.18/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch b/target/linux/generic/pending-6.18/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch
index 89c7751bbc..c96032be48 100644
--- a/target/linux/generic/pending-6.18/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch
+++ b/target/linux/generic/pending-6.18/760-05-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch
@@ -1,33 +1,55 @@
-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
+ * @ovrd: override value
@@ -95,41 +117,20 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+ 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 (!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;
++ 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;
+ }
++
++ *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);
}
diff --git a/target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch b/target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch
deleted file mode 100644
index f31e3cccee..0000000000
--- a/target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch
+++ /dev/null
@@ -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)
diff --git a/target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch b/target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch
index 935a82d168..a72fb14861 100644
--- a/target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch
+++ b/target/linux/generic/pending-6.18/760-06-net-dsa-mxl862xx-trap-link-local-and-multicast-snoop.patch
@@ -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_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.
+ */
-+#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
++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;
diff --git a/target/linux/generic/pending-6.18/760-08-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch b/target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch
index 48ffab50ec..979c301e97 100644
--- a/target/linux/generic/pending-6.18/760-08-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch
+++ b/target/linux/generic/pending-6.18/760-07-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-09-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch b/target/linux/generic/pending-6.18/760-08-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch
index 6cebfacd98..ad281fdf0f 100644
--- a/target/linux/generic/pending-6.18/760-09-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch
+++ b/target/linux/generic/pending-6.18/760-08-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-link-aggregation-support.patch b/target/linux/generic/pending-6.18/760-09-net-dsa-mxl862xx-add-link-aggregation-support.patch
index 3b50fae970..305b3d7204 100644
--- a/target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-link-aggregation-support.patch
+++ b/target/linux/generic/pending-6.18/760-09-net-dsa-mxl862xx-add-link-aggregation-support.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-11-net-dsa-mxl862xx-add-support-for-mirror-port.patch b/target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-support-for-mirror-port.patch
index 045abd1bc2..b12675e773 100644
--- a/target/linux/generic/pending-6.18/760-11-net-dsa-mxl862xx-add-support-for-mirror-port.patch
+++ b/target/linux/generic/pending-6.18/760-10-net-dsa-mxl862xx-add-support-for-mirror-port.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-12-net-dsa-wire-flash_update-devlink-callback-to-driver.patch b/target/linux/generic/pending-6.18/760-11-net-dsa-wire-flash_update-devlink-callback-to-driver.patch
index a12bfc33ef..ec266942de 100644
--- a/target/linux/generic/pending-6.18/760-12-net-dsa-wire-flash_update-devlink-callback-to-driver.patch
+++ b/target/linux/generic/pending-6.18/760-11-net-dsa-wire-flash_update-devlink-callback-to-driver.patch
@@ -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
diff --git a/target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch b/target/linux/generic/pending-6.18/760-12-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch
index 53a102904f..dc8b42e730 100644
--- a/target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch
+++ b/target/linux/generic/pending-6.18/760-12-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch
@@ -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
diff --git a/target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch b/target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch
index 9bfc096301..c0703d11f9 100644
--- a/target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch
+++ b/target/linux/generic/pending-6.18/760-13-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-implement-port-MTU-configuration.patch b/target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-implement-port-MTU-configuration.patch
index 0be764173c..07d78528d2 100644
--- a/target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-implement-port-MTU-configuration.patch
+++ b/target/linux/generic/pending-6.18/760-14-net-dsa-mxl862xx-implement-port-MTU-configuration.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch b/target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch
index 07a5f43a42..b4de64cb33 100644
--- a/target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch
+++ b/target/linux/generic/pending-6.18/760-15-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-17-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch b/target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch
index 445384aed5..39f1e52794 100644
--- a/target/linux/generic/pending-6.18/760-17-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch
+++ b/target/linux/generic/pending-6.18/760-16-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch
@@ -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;
diff --git a/target/linux/generic/pending-6.18/760-17-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch b/target/linux/generic/pending-6.18/760-17-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
new file mode 100644
index 0000000000..d4db97a25b
--- /dev/null
+++ b/target/linux/generic/pending-6.18/760-17-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
@@ -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(),
diff --git a/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch b/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch
new file mode 100644
index 0000000000..10d920e79d
--- /dev/null
+++ b/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch
@@ -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,
diff --git a/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch b/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
deleted file mode 100644
index bd2f15ca08..0000000000
--- a/target/linux/generic/pending-6.18/760-18-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch
+++ /dev/null
@@ -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(),
diff --git a/target/linux/generic/pending-6.18/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch b/target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch
index d0ad2eb2e6..4cebf99c14 100644
--- a/target/linux/generic/pending-6.18/760-27-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch
+++ b/target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-increase-CMD-timeout.patch
@@ -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
diff --git a/target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch b/target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch
deleted file mode 100644
index 154179c8f5..0000000000
--- a/target/linux/generic/pending-6.18/760-19-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch
+++ /dev/null
@@ -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 = {};