linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches
@ 2025-12-15  0:11 Daniel Golle
  2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Daniel Golle @ 2025-12-15  0:11 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Heiner Kallweit, Russell King,
	Simon Horman, netdev, devicetree, linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

Hi,

This series adds very basic DSA support for the MaxLinear MxL86252
(5 PHY ports) and MxL86282 (8 PHY ports) switches. The intent is to
validate and get feedback on the overall approach and driver structure,
especially the firmware-mediated host interface.

MxL862xx integrates a firmware running on an embedded processor (Zephyr
RTOS). Host interaction uses a simple API transported over MDIO/MMD.
This series includes only what's needed to pass traffic between user
ports and the CPU port: relayed MDIO to internal PHYs, basic port
enable/disable, and CPU-port special tagging.

Thanks for taking a look.

Changes since RFC v2
1/4, 2/4, 3/4: unchanged

4/4 net: dsa: add basic initial driver for MxL862xx switches
 * fix return value being uninitialized on error in mxl862xx_api_wrap()
 * add missing description in kerneldoc comment of
   struct mxl862xx_ss_sp_tag

Changes since initial RFC

1/4 dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx
 * better description in dt-bindings doc

2/4 net: dsa: add tag formats for MxL862xx switches
 * make sure all tag fields are initialized

3/4 net: mdio: add unlocked mdiodev C45 bus accessors
 * new patch

4/4 net: dsa: add basic initial driver for MxL862xx switches
 * make use of struct mdio_device
 * add phylink_mac_ops stubs
 * drop leftover nonsense from mxl862xx_phylink_get_caps()
 * fix endian conversions
 * use __le32 instead of enum types in over-the-wire structs
 * use existing MDIO_* macros whenever possible
 * simplify API constants to be more readable
 * use readx_poll_timeout instead of open-coding poll timeout loop
 * add mxl862xx_reg_read() and mxl862xx_reg_write() helpers
 * demystify error codes returned by the firmware
 * add #defines for mxl862xx_ss_sp_tag member values
 * move reset to dedicated function, clarify magic number being the
   reset command ID

Daniel Golle (4):
  dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx
  net: dsa: add tag formats for MxL862xx switches
  net: mdio: add unlocked mdiodev C45 bus accessors
  net: dsa: add basic initial driver for MxL862xx switches

 .../bindings/net/dsa/maxlinear,mxl862xx.yaml  | 162 ++++++++
 MAINTAINERS                                   |   8 +
 drivers/net/dsa/Kconfig                       |   2 +
 drivers/net/dsa/Makefile                      |   1 +
 drivers/net/dsa/mxl862xx/Kconfig              |  12 +
 drivers/net/dsa/mxl862xx/Makefile             |   3 +
 drivers/net/dsa/mxl862xx/mxl862xx-api.h       | 118 ++++++
 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h       |  28 ++
 drivers/net/dsa/mxl862xx/mxl862xx-host.c      | 230 +++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-host.h      |   4 +
 drivers/net/dsa/mxl862xx/mxl862xx.c           | 361 ++++++++++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx.h           |  24 ++
 include/linux/mdio.h                          |  13 +
 include/net/dsa.h                             |   2 +
 net/dsa/Kconfig                               |   7 +
 net/dsa/Makefile                              |   1 +
 net/dsa/tag_mxl862xx.c                        | 113 ++++++
 17 files changed, 1089 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
 create mode 100644 drivers/net/dsa/mxl862xx/Kconfig
 create mode 100644 drivers/net/dsa/mxl862xx/Makefile
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-api.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.c
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.c
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.h
 create mode 100644 net/dsa/tag_mxl862xx.c

