mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver
@ 2026-09-23 10:39 Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 1/8] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim
                   ` (7 more replies)
  0 siblings, 8 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim, Krzysztof Kozlowski

Add support for SoC-e Ethernet switch IP cores synthesized for and
implemented in FPGAs.

The series introduces the SoC-e vendor prefix and device tree bindings,
an EtherType-based SDSA tagger, an MDIO controller driver, and a DSA
switch driver.

The switch driver uses a memory-mapped control interface. During probe,
it detects the core version and the licensed and implemented port
counts, and verifies that the synthesized core provides DSA support.
Port counts from 3 through 31 are supported.

It supports Ethernet switching using MII, GMII, RMII, and RGMII port
interfaces. VLAN filtering and membership are also offloaded when the
corresponding hardware feature is synthesized. Unsupported hardware STP
offloading is disabled.

External MDIO outputs are exposed as separate logical MDIO buses sharing
the integrated MDIO controller. The controller supports Clause 22 and
Clause 45 transactions, while the generic MMIO MDIO mux selects the
external bus through the shared control register.

The drivers were tested with a SoC-e MRS 25.01 IP core on a Xilinx
ZynqMP platform with the following options enabled: 3 ports, DSA, IEEE
802.1w - RSTP, MAC Table Type with Hybrid SVL/IV, Port-based VLAN, QoS -
Priorities, Statistic Counters.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
Changes in v3:
- Rebase onto net-next/main and update tag protocol number in dsa.h
- Drop the deprecated ports and port@ node names and keep only
  ethernet-ports and ethernet-port@ in the binding.
- Drop labels from the switch binding example.
- Rename the MDIO master to MDIO controller.
- New Patches 2 and 5: Move the integrated MDIO controller into a
  separate driver and add the corresponding binding, Kconfig entry, and
  build integration.
- Switch from internal MDIO multiplexing to the generic MMIO MDIO mux
  framework for selecting the synthesized external MDIO buses.
- Check that the MDIO controller is idle before programming a
  transaction and wait for completion afterward.
- Replace MDIO magic values and resource indices with named definitions.
- Reduce MDIO timeout from 1s to more reasonable 1ms.
- Remove inline declarations and unnecessary checks from the remaining
  MDIO code.
- Use mdio-mux-mmioreg for the 11-bit MDIO bus selector at bits 26:16
  and access it through the upper 16-bit half of the shared control
  register.
- Update the switch binding to describe the MDIO controller, generic
  MMIO mux, child buses, and PHY references.
- Remove struct soce_probe_desc, since only one register layout is
  supported.
- Reject configurations with fewer than three implemented ports.
- Add port enable and disable callbacks to prevent unmanaged forwarding
  after driver unbind or module removal.
- Disable tagging during teardown.
- Rename and regroup register field macros to identify their associated
  registers.
- Remove obsolete and irrelevant comments.
- Add new comments for proper documentation and clarity.
- Switch to dev_err_probe() to improve probe error reporting.
- Embed struct dsa_switch in struct soce_priv instead of allocating it
  separately.
- Remove the trailing comma after the sentinel entry in the match table.

Follow-up validation and feature work using a new IP core:
- New patch 7: Add basic VLAN hardware offload through
  port_vlan_filtering, port_vlan_add, and port_vlan_del.
- Warn when the Port VLAN feature is unavailable and reject VLAN
  configuration with netlink extended acknowledgements.
- Reset VLAN offloading configuration during teardown.
- New patch 8: Disable STP offloading, which is not supported by the
  initial driver.
- Improvements in SDSA tag handling:
  - Add specific skb drop reasons for malformed SDSA frames.
  - Restore VLAN metadata from the SDSA tag on receive and encode skb
    VLAN metadata into the SDSA tag on transmit.

- Link to v2: https://patch.msgid.link/20260903-devel-vstrassheim-soce-dsa-ml-v2-0-fb0587cb466b@linutronix.de

Changes in v2:
- Correctly use net-next prefix
- Rework the binding into a single MMIO-based switch node
- Model hardware MDIO outputs as separate logical MDIO buses
- Detect the switch version, features, and port count from hardware
- Drop incomplete STP, bridge, and FDB offloading
- Derive phylink capabilities from each port's phy-mode
- Harden MDIO access and SDSA receive validation
- Address binding, naming, and coding style review comments

Link to v1: https://patch.msgid.link/20260729-devel-vstrassheim-soce-dsa-ml-v1-0-be569dae1b20@linutronix.de

To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Andrew Lunn <andrew@lunn.ch>
To: Vladimir Oltean <olteanv@gmail.com>
To: "David S. Miller" <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Vasilij Strassheim <v.strassheim@linutronix.de>
To: Simon Horman <horms@kernel.org>
To: Russell King <linux@armlinux.org.uk>
To: Andrew Lunn <andrew+netdev@lunn.ch>
To: Heiner Kallweit <hkallweit1@gmail.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: netdev@vger.kernel.org

---
Vasilij Strassheim (8):
      dt-bindings: vendor-prefixes: Add soce
      dt-bindings: net: Add SoC-e SWIP MDIO controller
      dt-bindings: net: dsa: Add SoC-e SWIP switch
      net: dsa: Add tag handling for SoC-e switches
      net: mdio: Add SoC-e SWIP MDIO controller driver
      net: dsa: soce: Add basic support for SoC-e switch IP cores
      net: dsa: soce: Add VLAN offload support
      net: dsa: soce: Disable unsupported hardware STP

 .../devicetree/bindings/net/dsa/soce,swip.yaml     | 162 +++++
 .../devicetree/bindings/net/soce,swip-mdio.yaml    |  47 ++
 .../devicetree/bindings/vendor-prefixes.yaml       |   2 +
 drivers/net/dsa/Kconfig                            |   2 +
 drivers/net/dsa/Makefile                           |   1 +
 drivers/net/dsa/soce/Kconfig                       |  14 +
 drivers/net/dsa/soce/Makefile                      |   4 +
 drivers/net/dsa/soce/soce_dsa.h                    |  38 ++
 drivers/net/dsa/soce/soce_dsa_core.c               | 737 +++++++++++++++++++++
 drivers/net/mdio/Kconfig                           |   7 +
 drivers/net/mdio/Makefile                          |   1 +
 drivers/net/mdio/mdio-soce.c                       | 239 +++++++
 include/net/dsa.h                                  |   2 +
 net/dsa/Kconfig                                    |   6 +
 net/dsa/Makefile                                   |   1 +
 net/dsa/tag_sdsa.c                                 | 158 +++++
 16 files changed, 1421 insertions(+)
---
base-commit: 944ae66642b726bd6b25ae71b1e9ff88a0e0bdb0
change-id: 20260729-devel-vstrassheim-soce-dsa-ml-20d6a5adb838

Best regards,
--  
Vasilij Strassheim <v.strassheim@linutronix.de>


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

* [PATCH net-next v3 1/8] dt-bindings: vendor-prefixes: Add soce
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller Vasilij Strassheim
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim, Krzysztof Kozlowski

Document binding for System-On-Chip Engineering, S.L.

Link: https://soc-e.com/
Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index ba2002969373..1c641e1f0ca6 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1581,6 +1581,8 @@ patternProperties:
     description: Standard Microsystems Corporation
   "^snps,.*":
     description: Synopsys, Inc.
+  "^soce,.*":
+    description: System-On-Chip Engineering, S.L.
   "^sochip,.*":
     description: Shenzhen SoChip Technology Co., Ltd.
   "^socionext,.*":

-- 
2.39.5


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

* [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 1/8] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 22:55   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  2026-09-23 10:39 ` [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch Vasilij Strassheim
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

Add a binding for the MDIO controller integrated into SoC-e SWIP
Ethernet switch IP cores.

The controller exposes separate register regions for transaction data
and for the shared transaction control and external bus selector
register. It acts as the parent bus for an MDIO mux used to access the
external MDIO interfaces synthesized into the switch core.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 .../devicetree/bindings/net/soce,swip-mdio.yaml    | 47 ++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml b/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml
new file mode 100644
index 000000000000..3cf4bde0508a
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/soce,swip-mdio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SoC-e switch IP MDIO controller
+
+maintainers:
+  - Vasilij Strassheim <v.strassheim@linutronix.de>
+
+description:
+  MDIO controller integrated into SoC-e Ethernet switch FPGA IP cores.
+  The controller accesses multiple external MDIO buses through a mux.
+
+$ref: mdio.yaml#
+
+properties:
+  compatible:
+    const: soce,swip-mdio-23-02
+
+  reg:
+    items:
+      - description: MDIO parameter, write data, and read data registers
+      - description: Shared MDIO transaction control and bus selection register
+
+  reg-names:
+    items:
+      - const: data
+      - const: control
+
+required:
+  - compatible
+  - reg
+  - reg-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    mdio@204 {
+        compatible = "soce,swip-mdio-23-02";
+        reg = <0x204 0xc>, <0x200 0x4>;
+        reg-names = "data", "control";
+        #address-cells = <1>;
+        #size-cells = <0>;
+    };

-- 
2.39.5


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

* [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 1/8] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 23:05   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  2026-09-23 10:39 ` [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches Vasilij Strassheim
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

Add a binding for SoC-e FPGA-based Ethernet switch IP cores.

The compatible identifies a register layout characterized by the core
and feature identification registers at the encoded offsets. The
concrete synthesis-time configuration is then autodetected from these
registers.

Describe the Ethernet ports and the optional integrated MDIO controller
with its generic MMIO mux and child buses.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 .../devicetree/bindings/net/dsa/soce,swip.yaml     | 162 +++++++++++++++++++++
 1 file changed, 162 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/dsa/soce,swip.yaml b/Documentation/devicetree/bindings/net/dsa/soce,swip.yaml
new file mode 100644
index 000000000000..d614fb2a29d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/soce,swip.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/soce,swip.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SoC-e ethernet switch IP core for FPGAs
+
+maintainers:
+  - Vasilij Strassheim <v.strassheim@linutronix.de>
+
+description:
+  SoC-e Ethernet switch IP cores are FPGA-based switches whose features
+  and number of ports are selected at synthesis time. Ports can connect
+  to CPUs, external PHYs, FPGA logic, or other switch cores. An integrated
+  MDIO controller accesses the per-port external buses through a mux.
+
+$ref: dsa.yaml#
+
+properties:
+  compatible:
+    const: soce,swip-00-04-0c-10
+    description:
+      Register layout with the core version register at offset 0x00 and
+      feature identification registers at offsets 0x04, 0x0c, and 0x10.
+      Switch instances using this register layout are autodetected from
+      these registers and use this compatible regardless of their
+      synthesis-time feature and port configuration.
+
+  reg:
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 1
+
+  ranges: true
+
+  mdio@204:
+    $ref: /schemas/net/soce,swip-mdio.yaml#
+    unevaluatedProperties: false
+    description:
+      Integrated MDIO controller bus on the master side of the mux.
+
+  mdio-mux@202:
+    $ref: /schemas/net/mdio-mux-mmioreg.yaml#
+    unevaluatedProperties: false
+
+  ethernet-ports:
+    type: object
+    patternProperties:
+      '^ethernet-port@[0-9a-f]+$':
+        type: object
+        $ref: dsa-port.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          reg:
+            maximum: 30
+            description:
+              Switch port index. Supported switch configurations have
+              up to 31 ports, numbered from 0 through 30.
+
+          phy-mode:
+            enum:
+              - mii
+              - gmii
+              - rmii
+              - rgmii
+              - rgmii-id
+              - rgmii-rxid
+              - rgmii-txid
+
+        required:
+          - reg
+          - phy-mode
+
+required:
+  - compatible
+  - reg
+  - ethernet-ports
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    ethernet-switch@80020000 {
+        compatible = "soce,swip-00-04-0c-10";
+        reg = <0x80020000 0x10000>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x80020000 0x10000>;
+
+        mdio_parent: mdio@204 {
+            compatible = "soce,swip-mdio-23-02";
+            reg = <0x204 0xc>, <0x200 0x4>;
+            reg-names = "data", "control";
+            #address-cells = <1>;
+            #size-cells = <0>;
+        };
+
+        mdio-mux@202 {
+            compatible = "mdio-mux-mmioreg", "mdio-mux";
+            reg = <0x202 0x2>;
+            mux-mask = <0x07ff>;
+            mdio-parent-bus = <&mdio_parent>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            mdio@0 {
+                reg = <0>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                switchphy0: ethernet-phy@1 {
+                    compatible = "ethernet-phy-ieee802.3-c22";
+                    reg = <1>;
+                };
+            };
+
+            mdio@1 {
+                reg = <1>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                switchphy1: ethernet-phy@1 {
+                    compatible = "ethernet-phy-ieee802.3-c22";
+                    reg = <1>;
+                };
+            };
+        };
+
+        ethernet-ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            ethernet-port@0 {
+                reg = <0>;
+                phy-handle = <&switchphy0>;
+                phy-mode = "rgmii-id";
+            };
+
+            ethernet-port@1 {
+                reg = <1>;
+                phy-handle = <&switchphy1>;
+                phy-mode = "rgmii-id";
+            };
+
+            ethernet-port@2 {
+                reg = <2>;
+                ethernet = <&eth0>;
+                phy-mode = "gmii";
+
+                fixed-link {
+                    speed = <1000>;
+                    full-duplex;
+                };
+            };
+        };
+    };

-- 
2.39.5


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

* [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
                   ` (2 preceding siblings ...)
  2026-09-23 10:39 ` [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
       [not found]   ` <20260924104003.A49F31F000FF@smtp.kernel.org>
  2026-09-27 12:28   ` netdev-bot+sashiko
  2026-09-23 10:39 ` [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver Vasilij Strassheim
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

SoC-e switches exchange frames with the conduit using an 8-byte SDSA
header inserted between the source MAC address and the original
EtherType. The header identifies itself with EtherType 0xdcdc and
carries the frame direction and a 10-bit source or destination port.

Add transmit support for FROM_CPU frames and receive support for TO_CPU
frames. Validate the EtherType, frame type, header length, and source
port before accepting received frames.

The SDSA header can also carry an 802.1Q TCI. Encode accelerated VLAN
metadata into the header on transmit and restore it as skb VLAN metadata
on receive.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 include/net/dsa.h  |   2 +
 net/dsa/Kconfig    |   6 ++
 net/dsa/Makefile   |   1 +
 net/dsa/tag_sdsa.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 167 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 5d12191b6f6f..8287fba8b107 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -62,6 +62,7 @@ struct tc_action;
 #define DSA_TAG_PROTO_KSZ8463_VALUE		34
 #define DSA_TAG_PROTO_MT7628_VALUE		35
 #define DSA_TAG_PROTO_KS8995_VALUE		36
+#define DSA_TAG_PROTO_SDSA_VALUE		37
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -101,6 +102,7 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_KSZ8463		= DSA_TAG_PROTO_KSZ8463_VALUE,
 	DSA_TAG_PROTO_MT7628		= DSA_TAG_PROTO_MT7628_VALUE,
 	DSA_TAG_PROTO_KS8995		= DSA_TAG_PROTO_KS8995_VALUE,
+	DSA_TAG_PROTO_SDSA		= DSA_TAG_PROTO_SDSA_VALUE,
 };
 
 struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 4f44bf3ede23..9b299cada316 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -194,6 +194,12 @@ config NET_DSA_TAG_RZN1_A5PSW
 	  Renesas RZ/N1 embedded switch that uses an 8 byte tag located after
 	  destination MAC address.
 
+config NET_DSA_TAG_SDSA
+	tristate "Tag driver for SoC-e switches using EtherType SDSA headers"
+	help
+	  Say Y or M if you want to enable support for tagging frames for the
+	  SoC-e switches.
+
 config NET_DSA_TAG_LAN9303
 	tristate "Tag driver for SMSC/Microchip LAN9303 family of switches"
 	help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 1f9cc30e9988..a4c8d0e6bc0f 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
 obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
 obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
 obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o
+obj-$(CONFIG_NET_DSA_TAG_SDSA) += tag_sdsa.o
 obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
 obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
 obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o
diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c
new file mode 100644
index 000000000000..8cc3fa357be4
--- /dev/null
+++ b/net/dsa/tag_sdsa.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020-2026 System on Chip engineering, S.L.
+ * Copyright (c) 2026 Linutronix GmbH
+ * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
+ */
+
+#include <linux/bitops.h>
+#include <linux/byteorder/generic.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/unaligned.h>
+
+#include "tag.h"
+
+#define SDSA_HLEN	8
+
+#define SDSA_NAME	"sdsa"
+#define ETH_P_SDSA	0xDCDC
+
+/* SDSA tag byte layout (after the 12-byte MAC header):
+ * Bytes 0-1: SDSA EtherType (0xDCDC)
+ * Bytes 2-3: Reserved
+ * Byte 4:    Frame type (bits 7-6), VLAN-info bit (bit 5), port[9:5] (bits 4-0)
+ * Byte 5:    Port[4:0] (bits 7-3)
+ * Bytes 6-7: PCP (bits 7-5) / CFI (bit 4) / VID (bits 3-0 + byte 7), only
+ *            meaningful when the VLAN-info bit is set.
+ */
+#define SDSA_TAG_FRAME_TYPE_MASK	GENMASK(7, 6)
+#define SDSA_TAG_VLAN_BIT		BIT(5)
+#define SDSA_TAG_PORT_HI_MASK		GENMASK(4, 0)
+#define SDSA_TAG_PORT_LO_MASK		GENMASK(7, 3)
+#define SDSA_TAG_PORT_HI_SHIFT		5
+#define SDSA_FRAME_TYPE_TO_CPU		0
+#define SDSA_FRAME_TYPE_FROM_CPU	1
+
+struct sdsa_tag {
+	__be16 ethertype;
+	__be16 reserved;
+	u8 frame_type_port_hi;
+	u8 port_lo;
+	__be16 vlan;
+};
+
+static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct dsa_port *dp = dsa_user_to_port(dev);
+	struct sdsa_tag *tag;
+	u16 vlan_tci;
+
+	BUILD_BUG_ON(sizeof(*tag) != SDSA_HLEN);
+
+	skb_push(skb, SDSA_HLEN);
+	dsa_alloc_etype_header(skb, SDSA_HLEN);
+
+	tag = dsa_etype_header_pos_tx(skb);
+	tag->ethertype = cpu_to_be16(ETH_P_SDSA);
+	tag->reserved = 0;
+	tag->frame_type_port_hi =
+		FIELD_PREP(SDSA_TAG_FRAME_TYPE_MASK, SDSA_FRAME_TYPE_FROM_CPU) |
+		FIELD_PREP(SDSA_TAG_PORT_HI_MASK,
+			   dp->index >> SDSA_TAG_PORT_HI_SHIFT);
+	tag->port_lo = FIELD_PREP(SDSA_TAG_PORT_LO_MASK, dp->index);
+	/* SDSA carries no TPID, so only encode 802.1Q C-tags. */
+	if (skb_vlan_tag_present(skb) &&
+	    skb->vlan_proto == htons(ETH_P_8021Q)) {
+		vlan_tci = skb_vlan_tag_get(skb);
+		__vlan_hwaccel_clear_tag(skb);
+		tag->frame_type_port_hi |= SDSA_TAG_VLAN_BIT;
+		tag->vlan = cpu_to_be16(vlan_tci);
+	} else {
+		tag->vlan = 0;
+	}
+
+	return skb;
+}
+
+static struct sk_buff *sdsa_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+	enum skb_drop_reason reason;
+	struct sdsa_tag *tag;
+	u16 dummy_vlan_tci;
+	bool vlan_present;
+	int source_port;
+	u16 encap_proto;
+	u8 frame_type;
+	u16 vlan_tci;
+
+	if (unlikely(!pskb_may_pull(skb, SDSA_HLEN))) {
+		reason = SKB_DROP_REASON_HDR_TRUNC;
+		goto out_drop;
+	}
+
+	tag = dsa_etype_header_pos_rx(skb);
+	if (unlikely(be16_to_cpu(tag->ethertype) != ETH_P_SDSA)) {
+		reason = SKB_DROP_REASON_DEV_HDR;
+		goto out_drop;
+	}
+
+	frame_type = FIELD_GET(SDSA_TAG_FRAME_TYPE_MASK,
+			       tag->frame_type_port_hi);
+	if (frame_type != SDSA_FRAME_TYPE_TO_CPU) {
+		reason = SKB_DROP_REASON_DEV_HDR;
+		goto out_drop;
+	}
+
+	vlan_present = tag->frame_type_port_hi & SDSA_TAG_VLAN_BIT;
+	vlan_tci = be16_to_cpu(tag->vlan);
+	encap_proto = get_unaligned_be16((u8 *)tag + SDSA_HLEN);
+
+	source_port = FIELD_GET(SDSA_TAG_PORT_HI_MASK,
+				tag->frame_type_port_hi) <<
+		      SDSA_TAG_PORT_HI_SHIFT;
+	source_port |= FIELD_GET(SDSA_TAG_PORT_LO_MASK, tag->port_lo);
+
+	skb->dev = dsa_conduit_find_user(dev, 0, source_port);
+	if (!skb->dev) {
+		reason = SKB_DROP_REASON_DEV_HDR;
+		goto out_drop;
+	}
+
+	skb_pull_rcsum(skb, SDSA_HLEN);
+	dsa_strip_etype_header(skb, SDSA_HLEN);
+	if (vlan_present) {
+		if (encap_proto == ETH_P_8021Q) {
+			skb_push_rcsum(skb, ETH_HLEN);
+			skb_reset_mac_header(skb);
+			if (__skb_vlan_pop(skb, &dummy_vlan_tci)) {
+				reason = SKB_DROP_REASON_NOMEM;
+				goto out_drop;
+			}
+			skb_pull_rcsum(skb, ETH_HLEN);
+		}
+
+		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
+	}
+
+	dsa_default_offload_fwd_mark(skb);
+	return skb;
+
+out_drop:
+	kfree_skb_reason(skb, reason);
+	return NULL;
+}
+
+static const struct dsa_device_ops sdsa_netdev_ops = {
+	.name = SDSA_NAME,
+	.proto = DSA_TAG_PROTO_SDSA,
+	.xmit = sdsa_xmit,
+	.rcv = sdsa_rcv,
+	.needed_headroom = SDSA_HLEN,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DSA tag driver for SoC-e SDSA protocol");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SDSA, SDSA_NAME);
+
+module_dsa_tag_driver(sdsa_netdev_ops);

-- 
2.39.5


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

* [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
                   ` (3 preceding siblings ...)
  2026-09-23 10:39 ` [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 23:10   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

Add a driver for the MDIO controller integrated into SoC-e Ethernet
switch IP cores. Support Clause 22 transactions and the separate address
and data cycles required for Clause 45 accesses.

The transaction control register is shared with the external MDIO bus
selector. The selector occupies bits 26:16 and is managed by a generic
MMIO MDIO mux through the upper 16-bit halfword. Preserve those bits
when starting a transaction.

Check that the controller is idle before programming transaction
registers and poll for completion after each transaction cycle. Return a
timeout instead of waiting indefinitely, and allow a later access to
retry if the hardware becomes idle again.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 drivers/net/mdio/Kconfig     |   7 ++
 drivers/net/mdio/Makefile    |   1 +
 drivers/net/mdio/mdio-soce.c | 239 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 247 insertions(+)

diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
index d44278f26fab..21f2b516880b 100644
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -189,6 +189,13 @@ config MDIO_REGMAP
 	  regmap. Users willing to use this driver must explicitly select
 	  REGMAP.
 
+config MDIO_SOCE
+	tristate "SoC-e MDIO controller"
+	depends on OF_MDIO && HAS_IOMEM
+	help
+	  This module provides a driver for the MDIO controller integrated
+	  into SoC-e Ethernet switch IP cores.
+
 config MDIO_THUNDER
 	tristate "ThunderX SOCs MDIO buses"
 	depends on 64BIT
diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
index 048586746026..079b46dea25f 100644
--- a/drivers/net/mdio/Makefile
+++ b/drivers/net/mdio/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_MDIO_OCTEON)		+= mdio-octeon.o
 obj-$(CONFIG_MDIO_PIC64HPSC)		+= mdio-pic64hpsc.o
 obj-$(CONFIG_MDIO_REALTEK_RTL9300)	+= mdio-realtek-rtl9300.o
 obj-$(CONFIG_MDIO_REGMAP)		+= mdio-regmap.o
+obj-$(CONFIG_MDIO_SOCE)			+= mdio-soce.o
 obj-$(CONFIG_MDIO_SUN4I)		+= mdio-sun4i.o
 obj-$(CONFIG_MDIO_THUNDER)		+= mdio-thunder.o
 obj-$(CONFIG_MDIO_XGENE)		+= mdio-xgene.o
diff --git a/drivers/net/mdio/mdio-soce.c b/drivers/net/mdio/mdio-soce.c
new file mode 100644
index 000000000000..59c0b3da483e
--- /dev/null
+++ b/drivers/net/mdio/mdio-soce.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020-2026 System on Chip engineering, S.L.
+ * Copyright (c) 2026 Linutronix GmbH
+ * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+
+#define SOCE_MDIO_TIMEOUT_US		1000
+
+#define SOCE_MDIO_PARAMS_OFFSET		0x0000
+#define SOCE_MDIO_WRITE_OFFSET		0x0004
+#define SOCE_MDIO_READ_OFFSET		0x0008
+#define SOCE_MDIO_DATA_IOMAP_IDX	0
+#define SOCE_MDIO_CTRL_IOMAP_IDX	1
+
+#define SOCE_MDIO_CTRL_BUS_MASK		GENMASK(26, 16)
+#define SOCE_MDIO_CTRL_TRANSTYPE_MASK	GENMASK(4, 3)
+#define SOCE_MDIO_CTRL_TRANSTYPE_WRITE	0x1
+#define SOCE_MDIO_CTRL_TRANSTYPE_READ	0x3
+#define SOCE_MDIO_CTRL_CLAUSE		BIT(1)
+#define SOCE_MDIO_CTRL_OPSTATUS		BIT(0)
+#define SOCE_MDIO_PARAMS_REGDEV_MASK	GENMASK(12, 8)
+#define SOCE_MDIO_PARAMS_PHYADDR_MASK	GENMASK(4, 0)
+#define SOCE_MDIO_READ_DATA_MASK	GENMASK(15, 0)
+
+struct soce_mdio {
+	void __iomem *ctrl;
+	void __iomem *data;
+};
+
+static void __iomem *soce_mdio_iomap(struct device *dev, int index)
+{
+	struct resource res;
+	int ret;
+
+	ret = of_address_to_resource(dev->of_node, index, &res);
+	if (ret)
+		return IOMEM_ERR_PTR(ret);
+
+	return devm_ioremap(dev, res.start, resource_size(&res));
+}
+
+static int soce_mdio_wait_for_idle(struct soce_mdio *priv)
+{
+	void __iomem *ctrl = priv->ctrl;
+	u32 val;
+
+	return readl_poll_timeout(ctrl, val,
+		!(val & SOCE_MDIO_CTRL_OPSTATUS), 10,
+		SOCE_MDIO_TIMEOUT_US);
+}
+
+static void soce_mdio_start(struct soce_mdio *priv, u32 command)
+{
+	void __iomem *ctrl = priv->ctrl;
+
+	/* Keep the currently selected MDIO bus while updating op bits. */
+	command |= readl(ctrl) & SOCE_MDIO_CTRL_BUS_MASK;
+	writel(command, ctrl);
+}
+
+static int soce_mdio_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+	struct soce_mdio *priv = bus->priv;
+	void __iomem *data = priv->data;
+	u32 command;
+	int ret;
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	writel(FIELD_PREP(SOCE_MDIO_PARAMS_REGDEV_MASK, regnum) |
+	       FIELD_PREP(SOCE_MDIO_PARAMS_PHYADDR_MASK, phy_addr),
+	       data + SOCE_MDIO_PARAMS_OFFSET);
+
+	command = FIELD_PREP(SOCE_MDIO_CTRL_TRANSTYPE_MASK,
+			     SOCE_MDIO_CTRL_TRANSTYPE_READ) |
+		  SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	return readl(data + SOCE_MDIO_READ_OFFSET) & SOCE_MDIO_READ_DATA_MASK;
+}
+
+static int soce_mdio_read_c45(struct mii_bus *bus, int phy_addr, int devad,
+			      int regnum)
+{
+	struct soce_mdio *priv = bus->priv;
+	void __iomem *data = priv->data;
+	u32 command;
+	int ret;
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	writel(FIELD_PREP(SOCE_MDIO_PARAMS_REGDEV_MASK, devad) |
+	       FIELD_PREP(SOCE_MDIO_PARAMS_PHYADDR_MASK, phy_addr),
+	       data + SOCE_MDIO_PARAMS_OFFSET);
+	writel(regnum, data + SOCE_MDIO_WRITE_OFFSET);
+
+	command = SOCE_MDIO_CTRL_CLAUSE | SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	command = FIELD_PREP(SOCE_MDIO_CTRL_TRANSTYPE_MASK,
+			     SOCE_MDIO_CTRL_TRANSTYPE_READ) |
+		  SOCE_MDIO_CTRL_CLAUSE | SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	return readl(data + SOCE_MDIO_READ_OFFSET) & SOCE_MDIO_READ_DATA_MASK;
+}
+
+static int soce_mdio_write(struct mii_bus *bus, int phy_addr, int regnum,
+			   u16 val)
+{
+	struct soce_mdio *priv = bus->priv;
+	void __iomem *data = priv->data;
+	u32 command;
+	int ret;
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	writel(FIELD_PREP(SOCE_MDIO_PARAMS_REGDEV_MASK, regnum) |
+	       FIELD_PREP(SOCE_MDIO_PARAMS_PHYADDR_MASK, phy_addr),
+	       data + SOCE_MDIO_PARAMS_OFFSET);
+	writel(val, data + SOCE_MDIO_WRITE_OFFSET);
+
+	command = FIELD_PREP(SOCE_MDIO_CTRL_TRANSTYPE_MASK,
+			     SOCE_MDIO_CTRL_TRANSTYPE_WRITE) |
+		  SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	return soce_mdio_wait_for_idle(priv);
+}
+
+static int soce_mdio_write_c45(struct mii_bus *bus, int phy_addr, int devad,
+			       int regnum, u16 val)
+{
+	struct soce_mdio *priv = bus->priv;
+	void __iomem *data = priv->data;
+	u32 command;
+	int ret;
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	writel(FIELD_PREP(SOCE_MDIO_PARAMS_REGDEV_MASK, devad) |
+	       FIELD_PREP(SOCE_MDIO_PARAMS_PHYADDR_MASK, phy_addr),
+	       data + SOCE_MDIO_PARAMS_OFFSET);
+	writel(regnum, data + SOCE_MDIO_WRITE_OFFSET);
+
+	command = SOCE_MDIO_CTRL_CLAUSE | SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	ret = soce_mdio_wait_for_idle(priv);
+	if (ret)
+		return ret;
+
+	writel(val, data + SOCE_MDIO_WRITE_OFFSET);
+
+	command = FIELD_PREP(SOCE_MDIO_CTRL_TRANSTYPE_MASK,
+			     SOCE_MDIO_CTRL_TRANSTYPE_WRITE) |
+		  SOCE_MDIO_CTRL_CLAUSE | SOCE_MDIO_CTRL_OPSTATUS;
+	soce_mdio_start(priv, command);
+
+	return soce_mdio_wait_for_idle(priv);
+}
+
+static int soce_mdio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct soce_mdio *priv;
+	struct mii_bus *bus;
+
+	bus = devm_mdiobus_alloc_size(dev, sizeof(*priv));
+	if (!bus)
+		return -ENOMEM;
+
+	priv = bus->priv;
+	priv->data = soce_mdio_iomap(dev, SOCE_MDIO_DATA_IOMAP_IDX);
+	if (IS_ERR(priv->data))
+		return PTR_ERR(priv->data);
+
+	priv->ctrl = soce_mdio_iomap(dev, SOCE_MDIO_CTRL_IOMAP_IDX);
+	if (IS_ERR(priv->ctrl))
+		return PTR_ERR(priv->ctrl);
+
+	bus->name = "soce mdio";
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+	bus->parent = dev;
+	bus->read = soce_mdio_read;
+	bus->write = soce_mdio_write;
+	bus->read_c45 = soce_mdio_read_c45;
+	bus->write_c45 = soce_mdio_write_c45;
+
+	return devm_of_mdiobus_register(dev, bus, dev->of_node);
+}
+
+static const struct of_device_id soce_mdio_of_match[] = {
+	{ .compatible = "soce,swip-mdio-23-02" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, soce_mdio_of_match);
+
+static struct platform_driver soce_mdio_driver = {
+	.probe = soce_mdio_probe,
+	.driver = {
+		.name = "soce-mdio",
+		.of_match_table = soce_mdio_of_match,
+	},
+};
+module_platform_driver(soce_mdio_driver);
+
+MODULE_AUTHOR("Vasilij Strassheim <v.strassheim@linutronix.de>");
+MODULE_DESCRIPTION("SoC-e MDIO controller driver");
+MODULE_LICENSE("GPL");

-- 
2.39.5


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

* [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
                   ` (4 preceding siblings ...)
  2026-09-23 10:39 ` [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 23:17   ` Andrew Lunn
                     ` (2 more replies)
  2026-09-23 10:39 ` [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support Vasilij Strassheim
  2026-09-23 10:39 ` [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP Vasilij Strassheim
  7 siblings, 3 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

Add a DSA driver for SoC-e FPGA-based Ethernet switch IP cores.

Read the core version and synthesis-time feature registers during probe.
Require DSA support and between 3 and 31 implemented ports, without
exceeding the licensed port count. Derive each port's phylink
capabilities from its phy-mode.

Enable "DSA custom rules" tagging for all frames during setup. This
directs ingress traffic from user ports to the CPU port while standalone
ports remain isolated. Enable and disable port ingress and egress
through the DSA port callbacks, and disable tagging again during
teardown so that unbinding the driver does not leave its managed
configuration active.

Populate the integrated MDIO controller and mux child devices used for
external PHY access.

Tested with a SoC-e MRS 25.01 IP core on a Xilinx ZynqMP platform.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 drivers/net/dsa/Kconfig              |   2 +
 drivers/net/dsa/Makefile             |   1 +
 drivers/net/dsa/soce/Kconfig         |  14 ++
 drivers/net/dsa/soce/Makefile        |   4 +
 drivers/net/dsa/soce/soce_dsa.h      |  26 +++
 drivers/net/dsa/soce/soce_dsa_core.c | 343 +++++++++++++++++++++++++++++++++++
 6 files changed, 390 insertions(+)

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index fe8cd5338fda..879fbede83f0 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -94,6 +94,8 @@ source "drivers/net/dsa/ocelot/Kconfig"
 
 source "drivers/net/dsa/qca/Kconfig"
 
+source "drivers/net/dsa/soce/Kconfig"
+
 source "drivers/net/dsa/sja1105/Kconfig"
 
 source "drivers/net/dsa/xrs700x/Kconfig"
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 7e637d56b35c..97de3d181440 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -26,4 +26,5 @@ obj-y				+= ocelot/
 obj-y				+= qca/
 obj-y				+= realtek/
 obj-y				+= sja1105/
+obj-y				+= soce/
 obj-y				+= xrs700x/
diff --git a/drivers/net/dsa/soce/Kconfig b/drivers/net/dsa/soce/Kconfig
new file mode 100644
index 000000000000..c31b7c5af695
--- /dev/null
+++ b/drivers/net/dsa/soce/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+config NET_DSA_SOCE
+	tristate "SoC-e switches"
+	depends on NET_DSA
+	depends on OF
+	depends on HAS_IOMEM
+	select MDIO_BUS_MUX_MMIOREG
+	select MDIO_SOCE
+	select NET_DSA_TAG_SDSA
+	help
+	  This enables support for switches based on SoC-e IP cores.
+	  Frames are exchanged with the CPU port using the SDSA DSA tag protocol.
+	  The driver supports switch variants whose features and number of ports
+	  are selected at synthesis time and detected at runtime.
diff --git a/drivers/net/dsa/soce/Makefile b/drivers/net/dsa/soce/Makefile
new file mode 100644
index 000000000000..2a6d95ef663f
--- /dev/null
+++ b/drivers/net/dsa/soce/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_NET_DSA_SOCE) += soce_dsa.o
+soce_dsa-objs := soce_dsa_core.o
diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h
new file mode 100644
index 000000000000..2acd4dbaf958
--- /dev/null
+++ b/drivers/net/dsa/soce/soce_dsa.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020-2026 System on Chip engineering, S.L.
+ * Copyright (c) 2026 Linutronix GmbH
+ * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
+ */
+
+#ifndef __SOCE_DSA_H
+#define __SOCE_DSA_H
+
+#include <linux/types.h>
+
+#include <net/dsa.h>
+
+#define SOCE_MAX_NUM_PORTS 31
+
+struct soce_dsa_local {
+	void __iomem *base_addr;
+};
+
+struct soce_priv {
+	struct soce_dsa_local local;
+	struct dsa_switch ds;
+};
+
+#endif /* __SOCE_DSA_H */
diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
new file mode 100644
index 000000000000..d391b11b94ad
--- /dev/null
+++ b/drivers/net/dsa/soce/soce_dsa_core.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020-2026 System on Chip engineering, S.L.
+ * Copyright (c) 2026 Linutronix GmbH
+ * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/phylink.h>
+#include <linux/platform_device.h>
+
+#include <net/dsa.h>
+
+#include "soce_dsa.h"
+
+#define SOCE_MIN_NUM_PORTS			3
+
+#define SOCE_CORE_VERSION_OFFSET		0x0000
+#define SOCE_CORE_VERSION_VERSION_SHIFT		24
+#define SOCE_CORE_VERSION_SUBVERSION_SHIFT	16
+#define SOCE_MIN_CORE_VERSION			0x24
+#define SOCE_MIN_CORE_SUBVERSION		0x01
+
+#define SOCE_LIC_FEATURES_OFFSET		0x0004
+#define SOCE_LIC_FEATURES_NUM_PORTS_MASK	GENMASK(31, 27)
+
+#define SOCE_IMPL_FEATURES0_OFFSET		0x000c
+#define SOCE_IMPL_FEATURES0_NUM_PORTS_MASK	GENMASK(31, 27)
+#define SOCE_IMPL_FEATURES0_PORT_VLAN		BIT(9)
+#define SOCE_IMPL_FEATURES0_DSA			BIT(23)
+
+#define SOCE_DSA_REGS_BASE			0x1200
+#define SOCE_TAG_ALL_FRAMES_CTRL_OFFSET		(SOCE_DSA_REGS_BASE + 0x001c)
+#define SOCE_TAG_ALL_FRAMES_CTRL_ENABLE		BIT(0)
+#define SOCE_CUSTOM_RULES_TAGGING_OFFSET	(SOCE_DSA_REGS_BASE + 0x0020)
+#define SOCE_CUSTOM_RULES_TAGGING_ENABLE	BIT(0)
+
+#define SOCE_PORTS_REGS_BASE			0x3000
+#define SOCE_PORTS_SELECTOR_OFFSET		SOCE_PORTS_REGS_BASE
+#define SOCE_PORTS_SELECTOR_PORT_MASK		GENMASK(7, 0)
+#define SOCE_PORTS_CTRL_OFFSET			(SOCE_PORTS_REGS_BASE + 0x0004)
+#define SOCE_PORTS_CTRL_INGR_EN			BIT(0)
+#define SOCE_PORTS_CTRL_EGR_EN			BIT(1)
+
+static void soce_phylink_get_caps(struct dsa_switch *ds, int port,
+				  struct phylink_config *config)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	phy_interface_t mode;
+	int ret;
+
+	ret = of_get_phy_mode(dp->dn, &mode);
+	if (ret)
+		return;
+
+	if (phy_interface_mode_is_rgmii(mode))
+		phy_interface_set_rgmii(config->supported_interfaces);
+	else
+		__set_bit(mode, config->supported_interfaces);
+
+	config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+	switch (mode) {
+	case PHY_INTERFACE_MODE_MII:
+		config->mac_capabilities |= MAC_10 | MAC_100;
+		break;
+	case PHY_INTERFACE_MODE_GMII:
+		config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000;
+		break;
+	case PHY_INTERFACE_MODE_RMII:
+		config->mac_capabilities |= MAC_10FD | MAC_100FD;
+		break;
+	default:
+		if (phy_interface_mode_is_rgmii(mode))
+			config->mac_capabilities |= MAC_10FD | MAC_100FD |
+						    MAC_1000FD;
+		break;
+	}
+}
+
+static int soce_sw_validate_core_version(u8 version, u8 subversion)
+{
+	if (version < SOCE_MIN_CORE_VERSION ||
+	    (version == SOCE_MIN_CORE_VERSION &&
+	     subversion < SOCE_MIN_CORE_SUBVERSION))
+		return -ENODEV;
+
+	return 0;
+}
+
+static void soce_sw_read_core_version(struct soce_dsa_local *local,
+				      u8 *version, u8 *subversion,
+				      u16 *revision)
+{
+	u32 regval;
+
+	regval = readl(local->base_addr + SOCE_CORE_VERSION_OFFSET);
+	*version = (u8)(regval >> SOCE_CORE_VERSION_VERSION_SHIFT);
+	*subversion = (u8)(regval >> SOCE_CORE_VERSION_SUBVERSION_SHIFT);
+	*revision = (u16)regval;
+}
+
+static int soce_sw_detect_features(struct soce_dsa_local *local,
+				   u32 *numports)
+{
+	void __iomem *base = local->base_addr;
+	u32 implemented_numports;
+	u32 licensed_numports;
+	u32 regval;
+
+	regval = readl(base + SOCE_LIC_FEATURES_OFFSET);
+	licensed_numports = FIELD_GET(SOCE_LIC_FEATURES_NUM_PORTS_MASK, regval);
+	if (!licensed_numports || licensed_numports > SOCE_MAX_NUM_PORTS)
+		return -EINVAL;
+
+	regval = readl(base + SOCE_IMPL_FEATURES0_OFFSET);
+	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
+		return -ENODEV;
+
+	implemented_numports =
+		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
+	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
+	    implemented_numports > licensed_numports)
+		return -EINVAL;
+
+	*numports = implemented_numports;
+
+	return 0;
+}
+
+static void soce_sw_enable_tagging(struct soce_dsa_local *local)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
+	regval |= SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
+	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
+
+	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
+	regval |= SOCE_CUSTOM_RULES_TAGGING_ENABLE;
+	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
+}
+
+static void soce_sw_disable_tagging(struct soce_dsa_local *local)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
+	regval &= ~SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
+	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
+
+	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
+	regval &= ~SOCE_CUSTOM_RULES_TAGGING_ENABLE;
+	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
+}
+
+static void soce_port_select(struct soce_dsa_local *local, int port)
+{
+	writel(FIELD_PREP(SOCE_PORTS_SELECTOR_PORT_MASK, port),
+	       local->base_addr + SOCE_PORTS_SELECTOR_OFFSET);
+}
+
+static void soce_port_set_enabled(struct soce_dsa_local *local, int port,
+				  bool enabled)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	soce_port_select(local, port);
+
+	regval = readl(base + SOCE_PORTS_CTRL_OFFSET);
+	if (enabled)
+		regval |= SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN;
+	else
+		regval &= ~(SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN);
+	writel(regval, base + SOCE_PORTS_CTRL_OFFSET);
+}
+
+static int soce_port_enable(struct dsa_switch *ds, int port,
+			    struct phy_device *phy)
+{
+	struct soce_priv *priv = ds->priv;
+
+	soce_port_set_enabled(&priv->local, port, true);
+
+	return 0;
+}
+
+static void soce_port_disable(struct dsa_switch *ds, int port)
+{
+	struct soce_priv *priv = ds->priv;
+
+	soce_port_set_enabled(&priv->local, port, false);
+}
+
+static int soce_setup(struct dsa_switch *ds)
+{
+	struct soce_priv *priv = ds->priv;
+
+	soce_sw_enable_tagging(&priv->local);
+
+	return 0;
+}
+
+static void soce_teardown(struct dsa_switch *ds)
+{
+	struct soce_priv *priv = ds->priv;
+
+	soce_sw_disable_tagging(&priv->local);
+}
+
+static enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds,
+						   int port,
+						   enum dsa_tag_protocol mprop)
+{
+	return DSA_TAG_PROTO_SDSA;
+}
+
+static const struct dsa_switch_ops soce_switch_ops = {
+	.get_tag_protocol	= soce_get_tag_protocol,
+	.setup			= soce_setup,
+	.teardown		= soce_teardown,
+	.phylink_get_caps	= soce_phylink_get_caps,
+	.port_enable		= soce_port_enable,
+	.port_disable		= soce_port_disable,
+};
+
+static int soce_sw_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct soce_dsa_local *local;
+	struct soce_priv *priv;
+	struct dsa_switch *ds;
+	u8 hw_subversion;
+	u16 hw_revision;
+	u32 hw_numports;
+	u8 hw_version;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	ds = &priv->ds;
+	ds->dev = dev;
+	ds->priv = priv;
+
+	local = &priv->local;
+	local->base_addr = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(local->base_addr))
+		return PTR_ERR(local->base_addr);
+
+	soce_sw_read_core_version(local, &hw_version, &hw_subversion,
+				  &hw_revision);
+
+	ret = soce_sw_detect_features(local, &hw_numports);
+	if (ret) {
+		if (ret == -ENODEV)
+			dev_err(dev, "switch core does not implement DSA\n");
+		else
+			dev_err(dev,
+				"invalid licensed or implemented features register\n");
+		return ret;
+	}
+
+	ret = soce_sw_validate_core_version(hw_version, hw_subversion);
+	if (ret) {
+		dev_err(dev, "unsupported switch core version %.2X.%.2X.%.4X\n",
+			hw_version, hw_subversion, hw_revision);
+		return ret;
+	}
+
+	ds->ops = &soce_switch_ops;
+	ds->num_ports = hw_numports;
+	ret = devm_of_platform_populate(dev);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to populate child devices\n");
+
+	dev_set_drvdata(dev, priv);
+
+	ret = dsa_register_switch(ds);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register DSA switch\n");
+
+	dev_info(dev,
+		 "probed soce switch core version %02x.%02x.%04x with %u ports\n",
+		 hw_version, hw_subversion, hw_revision, hw_numports);
+	return 0;
+}
+
+static void soce_sw_remove(struct platform_device *pdev)
+{
+	struct soce_priv *priv = platform_get_drvdata(pdev);
+
+	if (!priv)
+		return;
+
+	dsa_unregister_switch(&priv->ds);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static void soce_sw_shutdown(struct platform_device *pdev)
+{
+	struct soce_priv *priv = platform_get_drvdata(pdev);
+
+	if (!priv)
+		return;
+
+	dsa_switch_shutdown(&priv->ds);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static const struct of_device_id soce_of_match[] = {
+	{ .compatible = "soce,swip-00-04-0c-10" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver soce_driver = {
+	.probe = soce_sw_probe,
+	.remove = soce_sw_remove,
+	.shutdown = soce_sw_shutdown,
+	.driver = {
+		.name = "soce-swip",
+		.of_match_table = soce_of_match,
+	},
+};
+
+module_platform_driver(soce_driver);
+MODULE_DEVICE_TABLE(of, soce_of_match);
+MODULE_AUTHOR("Vasilij Strassheim <v.strassheim@linutronix.de>");
+MODULE_DESCRIPTION("Driver for SoC-e ethernet switch family");
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: mdio-soce mdio-mux-mmioreg");

-- 
2.39.5


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

* [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
                   ` (5 preceding siblings ...)
  2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 23:32   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  2026-09-23 10:39 ` [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP Vasilij Strassheim
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

Add VLAN filtering and membership offload for switch configurations that
implement the Port VLAN synthesis option. Without this feature active,
the switch removes VLAN information on CPU ingress without including the
removed information in the DSA tag. To avoid silent drops and
misbehaviour in this case, reject VLAN operations with a netlink
extended acknowledgment.

If the feature is available, configure ports in Hybrid and C-Port modes
and manage their PVID, ingress filtering and acceptance mode, and custom
egress untagging. Program the per-VID member and untagged port masks
through the hardware selector interface.

The port and VID selectors are shared by all VLAN operations, so
serialize selector and data register sequences with a dedicated mutex.
Track member and untagged masks in software and restore the shadow state
if programming fails.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 drivers/net/dsa/soce/soce_dsa.h      |  11 +
 drivers/net/dsa/soce/soce_dsa_core.c | 388 ++++++++++++++++++++++++++++++++++-
 2 files changed, 392 insertions(+), 7 deletions(-)

diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h
index 2acd4dbaf958..41533a89d680 100644
--- a/drivers/net/dsa/soce/soce_dsa.h
+++ b/drivers/net/dsa/soce/soce_dsa.h
@@ -9,6 +9,7 @@
 #define __SOCE_DSA_H
 
 #include <linux/types.h>
+#include <linux/mutex.h>
 
 #include <net/dsa.h>
 
@@ -18,8 +19,18 @@ struct soce_dsa_local {
 	void __iomem *base_addr;
 };
 
+struct soce_features {
+	u32 num_ports;
+	bool port_vlan;
+};
+
 struct soce_priv {
 	struct soce_dsa_local local;
+	struct soce_features features;
+	struct mutex vlan_lock; /* Serializes selector-based VLAN accesses */
+	u32 *vlan_members;
+	u32 *vlan_untagged;
+	u16 port_pvid[SOCE_MAX_NUM_PORTS];
 	struct dsa_switch ds;
 };
 
diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
index d391b11b94ad..f93ce9da8400 100644
--- a/drivers/net/dsa/soce/soce_dsa_core.c
+++ b/drivers/net/dsa/soce/soce_dsa_core.c
@@ -5,7 +5,10 @@
  * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
  */
 
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/of.h>
@@ -48,6 +51,39 @@
 #define SOCE_PORTS_CTRL_INGR_EN			BIT(0)
 #define SOCE_PORTS_CTRL_EGR_EN			BIT(1)
 
+#define SOCE_VLAN_REGS_BASE			0x0d00
+#define SOCE_VLAN_CTRL_OFFSET			SOCE_VLAN_REGS_BASE
+#define SOCE_VLAN_CTRL_ENABLE			BIT(0)
+#define SOCE_VLAN_RESET_OFFSET			(SOCE_VLAN_REGS_BASE + 0x0004)
+#define SOCE_VLAN_RESET_CMD			BIT(0)
+#define SOCE_VLAN_PORT_SEL_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0010)
+#define SOCE_VLAN_PORT_SEL_PORT_MASK		GENMASK(7, 0)
+#define SOCE_VLAN_PORT_MODE_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0014)
+#define SOCE_VLAN_PORT_MODE_HYBRID		0x2
+#define SOCE_VLAN_PORT_TYPE_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0018)
+#define SOCE_VLAN_PORT_TYPE_UNAWARE		0x0
+#define SOCE_VLAN_PORT_TYPE_C_PORT		0x1
+#define SOCE_VLAN_PORT_VLAN_OFFSET		(SOCE_VLAN_REGS_BASE + 0x001c)
+#define SOCE_VLAN_PORT_VLAN_PVID_MASK		GENMASK(11, 0)
+#define SOCE_VLAN_PORT_INGR_FILTER_OFFSET	(SOCE_VLAN_REGS_BASE + 0x0020)
+#define SOCE_VLAN_PORT_INGR_FILTER_EN		BIT(0)
+#define SOCE_VLAN_PORT_INGR_ACCEPT_OFFSET	(SOCE_VLAN_REGS_BASE + 0x0024)
+#define SOCE_VLAN_PORT_INGR_ACCEPT_ALL		0x0
+#define SOCE_VLAN_PORT_INGR_ACCEPT_TAGGED_ONLY	0x1
+#define SOCE_VLAN_PORT_EGR_TAG_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0028)
+#define SOCE_VLAN_PORT_EGR_TAG_MASK		GENMASK(1, 0)
+#define SOCE_VLAN_PORT_EGR_TAG_UNTAG_PORT	0x0
+#define SOCE_VLAN_PORT_EGR_TAG_CUSTOM_UNTAG	0x3
+#define SOCE_VLAN_VID_SEL_OFFSET		(SOCE_VLAN_REGS_BASE + 0x002c)
+#define SOCE_VLAN_VID_SEL_VID_MASK		GENMASK(11, 0)
+#define SOCE_VLAN_MEMBER_CTRL_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0030)
+#define SOCE_VLAN_MEMBER_CTRL_WRITE		BIT(0)
+#define SOCE_VLAN_MEMBER_PORTS_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0034)
+#define SOCE_VLAN_UNTAG_CTRL_OFFSET		(SOCE_VLAN_REGS_BASE + 0x003c)
+#define SOCE_VLAN_UNTAG_CTRL_WRITE		BIT(0)
+#define SOCE_VLAN_UNTAG_PORTS_OFFSET		(SOCE_VLAN_REGS_BASE + 0x0040)
+#define SOCE_VLAN_CMD_TIMEOUT_US		1000
+
 static void soce_phylink_get_caps(struct dsa_switch *ds, int port,
 				  struct phylink_config *config)
 {
@@ -107,7 +143,7 @@ static void soce_sw_read_core_version(struct soce_dsa_local *local,
 }
 
 static int soce_sw_detect_features(struct soce_dsa_local *local,
-				   u32 *numports)
+				   struct soce_features *features)
 {
 	void __iomem *base = local->base_addr;
 	u32 implemented_numports;
@@ -123,13 +159,15 @@ static int soce_sw_detect_features(struct soce_dsa_local *local,
 	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
 		return -ENODEV;
 
+	features->port_vlan = regval & SOCE_IMPL_FEATURES0_PORT_VLAN;
+
 	implemented_numports =
 		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
 	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
 	    implemented_numports > licensed_numports)
 		return -EINVAL;
 
-	*numports = implemented_numports;
+	features->num_ports = implemented_numports;
 
 	return 0;
 }
@@ -162,6 +200,178 @@ static void soce_sw_disable_tagging(struct soce_dsa_local *local)
 	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
 }
 
+static void soce_vlan_set_enabled(struct soce_dsa_local *local, bool enabled)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	regval = readl(base + SOCE_VLAN_CTRL_OFFSET);
+	if (enabled)
+		regval |= SOCE_VLAN_CTRL_ENABLE;
+	else
+		regval &= ~SOCE_VLAN_CTRL_ENABLE;
+	writel(regval, base + SOCE_VLAN_CTRL_OFFSET);
+}
+
+static int soce_vlan_reset(struct soce_dsa_local *local)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	writel(SOCE_VLAN_RESET_CMD, base + SOCE_VLAN_RESET_OFFSET);
+
+	return readl_poll_timeout(base + SOCE_VLAN_RESET_OFFSET,
+				  regval, !(regval & SOCE_VLAN_RESET_CMD), 10,
+				  SOCE_VLAN_CMD_TIMEOUT_US);
+}
+
+static int soce_vlan_wait_for_write(struct soce_dsa_local *local, u32 offset,
+				    u32 mask)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	return readl_poll_timeout(base + offset, regval,
+				  !(regval & mask), 10,
+				  SOCE_VLAN_CMD_TIMEOUT_US);
+}
+
+static void soce_vlan_select_port(struct soce_dsa_local *local, int port)
+{
+	writel(FIELD_PREP(SOCE_VLAN_PORT_SEL_PORT_MASK, port),
+	       local->base_addr + SOCE_VLAN_PORT_SEL_OFFSET);
+}
+
+static void soce_vlan_select_vid(struct soce_dsa_local *local, u16 vid)
+{
+	writel(FIELD_PREP(SOCE_VLAN_VID_SEL_VID_MASK, vid),
+	       local->base_addr + SOCE_VLAN_VID_SEL_OFFSET);
+}
+
+static void soce_vlan_config_port(struct soce_priv *priv, int port,
+				  bool vlan_filtering)
+{
+	struct soce_dsa_local *local = &priv->local;
+	void __iomem *base = local->base_addr;
+	u32 egress_tagging;
+	u32 port_type;
+	u32 regval;
+	u32 accept;
+
+	port_type = vlan_filtering ? SOCE_VLAN_PORT_TYPE_C_PORT :
+				     SOCE_VLAN_PORT_TYPE_UNAWARE;
+	egress_tagging = vlan_filtering ? SOCE_VLAN_PORT_EGR_TAG_CUSTOM_UNTAG :
+					   SOCE_VLAN_PORT_EGR_TAG_UNTAG_PORT;
+
+	soce_vlan_select_port(local, port);
+	writel(SOCE_VLAN_PORT_MODE_HYBRID, base + SOCE_VLAN_PORT_MODE_OFFSET);
+	writel(port_type, base + SOCE_VLAN_PORT_TYPE_OFFSET);
+	writel(FIELD_PREP(SOCE_VLAN_PORT_VLAN_PVID_MASK,
+			  priv->port_pvid[port]),
+	       base + SOCE_VLAN_PORT_VLAN_OFFSET);
+	writel(vlan_filtering ? SOCE_VLAN_PORT_INGR_FILTER_EN : 0,
+	       base + SOCE_VLAN_PORT_INGR_FILTER_OFFSET);
+
+	/* Without a PVID, untagged frames have no VLAN to be classified
+	 * into, so only accept tagged frames while filtering.
+	 */
+	accept = vlan_filtering && !priv->port_pvid[port] ?
+			 SOCE_VLAN_PORT_INGR_ACCEPT_TAGGED_ONLY :
+			 SOCE_VLAN_PORT_INGR_ACCEPT_ALL;
+	writel(accept, base + SOCE_VLAN_PORT_INGR_ACCEPT_OFFSET);
+
+	regval = readl(base + SOCE_VLAN_PORT_EGR_TAG_OFFSET);
+	regval &= ~SOCE_VLAN_PORT_EGR_TAG_MASK;
+	regval |= FIELD_PREP(SOCE_VLAN_PORT_EGR_TAG_MASK, egress_tagging);
+	writel(regval, base + SOCE_VLAN_PORT_EGR_TAG_OFFSET);
+}
+
+static int soce_vlan_write_entry(struct soce_priv *priv, u16 vid)
+{
+	struct soce_dsa_local *local = &priv->local;
+	void __iomem *base = local->base_addr;
+	u32 cpu_ports;
+	u32 untagged;
+	u32 members;
+	int ret;
+
+	/* The CPU port must be a tagged member of every active VLAN so
+	 * tagged frames can reach the conduit.
+	 */
+	cpu_ports = dsa_cpu_ports(&priv->ds);
+	members = priv->vlan_members[vid];
+	if (members)
+		members |= cpu_ports;
+	untagged = priv->vlan_untagged[vid] & ~cpu_ports;
+
+	soce_vlan_select_vid(local, vid);
+	writel(members, base + SOCE_VLAN_MEMBER_PORTS_OFFSET);
+	writel(SOCE_VLAN_MEMBER_CTRL_WRITE,
+	       base + SOCE_VLAN_MEMBER_CTRL_OFFSET);
+	ret = soce_vlan_wait_for_write(local, SOCE_VLAN_MEMBER_CTRL_OFFSET,
+				       SOCE_VLAN_MEMBER_CTRL_WRITE);
+	if (ret)
+		return ret;
+
+	writel(untagged, base + SOCE_VLAN_UNTAG_PORTS_OFFSET);
+	writel(SOCE_VLAN_UNTAG_CTRL_WRITE,
+	       base + SOCE_VLAN_UNTAG_CTRL_OFFSET);
+
+	return soce_vlan_wait_for_write(local, SOCE_VLAN_UNTAG_CTRL_OFFSET,
+					SOCE_VLAN_UNTAG_CTRL_WRITE);
+}
+
+static int soce_vlan_setup(struct dsa_switch *ds)
+{
+	struct soce_priv *priv = ds->priv;
+	struct soce_dsa_local *local;
+	struct dsa_port *dp;
+	int ret;
+
+	local = &priv->local;
+
+	if (!priv->features.port_vlan)
+		return 0;
+
+	ret = soce_vlan_reset(local);
+	if (ret) {
+		dev_err(ds->dev, "failed to reset VLAN configuration: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* Default every port to PVID 1, unfiltered, so standalone
+	 * forwarding keeps working before any bridge VLAN is configured.
+	 */
+	scoped_guard(mutex, &priv->vlan_lock) {
+		dsa_switch_for_each_available_port(dp, ds) {
+			priv->port_pvid[dp->index] = 1;
+			soce_vlan_config_port(priv, dp->index, false);
+		}
+		soce_vlan_set_enabled(local, true);
+	}
+
+	return 0;
+}
+
+static void soce_vlan_teardown(struct soce_priv *priv)
+{
+	struct soce_dsa_local *local = &priv->local;
+	int ret;
+
+	if (!priv->features.port_vlan)
+		return;
+
+	scoped_guard(mutex, &priv->vlan_lock) {
+		ret = soce_vlan_reset(local);
+		if (ret)
+			dev_warn(priv->ds.dev,
+				 "failed to reset VLAN configuration during teardown: %d\n",
+				 ret);
+		soce_vlan_set_enabled(local, false);
+	}
+}
+
 static void soce_port_select(struct soce_dsa_local *local, int port)
 {
 	writel(FIELD_PREP(SOCE_PORTS_SELECTOR_PORT_MASK, port),
@@ -204,6 +414,11 @@ static void soce_port_disable(struct dsa_switch *ds, int port)
 static int soce_setup(struct dsa_switch *ds)
 {
 	struct soce_priv *priv = ds->priv;
+	int ret;
+
+	ret = soce_vlan_setup(ds);
+	if (ret)
+		return ret;
 
 	soce_sw_enable_tagging(&priv->local);
 
@@ -213,8 +428,12 @@ static int soce_setup(struct dsa_switch *ds)
 static void soce_teardown(struct dsa_switch *ds)
 {
 	struct soce_priv *priv = ds->priv;
+	struct soce_dsa_local *local;
+
+	local = &priv->local;
 
-	soce_sw_disable_tagging(&priv->local);
+	soce_sw_disable_tagging(local);
+	soce_vlan_teardown(priv);
 }
 
 static enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds,
@@ -224,6 +443,130 @@ static enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds,
 	return DSA_TAG_PROTO_SDSA;
 }
 
+static int soce_port_vlan_add(struct dsa_switch *ds, int port,
+			      const struct switchdev_obj_port_vlan *vlan,
+			      struct netlink_ext_ack *extack)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct soce_priv *priv = ds->priv;
+	u32 port_mask = BIT(port);
+	u32 *untagged_ports;
+	u32 old_untagged;
+	u32 old_members;
+	bool untagged;
+	u32 *members;
+	int ret;
+
+	untagged_ports = priv->vlan_untagged;
+	members = priv->vlan_members;
+
+	if (!priv->features.port_vlan) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Port VLAN support is not implemented in the switch core");
+		return -EOPNOTSUPP;
+	}
+
+	if (!vlan->vid)
+		return 0;
+
+	untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+
+	scoped_guard(mutex, &priv->vlan_lock) {
+		old_members = members[vlan->vid];
+		old_untagged = untagged_ports[vlan->vid];
+
+		members[vlan->vid] |= port_mask;
+		if (untagged)
+			untagged_ports[vlan->vid] |= port_mask;
+		else
+			untagged_ports[vlan->vid] &= ~port_mask;
+
+		ret = soce_vlan_write_entry(priv, vlan->vid);
+		if (ret) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "failed to update VLAN hardware tables");
+			dev_err(ds->dev,
+				"failed to add VLAN %u on port %d: %d\n",
+				vlan->vid, port, ret);
+			members[vlan->vid] = old_members;
+			untagged_ports[vlan->vid] = old_untagged;
+			return ret;
+		}
+
+		if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+			priv->port_pvid[port] = vlan->vid;
+			soce_vlan_config_port(priv, port,
+					      dsa_port_is_vlan_filtering(dp));
+		}
+	}
+
+	return ret;
+}
+
+static int soce_port_vlan_del(struct dsa_switch *ds, int port,
+			      const struct switchdev_obj_port_vlan *vlan)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct soce_priv *priv = ds->priv;
+	u32 port_mask = BIT(port);
+	u32 *untagged_ports;
+	u32 old_untagged;
+	u32 old_members;
+	u32 *members;
+	int ret;
+
+	untagged_ports = priv->vlan_untagged;
+	members = priv->vlan_members;
+
+	if (!priv->features.port_vlan)
+		return -EOPNOTSUPP;
+
+	if (!vlan->vid)
+		return 0;
+
+	scoped_guard(mutex, &priv->vlan_lock) {
+		old_members = members[vlan->vid];
+		old_untagged = untagged_ports[vlan->vid];
+		members[vlan->vid] &= ~port_mask;
+		untagged_ports[vlan->vid] &= ~port_mask;
+		ret = soce_vlan_write_entry(priv, vlan->vid);
+		if (ret) {
+			dev_err(ds->dev,
+				"failed to delete VLAN %u from port %d: %d\n",
+				vlan->vid, port, ret);
+			members[vlan->vid] = old_members;
+			untagged_ports[vlan->vid] = old_untagged;
+			return ret;
+		}
+
+		if (priv->port_pvid[port] == vlan->vid) {
+			priv->port_pvid[port] = 0;
+			soce_vlan_config_port(priv, port,
+					      dsa_port_is_vlan_filtering(dp));
+		}
+	}
+
+	return ret;
+}
+
+static int soce_port_vlan_filtering(struct dsa_switch *ds, int port,
+				    bool vlan_filtering,
+				    struct netlink_ext_ack *extack)
+{
+	struct soce_priv *priv = ds->priv;
+
+	if (!priv->features.port_vlan) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Port VLAN support is not implemented in the switch core");
+		return -EOPNOTSUPP;
+	}
+
+	scoped_guard(mutex, &priv->vlan_lock)
+		soce_vlan_config_port(priv, port, vlan_filtering);
+
+	return 0;
+}
+
 static const struct dsa_switch_ops soce_switch_ops = {
 	.get_tag_protocol	= soce_get_tag_protocol,
 	.setup			= soce_setup,
@@ -231,6 +574,9 @@ static const struct dsa_switch_ops soce_switch_ops = {
 	.phylink_get_caps	= soce_phylink_get_caps,
 	.port_enable		= soce_port_enable,
 	.port_disable		= soce_port_disable,
+	.port_vlan_filtering	= soce_port_vlan_filtering,
+	.port_vlan_add		= soce_port_vlan_add,
+	.port_vlan_del		= soce_port_vlan_del,
 };
 
 static int soce_sw_probe(struct platform_device *pdev)
@@ -241,7 +587,6 @@ static int soce_sw_probe(struct platform_device *pdev)
 	struct dsa_switch *ds;
 	u8 hw_subversion;
 	u16 hw_revision;
-	u32 hw_numports;
 	u8 hw_version;
 	int ret;
 
@@ -261,7 +606,7 @@ static int soce_sw_probe(struct platform_device *pdev)
 	soce_sw_read_core_version(local, &hw_version, &hw_subversion,
 				  &hw_revision);
 
-	ret = soce_sw_detect_features(local, &hw_numports);
+	ret = soce_sw_detect_features(local, &priv->features);
 	if (ret) {
 		if (ret == -ENODEV)
 			dev_err(dev, "switch core does not implement DSA\n");
@@ -278,8 +623,35 @@ static int soce_sw_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	if (priv->features.port_vlan) {
+		ret = devm_mutex_init(dev, &priv->vlan_lock);
+		if (ret)
+			return ret;
+
+		priv->vlan_members =
+			devm_kcalloc(dev, VLAN_N_VID,
+				     sizeof(*priv->vlan_members), GFP_KERNEL);
+		if (!priv->vlan_members)
+			return -ENOMEM;
+
+		priv->vlan_untagged =
+			devm_kcalloc(dev, VLAN_N_VID,
+				     sizeof(*priv->vlan_untagged), GFP_KERNEL);
+		if (!priv->vlan_untagged)
+			return -ENOMEM;
+	} else {
+		dev_warn(dev,
+			 "VLAN-tagged frames received on the CPU port are unsupported\n");
+	}
+
 	ds->ops = &soce_switch_ops;
-	ds->num_ports = hw_numports;
+	ds->num_ports = priv->features.num_ports;
+
+	/* Force VLAN uppers always through the callbacks, so cores without
+	 * Port VLAN feature can reject them instead of silently dropping
+	 * VLAN frames.
+	 */
+	ds->needs_standalone_vlan_filtering = true;
 	ret = devm_of_platform_populate(dev);
 	if (ret)
 		return dev_err_probe(dev, ret,
@@ -294,7 +666,9 @@ static int soce_sw_probe(struct platform_device *pdev)
 
 	dev_info(dev,
 		 "probed soce switch core version %02x.%02x.%04x with %u ports\n",
-		 hw_version, hw_subversion, hw_revision, hw_numports);
+		 hw_version, hw_subversion, hw_revision,
+		 priv->features.num_ports);
+
 	return 0;
 }
 

-- 
2.39.5


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

* [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP
  2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
                   ` (6 preceding siblings ...)
  2026-09-23 10:39 ` [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support Vasilij Strassheim
@ 2026-09-23 10:39 ` Vasilij Strassheim
  2026-09-25 23:24   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  7 siblings, 2 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-23 10:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit
  Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger, Vasilij Strassheim

The driver does not implement hardware STP offloading yet. Switch cores
synthesized with the STP feature enabled may therefore retain their
reset configuration and block frame forwarding.

Detect the synthesized STP feature and disable it during switch setup,
leaving STP state handling to the Linux bridge.

Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
 drivers/net/dsa/soce/soce_dsa.h      |  1 +
 drivers/net/dsa/soce/soce_dsa_core.c | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h
index 41533a89d680..bb9e65567ec6 100644
--- a/drivers/net/dsa/soce/soce_dsa.h
+++ b/drivers/net/dsa/soce/soce_dsa.h
@@ -22,6 +22,7 @@ struct soce_dsa_local {
 struct soce_features {
 	u32 num_ports;
 	bool port_vlan;
+	bool stp;
 };
 
 struct soce_priv {
diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
index f93ce9da8400..3b9fde7a547e 100644
--- a/drivers/net/dsa/soce/soce_dsa_core.c
+++ b/drivers/net/dsa/soce/soce_dsa_core.c
@@ -36,6 +36,7 @@
 #define SOCE_IMPL_FEATURES0_OFFSET		0x000c
 #define SOCE_IMPL_FEATURES0_NUM_PORTS_MASK	GENMASK(31, 27)
 #define SOCE_IMPL_FEATURES0_PORT_VLAN		BIT(9)
+#define SOCE_IMPL_FEATURES0_STP			BIT(20)
 #define SOCE_IMPL_FEATURES0_DSA			BIT(23)
 
 #define SOCE_DSA_REGS_BASE			0x1200
@@ -51,6 +52,10 @@
 #define SOCE_PORTS_CTRL_INGR_EN			BIT(0)
 #define SOCE_PORTS_CTRL_EGR_EN			BIT(1)
 
+#define SOCE_STP_REGS_BASE			0x0f00
+#define SOCE_STP_CTRL_OFFSET			SOCE_STP_REGS_BASE
+#define SOCE_STP_CTRL_ENABLE			BIT(0)
+
 #define SOCE_VLAN_REGS_BASE			0x0d00
 #define SOCE_VLAN_CTRL_OFFSET			SOCE_VLAN_REGS_BASE
 #define SOCE_VLAN_CTRL_ENABLE			BIT(0)
@@ -160,6 +165,7 @@ static int soce_sw_detect_features(struct soce_dsa_local *local,
 		return -ENODEV;
 
 	features->port_vlan = regval & SOCE_IMPL_FEATURES0_PORT_VLAN;
+	features->stp = regval & SOCE_IMPL_FEATURES0_STP;
 
 	implemented_numports =
 		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
@@ -200,6 +206,16 @@ static void soce_sw_disable_tagging(struct soce_dsa_local *local)
 	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
 }
 
+static void soce_stp_disable(struct soce_dsa_local *local)
+{
+	void __iomem *base = local->base_addr;
+	u32 regval;
+
+	regval = readl(base + SOCE_STP_CTRL_OFFSET);
+	regval &= ~SOCE_STP_CTRL_ENABLE;
+	writel(regval, base + SOCE_STP_CTRL_OFFSET);
+}
+
 static void soce_vlan_set_enabled(struct soce_dsa_local *local, bool enabled)
 {
 	void __iomem *base = local->base_addr;
@@ -420,6 +436,10 @@ static int soce_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
+	/* Unconfigured hardware STP blocks frame forwarding. */
+	if (priv->features.stp)
+		soce_stp_disable(&priv->local);
+
 	soce_sw_enable_tagging(&priv->local);
 
 	return 0;

-- 
2.39.5


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

* Re: [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches
       [not found]   ` <20260924104003.A49F31F000FF@smtp.kernel.org>
@ 2026-09-25 12:46     ` Vasilij Strassheim
  0 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-25 12:46 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: robh, conor+dt, devicetree, Krzysztof Kozlowski, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Russell King, Andrew Lunn,
	Heiner Kallweit, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Thu, 2026-09-24 at 10:40 +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] net: dsa: tag_sdsa: unsupported VLAN tags left in hwaccel metadata
> --
> 
> commit c9312ab6752036eeab3076e94713c79223982016
> Author: Vasilij Strassheim <v.strassheim@linutronix.de>
> 
> net: dsa: Add tag handling for SoC-e switches
> 
> This commit adds a DSA tag driver for SoC-e switches, which use an 8-byte
> SDSA header carrying frame direction, port, and VLAN metadata. The header
> is inserted between the source MAC address and the original EtherType on
> transmit and receive.
> 
> > diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c
> > new file mode 100644
> > index 0000000000000..8cc3fa357be42
> > --- /dev/null
> > +++ b/net/dsa/tag_sdsa.c
> 
> [ ... ]
> 
> > +static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +	struct dsa_port *dp = dsa_user_to_port(dev);
> > +	struct sdsa_tag *tag;
> > +	u16 vlan_tci;
> 
> [ ... ]
> 
> > +	/* SDSA carries no TPID, so only encode 802.1Q C-tags. */
> > +	if (skb_vlan_tag_present(skb) &&
> > +	    skb->vlan_proto == htons(ETH_P_8021Q)) {
> > +		vlan_tci = skb_vlan_tag_get(skb);
> > +		__vlan_hwaccel_clear_tag(skb);
> > +		tag->frame_type_port_hi |= SDSA_TAG_VLAN_BIT;
> > +		tag->vlan = cpu_to_be16(vlan_tci);
> > +	} else {
> > +		tag->vlan = 0;
> > +	}
> 
> [Severity: High]
> Does this else branch leave unsupported VLAN tags (like 802.1ad) in the 
> skb's hardware accelerated metadata?
> 
> If a user configures an unsupported VLAN protocol on a DSA user port, the 
> skb will still have skb_vlan_tag_present(skb) set when reaching sdsa_xmit().
> Because this branch does not clear the tag via __vlan_hwaccel_clear_tag(skb),
> the conduit driver's MAC hardware might subsequently insert the remaining 
> VLAN tag at the MAC boundary.
> 
> Could this insertion displace the 8-byte SDSA header just pushed, causing 
> the switch hardware to receive unrecognized frames and drop them?
> 

That's right, I missed it. I will fix it in a similar way to tag_ks8995.

> > +
> > +	return skb;
> > +}
> 


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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-23 10:39 ` [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller Vasilij Strassheim
@ 2026-09-25 22:55   ` Andrew Lunn
  2026-09-30 14:00     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 22:55 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> The controller exposes separate register regions for transaction data
> and for the shared transaction control and external bus selector
> register.

> +examples:
> +  - |
> +    mdio@204 {
> +        compatible = "soce,swip-mdio-23-02";
> +        reg = <0x204 0xc>, <0x200 0x4>;

At least in the example, they are not separate?

   Andrew

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

* Re: [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch
  2026-09-23 10:39 ` [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch Vasilij Strassheim
@ 2026-09-25 23:05   ` Andrew Lunn
  2026-09-30 17:16     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:05 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> +  mdio@204:
> +    $ref: /schemas/net/soce,swip-mdio.yaml#
> +    unevaluatedProperties: false
> +    description:
> +      Integrated MDIO controller bus on the master side of the mux.
> +
> +  mdio-mux@202:
> +    $ref: /schemas/net/mdio-mux-mmioreg.yaml#
> +    unevaluatedProperties: false

> +        mdio_parent: mdio@204 {
> +            compatible = "soce,swip-mdio-23-02";
> +            reg = <0x204 0xc>, <0x200 0x4>;
> +            reg-names = "data", "control";
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +        };
> +
> +        mdio-mux@202 {
> +            compatible = "mdio-mux-mmioreg", "mdio-mux";
> +            reg = <0x202 0x2>;

So these two overlap? That is pretty unusual, so might be worth a comment somewhere.

	Andrew

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

* Re: [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver
  2026-09-23 10:39 ` [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver Vasilij Strassheim
@ 2026-09-25 23:10   ` Andrew Lunn
  2026-09-30 17:23     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:10 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
> index 048586746026..079b46dea25f 100644
> --- a/drivers/net/mdio/Makefile
> +++ b/drivers/net/mdio/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_MDIO_OCTEON)		+= mdio-octeon.o
>  obj-$(CONFIG_MDIO_PIC64HPSC)		+= mdio-pic64hpsc.o
>  obj-$(CONFIG_MDIO_REALTEK_RTL9300)	+= mdio-realtek-rtl9300.o
>  obj-$(CONFIG_MDIO_REGMAP)		+= mdio-regmap.o
> +obj-$(CONFIG_MDIO_SOCE)			+= mdio-soce.o
>  obj-$(CONFIG_MDIO_SUN4I)		+= mdio-sun4i.o

Maybe the indentation is wrong here?

Otherwise:

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
@ 2026-09-25 23:17   ` Andrew Lunn
  2026-09-30 17:26     ` Vasilij Strassheim
  2026-09-25 23:20   ` Andrew Lunn
  2026-09-27 12:28   ` netdev-bot+sashiko
  2 siblings, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:17 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> +static void soce_sw_read_core_version(struct soce_dsa_local *local,
> +				      u8 *version, u8 *subversion,
> +				      u16 *revision)
> +{
> +	u32 regval;
> +
> +	regval = readl(local->base_addr + SOCE_CORE_VERSION_OFFSET);
> +	*version = (u8)(regval >> SOCE_CORE_VERSION_VERSION_SHIFT);
> +	*subversion = (u8)(regval >> SOCE_CORE_VERSION_SUBVERSION_SHIFT);
> +	*revision = (u16)regval;

FIELD_GET() would make this more readable. 

> +static int soce_sw_detect_features(struct soce_dsa_local *local,
> +				   u32 *numports)
> +{
> +	void __iomem *base = local->base_addr;
> +	u32 implemented_numports;
> +	u32 licensed_numports;
> +	u32 regval;
> +
> +	regval = readl(base + SOCE_LIC_FEATURES_OFFSET);
> +	licensed_numports = FIELD_GET(SOCE_LIC_FEATURES_NUM_PORTS_MASK, regval);
> +	if (!licensed_numports || licensed_numports > SOCE_MAX_NUM_PORTS)
> +		return -EINVAL;
> +
> +	regval = readl(base + SOCE_IMPL_FEATURES0_OFFSET);
> +	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
> +		return -ENODEV;
> +
> +	implemented_numports =
> +		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
> +	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
> +	    implemented_numports > licensed_numports)
> +		return -EINVAL;

Maybe add dev_err() here for all these error cases. It will help
somebody debug why there switch fails to probe.

	 Andrew

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
  2026-09-25 23:17   ` Andrew Lunn
@ 2026-09-25 23:20   ` Andrew Lunn
  2026-09-30 18:15     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  2 siblings, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:20 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 23, 2026 at 12:39:33PM +0200, Vasilij Strassheim wrote:
> Add a DSA driver for SoC-e FPGA-based Ethernet switch IP cores.
> 
> Read the core version and synthesis-time feature registers during probe.
> Require DSA support and between 3 and 31 implemented ports, without
> exceeding the licensed port count. Derive each port's phylink
> capabilities from its phy-mode.
> 
> Enable "DSA custom rules" tagging for all frames during setup. This
> directs ingress traffic from user ports to the CPU port while standalone
> ports remain isolated. Enable and disable port ingress and egress
> through the DSA port callbacks, and disable tagging again during
> teardown so that unbinding the driver does not leave its managed
> configuration active.
> 
> Populate the integrated MDIO controller and mux child devices used for
> external PHY access.
> 
> Tested with a SoC-e MRS 25.01 IP core on a Xilinx ZynqMP platform.
> 
> Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
> ---
>  drivers/net/dsa/Kconfig              |   2 +
>  drivers/net/dsa/Makefile             |   1 +
>  drivers/net/dsa/soce/Kconfig         |  14 ++
>  drivers/net/dsa/soce/Makefile        |   4 +
>  drivers/net/dsa/soce/soce_dsa.h      |  26 +++
>  drivers/net/dsa/soce/soce_dsa_core.c | 343 +++++++++++++++++++++++++++++++++++
>  6 files changed, 390 insertions(+)
> 
> diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> index fe8cd5338fda..879fbede83f0 100644
> --- a/drivers/net/dsa/Kconfig
> +++ b/drivers/net/dsa/Kconfig
> @@ -94,6 +94,8 @@ source "drivers/net/dsa/ocelot/Kconfig"
>  
>  source "drivers/net/dsa/qca/Kconfig"
>  
> +source "drivers/net/dsa/soce/Kconfig"
> +
>  source "drivers/net/dsa/sja1105/Kconfig"
>  
>  source "drivers/net/dsa/xrs700x/Kconfig"
> diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> index 7e637d56b35c..97de3d181440 100644
> --- a/drivers/net/dsa/Makefile
> +++ b/drivers/net/dsa/Makefile
> @@ -26,4 +26,5 @@ obj-y				+= ocelot/
>  obj-y				+= qca/
>  obj-y				+= realtek/
>  obj-y				+= sja1105/
> +obj-y				+= soce/
>  obj-y				+= xrs700x/
> diff --git a/drivers/net/dsa/soce/Kconfig b/drivers/net/dsa/soce/Kconfig
> new file mode 100644
> index 000000000000..c31b7c5af695
> --- /dev/null
> +++ b/drivers/net/dsa/soce/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config NET_DSA_SOCE
> +	tristate "SoC-e switches"
> +	depends on NET_DSA
> +	depends on OF
> +	depends on HAS_IOMEM
> +	select MDIO_BUS_MUX_MMIOREG
> +	select MDIO_SOCE
> +	select NET_DSA_TAG_SDSA
> +	help
> +	  This enables support for switches based on SoC-e IP cores.
> +	  Frames are exchanged with the CPU port using the SDSA DSA tag protocol.
> +	  The driver supports switch variants whose features and number of ports
> +	  are selected at synthesis time and detected at runtime.
> diff --git a/drivers/net/dsa/soce/Makefile b/drivers/net/dsa/soce/Makefile
> new file mode 100644
> index 000000000000..2a6d95ef663f
> --- /dev/null
> +++ b/drivers/net/dsa/soce/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_NET_DSA_SOCE) += soce_dsa.o
> +soce_dsa-objs := soce_dsa_core.o
> diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h
> new file mode 100644
> index 000000000000..2acd4dbaf958
> --- /dev/null
> +++ b/drivers/net/dsa/soce/soce_dsa.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2020-2026 System on Chip engineering, S.L.
> + * Copyright (c) 2026 Linutronix GmbH
> + * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
> + */
> +
> +#ifndef __SOCE_DSA_H
> +#define __SOCE_DSA_H
> +
> +#include <linux/types.h>
> +
> +#include <net/dsa.h>
> +
> +#define SOCE_MAX_NUM_PORTS 31
> +
> +struct soce_dsa_local {
> +	void __iomem *base_addr;
> +};
> +
> +struct soce_priv {
> +	struct soce_dsa_local local;
> +	struct dsa_switch ds;
> +};
> +
> +#endif /* __SOCE_DSA_H */
> diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
> new file mode 100644
> index 000000000000..d391b11b94ad
> --- /dev/null
> +++ b/drivers/net/dsa/soce/soce_dsa_core.c
> @@ -0,0 +1,343 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2020-2026 System on Chip engineering, S.L.
> + * Copyright (c) 2026 Linutronix GmbH
> + * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/of_net.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy.h>
> +#include <linux/phylink.h>
> +#include <linux/platform_device.h>
> +
> +#include <net/dsa.h>
> +
> +#include "soce_dsa.h"
> +
> +#define SOCE_MIN_NUM_PORTS			3
> +
> +#define SOCE_CORE_VERSION_OFFSET		0x0000
> +#define SOCE_CORE_VERSION_VERSION_SHIFT		24
> +#define SOCE_CORE_VERSION_SUBVERSION_SHIFT	16
> +#define SOCE_MIN_CORE_VERSION			0x24
> +#define SOCE_MIN_CORE_SUBVERSION		0x01
> +
> +#define SOCE_LIC_FEATURES_OFFSET		0x0004
> +#define SOCE_LIC_FEATURES_NUM_PORTS_MASK	GENMASK(31, 27)
> +
> +#define SOCE_IMPL_FEATURES0_OFFSET		0x000c
> +#define SOCE_IMPL_FEATURES0_NUM_PORTS_MASK	GENMASK(31, 27)
> +#define SOCE_IMPL_FEATURES0_PORT_VLAN		BIT(9)
> +#define SOCE_IMPL_FEATURES0_DSA			BIT(23)
> +
> +#define SOCE_DSA_REGS_BASE			0x1200
> +#define SOCE_TAG_ALL_FRAMES_CTRL_OFFSET		(SOCE_DSA_REGS_BASE + 0x001c)
> +#define SOCE_TAG_ALL_FRAMES_CTRL_ENABLE		BIT(0)
> +#define SOCE_CUSTOM_RULES_TAGGING_OFFSET	(SOCE_DSA_REGS_BASE + 0x0020)
> +#define SOCE_CUSTOM_RULES_TAGGING_ENABLE	BIT(0)
> +
> +#define SOCE_PORTS_REGS_BASE			0x3000
> +#define SOCE_PORTS_SELECTOR_OFFSET		SOCE_PORTS_REGS_BASE
> +#define SOCE_PORTS_SELECTOR_PORT_MASK		GENMASK(7, 0)
> +#define SOCE_PORTS_CTRL_OFFSET			(SOCE_PORTS_REGS_BASE + 0x0004)
> +#define SOCE_PORTS_CTRL_INGR_EN			BIT(0)
> +#define SOCE_PORTS_CTRL_EGR_EN			BIT(1)
> +
> +static void soce_phylink_get_caps(struct dsa_switch *ds, int port,
> +				  struct phylink_config *config)
> +{
> +	struct dsa_port *dp = dsa_to_port(ds, port);
> +	phy_interface_t mode;
> +	int ret;
> +
> +	ret = of_get_phy_mode(dp->dn, &mode);
> +	if (ret)
> +		return;
> +
> +	if (phy_interface_mode_is_rgmii(mode))
> +		phy_interface_set_rgmii(config->supported_interfaces);
> +	else
> +		__set_bit(mode, config->supported_interfaces);
> +
> +	config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
> +
> +	switch (mode) {
> +	case PHY_INTERFACE_MODE_MII:
> +		config->mac_capabilities |= MAC_10 | MAC_100;
> +		break;
> +	case PHY_INTERFACE_MODE_GMII:
> +		config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000;
> +		break;
> +	case PHY_INTERFACE_MODE_RMII:
> +		config->mac_capabilities |= MAC_10FD | MAC_100FD;
> +		break;
> +	default:
> +		if (phy_interface_mode_is_rgmii(mode))
> +			config->mac_capabilities |= MAC_10FD | MAC_100FD |
> +						    MAC_1000FD;
> +		break;
> +	}
> +}
> +
> +static int soce_sw_validate_core_version(u8 version, u8 subversion)
> +{
> +	if (version < SOCE_MIN_CORE_VERSION ||
> +	    (version == SOCE_MIN_CORE_VERSION &&
> +	     subversion < SOCE_MIN_CORE_SUBVERSION))
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static void soce_sw_read_core_version(struct soce_dsa_local *local,
> +				      u8 *version, u8 *subversion,
> +				      u16 *revision)
> +{
> +	u32 regval;
> +
> +	regval = readl(local->base_addr + SOCE_CORE_VERSION_OFFSET);
> +	*version = (u8)(regval >> SOCE_CORE_VERSION_VERSION_SHIFT);
> +	*subversion = (u8)(regval >> SOCE_CORE_VERSION_SUBVERSION_SHIFT);
> +	*revision = (u16)regval;
> +}
> +
> +static int soce_sw_detect_features(struct soce_dsa_local *local,
> +				   u32 *numports)
> +{
> +	void __iomem *base = local->base_addr;
> +	u32 implemented_numports;
> +	u32 licensed_numports;
> +	u32 regval;
> +
> +	regval = readl(base + SOCE_LIC_FEATURES_OFFSET);
> +	licensed_numports = FIELD_GET(SOCE_LIC_FEATURES_NUM_PORTS_MASK, regval);
> +	if (!licensed_numports || licensed_numports > SOCE_MAX_NUM_PORTS)
> +		return -EINVAL;
> +
> +	regval = readl(base + SOCE_IMPL_FEATURES0_OFFSET);
> +	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
> +		return -ENODEV;
> +
> +	implemented_numports =
> +		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
> +	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
> +	    implemented_numports > licensed_numports)
> +		return -EINVAL;
> +
> +	*numports = implemented_numports;
> +
> +	return 0;
> +}
> +
> +static void soce_sw_enable_tagging(struct soce_dsa_local *local)
> +{
> +	void __iomem *base = local->base_addr;
> +	u32 regval;
> +
> +	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> +	regval |= SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
> +	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> +
> +	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> +	regval |= SOCE_CUSTOM_RULES_TAGGING_ENABLE;
> +	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> +}
> +
> +static void soce_sw_disable_tagging(struct soce_dsa_local *local)
> +{
> +	void __iomem *base = local->base_addr;
> +	u32 regval;
> +
> +	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> +	regval &= ~SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
> +	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> +
> +	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> +	regval &= ~SOCE_CUSTOM_RULES_TAGGING_ENABLE;
> +	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> +}
> +
> +static void soce_port_select(struct soce_dsa_local *local, int port)
> +{
> +	writel(FIELD_PREP(SOCE_PORTS_SELECTOR_PORT_MASK, port),
> +	       local->base_addr + SOCE_PORTS_SELECTOR_OFFSET);
> +}
> +
> +static void soce_port_set_enabled(struct soce_dsa_local *local, int port,
> +				  bool enabled)
> +{
> +	void __iomem *base = local->base_addr;
> +	u32 regval;
> +
> +	soce_port_select(local, port);
> +
> +	regval = readl(base + SOCE_PORTS_CTRL_OFFSET);
> +	if (enabled)
> +		regval |= SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN;
> +	else
> +		regval &= ~(SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN);
> +	writel(regval, base + SOCE_PORTS_CTRL_OFFSET);
> +}
> +
> +static int soce_port_enable(struct dsa_switch *ds, int port,
> +			    struct phy_device *phy)
> +{
> +	struct soce_priv *priv = ds->priv;
> +
> +	soce_port_set_enabled(&priv->local, port, true);
> +
> +	return 0;
> +}
> +
> +static void soce_port_disable(struct dsa_switch *ds, int port)
> +{
> +	struct soce_priv *priv = ds->priv;
> +
> +	soce_port_set_enabled(&priv->local, port, false);
> +}
> +
> +static int soce_setup(struct dsa_switch *ds)
> +{
> +	struct soce_priv *priv = ds->priv;
> +
> +	soce_sw_enable_tagging(&priv->local);

Once all the setup is finished, what is the state of the switch?

What we want is that the user ports are isolated from each other, and
can only exchange frames with the CPU. That makes the hardware
basically a port expander, and you do bridging in software. Later
patches can then add offload of bridging, and whatever else the
hardware can do, which Linux can also do in software.

	Andrew

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

* Re: [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP
  2026-09-23 10:39 ` [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP Vasilij Strassheim
@ 2026-09-25 23:24   ` Andrew Lunn
  2026-09-30 18:29     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:24 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 23, 2026 at 12:39:35PM +0200, Vasilij Strassheim wrote:
> The driver does not implement hardware STP offloading yet.

What exactly do you mean by STP offload? Can it do the actual
protocol? No DSA switch can do that, we always use the software
implementation. I'm not even sure it is possible to offload the actual
protocol.

	Andrew

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

* Re: [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support
  2026-09-23 10:39 ` [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support Vasilij Strassheim
@ 2026-09-25 23:32   ` Andrew Lunn
  2026-09-30 18:32     ` Vasilij Strassheim
  2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-25 23:32 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> The port and VID selectors are shared by all VLAN operations, so
> serialize selector and data register sequences with a dedicated mutex.
> Track member and untagged masks in software and restore the shadow state
> if programming fails.

Looking at other features of the switch, how many different mutex are
going to be needed? Is it better to have a single mutex which protects
everything?

	Andrew

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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-23 10:39 ` [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller Vasilij Strassheim
  2026-09-25 22:55   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] In soce,swip-mdio.yaml the MDIO controller owns the whole
  32-bit 'control' register (<0x200 0x4>), which the binding calls the…
- [Low] The only compatible is 'soce,swip-mdio-23-02', and neither the
  binding nor the commit message explains the '23-02' suffix.

--

dt-bindings: net: Add SoC-e SWIP MDIO controller

This adds a devicetree binding for the MDIO controller built into SoC-e
SWIP Ethernet switch IP cores. The controller has a data register region
and a shared transaction control and bus selector register. It is the
parent bus for an MDIO mux that reaches the external MDIO interfaces.

> diff --git a/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml b/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml
> new file mode 100644
> index 0000000000000..3cf4bde0508ab
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/soce,swip-mdio.yaml
> @@ -0,0 +1,47 @@

[ ... ]

> +description:
> +  MDIO controller integrated into SoC-e Ethernet switch FPGA IP cores.
> +  The controller accesses multiple external MDIO buses through a mux.
> +
> +$ref: mdio.yaml#
> +
> +properties:
> +  compatible:
> +    const: soce,swip-mdio-23-02

[Severity: Low]
What does the 23-02 suffix mean? Neither the binding nor the commit
message explains it. It looks like a YY-MM IP release tag. The DSA
driver commit in this series mentions testing an MRS 25.01 core.

This MDIO block is part of the larger SWIP switch. It sits at
0x200-0x20f inside the switch register window.
Documentation/devicetree/bindings/writing-bindings.rst says:

  For sub-blocks/components of bigger device (e.g. SoC blocks) use rather
  device-based compatible (e.g. SoC-based compatible), instead of custom
  versioning of that component.

Could the version scheme be documented, or could a device-based
compatible be used instead? As it stands, it isn't clear whether each
future IP release would need a new compatible.

> +
> +  reg:
> +    items:
> +      - description: MDIO parameter, write data, and read data registers
> +      - description: Shared MDIO transaction control and bus selection register

[Severity: Medium]
Is this the right way to describe the bus selector? This node claims the
whole 32-bit control register at <0x200 0x4>, bus selector field
included. The binding gives the mux no way to reach that field. There is
no #mux-control-cells and no mux child node, even though the description
says the buses are reached through a mux.

Later in the series, the example in "dt-bindings: net: dsa: Add SoC-e
SWIP switch" covers this with a sibling mdio-mux-mmioreg node:

Documentation/devicetree/bindings/net/dsa/soce,swip.yaml:
    mdio-mux@202 {
        ...
        reg = <0x202 0x2>;
        ...

That range is the upper halfword of the register that mdio@204 already
claims as control. The driver commit "net: mdio: Add SoC-e SWIP MDIO
controller driver" says the same thing: the selector is bits 26:16, and
a generic MMIO MDIO mux manages it through the upper 16-bit halfword.

The result is two sibling nodes with overlapping reg ranges for one
hardware register. This only works because neither driver requests its
region exclusively. soce_mdio_iomap() in drivers/net/mdio/mdio-soce.c
does:

    return devm_ioremap(dev, res.start, resource_size(&res));

mdio_mux_mmioreg_switch_fn() also does a plain ioremap() on every
switch. If either driver moved to devm_platform_ioremap_resource(),
probe would fail with -EBUSY.

Nothing fails with the current series. There is no -EBUSY today. The
mux's 16-bit read-modify-write and the 32-bit read-modify-write in
soce_mdio_start() are serialized: mdio_mux_read() and mdio_mux_write()
hold the parent bus mdio_lock around both switch_fn() and the parent
access.

The concern is the DT ABI. Once this binding is merged, moving to a
description without overlapping ranges would break compatibility. Two
options would avoid the overlap:

  - make this controller a mux provider with #mux-control-cells
  - have the controller register its child buses itself

Would it be better to choose one of these before the binding is merged?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch
  2026-09-23 10:39 ` [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch Vasilij Strassheim
  2026-09-25 23:05   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] Documentation/devicetree/bindings/net/dsa/soce,swip.yaml accepts
  the MMIO child nodes mdio@204 and mdio-mux@202 but does not require…
- [Low] In Documentation/devicetree/bindings/net/dsa/soce,swip.yaml, the
  per-port constraints (the dsa-port.yaml $ref, `reg` maximum 30,…

--

dt-bindings: net: dsa: Add SoC-e SWIP switch

This commit adds a DT binding for SoC-e FPGA-based Ethernet switch IP
cores. The compatible string encodes the layout of the identification
registers. The binding describes the Ethernet ports and the optional
integrated MDIO controller with its MMIO mux.

> diff --git a/Documentation/devicetree/bindings/net/dsa/soce,swip.yaml b/Documentation/devicetree/bindings/net/dsa/soce,swip.yaml
> new file mode 100644
> index 0000000000000..d614fb2a29d3d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/soce,swip.yaml

[ ... ]

> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 1
> +
> +  ranges: true
> +
> +  mdio@204:
> +    $ref: /schemas/net/soce,swip-mdio.yaml#
> +    unevaluatedProperties: false
> +    description:
> +      Integrated MDIO controller bus on the master side of the mux.
> +
> +  mdio-mux@202:
> +    $ref: /schemas/net/mdio-mux-mmioreg.yaml#
> +    unevaluatedProperties: false
> +
> +  ethernet-ports:
> +    type: object
> +    patternProperties:
> +      '^ethernet-port@[0-9a-f]+$':
> +        type: object
> +        $ref: dsa-port.yaml#
> +        unevaluatedProperties: false

[Severity: Low]
Should this pattern be '^(ethernet-)?port@[0-9a-f]+$'?

This schema includes ethernet-switch.yaml through dsa.yaml. That file
also accepts port@N nodes under ethernet-ports, with only a type check:

    '^(ethernet-)?port@[0-9a-f]+$':
      type: object
      description: Ethernet switch ports

As a result, a node named port@3 counts as evaluated and passes
validation. None of the checks here apply to it: the dsa-port.yaml
reference, the reg maximum, the required reg and phy-mode, and the
phy-mode enum.

dsa_switch_parse_ports_of() in net/dsa/dsa.c still uses such a node. It
walks every available child with for_each_available_child_of_node() and
does not check node names. An unvalidated phy-mode can then reach
soce_phylink_get_caps(), which only handles the listed modes.

The other DSA bindings that constrain port nodes (lantiq,gswip, mt7530,
ksz, sja1105, qca8k and others) all use '^(ethernet-)?port@[0-9a-f]+$'.

[ ... ]

> +required:
> +  - compatible
> +  - reg
> +  - ethernet-ports

[Severity: Low]
When the mdio@204 or mdio-mux@202 children are present, should ranges,
#address-cells and #size-cells be required?

As written, a switch node that has those MMIO children but no ranges
still passes dt_binding_check. The kernel then cannot translate the
children's reg:

drivers/of/address.c:__of_translate_address() {
    ...
	ranges = of_get_property(parent, rprop, &rlen);
	if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
	    strcmp(rprop, "dma-ranges")) {
		pr_debug("no ranges; cannot translate\n");
		return 1;
	}
    ...
}

So mdio_mux_mmioreg_probe() fails in of_address_to_resource() with
"could not obtain memory map". soce_mdio_iomap() in
drivers/net/mdio/mdio-soce.c fails the same way. No PHY behind the mux
can then be reached.

dtc already warns about missing cell properties, but it does not warn
about a missing ranges.

Would a dependentRequired (or dependencies) entry help here? It could
make mdio-mux@202 and mdio@204 require ranges, #address-cells and
#size-cells.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches
  2026-09-23 10:39 ` [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches Vasilij Strassheim
       [not found]   ` <20260924104003.A49F31F000FF@smtp.kernel.org>
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] sdsa_xmit() writes the SDSA header at offset 12 (skb_push +
  dsa_alloc_etype_header).
- [Low] Whether sdsa_xmit() puts the VLAN in the SDSA header on transmit
  depends on the conduit, and in-band 802.1Q tags are never handled.

--

net: dsa: Add tag handling for SoC-e switches

This adds a DSA tagging driver for SoC-e switches. The driver uses an
8-byte SDSA header with EtherType 0xdcdc, placed after the source MAC
address. It builds FROM_CPU frames on transmit and parses TO_CPU frames
on receive, and maps the optional 802.1Q TCI in the header to and from
skb VLAN hwaccel metadata.

> diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c
> new file mode 100644
> index 0000000000000..8cc3fa357be42
> --- /dev/null
> +++ b/net/dsa/tag_sdsa.c
> @@ -0,0 +1,158 @@

[ ... ]

> +static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_user_to_port(dev);
> +	struct sdsa_tag *tag;
> +	u16 vlan_tci;
> +
> +	BUILD_BUG_ON(sizeof(*tag) != SDSA_HLEN);
> +
> +	skb_push(skb, SDSA_HLEN);
> +	dsa_alloc_etype_header(skb, SDSA_HLEN);
> +
> +	tag = dsa_etype_header_pos_tx(skb);
> +	tag->ethertype = cpu_to_be16(ETH_P_SDSA);
> +	tag->reserved = 0;
> +	tag->frame_type_port_hi =
> +		FIELD_PREP(SDSA_TAG_FRAME_TYPE_MASK, SDSA_FRAME_TYPE_FROM_CPU) |
> +		FIELD_PREP(SDSA_TAG_PORT_HI_MASK,
> +			   dp->index >> SDSA_TAG_PORT_HI_SHIFT);
> +	tag->port_lo = FIELD_PREP(SDSA_TAG_PORT_LO_MASK, dp->index);
> +	/* SDSA carries no TPID, so only encode 802.1Q C-tags. */
> +	if (skb_vlan_tag_present(skb) &&
> +	    skb->vlan_proto == htons(ETH_P_8021Q)) {

[Severity: Low]
The commit message says "Encode accelerated VLAN metadata into the header
on transmit". Is the wire format meant to depend on which conduit is
used?

This branch only runs if the skb still has a hwaccel C-tag when it reaches
the tagger. The user port gets its features from the conduit:

net/dsa/user.c:dsa_user_setup_tagger() {
    ...
	user->features = conduit->vlan_features | NETIF_F_HW_TC;
    ...
}

If the conduit's vlan_features do not include NETIF_F_HW_VLAN_CTAG_TX,
validate_xmit_vlan() on the user netdev moves the tag in-band before
dsa_user_xmit() runs:

net/core/dev.c:validate_xmit_vlan() {
    ...
	if (skb_vlan_tag_present(skb) &&
	    !vlan_hw_offload_capable(features, skb->vlan_proto))
		skb = __vlan_hwaccel_push_inside(skb);
    ...
}

In that case the frame goes out with the VLAN-info bit clear, followed by
an in-band 0x8100 tag. This seems to apply to macb (Cadence GEM) on
ZynqMP, which has no HW_VLAN features. It also applies to most other
conduits, for example mlx4, nfp, bnxt, otx2, wangxun, and mtk_eth_soc,
which masks out CTAG_TX explicitly.

With stmmac (dma_cap.vlins) or bcmsysport, whose vlan_features do include
CTAG_TX, the same frame goes out with the TCI in the SDSA header instead.

Should sdsa_xmit() also handle in-band 802.1Q tags, so that the format is
the same on every conduit? Or could the commit message describe this
behaviour?

> +		vlan_tci = skb_vlan_tag_get(skb);
> +		__vlan_hwaccel_clear_tag(skb);
> +		tag->frame_type_port_hi |= SDSA_TAG_VLAN_BIT;
> +		tag->vlan = cpu_to_be16(vlan_tci);
> +	} else {
> +		tag->vlan = 0;
> +	}

[Severity: Medium]
What happens to an accelerated tag whose vlan_proto is not ETH_P_8021Q?
One example is an 802.1ad S-tag from an 802.1ad upper or from tc vlan
push.

Such a tag takes the else branch and stays in skb->vlan_all, but the SDSA
header is already at offset 12. When the skb is then sent on the conduit,
the S-tag can be inserted at ETH_HLEN, in front of the 0xDCDC EtherType.
The insertion is done either by the conduit hardware
(NETIF_F_HW_VLAN_STAG_TX) or by:

validate_xmit_vlan()
  __vlan_hwaccel_push_inside()
    __vlan_insert_tag()

The switch would then see 0x88a8 where it expects the SDSA header, and
would not recognise the FROM_CPU destination port. Depending on the
hardware, the frame could be dropped, or it could be forwarded like
ordinary CPU-port ingress.

For this to happen, the user netdev needs NETIF_F_HW_VLAN_STAG_TX, which
it can only get through conduit->vlan_features. No in-tree conduit seems
to set that today, but nothing in the tagger rules it out.

Other taggers guard against this case. ks8995_xmit() in
net/dsa/tag_ks8995.c does:

	if (unlikely(skb_vlan_tag_present(skb))) {
		skb = __vlan_hwaccel_push_inside(skb);

so that the conduit cannot insert the tag outside the special tag.
tag_sja1105.c does something similar.

Could any leftover hwaccel tag be pushed in-band with
__vlan_hwaccel_push_inside() before the SDSA header is inserted, with the
NULL return handled?

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver
  2026-09-23 10:39 ` [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver Vasilij Strassheim
  2026-09-25 23:10   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] soce_mdio_iomap() (drivers/net/mdio/mdio-soce.c:48) returns
  whatever devm_ioremap() returns.
- [Medium] The external MDIO bus selector (bits 26:16) lives in the same
  32-bit control register as OPSTATUS (bit 0), but the idle check does…

--

net: mdio: Add SoC-e SWIP MDIO controller driver

This adds a driver for the MDIO controller in SoC-e Ethernet switch IP
cores. It supports Clause 22 and Clause 45 accesses. The transaction
control register also holds an external MDIO bus selector in bits 26:16,
which a generic MMIO MDIO mux manages and which the driver preserves when
it starts a transaction.

> diff --git a/drivers/net/mdio/mdio-soce.c b/drivers/net/mdio/mdio-soce.c
> new file mode 100644
> index 0000000000000..59c0b3da483eb
> --- /dev/null
> +++ b/drivers/net/mdio/mdio-soce.c
> @@ -0,0 +1,239 @@

[ ... ]

> +static void __iomem *soce_mdio_iomap(struct device *dev, int index)
> +{
> +	struct resource res;
> +	int ret;
> +
> +	ret = of_address_to_resource(dev->of_node, index, &res);
> +	if (ret)
> +		return IOMEM_ERR_PTR(ret);
> +
> +	return devm_ioremap(dev, res.start, resource_size(&res));

[Severity: Medium]
Can a failed mapping get past the checks in soce_mdio_probe()?

devm_ioremap() returns NULL on failure, not an ERR_PTR. This happens both
when the devres allocation fails and when ioremap() fails:

lib/devres.c:__devm_ioremap() {
	void __iomem **ptr, *addr = NULL;
	...
	if (!ptr)
		return NULL;
	...
	} else
		devres_free(ptr);

	return addr;
}

soce_mdio_probe() checks both results only with IS_ERR():

	priv->data = soce_mdio_iomap(dev, SOCE_MDIO_DATA_IOMAP_IDX);
	if (IS_ERR(priv->data))
		return PTR_ERR(priv->data);

	priv->ctrl = soce_mdio_iomap(dev, SOCE_MDIO_CTRL_IOMAP_IDX);
	if (IS_ERR(priv->ctrl))
		return PTR_ERR(priv->ctrl);

IS_ERR(NULL) is false, so probe would continue into
devm_of_mdiobus_register() with a NULL base.

The first bus access would then dereference NULL. That access comes either
from a PHY child during registration or through the mux:

mdio_mux_read()
  soce_mdio_read()
    soce_mdio_wait_for_idle()
      readl_poll_timeout(NULL, ...)

Could soce_mdio_iomap() convert a NULL result into IOMEM_ERR_PTR(-ENOMEM),
or could probe use IS_ERR_OR_NULL()? A helper that also requests the region
doesn't fit here, because the parent switch driver already requests the
whole range.

> +}
> +
> +static int soce_mdio_wait_for_idle(struct soce_mdio *priv)
> +{
> +	void __iomem *ctrl = priv->ctrl;
> +	u32 val;
> +
> +	return readl_poll_timeout(ctrl, val,
> +		!(val & SOCE_MDIO_CTRL_OPSTATUS), 10,
> +		SOCE_MDIO_TIMEOUT_US);
> +}
> +
> +static void soce_mdio_start(struct soce_mdio *priv, u32 command)
> +{
> +	void __iomem *ctrl = priv->ctrl;
> +
> +	/* Keep the currently selected MDIO bus while updating op bits. */
> +	command |= readl(ctrl) & SOCE_MDIO_CTRL_BUS_MASK;
> +	writel(command, ctrl);
> +}
> +
> +static int soce_mdio_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> +	struct soce_mdio *priv = bus->priv;
> +	void __iomem *data = priv->data;
> +	u32 command;
> +	int ret;
> +
> +	ret = soce_mdio_wait_for_idle(priv);
> +	if (ret)
> +		return ret;

[Severity: Medium]
Does this idle check also protect the bus selector bits in the same
register?

The commit message says:

    Return a timeout instead of waiting indefinitely, and allow a later
    access to retry if the hardware becomes idle again.

So when a completion wait returns -ETIMEDOUT, the engine may still be busy.
Nothing aborts or resets it before mdio_lock is released, and there is no
mii_bus->reset callback.

If the next access goes through the mux to a different child bus,
mdio_mux_read() calls switch_fn before it calls the parent's read callback:

drivers/net/mdio/mdio-mux.c:mdio_mux_read() {
	...
	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
	...
	r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
	...
}

mdio_mux_mmioreg_switch_fn() then rewrites the selector halfword without
checking OPSTATUS:

			x = ioread16(p);
			y = (x & ~s->mask) | desired_child;
			if (x != y) {
				iowrite16((x & ~s->mask) | desired_child, p);

This means soce_mdio_wait_for_idle() runs only after bits 26:16 have
already changed.

If the hardware doesn't latch the selector when a transaction starts, could
the rest of the timed-out frame be sent to a different external MDIO bus?
The original PHY would get a truncated frame and the newly selected bus
would get a fragment from the middle of it. Neither the mux's current_child
nor the return code would show that this happened.

soce_mdio_write() and the c45 variants have the same ordering. Does this IP
latch the selector per transaction, or does the idle check also need to
cover the selector writes?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
  2026-09-25 23:17   ` Andrew Lunn
  2026-09-25 23:20   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  2 siblings, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 1 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] In soce_sw_probe() (drivers/net/dsa/soce/soce_dsa_core.c), child
  platform devices are created with devm_of_platform_populate() at line…
- [Medium] soce_setup() never puts the per-port INGR_EN/EGR_EN bits into
  a known state for user ports, and probe does not reset the core.

--

net: dsa: soce: Add basic support for SoC-e switch IP cores

This adds a DSA driver for SoC-e FPGA-based Ethernet switch IP cores. It
reads the core version and feature registers at probe and enables DSA
custom rules tagging during setup. The DSA port callbacks control port
ingress and egress, and probe populates the MDIO controller and mux child
devices.

> diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
> new file mode 100644
> index 0000000000000..d391b11b94ad6
> --- /dev/null
> +++ b/drivers/net/dsa/soce/soce_dsa_core.c

[ ... ]

> +static int soce_setup(struct dsa_switch *ds)
> +{
> +	struct soce_priv *priv = ds->priv;
> +
> +	soce_sw_enable_tagging(&priv->local);
> +
> +	return 0;
> +}

[Severity: Medium]
Is there anything that sets SOCE_PORTS_CTRL_INGR_EN and
SOCE_PORTS_CTRL_EGR_EN to a known state on user ports before tagging is
enabled here?

Probe does not reset the core. At setup time the DSA core calls
port_disable only for unused ports:

net/dsa/dsa.c:dsa_port_setup() {
    ...
	case DSA_PORT_TYPE_UNUSED:
		dsa_port_disable(dp);
		break;
    ...
}

User ports get no port_enable or port_disable call until their netdev is
opened or closed.

Suppose the IP comes out of reset with ports enabled, or a bootloader left
them enabled. Once soce_setup() turns on tag all frames and custom rules
tagging, could a user port that is administratively down still receive
frames and forward them tagged to the CPU port?

At the end of the series, soce_setup() resets VLAN, disables STP and
enables tagging. It still doesn't seem to change the per-port enable bits.
Would it make sense to clear them for all user ports here?

[ ... ]

> +	ds->ops = &soce_switch_ops;
> +	ds->num_ports = hw_numports;
> +	ret = devm_of_platform_populate(dev);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to populate child devices\n");
> +
> +	dev_set_drvdata(dev, priv);
> +
> +	ret = dsa_register_switch(ds);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to register DSA switch\n");

[Severity: High]
Can this probe loop without end when dsa_register_switch() returns
-EPROBE_DEFER?

devm_of_platform_populate() creates the child devices before
dsa_register_switch() runs. dsa_register_switch() can defer in fairly
common situations. One is a conduit netdev that isn't registered yet:

net/dsa/dsa.c:dsa_port_parse_of() {
    ...
		conduit = of_find_net_device_by_node(ethernet);
		of_node_put(ethernet);
		if (!conduit) {
			rtnl_unlock();
			return -EPROBE_DEFER;
    ...
}

It can also defer when dev_find_class() fails, or when the tagger lookup
in dsa_switch_setup_tag_protocol() fails:

		if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
			return -EPROBE_DEFER;

Documentation/driver-api/driver-model/driver.rst says:

  -EPROBE_DEFER must not be returned if probe() has already created child
  devices ... it may result in an infinite loop of .probe() calls

The sequence looks like this:

soce_sw_probe()
  devm_of_platform_populate()
    mdio-soce, mdio-mux-mmioreg and the PHY devices bind
      driver_bound()->driver_deferred_probe_trigger()
        deferred_trigger_count++
  dsa_register_switch()
    returns -EPROBE_DEFER
really_probe()
  sees the trigger count changed and triggers deferred probing again
devres removes the child devices
soce_sw_probe() runs again and creates them again

The children bind synchronously because MODULE_SOFTDEP loads their drivers
first. The DSA ethernet phandle is not a fw_devlink supplier property, so
nothing makes the switch probe wait for the conduit.

If the conduit never appears, for example because its driver is missing
or fails, would a kworker keep creating and removing the MDIO bus and PHY
devices indefinitely?

This still appears to be present at the end of the series.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support
  2026-09-23 10:39 ` [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support Vasilij Strassheim
  2026-09-25 23:32   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found 8 potential
issue(s) to consider.

Critical: 0 · High: 2 · Medium: 5 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] soce_sw_probe() sets ds->needs_standalone_vlan_filtering = true
  unconditionally (drivers/net/dsa/soce/soce_dsa_core.c:654), including…
- [High] net: dsa: soce: needs_standalone_vlan_filtering drops standalone
  traffic after bridge leave
- [Medium] The commit message says the patch adds "VLAN filtering and
  membership offload" and "configure[s] ports in Hybrid and C-Port modes…
- [Medium] The commit message says unsupported VLAN operations are
  rejected "with a netlink extended acknowledgment".
- [Medium] The driver sets ds->needs_standalone_vlan_filtering, so DSA
  advertises NETIF_F_HW_VLAN_CTAG_FILTER and sends 8021q-upper VIDs to…
- [Medium] soce_port_vlan_add() handles only the case where
  BRIDGE_VLAN_INFO_PVID is set (lines 496-500).
- [Medium] soce_vlan_write_entry() programs one VID with two independent
  hardware commands.
- [Low] soce_vlan_teardown() resets the hardware VLAN table
  (soce_vlan_reset()) and turns VLAN off, but leaves…

--

net: dsa: soce: Add VLAN offload support

This patch adds VLAN filtering and membership offload for SoC-e switch
cores that implement the Port VLAN synthesis option. On cores without that
option, VLAN operations are rejected. Per-VID member and untagged masks are
tracked in software and programmed through a selector interface, which a
new vlan_lock mutex serializes.

> diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
> index d391b11b94ad6..f93ce9da8400c 100644
> --- a/drivers/net/dsa/soce/soce_dsa_core.c
> +++ b/drivers/net/dsa/soce/soce_dsa_core.c

[ ... ]

> @@ -162,6 +200,178 @@ static void soce_sw_disable_tagging(struct soce_dsa_local *local)

[ ... ]

> +static int soce_vlan_write_entry(struct soce_priv *priv, u16 vid)
> +{
> +	struct soce_dsa_local *local = &priv->local;
> +	void __iomem *base = local->base_addr;
> +	u32 cpu_ports;
> +	u32 untagged;
> +	u32 members;
> +	int ret;
> +
> +	/* The CPU port must be a tagged member of every active VLAN so
> +	 * tagged frames can reach the conduit.
> +	 */
> +	cpu_ports = dsa_cpu_ports(&priv->ds);
> +	members = priv->vlan_members[vid];
> +	if (members)
> +		members |= cpu_ports;
> +	untagged = priv->vlan_untagged[vid] & ~cpu_ports;
> +
> +	soce_vlan_select_vid(local, vid);
> +	writel(members, base + SOCE_VLAN_MEMBER_PORTS_OFFSET);
> +	writel(SOCE_VLAN_MEMBER_CTRL_WRITE,
> +	       base + SOCE_VLAN_MEMBER_CTRL_OFFSET);
> +	ret = soce_vlan_wait_for_write(local, SOCE_VLAN_MEMBER_CTRL_OFFSET,
> +				       SOCE_VLAN_MEMBER_CTRL_WRITE);
> +	if (ret)
> +		return ret;
> +
> +	writel(untagged, base + SOCE_VLAN_UNTAG_PORTS_OFFSET);
> +	writel(SOCE_VLAN_UNTAG_CTRL_WRITE,
> +	       base + SOCE_VLAN_UNTAG_CTRL_OFFSET);
> +
> +	return soce_vlan_wait_for_write(local, SOCE_VLAN_UNTAG_CTRL_OFFSET,
> +					SOCE_VLAN_UNTAG_CTRL_WRITE);
> +}

[Severity: Medium]
What happens if the member command completes but the untag poll times out?

At that point the hardware member table already holds the new mask. The
callers, soce_port_vlan_add() and soce_port_vlan_del(), only restore the
software shadow:

    members[vlan->vid] = old_members;
    untagged_ports[vlan->vid] = old_untagged;
    return ret;

If an add fails this way, DSA and 8021q treat the VID as not added. The
port and the CPU ports are still members in hardware.

If a delete fails this way, the hardware has already removed the port, but
the shadow puts it back. The next soce_vlan_write_entry() for that VID then
re-adds the port in hardware.

There is also the case where the member command itself times out and the
WRITE bit may still be pending. The next VLAN operation rewrites
SOCE_VLAN_VID_SEL and the data registers without checking that the command
engine is idle. Could a stale command then hit another VID's entry? That
part depends on the hardware.

> +
> +static int soce_vlan_setup(struct dsa_switch *ds)
> +{

[ ... ]

> +	/* Default every port to PVID 1, unfiltered, so standalone
> +	 * forwarding keeps working before any bridge VLAN is configured.
> +	 */
> +	scoped_guard(mutex, &priv->vlan_lock) {
> +		dsa_switch_for_each_available_port(dp, ds) {
> +			priv->port_pvid[dp->index] = 1;
> +			soce_vlan_config_port(priv, dp->index, false);
> +		}
> +		soce_vlan_set_enabled(local, true);
> +	}

[Severity: Medium]
This leaves every available port, including the CPU port, in
SOCE_VLAN_PORT_TYPE_UNAWARE with ingress filtering off and
SOCE_VLAN_PORT_EGR_TAG_UNTAG_PORT. Can any reachable path move a port out
of that mode?

Because ds->needs_standalone_vlan_filtering is set, 8021q upper VIDs reach
soce_port_vlan_add(). soce_vlan_write_entry() then programs the member mask
and a custom untag mask that deliberately leaves out the CPU port.

The only path to C_PORT, INGR_FILTER_EN and EGR_TAG_CUSTOM_UNTAG is
soce_port_vlan_filtering(). That is not called without bridge offload (see
the comment on soce_switch_ops below).

The DSA core doesn't enable standalone filtering on its own. The only
needs_standalone_vlan_filtering handling in net/dsa/port.c is in
dsa_port_reset_vlan_filtering(), on bridge leave. hellcreek, the other user
of this flag, sets up standalone VLAN-aware isolation in the driver.

If the register names match the hardware behaviour, this mode ignores the
member and custom untag tables. Would 8021q upper TX frames then leave the
user port untagged, with no ingress VID filtering?

The exact meaning of EGR_TAG_UNTAG_PORT and PORT_TYPE_UNAWARE is inferred
from the macro names and should be checked against the SoC-e
documentation.

> +
> +	return 0;
> +}
> +
> +static void soce_vlan_teardown(struct soce_priv *priv)
> +{
> +	struct soce_dsa_local *local = &priv->local;
> +	int ret;
> +
> +	if (!priv->features.port_vlan)
> +		return;
> +
> +	scoped_guard(mutex, &priv->vlan_lock) {
> +		ret = soce_vlan_reset(local);
> +		if (ret)
> +			dev_warn(priv->ds.dev,
> +				 "failed to reset VLAN configuration during teardown: %d\n",
> +				 ret);
> +		soce_vlan_set_enabled(local, false);
> +	}
> +}

[Severity: Low]
Should priv->vlan_members[] and priv->vlan_untagged[] also be cleared here?

The hardware VLAN table is reset, but both arrays keep their contents.
soce_vlan_setup() only re-initialises port_pvid[]. The per-VID arrays are
zeroed only once, by devm_kcalloc() in soce_sw_probe().

DSA can call teardown and then setup again on the same priv without a new
probe. One example is a multi-switch tree where another member switch is
removed and re-probed: dsa_tree_teardown() runs, followed by
dsa_tree_setup().

The shadow normally drains through port_vlan_del before teardown. It does
not drain when soce_port_vlan_del() fails, because that path puts the old
bits back after the core has already forgotten the VLAN.

Can those stale bits survive the reset and be ORed into the next
soce_port_vlan_add() for that VID, re-adding ports nobody configured?

Clearing both arrays under vlan_lock in setup or teardown would keep the
shadow in step with the hardware reset.

[ ... ]

> @@ -224,6 +443,130 @@ static enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds,
>  	return DSA_TAG_PROTO_SDSA;
>  }
>  
> +static int soce_port_vlan_add(struct dsa_switch *ds, int port,
> +			      const struct switchdev_obj_port_vlan *vlan,
> +			      struct netlink_ext_ack *extack)
> +{

[ ... ]

> +	if (!priv->features.port_vlan) {
> +		NL_SET_ERR_MSG_MOD(extack,
> +				   "Port VLAN support is not implemented in the switch core");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (!vlan->vid)
> +		return 0;

[Severity: Medium]
The commit message says unsupported VLAN operations are rejected "with a
netlink extended acknowledgment". Does this extack actually reach
userspace for 8021q uppers?

The call chain vlan_newlink()->register_vlan_dev()->vlan_vid_add()->
ndo_vlan_rx_add_vid carries no extack. dsa_user_vlan_rx_add_vid() fills in
a local extack on the stack and only logs it:

    ret = dsa_port_vlan_add(dp, &vlan, &extack);
    if (ret) {
        if (extack._msg)
            netdev_err(dev, "%s\n", extack._msg);
        return ret;
    }

As a result, userspace only gets a bare -EOPNOTSUPP.

There is a second issue: the feature check runs before the VID 0 early
return.

When the 8021q module is loaded, vlan_vid0_add() calls
vlan_vid_add(dev, htons(ETH_P_8021Q), 0) on every NETDEV_UP for netdevs
with NETIF_F_HW_VLAN_CTAG_FILTER. With this patch, that is every soce user
port.

On cores without Port VLAN, won't this log "Port VLAN support is not
implemented in the switch core" at error level every time an interface
comes up, even though VID 0 needs no hardware work? Moving the !vlan->vid
check above the feature check would avoid that.

[ ... ]

> +		if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
> +			priv->port_pvid[port] = vlan->vid;
> +			soce_vlan_config_port(priv, port,
> +					      dsa_port_is_vlan_filtering(dp));
> +		}
> +	}

[Severity: Medium]
This handles only the case where BRIDGE_VLAN_INFO_PVID is set. What happens
when an existing PVID VLAN is notified again without the PVID flag?

struct switchdev_obj_port_vlan documents that notifications with
changed=true carry PVID/UNTAGGED flag changes for a VLAN that already
exists. nbp_vlan_add() sends these, and dsa_port_do_vlan_add() passes them
straight to the driver for user ports.

For example:

    bridge vlan add dev swpX vid 10 pvid untagged
    bridge vlan add dev swpX vid 10

The second command arrives as an add with changed=true and no PVID flag.
port_pvid[port], the hardware PVID register and ACCEPT_ALL all stay at
VID 10, and the callback still returns success.

At that point the bridge expects untagged ingress to be dropped. Wouldn't
the hardware still classify it into VID 10?

Something like this might be needed:

    else if (priv->port_pvid[port] == vlan->vid) {
        priv->port_pvid[port] = 0;
        soce_vlan_config_port(priv, port, dsa_port_is_vlan_filtering(dp));
    }

This can't be reached yet, at this revision or at the end of the series,
because there is no .port_bridge_join. Any follow-up that adds bridge
offload would make it reachable.

[ ... ]

>  static const struct dsa_switch_ops soce_switch_ops = {
>  	.get_tag_protocol	= soce_get_tag_protocol,
>  	.setup			= soce_setup,
> @@ -231,6 +574,9 @@ static const struct dsa_switch_ops soce_switch_ops = {
>  	.phylink_get_caps	= soce_phylink_get_caps,
>  	.port_enable		= soce_port_enable,
>  	.port_disable		= soce_port_disable,
> +	.port_vlan_filtering	= soce_port_vlan_filtering,
> +	.port_vlan_add		= soce_port_vlan_add,
> +	.port_vlan_del		= soce_port_vlan_del,
>  };

[Severity: Medium]
The commit message says the patch adds "VLAN filtering and membership
offload" and will "configure ports in Hybrid and C-Port modes and manage
their PVID, ingress filtering and acceptance mode, and custom egress
untagging".

Can any of the filtering, PVID or untagged logic run without a
.port_bridge_join op?

Without that op, dsa_switch_bridge_join() returns -EOPNOTSUPP.
dsa_user_changeupper() then turns this into a software bridge join, so
dp->bridge stays NULL:

    if (err == -EOPNOTSUPP) {
        NL_SET_ERR_MSG_WEAK_MOD(extack, "Offloading not supported");
        err = 0;
    }

ds->ops->port_vlan_filtering has three callers:

  - dsa_port_switchdev_sync_attrs(), after a successful join
  - dsa_port_reset_vlan_filtering(), on leaving an offloaded bridge
  - the SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING handler, which requires
    dsa_port_offloads_bridge_dev()

None of these can run, so soce_port_vlan_filtering() looks unreachable.

Bridge VLAN objects hit the !dp->bridge check in dsa_user_host_vlan_add()
and fall back to vlan_vid_add(). That ends up in
dsa_user_vlan_rx_add_vid(), which has:

    /* This API only allows programming tagged, non-PVID VIDs */
    .flags = 0,

So soce_port_vlan_add() never sees BRIDGE_VLAN_INFO_PVID or
BRIDGE_VLAN_INFO_UNTAGGED. Ports never leave the unfiltered PVID 1 state
set up by soce_vlan_setup().

The only live path is tagged, non-PVID membership, for 8021q uppers or
software bridge VIDs.

This is still the case at the end of the series; the last patch, "net: dsa:
soce: Disable unsupported hardware STP", only adds STP disabling. Should
the commit message be adjusted, or should bridge offload be added?

[ ... ]

> @@ -278,8 +623,35 @@ static int soce_sw_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  

[ ... ]

>  	ds->ops = &soce_switch_ops;
> -	ds->num_ports = hw_numports;
> +	ds->num_ports = priv->features.num_ports;
> +
> +	/* Force VLAN uppers always through the callbacks, so cores without
> +	 * Port VLAN feature can reject them instead of silently dropping
> +	 * VLAN frames.
> +	 */
> +	ds->needs_standalone_vlan_filtering = true;

[Severity: High]
What happens to a port that leaves a bridge while this flag is set?

With needs_standalone_vlan_filtering, dsa_port_bridge_leave() calls
dsa_port_reset_vlan_filtering(). When the bridge being left was
VLAN-unaware, that function forces vlan_filtering=true.

The call ends up in soce_port_vlan_filtering(), which calls
soce_vlan_config_port(). That switches the port to
SOCE_VLAN_PORT_TYPE_C_PORT with SOCE_VLAN_PORT_INGR_FILTER_EN set.

Nothing in the driver gives a standalone port a VLAN to be classified
into. soce_vlan_setup() sets port_pvid[] to 1. It never adds the port
to vlan_members[1], and it never programs the VID 1 member entry.

By the time the port is standalone again, the bridge has also flushed
its own VLANs through soce_port_vlan_del(). One of two things then
happens:

  - port_pvid[] is still 1. Untagged frames are classified into
    VID 1, and the ingress filter drops them because the port is not
    a member.
  - VID 1 was deleted as the PVID, so port_pvid[] is 0.
    SOCE_VLAN_PORT_INGR_ACCEPT_TAGGED_ONLY then drops untagged frames
    at ingress.

In both cases untagged standalone traffic on that port stops after a
bridge join/leave cycle. This contradicts the comment in
soce_vlan_setup(), which says standalone forwarding keeps working.

hellcreek, the other user of this flag, programs a private per-port
VLAN so that standalone ports stay reachable with filtering enabled.
Is something similar planned here?

Alternatively, would it be simpler not to set the flag at all? Cores
without Port VLAN could then reject VLAN uppers from
.port_prechangeupper instead.

This is latent at this revision. Without .port_bridge_join, the join
is rolled back and dp->bridge stays NULL. dsa_port_bridge_leave()
then returns early. The problem becomes reachable as soon as bridge
offload is added.

[Severity: High]
This flag is set even when priv->features.port_vlan is false. Does that
break plain bridge membership on cores without Port VLAN?

With the flag set, dsa_user_setup_tagger() advertises
NETIF_F_HW_VLAN_CTAG_FILTER on every user netdev.

The failure needs CONFIG_VLAN_8021Q and CONFIG_BRIDGE_VLAN_FILTERING and
the default vlan_default_pvid=1. It happens whatever the bridge's
vlan_filtering setting is:

ip link set lanX master br0
  br_add_if()
    nbp_vlan_init()
      nbp_vlan_add(vid 1, PVID | UNTAGGED)
        __vlan_add()
          __vlan_vid_add()
            switchdev returns -EOPNOTSUPP (!dp->bridge)
            vlan_vid_add(dev, 8021Q, 1)
              dsa_user_vlan_rx_add_vid()
                dsa_port_vlan_add()
                  soce_port_vlan_add()
                    returns -EOPNOTSUPP (!features.port_vlan)

nbp_vlan_init() then fails, and br_add_if() aborts the enslave:

    err = nbp_vlan_init(p, extack);
    if (err) {
        netdev_err(dev, "failed to initialize vlan filtering on this port\n");
        goto err6;
    }

Software bridging on these cores worked with the previous patch in the
series.

Neither the commit message ("reject VLAN operations") nor the probe
comment says that bridge membership is now refused. The refusal is also
inconsistent: a bridge created with vlan_default_pvid 0 still works, and
standalone ports see the same VLAN-stripping behaviour.

Is this refusal intended for cores without Port VLAN?

> +
>  	ret = devm_of_platform_populate(dev);
>  	if (ret)
>  		return dev_err_probe(dev, ret,

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP
  2026-09-23 10:39 ` [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP Vasilij Strassheim
  2026-09-25 23:24   ` Andrew Lunn
@ 2026-09-27 12:28   ` netdev-bot+sashiko
  1 sibling, 0 replies; 38+ messages in thread
From: netdev-bot+sashiko @ 2026-09-27 12:28 UTC (permalink / raw)
  To: v.strassheim
  Cc: robh, krzk+dt, conor+dt, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, linux, andrew+netdev, hkallweit1, devicetree,
	linux-kernel, netdev, martin.kaistra, b.spranger

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] Series-structure concern.

--

net: dsa: soce: Disable unsupported hardware STP

This commit checks the STP synthesis bit in the implemented features
register. When the bit is set, soce_setup() clears the STP control
enable bit, so STP state handling is left to the Linux bridge.

> diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
> index f93ce9da8400c..3b9fde7a547e7 100644
> --- a/drivers/net/dsa/soce/soce_dsa_core.c
> +++ b/drivers/net/dsa/soce/soce_dsa_core.c

[ ... ]

> @@ -160,6 +165,7 @@ static int soce_sw_detect_features(struct soce_dsa_local *local,
>  		return -ENODEV;
>  
>  	features->port_vlan = regval & SOCE_IMPL_FEATURES0_PORT_VLAN;
> +	features->stp = regval & SOCE_IMPL_FEATURES0_STP;
>  
>  	implemented_numports =
>  		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);

[ ... ]

> @@ -420,6 +436,10 @@ static int soce_setup(struct dsa_switch *ds)
>  	if (ret)
>  		return ret;
>  
> +	/* Unconfigured hardware STP blocks frame forwarding. */
> +	if (priv->features.stp)
> +		soce_stp_disable(&priv->local);
> +
>  	soce_sw_enable_tagging(&priv->local);
>  
>  	return 0;

[Severity: Low]
This isn't a bug, but could this be folded into the base driver patch?

Two earlier patches in the series already accept cores synthesized with
SOCE_IMPL_FEATURES0_STP set, but neither touches SOCE_STP_CTRL_OFFSET:

  net: dsa: soce: Add basic support for SoC-e switch IP cores
  net: dsa: soce: Add VLAN offload support

This commit's message says such cores block frame forwarding. So if you
bisect to either of those commits on an STP-synthesized core, the driver
would probe and then forward nothing.

The whole driver is new in this series, so nothing that worked before
regresses. A Fixes: tag pointing at an unmerged commit in the same series
wouldn't be wanted either. Squashing this change into the base driver
patch would avoid that intermediate state.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923-devel-vstrassheim-soce-dsa-ml-v3-0-ddebafcb9ba7%40linutronix.de

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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-25 22:55   ` Andrew Lunn
@ 2026-09-30 14:00     ` Vasilij Strassheim
  2026-09-30 15:14       ` Andrew Lunn
  0 siblings, 1 reply; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 14:00 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 00:55 +0200, Andrew Lunn wrote:
> > The controller exposes separate register regions for transaction data
> > and for the shared transaction control and external bus selector
> > register.
> 
> > +examples:
> > +  - |
> > +    mdio@204 {
> > +        compatible = "soce,swip-mdio-23-02";
> > +        reg = <0x204 0xc>, <0x200 0x4>;
> 
> At least in the example, they are not separate?

Not separate regions but registers...
I'm obviously bad at documenting things.

The current information in the commit message is misleading and
irrelevant. Looking at the bot's feedback, it's at the same time not
clear enough yet that the mdio controller part is mapped within the
switch memory and can't be used separately from it.

I will update the commit message to something like this:
Add a binding for the MDIO controller integrated into SoC-e SWIP
Ethernet switch IP cores.
The controller shares the memory of the synthesized switch IP core and
cannot be used independently.

> 
>    Andrew

Thanks,
Vasilij

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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-30 14:00     ` Vasilij Strassheim
@ 2026-09-30 15:14       ` Andrew Lunn
  2026-09-30 17:13         ` Vasilij Strassheim
  0 siblings, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-30 15:14 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 30, 2026 at 04:00:56PM +0200, Vasilij Strassheim wrote:
> On Sat, 2026-09-26 at 00:55 +0200, Andrew Lunn wrote:
> > > The controller exposes separate register regions for transaction data
> > > and for the shared transaction control and external bus selector
> > > register.
> > 
> > > +examples:
> > > +  - |
> > > +    mdio@204 {
> > > +        compatible = "soce,swip-mdio-23-02";
> > > +        reg = <0x204 0xc>, <0x200 0x4>;
> > 
> > At least in the example, they are not separate?
> 
> Not separate regions but registers...
> I'm obviously bad at documenting things.
> 
> The current information in the commit message is misleading and
> irrelevant. Looking at the bot's feedback, it's at the same time not
> clear enough yet that the mdio controller part is mapped within the
> switch memory and can't be used separately from it.
> 
> I will update the commit message to something like this:
> Add a binding for the MDIO controller integrated into SoC-e SWIP
> Ethernet switch IP cores.
> The controller shares the memory of the synthesized switch IP core and
> cannot be used independently.

I think part of the issue is the compatible. That suggests it is a
separate device, with its own driver. But it is actually driven by the
switch driver.

       Andrew

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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-30 15:14       ` Andrew Lunn
@ 2026-09-30 17:13         ` Vasilij Strassheim
  2026-09-30 18:24           ` Andrew Lunn
  0 siblings, 1 reply; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 17:13 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, 2026-09-30 at 17:14 +0200, Andrew Lunn wrote:
> On Wed, Sep 30, 2026 at 04:00:56PM +0200, Vasilij Strassheim wrote:
> > On Sat, 2026-09-26 at 00:55 +0200, Andrew Lunn wrote:
> > > > The controller exposes separate register regions for transaction data
> > > > and for the shared transaction control and external bus selector
> > > > register.
> > > 
> > > > +examples:
> > > > +  - |
> > > > +    mdio@204 {
> > > > +        compatible = "soce,swip-mdio-23-02";
> > > > +        reg = <0x204 0xc>, <0x200 0x4>;
> > > 
> > > At least in the example, they are not separate?
> > 
> > Not separate regions but registers...
> > I'm obviously bad at documenting things.
> > 
> > The current information in the commit message is misleading and
> > irrelevant. Looking at the bot's feedback, it's at the same time not
> > clear enough yet that the mdio controller part is mapped within the
> > switch memory and can't be used separately from it.
> > 
> > I will update the commit message to something like this:
> > Add a binding for the MDIO controller integrated into SoC-e SWIP
> > Ethernet switch IP cores.
> > The controller shares the memory of the synthesized switch IP core and
> > cannot be used independently.
> 
> I think part of the issue is the compatible. That suggests it is a
> separate device, with its own driver. But it is actually driven by the
> switch driver.

I can't avoid the compatible right now. Somehow it is still a separate
functionality. I hope the following example doesn't cause unnecessary
confusion, but rather helps to understand the system better:

The relationship between the MDIO controller and the switch core is more
like that of tools in a Swiss Army knife.
There are a few standalone tools, such as the knife and the screwdriver.
These can also be described on their own. But they only make sense when
they are attached to the knife as a whole. As soon as one takes it
apart, the individual tools can no longer be used effectively. At the
same time, no one needs to worry about the screwdriver if they only need
the knife.

I haven't found anything that I could use as a reference for the
documentation, so far. So right now, I can't think of a better solution
than document it as clearly as possible for the next version.
Do you (or anyone else) have any advice on the best way for me to describe
this in the documentation?

> 
>        Andrew

Thanks,
Vasilij


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

* Re: [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch
  2026-09-25 23:05   ` Andrew Lunn
@ 2026-09-30 17:16     ` Vasilij Strassheim
  0 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 17:16 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:05 +0200, Andrew Lunn wrote:
> > +  mdio@204:
> > +    $ref: /schemas/net/soce,swip-mdio.yaml#
> > +    unevaluatedProperties: false
> > +    description:
> > +      Integrated MDIO controller bus on the master side of the mux.
> > +
> > +  mdio-mux@202:
> > +    $ref: /schemas/net/mdio-mux-mmioreg.yaml#
> > +    unevaluatedProperties: false
> 
> > +        mdio_parent: mdio@204 {
> > +            compatible = "soce,swip-mdio-23-02";
> > +            reg = <0x204 0xc>, <0x200 0x4>;
> > +            reg-names = "data", "control";
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +        };
> > +
> > +        mdio-mux@202 {
> > +            compatible = "mdio-mux-mmioreg", "mdio-mux";
> > +            reg = <0x202 0x2>;
> 
> So these two overlap? That is pretty unusual, so might be worth a comment somewhere.

No, they don't. I accidentally included the entire register in the
documentation. However, this register can be split, since only 2 bytes
are used.
I will fix this in both bindings and their examples:
"reg = <0x204 0xc>, <0x200 0x2>;"

> 
> 	Andrew

Thanks,
Vasilij

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

* Re: [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver
  2026-09-25 23:10   ` Andrew Lunn
@ 2026-09-30 17:23     ` Vasilij Strassheim
  2026-09-30 18:20       ` Andrew Lunn
  0 siblings, 1 reply; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 17:23 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:10 +0200, Andrew Lunn wrote:
> > diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
> > index 048586746026..079b46dea25f 100644
> > --- a/drivers/net/mdio/Makefile
> > +++ b/drivers/net/mdio/Makefile
> > @@ -23,6 +23,7 @@ obj-$(CONFIG_MDIO_OCTEON)		+= mdio-octeon.o
> >  obj-$(CONFIG_MDIO_PIC64HPSC)		+= mdio-pic64hpsc.o
> >  obj-$(CONFIG_MDIO_REALTEK_RTL9300)	+= mdio-realtek-rtl9300.o
> >  obj-$(CONFIG_MDIO_REGMAP)		+= mdio-regmap.o
> > +obj-$(CONFIG_MDIO_SOCE)			+= mdio-soce.o
> >  obj-$(CONFIG_MDIO_SUN4I)		+= mdio-sun4i.o
> 
> Maybe the indentation is wrong here?

This looks fine in code.

> 
> Otherwise:
> 
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>

Thanks, but I can not use it yet. Need to fix an issue with
devm_ioremap() return value check in the next version.

> 
>     Andrew

Vasilij

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-25 23:17   ` Andrew Lunn
@ 2026-09-30 17:26     ` Vasilij Strassheim
  0 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 17:26 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:17 +0200, Andrew Lunn wrote:
> > +static void soce_sw_read_core_version(struct soce_dsa_local *local,
> > +				      u8 *version, u8 *subversion,
> > +				      u16 *revision)
> > +{
> > +	u32 regval;
> > +
> > +	regval = readl(local->base_addr + SOCE_CORE_VERSION_OFFSET);
> > +	*version = (u8)(regval >> SOCE_CORE_VERSION_VERSION_SHIFT);
> > +	*subversion = (u8)(regval >> SOCE_CORE_VERSION_SUBVERSION_SHIFT);
> > +	*revision = (u16)regval;
> 
> FIELD_GET() would make this more readable. 

Yes, I will fix it.

> > +static int soce_sw_detect_features(struct soce_dsa_local *local,
> > +				   u32 *numports)
> > +{
> > +	void __iomem *base = local->base_addr;
> > +	u32 implemented_numports;
> > +	u32 licensed_numports;
> > +	u32 regval;
> > +
> > +	regval = readl(base + SOCE_LIC_FEATURES_OFFSET);
> > +	licensed_numports = FIELD_GET(SOCE_LIC_FEATURES_NUM_PORTS_MASK, regval);
> > +	if (!licensed_numports || licensed_numports > SOCE_MAX_NUM_PORTS)
> > +		return -EINVAL;
> > +
> > +	regval = readl(base + SOCE_IMPL_FEATURES0_OFFSET);
> > +	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
> > +		return -ENODEV;
> > +
> > +	implemented_numports =
> > +		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
> > +	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
> > +	    implemented_numports > licensed_numports)
> > +		return -EINVAL;
> 
> Maybe add dev_err() here for all these error cases. It will help
> somebody debug why there switch fails to probe.

Sure, I will add them.

> 
> 	 Andrew

Thanks,
Vasilij

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-25 23:20   ` Andrew Lunn
@ 2026-09-30 18:15     ` Vasilij Strassheim
  2026-09-30 18:29       ` Andrew Lunn
  0 siblings, 1 reply; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 18:15 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:20 +0200, Andrew Lunn wrote:
> On Wed, Sep 23, 2026 at 12:39:33PM +0200, Vasilij Strassheim wrote:
> > Add a DSA driver for SoC-e FPGA-based Ethernet switch IP cores.
> > 
> > Read the core version and synthesis-time feature registers during probe.
> > Require DSA support and between 3 and 31 implemented ports, without
> > exceeding the licensed port count. Derive each port's phylink
> > capabilities from its phy-mode.
> > 
> > Enable "DSA custom rules" tagging for all frames during setup. This
> > directs ingress traffic from user ports to the CPU port while standalone
> > ports remain isolated. Enable and disable port ingress and egress
> > through the DSA port callbacks, and disable tagging again during
> > teardown so that unbinding the driver does not leave its managed
> > configuration active.
> > 
> > Populate the integrated MDIO controller and mux child devices used for
> > external PHY access.
> > 
> > Tested with a SoC-e MRS 25.01 IP core on a Xilinx ZynqMP platform.
> > 
> > Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
> > ---
> >  drivers/net/dsa/Kconfig              |   2 +
> >  drivers/net/dsa/Makefile             |   1 +
> >  drivers/net/dsa/soce/Kconfig         |  14 ++
> >  drivers/net/dsa/soce/Makefile        |   4 +
> >  drivers/net/dsa/soce/soce_dsa.h      |  26 +++
> >  drivers/net/dsa/soce/soce_dsa_core.c | 343 +++++++++++++++++++++++++++++++++++
> >  6 files changed, 390 insertions(+)
> > 
> > diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
> > index fe8cd5338fda..879fbede83f0 100644
> > --- a/drivers/net/dsa/Kconfig
> > +++ b/drivers/net/dsa/Kconfig
> > @@ -94,6 +94,8 @@ source "drivers/net/dsa/ocelot/Kconfig"
> >  
> >  source "drivers/net/dsa/qca/Kconfig"
> >  
> > +source "drivers/net/dsa/soce/Kconfig"
> > +
> >  source "drivers/net/dsa/sja1105/Kconfig"
> >  
> >  source "drivers/net/dsa/xrs700x/Kconfig"
> > diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
> > index 7e637d56b35c..97de3d181440 100644
> > --- a/drivers/net/dsa/Makefile
> > +++ b/drivers/net/dsa/Makefile
> > @@ -26,4 +26,5 @@ obj-y				+= ocelot/
> >  obj-y				+= qca/
> >  obj-y				+= realtek/
> >  obj-y				+= sja1105/
> > +obj-y				+= soce/
> >  obj-y				+= xrs700x/
> > diff --git a/drivers/net/dsa/soce/Kconfig b/drivers/net/dsa/soce/Kconfig
> > new file mode 100644
> > index 000000000000..c31b7c5af695
> > --- /dev/null
> > +++ b/drivers/net/dsa/soce/Kconfig
> > @@ -0,0 +1,14 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +config NET_DSA_SOCE
> > +	tristate "SoC-e switches"
> > +	depends on NET_DSA
> > +	depends on OF
> > +	depends on HAS_IOMEM
> > +	select MDIO_BUS_MUX_MMIOREG
> > +	select MDIO_SOCE
> > +	select NET_DSA_TAG_SDSA
> > +	help
> > +	  This enables support for switches based on SoC-e IP cores.
> > +	  Frames are exchanged with the CPU port using the SDSA DSA tag protocol.
> > +	  The driver supports switch variants whose features and number of ports
> > +	  are selected at synthesis time and detected at runtime.
> > diff --git a/drivers/net/dsa/soce/Makefile b/drivers/net/dsa/soce/Makefile
> > new file mode 100644
> > index 000000000000..2a6d95ef663f
> > --- /dev/null
> > +++ b/drivers/net/dsa/soce/Makefile
> > @@ -0,0 +1,4 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-$(CONFIG_NET_DSA_SOCE) += soce_dsa.o
> > +soce_dsa-objs := soce_dsa_core.o
> > diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h
> > new file mode 100644
> > index 000000000000..2acd4dbaf958
> > --- /dev/null
> > +++ b/drivers/net/dsa/soce/soce_dsa.h
> > @@ -0,0 +1,26 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (c) 2020-2026 System on Chip engineering, S.L.
> > + * Copyright (c) 2026 Linutronix GmbH
> > + * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
> > + */
> > +
> > +#ifndef __SOCE_DSA_H
> > +#define __SOCE_DSA_H
> > +
> > +#include <linux/types.h>
> > +
> > +#include <net/dsa.h>
> > +
> > +#define SOCE_MAX_NUM_PORTS 31
> > +
> > +struct soce_dsa_local {
> > +	void __iomem *base_addr;
> > +};
> > +
> > +struct soce_priv {
> > +	struct soce_dsa_local local;
> > +	struct dsa_switch ds;
> > +};
> > +
> > +#endif /* __SOCE_DSA_H */
> > diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c
> > new file mode 100644
> > index 000000000000..d391b11b94ad
> > --- /dev/null
> > +++ b/drivers/net/dsa/soce/soce_dsa_core.c
> > @@ -0,0 +1,343 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2020-2026 System on Chip engineering, S.L.
> > + * Copyright (c) 2026 Linutronix GmbH
> > + * Author: Vasilij Strassheim <v.strassheim@linutronix.de>
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/of.h>
> > +#include <linux/of_net.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy.h>
> > +#include <linux/phylink.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include <net/dsa.h>
> > +
> > +#include "soce_dsa.h"
> > +
> > +#define SOCE_MIN_NUM_PORTS			3
> > +
> > +#define SOCE_CORE_VERSION_OFFSET		0x0000
> > +#define SOCE_CORE_VERSION_VERSION_SHIFT		24
> > +#define SOCE_CORE_VERSION_SUBVERSION_SHIFT	16
> > +#define SOCE_MIN_CORE_VERSION			0x24
> > +#define SOCE_MIN_CORE_SUBVERSION		0x01
> > +
> > +#define SOCE_LIC_FEATURES_OFFSET		0x0004
> > +#define SOCE_LIC_FEATURES_NUM_PORTS_MASK	GENMASK(31, 27)
> > +
> > +#define SOCE_IMPL_FEATURES0_OFFSET		0x000c
> > +#define SOCE_IMPL_FEATURES0_NUM_PORTS_MASK	GENMASK(31, 27)
> > +#define SOCE_IMPL_FEATURES0_PORT_VLAN		BIT(9)
> > +#define SOCE_IMPL_FEATURES0_DSA			BIT(23)
> > +
> > +#define SOCE_DSA_REGS_BASE			0x1200
> > +#define SOCE_TAG_ALL_FRAMES_CTRL_OFFSET		(SOCE_DSA_REGS_BASE + 0x001c)
> > +#define SOCE_TAG_ALL_FRAMES_CTRL_ENABLE		BIT(0)
> > +#define SOCE_CUSTOM_RULES_TAGGING_OFFSET	(SOCE_DSA_REGS_BASE + 0x0020)
> > +#define SOCE_CUSTOM_RULES_TAGGING_ENABLE	BIT(0)
> > +
> > +#define SOCE_PORTS_REGS_BASE			0x3000
> > +#define SOCE_PORTS_SELECTOR_OFFSET		SOCE_PORTS_REGS_BASE
> > +#define SOCE_PORTS_SELECTOR_PORT_MASK		GENMASK(7, 0)
> > +#define SOCE_PORTS_CTRL_OFFSET			(SOCE_PORTS_REGS_BASE + 0x0004)
> > +#define SOCE_PORTS_CTRL_INGR_EN			BIT(0)
> > +#define SOCE_PORTS_CTRL_EGR_EN			BIT(1)
> > +
> > +static void soce_phylink_get_caps(struct dsa_switch *ds, int port,
> > +				  struct phylink_config *config)
> > +{
> > +	struct dsa_port *dp = dsa_to_port(ds, port);
> > +	phy_interface_t mode;
> > +	int ret;
> > +
> > +	ret = of_get_phy_mode(dp->dn, &mode);
> > +	if (ret)
> > +		return;
> > +
> > +	if (phy_interface_mode_is_rgmii(mode))
> > +		phy_interface_set_rgmii(config->supported_interfaces);
> > +	else
> > +		__set_bit(mode, config->supported_interfaces);
> > +
> > +	config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
> > +
> > +	switch (mode) {
> > +	case PHY_INTERFACE_MODE_MII:
> > +		config->mac_capabilities |= MAC_10 | MAC_100;
> > +		break;
> > +	case PHY_INTERFACE_MODE_GMII:
> > +		config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000;
> > +		break;
> > +	case PHY_INTERFACE_MODE_RMII:
> > +		config->mac_capabilities |= MAC_10FD | MAC_100FD;
> > +		break;
> > +	default:
> > +		if (phy_interface_mode_is_rgmii(mode))
> > +			config->mac_capabilities |= MAC_10FD | MAC_100FD |
> > +						    MAC_1000FD;
> > +		break;
> > +	}
> > +}
> > +
> > +static int soce_sw_validate_core_version(u8 version, u8 subversion)
> > +{
> > +	if (version < SOCE_MIN_CORE_VERSION ||
> > +	    (version == SOCE_MIN_CORE_VERSION &&
> > +	     subversion < SOCE_MIN_CORE_SUBVERSION))
> > +		return -ENODEV;
> > +
> > +	return 0;
> > +}
> > +
> > +static void soce_sw_read_core_version(struct soce_dsa_local *local,
> > +				      u8 *version, u8 *subversion,
> > +				      u16 *revision)
> > +{
> > +	u32 regval;
> > +
> > +	regval = readl(local->base_addr + SOCE_CORE_VERSION_OFFSET);
> > +	*version = (u8)(regval >> SOCE_CORE_VERSION_VERSION_SHIFT);
> > +	*subversion = (u8)(regval >> SOCE_CORE_VERSION_SUBVERSION_SHIFT);
> > +	*revision = (u16)regval;
> > +}
> > +
> > +static int soce_sw_detect_features(struct soce_dsa_local *local,
> > +				   u32 *numports)
> > +{
> > +	void __iomem *base = local->base_addr;
> > +	u32 implemented_numports;
> > +	u32 licensed_numports;
> > +	u32 regval;
> > +
> > +	regval = readl(base + SOCE_LIC_FEATURES_OFFSET);
> > +	licensed_numports = FIELD_GET(SOCE_LIC_FEATURES_NUM_PORTS_MASK, regval);
> > +	if (!licensed_numports || licensed_numports > SOCE_MAX_NUM_PORTS)
> > +		return -EINVAL;
> > +
> > +	regval = readl(base + SOCE_IMPL_FEATURES0_OFFSET);
> > +	if (!(regval & SOCE_IMPL_FEATURES0_DSA))
> > +		return -ENODEV;
> > +
> > +	implemented_numports =
> > +		FIELD_GET(SOCE_IMPL_FEATURES0_NUM_PORTS_MASK, regval);
> > +	if (implemented_numports < SOCE_MIN_NUM_PORTS ||
> > +	    implemented_numports > licensed_numports)
> > +		return -EINVAL;
> > +
> > +	*numports = implemented_numports;
> > +
> > +	return 0;
> > +}
> > +
> > +static void soce_sw_enable_tagging(struct soce_dsa_local *local)
> > +{
> > +	void __iomem *base = local->base_addr;
> > +	u32 regval;
> > +
> > +	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> > +	regval |= SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
> > +	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> > +
> > +	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> > +	regval |= SOCE_CUSTOM_RULES_TAGGING_ENABLE;
> > +	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> > +}
> > +
> > +static void soce_sw_disable_tagging(struct soce_dsa_local *local)
> > +{
> > +	void __iomem *base = local->base_addr;
> > +	u32 regval;
> > +
> > +	regval = readl(base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> > +	regval &= ~SOCE_TAG_ALL_FRAMES_CTRL_ENABLE;
> > +	writel(regval, base + SOCE_TAG_ALL_FRAMES_CTRL_OFFSET);
> > +
> > +	regval = readl(base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> > +	regval &= ~SOCE_CUSTOM_RULES_TAGGING_ENABLE;
> > +	writel(regval, base + SOCE_CUSTOM_RULES_TAGGING_OFFSET);
> > +}
> > +
> > +static void soce_port_select(struct soce_dsa_local *local, int port)
> > +{
> > +	writel(FIELD_PREP(SOCE_PORTS_SELECTOR_PORT_MASK, port),
> > +	       local->base_addr + SOCE_PORTS_SELECTOR_OFFSET);
> > +}
> > +
> > +static void soce_port_set_enabled(struct soce_dsa_local *local, int port,
> > +				  bool enabled)
> > +{
> > +	void __iomem *base = local->base_addr;
> > +	u32 regval;
> > +
> > +	soce_port_select(local, port);
> > +
> > +	regval = readl(base + SOCE_PORTS_CTRL_OFFSET);
> > +	if (enabled)
> > +		regval |= SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN;
> > +	else
> > +		regval &= ~(SOCE_PORTS_CTRL_INGR_EN | SOCE_PORTS_CTRL_EGR_EN);
> > +	writel(regval, base + SOCE_PORTS_CTRL_OFFSET);
> > +}
> > +
> > +static int soce_port_enable(struct dsa_switch *ds, int port,
> > +			    struct phy_device *phy)
> > +{
> > +	struct soce_priv *priv = ds->priv;
> > +
> > +	soce_port_set_enabled(&priv->local, port, true);
> > +
> > +	return 0;
> > +}
> > +
> > +static void soce_port_disable(struct dsa_switch *ds, int port)
> > +{
> > +	struct soce_priv *priv = ds->priv;
> > +
> > +	soce_port_set_enabled(&priv->local, port, false);
> > +}
> > +
> > +static int soce_setup(struct dsa_switch *ds)
> > +{
> > +	struct soce_priv *priv = ds->priv;
> > +
> > +	soce_sw_enable_tagging(&priv->local);
> 
> Once all the setup is finished, what is the state of the switch?
> 
> What we want is that the user ports are isolated from each other, and
> can only exchange frames with the CPU. That makes the hardware
> basically a port expander, and you do bridging in software. Later
> patches can then add offload of bridging, and whatever else the
> hardware can do, which Linux can also do in software.

That is exactly the state I want to achieve after the setup.

Although there are options to configure various filters, mirrors, and
special DSA tagging, none of them make sense for initial Linux driver.
Therefore, I only enable “DSA tagging” for all packets so that the ports
are separated and everything is forwarded to the CPU port.

I noticed some unusual behavior in two areas for which I have not found
an explanation in the documentation.
- If the VLAN port feature is disabled during synthesis, the VLAN tags
  are removed on ingress to the CPU port.
- If the STP offloading feature is enabled during synthesis, forwarding
  between ports does not work.

I therefore tried to address and document this during the setup so that
DSA would work correctly under Linux.

After setup, the output of "ip a" tool looks something like this:
1: end0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1508 qdisc mq state UP ...
2: PORT_0@end0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 ...
3: PORT_1@end0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...

Software bridging basically works this way, too. However, Sashiko
rightly pointed out that I broke bridging with the later patch for VLAN
offloading, when the feature isn't enabled.
But I'll address that separately there.

> 
> 	Andrew

Thanks,
Vasilij


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

* Re: [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver
  2026-09-30 17:23     ` Vasilij Strassheim
@ 2026-09-30 18:20       ` Andrew Lunn
  0 siblings, 0 replies; 38+ messages in thread
From: Andrew Lunn @ 2026-09-30 18:20 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 30, 2026 at 07:23:38PM +0200, Vasilij Strassheim wrote:
> On Sat, 2026-09-26 at 01:10 +0200, Andrew Lunn wrote:
> > > diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
> > > index 048586746026..079b46dea25f 100644
> > > --- a/drivers/net/mdio/Makefile
> > > +++ b/drivers/net/mdio/Makefile
> > > @@ -23,6 +23,7 @@ obj-$(CONFIG_MDIO_OCTEON)		+= mdio-octeon.o
> > >  obj-$(CONFIG_MDIO_PIC64HPSC)		+= mdio-pic64hpsc.o
> > >  obj-$(CONFIG_MDIO_REALTEK_RTL9300)	+= mdio-realtek-rtl9300.o
> > >  obj-$(CONFIG_MDIO_REGMAP)		+= mdio-regmap.o
> > > +obj-$(CONFIG_MDIO_SOCE)			+= mdio-soce.o
> > >  obj-$(CONFIG_MDIO_SUN4I)		+= mdio-sun4i.o
> > 
> > Maybe the indentation is wrong here?
> 
> This looks fine in code.

Sometimes happens with tabs that the patch looks wrong, but is in fact
correct.

Thanks for checking.

	Andrew

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

* Re: [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller
  2026-09-30 17:13         ` Vasilij Strassheim
@ 2026-09-30 18:24           ` Andrew Lunn
  0 siblings, 0 replies; 38+ messages in thread
From: Andrew Lunn @ 2026-09-30 18:24 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 30, 2026 at 07:13:35PM +0200, Vasilij Strassheim wrote:
> On Wed, 2026-09-30 at 17:14 +0200, Andrew Lunn wrote:
> > On Wed, Sep 30, 2026 at 04:00:56PM +0200, Vasilij Strassheim wrote:
> > > On Sat, 2026-09-26 at 00:55 +0200, Andrew Lunn wrote:
> > > > > The controller exposes separate register regions for transaction data
> > > > > and for the shared transaction control and external bus selector
> > > > > register.
> > > > 
> > > > > +examples:
> > > > > +  - |
> > > > > +    mdio@204 {
> > > > > +        compatible = "soce,swip-mdio-23-02";
> > > > > +        reg = <0x204 0xc>, <0x200 0x4>;
> > > > 
> > > > At least in the example, they are not separate?
> > > 
> > > Not separate regions but registers...
> > > I'm obviously bad at documenting things.
> > > 
> > > The current information in the commit message is misleading and
> > > irrelevant. Looking at the bot's feedback, it's at the same time not
> > > clear enough yet that the mdio controller part is mapped within the
> > > switch memory and can't be used separately from it.
> > > 
> > > I will update the commit message to something like this:
> > > Add a binding for the MDIO controller integrated into SoC-e SWIP
> > > Ethernet switch IP cores.
> > > The controller shares the memory of the synthesized switch IP core and
> > > cannot be used independently.
> > 
> > I think part of the issue is the compatible. That suggests it is a
> > separate device, with its own driver. But it is actually driven by the
> > switch driver.
> 
> I can't avoid the compatible right now. Somehow it is still a separate
> functionality. I hope the following example doesn't cause unnecessary
> confusion, but rather helps to understand the system better:
> 
> The relationship between the MDIO controller and the switch core is more
> like that of tools in a Swiss Army knife.
> There are a few standalone tools, such as the knife and the screwdriver.
> These can also be described on their own. But they only make sense when
> they are attached to the knife as a whole. As soon as one takes it
> apart, the individual tools can no longer be used effectively. At the
> same time, no one needs to worry about the screwdriver if they only need
> the knife.

Maybe consider an MFD.

You then do get independent devices.

Also, with the current structure, i'm not sure how the mdio-mux is
getting instantiated. You list it inside the switch node, so i don't
think i will get turned into a platform device and probed. An MFD
might helper, maybe.

There are other switches which are described as MFD, so it is not
unknown.

      Andrew

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-30 18:15     ` Vasilij Strassheim
@ 2026-09-30 18:29       ` Andrew Lunn
  2026-09-30 18:49         ` Vasilij Strassheim
  0 siblings, 1 reply; 38+ messages in thread
From: Andrew Lunn @ 2026-09-30 18:29 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

> > Once all the setup is finished, what is the state of the switch?
> > 
> > What we want is that the user ports are isolated from each other, and
> > can only exchange frames with the CPU. That makes the hardware
> > basically a port expander, and you do bridging in software. Later
> > patches can then add offload of bridging, and whatever else the
> > hardware can do, which Linux can also do in software.
> 
> That is exactly the state I want to achieve after the setup.

Great. Just looking at the code, this is not obvious. Most switches
tend to default to all ports can talk to all ports, and you need to
tell it which port is the CPU port, and block everything else. So i
look for such code, and did not see anything here.

     Andrew

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

* Re: [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP
  2026-09-25 23:24   ` Andrew Lunn
@ 2026-09-30 18:29     ` Vasilij Strassheim
  2026-09-30 18:41       ` Andrew Lunn
  0 siblings, 1 reply; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 18:29 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:24 +0200, Andrew Lunn wrote:
> On Wed, Sep 23, 2026 at 12:39:35PM +0200, Vasilij Strassheim wrote:
> > The driver does not implement hardware STP offloading yet.
> 
> What exactly do you mean by STP offload? Can it do the actual
> protocol? No DSA switch can do that, we always use the software
> implementation. I'm not even sure it is possible to offload the actual
> protocol.

That's not a complete offload. The documentation says:
"The Spanning Tree protocols is supported as a mixed hardware and
software solution. In the hardware section, IP core is in charge of
forwarding BPDU frames to/from the CPU port and managing frames
according to the STP port states."
And:
"Spanning Tree (STP/RSTP/MSTP) algorithm runs in a software application
in the CPU. The CPU is in charge of configuring the switch port states
based on the processed BPDU frames."

There are a few registers for this. However, it's enough to just turn
off the feature and use only the software implementation in Linux.

I will update the documentation accordingly and stop using the term
“offloading” to avoid confusion.

> 
> 	Andrew

Thanks,
Vasilij

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

* Re: [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support
  2026-09-25 23:32   ` Andrew Lunn
@ 2026-09-30 18:32     ` Vasilij Strassheim
  0 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 18:32 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Sat, 2026-09-26 at 01:32 +0200, Andrew Lunn wrote:
> > The port and VID selectors are shared by all VLAN operations, so
> > serialize selector and data register sequences with a dedicated mutex.
> > Track member and untagged masks in software and restore the shadow state
> > if programming fails.
> 
> Looking at other features of the switch, how many different mutex are
> going to be needed? Is it better to have a single mutex which protects
> everything?

Yes, this all relates to configuration and is therefore not critical.
One mutex is sufficient and makes the code clearer.
I will change that.

> 
> 	Andrew

Thanks,
Vasilij

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

* Re: [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP
  2026-09-30 18:29     ` Vasilij Strassheim
@ 2026-09-30 18:41       ` Andrew Lunn
  0 siblings, 0 replies; 38+ messages in thread
From: Andrew Lunn @ 2026-09-30 18:41 UTC (permalink / raw)
  To: Vasilij Strassheim
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, Sep 30, 2026 at 08:29:37PM +0200, Vasilij Strassheim wrote:
> On Sat, 2026-09-26 at 01:24 +0200, Andrew Lunn wrote:
> > On Wed, Sep 23, 2026 at 12:39:35PM +0200, Vasilij Strassheim wrote:
> > > The driver does not implement hardware STP offloading yet.
> > 
> > What exactly do you mean by STP offload? Can it do the actual
> > protocol? No DSA switch can do that, we always use the software
> > implementation. I'm not even sure it is possible to offload the actual
> > protocol.
> 
> That's not a complete offload. The documentation says:
> "The Spanning Tree protocols is supported as a mixed hardware and
> software solution. In the hardware section, IP core is in charge of
> forwarding BPDU frames to/from the CPU port and managing frames
> according to the STP port states."
> And:
> "Spanning Tree (STP/RSTP/MSTP) algorithm runs in a software application
> in the CPU. The CPU is in charge of configuring the switch port states
> based on the processed BPDU frames."

This sounds about normal, nothing special. The switch has to be
involved, since it needs to pass BPDU frames when the port is in
blocked state, etc.

> There are a few registers for this. However, it's enough to just turn
> off the feature and use only the software implementation in Linux.

It is going to be more interesting when you get to offloading. If the
switch is not synthesised with this feature, you cannot offload
hardware bridging, it needs to stay in software.

	Andrew

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

* Re: [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores
  2026-09-30 18:29       ` Andrew Lunn
@ 2026-09-30 18:49         ` Vasilij Strassheim
  0 siblings, 0 replies; 38+ messages in thread
From: Vasilij Strassheim @ 2026-09-30 18:49 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Russell King, Andrew Lunn, Heiner Kallweit,
	devicetree, linux-kernel, netdev, Martin Kaistra,
	Benedikt Spranger

On Wed, 2026-09-30 at 20:29 +0200, Andrew Lunn wrote:
> > > Once all the setup is finished, what is the state of the switch?
> > > 
> > > What we want is that the user ports are isolated from each other, and
> > > can only exchange frames with the CPU. That makes the hardware
> > > basically a port expander, and you do bridging in software. Later
> > > patches can then add offload of bridging, and whatever else the
> > > hardware can do, which Linux can also do in software.
> > 
> > That is exactly the state I want to achieve after the setup.
> 
> Great. Just looking at the code, this is not obvious. Most switches
> tend to default to all ports can talk to all ports, and you need to
> tell it which port is the CPU port, and block everything else. So i
> look for such code, and did not see anything here.

The CPU port is set during synthesis of the jellyware. The driver must
therefore assume that it will be configured correctly in the DT.
There is only one read-only register that I could use to determine
which port has been configured as the CPU port. Until now, I thought it
wasn't worth checking.
I will take a closer look at that, maybe it is worth to reduce any
assumptions.

> 
>      Andrew

Thanks,
Vasilij

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

end of thread, other threads:[~2026-09-30 18:49 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 10:39 [PATCH net-next v3 0/8] net: dsa: Add SoC-e DSA driver Vasilij Strassheim
2026-09-23 10:39 ` [PATCH net-next v3 1/8] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim
2026-09-23 10:39 ` [PATCH net-next v3 2/8] dt-bindings: net: Add SoC-e SWIP MDIO controller Vasilij Strassheim
2026-09-25 22:55   ` Andrew Lunn
2026-09-30 14:00     ` Vasilij Strassheim
2026-09-30 15:14       ` Andrew Lunn
2026-09-30 17:13         ` Vasilij Strassheim
2026-09-30 18:24           ` Andrew Lunn
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 3/8] dt-bindings: net: dsa: Add SoC-e SWIP switch Vasilij Strassheim
2026-09-25 23:05   ` Andrew Lunn
2026-09-30 17:16     ` Vasilij Strassheim
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 4/8] net: dsa: Add tag handling for SoC-e switches Vasilij Strassheim
     [not found]   ` <20260924104003.A49F31F000FF@smtp.kernel.org>
2026-09-25 12:46     ` Vasilij Strassheim
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 5/8] net: mdio: Add SoC-e SWIP MDIO controller driver Vasilij Strassheim
2026-09-25 23:10   ` Andrew Lunn
2026-09-30 17:23     ` Vasilij Strassheim
2026-09-30 18:20       ` Andrew Lunn
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 6/8] net: dsa: soce: Add basic support for SoC-e switch IP cores Vasilij Strassheim
2026-09-25 23:17   ` Andrew Lunn
2026-09-30 17:26     ` Vasilij Strassheim
2026-09-25 23:20   ` Andrew Lunn
2026-09-30 18:15     ` Vasilij Strassheim
2026-09-30 18:29       ` Andrew Lunn
2026-09-30 18:49         ` Vasilij Strassheim
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 7/8] net: dsa: soce: Add VLAN offload support Vasilij Strassheim
2026-09-25 23:32   ` Andrew Lunn
2026-09-30 18:32     ` Vasilij Strassheim
2026-09-27 12:28   ` netdev-bot+sashiko
2026-09-23 10:39 ` [PATCH net-next v3 8/8] net: dsa: soce: Disable unsupported hardware STP Vasilij Strassheim
2026-09-25 23:24   ` Andrew Lunn
2026-09-30 18:29     ` Vasilij Strassheim
2026-09-30 18:41       ` Andrew Lunn
2026-09-27 12:28   ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®