* [PATCH 0/4] net: dsa: Add SoC-e DSA driver
@ 2026-07-29 16:36 Vasilij Strassheim
2026-07-29 16:36 ` [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim
` (4 more replies)
0 siblings, 5 replies; 18+ messages in thread
From: Vasilij Strassheim @ 2026-07-29 16:36 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
Cc: devicetree, linux-kernel, netdev, Martin Kaistra,
Vasilij Strassheim
Add initial support for the DSA driver for SoC-e FPGA-based (IP core)
Ethernet switches.
This first functional driver baseline is kept focused on Managed
Redundant Switch (MRS) which is also runtime-tested (25.01 IP using
24.01 layout) on ZynqMP hardware. It includes basic support for
networking (STP, FDB and Phylink), but not the IEEE 802.1Q VLAN tagging.
This will likely be added in the future.
This work is based on non-publicly available SoC-e DSA driver code, with
permission from the company to contribute it under different authorship.
There are different variants of the SoC-e switch IP, which can be
additionally configured at runtime with proprietary userspace software.
All variants share the same DSA features with a few differences in
register layout. Additional SoC-e switch variants might be introduced
later in separate patch series.
Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de>
---
Vasilij Strassheim (4):
dt-bindings: vendor-prefixes: Add soce
dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings
net: dsa: Add support for SoC-e SDSA tags
net: dsa: soce: Add initial driver support for MRS switches
.../bindings/net/dsa/soce,switch-dsa.yaml | 195 +++++++++++
.../bindings/net/dsa/soce,switch-ip.yaml | 54 +++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/net/dsa/Kconfig | 2 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/soce/Kconfig | 12 +
drivers/net/dsa/soce/Makefile | 6 +
drivers/net/dsa/soce/soce_common.c | 159 +++++++++
drivers/net/dsa/soce/soce_common.h | 36 ++
drivers/net/dsa/soce/soce_dsa.h | 41 +++
drivers/net/dsa/soce/soce_dsa_core.c | 354 +++++++++++++++++++
drivers/net/dsa/soce/soce_mdio.c | 381 +++++++++++++++++++++
drivers/net/dsa/soce/soce_mdio.h | 40 +++
drivers/net/dsa/soce/soce_mrs.h | 21 ++
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +
net/dsa/Makefile | 1 +
net/dsa/tag_sdsa.c | 102 ++++++
18 files changed, 1415 insertions(+)
---
base-commit: 9fb3b84c9577707db14e633ea3c57048380bf822
change-id: 20260729-devel-vstrassheim-soce-dsa-ml-20d6a5adb838
Best regards,
--
Vasilij Strassheim <v.strassheim@linutronix.de>
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim @ 2026-07-29 16:36 ` Vasilij Strassheim 2026-07-30 8:50 ` Krzysztof Kozlowski 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim ` (3 subsequent siblings) 4 siblings, 1 reply; 18+ messages in thread From: Vasilij Strassheim @ 2026-07-29 16:36 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 Cc: devicetree, linux-kernel, netdev, Martin Kaistra, Vasilij Strassheim Add the soce vendor prefix for System-On-Chip Engineering, S.L. This prefix is used by the new SoC-e Ethernet switch DT bindings. Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de> --- 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 396044f368e7..cf7eefff4230 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1553,6 +1553,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 related [flat|nested] 18+ messages in thread
* Re: [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce 2026-07-29 16:36 ` [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim @ 2026-07-30 8:50 ` Krzysztof Kozlowski 0 siblings, 0 replies; 18+ messages in thread From: Krzysztof Kozlowski @ 2026-07-30 8:50 UTC (permalink / raw) To: Vasilij Strassheim Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King, devicetree, linux-kernel, netdev, Martin Kaistra On Wed, Jul 29, 2026 at 06:36:54PM +0200, Vasilij Strassheim wrote: > Add the soce vendor prefix for System-On-Chip Engineering, S.L. > > This prefix is used by the new SoC-e Ethernet switch DT bindings. Drop, implied. You explain here things which are not obvious. You can also look at git history to see how this is done. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim 2026-07-29 16:36 ` [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim @ 2026-07-29 16:36 ` Vasilij Strassheim 2026-07-29 17:11 ` Andrew Lunn ` (4 more replies) 2026-07-29 16:36 ` [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags Vasilij Strassheim ` (2 subsequent siblings) 4 siblings, 5 replies; 18+ messages in thread From: Vasilij Strassheim @ 2026-07-29 16:36 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 Cc: devicetree, linux-kernel, netdev, Martin Kaistra, Vasilij Strassheim Add DT schema documentation for SoC-e FPGA switch devices. The binding is split into: - a switch IP node describing the MMIO register block - a DSA MDIO node describing the DSA-facing switch instance Both nodes are linked through the soce,switch-ip phandle. Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de> --- .../bindings/net/dsa/soce,switch-dsa.yaml | 195 +++++++++++++++++++++ .../bindings/net/dsa/soce,switch-ip.yaml | 54 ++++++ 2 files changed, 249 insertions(+) diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml new file mode 100644 index 000000000000..a04e2838d14e --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/soce,switch-dsa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SoC-e DSA switch MDIO device + +maintainers: + - SoC-e <info@soc-e.com> + +description: | + This node is the MDIO-bus device entry for the SoC-e DSA switch driver. + It is a lightweight configuration node that lives on the MDIO bus of the + SoC Ethernet controller acting as the DSA conduit (CPU port). + + The switch hardware itself (MMIO register interface) is a separate IP core + described by a companion node with one of the SoC-e compatible strings, + for example: + - soce,mrs-<version> + See the consolidated IP-core binding: + - Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml + The two nodes are linked via the "soce,switch-ip" phandle property. + + Port sub-nodes follow the DSA port binding documented in dsa-port.yaml. + User ports should reference PHY nodes through "phy-handle". These PHY nodes + live under the optional "mdio" child node of the switch and are exposed on + the switch's synthetic DSA user MDIO bus. + + The PHY-specific properties "soce,mdio-output" and "soce,phy-addr" describe + the MDIO bus index and hardware PHY address used by the on-chip MDIO master + of the switch IP core. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + const: soce,switch-dsa + + reg: + description: MDIO address of this device on the parent MDIO bus. + maxItems: 1 + + soce,switch-ip: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandle to the SoC-e switch IP core node that provides the MMIO register + window for this switch instance. The referenced node must carry a + compatible string documented in + Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml. + + dsa,member: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + description: + DSA cluster membership; see Documentation/devicetree/bindings/net/dsa/dsa.yaml. + + mdio: + $ref: /schemas/net/mdio.yaml# + description: | + Optional child MDIO bus exposing PHY devices attached to switch user + ports. PHY nodes referenced by port "phy-handle" properties should be + placed here. The PHY address seen by Linux on this bus is the switch port + index, not necessarily the hardware PHY address selected internally by + the switch IP. + unevaluatedProperties: false + + patternProperties: + "^ethernet-phy@[0-9a-f]+$": + type: object + $ref: /schemas/net/ethernet-phy.yaml# + unevaluatedProperties: false + description: + PHY attached to a switch user port through the synthetic switch MDIO + bus. + properties: + soce,mdio-output: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Zero-based index of the MDIO bus output of the switch IP core to + which this PHY is connected. + + soce,phy-addr: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Hardware PHY address on the MDIO bus selected by + "soce,mdio-output". + + required: + - soce,mdio-output + - soce,phy-addr + + ports: + type: object + description: Container node for the switch port sub-nodes. + properties: + '#address-cells': + const: 1 + '#size-cells': + const: 0 + + patternProperties: + "^port@[0-9a-f]+$": + $ref: dsa-port.yaml# + unevaluatedProperties: false + description: | + Switch port. In addition to the standard DSA port properties + (label, phy-mode, ethernet, fixed-link) the following + vendor-specific properties are used for data ports connected to + external PHYs. + + required: + - '#address-cells' + - '#size-cells' + + additionalProperties: false + +required: + - compatible + - reg + - soce,switch-ip + - ports + +additionalProperties: false + +examples: + - | + /* Excerpt from a Xilinx ZynqMP design (ARM64) */ + mdio { + #address-cells = <1>; + #size-cells = <0>; + + soce_switch: switch@10 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "soce,switch-dsa"; + reg = <0x10>; + soce,switch-ip = <&soce_switch_ip>; + dsa,member = <0 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0x0>; + label = "PORT_0"; + phy-handle = <&switchphy0>; + phy-mode = "rgmii-id"; + }; + + port@1 { + reg = <0x1>; + label = "PORT_1"; + phy-handle = <&switchphy1>; + phy-mode = "rgmii-id"; + }; + + port@2 { + reg = <0x2>; + label = "INTERNAL_PORT"; + ethernet = <&gem3>; + phy-mode = "gmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switchphy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x0>; + soce,mdio-output = <0x00000000>; + soce,phy-addr = <1>; + }; + + switchphy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + soce,mdio-output = <0x00000001>; + soce,phy-addr = <1>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml new file mode 100644 index 000000000000..ba9bb396235f --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/soce,switch-ip.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SoC-e Ethernet Switch IP core + +maintainers: + - SoC-e <info@soc-e.com> + +description: | + SoC-e Ethernet switch IP cores are configurable FPGA switch fabrics with + Layer-2 bridging features (including STP/MSTP state control and FDB + fast-age support). Some variants also provide TSN functionality. + + This binding describes only the MMIO switch IP core node. The Linux DSA + driver is bound via a companion MDIO node described in: + Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml. + +properties: + compatible: + enum: + - soce,mrs-21-01 + - soce,mrs-23-02 + - soce,mrs-24-01 + + reg: + description: | + MMIO base address and size of the switch register window. + On 64-bit platforms (ARM64), this is typically encoded as two-cell + address and two-cell size values. + maxItems: 1 + + soce,num-ports: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Total number of switch ports (external/user ports plus the CPU uplink + port). Must be greater than zero and not exceed the selected IP variant. + +required: + - compatible + - reg + - soce,num-ports + +additionalProperties: false + +examples: + - | + switch_ip: switch@80020000 { + compatible = "soce,mrs-24-01"; + reg = <0x0 0x80020000 0x0 0x10000>; + soce,num-ports = <3>; + }; -- 2.39.5 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim @ 2026-07-29 17:11 ` Andrew Lunn 2026-07-29 18:07 ` Rob Herring (Arm) ` (3 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Andrew Lunn @ 2026-07-29 17:11 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, devicetree, linux-kernel, netdev, Martin Kaistra > +description: | > + This node is the MDIO-bus device entry for the SoC-e DSA switch driver. > + It is a lightweight configuration node that lives on the MDIO bus of the > + SoC Ethernet controller acting as the DSA conduit (CPU port). Must it be the conduit interfaces MDIO bus? There are boards which exist using Marvell switches where the MDIO bus of the conduit interface is not a Linux MDIO bus, because it is an intel i210, and intel likes reinventing the wheel and does not use the Linux MDIO bus infrastructure. So the MDIO interface of the switch is connected to two GPIOs and a Linux bit bashing MDIO bus is used. > + soce,num-ports: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: | > + Total number of switch ports (external/user ports plus the CPU uplink > + port). Must be greater than zero and not exceed the selected IP variant. Why is this needed if we already have a description of the ports? Andrew ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim 2026-07-29 17:11 ` Andrew Lunn @ 2026-07-29 18:07 ` Rob Herring (Arm) 2026-07-30 8:56 ` Krzysztof Kozlowski ` (2 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Rob Herring (Arm) @ 2026-07-29 18:07 UTC (permalink / raw) To: Vasilij Strassheim Cc: Russell King, Paolo Abeni, netdev, Conor Dooley, Vladimir Oltean, Simon Horman, David S. Miller, Martin Kaistra, linux-kernel, Krzysztof Kozlowski, Andrew Lunn, Eric Dumazet, devicetree, Jakub Kicinski On Wed, 29 Jul 2026 18:36:55 +0200, Vasilij Strassheim wrote: > Add DT schema documentation for SoC-e FPGA switch devices. > The binding is split into: > - a switch IP node describing the MMIO register block > - a DSA MDIO node describing the DSA-facing switch instance > Both nodes are linked through the soce,switch-ip phandle. > > Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de> > --- > .../bindings/net/dsa/soce,switch-dsa.yaml | 195 +++++++++++++++++++++ > .../bindings/net/dsa/soce,switch-ip.yaml | 54 ++++++ > 2 files changed, 249 insertions(+) > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: Documentation/devicetree/bindings/net/dsa/soce,switch-ip.example.dtb: switch@80020000 (soce,mrs-24-01): reg: [[0, 2147614720], [0, 65536]] is too long from schema $id: http://devicetree.org/schemas/net/dsa/soce,switch-ip.yaml doc reference errors (make refcheckdocs): See https://patchwork.kernel.org/project/devicetree/patch/20260729-devel-vstrassheim-soce-dsa-ml-v1-2-be569dae1b20@linutronix.de The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim 2026-07-29 17:11 ` Andrew Lunn 2026-07-29 18:07 ` Rob Herring (Arm) @ 2026-07-30 8:56 ` Krzysztof Kozlowski 2026-07-30 8:56 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 4 siblings, 0 replies; 18+ messages in thread From: Krzysztof Kozlowski @ 2026-07-30 8:56 UTC (permalink / raw) To: Vasilij Strassheim Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King, devicetree, linux-kernel, netdev, Martin Kaistra On Wed, Jul 29, 2026 at 06:36:55PM +0200, Vasilij Strassheim wrote: > Add DT schema documentation for SoC-e FPGA switch devices. > The binding is split into: > - a switch IP node describing the MMIO register block > - a DSA MDIO node describing the DSA-facing switch instance > Both nodes are linked through the soce,switch-ip phandle. > > Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de> > --- > .../bindings/net/dsa/soce,switch-dsa.yaml | 195 +++++++++++++++++++++ > .../bindings/net/dsa/soce,switch-ip.yaml | 54 ++++++ > 2 files changed, 249 insertions(+) > > diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml > new file mode 100644 > index 000000000000..a04e2838d14e > --- /dev/null > +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml > @@ -0,0 +1,195 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/net/dsa/soce,switch-dsa.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: SoC-e DSA switch MDIO device > + > +maintainers: > + - SoC-e <info@soc-e.com> Nope, as explained more than once group boxes are not accepted. > + > +description: | > + This node is the MDIO-bus device entry for the SoC-e DSA switch driver. Node is not for a driver. Explain the hardware, not drivers, not DTS. > + It is a lightweight configuration node that lives on the MDIO bus of the > + SoC Ethernet controller acting as the DSA conduit (CPU port). > + > + The switch hardware itself (MMIO register interface) is a separate IP core > + described by a companion node with one of the SoC-e compatible strings, > + for example: > + - soce,mrs-<version> How is this relevant here? do you describe this compatible? > + See the consolidated IP-core binding: > + - Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml > + The two nodes are linked via the "soce,switch-ip" phandle property. > + > + Port sub-nodes follow the DSA port binding documented in dsa-port.yaml. > + User ports should reference PHY nodes through "phy-handle". These PHY nodes > + live under the optional "mdio" child node of the switch and are exposed on > + the switch's synthetic DSA user MDIO bus. > + > + The PHY-specific properties "soce,mdio-output" and "soce,phy-addr" describe > + the MDIO bus index and hardware PHY address used by the on-chip MDIO master > + of the switch IP core. > + > +properties: > + '#address-cells': > + const: 1 > + > + '#size-cells': > + const: 0 You need to follow DTS coding style. > + > + compatible: > + const: soce,switch-dsa Way too generic. I understand that SoCe will NEVER - and you certify that - develop a second, different "switch-dsa" model and they call this one like this? > + > + reg: > + description: MDIO address of this device on the parent MDIO bus. Drop. > + maxItems: 1 > + > + soce,switch-ip: > + $ref: /schemas/types.yaml#/definitions/phandle > + description: | > + Phandle to the SoC-e switch IP core node that provides the MMIO register > + window for this switch instance. The referenced node must carry a > + compatible string documented in > + Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml. > + > + dsa,member: > + $ref: /schemas/types.yaml#/definitions/uint32-array Why do you need to redefine existing property? > + minItems: 2 > + maxItems: 2 > + description: > + DSA cluster membership; see Documentation/devicetree/bindings/net/dsa/dsa.yaml. Drop. Do not redefine properties. See other bindings how this is done. > + > + mdio: > + $ref: /schemas/net/mdio.yaml# > + description: | > + Optional child MDIO bus exposing PHY devices attached to switch user > + ports. PHY nodes referenced by port "phy-handle" properties should be > + placed here. The PHY address seen by Linux on this bus is the switch port > + index, not necessarily the hardware PHY address selected internally by > + the switch IP. > + unevaluatedProperties: false > + > + patternProperties: > + "^ethernet-phy@[0-9a-f]+$": Use consistent quotes. > + type: object > + $ref: /schemas/net/ethernet-phy.yaml# > + unevaluatedProperties: false > + description: > + PHY attached to a switch user port through the synthetic switch MDIO > + bus. > + properties: > + soce,mdio-output: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: | > + Zero-based index of the MDIO bus output of the switch IP core to > + which this PHY is connected. > + > + soce,phy-addr: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: | > + Hardware PHY address on the MDIO bus selected by > + "soce,mdio-output". > + > + required: > + - soce,mdio-output > + - soce,phy-addr > + > + ports: > + type: object > + description: Container node for the switch port sub-nodes. > + properties: > + '#address-cells': > + const: 1 > + '#size-cells': > + const: 0 > + > + patternProperties: > + "^port@[0-9a-f]+$": > + $ref: dsa-port.yaml# > + unevaluatedProperties: false > + description: | > + Switch port. In addition to the standard DSA port properties > + (label, phy-mode, ethernet, fixed-link) the following > + vendor-specific properties are used for data ports connected to > + external PHYs. > + > + required: > + - '#address-cells' > + - '#size-cells' > + > + additionalProperties: false > + > +required: > + - compatible > + - reg > + - soce,switch-ip > + - ports > + > +additionalProperties: false > + > +examples: > + - | > + /* Excerpt from a Xilinx ZynqMP design (ARM64) */ > + mdio { > + #address-cells = <1>; > + #size-cells = <0>; > + > + soce_switch: switch@10 { > + #address-cells = <1>; > + #size-cells = <0>; > + compatible = "soce,switch-dsa"; > + reg = <0x10>; > + soce,switch-ip = <&soce_switch_ip>; > + dsa,member = <0 0>; > + > + ports { > + #address-cells = <1>; > + #size-cells = <0>; > + > + port@0 { > + reg = <0x0>; > + label = "PORT_0"; > + phy-handle = <&switchphy0>; > + phy-mode = "rgmii-id"; > + }; > + > + port@1 { > + reg = <0x1>; > + label = "PORT_1"; > + phy-handle = <&switchphy1>; > + phy-mode = "rgmii-id"; > + }; > + > + port@2 { > + reg = <0x2>; > + label = "INTERNAL_PORT"; > + ethernet = <&gem3>; > + phy-mode = "gmii"; > + fixed-link { > + speed = <1000>; > + full-duplex; > + }; > + }; > + }; > + > + mdio { > + #address-cells = <1>; > + #size-cells = <0>; > + > + switchphy0: ethernet-phy@0 { > + compatible = "ethernet-phy-ieee802.3-c22"; > + reg = <0x0>; > + soce,mdio-output = <0x00000000>; > + soce,phy-addr = <1>; > + }; > + > + switchphy1: ethernet-phy@1 { > + compatible = "ethernet-phy-ieee802.3-c22"; > + reg = <0x1>; > + soce,mdio-output = <0x00000001>; > + soce,phy-addr = <1>; > + }; > + }; > + }; > + }; > diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml > new file mode 100644 > index 000000000000..ba9bb396235f > --- /dev/null > +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml Filename must match compatible. > @@ -0,0 +1,54 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/net/dsa/soce,switch-ip.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: SoC-e Ethernet Switch IP core > + > +maintainers: > + - SoC-e <info@soc-e.com> > + > +description: | > + SoC-e Ethernet switch IP cores are configurable FPGA switch fabrics with > + Layer-2 bridging features (including STP/MSTP state control and FDB > + fast-age support). Some variants also provide TSN functionality. > + > + This binding describes only the MMIO switch IP core node. The Linux DSA > + driver is bound via a companion MDIO node described in: > + Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml. > + > +properties: > + compatible: > + enum: > + - soce,mrs-21-01 > + - soce,mrs-23-02 > + - soce,mrs-24-01 > + > + reg: > + description: | > + MMIO base address and size of the switch register window. > + On 64-bit platforms (ARM64), this is typically encoded as two-cell > + address and two-cell size values. Pointless. Please open any other existing binding. > + maxItems: 1 > + > + soce,num-ports: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: | > + Total number of switch ports (external/user ports plus the CPU uplink > + port). Must be greater than zero and not exceed the selected IP variant. Either implied by compatible or not properly placed. Looks like propertt of the DSA. > + > +required: > + - compatible > + - reg > + - soce,num-ports > + > +additionalProperties: false > + > +examples: > + - | > + switch_ip: switch@80020000 { ethernet-switch And drop unused label. Considering this wasn't tested I should not even give here review... Best regards, Krzysztof ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim ` (2 preceding siblings ...) 2026-07-30 8:56 ` Krzysztof Kozlowski @ 2026-07-30 8:56 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 4 siblings, 0 replies; 18+ messages in thread From: Krzysztof Kozlowski @ 2026-07-30 8:56 UTC (permalink / raw) To: Vasilij Strassheim Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King, devicetree, linux-kernel, netdev, Martin Kaistra On Wed, Jul 29, 2026 at 06:36:55PM +0200, Vasilij Strassheim wrote: > Add DT schema documentation for SoC-e FPGA switch devices. > The binding is split into: > - a switch IP node describing the MMIO register block > - a DSA MDIO node describing the DSA-facing switch instance > Both nodes are linked through the soce,switch-ip phandle. A nit, subject: drop second/last, redundant "bindings". The "dt-bindings" prefix is already stating that these are bindings. See also: https://elixir.bootlin.com/linux/v7.1-rc7/source/Documentation/devicetree/bindings/submitting-patches.rst#L23 Best regards, Krzysztof ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim ` (3 preceding siblings ...) 2026-07-30 8:56 ` Krzysztof Kozlowski @ 2026-07-30 16:37 ` sashiko-bot 4 siblings, 0 replies; 18+ messages in thread From: sashiko-bot @ 2026-07-30 16:37 UTC (permalink / raw) To: Vasilij Strassheim; +Cc: conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [Medium] The `mdio` bus models a Linux software-synthetic topology rather than the actual hardware layout, misusing `reg` and inventing custom physical address properties. - [Medium] Setting `unevaluatedProperties: false` on the child `ethernet-phy` node breaks validation for vendor-specific PHY properties. - [Low] The device tree example in `soce,switch-dsa.yaml` fails to compile because it references undefined phandle labels (`&soce_switch_ip` and `&gem3`). - [Low] The device tree example in `soce,switch-ip.yaml` provides a 4-cell `reg` property without a parent node specifying `#address-cells` and `#size-cells`, leading to validation failures. - [Low] The `ports` sub-node pattern property description claims to define vendor-specific properties, but none are actually listed or defined. - [Low] Unnecessary `#address-cells` and `#size-cells` defined at the root node level, causing dtc warnings. - [Low] The `soce,num-ports` property is missing constraints that are explicitly stated in its description. -- --- Patch [2]: [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings --- commit 28c590589436788e107ab82bf7fbd0fdcad40087 Author: Vasilij Strassheim <v.strassheim@linutronix.de> Subject: dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings This commit adds DT schema documentation for SoC-e FPGA switch devices. The binding is split into a switch IP node describing the MMIO register block and a DSA MDIO node describing the DSA-facing switch instance. > diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml > new file mode 100644 > index 0000000000000..a04e2838d14eb > --- /dev/null > +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-dsa.yaml > @@ -0,0 +1,195 @@ [ ... ] > +properties: > + '#address-cells': > + const: 1 > + > + '#size-cells': > + const: 0 [Severity: Low] Are #address-cells and #size-cells necessary at the root node level? Since the only allowed sub-nodes (ports and mdio) do not use a @ unit-address, compiling device trees conforming to this schema might emit avoid_unnecessary_addr_size dtc warnings. [ ... ] > + mdio: > + $ref: /schemas/net/mdio.yaml# > + description: | > + Optional child MDIO bus exposing PHY devices attached to switch user > + ports. PHY nodes referenced by port "phy-handle" properties should be > + placed here. The PHY address seen by Linux on this bus is the switch port > + index, not necessarily the hardware PHY address selected internally by > + the switch IP. [Severity: Medium] Does this model a Linux software-synthetic topology rather than the actual hardware layout? Device tree bindings are expected to describe the hardware, rather than modeling internal software abstractions like Linux's synthetic MDIO bus mapping. Using custom properties for the hardware bus index instead of natural mdio subnodes might violate core DT design principles. > + unevaluatedProperties: false > + > + patternProperties: > + "^ethernet-phy@[0-9a-f]+$": > + type: object > + $ref: /schemas/net/ethernet-phy.yaml# > + unevaluatedProperties: false [Severity: Medium] Will setting unevaluatedProperties: false on the child ethernet-phy node break validation for valid vendor-specific PHY properties? Device trees using specific PHY hardware with valid vendor properties (like ti,rx-internal-delay) might fail schema validation because the parent schema strictly forbids them. [ ... ] > + ports: > + type: object > + description: Container node for the switch port sub-nodes. > + properties: > + '#address-cells': > + const: 1 > + '#size-cells': > + const: 0 > + > + patternProperties: > + "^port@[0-9a-f]+$": > + $ref: dsa-port.yaml# > + unevaluatedProperties: false > + description: | > + Switch port. In addition to the standard DSA port properties > + (label, phy-mode, ethernet, fixed-link) the following > + vendor-specific properties are used for data ports connected to > + external PHYs. [Severity: Low] Could this description be updated? It claims to define vendor-specific properties used for data ports connected to external PHYs, but the description abruptly ends and no such properties are actually listed or defined. [ ... ] > +examples: > + - | > + /* Excerpt from a Xilinx ZynqMP design (ARM64) */ > + mdio { > + #address-cells = <1>; > + #size-cells = <0>; > + > + soce_switch: switch@10 { > + #address-cells = <1>; > + #size-cells = <0>; > + compatible = "soce,switch-dsa"; > + reg = <0x10>; > + soce,switch-ip = <&soce_switch_ip>; [Severity: Low] Does this example fail to compile? The phandle label &soce_switch_ip does not appear to be defined in this example snippet, which might cause dt_binding_check to fail when dtc attempts to resolve it. [ ... ] > + port@2 { > + reg = <0x2>; > + label = "INTERNAL_PORT"; > + ethernet = <&gem3>; [Severity: Low] Similar to &soce_switch_ip, the &gem3 ethernet controller label also appears to be undefined in this example. > diff --git a/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml > new file mode 100644 > index 0000000000000..ba9bb396235f2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/net/dsa/soce,switch-ip.yaml > @@ -0,0 +1,54 @@ [ ... ] > + soce,num-ports: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: | > + Total number of switch ports (external/user ports plus the CPU uplink > + port). Must be greater than zero and not exceed the selected IP variant. [Severity: Low] Should there be explicit JSON schema constraints (such as minimum: 1) added for this property? The description mentions it must be greater than zero, but invalid device trees specifying soce,num-ports = <0>; might pass validation without the constraint. [ ... ] > +examples: > + - | > + switch_ip: switch@80020000 { > + compatible = "soce,mrs-24-01"; > + reg = <0x0 0x80020000 0x0 0x10000>; [Severity: Low] Will this 4-cell reg property cause a validation failure? The implicit parent provides #address-cells = <1>, so this might parse as 2 items and exceed the maxItems: 1 constraint. It likely requires a parent node (e.g., a bus node) defining #address-cells = <2> and #size-cells = <2>. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260729-devel-vstrassheim-soce-dsa-ml-v1-0-be569dae1b20@linutronix.de?part=2 ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim 2026-07-29 16:36 ` [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim @ 2026-07-29 16:36 ` Vasilij Strassheim 2026-07-29 17:22 ` Andrew Lunn 2026-07-30 16:37 ` sashiko-bot 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim 2026-07-29 16:56 ` [PATCH 0/4] net: dsa: Add SoC-e DSA driver Andrew Lunn 4 siblings, 2 replies; 18+ messages in thread From: Vasilij Strassheim @ 2026-07-29 16:36 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 Cc: devicetree, linux-kernel, netdev, Martin Kaistra, Vasilij Strassheim Add a DSA tagger for the SoC-e SDSA header format used between the conduit and SoC-e switch ports. The implementation adds TX tag insertion and RX tag parsing and registers a new DSA tag protocol. 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 | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 8c16ef23cc10..744bbb4d54ea 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -59,6 +59,7 @@ struct tc_action; #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31 #define DSA_TAG_PROTO_MXL862_VALUE 32 #define DSA_TAG_PROTO_NETC_VALUE 33 +#define DSA_TAG_PROTO_SDSA_VALUE 34 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -95,6 +96,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE, DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE, + DSA_TAG_PROTO_SDSA = DSA_TAG_PROTO_SDSA_VALUE, }; struct dsa_switch; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index d5e725b90d78..1e1b0f2fb726 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -98,6 +98,12 @@ config NET_DSA_TAG_EDSA Say Y or M if you want to enable support for tagging frames for the Marvell switches which use EtherType DSA headers. +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_MTK tristate "Tag driver for Mediatek switches" help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index b8c2667cd14a..dc154f10e0a0 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -23,6 +23,7 @@ dsa_core-y += \ obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o +obj-$(CONFIG_NET_DSA_TAG_SDSA) += tag_sdsa.o obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c new file mode 100644 index 000000000000..b32dca1e367c --- /dev/null +++ b/net/dsa/tag_sdsa.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#include <linux/bitops.h> +#include <linux/etherdevice.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_MASK GENMASK(7, 3) + +static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_port *dp = dsa_user_to_port(dev); + u8 *sdsa_header; + + if (skb_cow_head(skb, SDSA_HLEN) < 0) + return NULL; + + skb_push(skb, SDSA_HLEN); + dsa_alloc_etype_header(skb, SDSA_HLEN); + + /* Construct the FROM_CPU DSA tag. */ + sdsa_header = dsa_etype_header_pos_tx(skb); + sdsa_header[0] = (ETH_P_SDSA >> 8) & 0xff; + sdsa_header[1] = ETH_P_SDSA & 0xff; + sdsa_header[2] = 0x00; /* reserved */ + sdsa_header[3] = 0x00; /* reserved */ + sdsa_header[4] = FIELD_PREP(SDSA_TAG_FRAME_TYPE_MASK, 1) | + FIELD_PREP(SDSA_TAG_PORT_HI_MASK, dp->index >> 5); + sdsa_header[5] = FIELD_PREP(SDSA_TAG_PORT_MASK, dp->index); + sdsa_header[6] = 0x00; /* VLAN not supported */ + sdsa_header[7] = 0x00; /* VLAN not supported */ + + return skb; +} + +static struct sk_buff *sdsa_rcv(struct sk_buff *skb, struct net_device *dev) +{ + u8 *sdsa_header; + int source_port; + u8 frame_type; + + if (unlikely(!pskb_may_pull(skb, SDSA_HLEN))) + return NULL; + + sdsa_header = dsa_etype_header_pos_rx(skb); + + /* Check that the frame type is TO_CPU. */ + frame_type = FIELD_GET(SDSA_TAG_FRAME_TYPE_MASK, sdsa_header[4]); + if (frame_type != 0) + return NULL; + + /* VLAN-tagged traffic is intentionally unsupported yet. */ + if (sdsa_header[4] & SDSA_TAG_VLAN_BIT) + return NULL; + + /* Determine the source port from the two port fields. */ + source_port = ((sdsa_header[4] & 0x1f) << 5) | + ((sdsa_header[5] & 0xf8) >> 3); + + skb->dev = dsa_conduit_find_user(dev, 0, source_port); + if (!skb->dev) + return NULL; + + skb_pull_rcsum(skb, SDSA_HLEN); + dsa_strip_etype_header(skb, SDSA_HLEN); + + dsa_default_offload_fwd_mark(skb); + return skb; +} + +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 related [flat|nested] 18+ messages in thread
* Re: [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags 2026-07-29 16:36 ` [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags Vasilij Strassheim @ 2026-07-29 17:22 ` Andrew Lunn 2026-07-30 16:37 ` sashiko-bot 1 sibling, 0 replies; 18+ messages in thread From: Andrew Lunn @ 2026-07-29 17:22 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, devicetree, linux-kernel, netdev, Martin Kaistra > @@ -98,6 +98,12 @@ config NET_DSA_TAG_EDSA > Say Y or M if you want to enable support for tagging frames for the > Marvell switches which use EtherType DSA headers. > > +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. > + These entries are sorted, so it probably should be between NET_DSA_TAG_RZN1_A5PSW and NET_DSA_TAG_LAN9303. > @@ -23,6 +23,7 @@ dsa_core-y += \ > obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o > obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o > obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o > +obj-$(CONFIG_NET_DSA_TAG_SDSA) += tag_sdsa.o > obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o Also sorted, and this is the wrong spot. > +#define SDSA_HLEN 8 > + > +#define SDSA_NAME "sdsa" Does SDSA mean anything? Or have you taken net/dsa/tag_dsa.c, and just changed edsa to sdsa? > +static struct sk_buff *sdsa_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct dsa_port *dp = dsa_user_to_port(dev); > + u8 *sdsa_header; > + > + if (skb_cow_head(skb, SDSA_HLEN) < 0) > + return NULL; > + > + skb_push(skb, SDSA_HLEN); > + dsa_alloc_etype_header(skb, SDSA_HLEN); > + > + /* Construct the FROM_CPU DSA tag. */ Is FROM_CPU a concept for this device? Are there other types of tag? > + sdsa_header = dsa_etype_header_pos_tx(skb); > + sdsa_header[0] = (ETH_P_SDSA >> 8) & 0xff; > + sdsa_header[1] = ETH_P_SDSA & 0xff; > + sdsa_header[2] = 0x00; /* reserved */ > + sdsa_header[3] = 0x00; /* reserved */ > + sdsa_header[4] = FIELD_PREP(SDSA_TAG_FRAME_TYPE_MASK, 1) | > + FIELD_PREP(SDSA_TAG_PORT_HI_MASK, dp->index >> 5); > + sdsa_header[5] = FIELD_PREP(SDSA_TAG_PORT_MASK, dp->index); > + sdsa_header[6] = 0x00; /* VLAN not supported */ > + sdsa_header[7] = 0x00; /* VLAN not supported */ > + > + return skb; > +} > + > +static struct sk_buff *sdsa_rcv(struct sk_buff *skb, struct net_device *dev) > +{ > + u8 *sdsa_header; > + int source_port; > + u8 frame_type; > + > + if (unlikely(!pskb_may_pull(skb, SDSA_HLEN))) > + return NULL; > + > + sdsa_header = dsa_etype_header_pos_rx(skb); > + > + /* Check that the frame type is TO_CPU. */ > + frame_type = FIELD_GET(SDSA_TAG_FRAME_TYPE_MASK, sdsa_header[4]); > + if (frame_type != 0) #define for TO_CPU? Andrew ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags 2026-07-29 16:36 ` [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags Vasilij Strassheim 2026-07-29 17:22 ` Andrew Lunn @ 2026-07-30 16:37 ` sashiko-bot 1 sibling, 0 replies; 18+ messages in thread From: sashiko-bot @ 2026-07-30 16:37 UTC (permalink / raw) To: Vasilij Strassheim; +Cc: robh, devicetree, conor+dt Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] The `sdsa_xmit` and `sdsa_rcv` functions leak the SKB on all error paths by returning `NULL` without freeing the buffer, leading to memory exhaustion. -- --- Patch [3]: [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags --- commit d07af353633eb9eecc24042cfdf75f9ecf082368 Author: Vasilij Strassheim <v.strassheim@linutronix.de> net: dsa: Add support for SoC-e SDSA tags Adds a DSA tagger for the SoC-e SDSA header format used between the conduit and SoC-e switch ports. > diff --git a/net/dsa/tag_sdsa.c b/net/dsa/tag_sdsa.c > new file mode 100644 > --- /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); > + u8 *sdsa_header; > + > + if (skb_cow_head(skb, SDSA_HLEN) < 0) > + return NULL; [Severity: High] Does this leak the skb when skb_cow_head() fails? The DSA core expects the tagger to consume and free the skb if it returns NULL on transmission. Should this call kfree_skb() before returning? [ ... ] > +static struct sk_buff *sdsa_rcv(struct sk_buff *skb, struct net_device *dev) > +{ > + u8 *sdsa_header; > + int source_port; > + u8 frame_type; > + > + if (unlikely(!pskb_may_pull(skb, SDSA_HLEN))) > + return NULL; [Severity: High] Does this error path leak the skb? If a malformed packet causes pskb_may_pull() to fail, returning NULL without freeing the buffer could allow network traffic to cause memory exhaustion. > + > + sdsa_header = dsa_etype_header_pos_rx(skb); > + > + /* Check that the frame type is TO_CPU. */ > + frame_type = FIELD_GET(SDSA_TAG_FRAME_TYPE_MASK, sdsa_header[4]); > + if (frame_type != 0) > + return NULL; [Severity: High] Should the skb be freed here when the frame type check fails to avoid a memory leak? > + > + /* VLAN-tagged traffic is intentionally unsupported yet. */ > + if (sdsa_header[4] & SDSA_TAG_VLAN_BIT) > + return NULL; [Severity: High] Could unsupported VLAN-tagged traffic trigger a memory leak here? > + > + /* Determine the source port from the two port fields. */ > + source_port = ((sdsa_header[4] & 0x1f) << 5) | > + ((sdsa_header[5] & 0xf8) >> 3); > + > + skb->dev = dsa_conduit_find_user(dev, 0, source_port); > + if (!skb->dev) > + return NULL; [Severity: High] Does this also leak the skb if the conduit cannot find a corresponding user device? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260729-devel-vstrassheim-soce-dsa-ml-v1-0-be569dae1b20@linutronix.de?part=3 ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim ` (2 preceding siblings ...) 2026-07-29 16:36 ` [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags Vasilij Strassheim @ 2026-07-29 16:36 ` Vasilij Strassheim 2026-07-29 17:31 ` Andrew Lunn ` (3 more replies) 2026-07-29 16:56 ` [PATCH 0/4] net: dsa: Add SoC-e DSA driver Andrew Lunn 4 siblings, 4 replies; 18+ messages in thread From: Vasilij Strassheim @ 2026-07-29 16:36 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 Cc: devicetree, linux-kernel, netdev, Martin Kaistra, Vasilij Strassheim Add initial support for SoC-e switches that are based on FPGA IP cores. This driver provides MDIO access helpers for supported IP register layouts and common STP/FDB handling. Managed Redundant Switch (MRS) variant is introduced first with variant descriptors and register layout mapping. The implementation has been designed so that additional variants can be added in the future with minimal effort. Tested on SoC-e MRS 25.01 hardware using the 24.01 register layout. Signed-off-by: Vasilij Strassheim <v.strassheim@linutronix.de> --- drivers/net/dsa/Kconfig | 2 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/soce/Kconfig | 12 ++ drivers/net/dsa/soce/Makefile | 6 + drivers/net/dsa/soce/soce_common.c | 159 +++++++++++++++ drivers/net/dsa/soce/soce_common.h | 36 ++++ drivers/net/dsa/soce/soce_dsa.h | 41 ++++ drivers/net/dsa/soce/soce_dsa_core.c | 354 ++++++++++++++++++++++++++++++++ drivers/net/dsa/soce/soce_mdio.c | 381 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/soce/soce_mdio.h | 40 ++++ drivers/net/dsa/soce/soce_mrs.h | 21 ++ 11 files changed, 1053 insertions(+) diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 4ab567c5bbaf..6bf5fd48599b 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -82,6 +82,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 d2975badffc0..624b453f576a 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..97d4e4e838d9 --- /dev/null +++ b/drivers/net/dsa/soce/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config NET_DSA_SOCE + tristate "Soce switches" + depends on NET_DSA + depends on OF + depends on HAS_IOMEM + select NET_DSA_TAG_SDSA + help + This enables support for switches based on IP core from SoC-E. + Frames are exchanged with the CPU port using the SDSA DSA tag protocol. + The driver supports different variants and versions of switches which + are configurable at runtime. diff --git a/drivers/net/dsa/soce/Makefile b/drivers/net/dsa/soce/Makefile new file mode 100644 index 000000000000..a6e74b7d57df --- /dev/null +++ b/drivers/net/dsa/soce/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_NET_DSA_SOCE) += soce_dsa.o +soce_dsa-objs := soce_dsa_core.o +soce_dsa-objs += soce_mdio.o +soce_dsa-objs += soce_common.o diff --git a/drivers/net/dsa/soce/soce_common.c b/drivers/net/dsa/soce/soce_common.c new file mode 100644 index 000000000000..f7f345a690e4 --- /dev/null +++ b/drivers/net/dsa/soce/soce_common.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ +#include <linux/if_bridge.h> +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> + +#include "soce_dsa.h" +#include "soce_mdio.h" +#include "soce_common.h" + +#define SOCE_FAST_AGE_POLL_US 10 +#define SOCE_FAST_AGE_TIMEOUT_US 100000 + +#define SOCE_STP_PORTS_PER_STATUS_REG 16 +#define SOCE_STP_PORT_STATE_WIDTH 2 +#define SOCE_STP_PORT_STATE_WRITE BIT(0) +#define SOCE_STP_MSTID_CIST 0 + +#define SOCE_STP_PORT_STATE_MASK GENMASK(SOCE_STP_PORT_STATE_WIDTH - 1, 0) + +#define SOCE_FDB_CLEAR_UNDER_PROCESS BIT(7) +#define SOCE_FDB_CLEAR_FLUSH_MSTID BIT(4) + +static u8 soce_map_stp_state(u8 state) +{ + switch (state) { + case BR_STATE_DISABLED: + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + return 0; + case BR_STATE_LEARNING: + return 1; + case BR_STATE_FORWARDING: + default: + return 3; + } +} + +static void soce_stp_state_set(struct dsa_switch *ds, int port, u8 state, + const struct soce_layout *layout) +{ + void __iomem *p_mstp_mstid, *p_mstp_ps_hi, *p_mstp_ps_lo, *p_mstp_pc; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + u32 ps_hi_n, ps_lo_n; + u8 stp_state; + + local = &priv->local; + if (!layout || !local->base_addr) + return; + + stp_state = soce_map_stp_state(state); + p_mstp_pc = local->base_addr + layout->mstp_port_state_control; + p_mstp_mstid = local->base_addr + layout->mstp_mstid; + p_mstp_ps_lo = local->base_addr + layout->mstp_port_state_lo; + p_mstp_ps_hi = local->base_addr + layout->mstp_port_state_hi; + + /* Keep the extra MSTP MSTID write taken from the vendor sequence. */ + writel(SOCE_STP_MSTID_CIST, p_mstp_mstid); + + ps_lo_n = readl(p_mstp_ps_lo); + ps_hi_n = readl(p_mstp_ps_hi); + + if (port >= SOCE_STP_PORTS_PER_STATUS_REG) { + port -= SOCE_STP_PORTS_PER_STATUS_REG; + ps_hi_n = + ((ps_hi_n & ~(SOCE_STP_PORT_STATE_MASK + << (port * SOCE_STP_PORT_STATE_WIDTH))) | + (stp_state << (port * SOCE_STP_PORT_STATE_WIDTH))); + } else { + ps_lo_n = + ((ps_lo_n & ~(SOCE_STP_PORT_STATE_MASK + << (port * SOCE_STP_PORT_STATE_WIDTH))) | + (stp_state << (port * SOCE_STP_PORT_STATE_WIDTH))); + } + + writel(SOCE_STP_MSTID_CIST, p_mstp_mstid); + writel(ps_lo_n, p_mstp_ps_lo); + writel(ps_hi_n, p_mstp_ps_hi); + writel(SOCE_STP_PORT_STATE_WRITE, p_mstp_pc); +} + +static void soce_fast_age(struct dsa_switch *ds, int port, + const struct soce_layout *layout) +{ + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + void __iomem *p_mes_ctrl; + u32 val; + + local = &priv->local; + if (!layout || !local->base_addr) + return; + + p_mes_ctrl = local->base_addr + layout->fdb_ctrl; + if (readl_poll_timeout(p_mes_ctrl, val, + !(val & SOCE_FDB_CLEAR_UNDER_PROCESS), + SOCE_FAST_AGE_POLL_US, SOCE_FAST_AGE_TIMEOUT_US)) + dev_warn(ds->dev, "SoC-e fast_age timeout on port %d\n", port); + + writel(SOCE_FDB_CLEAR_FLUSH_MSTID, p_mes_ctrl); +} + +int soce_setup(struct dsa_switch *ds) +{ + return soce_user_mii_bus_register(ds); +} + +void soce_teardown(struct dsa_switch *ds) +{ + soce_user_mii_bus_unregister(ds); +} + +int soce_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) +{ + return 0; +} + +void soce_port_disable(struct dsa_switch *ds, int port) +{ +} + +enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprop) +{ + return DSA_TAG_PROTO_SDSA; +} + +/* Bridge membership is handled by the switch forwarding model directly. */ +int soce_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + return 0; +} + +void soce_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ +} + +void soce_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct soce_priv *priv = ds->priv; + + soce_stp_state_set(ds, port, state, priv->local.layout); +} + +void soce_port_fast_age(struct dsa_switch *ds, int port) +{ + struct soce_priv *priv = ds->priv; + + soce_fast_age(ds, port, priv->local.layout); +} + diff --git a/drivers/net/dsa/soce/soce_common.h b/drivers/net/dsa/soce/soce_common.h new file mode 100644 index 000000000000..2423ba167d9f --- /dev/null +++ b/drivers/net/dsa/soce/soce_common.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#ifndef __SOCE_COMMON_H +#define __SOCE_COMMON_H + +#include <net/dsa.h> + +struct phy_device; +struct netlink_ext_ack; + +struct soce_layout { + u32 mstp_port_state_control; + u32 mstp_mstid; + u32 mstp_port_state_lo; + u32 mstp_port_state_hi; + u32 fdb_ctrl; +}; + +int soce_setup(struct dsa_switch *ds); +void soce_teardown(struct dsa_switch *ds); +int soce_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy); +void soce_port_disable(struct dsa_switch *ds, int port); +enum dsa_tag_protocol soce_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprop); +int soce_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack); +void soce_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); +void soce_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +void soce_port_fast_age(struct dsa_switch *ds, int port); + +#endif /* __SOCE_COMMON_H */ diff --git a/drivers/net/dsa/soce/soce_dsa.h b/drivers/net/dsa/soce/soce_dsa.h new file mode 100644 index 000000000000..edc79d69891d --- /dev/null +++ b/drivers/net/dsa/soce/soce_dsa.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#ifndef __SOCE_DSA_H +#define __SOCE_DSA_H + +#define SOCE_MAX_NUM_PORTS 30 + +struct soce_mdio { + int mdio_output; + int phy_addr; +}; + +struct dsa_switch; +struct soce_layout; + +struct soce_mdio_ops { + int (*phy_read)(struct dsa_switch *ds, int port, int regnum); + int (*phy_write)(struct dsa_switch *ds, int port, int regnum, u16 val); + int (*phy_read_c45)(struct dsa_switch *ds, int port, int devad, + int regnum); + int (*phy_write_c45)(struct dsa_switch *ds, int port, int devad, + int regnum, u16 val); +}; + +struct soce_dsa_local { + void __iomem *base_addr; + void __iomem *mdio_master_addr; + struct soce_mdio mdio_info[SOCE_MAX_NUM_PORTS]; + const struct soce_mdio_ops *mdio_ops; + const struct soce_layout *layout; +}; + +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..b88d00c56d71 --- /dev/null +++ b/drivers/net/dsa/soce/soce_dsa_core.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/netdevice.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <net/dsa.h> + +#include <linux/of_net.h> +#include <linux/of_platform.h> +#include <linux/if_bridge.h> +#include <linux/mdio.h> +#include <linux/of_mdio.h> +#include <linux/etherdevice.h> +#include <linux/phylink.h> + +#include "soce_dsa.h" +#include "soce_common.h" +#include "soce_mrs.h" +#include "soce_mdio.h" + +static void soce_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + if (dsa_is_cpu_port(ds, port)) { + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + config->mac_capabilities = MAC_10 | MAC_100 | MAC_1000 | + MAC_SYM_PAUSE | MAC_ASYM_PAUSE; + return; + } + + phy_interface_set_rgmii(config->supported_interfaces); + config->mac_capabilities = MAC_10 | MAC_100 | MAC_1000 | MAC_SYM_PAUSE | + MAC_ASYM_PAUSE; +} + +static int soce_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + + local = &priv->local; + if (!local->mdio_ops || !local->mdio_ops->phy_read) + return 0xffff; + + return local->mdio_ops->phy_read(ds, port, regnum); +} + +static int soce_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +{ + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + + local = &priv->local; + if (!local->mdio_ops || !local->mdio_ops->phy_write) + return -EOPNOTSUPP; + + return local->mdio_ops->phy_write(ds, port, regnum, val); +} + +static const struct soce_mdio_ops soce_mdio_ops_20_03 = { + .phy_read = soce_mdio_20_03_read, + .phy_write = soce_mdio_20_03_write, +}; + +static const struct soce_mdio_ops soce_mdio_ops_c22_c45 = { + .phy_read = soce_mdio_23_02_read, + .phy_write = soce_mdio_23_02_write, + .phy_read_c45 = soce_mdio_23_02_read_c45, + .phy_write_c45 = soce_mdio_23_02_write_c45, +}; + +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, + .phy_read = soce_phy_read, + .phy_write = soce_phy_write, + .port_enable = soce_port_enable, + .port_disable = soce_port_disable, + .port_bridge_join = soce_port_bridge_join, + .port_bridge_leave = soce_port_bridge_leave, + .port_stp_state_set = soce_port_stp_state_set, + .port_fast_age = soce_port_fast_age, +}; + +struct soce_variant_desc { + const char *compatible; + u32 mdio_master_offset; + const struct soce_layout *layout; + const struct soce_mdio_ops *mdio_ops; +}; + +static const struct soce_layout soce_mrs_21_01_layout = { + .mstp_port_state_control = SOCE_MRS_21_01_MSTP_PORT_STATE_CONTROL_REG, + .mstp_mstid = SOCE_MRS_21_01_MSTP_MSTID_OFFSET, + .mstp_port_state_lo = SOCE_MRS_21_01_MSTP_PORT_STATE_OFFSET_LO, + .mstp_port_state_hi = SOCE_MRS_21_01_MSTP_PORT_STATE_OFFSET_HI, + .fdb_ctrl = SOCE_MRS_21_01_FDB_CTRL_OFFSET, +}; + +static const struct soce_layout soce_mrs_24_01_layout = { + .mstp_port_state_control = SOCE_MRS_24_01_MSTP_PORT_STATE_CONTROL_REG, + .mstp_mstid = SOCE_MRS_24_01_MSTP_MSTID_OFFSET, + .mstp_port_state_lo = SOCE_MRS_24_01_MSTP_PORT_STATE_OFFSET_LO, + .mstp_port_state_hi = SOCE_MRS_24_01_MSTP_PORT_STATE_OFFSET_HI, + .fdb_ctrl = SOCE_MRS_24_01_FDB_CTRL_OFFSET, +}; + +static const struct soce_variant_desc soce_variants[] = { + { + .compatible = "soce,mrs-21-01", + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, + .layout = &soce_mrs_21_01_layout, + .mdio_ops = &soce_mdio_ops_20_03, + }, + { + .compatible = "soce,mrs-23-02", + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, + .layout = &soce_mrs_24_01_layout, + .mdio_ops = &soce_mdio_ops_c22_c45, + }, + { + .compatible = "soce,mrs-24-01", + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, + .layout = &soce_mrs_24_01_layout, + .mdio_ops = &soce_mdio_ops_c22_c45, + }, + { /* sentinel */ }, +}; + +static const struct soce_variant_desc * +soce_match_variant(const char *compatible) +{ + int i; + + for (i = 0; soce_variants[i].compatible; i++) { + if (!strcmp(compatible, soce_variants[i].compatible)) + return &soce_variants[i]; + } + + return NULL; +} + +static int soce_sw_parse_port_mdio_config(struct soce_dsa_local *local, + struct device *dev, + struct device_node *port_node, + u32 port) +{ + struct device_node *phy_node; + u32 val; + int ret; + + if (of_find_property(port_node, "ethernet", NULL) || + of_find_property(port_node, "link", NULL)) { + if (of_find_property(port_node, "phy-handle", NULL)) { + dev_err(dev, + "phy-handle not allowed on CPU/DSA port %u\n", + port); + return -EINVAL; + } + + return 0; + } + + phy_node = of_parse_phandle(port_node, "phy-handle", 0); + if (!phy_node) { + if (of_phy_is_fixed_link(port_node)) + return 0; + + dev_err(dev, "user port %u requires phy-handle or fixed-link\n", + port); + return -EINVAL; + } + + ret = of_property_read_u32(phy_node, "soce,mdio-output", &val); + if (ret) { + dev_err(dev, + "missing soce,mdio-output in %pOF referenced by port %u\n", + phy_node, port); + goto out_put_phy; + } + local->mdio_info[port].mdio_output = val; + + ret = of_property_read_u32(phy_node, "soce,phy-addr", &val); + if (ret) { + dev_err(dev, + "missing soce,phy-addr in %pOF referenced by port %u\n", + phy_node, port); + goto out_put_phy; + } + local->mdio_info[port].phy_addr = val; + +out_put_phy: + of_node_put(phy_node); + return ret; +} + +static int soce_sw_parse_port_mdio(struct soce_priv *priv, struct device *dev, + u32 numports) +{ + struct soce_dsa_local *local = &priv->local; + struct device_node *ports_node; + struct device_node *port_node; + u32 port; + int ret; + + ports_node = of_get_child_by_name(dev->of_node, "ports"); + if (!ports_node) { + dev_err(dev, "missing ports node\n"); + return -EINVAL; + } + + for_each_available_child_of_node(ports_node, port_node) { + if (of_property_read_u32(port_node, "reg", &port)) + continue; + + if (port >= numports || port >= SOCE_MAX_NUM_PORTS) { + dev_warn(dev, + "ignoring invalid port index %u in %pOF\n", + port, port_node); + continue; + } + + ret = soce_sw_parse_port_mdio_config(local, dev, port_node, + port); + if (ret) { + of_node_put(port_node); + of_node_put(ports_node); + return ret; + } + } + + of_node_put(ports_node); + return 0; +} + +static int soce_sw_probe(struct mdio_device *mdiodev) +{ + const struct soce_variant_desc *variant; + struct device *dev = &mdiodev->dev; + struct device_node *switch_node; + struct soce_dsa_local *local; + const char *soce_compatible; + struct soce_priv *priv; + u32 numports; + int ret; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->priv = priv; + local = &priv->local; + + switch_node = of_parse_phandle(dev->of_node, "soce,switch-ip", 0); + if (!switch_node) { + dev_err(dev, "missing or invalid switch IP reference\n"); + return -EINVAL; + } + + ret = of_property_read_string(switch_node, "compatible", + &soce_compatible); + if (ret) { + dev_err(dev, "missing compatible property in %pOF\n", + switch_node); + of_node_put(switch_node); + return -EINVAL; + } + + variant = soce_match_variant(soce_compatible); + if (!variant) { + dev_err(dev, "unsupported compatible '%s' in %pOF\n", + soce_compatible, switch_node); + of_node_put(switch_node); + return -ENODEV; + } + + ret = of_property_read_u32(switch_node, "soce,num-ports", &numports); + if (ret) { + dev_err(dev, "missing soce,num-ports in %pOF\n", switch_node); + of_node_put(switch_node); + return -EINVAL; + } + if (numports == 0 || numports > SOCE_MAX_NUM_PORTS) { + dev_err(dev, "invalid soce,num-ports %u (max %d)\n", numports, + SOCE_MAX_NUM_PORTS); + of_node_put(switch_node); + return -EINVAL; + } + + local->base_addr = devm_of_iomap(dev, switch_node, 0, NULL); + if (IS_ERR(local->base_addr)) { + dev_err(dev, "failed to map switch register space for %pOF\n", + switch_node); + of_node_put(switch_node); + return PTR_ERR(local->base_addr); + } + + priv->ds->ops = &soce_switch_ops; + local->mdio_ops = variant->mdio_ops; + local->mdio_master_addr = + local->base_addr + variant->mdio_master_offset; + local->layout = variant->layout; + of_node_put(switch_node); + + priv->ds->num_ports = numports; + ret = soce_sw_parse_port_mdio(priv, dev, numports); + if (ret) + return ret; + + dev_set_drvdata(&mdiodev->dev, priv); + + dev_info(dev, "soce %s SDSA driver probed.\n", soce_compatible); + + return dsa_register_switch(priv->ds); +} + +static void soce_sw_remove(struct mdio_device *mdiodev) +{ + struct soce_priv *priv = dev_get_drvdata(&mdiodev->dev); + + dsa_unregister_switch(priv->ds); +} + +static const struct of_device_id soce_of_match[] = { + { .compatible = "soce,switch-dsa" }, + { /* sentinel */ }, +}; + +static struct mdio_driver soce_mdio_driver = { + .mdiodrv.driver = { + .name = "soce-switch-dsa", + .of_match_table = soce_of_match, + }, + .probe = soce_sw_probe, + .remove = soce_sw_remove, +}; + +mdio_module_driver(soce_mdio_driver); +MODULE_AUTHOR("Vasilij Strassheim <v.strassheim@linutronix.de>"); +MODULE_DESCRIPTION("Driver for SoC-e ethernet switch family"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/soce/soce_mdio.c b/drivers/net/dsa/soce/soce_mdio.c new file mode 100644 index 000000000000..241fc373de8b --- /dev/null +++ b/drivers/net/dsa/soce/soce_mdio.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#include <linux/mdio.h> +#include <linux/of_mdio.h> +#include <linux/phy.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/bits.h> +#include <net/dsa.h> +#include "soce_dsa.h" +#include "soce_mdio.h" + +#define SOCE_MDIO_TIMEOUT 1000 + +/* MDIO 20.03 control word layout: + * [31:27] PHY address, [26:22] register address, [21] operation, + * [20] start/operation in progress, [7:0] MDIO bus selection. + */ +#define SOCE_MDIO_20_03_CTRL_PHY_ADDR_SHIFT 27 +#define SOCE_MDIO_20_03_CTRL_REG_ADDR_SHIFT 22 +#define SOCE_MDIO_20_03_CTRL_OP_SHIFT 21 +#define SOCE_MDIO_20_03_CTRL_START_SHIFT 20 +#define SOCE_MDIO_20_03_CTRL_OP_WRITE 0U + +#define SOCE_MDIO_20_03_CTRL_OP_READ BIT(SOCE_MDIO_20_03_CTRL_OP_SHIFT) +#define SOCE_MDIO_20_03_CTRL_START BIT(SOCE_MDIO_20_03_CTRL_START_SHIFT) + +static int soce_user_phy_read(struct mii_bus *bus, int addr, int reg) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->phys_mii_mask & BIT(addr)) + return ds->ops->phy_read(ds, addr, reg); + + return 0xffff; +} + +static int soce_user_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->phys_mii_mask & BIT(addr)) + return ds->ops->phy_write(ds, addr, reg, val); + + return 0; +} + +static int soce_user_phy_read_c45(struct mii_bus *bus, int addr, int devad, + int regnum) +{ + struct dsa_switch *ds = bus->priv; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + + if (!(ds->phys_mii_mask & BIT(addr))) + return 0xffff; + + local = &priv->local; + if (!local->mdio_ops || !local->mdio_ops->phy_read_c45) + return -EOPNOTSUPP; + + return local->mdio_ops->phy_read_c45(ds, addr, devad, regnum); +} + +static int soce_user_phy_write_c45(struct mii_bus *bus, int addr, int devad, + int regnum, u16 val) +{ + struct dsa_switch *ds = bus->priv; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + + if (!(ds->phys_mii_mask & BIT(addr))) + return 0; + + local = &priv->local; + if (!local->mdio_ops || !local->mdio_ops->phy_write_c45) + return -EOPNOTSUPP; + + return local->mdio_ops->phy_write_c45(ds, addr, devad, regnum, val); +} + +int soce_user_mii_bus_register(struct dsa_switch *ds) +{ + struct device_node *mdio_np; + int err; + + if (ds->user_mii_bus) + return 0; + + ds->user_mii_bus = mdiobus_alloc(); + if (!ds->user_mii_bus) + return -ENOMEM; + + ds->user_mii_bus->priv = ds; + ds->user_mii_bus->name = "soce user mii"; + ds->user_mii_bus->read = soce_user_phy_read; + ds->user_mii_bus->write = soce_user_phy_write; + ds->user_mii_bus->read_c45 = soce_user_phy_read_c45; + ds->user_mii_bus->write_c45 = soce_user_phy_write_c45; + snprintf(ds->user_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", + ds->dst->index, ds->index); + ds->user_mii_bus->parent = ds->dev; + ds->user_mii_bus->phy_mask = ~ds->phys_mii_mask; + + mdio_np = of_get_child_by_name(ds->dev->of_node, "mdio"); + err = of_mdiobus_register(ds->user_mii_bus, mdio_np); + of_node_put(mdio_np); + + if (err) { + mdiobus_free(ds->user_mii_bus); + ds->user_mii_bus = NULL; + return err; + } + + return 0; +} + +void soce_user_mii_bus_unregister(struct dsa_switch *ds) +{ + if (!ds || !ds->user_mii_bus) + return; + + mdiobus_unregister(ds->user_mii_bus); + mdiobus_free(ds->user_mii_bus); + ds->user_mii_bus = NULL; +} + +static int soce_mdio_20_03_wait_for_idle(struct dsa_switch *ds) +{ + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + void __iomem *ctrl; + u32 val; + + local = &priv->local; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + return readl_poll_timeout(ctrl, val, + !(val & SOCE_MDIO_20_03_CTRL_START), 10, + SOCE_MDIO_TIMEOUT * 1000); +} + +int soce_mdio_20_03_read(struct dsa_switch *ds, int portnumber, int regnum) +{ + struct soce_priv *priv = ds->priv; + void __iomem *ctrl, *read_reg; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + int ret; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + read_reg = local->mdio_master_addr + SOCE_MDIO_20_03_READ_OFFSET; + + regvalue = (mdio->phy_addr << SOCE_MDIO_20_03_CTRL_PHY_ADDR_SHIFT) | + (regnum << SOCE_MDIO_20_03_CTRL_REG_ADDR_SHIFT) | + SOCE_MDIO_20_03_CTRL_OP_READ | + SOCE_MDIO_20_03_CTRL_START | + mdio->mdio_output; + writel(regvalue, ctrl); + + ret = soce_mdio_20_03_wait_for_idle(ds); + if (ret) + return ret; + + return readl(read_reg) & 0xffff; +} + +int soce_mdio_20_03_write(struct dsa_switch *ds, int portnumber, int regnum, + u16 val) +{ + struct soce_priv *priv = ds->priv; + void __iomem *ctrl, *write_reg; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + write_reg = local->mdio_master_addr + SOCE_MDIO_20_03_WRITE_OFFSET; + + /* Write data first */ + writel(val, write_reg); + + /* Then write control */ + regvalue = (mdio->phy_addr << SOCE_MDIO_20_03_CTRL_PHY_ADDR_SHIFT) | + (regnum << SOCE_MDIO_20_03_CTRL_REG_ADDR_SHIFT) | + SOCE_MDIO_20_03_CTRL_OP_WRITE | + SOCE_MDIO_20_03_CTRL_START | + mdio->mdio_output; + writel(regvalue, ctrl); + + return soce_mdio_20_03_wait_for_idle(ds); +} + +static int soce_mdio_23_02_wait_for_idle(struct dsa_switch *ds) +{ + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + void __iomem *ctrl; + u32 val; + + local = &priv->local; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + + return readl_poll_timeout(ctrl, val, + !(val & (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)), 10, + SOCE_MDIO_TIMEOUT * 1000); +} + +static int soce_mdio_23_02_read_c22(struct dsa_switch *ds, int portnumber, + int regnum) +{ + void __iomem *ctrl, *params, *read_reg; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + int ret; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + params = local->mdio_master_addr + SOCE_MDIO_23_02_PARAMS_OFFSET; + read_reg = local->mdio_master_addr + SOCE_MDIO_23_02_READ_OFFSET; + + regvalue = (regnum << SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET) + + (mdio->phy_addr << SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET); + writel(regvalue, params); + + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x3 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x0 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + ret = soce_mdio_23_02_wait_for_idle(ds); + if (ret) + return ret; + + return readl(read_reg) & 0xffff; +} + +int soce_mdio_23_02_read_c45(struct dsa_switch *ds, int portnumber, int devad, + int regnum) +{ + void __iomem *ctrl, *params, *read_reg, *write_reg; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + int ret; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + params = local->mdio_master_addr + SOCE_MDIO_23_02_PARAMS_OFFSET; + write_reg = local->mdio_master_addr + SOCE_MDIO_23_02_WRITE_OFFSET; + read_reg = local->mdio_master_addr + SOCE_MDIO_23_02_READ_OFFSET; + + regvalue = (devad << SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET) + + (mdio->phy_addr << SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET); + writel(regvalue, params); + + writel(regnum, write_reg); + + /* address cycle */ + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x0 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + ret = soce_mdio_23_02_wait_for_idle(ds); + if (ret) + return ret; + + /* read cycle */ + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x3 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + ret = soce_mdio_23_02_wait_for_idle(ds); + if (ret) + return ret; + + return readl(read_reg) & 0xffff; +} + +int soce_mdio_23_02_read(struct dsa_switch *ds, int portnumber, int regnum) +{ + return soce_mdio_23_02_read_c22(ds, portnumber, regnum); +} + +static int soce_mdio_23_02_write_c22(struct dsa_switch *ds, int portnumber, + int regnum, u16 val) +{ + void __iomem *ctrl, *params, *write_reg; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + params = local->mdio_master_addr + SOCE_MDIO_23_02_PARAMS_OFFSET; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + write_reg = local->mdio_master_addr + SOCE_MDIO_23_02_WRITE_OFFSET; + + regvalue = (regnum << SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET) + + (mdio->phy_addr << SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET); + writel(regvalue, params); + + writel(val, write_reg); + + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x0 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + return soce_mdio_23_02_wait_for_idle(ds); +} + +int soce_mdio_23_02_write_c45(struct dsa_switch *ds, int portnumber, int devad, + int regnum, u16 val) +{ + void __iomem *ctrl, *params, *write_reg; + struct soce_priv *priv = ds->priv; + struct soce_dsa_local *local; + struct soce_mdio *mdio; + u32 regvalue; + int ret; + + local = &priv->local; + mdio = &local->mdio_info[portnumber]; + ctrl = local->mdio_master_addr + SOCE_MDIO_CTRL_OFFSET; + params = local->mdio_master_addr + SOCE_MDIO_23_02_PARAMS_OFFSET; + write_reg = local->mdio_master_addr + SOCE_MDIO_23_02_WRITE_OFFSET; + + regvalue = (devad << SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET) + + (mdio->phy_addr << SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET); + writel(regvalue, params); + + writel(regnum, write_reg); + + /* address cycle */ + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x0 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + ret = soce_mdio_23_02_wait_for_idle(ds); + if (ret) + return ret; + + writel(val, write_reg); + + /* write cycle */ + regvalue = ((mdio->mdio_output << SOCE_MDIO_23_02_CTRL_BUS_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET) + + (0x1 << SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET)); + writel(regvalue, ctrl); + + return soce_mdio_23_02_wait_for_idle(ds); +} + +int soce_mdio_23_02_write(struct dsa_switch *ds, int portnumber, int regnum, + u16 val) +{ + return soce_mdio_23_02_write_c22(ds, portnumber, regnum, val); +} diff --git a/drivers/net/dsa/soce/soce_mdio.h b/drivers/net/dsa/soce/soce_mdio.h new file mode 100644 index 000000000000..a71158ed06b3 --- /dev/null +++ b/drivers/net/dsa/soce/soce_mdio.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#ifndef __SOCE_MDIO_H +#define __SOCE_MDIO_H + +#define SOCE_MDIO_CTRL_OFFSET 0x0000 + +/* for older versions */ +#define SOCE_MDIO_20_03_WRITE_OFFSET 0x0004 +#define SOCE_MDIO_20_03_READ_OFFSET 0x0008 + +/* for newer versions */ +#define SOCE_MDIO_23_02_PARAMS_OFFSET 0x0004 +#define SOCE_MDIO_23_02_WRITE_OFFSET 0x0008 +#define SOCE_MDIO_23_02_READ_OFFSET 0x000c +#define SOCE_MDIO_23_02_CTRL_BUS_OFFSET 16 +#define SOCE_MDIO_23_02_CTRL_TRANSTYPE_OFFSET 3 +#define SOCE_MDIO_23_02_CTRL_CLAUSE_OFFSET 1 +#define SOCE_MDIO_23_02_CTRL_OPSTATUS_OFFSET 0 + +#define SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET 8 +#define SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET 0 + +int soce_mdio_20_03_read(struct dsa_switch *ds, int portnumber, int regnum); +int soce_mdio_20_03_write(struct dsa_switch *ds, int portnumber, int regnum, + u16 val); + +int soce_mdio_23_02_read(struct dsa_switch *ds, int portnumber, int regnum); +int soce_mdio_23_02_write(struct dsa_switch *ds, int portnumber, int regnum, + u16 val); +int soce_mdio_23_02_read_c45(struct dsa_switch *ds, int portnumber, int devad, + int regnum); +int soce_mdio_23_02_write_c45(struct dsa_switch *ds, int portnumber, int devad, + int regnum, u16 val); +int soce_user_mii_bus_register(struct dsa_switch *ds); +void soce_user_mii_bus_unregister(struct dsa_switch *ds); +#endif /* __SOCE_MDIO_H */ diff --git a/drivers/net/dsa/soce/soce_mrs.h b/drivers/net/dsa/soce/soce_mrs.h new file mode 100644 index 000000000000..cf2b29f1297e --- /dev/null +++ b/drivers/net/dsa/soce/soce_mrs.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020 System on Chip engineering, S.L. + * Copyright (c) 2026 Linutronix GmbH + */ + +#ifndef __SOCE_MRS_H +#define __SOCE_MRS_H + +#define SOCE_MRS_21_01_MSTP_PORT_STATE_CONTROL_REG 0x0D04 +#define SOCE_MRS_21_01_MSTP_MSTID_OFFSET 0x0D08 +#define SOCE_MRS_21_01_MSTP_PORT_STATE_OFFSET_LO 0x0D0C +#define SOCE_MRS_21_01_MSTP_PORT_STATE_OFFSET_HI 0x0D10 +#define SOCE_MRS_24_01_MSTP_PORT_STATE_CONTROL_REG 0x0F04 +#define SOCE_MRS_24_01_MSTP_MSTID_OFFSET 0x0F08 +#define SOCE_MRS_24_01_MSTP_PORT_STATE_OFFSET_LO 0x0F0C +#define SOCE_MRS_24_01_MSTP_PORT_STATE_OFFSET_HI 0x0F10 +#define SOCE_MRS_21_01_FDB_CTRL_OFFSET 0x0A0C +#define SOCE_MRS_24_01_FDB_CTRL_OFFSET 0x0C0C +#define SOCE_MRS_21_01_MDIO_MASTER_OFFSET 0x0200 + +#endif /* __SOCE_MRS_H */ -- 2.39.5 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim @ 2026-07-29 17:31 ` Andrew Lunn 2026-07-29 20:05 ` Andrew Lunn ` (2 subsequent siblings) 3 siblings, 0 replies; 18+ messages in thread From: Andrew Lunn @ 2026-07-29 17:31 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, devicetree, linux-kernel, netdev, Martin Kaistra > +static int soce_sw_probe(struct mdio_device *mdiodev) > +{ > + const struct soce_variant_desc *variant; > + struct device *dev = &mdiodev->dev; > + struct device_node *switch_node; > + struct soce_dsa_local *local; > + const char *soce_compatible; > + struct soce_priv *priv; > + u32 numports; > + int ret; > + > + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); > + if (!priv->ds) > + return -ENOMEM; > + > + priv->ds->dev = dev; > + priv->ds->priv = priv; > + local = &priv->local; > + > + switch_node = of_parse_phandle(dev->of_node, "soce,switch-ip", 0); > + if (!switch_node) { > + dev_err(dev, "missing or invalid switch IP reference\n"); > + return -EINVAL; > + } > + > + ret = of_property_read_string(switch_node, "compatible", > + &soce_compatible); > + if (ret) { > + dev_err(dev, "missing compatible property in %pOF\n", > + switch_node); > + of_node_put(switch_node); > + return -EINVAL; > + } > + > + variant = soce_match_variant(soce_compatible); > + if (!variant) { > + dev_err(dev, "unsupported compatible '%s' in %pOF\n", > + soce_compatible, switch_node); > + of_node_put(switch_node); > + return -ENODEV; > + } > + > + ret = of_property_read_u32(switch_node, "soce,num-ports", &numports); > + if (ret) { > + dev_err(dev, "missing soce,num-ports in %pOF\n", switch_node); > + of_node_put(switch_node); > + return -EINVAL; > + } > + if (numports == 0 || numports > SOCE_MAX_NUM_PORTS) { > + dev_err(dev, "invalid soce,num-ports %u (max %d)\n", numports, > + SOCE_MAX_NUM_PORTS); > + of_node_put(switch_node); > + return -EINVAL; > + } > + > + local->base_addr = devm_of_iomap(dev, switch_node, 0, NULL); > + if (IS_ERR(local->base_addr)) { > + dev_err(dev, "failed to map switch register space for %pOF\n", > + switch_node); > + of_node_put(switch_node); > + return PTR_ERR(local->base_addr); > + } > + > + priv->ds->ops = &soce_switch_ops; > + local->mdio_ops = variant->mdio_ops; > + local->mdio_master_addr = > + local->base_addr + variant->mdio_master_offset; > + local->layout = variant->layout; > + of_node_put(switch_node); > + > + priv->ds->num_ports = numports; > + ret = soce_sw_parse_port_mdio(priv, dev, numports); > + if (ret) > + return ret; > + > + dev_set_drvdata(&mdiodev->dev, priv); > + > + dev_info(dev, "soce %s SDSA driver probed.\n", soce_compatible); > + > + return dsa_register_switch(priv->ds); > +} Looking at this struct mdio_device *mdiodev is not used, other than for ->dev. Is this device actually on an MDIO bus? Andrew ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim 2026-07-29 17:31 ` Andrew Lunn @ 2026-07-29 20:05 ` Andrew Lunn 2026-07-30 9:01 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 3 siblings, 0 replies; 18+ messages in thread From: Andrew Lunn @ 2026-07-29 20: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, devicetree, linux-kernel, netdev, Martin Kaistra > +static u8 soce_map_stp_state(u8 state) > +{ > + switch (state) { > + case BR_STATE_DISABLED: > + case BR_STATE_BLOCKING: > + case BR_STATE_LISTENING: > + return 0; > + case BR_STATE_LEARNING: > + return 1; > + case BR_STATE_FORWARDING: > + default: > + return 3; #defines for 0, 1 and 3. What does 2 mean? Since you don't implement bridge offload, i'm not sure having STP states makes any sense. > +static void soce_fast_age(struct dsa_switch *ds, int port, > + const struct soce_layout *layout) > +{ > + struct soce_priv *priv = ds->priv; > + struct soce_dsa_local *local; > + void __iomem *p_mes_ctrl; > + u32 val; Does aging make any sense without bridge offload? > +/* Bridge membership is handled by the switch forwarding model directly. */ > +int soce_port_bridge_join(struct dsa_switch *ds, int port, > + struct dsa_bridge bridge, bool *tx_fwd_offload, > + struct netlink_ext_ack *extack) > +{ > + return 0; > +} > + > +void soce_port_bridge_leave(struct dsa_switch *ds, int port, > + struct dsa_bridge bridge) > +{ > +} If you don't support bridge offload, you should not need these. > +static void soce_phylink_get_caps(struct dsa_switch *ds, int port, > + struct phylink_config *config) > +{ > + if (dsa_is_cpu_port(ds, port)) { > + __set_bit(PHY_INTERFACE_MODE_GMII, Is that really true? What is part of the IP core, and what is to do with integration of the core into the system as a whole? It seems like RGMII is also supported, so why cannot i instantiate a RGMII MII blob for the CPU port as well? > + config->supported_interfaces); > + config->mac_capabilities = MAC_10 | MAC_100 | MAC_1000 | > + MAC_SYM_PAUSE | MAC_ASYM_PAUSE; > + return; > + } > + > + phy_interface_set_rgmii(config->supported_interfaces); And flipping that around, if the core supports GMII, could i instantiate a GMII user port? > +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, > + .phy_read = soce_phy_read, > + .phy_write = soce_phy_write, phy_read and phy_write are legacy. Instantiate a Linux MDIO bus and don't list anything here. > +static const struct soce_variant_desc * > +soce_match_variant(const char *compatible) > +{ > + int i; > + > + for (i = 0; soce_variants[i].compatible; i++) { > + if (!strcmp(compatible, soce_variants[i].compatible)) > + return &soce_variants[i]; > + } > + > + return NULL; This kind of looks like a reinvention of of_device_get_match_data() > +static int soce_sw_parse_port_mdio_config(struct soce_dsa_local *local, > + struct device *dev, > + struct device_node *port_node, > + u32 port) > +{ > + struct device_node *phy_node; > + u32 val; > + int ret; > + > + if (of_find_property(port_node, "ethernet", NULL) || > + of_find_property(port_node, "link", NULL)) { > + if (of_find_property(port_node, "phy-handle", NULL)) { > + dev_err(dev, > + "phy-handle not allowed on CPU/DSA port %u\n", > + port); > + return -EINVAL; > + } > + > + return 0; > + } Why is all of this parsing of DT needed? These should all be standard properties which the DSA core handles. This currently has the feel of being a "vendor crap" driver. Please spend some time to clean this driver up, bring it up to Mainline standards. Andrew ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim 2026-07-29 17:31 ` Andrew Lunn 2026-07-29 20:05 ` Andrew Lunn @ 2026-07-30 9:01 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 3 siblings, 0 replies; 18+ messages in thread From: Krzysztof Kozlowski @ 2026-07-30 9:01 UTC (permalink / raw) To: Vasilij Strassheim Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Russell King, devicetree, linux-kernel, netdev, Martin Kaistra On Wed, Jul 29, 2026 at 06:36:57PM +0200, Vasilij Strassheim wrote: > +static const struct soce_variant_desc soce_variants[] = { > + { > + .compatible = "soce,mrs-21-01", > + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, > + .layout = &soce_mrs_21_01_layout, > + .mdio_ops = &soce_mdio_ops_20_03, > + }, > + { > + .compatible = "soce,mrs-23-02", > + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, > + .layout = &soce_mrs_24_01_layout, > + .mdio_ops = &soce_mdio_ops_c22_c45, > + }, > + { > + .compatible = "soce,mrs-24-01", > + .mdio_master_offset = SOCE_MRS_21_01_MDIO_MASTER_OFFSET, > + .layout = &soce_mrs_24_01_layout, > + .mdio_ops = &soce_mdio_ops_c22_c45, > + }, > + { /* sentinel */ }, > +}; > + > +static const struct soce_variant_desc * > +soce_match_variant(const char *compatible) > +{ > + int i; > + > + for (i = 0; soce_variants[i].compatible; i++) { > + if (!strcmp(compatible, soce_variants[i].compatible)) > + return &soce_variants[i]; Do not re-invent OF matching. This is supposed to be of_device_id table. > + } > + > + return NULL; > +} > + > +static int soce_sw_parse_port_mdio_config(struct soce_dsa_local *local, > + struct device *dev, > + struct device_node *port_node, > + u32 port) > +{ > + struct device_node *phy_node; > + u32 val; > + int ret; > + > + if (of_find_property(port_node, "ethernet", NULL) || > + of_find_property(port_node, "link", NULL)) { > + if (of_find_property(port_node, "phy-handle", NULL)) { > + dev_err(dev, > + "phy-handle not allowed on CPU/DSA port %u\n", > + port); > + return -EINVAL; > + } > + > + return 0; > + } > + > + phy_node = of_parse_phandle(port_node, "phy-handle", 0); > + if (!phy_node) { > + if (of_phy_is_fixed_link(port_node)) > + return 0; > + > + dev_err(dev, "user port %u requires phy-handle or fixed-link\n", > + port); > + return -EINVAL; > + } > + > + ret = of_property_read_u32(phy_node, "soce,mdio-output", &val); > + if (ret) { > + dev_err(dev, > + "missing soce,mdio-output in %pOF referenced by port %u\n", > + phy_node, port); > + goto out_put_phy; > + } > + local->mdio_info[port].mdio_output = val; > + > + ret = of_property_read_u32(phy_node, "soce,phy-addr", &val); > + if (ret) { > + dev_err(dev, > + "missing soce,phy-addr in %pOF referenced by port %u\n", > + phy_node, port); > + goto out_put_phy; > + } > + local->mdio_info[port].phy_addr = val; > + > +out_put_phy: > + of_node_put(phy_node); > + return ret; > +} > + > +static int soce_sw_parse_port_mdio(struct soce_priv *priv, struct device *dev, > + u32 numports) > +{ > + struct soce_dsa_local *local = &priv->local; > + struct device_node *ports_node; > + struct device_node *port_node; > + u32 port; > + int ret; > + > + ports_node = of_get_child_by_name(dev->of_node, "ports"); > + if (!ports_node) { > + dev_err(dev, "missing ports node\n"); > + return -EINVAL; > + } > + > + for_each_available_child_of_node(ports_node, port_node) { Why not scoped? > + if (of_property_read_u32(port_node, "reg", &port)) > + continue; > + > + if (port >= numports || port >= SOCE_MAX_NUM_PORTS) { > + dev_warn(dev, > + "ignoring invalid port index %u in %pOF\n", > + port, port_node); > + continue; > + } > + > + ret = soce_sw_parse_port_mdio_config(local, dev, port_node, > + port); > + if (ret) { > + of_node_put(port_node); > + of_node_put(ports_node); > + return ret; > + } > + } > + > + of_node_put(ports_node); > + return 0; > +} > + > +static int soce_sw_probe(struct mdio_device *mdiodev) > +{ > + const struct soce_variant_desc *variant; > + struct device *dev = &mdiodev->dev; > + struct device_node *switch_node; > + struct soce_dsa_local *local; > + const char *soce_compatible; > + struct soce_priv *priv; > + u32 numports; > + int ret; > + > + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); > + if (!priv->ds) > + return -ENOMEM; > + > + priv->ds->dev = dev; > + priv->ds->priv = priv; > + local = &priv->local; > + > + switch_node = of_parse_phandle(dev->of_node, "soce,switch-ip", 0); > + if (!switch_node) { > + dev_err(dev, "missing or invalid switch IP reference\n"); > + return -EINVAL; > + } > + > + ret = of_property_read_string(switch_node, "compatible", > + &soce_compatible); > + if (ret) { > + dev_err(dev, "missing compatible property in %pOF\n", > + switch_node); > + of_node_put(switch_node); > + return -EINVAL; > + } > + > + variant = soce_match_variant(soce_compatible); > + if (!variant) { > + dev_err(dev, "unsupported compatible '%s' in %pOF\n", > + soce_compatible, switch_node); > + of_node_put(switch_node); Use proper error handling exit paths with goto. > + return -ENODEV; > + } > + > + ret = of_property_read_u32(switch_node, "soce,num-ports", &numports); > + if (ret) { > + dev_err(dev, "missing soce,num-ports in %pOF\n", switch_node); > + of_node_put(switch_node); > + return -EINVAL; > + } > + if (numports == 0 || numports > SOCE_MAX_NUM_PORTS) { > + dev_err(dev, "invalid soce,num-ports %u (max %d)\n", numports, > + SOCE_MAX_NUM_PORTS); > + of_node_put(switch_node); > + return -EINVAL; > + } > + > + local->base_addr = devm_of_iomap(dev, switch_node, 0, NULL); > + if (IS_ERR(local->base_addr)) { > + dev_err(dev, "failed to map switch register space for %pOF\n", > + switch_node); > + of_node_put(switch_node); > + return PTR_ERR(local->base_addr); > + } > + > + priv->ds->ops = &soce_switch_ops; > + local->mdio_ops = variant->mdio_ops; > + local->mdio_master_addr = > + local->base_addr + variant->mdio_master_offset; > + local->layout = variant->layout; > + of_node_put(switch_node); > + > + priv->ds->num_ports = numports; > + ret = soce_sw_parse_port_mdio(priv, dev, numports); > + if (ret) > + return ret; > + > + dev_set_drvdata(&mdiodev->dev, priv); > + > + dev_info(dev, "soce %s SDSA driver probed.\n", soce_compatible); Drop, driver should be silent on success. > + > + return dsa_register_switch(priv->ds); > +} > + > +static void soce_sw_remove(struct mdio_device *mdiodev) > +{ > + struct soce_priv *priv = dev_get_drvdata(&mdiodev->dev); > + > + dsa_unregister_switch(priv->ds); > +} > + > +static const struct of_device_id soce_of_match[] = { > + { .compatible = "soce,switch-dsa" }, > + { /* sentinel */ }, > +}; > + > +static struct mdio_driver soce_mdio_driver = { > + .mdiodrv.driver = { > + .name = "soce-switch-dsa", > + .of_match_table = soce_of_match, > + }, > + .probe = soce_sw_probe, > + .remove = soce_sw_remove, > +}; Best regards, Krzysztof ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim ` (2 preceding siblings ...) 2026-07-30 9:01 ` Krzysztof Kozlowski @ 2026-07-30 16:37 ` sashiko-bot 3 siblings, 0 replies; 18+ messages in thread From: sashiko-bot @ 2026-07-30 16:37 UTC (permalink / raw) To: Vasilij Strassheim; +Cc: conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [High] Incorrect error check for `devm_of_iomap()` leads to potential NULL pointer dereference on mapping failure. - [High] The `ds->phys_mii_mask` is never initialized, causing the driver's custom MDIO bus to silently ignore all PHY reads and writes. - [High] Concurrent MDIO accesses via `slave_mii_bus` and `user_mii_bus` race on shared hardware registers, causing state corruption. - [Medium] Missing bitmasks on input parameters in MDIO hardware register construction allows out-of-bounds values to corrupt adjacent register fields. - [Medium] The driver unconditionally issues a flush command to the hardware FDB controller even if the previous command is still being processed. - [Medium] Asynchronous, switch-wide FDB flush is performed instead of a synchronous, per-port flush. -- --- Patch [4]: [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches --- commit 4ad10a20f72f216349a16415f424608c5d3e5c86 Author: Vasilij Strassheim <v.strassheim@linutronix.de> net: dsa: soce: Add initial driver support for MRS switches This commit adds initial support for SoC-e switches based on FPGA IP cores. It provides MDIO access helpers for supported IP register layouts and common STP/FDB handling. > diff --git a/drivers/net/dsa/soce/soce_common.c b/drivers/net/dsa/soce/soce_common.c > index 0000000000000..f7f345a690e46 > --- /dev/null > +++ b/drivers/net/dsa/soce/soce_common.c [ ... ] > +static void soce_fast_age(struct dsa_switch *ds, int port, > + const struct soce_layout *layout) > +{ > + struct soce_priv *priv = ds->priv; > + struct soce_dsa_local *local; > + void __iomem *p_mes_ctrl; > + u32 val; > + > + local = &priv->local; > + if (!layout || !local->base_addr) > + return; > + > + p_mes_ctrl = local->base_addr + layout->fdb_ctrl; > + if (readl_poll_timeout(p_mes_ctrl, val, > + !(val & SOCE_FDB_CLEAR_UNDER_PROCESS), > + SOCE_FAST_AGE_POLL_US, SOCE_FAST_AGE_TIMEOUT_US)) > + dev_warn(ds->dev, "SoC-e fast_age timeout on port %d\n", port); > + > + writel(SOCE_FDB_CLEAR_FLUSH_MSTID, p_mes_ctrl); [Severity: Medium] Does this code continue to issue the flush command even if the hardware is still processing the previous one? If readl_poll_timeout() fails, it prints a warning but unconditionally executes the writel() below it. [Severity: Medium] Will this flush the entire MSTID globally rather than just the specified port? The command ignores the port argument. Additionally, it appears to return immediately without waiting for the clear operation to complete. > +} [ ... ] > diff --git a/drivers/net/dsa/soce/soce_dsa_core.c b/drivers/net/dsa/soce/soce_dsa_core.c > index 0000000000000..b88d00c56d718 > --- /dev/null > +++ b/drivers/net/dsa/soce/soce_dsa_core.c [ ... ] > +static int soce_phy_read(struct dsa_switch *ds, int port, int regnum) > +{ > + struct soce_priv *priv = ds->priv; > + struct soce_dsa_local *local; > + > + local = &priv->local; > + if (!local->mdio_ops || !local->mdio_ops->phy_read) > + return 0xffff; > + > + return local->mdio_ops->phy_read(ds, port, regnum); > +} [Severity: High] Can concurrent MDIO accesses race on the shared hardware registers here? The DSA core implicitly registers the slave_mii_bus, and the driver explicitly registers a user_mii_bus. Both buses have their own independent mdio_lock mutex, but they share the same underlying MMIO routines. [ ... ] > +static int soce_sw_probe(struct mdio_device *mdiodev) > +{ [ ... ] > + local->base_addr = devm_of_iomap(dev, switch_node, 0, NULL); > + if (IS_ERR(local->base_addr)) { > + dev_err(dev, "failed to map switch register space for %pOF\n", > + switch_node); > + of_node_put(switch_node); > + return PTR_ERR(local->base_addr); > + } [Severity: High] Does devm_of_iomap() return NULL on failure rather than an error pointer? Checking IS_ERR() here will evaluate to false if it returns NULL, leading to a potential NULL pointer dereference later when accessing the MMIO base. [ ... ] > diff --git a/drivers/net/dsa/soce/soce_mdio.c b/drivers/net/dsa/soce/soce_mdio.c > index 0000000000000..241fc373de8b8 > --- /dev/null > +++ b/drivers/net/dsa/soce/soce_mdio.c [ ... ] > +static int soce_user_phy_read(struct mii_bus *bus, int addr, int reg) > +{ > + struct dsa_switch *ds = bus->priv; > + > + if (ds->phys_mii_mask & BIT(addr)) > + return ds->ops->phy_read(ds, addr, reg); > + > + return 0xffff; > +} [Severity: High] Is ds->phys_mii_mask ever initialized? It appears to default to 0 when allocated in soce_sw_probe(). If it is always 0, this check will fail and all user PHY reads will silently return 0xffff. [ ... ] > +int soce_user_mii_bus_register(struct dsa_switch *ds) > +{ [ ... ] > + ds->user_mii_bus->parent = ds->dev; > + ds->user_mii_bus->phy_mask = ~ds->phys_mii_mask; [Severity: High] Since ds->phys_mii_mask defaults to 0, will setting phy_mask to the inverted value instruct the MDIO core to ignore all PHYs and prevent probing entirely? [ ... ] > +int soce_mdio_20_03_write(struct dsa_switch *ds, int portnumber, int regnum, > + u16 val) > +{ [ ... ] > + /* Then write control */ > + regvalue = (mdio->phy_addr << SOCE_MDIO_20_03_CTRL_PHY_ADDR_SHIFT) | > + (regnum << SOCE_MDIO_20_03_CTRL_REG_ADDR_SHIFT) | > + SOCE_MDIO_20_03_CTRL_OP_WRITE | > + SOCE_MDIO_20_03_CTRL_START | > + mdio->mdio_output; > + writel(regvalue, ctrl); [Severity: Medium] Should the phy_addr and regnum inputs be bounded or masked before shifting? If an oversized regnum is passed in via userspace ioctls, or a malformed DT provides a large soce,phy-addr, the shifted bits could overflow into adjacent control register fields. [ ... ] > +static int soce_mdio_23_02_read_c22(struct dsa_switch *ds, int portnumber, > + int regnum) > +{ [ ... ] > + regvalue = (regnum << SOCE_MDIO_23_02_CTRL_REGADDRDEVTYPE_OFFSET) + > + (mdio->phy_addr << SOCE_MDIO_23_02_CTRL_PHYADDR_OFFSET); > + writel(regvalue, params); [Severity: Medium] Similarly here, can the addition of unmasked values risk arithmetic overflow into adjacent bit ranges if the inputs are out-of-bounds? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260729-devel-vstrassheim-soce-dsa-ml-v1-0-be569dae1b20@linutronix.de?part=4 ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 0/4] net: dsa: Add SoC-e DSA driver 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim ` (3 preceding siblings ...) 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim @ 2026-07-29 16:56 ` Andrew Lunn 4 siblings, 0 replies; 18+ messages in thread From: Andrew Lunn @ 2026-07-29 16:56 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, devicetree, linux-kernel, netdev, Martin Kaistra On Wed, Jul 29, 2026 at 06:36:53PM +0200, Vasilij Strassheim wrote: > Add initial support for the DSA driver for SoC-e FPGA-based (IP core) > Ethernet switches. Let me start with a dumb question. Is this a true soft core switch, which is synthesised and running in the FPGA? Or is it hardware at the edge of the FPGA SoC? If it is synthesised, do we have to deal with different synthesis options? Are there registers which indicate what these options are? Where as if it is fixed hardware in the SoC we don't need to worry so much about variations, it is fixed at the SoC level. > There are different variants of the SoC-e switch IP, which can be > additionally configured at runtime with proprietary userspace software. What sort of configuration? Andrew ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-07-30 16:37 UTC | newest] Thread overview: 18+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-29 16:36 [PATCH 0/4] net: dsa: Add SoC-e DSA driver Vasilij Strassheim 2026-07-29 16:36 ` [PATCH 1/4] dt-bindings: vendor-prefixes: Add soce Vasilij Strassheim 2026-07-30 8:50 ` Krzysztof Kozlowski 2026-07-29 16:36 ` [PATCH 2/4] dt-bindings: net: dsa: Add SoC-e switch IP and DSA bindings Vasilij Strassheim 2026-07-29 17:11 ` Andrew Lunn 2026-07-29 18:07 ` Rob Herring (Arm) 2026-07-30 8:56 ` Krzysztof Kozlowski 2026-07-30 8:56 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 2026-07-29 16:36 ` [PATCH 3/4] net: dsa: Add support for SoC-e SDSA tags Vasilij Strassheim 2026-07-29 17:22 ` Andrew Lunn 2026-07-30 16:37 ` sashiko-bot 2026-07-29 16:36 ` [PATCH 4/4] net: dsa: soce: Add initial driver support for MRS switches Vasilij Strassheim 2026-07-29 17:31 ` Andrew Lunn 2026-07-29 20:05 ` Andrew Lunn 2026-07-30 9:01 ` Krzysztof Kozlowski 2026-07-30 16:37 ` sashiko-bot 2026-07-29 16:56 ` [PATCH 0/4] net: dsa: Add SoC-e DSA driver Andrew Lunn
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.