-- 
2.52.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx
  2025-12-15  0:11 [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches Daniel Golle
@ 2025-12-15  0:11 ` Daniel Golle
  2025-12-16 22:48   ` Vladimir Oltean
  2025-12-17  1:04   ` Rob Herring
  2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 11+ messages in thread
From: Daniel Golle @ 2025-12-15  0:11 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Heiner Kallweit, Russell King,
	Simon Horman, netdev, devicetree, linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

Add documentation and an example for MaxLinear MxL86282 and MxL86252
switches.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
RFC v3: no changes
RFC v2: better description in dt-bindings doc

 .../bindings/net/dsa/maxlinear,mxl862xx.yaml  | 162 ++++++++++++++++++
 MAINTAINERS                                   |   6 +
 2 files changed, 168 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml

diff --git a/Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml b/Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
new file mode 100644
index 0000000000000..159b64d5474b4
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/maxlinear,mxl862xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MaxLinear MxL862xx Ethernet Switch Family
+
+maintainers:
+  - Daniel Golle <daniel@makrotopia.org>
+
+description:
+  The MaxLinear MxL862xx switch family are multi-port Ethernet switches with
+  integrated 2.5GE PHYs. The MxL86252 has five PHY ports and the MxL86282
+  has eight PHY ports. Both models come with two 10 Gigabit/s SerDes
+  interfaces to be used to connect external PHYs or SFP cages, or as CPU
+  port.
+
+allOf:
+  - $ref: dsa.yaml#/$defs/ethernet-ports
+
+properties:
+  compatible:
+    enum:
+      - maxlinear,mxl86252
+      - maxlinear,mxl86282
+
+  reg:
+    maxItems: 1
+    description: MDIO address of the switch
+
+  mdio:
+    $ref: /schemas/net/mdio.yaml#
+    unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        switch@0 {
+            compatible = "maxlinear,mxl86282";
+            reg = <0>;
+
+            ethernet-ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    label = "lan1";
+                    phy-handle = <&phy0>;
+                    phy-mode = "internal";
+                };
+
+                port@1 {
+                    reg = <1>;
+                    label = "lan2";
+                    phy-handle = <&phy1>;
+                    phy-mode = "internal";
+                };
+
+                port@2 {
+                    reg = <2>;
+                    label = "lan3";
+                    phy-handle = <&phy2>;
+                    phy-mode = "internal";
+                };
+
+                port@3 {
+                    reg = <3>;
+                    label = "lan4";
+                    phy-handle = <&phy3>;
+                    phy-mode = "internal";
+                };
+
+                port@4 {
+                    reg = <4>;
+                    label = "lan5";
+                    phy-handle = <&phy4>;
+                    phy-mode = "internal";
+                };
+
+                port@5 {
+                    reg = <5>;
+                    label = "lan6";
+                    phy-handle = <&phy5>;
+                    phy-mode = "internal";
+                };
+
+                port@6 {
+                    reg = <6>;
+                    label = "lan7";
+                    phy-handle = <&phy6>;
+                    phy-mode = "internal";
+                };
+
+                port@7 {
+                    reg = <7>;
+                    label = "lan8";
+                    phy-handle = <&phy7>;
+                    phy-mode = "internal";
+                };
+
+                port@8 {
+                    reg = <8>;
+                    label = "cpu";
+                    ethernet = <&gmac0>;
+                    phy-mode = "usxgmii";
+
+                    fixed-link {
+                        speed = <10000>;
+                        full-duplex;
+                    };
+                };
+            };
+
+            mdio {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                phy0: ethernet-phy@0 {
+                    reg = <0>;
+                };
+
+                phy1: ethernet-phy@1 {
+                    reg = <1>;
+                };
+
+                phy2: ethernet-phy@2 {
+                    reg = <2>;
+                };
+
+                phy3: ethernet-phy@3 {
+                    reg = <3>;
+                };
+
+                phy4: ethernet-phy@4 {
+                    reg = <4>;
+                };
+
+                phy5: ethernet-phy@5 {
+                    reg = <5>;
+                };
+
+                phy6: ethernet-phy@6 {
+                    reg = <6>;
+                };
+
+                phy7: ethernet-phy@7 {
+                    reg = <7>;
+                };
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 9707f53d62935..c433a15d9797a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15604,6 +15604,12 @@ S:	Supported
 F:	drivers/net/phy/mxl-86110.c
 F:	drivers/net/phy/mxl-gpy.c
 
+MAXLINEAR MXL862XX SWITCH DRIVER
+M:	Daniel Golle <daniel@makrotopia.org>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
+
 MCAN DEVICE DRIVER
 M:	Markus Schneider-Pargmann <msp@baylibre.com>
 L:	linux-can@vger.kernel.org
-- 
2.52.0

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches
  2025-12-15  0:11 [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches Daniel Golle
  2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
@ 2025-12-15  0:11 ` Daniel Golle
  2025-12-15  1:46   ` Andrew Lunn
                     ` (2 more replies)
  2025-12-15  0:12 ` [PATCH RFC net-next v3 3/4] net: mdio: add unlocked mdiodev C45 bus accessors Daniel Golle
  2025-12-15  0:12 ` [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches Daniel Golle
  3 siblings, 3 replies; 11+ messages in thread
From: Daniel Golle @ 2025-12-15  0:11 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Heiner Kallweit, Russell King,
	Simon Horman, netdev, devicetree, linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

Add proprietary special tag format for the MaxLinear MXL862xx family of
switches. While using the same Ethertype as MaxLinear's GSW1xx swtiches,
the actual tag format differs significantly, hence we need a dedicated
tag driver for that.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
RFC v3: no changes
RFC v2: make sure all tag fields are initialized

 MAINTAINERS            |   1 +
 include/net/dsa.h      |   2 +
 net/dsa/Kconfig        |   7 +++
 net/dsa/Makefile       |   1 +
 net/dsa/tag_mxl862xx.c | 113 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 124 insertions(+)
 create mode 100644 net/dsa/tag_mxl862xx.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c433a15d9797a..a20498cc8320b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15609,6 +15609,7 @@ M:	Daniel Golle <daniel@makrotopia.org>
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
+F:	net/dsa/tag_mxl862xx.c
 
 MCAN DEVICE DRIVER
 M:	Markus Schneider-Pargmann <msp@baylibre.com>
diff --git a/include/net/dsa.h b/include/net/dsa.h
index e40cdc12f7f39..e4c2b47a2a46e 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -57,6 +57,7 @@ struct tc_action;
 #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE	29
 #define DSA_TAG_PROTO_YT921X_VALUE		30
 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE		31
+#define DSA_TAG_PROTO_MXL862_VALUE		32
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -91,6 +92,7 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_VSC73XX_8021Q	= DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
 	DSA_TAG_PROTO_YT921X		= DSA_TAG_PROTO_YT921X_VALUE,
 	DSA_TAG_PROTO_MXL_GSW1XX	= DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
+	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE,
 };
 
 struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index f86b30742122f..c897d62326f5b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -145,6 +145,13 @@ config NET_DSA_TAG_QCA
 	  Say Y or M if you want to enable support for tagging frames for
 	  the Qualcomm Atheros QCA8K switches.
 
+config NET_DSA_TAG_MXL862
+	tristate "Tag driver for MxL862xx switches"
+	help
+	  Say Y or M if you want to enable support for tagging frames for the
+	  Maxlinear MxL86252 and MxL86282 switches using their native 8-byte
+	  tagging protocol.
+
 config NET_DSA_TAG_RTL4_A
 	tristate "Tag driver for Realtek 4 byte protocol A tags"
 	help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 42d173f5a7013..dbe2a742e3322 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
 obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
 obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
 obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
+obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o
 obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
 obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
 obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
diff --git a/net/dsa/tag_mxl862xx.c b/net/dsa/tag_mxl862xx.c
new file mode 100644
index 0000000000000..9c5e5f90dcb63
--- /dev/null
+++ b/net/dsa/tag_mxl862xx.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DSA Special Tag for MaxLinear 862xx switch chips
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2024 MaxLinear Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/dsa.h>
+#include "tag.h"
+
+#define MXL862_NAME	"mxl862xx"
+
+/* To define the outgoing port and to discover the incoming port a special
+ * tag is used by the GSW1xx.
+ *
+ *       Dest MAC       Src MAC    special TAG        EtherType
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
+ *                                |<--------------->|
+ */
+
+#define MXL862_HEADER_LEN 8
+
+/* Byte 7 */
+#define MXL862_IGP_EGP GENMASK(3, 0)
+
+static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb,
+				       struct net_device *dev)
+{
+	struct dsa_port *dp = dsa_user_to_port(dev);
+	struct dsa_port *cpu_dp = dp->cpu_dp;
+	unsigned int cpu_port = cpu_dp->index + 1;
+	unsigned int usr_port = dp->index + 1;
+	__be16 *mxl862_tag;
+
+	if (!skb)
+		return skb;
+
+	/* provide additional space 'MXL862_HEADER_LEN' bytes */
+	skb_push(skb, MXL862_HEADER_LEN);
+
+	/* shift MAC address to the beginnig of the enlarged buffer,
+	 * releasing the space required for DSA tag (between MAC address and
+	 * Ethertype)
+	 */
+	dsa_alloc_etype_header(skb, MXL862_HEADER_LEN);
+
+	/* special tag ingress */
+	mxl862_tag = dsa_etype_header_pos_tx(skb);
+	mxl862_tag[0] = htons(ETH_P_MXLGSW);
+	mxl862_tag[1] = 0;
+	mxl862_tag[2] = htons(usr_port + 16 - cpu_port);
+	mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port));
+
+	return skb;
+}
+
+static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	int port;
+	__be16 *mxl862_tag;
+
+	if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) {
+		dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n");
+		return NULL;
+	}
+
+	mxl862_tag = dsa_etype_header_pos_rx(skb);
+
+	if (unlikely(mxl862_tag[0] != htons(ETH_P_MXLGSW))) {
+		dev_warn_ratelimited(&dev->dev, "Invalid special tag marker, packet dropped\n");
+		dev_warn_ratelimited(&dev->dev, "Rx Packet Tag: %8ph\n",
+				     mxl862_tag);
+		return NULL;
+	}
+
+	/* Get source port information */
+	port = FIELD_GET(MXL862_IGP_EGP, ntohs(mxl862_tag[3]));
+	port = port - 1;
+	skb->dev = dsa_conduit_find_user(dev, 0, port);
+	if (!skb->dev) {
+		dev_warn_ratelimited(&dev->dev, "Invalid source port, packet dropped\n");
+		dev_warn_ratelimited(&dev->dev, "Rx Packet Tag: %8ph\n",
+				     mxl862_tag);
+		return NULL;
+	}
+
+	/* remove the MxL862xx special tag between the MAC addresses and the
+	 * current ethertype field.
+	 */
+	skb_pull_rcsum(skb, MXL862_HEADER_LEN);
+	dsa_strip_etype_header(skb, MXL862_HEADER_LEN);
+
+	return skb;
+}
+
+static const struct dsa_device_ops mxl862_netdev_ops = {
+	.name = "mxl862",
+	.proto = DSA_TAG_PROTO_MXL862,
+	.xmit = mxl862_tag_xmit,
+	.rcv = mxl862_tag_rcv,
+	.needed_headroom = MXL862_HEADER_LEN,
+};
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME);
+MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches");
+MODULE_LICENSE("GPL");
+
+module_dsa_tag_driver(mxl862_netdev_ops);
-- 
2.52.0

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC net-next v3 3/4] net: mdio: add unlocked mdiodev C45 bus accessors
  2025-12-15  0:11 [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches Daniel Golle
  2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
  2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
@ 2025-12-15  0:12 ` Daniel Golle
  2025-12-15  0:12 ` [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches Daniel Golle
  3 siblings, 0 replies; 11+ messages in thread
From: Daniel Golle @ 2025-12-15  0:12 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Heiner Kallweit, Russell King,
	Simon Horman, netdev, devicetree, linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

Add helper inline functions __mdiodev_c45_read() and
__mdiodev_c45_write(), which are the C45 equivalents of the existing
__mdiodev_read() and __mdiodev_write() added by commit e6a45700e7e1
("net: mdio: add unlocked mdiobus and mdiodev bus accessors")

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
RFC v3: no changes
RFC v2: add this patch, initial submission

 include/linux/mdio.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 42d6d47e445b7..f39b4dba5cd4f 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -648,6 +648,19 @@ static inline int mdiodev_modify_changed(struct mdio_device *mdiodev,
 				      mask, set);
 }
 
+static inline int __mdiodev_c45_read(struct mdio_device *mdiodev, int devad,
+				     u16 regnum)
+{
+	return __mdiobus_c45_read(mdiodev->bus, mdiodev->addr, devad, regnum);
+}
+
+static inline int __mdiodev_c45_write(struct mdio_device *mdiodev, u32 devad,
+				      u16 regnum, u16 val)
+{
+	return __mdiobus_c45_write(mdiodev->bus, mdiodev->addr, devad, regnum,
+				 val);
+}
+
 static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
 				     u32 regnum, u16 mask, u16 set)
 {
-- 
2.52.0

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches
  2025-12-15  0:11 [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches Daniel Golle
                   ` (2 preceding siblings ...)
  2025-12-15  0:12 ` [PATCH RFC net-next v3 3/4] net: mdio: add unlocked mdiodev C45 bus accessors Daniel Golle
@ 2025-12-15  0:12 ` Daniel Golle
  2025-12-16 22:43   ` Vladimir Oltean
  3 siblings, 1 reply; 11+ messages in thread
From: Daniel Golle @ 2025-12-15  0:12 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Heiner Kallweit, Russell King,
	Simon Horman, netdev, devicetree, linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

Add very basic DSA driver for MaxLinear's MxL862xx switches.

In contrast to previous MaxLinear switches the MxL862xx has a built-in
processor that runs a sophisticated firmware based on Zephyr RTOS.
Interaction between the host and the switch hence is organized using a
software API of that firmware rather than accessing hardware registers
directly.

Add descriptions of the most basic firmware API calls to access the
built-in MDIO bus hosting the 2.5GE PHYs, basic port control as well as
setting up the CPU port.

Implement a very basic DSA driver using that API which is sufficient to
get packets flowing between the user ports and the CPU port.

The firmware offers all features one would expect from a modern switch
hardware, they will be added one by one in follow-up patch series.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
RFC v3:
 * fix return value being uninitialized on error in mxl862xx_api_wrap()
 * add missing descrition in kerneldoc comment of
   struct mxl862xx_ss_sp_tag

RFC v2:
 * make use of struct mdio_device
 * add phylink_mac_ops stubs
 * drop leftover nonsense from mxl862xx_phylink_get_caps()
 * use __le32 instead of enum types in over-the-wire structs
 * use existing MDIO_* macros whenever possible
 * simplify API constants to be more readable
 * use readx_poll_timeout instead of open-coding poll timeout loop
 * add mxl862xx_reg_read() and mxl862xx_reg_write() helpers
 * demystify error codes returned by the firmware
 * add #defines for mxl862xx_ss_sp_tag member values
 * move reset to dedicated function, clarify magic number being the
   reset command ID

 MAINTAINERS                              |   1 +
 drivers/net/dsa/Kconfig                  |   2 +
 drivers/net/dsa/Makefile                 |   1 +
 drivers/net/dsa/mxl862xx/Kconfig         |  12 +
 drivers/net/dsa/mxl862xx/Makefile        |   3 +
 drivers/net/dsa/mxl862xx/mxl862xx-api.h  | 118 ++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h  |  28 ++
 drivers/net/dsa/mxl862xx/mxl862xx-host.c | 230 +++++++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-host.h |   4 +
 drivers/net/dsa/mxl862xx/mxl862xx.c      | 361 +++++++++++++++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx.h      |  24 ++
 11 files changed, 784 insertions(+)
 create mode 100644 drivers/net/dsa/mxl862xx/Kconfig
 create mode 100644 drivers/net/dsa/mxl862xx/Makefile
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-api.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.c
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.h
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.c
 create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.h

diff --git a/MAINTAINERS b/MAINTAINERS
index a20498cc8320b..17ca0351cc5b6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15609,6 +15609,7 @@ M:	Daniel Golle <daniel@makrotopia.org>
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
+F:	drivers/net/dsa/mxl862xx/
 F:	net/dsa/tag_mxl862xx.c
 
 MCAN DEVICE DRIVER
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 7eb301fd987d1..18f6e8b7f4cb2 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -74,6 +74,8 @@ source "drivers/net/dsa/microchip/Kconfig"
 
 source "drivers/net/dsa/mv88e6xxx/Kconfig"
 
+source "drivers/net/dsa/mxl862xx/Kconfig"
+
 source "drivers/net/dsa/ocelot/Kconfig"
 
 source "drivers/net/dsa/qca/Kconfig"
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 16de4ba3fa388..f5a463b87ec25 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -20,6 +20,7 @@ obj-y				+= hirschmann/
 obj-y				+= lantiq/
 obj-y				+= microchip/
 obj-y				+= mv88e6xxx/
+obj-y				+= mxl862xx/
 obj-y				+= ocelot/
 obj-y				+= qca/
 obj-y				+= realtek/
diff --git a/drivers/net/dsa/mxl862xx/Kconfig b/drivers/net/dsa/mxl862xx/Kconfig
new file mode 100644
index 0000000000000..5c538dfc2763e
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_MXL862
+	tristate "MaxLinear MxL862xx"
+	depends on NET_DSA
+	select MAXLINEAR_GPHY
+	select NET_DSA_TAG_MXL862
+	help
+	  This enables support for the MaxLinear MxL862xx switch family.
+	  These switches got two 10GE SerDes interfaces, one typically
+	  used as CPU port.
+	   MxL86282 Eight 2.5 Gigabit PHYs
+	   MxL86252 Five 2.5 Gigabit PHYs
\ No newline at end of file
diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/Makefile
new file mode 100644
index 0000000000000..d23dd3cd511d4
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
+mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
new file mode 100644
index 0000000000000..e2b0a04366aa6
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * struct mdio_relay_data - relayed access to the switch internal MDIO bus
+ * @data: data to be read or written
+ * @phy: PHY index
+ * @mmd: MMD device
+ * @reg: register rndex
+ */
+struct mdio_relay_data {
+	__le16 data;
+	u8 phy;
+	u8 mmd;
+	__le16 reg;
+} __packed;
+
+/* Register access parameter to directly modify internal registers */
+struct mxl862xx_register_mod {
+	__le16 addr;
+	__le16 data;
+	__le16 mask;
+} __packed;
+
+#define MXL862XX_SS_SP_TAG_MASK_RX			BIT(0)
+#define MXL862XX_SS_SP_TAG_MASK_TX			BIT(1)
+#define MXL862XX_SS_SP_TAG_MASK_RX_PEN			BIT(2)
+#define MXL862XX_SS_SP_TAG_MASK_TX_PEN			BIT(3)
+
+#define MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT		0
+#define MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT		1
+#define MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT		2
+
+#define MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE		0
+#define MXL862XX_SS_SP_TAG_TX_TAG_REPLACE		1
+#define MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE		2
+#define MXL862XX_SS_SP_TAG_TX_TAG_REMOVE		3
+
+/**
+ * struct mxl862xx_ss_sp_tag - Special tag port settings
+ * @pid: port ID (1~16)
+ * @mask: bit value 1 to indicate valid field
+ *	0 - rx
+ *	1 - tx
+ *	2 - rx_pen
+ *	3 - tx_pen
+ * @rx: RX special tag mode
+ *	0 - packet does NOT have special tag and special tag is NOT inserted
+ *	1 - packet does NOT have special tag and special tag is inserted
+ *	2 - packet has special tag and special tag is NOT inserted
+ * @tx: TX special tag mode
+ *	0 - packet does NOT have special tag and special tag is NOT removed
+ *	1 - packet has special tag and special tag is replaced
+ *	2 - packet has special tag and special tag is NOT removed
+ *	3 - packet has special tag and special tag is removed
+ * @rx_pen: RX special tag info over preamble
+ *	0 - special tag info inserted from byte 2 to 7 are all 0
+ *	1 - special tag byte 5 is 16, other bytes from 2 to 7 are 0
+ *	2 - special tag byte 5 is from preamble field, others are 0
+ *	3 - special tag byte 2 to 7 are from preabmle field
+ * @tx_pen: TX special tag info over preamble
+ *	0 - disabled
+ *	1 - enabled
+ */
+struct mxl862xx_ss_sp_tag {
+	u8 pid;
+	u8 mask;
+	u8 rx;
+	u8 tx;
+	u8 rx_pen;
+	u8 tx_pen;
+} __packed;
+
+/**
+ * enum mxl862xx_logical_port_mode - Logical port mode
+ * @MXL862XX_LOGICAL_PORT_8BIT_WLAN: WLAN with 8-bit station ID
+ * @MXL862XX_LOGICAL_PORT_9BIT_WLAN: WLAN with 9-bit station ID
+ * @MXL862XX_LOGICAL_PORT_GPON: GPON OMCI context
+ * @MXL862XX_LOGICAL_PORT_EPON: EPON context
+ * @MXL862XX_LOGICAL_PORT_GINT: G.INT context
+ * @MXL862XX_LOGICAL_PORT_OTHER: Others
+ */
+enum mxl862xx_logical_port_mode {
+	MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0,
+	MXL862XX_LOGICAL_PORT_9BIT_WLAN,
+	MXL862XX_LOGICAL_PORT_GPON,
+	MXL862XX_LOGICAL_PORT_EPON,
+	MXL862XX_LOGICAL_PORT_GINT,
+	MXL862XX_LOGICAL_PORT_OTHER = 0xFF,
+};
+
+/**
+ * struct mxl862xx_ctp_port_assignment - CTP Port Assignment/association with logical port
+ * @logical_port_id: Logical Port Id. The valid range is hardware dependent
+ * @first_ctp_port_id: First CTP Port ID mapped to above logical port ID
+ * @number_of_ctp_port: Total number of CTP Ports mapped above logical port ID
+ * @mode: See &enum mxl862xx_logical_port_mode
+ * @bridge_port_id: Bridge ID (FID)
+ */
+struct mxl862xx_ctp_port_assignment {
+	u8 logical_port_id;
+	__le16 first_ctp_port_id;
+	__le16 number_of_ctp_port;
+	__le32 mode; /* enum mxl862xx_logical_port_mode */
+	__le16 bridge_port_id;
+} __packed;
+
+/**
+ * struct mxl862xx_sys_fw_image_version - Firmware version information
+ * @iv_major: firmware major version
+ * @iv_minor: firmware minor version
+ * @iv_revision: firmware revision
+ * @iv_build_num: firmware build number
+ */
+struct mxl862xx_sys_fw_image_version {
+	u8 iv_major;
+	u8 iv_minor;
+	__le16 iv_revision;
+	__le32 iv_build_num;
+} __packed;
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
new file mode 100644
index 0000000000000..db6a4c3f54f22
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#define MXL862XX_MMD_DEV 30
+#define MXL862XX_MMD_REG_CTRL 0
+#define MXL862XX_MMD_REG_LEN_RET 1
+#define MXL862XX_MMD_REG_DATA_FIRST 2
+#define MXL862XX_MMD_REG_DATA_LAST 95
+#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
+	(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
+
+#define MXL862XX_COMMON_MAGIC 0x100
+#define MXL862XX_CTP_MAGIC 0x500
+#define MXL862XX_SS_MAGIC 0x1600
+#define GPY_GPY2XX_MAGIC 0x1800
+#define SYS_MISC_MAGIC 0x1900
+
+#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
+
+#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3)
+
+#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02)
+
+#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01)
+#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02)
+
+#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02)
+
+#define MMD_API_MAXIMUM_ID 0x7FFF
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.c b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
new file mode 100644
index 0000000000000..ca13c43bd9efa
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Based upon the Maxlinear SDK driver
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2025 John Crispin <john@phrozen.org>
+ * Copyright (C) 2024 MaxLinear Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/iopoll.h>
+#include <net/dsa.h>
+#include "mxl862xx.h"
+#include "mxl862xx-host.h"
+
+#define CTRL_BUSY_MASK			BIT(15)
+
+#define MXL862XX_MMD_REG_CTRL		0
+#define MXL862XX_MMD_REG_LEN_RET	1
+#define MXL862XX_MMD_REG_DATA_FIRST	2
+#define MXL862XX_MMD_REG_DATA_LAST	95
+#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
+		(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
+
+#define MMD_API_SET_DATA_0		2
+#define MMD_API_GET_DATA_0		5
+#define MMD_API_RST_DATA		8
+
+#define MXL862XX_SWITCH_RESET 0x9907
+
+static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr)
+{
+	return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr);
+}
+
+static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data)
+{
+	return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data);
+}
+
+static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv)
+{
+	return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL);
+}
+
+static int mxl862xx_busy_wait(struct mxl862xx_priv *priv)
+{
+	int val;
+
+	return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
+				  !(val & CTRL_BUSY_MASK), 15, 10000);
+}
+
+static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words)
+{
+	int ret;
+	u16 cmd;
+
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
+				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
+	if (ret < 0)
+		return ret;
+
+	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
+	if (!(cmd < 2))
+		return -EINVAL;
+
+	cmd += MMD_API_SET_DATA_0;
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
+				 cmd | CTRL_BUSY_MASK);
+	if (ret < 0)
+		return ret;
+
+	return mxl862xx_busy_wait(priv);
+}
+
+static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words)
+{
+	int ret;
+	u16 cmd;
+
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
+				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
+	if (ret < 0)
+		return ret;
+
+	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
+	if (!(cmd > 0 && cmd < 3))
+		return -EINVAL;
+
+	cmd += MMD_API_GET_DATA_0;
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
+				 cmd | CTRL_BUSY_MASK);
+	if (ret < 0)
+		return ret;
+
+	return mxl862xx_busy_wait(priv);
+}
+
+static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size)
+{
+	int ret;
+
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size);
+	if (ret)
+		return ret;
+
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
+				 cmd | CTRL_BUSY_MASK);
+	if (ret)
+		return ret;
+
+	ret = mxl862xx_busy_wait(priv);
+	if (ret)
+		return ret;
+
+	ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET);
+	/* handle errors returned by the firmware as -EIO
+	 * The firmware is based on Zephyr OS and uses the errors as
+	 * defined in errno.h of Zephyr OS. See
+	 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h
+	 */
+	if ((s16)ret < 0) {
+		dev_err(&priv->mdiodev->dev, "CMD %04x returned error %d\n",
+			cmd, (s16)ret);
+		return -EIO;
+	}
+
+	return ret;
+}
+
+int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data,
+		      u16 size, bool read)
+{
+	__le16 *data = _data;
+	u16 max, i;
+	int ret, cmd_ret;
+
+	dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data);
+
+	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+	max = (size + 1) / 2;
+
+	ret = mxl862xx_busy_wait(priv);
+	if (ret < 0)
+		goto out;
+
+	for (i = 0; i < max; i++) {
+		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+		if (i && off == 0) {
+			/* Send command to set data when every
+			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
+			 */
+			ret = mxl862xx_set_data(priv, i);
+			if (ret < 0)
+				goto out;
+		}
+
+		ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off,
+					 le16_to_cpu(data[i]));
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = mxl862xx_send_cmd(priv, cmd, size);
+	if (ret < 0 || !read)
+		goto out;
+
+	/* store result of mxl862xx_send_cmd() */
+	cmd_ret = ret;
+
+	for (i = 0; i < max; i++) {
+		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+		if (i && off == 0) {
+			/* Send command to fetch next batch of data when every
+			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read.
+			 */
+			ret = mxl862xx_get_data(priv, i);
+			if (ret < 0)
+				goto out;
+		}
+
+		ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off);
+		if (ret < 0)
+			goto out;
+
+		if ((i * 2 + 1) == size) {
+			/* Special handling for last BYTE if it's not WORD
+			 * aligned.
+			 */
+			*(uint8_t *)&data[i] = ret & 0xFF;
+		} else {
+			data[i] = cpu_to_le16((u16)ret);
+		}
+	}
+
+	/* on success return the result of the mxl862xx_send_cmd() */
+	ret = cmd_ret;
+
+	dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data);
+
+out:
+	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
+
+	return ret;
+}
+
+int mxl862xx_reset(struct mxl862xx_priv *priv)
+{
+	int ret;
+
+	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+	/* Software reset */
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0);
+	if (ret)
+		goto out;
+
+	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET);
+out:
+	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
+
+	if (!ret)
+		usleep_range(4000000, 6000000);
+
+	return ret;
+}
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.h b/drivers/net/dsa/mxl862xx/mxl862xx-host.h
new file mode 100644
index 0000000000000..eb5acb81feea6
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, bool read);
+int mxl862xx_reset(struct mxl862xx_priv *priv);
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
new file mode 100644
index 0000000000000..8e0dcfeb3b5c2
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for MaxLinear MxL862xx switch family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ * Copyright (C) 2025 John Crispin <john@phrozen.org>
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/phylink.h>
+#include <net/dsa.h>
+
+#include "mxl862xx.h"
+#include "mxl862xx-api.h"
+#include "mxl862xx-cmd.h"
+#include "mxl862xx-host.h"
+
+#define MXL862XX_API_WRITE(dev, cmd, data) \
+	mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false)
+#define MXL862XX_API_READ(dev, cmd, data) \
+	mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true)
+
+#define DSA_MXL_PORT(port) ((port) + 1)
+
+#define MXL862XX_SDMA_PCTRLP(p) (0xBC0 + ((p) * 0x6))
+#define MXL862XX_SDMA_PCTRL_EN BIT(0)
+
+#define MXL862XX_FDMA_PCTRLP(p) (0xA80 + ((p) * 0x6))
+#define MXL862XX_FDMA_PCTRL_EN BIT(0)
+
+/* PHY access via firmware relay */
+static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port,
+				 int devadd, int reg)
+{
+	struct mdio_relay_data param = {
+		.phy = port,
+		.mmd = devadd,
+		.reg = cpu_to_le16(reg),
+	};
+	int ret;
+
+	ret = MXL862XX_API_READ(priv, INT_GPHY_READ, param);
+	if (ret)
+		return ret;
+
+	return le16_to_cpu(param.data);
+}
+
+static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port,
+				  int devadd, int reg, u16 data)
+{
+	struct mdio_relay_data param = {
+		.phy = port,
+		.mmd = devadd,
+		.reg = cpu_to_le16(reg),
+		.data = cpu_to_le16(data),
+	};
+
+	return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param);
+}
+
+static int mxl862xx_phy_read(struct dsa_switch *ds, int port, int reg)
+{
+	return mxl862xx_phy_read_mmd(ds->priv, port, 0, reg);
+}
+
+static int mxl862xx_phy_write(struct dsa_switch *ds, int port, int reg, u16 data)
+{
+	return mxl862xx_phy_write_mmd(ds->priv, port, 0, reg, data);
+}
+
+static int mxl862xx_configure_tag_proto(struct dsa_port *dp, bool enable)
+{
+	struct mxl862xx_ctp_port_assignment assign = {
+		.number_of_ctp_port = cpu_to_le16(enable ? (32 - DSA_MXL_PORT(dp->index)) : 1),
+		.logical_port_id = DSA_MXL_PORT(dp->index),
+		.first_ctp_port_id = cpu_to_le16(DSA_MXL_PORT(dp->index)),
+		.mode = cpu_to_le32(MXL862XX_LOGICAL_PORT_GPON),
+	};
+	struct mxl862xx_ss_sp_tag tag = {
+		.pid = DSA_MXL_PORT(dp->index),
+		.mask = MXL862XX_SS_SP_TAG_MASK_RX | MXL862XX_SS_SP_TAG_MASK_TX,
+		.rx = enable ? MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT :
+			       MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT,
+		.tx = enable ? MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE :
+			       MXL862XX_SS_SP_TAG_TX_TAG_REMOVE,
+	};
+	int ret;
+
+	ret = MXL862XX_API_WRITE(dp->ds->priv, MXL862XX_SS_SPTAG_SET, tag);
+	if (ret)
+		return ret;
+
+	return MXL862XX_API_WRITE(dp->ds->priv, MXL862XX_CTP_PORTASSIGNMENTSET, assign);
+}
+
+static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable)
+{
+	struct mxl862xx_register_mod sdma = {
+		.addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(DSA_MXL_PORT(port))),
+		.data = cpu_to_le16(enable ? MXL862XX_SDMA_PCTRL_EN : 0),
+		.mask = cpu_to_le16(MXL862XX_SDMA_PCTRL_EN),
+	};
+	struct mxl862xx_register_mod fdma = {
+		.addr = cpu_to_le16(MXL862XX_FDMA_PCTRLP(DSA_MXL_PORT(port))),
+		.data = cpu_to_le16(enable ? MXL862XX_FDMA_PCTRL_EN : 0),
+		.mask = cpu_to_le16(MXL862XX_FDMA_PCTRL_EN),
+	};
+	int ret;
+
+	if (!dsa_is_user_port(ds, port))
+		return 0;
+
+	ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, sdma);
+	if (ret)
+		return ret;
+
+	return MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, fdma);
+}
+
+static int mxl862xx_port_enable(struct dsa_switch *ds, int port,
+				struct phy_device *phydev)
+{
+	return mxl862xx_port_state(ds, port, true);
+}
+
+static void mxl862xx_port_disable(struct dsa_switch *ds, int port)
+{
+	mxl862xx_port_state(ds, port, false);
+}
+
+static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum)
+{
+	return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum);
+}
+
+static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port,
+				      int regnum, u16 val)
+{
+	return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val);
+}
+
+static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port,
+					 int devadd, int regnum)
+{
+	return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum);
+}
+
+static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port,
+					  int devadd, int regnum, u16 val)
+{
+	return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val);
+}
+
+static int mxl862xx_setup_mdio(struct dsa_switch *ds)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct device *dev = ds->dev;
+	struct device_node *mdio_np;
+	struct mii_bus *bus;
+	static int idx;
+	int ret;
+
+	bus = devm_mdiobus_alloc(dev);
+	if (!bus)
+		return -ENOMEM;
+
+	bus->priv = priv;
+	ds->user_mii_bus = bus;
+	bus->name = KBUILD_MODNAME "-mii";
+	snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
+	bus->read_c45 = mxl862xx_phy_read_c45_mii_bus;
+	bus->write_c45 = mxl862xx_phy_write_c45_mii_bus;
+	bus->read = mxl862xx_phy_read_mii_bus;
+	bus->write = mxl862xx_phy_write_mii_bus;
+	bus->parent = dev;
+	bus->phy_mask = ~ds->phys_mii_mask;
+
+	mdio_np = of_get_child_by_name(dev->of_node, "mdio");
+	if (!mdio_np)
+		return -ENODEV;
+
+	ret = devm_of_mdiobus_register(dev, bus, mdio_np);
+	of_node_put(mdio_np);
+
+	return ret;
+}
+
+static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
+				      struct phylink_config *config)
+{
+	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+				   MAC_100 | MAC_1000 | MAC_2500FD;
+
+	__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+		  config->supported_interfaces);
+}
+
+static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+						       int port,
+						       enum dsa_tag_protocol m)
+{
+	return DSA_TAG_PROTO_MXL862;
+}
+
+static int mxl862xx_setup(struct dsa_switch *ds)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct dsa_port *cpu_dp;
+	int ret;
+
+	ret = mxl862xx_reset(priv);
+	if (ret)
+		return ret;
+
+	ret = mxl862xx_setup_mdio(ds);
+	if (ret)
+		return ret;
+
+	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
+		ret = mxl862xx_configure_tag_proto(cpu_dp, true);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct dsa_switch_ops mxl862xx_switch_ops = {
+	.get_tag_protocol = mxl862xx_get_tag_protocol,
+	.phylink_get_caps = mxl862xx_phylink_get_caps,
+	.phy_read = mxl862xx_phy_read,
+	.phy_write = mxl862xx_phy_write,
+	.port_disable = mxl862xx_port_disable,
+	.port_enable = mxl862xx_port_enable,
+	.setup = mxl862xx_setup,
+};
+
+static void mxl862xx_phylink_mac_config(struct phylink_config *config,
+					unsigned int mode,
+					const struct phylink_link_state *state)
+{
+}
+
+static void mxl862xx_phylink_mac_link_down(struct phylink_config *config,
+					   unsigned int mode,
+					   phy_interface_t interface)
+{
+}
+
+static void mxl862xx_phylink_mac_link_up(struct phylink_config *config,
+					 struct phy_device *phydev,
+					 unsigned int mode,
+					 phy_interface_t interface,
+					 int speed, int duplex,
+					 bool tx_pause, bool rx_pause)
+{
+}
+
+static struct phylink_pcs *
+mxl862xx_phylink_mac_select_pcs(struct phylink_config *config,
+				phy_interface_t interface)
+{
+	return NULL;
+}
+
+static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = {
+	.mac_config = mxl862xx_phylink_mac_config,
+	.mac_link_down = mxl862xx_phylink_mac_link_down,
+	.mac_link_up = mxl862xx_phylink_mac_link_up,
+	.mac_select_pcs = mxl862xx_phylink_mac_select_pcs,
+};
+
+static int mxl862xx_probe(struct mdio_device *mdiodev)
+{
+	struct device *dev = &mdiodev->dev;
+	struct mxl862xx_priv *priv;
+	struct dsa_switch *ds;
+	struct mxl862xx_sys_fw_image_version fw;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->mdiodev = mdiodev;
+	priv->hw_info = of_device_get_match_data(dev);
+	if (!priv->hw_info)
+		return -EINVAL;
+
+	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+	if (!ds)
+		return -ENOMEM;
+
+	priv->ds = ds;
+	ds->dev = dev;
+	ds->priv = priv;
+	ds->ops = &mxl862xx_switch_ops;
+	ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
+	ds->num_ports = priv->hw_info->max_ports;
+
+	dev_set_drvdata(dev, ds);
+
+	ret = dsa_register_switch(ds);
+	if (ret)
+		return ret;
+
+	ret = MXL862XX_API_READ(priv, SYS_MISC_FW_VERSION, fw);
+	if (!ret)
+		dev_info(dev, "Firmware version %d.%d.%d.%d\n",
+			 fw.iv_major, fw.iv_minor,
+			 fw.iv_revision, fw.iv_build_num);
+
+	return 0;
+}
+
+static void mxl862xx_remove(struct mdio_device *mdiodev)
+{
+	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
+
+	dsa_unregister_switch(ds);
+}
+
+static const struct mxl862xx_hw_info mxl86282_data = {
+	.max_ports = MXL862XX_MAX_PORT_NUM,
+	.phy_ports = MXL86282_PHY_PORT_NUM,
+	.ext_ports = MXL86282_EXT_PORT_NUM,
+};
+
+static const struct mxl862xx_hw_info mxl86252_data = {
+	.max_ports = MXL862XX_MAX_PORT_NUM,
+	.phy_ports = MXL86252_PHY_PORT_NUM,
+	.ext_ports = MXL86252_EXT_PORT_NUM,
+};
+
+static const struct of_device_id mxl862xx_of_match[] = {
+	{ .compatible = "maxlinear,mxl86282", .data = &mxl86282_data },
+	{ .compatible = "maxlinear,mxl86252", .data = &mxl86252_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mxl862xx_of_match);
+
+static struct mdio_driver mxl862xx_driver = {
+	.probe  = mxl862xx_probe,
+	.remove = mxl862xx_remove,
+	.mdiodrv.driver = {
+		.name = "mxl862xx",
+		.of_match_table = mxl862xx_of_match,
+	},
+};
+
+mdio_module_driver(mxl862xx_driver);
+
+MODULE_DESCRIPTION("Minimal driver for MaxLinear MxL862xx switch family");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxl862xx");
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx/mxl862xx.h
new file mode 100644
index 0000000000000..66d194db8d6dd
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#define MXL862XX_MAX_PHY_PORT_NUM	8
+#define MXL862XX_MAX_EXT_PORT_NUM	7
+#define MXL862XX_MAX_PORT_NUM		(MXL862XX_MAX_PHY_PORT_NUM + \
+					 MXL862XX_MAX_EXT_PORT_NUM)
+
+#define MXL86252_PHY_PORT_NUM		5
+#define MXL86282_PHY_PORT_NUM		8
+
+#define MXL86252_EXT_PORT_NUM		2
+#define MXL86282_EXT_PORT_NUM		2
+
+struct mxl862xx_hw_info {
+	u8 max_ports;
+	u8 phy_ports;
+	u8 ext_ports;
+};
+
+struct mxl862xx_priv {
+	struct dsa_switch *ds;
+	struct mdio_device *mdiodev;
+	const struct mxl862xx_hw_info *hw_info;
+};
-- 
2.52.0

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches
  2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
@ 2025-12-15  1:46   ` Andrew Lunn
  2025-12-15 14:28   ` Simon Horman
  2025-12-16 20:39   ` Vladimir Oltean
  2 siblings, 0 replies; 11+ messages in thread
From: Andrew Lunn @ 2025-12-15  1:46 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, Simon Horman, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> index f86b30742122f..c897d62326f5b 100644
> --- a/net/dsa/Kconfig
> +++ b/net/dsa/Kconfig
> @@ -145,6 +145,13 @@ config NET_DSA_TAG_QCA
>  	  Say Y or M if you want to enable support for tagging frames for
>  	  the Qualcomm Atheros QCA8K switches.
>  
> +config NET_DSA_TAG_MXL862
> +	tristate "Tag driver for MxL862xx switches"
> +	help
> +	  Say Y or M if you want to enable support for tagging frames for the
> +	  Maxlinear MxL86252 and MxL86282 switches using their native 8-byte
> +	  tagging protocol.
> +

This file is mostly sorted. So this entry should be between
NET_DSA_TAG_MTK and NET_DSA_TAG_MXL_GSW1XX. It would also be good to
try to make it more uniform with the NET_DSA_TAG_MXL_GSW1XX. Maybe
NET_DSA_TAG_MXL_862xx?

	Andrew

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches
  2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
  2025-12-15  1:46   ` Andrew Lunn
@ 2025-12-15 14:28   ` Simon Horman
  2025-12-16 20:39   ` Vladimir Oltean
  2 siblings, 0 replies; 11+ messages in thread
From: Simon Horman @ 2025-12-15 14:28 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiner Kallweit, Russell King, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

On Mon, Dec 15, 2025 at 12:11:43AM +0000, Daniel Golle wrote:

...

> diff --git a/net/dsa/tag_mxl862xx.c b/net/dsa/tag_mxl862xx.c
> new file mode 100644
> index 0000000000000..9c5e5f90dcb63
> --- /dev/null
> +++ b/net/dsa/tag_mxl862xx.c
> @@ -0,0 +1,113 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * DSA Special Tag for MaxLinear 862xx switch chips
> + *
> + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (C) 2024 MaxLinear Inc.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <net/dsa.h>
> +#include "tag.h"
> +
> +#define MXL862_NAME	"mxl862xx"
> +
> +/* To define the outgoing port and to discover the incoming port a special
> + * tag is used by the GSW1xx.
> + *
> + *       Dest MAC       Src MAC    special TAG        EtherType
> + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
> + *                                |<--------------->|
> + */
> +
> +#define MXL862_HEADER_LEN 8
> +
> +/* Byte 7 */
> +#define MXL862_IGP_EGP GENMASK(3, 0)
> +
> +static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb,
> +				       struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_user_to_port(dev);
> +	struct dsa_port *cpu_dp = dp->cpu_dp;
> +	unsigned int cpu_port = cpu_dp->index + 1;
> +	unsigned int usr_port = dp->index + 1;
> +	__be16 *mxl862_tag;

Hi Daniel,

Please arrange local variables in reverse xmas tree order.
Even if it means separating declaration and initialisation.

FWIIW, I would probably go for:

	struct dsa_port *dp = dsa_user_to_port(dev);
	struct dsa_port *cpu_dp = dp->cpu_dp;
	unsigned int cpu_port, usr_port;
	__be16 *mxl862_tag;

	cpu_port = cpu_dp->index + 1;
	usr_port = dp->index + 1;

> +
> +	if (!skb)
> +		return skb;
> +
> +	/* provide additional space 'MXL862_HEADER_LEN' bytes */
> +	skb_push(skb, MXL862_HEADER_LEN);
> +
> +	/* shift MAC address to the beginnig of the enlarged buffer,

s/beginnig/beginning/

> +	 * releasing the space required for DSA tag (between MAC address and
> +	 * Ethertype)
> +	 */
> +	dsa_alloc_etype_header(skb, MXL862_HEADER_LEN);
> +
> +	/* special tag ingress */
> +	mxl862_tag = dsa_etype_header_pos_tx(skb);
> +	mxl862_tag[0] = htons(ETH_P_MXLGSW);
> +	mxl862_tag[1] = 0;
> +	mxl862_tag[2] = htons(usr_port + 16 - cpu_port);
> +	mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port));
> +
> +	return skb;
> +}

...

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches
  2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
  2025-12-15  1:46   ` Andrew Lunn
  2025-12-15 14:28   ` Simon Horman
@ 2025-12-16 20:39   ` Vladimir Oltean
  2 siblings, 0 replies; 11+ messages in thread
From: Vladimir Oltean @ 2025-12-16 20:39 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, Simon Horman, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

On Mon, Dec 15, 2025 at 12:11:43AM +0000, Daniel Golle wrote:
> Add proprietary special tag format for the MaxLinear MXL862xx family of
> switches. While using the same Ethertype as MaxLinear's GSW1xx swtiches,

s/swtiches/switches/

> the actual tag format differs significantly, hence we need a dedicated
> tag driver for that.

Reusing the same EtherType for two different DSA tagging protocols is
very bad news, possibly with implications also for libpcap. Is the
EtherType configurable in the MXL862xx family?

> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> RFC v3: no changes
> RFC v2: make sure all tag fields are initialized
> 
>  MAINTAINERS            |   1 +
>  include/net/dsa.h      |   2 +
>  net/dsa/Kconfig        |   7 +++
>  net/dsa/Makefile       |   1 +
>  net/dsa/tag_mxl862xx.c | 113 +++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 124 insertions(+)
>  create mode 100644 net/dsa/tag_mxl862xx.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c433a15d9797a..a20498cc8320b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15609,6 +15609,7 @@ M:	Daniel Golle <daniel@makrotopia.org>
>  L:	netdev@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
> +F:	net/dsa/tag_mxl862xx.c
>  
>  MCAN DEVICE DRIVER
>  M:	Markus Schneider-Pargmann <msp@baylibre.com>
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index e40cdc12f7f39..e4c2b47a2a46e 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -57,6 +57,7 @@ struct tc_action;
>  #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE	29
>  #define DSA_TAG_PROTO_YT921X_VALUE		30
>  #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE		31
> +#define DSA_TAG_PROTO_MXL862_VALUE		32
>  
>  enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
> @@ -91,6 +92,7 @@ enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_VSC73XX_8021Q	= DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
>  	DSA_TAG_PROTO_YT921X		= DSA_TAG_PROTO_YT921X_VALUE,
>  	DSA_TAG_PROTO_MXL_GSW1XX	= DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
> +	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE,
>  };
>  
>  struct dsa_switch;
> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> index f86b30742122f..c897d62326f5b 100644
> --- a/net/dsa/Kconfig
> +++ b/net/dsa/Kconfig
> @@ -145,6 +145,13 @@ config NET_DSA_TAG_QCA
>  	  Say Y or M if you want to enable support for tagging frames for
>  	  the Qualcomm Atheros QCA8K switches.
>  
> +config NET_DSA_TAG_MXL862
> +	tristate "Tag driver for MxL862xx switches"
> +	help
> +	  Say Y or M if you want to enable support for tagging frames for the
> +	  Maxlinear MxL86252 and MxL86282 switches using their native 8-byte

MaxLinear with capital L

> +	  tagging protocol.
> +
>  config NET_DSA_TAG_RTL4_A
>  	tristate "Tag driver for Realtek 4 byte protocol A tags"
>  	help
> diff --git a/net/dsa/Makefile b/net/dsa/Makefile
> index 42d173f5a7013..dbe2a742e3322 100644
> --- a/net/dsa/Makefile
> +++ b/net/dsa/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
>  obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
>  obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
>  obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
> +obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o
>  obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
>  obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
>  obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
> diff --git a/net/dsa/tag_mxl862xx.c b/net/dsa/tag_mxl862xx.c
> new file mode 100644
> index 0000000000000..9c5e5f90dcb63
> --- /dev/null
> +++ b/net/dsa/tag_mxl862xx.c
> @@ -0,0 +1,113 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * DSA Special Tag for MaxLinear 862xx switch chips
> + *
> + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (C) 2024 MaxLinear Inc.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <net/dsa.h>
> +#include "tag.h"
> +
> +#define MXL862_NAME	"mxl862xx"
> +
> +/* To define the outgoing port and to discover the incoming port a special
> + * tag is used by the GSW1xx.
> + *
> + *       Dest MAC       Src MAC    special TAG        EtherType
> + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
> + *                                |<--------------->|
> + */
> +
> +#define MXL862_HEADER_LEN 8
> +
> +/* Byte 7 */
> +#define MXL862_IGP_EGP GENMASK(3, 0)
> +
> +static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb,
> +				       struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_user_to_port(dev);
> +	struct dsa_port *cpu_dp = dp->cpu_dp;
> +	unsigned int cpu_port = cpu_dp->index + 1;
> +	unsigned int usr_port = dp->index + 1;
> +	__be16 *mxl862_tag;
> +
> +	if (!skb)
> +		return skb;
> +
> +	/* provide additional space 'MXL862_HEADER_LEN' bytes */
> +	skb_push(skb, MXL862_HEADER_LEN);
> +
> +	/* shift MAC address to the beginnig of the enlarged buffer,
> +	 * releasing the space required for DSA tag (between MAC address and
> +	 * Ethertype)
> +	 */
> +	dsa_alloc_etype_header(skb, MXL862_HEADER_LEN);
> +
> +	/* special tag ingress */
> +	mxl862_tag = dsa_etype_header_pos_tx(skb);
> +	mxl862_tag[0] = htons(ETH_P_MXLGSW);
> +	mxl862_tag[1] = 0;
> +	mxl862_tag[2] = htons(usr_port + 16 - cpu_port);

Can you place a comment on the meaning of these port manipulations
(cpu_dp->index + 1, dp->index + 1, usr_port + 16 - cpu_port,
port = port - 1 in rcv())?

> +	mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port));
> +
> +	return skb;
> +}
> +
> +static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
> +				      struct net_device *dev)
> +{
> +	int port;
> +	__be16 *mxl862_tag;
> +
> +	if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) {
> +		dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n");
> +		return NULL;
> +	}
> +
> +	mxl862_tag = dsa_etype_header_pos_rx(skb);
> +
> +	if (unlikely(mxl862_tag[0] != htons(ETH_P_MXLGSW))) {
> +		dev_warn_ratelimited(&dev->dev, "Invalid special tag marker, packet dropped\n");
> +		dev_warn_ratelimited(&dev->dev, "Rx Packet Tag: %8ph\n",
> +				     mxl862_tag);
> +		return NULL;
> +	}
> +
> +	/* Get source port information */
> +	port = FIELD_GET(MXL862_IGP_EGP, ntohs(mxl862_tag[3]));
> +	port = port - 1;
> +	skb->dev = dsa_conduit_find_user(dev, 0, port);
> +	if (!skb->dev) {
> +		dev_warn_ratelimited(&dev->dev, "Invalid source port, packet dropped\n");
> +		dev_warn_ratelimited(&dev->dev, "Rx Packet Tag: %8ph\n",
> +				     mxl862_tag);
> +		return NULL;
> +	}
> +
> +	/* remove the MxL862xx special tag between the MAC addresses and the
> +	 * current ethertype field.
> +	 */
> +	skb_pull_rcsum(skb, MXL862_HEADER_LEN);
> +	dsa_strip_etype_header(skb, MXL862_HEADER_LEN);
> +
> +	return skb;
> +}
> +
> +static const struct dsa_device_ops mxl862_netdev_ops = {
> +	.name = "mxl862",
> +	.proto = DSA_TAG_PROTO_MXL862,
> +	.xmit = mxl862_tag_xmit,
> +	.rcv = mxl862_tag_rcv,
> +	.needed_headroom = MXL862_HEADER_LEN,
> +};
> +
> +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME);
> +MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches");
> +MODULE_LICENSE("GPL");
> +
> +module_dsa_tag_driver(mxl862_netdev_ops);
> -- 
> 2.52.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches
  2025-12-15  0:12 ` [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches Daniel Golle
@ 2025-12-16 22:43   ` Vladimir Oltean
  0 siblings, 0 replies; 11+ messages in thread
From: Vladimir Oltean @ 2025-12-16 22:43 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, Simon Horman, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

On Mon, Dec 15, 2025 at 12:12:20AM +0000, Daniel Golle wrote:
> Add very basic DSA driver for MaxLinear's MxL862xx switches.
> 
> In contrast to previous MaxLinear switches the MxL862xx has a built-in
> processor that runs a sophisticated firmware based on Zephyr RTOS.
> Interaction between the host and the switch hence is organized using a
> software API of that firmware rather than accessing hardware registers
> directly.
> 
> Add descriptions of the most basic firmware API calls to access the
> built-in MDIO bus hosting the 2.5GE PHYs, basic port control as well as
> setting up the CPU port.
> 
> Implement a very basic DSA driver using that API which is sufficient to
> get packets flowing between the user ports and the CPU port.
> 
> The firmware offers all features one would expect from a modern switch
> hardware, they will be added one by one in follow-up patch series.
> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> RFC v3:
>  * fix return value being uninitialized on error in mxl862xx_api_wrap()
>  * add missing descrition in kerneldoc comment of
>    struct mxl862xx_ss_sp_tag
> 
> RFC v2:
>  * make use of struct mdio_device
>  * add phylink_mac_ops stubs
>  * drop leftover nonsense from mxl862xx_phylink_get_caps()
>  * use __le32 instead of enum types in over-the-wire structs
>  * use existing MDIO_* macros whenever possible
>  * simplify API constants to be more readable
>  * use readx_poll_timeout instead of open-coding poll timeout loop
>  * add mxl862xx_reg_read() and mxl862xx_reg_write() helpers
>  * demystify error codes returned by the firmware
>  * add #defines for mxl862xx_ss_sp_tag member values
>  * move reset to dedicated function, clarify magic number being the
>    reset command ID
> 
>  MAINTAINERS                              |   1 +
>  drivers/net/dsa/Kconfig                  |   2 +
>  drivers/net/dsa/Makefile                 |   1 +
>  drivers/net/dsa/mxl862xx/Kconfig         |  12 +
>  drivers/net/dsa/mxl862xx/Makefile        |   3 +
>  drivers/net/dsa/mxl862xx/mxl862xx-api.h  | 118 ++++++++
>  drivers/net/dsa/mxl862xx/mxl862xx-cmd.h  |  28 ++
>  drivers/net/dsa/mxl862xx/mxl862xx-host.c | 230 +++++++++++++++
>  drivers/net/dsa/mxl862xx/mxl862xx-host.h |   4 +
>  drivers/net/dsa/mxl862xx/mxl862xx.c      | 361 +++++++++++++++++++++++
>  drivers/net/dsa/mxl862xx/mxl862xx.h      |  24 ++
>  11 files changed, 784 insertions(+)
>  create mode 100644 drivers/net/dsa/mxl862xx/Kconfig
>  create mode 100644 drivers/net/dsa/mxl862xx/Makefile
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-api.h
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.c
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.h
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.c
>  create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a20498cc8320b..17ca0351cc5b6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15609,6 +15609,7 @@ M:	Daniel Golle <daniel@makrotopia.org>
>  L:	netdev@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
> +F:	drivers/net/dsa/mxl862xx/
>  F:	net/dsa/tag_mxl862xx.c
>  
>  MCAN DEVICE DRIVER
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index 7eb301fd987d1..18f6e8b7f4cb2 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -74,6 +74,8 @@ source "drivers/net/dsa/microchip/Kconfig"
>  
>  source "drivers/net/dsa/mv88e6xxx/Kconfig"
>  
> +source "drivers/net/dsa/mxl862xx/Kconfig"
> +
>  source "drivers/net/dsa/ocelot/Kconfig"
>  
>  source "drivers/net/dsa/qca/Kconfig"
> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> index 16de4ba3fa388..f5a463b87ec25 100644
> --- a/drivers/net/dsa/Makefile
> +++ b/drivers/net/dsa/Makefile
> @@ -20,6 +20,7 @@ obj-y				+= hirschmann/
>  obj-y				+= lantiq/
>  obj-y				+= microchip/
>  obj-y				+= mv88e6xxx/
> +obj-y				+= mxl862xx/
>  obj-y				+= ocelot/
>  obj-y				+= qca/
>  obj-y				+= realtek/
> diff --git a/drivers/net/dsa/mxl862xx/Kconfig b/drivers/net/dsa/mxl862xx/Kconfig
> new file mode 100644
> index 0000000000000..5c538dfc2763e
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config NET_DSA_MXL862
> +	tristate "MaxLinear MxL862xx"
> +	depends on NET_DSA
> +	select MAXLINEAR_GPHY
> +	select NET_DSA_TAG_MXL862
> +	help
> +	  This enables support for the MaxLinear MxL862xx switch family.
> +	  These switches got two 10GE SerDes interfaces, one typically

s/got/have/

> +	  used as CPU port.
> +	   MxL86282 Eight 2.5 Gigabit PHYs
> +	   MxL86252 Five 2.5 Gigabit PHYs

These sentences are missing the verb "has"?

> \ No newline at end of file
> diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/Makefile
> new file mode 100644
> index 0000000000000..d23dd3cd511d4
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
> +mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
> new file mode 100644
> index 0000000000000..e2b0a04366aa6
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
> @@ -0,0 +1,118 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/**
> + * struct mdio_relay_data - relayed access to the switch internal MDIO bus
> + * @data: data to be read or written
> + * @phy: PHY index
> + * @mmd: MMD device
> + * @reg: register rndex

s/rndex/index/

> + */
> +struct mdio_relay_data {
> +	__le16 data;
> +	u8 phy;
> +	u8 mmd;
> +	__le16 reg;
> +} __packed;
> +
> +/* Register access parameter to directly modify internal registers */
> +struct mxl862xx_register_mod {
> +	__le16 addr;
> +	__le16 data;
> +	__le16 mask;
> +} __packed;
> +
> +#define MXL862XX_SS_SP_TAG_MASK_RX			BIT(0)
> +#define MXL862XX_SS_SP_TAG_MASK_TX			BIT(1)
> +#define MXL862XX_SS_SP_TAG_MASK_RX_PEN			BIT(2)
> +#define MXL862XX_SS_SP_TAG_MASK_TX_PEN			BIT(3)
> +
> +#define MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT		0
> +#define MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT		1
> +#define MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT		2
> +
> +#define MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE		0
> +#define MXL862XX_SS_SP_TAG_TX_TAG_REPLACE		1
> +#define MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE		2
> +#define MXL862XX_SS_SP_TAG_TX_TAG_REMOVE		3
> +
> +/**
> + * struct mxl862xx_ss_sp_tag - Special tag port settings
> + * @pid: port ID (1~16)
> + * @mask: bit value 1 to indicate valid field
> + *	0 - rx
> + *	1 - tx
> + *	2 - rx_pen
> + *	3 - tx_pen
> + * @rx: RX special tag mode
> + *	0 - packet does NOT have special tag and special tag is NOT inserted
> + *	1 - packet does NOT have special tag and special tag is inserted
> + *	2 - packet has special tag and special tag is NOT inserted
> + * @tx: TX special tag mode
> + *	0 - packet does NOT have special tag and special tag is NOT removed
> + *	1 - packet has special tag and special tag is replaced
> + *	2 - packet has special tag and special tag is NOT removed
> + *	3 - packet has special tag and special tag is removed
> + * @rx_pen: RX special tag info over preamble
> + *	0 - special tag info inserted from byte 2 to 7 are all 0
> + *	1 - special tag byte 5 is 16, other bytes from 2 to 7 are 0
> + *	2 - special tag byte 5 is from preamble field, others are 0
> + *	3 - special tag byte 2 to 7 are from preabmle field
> + * @tx_pen: TX special tag info over preamble
> + *	0 - disabled
> + *	1 - enabled
> + */
> +struct mxl862xx_ss_sp_tag {
> +	u8 pid;
> +	u8 mask;
> +	u8 rx;
> +	u8 tx;
> +	u8 rx_pen;
> +	u8 tx_pen;
> +} __packed;
> +
> +/**
> + * enum mxl862xx_logical_port_mode - Logical port mode
> + * @MXL862XX_LOGICAL_PORT_8BIT_WLAN: WLAN with 8-bit station ID
> + * @MXL862XX_LOGICAL_PORT_9BIT_WLAN: WLAN with 9-bit station ID
> + * @MXL862XX_LOGICAL_PORT_GPON: GPON OMCI context
> + * @MXL862XX_LOGICAL_PORT_EPON: EPON context
> + * @MXL862XX_LOGICAL_PORT_GINT: G.INT context
> + * @MXL862XX_LOGICAL_PORT_OTHER: Others
> + */
> +enum mxl862xx_logical_port_mode {
> +	MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0,
> +	MXL862XX_LOGICAL_PORT_9BIT_WLAN,
> +	MXL862XX_LOGICAL_PORT_GPON,
> +	MXL862XX_LOGICAL_PORT_EPON,
> +	MXL862XX_LOGICAL_PORT_GINT,
> +	MXL862XX_LOGICAL_PORT_OTHER = 0xFF,
> +};
> +
> +/**
> + * struct mxl862xx_ctp_port_assignment - CTP Port Assignment/association with logical port

What is a CTP port? Logical port? Why is the "GPON" logical port mode
for the DSA CPU port preferable to its alternatives?

> + * @logical_port_id: Logical Port Id. The valid range is hardware dependent
> + * @first_ctp_port_id: First CTP Port ID mapped to above logical port ID
> + * @number_of_ctp_port: Total number of CTP Ports mapped above logical port ID
> + * @mode: See &enum mxl862xx_logical_port_mode
> + * @bridge_port_id: Bridge ID (FID)
> + */
> +struct mxl862xx_ctp_port_assignment {
> +	u8 logical_port_id;
> +	__le16 first_ctp_port_id;
> +	__le16 number_of_ctp_port;
> +	__le32 mode; /* enum mxl862xx_logical_port_mode */
> +	__le16 bridge_port_id;
> +} __packed;

Can you confirm this alignment is correct? __le32 mode begins with byte
offset 5 of the structure. I haven't decompiled the code, but I suppose
the compiler will have to perform byte by byte accesses.

> +
> +/**
> + * struct mxl862xx_sys_fw_image_version - Firmware version information
> + * @iv_major: firmware major version
> + * @iv_minor: firmware minor version
> + * @iv_revision: firmware revision
> + * @iv_build_num: firmware build number
> + */
> +struct mxl862xx_sys_fw_image_version {
> +	u8 iv_major;
> +	u8 iv_minor;
> +	__le16 iv_revision;
> +	__le32 iv_build_num;
> +} __packed;
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> new file mode 100644
> index 0000000000000..db6a4c3f54f22
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#define MXL862XX_MMD_DEV 30
> +#define MXL862XX_MMD_REG_CTRL 0
> +#define MXL862XX_MMD_REG_LEN_RET 1
> +#define MXL862XX_MMD_REG_DATA_FIRST 2
> +#define MXL862XX_MMD_REG_DATA_LAST 95
> +#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
> +	(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
> +
> +#define MXL862XX_COMMON_MAGIC 0x100
> +#define MXL862XX_CTP_MAGIC 0x500
> +#define MXL862XX_SS_MAGIC 0x1600
> +#define GPY_GPY2XX_MAGIC 0x1800
> +#define SYS_MISC_MAGIC 0x1900
> +
> +#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
> +
> +#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3)
> +
> +#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02)
> +
> +#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01)
> +#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02)
> +
> +#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02)
> +
> +#define MMD_API_MAXIMUM_ID 0x7FFF
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.c b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
> new file mode 100644
> index 0000000000000..ca13c43bd9efa
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c
> @@ -0,0 +1,230 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Based upon the Maxlinear SDK driver
> + *
> + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (C) 2025 John Crispin <john@phrozen.org>
> + * Copyright (C) 2024 MaxLinear Inc.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/iopoll.h>
> +#include <net/dsa.h>
> +#include "mxl862xx.h"
> +#include "mxl862xx-host.h"
> +
> +#define CTRL_BUSY_MASK			BIT(15)
> +
> +#define MXL862XX_MMD_REG_CTRL		0
> +#define MXL862XX_MMD_REG_LEN_RET	1
> +#define MXL862XX_MMD_REG_DATA_FIRST	2
> +#define MXL862XX_MMD_REG_DATA_LAST	95
> +#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
> +		(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
> +
> +#define MMD_API_SET_DATA_0		2
> +#define MMD_API_GET_DATA_0		5
> +#define MMD_API_RST_DATA		8
> +
> +#define MXL862XX_SWITCH_RESET 0x9907
> +
> +static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr)
> +{
> +	return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr);
> +}
> +
> +static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data)
> +{
> +	return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data);
> +}
> +
> +static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv)
> +{
> +	return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL);
> +}
> +
> +static int mxl862xx_busy_wait(struct mxl862xx_priv *priv)
> +{
> +	int val;
> +
> +	return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
> +				  !(val & CTRL_BUSY_MASK), 15, 10000);
> +}
> +
> +static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words)
> +{
> +	int ret;
> +	u16 cmd;
> +
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
> +				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
> +	if (ret < 0)
> +		return ret;
> +
> +	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
> +	if (!(cmd < 2))
> +		return -EINVAL;
> +
> +	cmd += MMD_API_SET_DATA_0;
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
> +				 cmd | CTRL_BUSY_MASK);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mxl862xx_busy_wait(priv);
> +}
> +
> +static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words)
> +{
> +	int ret;
> +	u16 cmd;
> +
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET,
> +				 MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
> +	if (ret < 0)
> +		return ret;
> +
> +	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
> +	if (!(cmd > 0 && cmd < 3))
> +		return -EINVAL;
> +
> +	cmd += MMD_API_GET_DATA_0;
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
> +				 cmd | CTRL_BUSY_MASK);
> +	if (ret < 0)
> +		return ret;
> +
> +	return mxl862xx_busy_wait(priv);
> +}
> +
> +static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size)

Can you document what this function is supposed to return on success?
Seems something non-zero, otherwise you wouldn't bother saving it in cmd_ret,
I guess.

> +{
> +	int ret;
> +
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL,
> +				 cmd | CTRL_BUSY_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxl862xx_busy_wait(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET);
> +	/* handle errors returned by the firmware as -EIO
> +	 * The firmware is based on Zephyr OS and uses the errors as
> +	 * defined in errno.h of Zephyr OS. See
> +	 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h
> +	 */
> +	if ((s16)ret < 0) {
> +		dev_err(&priv->mdiodev->dev, "CMD %04x returned error %d\n",
> +			cmd, (s16)ret);
> +		return -EIO;
> +	}
> +
> +	return ret;
> +}
> +
> +int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data,
> +		      u16 size, bool read)
> +{
> +	__le16 *data = _data;
> +	u16 max, i;
> +	int ret, cmd_ret;
> +
> +	dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data);
> +
> +	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
> +
> +	max = (size + 1) / 2;
> +
> +	ret = mxl862xx_busy_wait(priv);
> +	if (ret < 0)
> +		goto out;
> +
> +	for (i = 0; i < max; i++) {
> +		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
> +
> +		if (i && off == 0) {
> +			/* Send command to set data when every
> +			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
> +			 */
> +			ret = mxl862xx_set_data(priv, i);
> +			if (ret < 0)
> +				goto out;
> +		}
> +
> +		ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off,
> +					 le16_to_cpu(data[i]));
> +		if (ret < 0)
> +			goto out;

I have only superficially looked at this, but doesn't writing need
special handling if the buffer size is odd, like reading does?

> +	}
> +
> +	ret = mxl862xx_send_cmd(priv, cmd, size);
> +	if (ret < 0 || !read)
> +		goto out;
> +
> +	/* store result of mxl862xx_send_cmd() */
> +	cmd_ret = ret;
> +
> +	for (i = 0; i < max; i++) {
> +		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
> +
> +		if (i && off == 0) {
> +			/* Send command to fetch next batch of data when every
> +			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read.
> +			 */
> +			ret = mxl862xx_get_data(priv, i);
> +			if (ret < 0)
> +				goto out;
> +		}
> +
> +		ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off);
> +		if (ret < 0)
> +			goto out;
> +
> +		if ((i * 2 + 1) == size) {
> +			/* Special handling for last BYTE if it's not WORD
> +			 * aligned.
> +			 */
> +			*(uint8_t *)&data[i] = ret & 0xFF;
> +		} else {
> +			data[i] = cpu_to_le16((u16)ret);
> +		}
> +	}
> +
> +	/* on success return the result of the mxl862xx_send_cmd() */
> +	ret = cmd_ret;
> +
> +	dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data);
> +
> +out:
> +	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
> +
> +	return ret;
> +}
> +
> +int mxl862xx_reset(struct mxl862xx_priv *priv)
> +{
> +	int ret;
> +
> +	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
> +
> +	/* Software reset */
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0);
> +	if (ret)
> +		goto out;
> +
> +	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET);
> +out:
> +	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
> +
> +	if (!ret)
> +		usleep_range(4000000, 6000000);

This unconditionally sleeps 4 to 6 seconds after a reset?
Does the reset command reboot Zephyr?
Are we unable to know when we have communication with the firmware again?
Like read SYS_MISC_FW_VERSION until it's equal to what was originally
read during probing? Does this save any time?

> +
> +	return ret;
> +}
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.h b/drivers/net/dsa/mxl862xx/mxl862xx-host.h
> new file mode 100644
> index 0000000000000..eb5acb81feea6
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h
> @@ -0,0 +1,4 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, bool read);
> +int mxl862xx_reset(struct mxl862xx_priv *priv);
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
> new file mode 100644
> index 0000000000000..8e0dcfeb3b5c2
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
> @@ -0,0 +1,361 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Driver for MaxLinear MxL862xx switch family
> + *
> + * Copyright (C) 2024 MaxLinear Inc.
> + * Copyright (C) 2025 John Crispin <john@phrozen.org>
> + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/of_device.h>
> +#include <linux/of_mdio.h>
> +#include <linux/phy.h>
> +#include <linux/phylink.h>
> +#include <net/dsa.h>
> +
> +#include "mxl862xx.h"
> +#include "mxl862xx-api.h"
> +#include "mxl862xx-cmd.h"
> +#include "mxl862xx-host.h"
> +
> +#define MXL862XX_API_WRITE(dev, cmd, data) \
> +	mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false)
> +#define MXL862XX_API_READ(dev, cmd, data) \
> +	mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true)
> +
> +#define DSA_MXL_PORT(port) ((port) + 1)
> +
> +#define MXL862XX_SDMA_PCTRLP(p) (0xBC0 + ((p) * 0x6))
> +#define MXL862XX_SDMA_PCTRL_EN BIT(0)
> +
> +#define MXL862XX_FDMA_PCTRLP(p) (0xA80 + ((p) * 0x6))
> +#define MXL862XX_FDMA_PCTRL_EN BIT(0)
> +
> +/* PHY access via firmware relay */
> +static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port,
> +				 int devadd, int reg)
> +{
> +	struct mdio_relay_data param = {
> +		.phy = port,
> +		.mmd = devadd,
> +		.reg = cpu_to_le16(reg),
> +	};
> +	int ret;
> +
> +	ret = MXL862XX_API_READ(priv, INT_GPHY_READ, param);
> +	if (ret)
> +		return ret;
> +
> +	return le16_to_cpu(param.data);
> +}
> +
> +static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port,
> +				  int devadd, int reg, u16 data)
> +{
> +	struct mdio_relay_data param = {
> +		.phy = port,
> +		.mmd = devadd,
> +		.reg = cpu_to_le16(reg),
> +		.data = cpu_to_le16(data),
> +	};
> +
> +	return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param);
> +}
> +
> +static int mxl862xx_phy_read(struct dsa_switch *ds, int port, int reg)
> +{
> +	return mxl862xx_phy_read_mmd(ds->priv, port, 0, reg);
> +}
> +
> +static int mxl862xx_phy_write(struct dsa_switch *ds, int port, int reg, u16 data)
> +{
> +	return mxl862xx_phy_write_mmd(ds->priv, port, 0, reg, data);
> +}
> +
> +static int mxl862xx_configure_tag_proto(struct dsa_port *dp, bool enable)

Why pass "bool enable" if you always set it to true?

> +{
> +	struct mxl862xx_ctp_port_assignment assign = {
> +		.number_of_ctp_port = cpu_to_le16(enable ? (32 - DSA_MXL_PORT(dp->index)) : 1),
> +		.logical_port_id = DSA_MXL_PORT(dp->index),
> +		.first_ctp_port_id = cpu_to_le16(DSA_MXL_PORT(dp->index)),
> +		.mode = cpu_to_le32(MXL862XX_LOGICAL_PORT_GPON),
> +	};
> +	struct mxl862xx_ss_sp_tag tag = {
> +		.pid = DSA_MXL_PORT(dp->index),
> +		.mask = MXL862XX_SS_SP_TAG_MASK_RX | MXL862XX_SS_SP_TAG_MASK_TX,
> +		.rx = enable ? MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT :
> +			       MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT,
> +		.tx = enable ? MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE :
> +			       MXL862XX_SS_SP_TAG_TX_TAG_REMOVE,
> +	};
> +	int ret;
> +
> +	ret = MXL862XX_API_WRITE(dp->ds->priv, MXL862XX_SS_SPTAG_SET, tag);
> +	if (ret)
> +		return ret;
> +
> +	return MXL862XX_API_WRITE(dp->ds->priv, MXL862XX_CTP_PORTASSIGNMENTSET, assign);
> +}
> +
> +static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable)
> +{
> +	struct mxl862xx_register_mod sdma = {
> +		.addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(DSA_MXL_PORT(port))),
> +		.data = cpu_to_le16(enable ? MXL862XX_SDMA_PCTRL_EN : 0),
> +		.mask = cpu_to_le16(MXL862XX_SDMA_PCTRL_EN),
> +	};
> +	struct mxl862xx_register_mod fdma = {
> +		.addr = cpu_to_le16(MXL862XX_FDMA_PCTRLP(DSA_MXL_PORT(port))),
> +		.data = cpu_to_le16(enable ? MXL862XX_FDMA_PCTRL_EN : 0),
> +		.mask = cpu_to_le16(MXL862XX_FDMA_PCTRL_EN),
> +	};
> +	int ret;
> +
> +	if (!dsa_is_user_port(ds, port))
> +		return 0;

I am a bit surprised that the CPU ports don't need the Fetch DMA and the
Store DMA enabled? Perhaps I don't understand what these blocks do.

> +
> +	ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, sdma);
> +	if (ret)
> +		return ret;
> +
> +	return MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, fdma);
> +}
> +
> +static int mxl862xx_port_enable(struct dsa_switch *ds, int port,
> +				struct phy_device *phydev)
> +{
> +	return mxl862xx_port_state(ds, port, true);
> +}
> +
> +static void mxl862xx_port_disable(struct dsa_switch *ds, int port)
> +{
> +	mxl862xx_port_state(ds, port, false);
> +}
> +
> +static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum)
> +{
> +	return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum);
> +}
> +
> +static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port,
> +				      int regnum, u16 val)
> +{
> +	return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val);
> +}
> +
> +static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port,
> +					 int devadd, int regnum)
> +{
> +	return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum);
> +}
> +
> +static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port,
> +					  int devadd, int regnum, u16 val)
> +{
> +	return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val);
> +}
> +
> +static int mxl862xx_setup_mdio(struct dsa_switch *ds)
> +{
> +	struct mxl862xx_priv *priv = ds->priv;
> +	struct device *dev = ds->dev;
> +	struct device_node *mdio_np;
> +	struct mii_bus *bus;
> +	static int idx;
> +	int ret;
> +
> +	bus = devm_mdiobus_alloc(dev);
> +	if (!bus)
> +		return -ENOMEM;
> +
> +	bus->priv = priv;
> +	ds->user_mii_bus = bus;
> +	bus->name = KBUILD_MODNAME "-mii";
> +	snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
> +	bus->read_c45 = mxl862xx_phy_read_c45_mii_bus;
> +	bus->write_c45 = mxl862xx_phy_write_c45_mii_bus;
> +	bus->read = mxl862xx_phy_read_mii_bus;
> +	bus->write = mxl862xx_phy_write_mii_bus;
> +	bus->parent = dev;
> +	bus->phy_mask = ~ds->phys_mii_mask;
> +
> +	mdio_np = of_get_child_by_name(dev->of_node, "mdio");
> +	if (!mdio_np)
> +		return -ENODEV;
> +
> +	ret = devm_of_mdiobus_register(dev, bus, mdio_np);
> +	of_node_put(mdio_np);
> +
> +	return ret;
> +}
> +
> +static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
> +				      struct phylink_config *config)
> +{
> +	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
> +				   MAC_100 | MAC_1000 | MAC_2500FD;
> +
> +	__set_bit(PHY_INTERFACE_MODE_INTERNAL,
> +		  config->supported_interfaces);
> +}
> +
> +static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
> +						       int port,
> +						       enum dsa_tag_protocol m)
> +{
> +	return DSA_TAG_PROTO_MXL862;
> +}
> +
> +static int mxl862xx_setup(struct dsa_switch *ds)
> +{
> +	struct mxl862xx_priv *priv = ds->priv;
> +	struct dsa_port *cpu_dp;
> +	int ret;
> +
> +	ret = mxl862xx_reset(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = mxl862xx_setup_mdio(ds);
> +	if (ret)
> +		return ret;
> +
> +	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
> +		ret = mxl862xx_configure_tag_proto(cpu_dp, true);
> +		if (ret)
> +			return ret;
> +	}

Do the ports know not to forward packets between each other by default,
just to the CPU port? Which setting does that? Is address learning
turned off on user ports, since they are operating as standalone?

> +
> +	return 0;
> +}
> +
> +static const struct dsa_switch_ops mxl862xx_switch_ops = {
> +	.get_tag_protocol = mxl862xx_get_tag_protocol,
> +	.phylink_get_caps = mxl862xx_phylink_get_caps,
> +	.phy_read = mxl862xx_phy_read,
> +	.phy_write = mxl862xx_phy_write,
> +	.port_disable = mxl862xx_port_disable,
> +	.port_enable = mxl862xx_port_enable,
> +	.setup = mxl862xx_setup,
> +};
> +
> +static void mxl862xx_phylink_mac_config(struct phylink_config *config,
> +					unsigned int mode,
> +					const struct phylink_link_state *state)
> +{
> +}
> +
> +static void mxl862xx_phylink_mac_link_down(struct phylink_config *config,
> +					   unsigned int mode,
> +					   phy_interface_t interface)
> +{
> +}
> +
> +static void mxl862xx_phylink_mac_link_up(struct phylink_config *config,
> +					 struct phy_device *phydev,
> +					 unsigned int mode,
> +					 phy_interface_t interface,
> +					 int speed, int duplex,
> +					 bool tx_pause, bool rx_pause)
> +{
> +}
> +
> +static struct phylink_pcs *
> +mxl862xx_phylink_mac_select_pcs(struct phylink_config *config,
> +				phy_interface_t interface)
> +{
> +	return NULL;
> +}
> +
> +static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = {
> +	.mac_config = mxl862xx_phylink_mac_config,
> +	.mac_link_down = mxl862xx_phylink_mac_link_down,
> +	.mac_link_up = mxl862xx_phylink_mac_link_up,
> +	.mac_select_pcs = mxl862xx_phylink_mac_select_pcs,
> +};
> +
> +static int mxl862xx_probe(struct mdio_device *mdiodev)
> +{
> +	struct device *dev = &mdiodev->dev;
> +	struct mxl862xx_priv *priv;
> +	struct dsa_switch *ds;
> +	struct mxl862xx_sys_fw_image_version fw;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->mdiodev = mdiodev;
> +	priv->hw_info = of_device_get_match_data(dev);
> +	if (!priv->hw_info)
> +		return -EINVAL;
> +
> +	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
> +	if (!ds)
> +		return -ENOMEM;
> +
> +	priv->ds = ds;
> +	ds->dev = dev;
> +	ds->priv = priv;
> +	ds->ops = &mxl862xx_switch_ops;
> +	ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops;
> +	ds->num_ports = priv->hw_info->max_ports;
> +
> +	dev_set_drvdata(dev, ds);
> +
> +	ret = dsa_register_switch(ds);
> +	if (ret)
> +		return ret;
> +
> +	ret = MXL862XX_API_READ(priv, SYS_MISC_FW_VERSION, fw);
> +	if (!ret)
> +		dev_info(dev, "Firmware version %d.%d.%d.%d\n",
> +			 fw.iv_major, fw.iv_minor,
> +			 fw.iv_revision, fw.iv_build_num);
> +
> +	return 0;
> +}
> +
> +static void mxl862xx_remove(struct mdio_device *mdiodev)
> +{
> +	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
> +
> +	dsa_unregister_switch(ds);
> +}
> +
> +static const struct mxl862xx_hw_info mxl86282_data = {
> +	.max_ports = MXL862XX_MAX_PORT_NUM,
> +	.phy_ports = MXL86282_PHY_PORT_NUM,
> +	.ext_ports = MXL86282_EXT_PORT_NUM,
> +};
> +
> +static const struct mxl862xx_hw_info mxl86252_data = {
> +	.max_ports = MXL862XX_MAX_PORT_NUM,
> +	.phy_ports = MXL86252_PHY_PORT_NUM,
> +	.ext_ports = MXL86252_EXT_PORT_NUM,
> +};
> +
> +static const struct of_device_id mxl862xx_of_match[] = {
> +	{ .compatible = "maxlinear,mxl86282", .data = &mxl86282_data },
> +	{ .compatible = "maxlinear,mxl86252", .data = &mxl86252_data },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, mxl862xx_of_match);
> +
> +static struct mdio_driver mxl862xx_driver = {
> +	.probe  = mxl862xx_probe,
> +	.remove = mxl862xx_remove,

This is missing the non-optional shutdown hook again.

> +	.mdiodrv.driver = {
> +		.name = "mxl862xx",
> +		.of_match_table = mxl862xx_of_match,
> +	},
> +};
> +
> +mdio_module_driver(mxl862xx_driver);
> +
> +MODULE_DESCRIPTION("Minimal driver for MaxLinear MxL862xx switch family");

I suspect this description may not age well, and may remain "minimal"
even when the driver gains more substantial features.

> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:mxl862xx");

What is the role of this MODULE_ALIAS?

> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx/mxl862xx.h
> new file mode 100644
> index 0000000000000..66d194db8d6dd
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#define MXL862XX_MAX_PHY_PORT_NUM	8
> +#define MXL862XX_MAX_EXT_PORT_NUM	7
> +#define MXL862XX_MAX_PORT_NUM		(MXL862XX_MAX_PHY_PORT_NUM + \
> +					 MXL862XX_MAX_EXT_PORT_NUM)
> +
> +#define MXL86252_PHY_PORT_NUM		5
> +#define MXL86282_PHY_PORT_NUM		8
> +
> +#define MXL86252_EXT_PORT_NUM		2
> +#define MXL86282_EXT_PORT_NUM		2
> +
> +struct mxl862xx_hw_info {
> +	u8 max_ports;
> +	u8 phy_ports;
> +	u8 ext_ports;
> +};
> +
> +struct mxl862xx_priv {
> +	struct dsa_switch *ds;
> +	struct mdio_device *mdiodev;
> +	const struct mxl862xx_hw_info *hw_info;
> +};
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx
  2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
@ 2025-12-16 22:48   ` Vladimir Oltean
  2025-12-17  1:04   ` Rob Herring
  1 sibling, 0 replies; 11+ messages in thread
From: Vladimir Oltean @ 2025-12-16 22:48 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, Simon Horman, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

On Mon, Dec 15, 2025 at 12:11:22AM +0000, Daniel Golle wrote:
> +examples:
> +  - |
> +    mdio {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        switch@0 {
> +            compatible = "maxlinear,mxl86282";
> +            reg = <0>;
> +
> +            ethernet-ports {
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +
> +                port@0 {
> +                    reg = <0>;
> +                    label = "lan1";

Please remove port labels from the example.

> +                    phy-handle = <&phy0>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@1 {
> +                    reg = <1>;
> +                    label = "lan2";
> +                    phy-handle = <&phy1>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@2 {
> +                    reg = <2>;
> +                    label = "lan3";
> +                    phy-handle = <&phy2>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@3 {
> +                    reg = <3>;
> +                    label = "lan4";
> +                    phy-handle = <&phy3>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@4 {
> +                    reg = <4>;
> +                    label = "lan5";
> +                    phy-handle = <&phy4>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@5 {
> +                    reg = <5>;
> +                    label = "lan6";
> +                    phy-handle = <&phy5>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@6 {
> +                    reg = <6>;
> +                    label = "lan7";
> +                    phy-handle = <&phy6>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@7 {
> +                    reg = <7>;
> +                    label = "lan8";
> +                    phy-handle = <&phy7>;
> +                    phy-mode = "internal";
> +                };
> +
> +                port@8 {
> +                    reg = <8>;
> +                    label = "cpu";
> +                    ethernet = <&gmac0>;
> +                    phy-mode = "usxgmii";
> +
> +                    fixed-link {
> +                        speed = <10000>;
> +                        full-duplex;
> +                    };
> +                };
> +            };

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx
  2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
  2025-12-16 22:48   ` Vladimir Oltean
@ 2025-12-17  1:04   ` Rob Herring
  1 sibling, 0 replies; 11+ messages in thread
From: Rob Herring @ 2025-12-17  1:04 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, Simon Horman, netdev, devicetree,
	linux-kernel, Frank Wunderlich, Chad Monroe, Cezary Wilmanski,
	Avinash Jayaraman, Bing tao Xu, Liang Xu, Juraj Povazanec,
	Fanni (Fang-Yi) Chan, Benny (Ying-Tsan) Weng, Livia M. Rosu,
	John Crispin

On Mon, Dec 15, 2025 at 12:11:22AM +0000, Daniel Golle wrote:
> Add documentation and an example for MaxLinear MxL86282 and MxL86252
> switches.

Since you have to resend, drop 'bindings for' in the subject.

> 
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> RFC v3: no changes
> RFC v2: better description in dt-bindings doc
> 
>  .../bindings/net/dsa/maxlinear,mxl862xx.yaml  | 162 ++++++++++++++++++
>  MAINTAINERS                                   |   6 +
>  2 files changed, 168 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-12-17  1:04 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-15  0:11 [PATCH RFC net-next v3 0/4] net: dsa: initial support for MaxLinear MxL862xx switches Daniel Golle
2025-12-15  0:11 ` [PATCH RFC net-next v3 1/4] dt-bindings: net: dsa: add bindings for MaxLinear MxL862xx Daniel Golle
2025-12-16 22:48   ` Vladimir Oltean
2025-12-17  1:04   ` Rob Herring
2025-12-15  0:11 ` [PATCH RFC net-next v3 2/4] net: dsa: add tag formats for MxL862xx switches Daniel Golle
2025-12-15  1:46   ` Andrew Lunn
2025-12-15 14:28   ` Simon Horman
2025-12-16 20:39   ` Vladimir Oltean
2025-12-15  0:12 ` [PATCH RFC net-next v3 3/4] net: mdio: add unlocked mdiodev C45 bus accessors Daniel Golle
2025-12-15  0:12 ` [PATCH RFC net-next v3 4/4] net: dsa: add basic initial driver for MxL862xx switches Daniel Golle
2025-12-16 22:43   ` Vladimir Oltean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).