* [RFC PATCH v11 net-next 01/10] dt-bindings: net: make internal-delay-ps based on phy-mode
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-29 20:41 ` Rob Herring
2022-03-25 16:53 ` [RFC PATCH v11 net-next 02/10] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
` (8 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
*-internal-delay-ps properties would be applicable only for RGMII interface
modes.
It is changed as per the request,
https://lore.kernel.org/netdev/d8e5f6a8-a7e1-dabd-f4b4-ea8ea21d0a1d@gmail.com/
Ran dt_binding_check to confirm nothing is broken.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
.../bindings/net/ethernet-controller.yaml | 37 +++++++++++++------
1 file changed, 25 insertions(+), 12 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index 34c5463abcec..dc86a6479a86 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -123,12 +123,6 @@ properties:
and is useful for determining certain configuration settings
such as flow control thresholds.
- rx-internal-delay-ps:
- description: |
- RGMII Receive Clock Delay defined in pico seconds.
- This is used for controllers that have configurable RX internal delays.
- If this property is present then the MAC applies the RX delay.
-
sfp:
$ref: /schemas/types.yaml#/definitions/phandle
description:
@@ -140,12 +134,6 @@ properties:
The size of the controller\'s transmit fifo in bytes. This
is used for components that can have configurable fifo sizes.
- tx-internal-delay-ps:
- description: |
- RGMII Transmit Clock Delay defined in pico seconds.
- This is used for controllers that have configurable TX internal delays.
- If this property is present then the MAC applies the TX delay.
-
managed:
description:
Specifies the PHY management type. If auto is set and fixed-link
@@ -222,6 +210,31 @@ properties:
required:
- speed
+allOf:
+ - if:
+ properties:
+ phy-mode:
+ contains:
+ enum:
+ - rgmii
+ - rgmii-rxid
+ - rgmii-txid
+ - rgmii-id
+ then:
+ properties:
+ rx-internal-delay-ps:
+ description:
+ RGMII Receive Clock Delay defined in pico seconds.This is
+ used for controllers that have configurable RX internal
+ delays. If this property is present then the MAC applies
+ the RX delay.
+ tx-internal-delay-ps:
+ description:
+ RGMII Transmit Clock Delay defined in pico seconds.This is
+ used for controllers that have configurable TX internal
+ delays. If this property is present then the MAC applies
+ the TX delay.
+
additionalProperties: true
...
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v11 net-next 01/10] dt-bindings: net: make internal-delay-ps based on phy-mode
2022-03-25 16:53 ` [RFC PATCH v11 net-next 01/10] dt-bindings: net: make internal-delay-ps based on phy-mode Prasanna Vengateshan
@ 2022-03-29 20:41 ` Rob Herring
0 siblings, 0 replies; 16+ messages in thread
From: Rob Herring @ 2022-03-29 20:41 UTC (permalink / raw)
To: Prasanna Vengateshan
Cc: andrew, netdev, olteanv, UNGLinuxDriver, woojung.huh, hkallweit1,
linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
devicetree, pabeni
On Fri, Mar 25, 2022 at 10:23:32PM +0530, Prasanna Vengateshan wrote:
> *-internal-delay-ps properties would be applicable only for RGMII interface
> modes.
>
> It is changed as per the request,
> https://lore.kernel.org/netdev/d8e5f6a8-a7e1-dabd-f4b4-ea8ea21d0a1d@gmail.com/
>
> Ran dt_binding_check to confirm nothing is broken.
>
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> ---
> .../bindings/net/ethernet-controller.yaml | 37 +++++++++++++------
> 1 file changed, 25 insertions(+), 12 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> index 34c5463abcec..dc86a6479a86 100644
> --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
> @@ -123,12 +123,6 @@ properties:
> and is useful for determining certain configuration settings
> such as flow control thresholds.
>
> - rx-internal-delay-ps:
> - description: |
> - RGMII Receive Clock Delay defined in pico seconds.
> - This is used for controllers that have configurable RX internal delays.
> - If this property is present then the MAC applies the RX delay.
> -
> sfp:
> $ref: /schemas/types.yaml#/definitions/phandle
> description:
> @@ -140,12 +134,6 @@ properties:
> The size of the controller\'s transmit fifo in bytes. This
> is used for components that can have configurable fifo sizes.
>
> - tx-internal-delay-ps:
> - description: |
> - RGMII Transmit Clock Delay defined in pico seconds.
> - This is used for controllers that have configurable TX internal delays.
> - If this property is present then the MAC applies the TX delay.
> -
> managed:
> description:
> Specifies the PHY management type. If auto is set and fixed-link
> @@ -222,6 +210,31 @@ properties:
> required:
> - speed
>
> +allOf:
> + - if:
> + properties:
> + phy-mode:
> + contains:
> + enum:
> + - rgmii
> + - rgmii-rxid
> + - rgmii-txid
> + - rgmii-id
> + then:
Did you test this?
The 'then' has no effect. It's at the wrong indentation. It should be
the same as 'if'.
> + properties:
> + rx-internal-delay-ps:
> + description:
> + RGMII Receive Clock Delay defined in pico seconds.This is
> + used for controllers that have configurable RX internal
> + delays. If this property is present then the MAC applies
> + the RX delay.
> + tx-internal-delay-ps:
> + description:
> + RGMII Transmit Clock Delay defined in pico seconds.This is
> + used for controllers that have configurable TX internal
> + delays. If this property is present then the MAC applies
> + the TX delay.
> +
> additionalProperties: true
>
> ...
> --
> 2.30.2
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 02/10] dt-bindings: net: dsa: dt bindings for microchip lan937x
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 01/10] dt-bindings: net: make internal-delay-ps based on phy-mode Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-04-08 22:40 ` Vladimir Oltean
2022-03-25 16:53 ` [RFC PATCH v11 net-next 03/10] net: dsa: move mib->cnt_ptr reset code to ksz_common.c Prasanna Vengateshan
` (7 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni,
Rob Herring
Documentation in .yaml format and updates to the MAINTAINERS
Also 'make dt_binding_check' is passed.
RGMII internal delay values for the mac is retrieved from
rx-internal-delay-ps & tx-internal-delay-ps as per the feedback from
v3 patch series.
https://lore.kernel.org/netdev/20210802121550.gqgbipqdvp5x76ii@skbuf/
It supports only the delay value of 0ns and 2ns.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../bindings/net/dsa/microchip,lan937x.yaml | 160 ++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 161 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
new file mode 100644
index 000000000000..8974506d8f69
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
@@ -0,0 +1,160 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/microchip,lan937x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LAN937x Ethernet Switch Series Tree Bindings
+
+maintainers:
+ - UNGLinuxDriver@microchip.com
+
+allOf:
+ - $ref: dsa.yaml#
+
+properties:
+ compatible:
+ enum:
+ - microchip,lan9370
+ - microchip,lan9371
+ - microchip,lan9372
+ - microchip,lan9373
+ - microchip,lan9374
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 50000000
+
+ reset-gpios:
+ description: Optional gpio specifier for a reset line
+ maxItems: 1
+
+ mdio:
+ $ref: /schemas/net/mdio.yaml#
+ unevaluatedProperties: false
+
+ tx-internal-delay-ps:
+ enum: [0, 2000]
+ default: 0
+
+ rx-internal-delay-ps:
+ enum: [0, 2000]
+ default: 0
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ //Ethernet switch connected via spi to the host
+ ethernet {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ lan9374: switch@0 {
+ compatible = "microchip,lan9374";
+ reg = <0>;
+
+ spi-max-frequency = <44000000>;
+
+ ethernet-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ label = "lan1";
+ phy-mode = "internal";
+ phy-handle = <&t1phy0>;
+ };
+ port@1 {
+ reg = <1>;
+ label = "lan2";
+ phy-mode = "internal";
+ phy-handle = <&t1phy1>;
+ };
+ port@2 {
+ reg = <2>;
+ label = "lan4";
+ phy-mode = "internal";
+ phy-handle = <&t1phy2>;
+ };
+ port@3 {
+ reg = <3>;
+ label = "lan6";
+ phy-mode = "internal";
+ phy-handle = <&t1phy3>;
+ };
+ port@4 {
+ reg = <4>;
+ phy-mode = "rgmii";
+ ethernet = <ðernet>;
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ port@5 {
+ reg = <5>;
+ label = "lan7";
+ phy-mode = "rgmii";
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ port@6 {
+ reg = <6>;
+ label = "lan5";
+ phy-mode = "internal";
+ phy-handle = <&t1phy6>;
+ };
+ port@7 {
+ reg = <7>;
+ label = "lan3";
+ phy-mode = "internal";
+ phy-handle = <&t1phy7>;
+ };
+ };
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ t1phy0: ethernet-phy@0{
+ reg = <0x0>;
+ };
+ t1phy1: ethernet-phy@1{
+ reg = <0x1>;
+ };
+ t1phy2: ethernet-phy@2{
+ reg = <0x2>;
+ };
+ t1phy3: ethernet-phy@3{
+ reg = <0x3>;
+ };
+ t1phy6: ethernet-phy@6{
+ reg = <0x6>;
+ };
+ t1phy7: ethernet-phy@7{
+ reg = <0x7>;
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 91c04cb65247..373eaee3c8b9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12719,6 +12719,7 @@ M: UNGLinuxDriver@microchip.com
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
+F: Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
F: drivers/net/dsa/microchip/*
F: include/linux/platform_data/microchip-ksz.h
F: net/dsa/tag_ksz.c
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v11 net-next 02/10] dt-bindings: net: dsa: dt bindings for microchip lan937x
2022-03-25 16:53 ` [RFC PATCH v11 net-next 02/10] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
@ 2022-04-08 22:40 ` Vladimir Oltean
0 siblings, 0 replies; 16+ messages in thread
From: Vladimir Oltean @ 2022-04-08 22:40 UTC (permalink / raw)
To: Prasanna Vengateshan
Cc: andrew, netdev, robh+dt, UNGLinuxDriver, woojung.huh, hkallweit1,
linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
devicetree, pabeni, Rob Herring
On Fri, Mar 25, 2022 at 10:23:33PM +0530, Prasanna Vengateshan wrote:
> Documentation in .yaml format and updates to the MAINTAINERS
> Also 'make dt_binding_check' is passed.
>
> RGMII internal delay values for the mac is retrieved from
> rx-internal-delay-ps & tx-internal-delay-ps as per the feedback from
> v3 patch series.
> https://lore.kernel.org/netdev/20210802121550.gqgbipqdvp5x76ii@skbuf/
>
> It supports only the delay value of 0ns and 2ns.
>
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> ---
> .../bindings/net/dsa/microchip,lan937x.yaml | 160 ++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 161 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
>
> diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> new file mode 100644
> index 000000000000..8974506d8f69
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> @@ -0,0 +1,160 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/dsa/microchip,lan937x.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: LAN937x Ethernet Switch Series Tree Bindings
> +
> +maintainers:
> + - UNGLinuxDriver@microchip.com
> +
> +allOf:
> + - $ref: dsa.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - microchip,lan9370
> + - microchip,lan9371
> + - microchip,lan9372
> + - microchip,lan9373
> + - microchip,lan9374
> +
> + reg:
> + maxItems: 1
> +
> + spi-max-frequency:
> + maximum: 50000000
> +
> + reset-gpios:
> + description: Optional gpio specifier for a reset line
> + maxItems: 1
> +
> + mdio:
> + $ref: /schemas/net/mdio.yaml#
> + unevaluatedProperties: false
> +
> + tx-internal-delay-ps:
> + enum: [0, 2000]
> + default: 0
> +
> + rx-internal-delay-ps:
> + enum: [0, 2000]
> + default: 0
Why are "tx-internal-delay-ps" and "rx-internal-delay-ps" properties of
the switch node and not of individual port nodes?
> +
> +required:
> + - compatible
> + - reg
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + //Ethernet switch connected via spi to the host
Comment seems off, this node doesn't correspond to an Ethernet switch
but to the DSA master.
> + ethernet {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + fixed-link {
> + speed = <1000>;
> + full-duplex;
> + };
> + };
> +
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + lan9374: switch@0 {
> + compatible = "microchip,lan9374";
> + reg = <0>;
> +
> + spi-max-frequency = <44000000>;
> +
> + ethernet-ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
Some blank lines here and there to separate the nodes from the
properties and from each other would go a long way. Similarly,
properties don't need new lines between each other ("reg" and
"spi-max-frequency").
> + port@0 {
> + reg = <0>;
> + label = "lan1";
> + phy-mode = "internal";
> + phy-handle = <&t1phy0>;
> + };
> + port@1 {
> + reg = <1>;
> + label = "lan2";
> + phy-mode = "internal";
> + phy-handle = <&t1phy1>;
> + };
> + port@2 {
> + reg = <2>;
> + label = "lan4";
> + phy-mode = "internal";
> + phy-handle = <&t1phy2>;
> + };
> + port@3 {
> + reg = <3>;
> + label = "lan6";
> + phy-mode = "internal";
> + phy-handle = <&t1phy3>;
> + };
> + port@4 {
> + reg = <4>;
> + phy-mode = "rgmii";
> + ethernet = <ðernet>;
shouldn't the "ethernet" node have a label for this to work? Does this
example compile?
> + fixed-link {
> + speed = <1000>;
> + full-duplex;
> + };
> + };
> + port@5 {
> + reg = <5>;
> + label = "lan7";
> + phy-mode = "rgmii";
> + fixed-link {
> + speed = <1000>;
> + full-duplex;
> + };
> + };
> + port@6 {
> + reg = <6>;
> + label = "lan5";
> + phy-mode = "internal";
> + phy-handle = <&t1phy6>;
> + };
> + port@7 {
> + reg = <7>;
> + label = "lan3";
> + phy-mode = "internal";
> + phy-handle = <&t1phy7>;
> + };
> + };
> +
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + t1phy0: ethernet-phy@0{
> + reg = <0x0>;
> + };
> + t1phy1: ethernet-phy@1{
> + reg = <0x1>;
> + };
> + t1phy2: ethernet-phy@2{
> + reg = <0x2>;
> + };
> + t1phy3: ethernet-phy@3{
> + reg = <0x3>;
> + };
> + t1phy6: ethernet-phy@6{
> + reg = <0x6>;
> + };
> + t1phy7: ethernet-phy@7{
> + reg = <0x7>;
> + };
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 91c04cb65247..373eaee3c8b9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12719,6 +12719,7 @@ M: UNGLinuxDriver@microchip.com
> L: netdev@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
> +F: Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> F: drivers/net/dsa/microchip/*
> F: include/linux/platform_data/microchip-ksz.h
> F: net/dsa/tag_ksz.c
> --
> 2.30.2
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 03/10] net: dsa: move mib->cnt_ptr reset code to ksz_common.c
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 01/10] dt-bindings: net: make internal-delay-ps based on phy-mode Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 02/10] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 04/10] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
` (6 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
mib->cnt_ptr resetting is handled in multiple places as part of
port_init_cnt(). Hence moved mib->cnt_ptr code to ksz common layer
and removed from individual product files.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/microchip/ksz8795.c | 2 --
drivers/net/dsa/microchip/ksz9477.c | 3 ---
drivers/net/dsa/microchip/ksz_common.c | 8 +++++++-
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
index b2752978cb09..2da4e85e33b0 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -468,8 +468,6 @@ static void ksz8_port_init_cnt(struct ksz_device *dev, int port)
dropped, &mib->counters[mib->cnt_ptr]);
++mib->cnt_ptr;
}
- mib->cnt_ptr = 0;
- memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
}
static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 8222c8a6c5ec..346ce840f871 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -410,9 +410,6 @@ static void ksz9477_port_init_cnt(struct ksz_device *dev, int port)
ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0);
mutex_unlock(&mib->cnt_mutex);
-
- mib->cnt_ptr = 0;
- memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
}
static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds,
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 8014b18d9391..36f8ed03f615 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -146,8 +146,14 @@ void ksz_init_mib_timer(struct ksz_device *dev)
INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work);
- for (i = 0; i < dev->port_cnt; i++)
+ for (i = 0; i < dev->port_cnt; i++) {
+ struct ksz_port_mib *mib = &dev->ports[i].mib;
+
dev->dev_ops->port_init_cnt(dev, i);
+
+ mib->cnt_ptr = 0;
+ memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
+ }
}
EXPORT_SYMBOL_GPL(ksz_init_mib_timer);
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 04/10] net: dsa: tag_ksz: add tag handling for Microchip LAN937x
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (2 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 03/10] net: dsa: move mib->cnt_ptr reset code to ksz_common.c Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 05/10] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
` (5 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
The Microchip LAN937X switches have a tagging protocol which is
very similar to KSZ tagging. So that the implementation is added to
tag_ksz.c and reused common APIs
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 2 ++
net/dsa/Kconfig | 4 ++--
net/dsa/tag_ksz.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 934958fda962..e19da7c00ba0 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -53,6 +53,7 @@ struct phylink_link_state;
#define DSA_TAG_PROTO_SJA1110_VALUE 23
#define DSA_TAG_PROTO_RTL8_4_VALUE 24
#define DSA_TAG_PROTO_RTL8_4T_VALUE 25
+#define DSA_TAG_PROTO_LAN937X_VALUE 26
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -81,6 +82,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_SJA1110 = DSA_TAG_PROTO_SJA1110_VALUE,
DSA_TAG_PROTO_RTL8_4 = DSA_TAG_PROTO_RTL8_4_VALUE,
DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE,
+ DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 8cb87b5067ee..6d0414c9f7f4 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -87,10 +87,10 @@ config NET_DSA_TAG_MTK
Mediatek switches.
config NET_DSA_TAG_KSZ
- tristate "Tag driver for Microchip 8795/9477/9893 families of switches"
+ tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches"
help
Say Y if you want to enable support for tagging frames for the
- Microchip 8795/9477/9893 families of switches.
+ Microchip 8795/937x/9477/9893 families of switches.
config NET_DSA_TAG_OCELOT
tristate "Tag driver for Ocelot family of switches, using NPI port"
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 3509fc967ca9..38fa19c1e2d5 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -193,10 +193,69 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893);
+/* For xmit, 2 bytes are added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : represents tag override, lookup and valid
+ * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
+ *
+ * For rcv, 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ * (eg, 0x00=port1, 0x02=port3, 0x07=port8)
+ */
+#define LAN937X_EGRESS_TAG_LEN 2
+
+#define LAN937X_TAIL_TAG_BLOCKING_OVERRIDE BIT(11)
+#define LAN937X_TAIL_TAG_LOOKUP BIT(12)
+#define LAN937X_TAIL_TAG_VALID BIT(13)
+#define LAN937X_TAIL_TAG_PORT_MASK 7
+
+static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ const struct ethhdr *hdr = eth_hdr(skb);
+ __be16 *tag;
+ u16 val;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))
+ return NULL;
+
+ tag = skb_put(skb, LAN937X_EGRESS_TAG_LEN);
+
+ val = BIT(dp->index);
+
+ if (is_link_local_ether_addr(hdr->h_dest))
+ val |= LAN937X_TAIL_TAG_BLOCKING_OVERRIDE;
+
+ /* Tail tag valid bit - This bit should always be set by the CPU */
+ val |= LAN937X_TAIL_TAG_VALID;
+
+ put_unaligned_be16(val, tag);
+
+ return skb;
+}
+
+static const struct dsa_device_ops lan937x_netdev_ops = {
+ .name = "lan937x",
+ .proto = DSA_TAG_PROTO_LAN937X,
+ .xmit = lan937x_xmit,
+ .rcv = ksz9477_rcv,
+ .needed_tailroom = LAN937X_EGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(lan937x_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X);
+
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
+ &DSA_TAG_DRIVER_NAME(lan937x_netdev_ops),
};
module_dsa_tag_drivers(dsa_tag_driver_array);
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 05/10] net: dsa: microchip: add DSA support for microchip lan937x
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (3 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 04/10] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-04-08 23:25 ` Vladimir Oltean
2022-03-25 16:53 ` [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
` (4 subsequent siblings)
9 siblings, 1 reply; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
Basic DSA driver support for lan937x and the device will be
configured through SPI interface.
drivers/net/dsa/microchip/ path is already part of MAINTAINERS &
the new files come under this path. Hence no update needed to the
MAINTAINERS
Reused KSZ APIs for port_bridge_join() & port_bridge_leave() and
added support for port_stp_state_set() & port_fast_age().
lan937x_flush_dyn_mac_table() which gets called from
port_fast_age() of KSZ common layer, hence added support for it.
RGMII internal delay values for the mac is retrieved from
rx-internal-delay-ps & tx-internal-delay-ps as per the feedback from
v3 patch series.
https://lore.kernel.org/netdev/20210802121550.gqgbipqdvp5x76ii@skbuf/
It supports standard delay 2ns only. If the property is not found, the value
will be forced to 0.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
drivers/net/dsa/microchip/Kconfig | 12 +
drivers/net/dsa/microchip/Makefile | 5 +
drivers/net/dsa/microchip/ksz_common.h | 5 +
drivers/net/dsa/microchip/lan937x_dev.c | 631 +++++++++++++++++++++
drivers/net/dsa/microchip/lan937x_dev.h | 117 ++++
drivers/net/dsa/microchip/lan937x_main.c | 320 +++++++++++
drivers/net/dsa/microchip/lan937x_reg.h | 688 +++++++++++++++++++++++
drivers/net/dsa/microchip/lan937x_spi.c | 236 ++++++++
8 files changed, 2014 insertions(+)
create mode 100644 drivers/net/dsa/microchip/lan937x_dev.c
create mode 100644 drivers/net/dsa/microchip/lan937x_dev.h
create mode 100644 drivers/net/dsa/microchip/lan937x_main.c
create mode 100644 drivers/net/dsa/microchip/lan937x_reg.h
create mode 100644 drivers/net/dsa/microchip/lan937x_spi.c
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
index c9e2a8989556..f329cca934ee 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -3,6 +3,18 @@ config NET_DSA_MICROCHIP_KSZ_COMMON
select NET_DSA_TAG_KSZ
tristate
+config NET_DSA_MICROCHIP_LAN937X
+ tristate "Microchip LAN937X series SPI connected switch support"
+ depends on NET_DSA && SPI
+ select NET_DSA_MICROCHIP_KSZ_COMMON
+ select REGMAP_SPI
+ help
+ This driver adds support for Microchip LAN937X series
+ switch chips.
+
+ Select to enable support for registering switches configured
+ through SPI.
+
menuconfig NET_DSA_MICROCHIP_KSZ9477
tristate "Microchip KSZ9477 series switch support"
depends on NET_DSA
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 2a03b21a3386..28d8eb62a795 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -6,3 +6,8 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795) += ksz8795.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795_SPI) += ksz8795_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
+
+obj-$(CONFIG_NET_DSA_MICROCHIP_LAN937X) += lan937x.o
+lan937x-objs := lan937x_dev.o
+lan937x-objs += lan937x_main.o
+lan937x-objs += lan937x_spi.o
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 485d4a948c38..372170571088 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -42,6 +42,8 @@ struct ksz_port {
struct ksz_port_mib mib;
phy_interface_t interface;
u16 max_frame;
+ u32 rgmii_tx_val;
+ u32 rgmii_rx_val;
};
struct ksz_device {
@@ -148,6 +150,9 @@ void ksz_switch_remove(struct ksz_device *dev);
int ksz8_switch_register(struct ksz_device *dev);
int ksz9477_switch_register(struct ksz_device *dev);
+int lan937x_switch_register(struct ksz_device *dev);
+
+int lan937x_check_device_id(struct ksz_device *dev);
void ksz_update_port_member(struct ksz_device *dev, int port);
void ksz_init_mib_timer(struct ksz_device *dev);
diff --git a/drivers/net/dsa/microchip/lan937x_dev.c b/drivers/net/dsa/microchip/lan937x_dev.c
new file mode 100644
index 000000000000..784a24d0aa08
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.c
@@ -0,0 +1,631 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip lan937x dev ops functions
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "lan937x_reg.h"
+#include "ksz_common.h"
+#include "lan937x_dev.h"
+
+const struct mib_names lan937x_mib_names[] = {
+ { 0x00, "rx_hi" },
+ { 0x01, "rx_undersize" },
+ { 0x02, "rx_fragments" },
+ { 0x03, "rx_oversize" },
+ { 0x04, "rx_jabbers" },
+ { 0x05, "rx_symbol_err" },
+ { 0x06, "rx_crc_err" },
+ { 0x07, "rx_align_err" },
+ { 0x08, "rx_mac_ctrl" },
+ { 0x09, "rx_pause" },
+ { 0x0A, "rx_bcast" },
+ { 0x0B, "rx_mcast" },
+ { 0x0C, "rx_ucast" },
+ { 0x0D, "rx_64_or_less" },
+ { 0x0E, "rx_65_127" },
+ { 0x0F, "rx_128_255" },
+ { 0x10, "rx_256_511" },
+ { 0x11, "rx_512_1023" },
+ { 0x12, "rx_1024_1522" },
+ { 0x13, "rx_1523_2000" },
+ { 0x14, "rx_2001" },
+ { 0x15, "tx_hi" },
+ { 0x16, "tx_late_col" },
+ { 0x17, "tx_pause" },
+ { 0x18, "tx_bcast" },
+ { 0x19, "tx_mcast" },
+ { 0x1A, "tx_ucast" },
+ { 0x1B, "tx_deferred" },
+ { 0x1C, "tx_total_col" },
+ { 0x1D, "tx_exc_col" },
+ { 0x1E, "tx_single_col" },
+ { 0x1F, "tx_mult_col" },
+ { 0x80, "rx_total" },
+ { 0x81, "tx_total" },
+ { 0x82, "rx_discards" },
+ { 0x83, "tx_discards" },
+};
+
+int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+ return regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
+}
+
+int lan937x_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
+ bool set)
+{
+ return regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset),
+ bits, set ? bits : 0);
+}
+
+int lan937x_pread8(struct ksz_device *dev, int port, int offset, u8 *data)
+{
+ return ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pread16(struct ksz_device *dev, int port, int offset, u16 *data)
+{
+ return ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pread32(struct ksz_device *dev, int port, int offset, u32 *data)
+{
+ return ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite8(struct ksz_device *dev, int port, int offset, u8 data)
+{
+ return ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite16(struct ksz_device *dev, int port, int offset, u16 data)
+{
+ return ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite32(struct ksz_device *dev, int port, int offset, u32 data)
+{
+ return ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_cfg_port_member(struct ksz_device *dev, int port, u8 member)
+{
+ lan937x_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member);
+}
+
+static void lan937x_flush_dyn_mac_table(struct ksz_device *dev, int port)
+{
+ unsigned int value;
+ u8 data;
+
+ regmap_update_bits(dev->regmap[0], REG_SW_LUE_CTRL_2,
+ SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S,
+ SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S);
+
+ if (port < dev->port_cnt) {
+ /* flush individual port */
+ lan937x_pread8(dev, port, P_STP_CTRL, &data);
+ if (!(data & PORT_LEARN_DISABLE))
+ lan937x_pwrite8(dev, port, P_STP_CTRL,
+ (data | PORT_LEARN_DISABLE));
+ lan937x_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE,
+ true);
+
+ regmap_read_poll_timeout(dev->regmap[0], S_FLUSH_TABLE_CTRL,
+ value,
+ !(value & SW_FLUSH_DYN_MAC_TABLE), 10,
+ 1000);
+
+ lan937x_pwrite8(dev, port, P_STP_CTRL, data);
+ } else {
+ /* flush all */
+ lan937x_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_STP_TABLE, true);
+ }
+}
+
+static void lan937x_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
+ u64 *cnt)
+{
+ unsigned int val;
+ u32 data;
+ int ret;
+
+ /* Enable MIB Counter read */
+ data = MIB_COUNTER_READ;
+ data |= (addr << MIB_COUNTER_INDEX_S);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT, data);
+
+ ret = regmap_read_poll_timeout(dev->regmap[2],
+ PORT_CTRL_ADDR(port,
+ REG_PORT_MIB_CTRL_STAT),
+ val, !(val & MIB_COUNTER_READ),
+ 10, 1000);
+ if (ret) {
+ dev_err(dev->dev, "Failed to get MIB\n");
+ return;
+ }
+
+ /* count resets upon read */
+ lan937x_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+ *cnt += data;
+}
+
+void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+ u64 *dropped, u64 *cnt)
+{
+ addr = lan937x_mib_names[addr].index;
+ lan937x_r_mib_cnt(dev, port, addr, cnt);
+}
+
+static void lan937x_r_mib_stats64(struct ksz_device *dev, int port)
+{
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ struct rtnl_link_stats64 *s;
+ u64 *ctr = mib->counters;
+
+ s = &mib->stats64;
+ spin_lock(&mib->stats64_lock);
+
+ s->rx_packets = ctr[lan937x_mib_rx_mcast] +
+ ctr[lan937x_mib_rx_bcast] +
+ ctr[lan937x_mib_rx_ucast] +
+ ctr[lan937x_mib_rx_pause];
+
+ s->tx_packets = ctr[lan937x_mib_tx_mcast] +
+ ctr[lan937x_mib_tx_bcast] +
+ ctr[lan937x_mib_tx_ucast] +
+ ctr[lan937x_mib_tx_pause];
+
+ s->rx_bytes = ctr[lan937x_mib_rx_total];
+ s->tx_bytes = ctr[lan937x_mib_tx_total];
+
+ s->rx_errors = ctr[lan937x_mib_rx_fragments] +
+ ctr[lan937x_mib_rx_jabbers] +
+ ctr[lan937x_mib_rx_sym_err] +
+ ctr[lan937x_mib_rx_align_err] +
+ ctr[lan937x_mib_rx_crc_err];
+
+ s->tx_errors = ctr[lan937x_mib_tx_exc_col] +
+ ctr[lan937x_mib_tx_late_col];
+
+ s->rx_dropped = ctr[lan937x_mib_rx_discard];
+ s->tx_dropped = ctr[lan937x_mib_tx_discard];
+ s->multicast = ctr[lan937x_mib_rx_mcast];
+
+ s->collisions = ctr[lan937x_mib_tx_late_col] +
+ ctr[lan937x_mib_tx_single_col] +
+ ctr[lan937x_mib_tx_mult_col];
+
+ s->rx_length_errors = ctr[lan937x_mib_rx_fragments] +
+ ctr[lan937x_mib_rx_jabbers];
+
+ s->rx_crc_errors = ctr[lan937x_mib_rx_crc_err];
+ s->rx_frame_errors = ctr[lan937x_mib_rx_align_err];
+ s->tx_aborted_errors = ctr[lan937x_mib_tx_exc_col];
+ s->tx_window_errors = ctr[lan937x_mib_tx_late_col];
+
+ spin_unlock(&mib->stats64_lock);
+}
+
+static void lan937x_port_init_cnt(struct ksz_device *dev, int port)
+{
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+
+ /* flush all enabled port MIB counters */
+ mutex_lock(&mib->cnt_mutex);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT,
+ MIB_COUNTER_FLUSH_FREEZE);
+ ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
+ lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT, 0);
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+int lan937x_reset_switch(struct ksz_device *dev)
+{
+ u32 data32;
+ u8 data8;
+ int ret;
+
+ /* reset switch */
+ ret = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Auto Aging */
+ ret = ksz_write8(dev, REG_SW_LUE_CTRL_1, data8 | SW_LINK_AUTO_AGING);
+ if (ret < 0)
+ return ret;
+
+ /* disable interrupts */
+ ret = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
+ if (ret < 0)
+ return ret;
+
+ return ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+}
+
+static int lan937x_switch_detect(struct ksz_device *dev)
+{
+ u32 id32;
+ int ret;
+
+ /* Read Chip ID */
+ ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
+ if (ret < 0)
+ return ret;
+
+ if (id32 != 0 && id32 != 0xffffffff) {
+ dev->chip_id = id32;
+ dev_info(dev->dev, "Chip ID: 0x%x", id32);
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void lan937x_switch_exit(struct ksz_device *dev)
+{
+ lan937x_reset_switch(dev);
+}
+
+int lan937x_enable_spi_indirect_access(struct ksz_device *dev)
+{
+ u16 data16;
+ u8 data8;
+ int ret;
+
+ ret = ksz_read8(dev, REG_GLOBAL_CTRL_0, &data8);
+ if (ret < 0)
+ return ret;
+
+ /* Check if PHY register is blocked */
+ if (data8 & SW_PHY_REG_BLOCK) {
+ /* Enable Phy access through SPI */
+ data8 &= ~SW_PHY_REG_BLOCK;
+
+ ret = ksz_write8(dev, REG_GLOBAL_CTRL_0, data8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16);
+ if (ret < 0)
+ return ret;
+
+ /* Allow SPI access */
+ data16 |= VPHY_SPI_INDIRECT_ENABLE;
+
+ return ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16);
+}
+
+static u32 lan937x_get_port_addr(int port, int offset)
+{
+ return PORT_CTRL_ADDR(port, offset);
+}
+
+bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port)
+{
+ /* Check if the port is RGMII */
+ if (port == LAN937X_RGMII_1_PORT || port == LAN937X_RGMII_2_PORT)
+ return false;
+
+ /* Check if the port is SGMII */
+ if (port == LAN937X_SGMII_PORT &&
+ GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_73)
+ return false;
+
+ return true;
+}
+
+bool lan937x_is_rgmii_port(struct ksz_device *dev, int port)
+{
+ /* Check if the port is RGMII */
+ if (port == LAN937X_RGMII_1_PORT || port == LAN937X_RGMII_2_PORT)
+ return true;
+
+ return false;
+}
+
+bool lan937x_is_internal_base_tx_phy_port(struct ksz_device *dev, int port)
+{
+ /* Check if the port is internal tx phy port */
+ if (lan937x_is_internal_phy_port(dev, port) &&
+ port == LAN937X_TXPHY_PORT)
+ if ((GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_71) ||
+ (GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_72))
+ return true;
+
+ return false;
+}
+
+bool lan937x_is_internal_base_t1_phy_port(struct ksz_device *dev, int port)
+{
+ /* Check if the port is internal t1 phy port */
+ if (lan937x_is_internal_phy_port(dev, port) &&
+ !lan937x_is_internal_base_tx_phy_port(dev, port))
+ return true;
+
+ return false;
+}
+
+static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg)
+{
+ u16 temp, addr_base;
+
+ if (lan937x_is_internal_base_tx_phy_port(dev, addr))
+ addr_base = REG_PORT_TX_PHY_CTRL_BASE;
+ else
+ addr_base = REG_PORT_T1_PHY_CTRL_BASE;
+
+ /* get register address based on the logical port */
+ temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
+
+ return ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+}
+
+int lan937x_internal_phy_write(struct ksz_device *dev, int addr, int reg,
+ u16 val)
+{
+ unsigned int value;
+ int ret;
+
+ /* Check for internal phy port */
+ if (!lan937x_is_internal_phy_port(dev, addr))
+ return -EOPNOTSUPP;
+
+ ret = lan937x_vphy_ind_addr_wr(dev, addr, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Write the data to be written to the VPHY reg */
+ ret = ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
+ if (ret < 0)
+ return ret;
+
+ /* Write the Write En and Busy bit */
+ ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2,
+ (VPHY_IND_WRITE | VPHY_IND_BUSY));
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2,
+ value, !(value & VPHY_IND_BUSY), 10,
+ 1000);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to write phy register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int lan937x_internal_phy_read(struct ksz_device *dev, int addr, int reg,
+ u16 *val)
+{
+ unsigned int value;
+ int ret;
+
+ /* Check for internal phy port, return 0xffff for non-existent phy */
+ if (!lan937x_is_internal_phy_port(dev, addr))
+ return 0xffff;
+
+ ret = lan937x_vphy_ind_addr_wr(dev, addr, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Write Read and Busy bit to start the transaction */
+ ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2,
+ value, !(value & VPHY_IND_BUSY), 10,
+ 1000);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read phy register\n");
+ return ret;
+ }
+
+ /* Read the VPHY register which has the PHY data */
+ return ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
+}
+
+void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+ struct dsa_switch *ds = dev->ds;
+ u8 member;
+
+ /* enable tag tail for host port */
+ if (cpu_port)
+ lan937x_port_cfg(dev, port, REG_PORT_CTRL_0,
+ PORT_TAIL_TAG_ENABLE, true);
+
+ /* disable frame check length field */
+ lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_FR_CHK_LENGTH,
+ false);
+
+ /* set back pressure for half duplex */
+ lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE,
+ true);
+
+ /* enable 802.1p priority */
+ lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
+
+ if (!lan937x_is_internal_phy_port(dev, port))
+ lan937x_port_cfg(dev, port, REG_PORT_XMII_CTRL_0,
+ PORT_TX_FLOW_CTRL | PORT_RX_FLOW_CTRL,
+ true);
+
+ if (cpu_port)
+ member = dsa_user_ports(ds);
+ else
+ member = BIT(dsa_upstream_port(ds, port));
+
+ lan937x_cfg_port_member(dev, port, member);
+}
+
+static int lan937x_sw_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct ksz_device *dev = bus->priv;
+ u16 val;
+ int ret;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ ret = lan937x_internal_phy_read(dev, addr, regnum, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int lan937x_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ struct ksz_device *dev = bus->priv;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ return lan937x_internal_phy_write(dev, addr, regnum, val);
+}
+
+static int lan937x_mdio_register(struct ksz_device *dev)
+{
+ struct dsa_switch *ds = dev->ds;
+ struct device_node *mdio_np;
+ struct mii_bus *bus;
+ int ret;
+
+ mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio");
+ if (!mdio_np) {
+ dev_err(ds->dev, "no MDIO bus node\n");
+ return -ENODEV;
+ }
+
+ bus = devm_mdiobus_alloc(ds->dev);
+ if (!bus) {
+ of_node_put(mdio_np);
+ return -ENOMEM;
+ }
+
+ bus->priv = dev;
+ bus->read = lan937x_sw_mdio_read;
+ bus->write = lan937x_sw_mdio_write;
+ bus->name = "lan937x slave smi";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index);
+ bus->parent = ds->dev;
+ bus->phy_mask = ~ds->phys_mii_mask;
+
+ ds->slave_mii_bus = bus;
+
+ ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np);
+ if (ret) {
+ dev_err(ds->dev, "unable to register MDIO bus %s\n",
+ bus->id);
+ }
+
+ of_node_put(mdio_np);
+
+ return ret;
+}
+
+static int lan937x_switch_init(struct ksz_device *dev)
+{
+ int i, ret;
+
+ dev->ds->ops = &lan937x_switch_ops;
+
+ /* Check device tree */
+ ret = lan937x_check_device_id(dev);
+ if (ret < 0)
+ return ret;
+
+ dev->port_mask = (1 << dev->port_cnt) - 1;
+
+ dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
+ dev->mib_cnt = ARRAY_SIZE(lan937x_mib_names);
+
+ dev->ports = devm_kzalloc(dev->dev,
+ dev->port_cnt * sizeof(struct ksz_port),
+ GFP_KERNEL);
+ if (!dev->ports)
+ return -ENOMEM;
+
+ for (i = 0; i < dev->port_cnt; i++) {
+ spin_lock_init(&dev->ports[i].mib.stats64_lock);
+ mutex_init(&dev->ports[i].mib.cnt_mutex);
+ dev->ports[i].mib.counters =
+ devm_kzalloc(dev->dev,
+ sizeof(u64) * (dev->mib_cnt + 1),
+ GFP_KERNEL);
+
+ if (!dev->ports[i].mib.counters)
+ return -ENOMEM;
+ }
+
+ /* set the real number of ports */
+ dev->ds->num_ports = dev->port_cnt;
+ return 0;
+}
+
+static int lan937x_init(struct ksz_device *dev)
+{
+ int ret;
+
+ ret = lan937x_switch_init(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to initialize the switch");
+ return ret;
+ }
+
+ /* enable Indirect Access from SPI to the VPHY registers */
+ ret = lan937x_enable_spi_indirect_access(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to enable spi indirect access");
+ return ret;
+ }
+
+ ret = lan937x_mdio_register(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to register the mdio");
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct ksz_dev_ops lan937x_dev_ops = {
+ .get_port_addr = lan937x_get_port_addr,
+ .cfg_port_member = lan937x_cfg_port_member,
+ .flush_dyn_mac_table = lan937x_flush_dyn_mac_table,
+ .port_setup = lan937x_port_setup,
+ .r_mib_cnt = lan937x_r_mib_cnt,
+ .r_mib_pkt = lan937x_r_mib_pkt,
+ .port_init_cnt = lan937x_port_init_cnt,
+ .r_mib_stat64 = lan937x_r_mib_stats64,
+ .shutdown = lan937x_reset_switch,
+ .detect = lan937x_switch_detect,
+ .init = lan937x_init,
+ .exit = lan937x_switch_exit,
+};
diff --git a/drivers/net/dsa/microchip/lan937x_dev.h b/drivers/net/dsa/microchip/lan937x_dev.h
new file mode 100644
index 000000000000..eba36912969a
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip lan937x dev ops headers
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+
+#ifndef __LAN937X_CFG_H
+#define __LAN937X_CFG_H
+
+int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set);
+int lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
+ u8 bits, bool set);
+int lan937x_pread8(struct ksz_device *dev, int port, int offset,
+ u8 *data);
+int lan937x_pread16(struct ksz_device *dev, int port, int offset,
+ u16 *data);
+int lan937x_pread32(struct ksz_device *dev, int port, int offset,
+ u32 *data);
+int lan937x_pwrite8(struct ksz_device *dev, int port,
+ int offset, u8 data);
+int lan937x_pwrite16(struct ksz_device *dev, int port,
+ int offset, u16 data);
+int lan937x_pwrite32(struct ksz_device *dev, int port,
+ int offset, u32 data);
+int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
+ int reg, u16 val);
+int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
+ int reg, u16 *val);
+bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port);
+bool lan937x_is_internal_base_tx_phy_port(struct ksz_device *dev, int port);
+bool lan937x_is_internal_base_t1_phy_port(struct ksz_device *dev, int port);
+bool lan937x_is_rgmii_port(struct ksz_device *dev, int port);
+int lan937x_reset_switch(struct ksz_device *dev);
+void lan937x_cfg_port_member(struct ksz_device *dev, int port,
+ u8 member);
+void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port);
+int lan937x_enable_spi_indirect_access(struct ksz_device *dev);
+void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+ u64 *dropped, u64 *cnt);
+
+struct mib_names {
+ int index;
+ char string[ETH_GSTRING_LEN];
+};
+
+enum lan937x_mib_list {
+ lan937x_mib_rx_hi_pri_byte = 0,
+ lan937x_mib_rx_undersize,
+ lan937x_mib_rx_fragments,
+ lan937x_mib_rx_oversize,
+ lan937x_mib_rx_jabbers,
+ lan937x_mib_rx_sym_err,
+ lan937x_mib_rx_crc_err,
+ lan937x_mib_rx_align_err,
+ lan937x_mib_rx_mac_ctrl,
+ lan937x_mib_rx_pause,
+ lan937x_mib_rx_bcast,
+ lan937x_mib_rx_mcast,
+ lan937x_mib_rx_ucast,
+ lan937x_mib_rx_64_or_less,
+ lan937x_mib_rx_65_127,
+ lan937x_mib_rx_128_255,
+ lan937x_mib_rx_256_511,
+ lan937x_mib_rx_512_1023,
+ lan937x_mib_rx_1024_1522,
+ lan937x_mib_rx_1523_2000,
+ lan937x_mib_rx_2001,
+ lan937x_mib_tx_hi_pri_byte,
+ lan937x_mib_tx_late_col,
+ lan937x_mib_tx_pause,
+ lan937x_mib_tx_bcast,
+ lan937x_mib_tx_mcast,
+ lan937x_mib_tx_ucast,
+ lan937x_mib_tx_deferred,
+ lan937x_mib_tx_total_col,
+ lan937x_mib_tx_exc_col,
+ lan937x_mib_tx_single_col,
+ lan937x_mib_tx_mult_col,
+ lan937x_mib_rx_total,
+ lan937x_mib_tx_total,
+ lan937x_mib_rx_discard,
+ lan937x_mib_tx_discard,
+};
+
+struct lan_alu_struct {
+ /* entry 1 */
+ u32 is_static:1;
+ u32 is_src_filter:1;
+ u32 is_dst_filter:1;
+ u32 prio_age:3;
+ u32 _reserv_0_1:23;
+ u32 mstp:3;
+ /* entry 2 */
+ u32 is_override:1;
+ u32 is_use_fid:1;
+ u32 _reserv_1_1:22;
+ u32 port_forward:8;
+ /* entry 3 & 4*/
+ u32 _reserv_2_1:9;
+ u32 fid:7;
+ u8 mac[ETH_ALEN];
+};
+
+struct lan937x_vlan {
+ /* entry 1 */
+ bool valid;
+ u8 fid;
+ /* entry 2 */
+ u32 untag_prtmap;
+ /* entry 3 */
+ u32 fwd_map;
+};
+
+extern const struct dsa_switch_ops lan937x_switch_ops;
+extern const struct ksz_dev_ops lan937x_dev_ops;
+extern const struct mib_names lan937x_mib_names[];
+
+#endif
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
new file mode 100644
index 000000000000..b5164be297c8
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver main logic
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
+#include <linux/math.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "lan937x_reg.h"
+#include "ksz_common.h"
+#include "lan937x_dev.h"
+
+static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
+ int port,
+ enum dsa_tag_protocol mp)
+{
+ return DSA_TAG_PROTO_LAN937X_VALUE;
+}
+
+static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 val;
+ int ret;
+
+ ret = lan937x_internal_phy_read(dev, addr, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
+ u16 val)
+{
+ struct ksz_device *dev = ds->priv;
+
+ return lan937x_internal_phy_write(dev, addr, reg, val);
+}
+
+static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
+ u8 state)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *p;
+ u8 data;
+
+ lan937x_pread8(dev, port, P_STP_CTRL, &data);
+ data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ data |= PORT_LEARN_DISABLE;
+ break;
+ case BR_STATE_LISTENING:
+ data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+ break;
+ case BR_STATE_LEARNING:
+ data |= PORT_RX_ENABLE;
+ break;
+ case BR_STATE_FORWARDING:
+ data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+ break;
+ case BR_STATE_BLOCKING:
+ data |= PORT_LEARN_DISABLE;
+ break;
+ default:
+ dev_err(ds->dev, "invalid STP state: %d\n", state);
+ return;
+ }
+
+ lan937x_pwrite8(dev, port, P_STP_CTRL, data);
+
+ p = &dev->ports[port];
+ p->stp_state = state;
+
+ ksz_update_port_member(dev, port);
+}
+
+static void lan937x_config_cpu_port(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ int i;
+
+ ds->num_ports = dev->port_cnt;
+
+ for (i = 0; i < dev->port_cnt; i++) {
+ if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+ dev->cpu_port = i;
+
+ /* enable cpu port */
+ lan937x_port_setup(dev, i, true);
+ }
+ }
+
+ for (i = 0; i < dev->port_cnt; i++) {
+ if (i == dev->cpu_port)
+ continue;
+
+ lan937x_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+ }
+}
+
+static int lan937x_set_rgmii_delay(struct ksz_device *dev, int port,
+ u32 val, bool is_tx)
+{
+ struct ksz_port *p = &dev->ports[port];
+ const char *name[2] = { "rx", "tx" };
+
+ /* alert if delay is out of range */
+ if (val != 0 && val != 2000) {
+ dev_err(dev->dev,
+ "rgmii %s delay %d is out of range for the port %d\n",
+ name[is_tx], val, port);
+ return -EOPNOTSUPP;
+ }
+
+ /* get the valid value & store it for delay calculation */
+ if (is_tx)
+ p->rgmii_tx_val = val;
+ else
+ p->rgmii_rx_val = val;
+
+ return 0;
+}
+
+static int lan937x_parse_dt_rgmii_delay(struct ksz_device *dev)
+{
+ struct device_node *ports, *port;
+ int err, p;
+ u32 val;
+
+ ports = of_get_child_by_name(dev->dev->of_node, "ports");
+ if (!ports)
+ ports = of_get_child_by_name(dev->dev->of_node,
+ "ethernet-ports");
+ if (!ports) {
+ dev_err(dev->dev, "no ports child node found\n");
+ return -EINVAL;
+ }
+
+ for_each_available_child_of_node(ports, port) {
+ err = of_property_read_u32(port, "reg", &p);
+ if (err) {
+ dev_err(dev->dev, "Port num not defined in the DT, \"reg\" property\n");
+ of_node_put(ports);
+ of_node_put(port);
+ return err;
+ }
+
+ /* skip for internal ports */
+ if (lan937x_is_internal_phy_port(dev, p))
+ continue;
+
+ if (of_property_read_u32(port, "rx-internal-delay-ps", &val))
+ val = 0;
+
+ err = lan937x_set_rgmii_delay(dev, p, val, false);
+ if (err)
+ break;
+
+ if (of_property_read_u32(port, "tx-internal-delay-ps", &val))
+ val = 0;
+
+ err = lan937x_set_rgmii_delay(dev, p, val, true);
+ if (err)
+ break;
+ }
+
+ of_node_put(ports);
+ return err;
+}
+
+static int lan937x_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ u32 data32;
+ u8 data8;
+ int ret;
+
+ ret = lan937x_reset_switch(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Auto Aging */
+ ret = ksz_write8(dev, REG_SW_LUE_CTRL_1, data8 | SW_LINK_AUTO_AGING);
+ if (ret < 0)
+ return ret;
+
+ /* disable interrupts */
+ ret = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
+ if (ret < 0)
+ return ret;
+
+ /* Read interrupt status register */
+ ret = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+ if (ret < 0)
+ return ret;
+
+ /* Apply rgmii internal delay for the mac based on device tree */
+ ret = lan937x_parse_dt_rgmii_delay(dev);
+ if (ret < 0)
+ return ret;
+
+ /* The VLAN aware is a global setting. Mixed vlan
+ * filterings are not supported.
+ */
+ ds->vlan_filtering_is_global = true;
+
+ /* Configure cpu port */
+ lan937x_config_cpu_port(ds);
+
+ /* Enable aggressive back off for half duplex & UNH mode */
+ lan937x_cfg(dev, REG_SW_MAC_CTRL_0,
+ (SW_PAUSE_UNH_MODE | SW_NEW_BACKOFF | SW_AGGR_BACKOFF),
+ true);
+
+ /* If NO_EXC_COLLISION_DROP bit is set, the switch will not drop
+ * packets when 16 or more collisions occur
+ */
+ lan937x_cfg(dev, REG_SW_MAC_CTRL_1, NO_EXC_COLLISION_DROP, true);
+
+ /* Enable reserved multicast table */
+ lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true);
+
+ /* enable global MIB counter freeze function */
+ lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true);
+
+ /* disable CLK125 & CLK25, 1: disable, 0: enable */
+ lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1,
+ (SW_CLK125_ENB | SW_CLK25_ENB), true);
+
+ lan937x_enable_spi_indirect_access(dev);
+
+ /* start switch */
+ lan937x_cfg(dev, REG_SW_OPERATION, SW_START, true);
+
+ ksz_init_mib_timer(dev);
+
+ return 0;
+}
+
+static int lan937x_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret;
+
+ new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN;
+
+ if (dsa_is_cpu_port(ds, port))
+ new_mtu += LAN937X_TAG_LEN;
+
+ if (new_mtu >= FR_MIN_SIZE)
+ ret = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0,
+ PORT_JUMBO_EN, true);
+ else
+ ret = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0,
+ PORT_JUMBO_EN, false);
+ if (ret < 0) {
+ dev_err(ds->dev, "failed to enable jumbo\n");
+ return ret;
+ }
+
+ /* Write the frame size in PORT_MAX_FR_SIZE register */
+ ret = lan937x_pwrite16(dev, port, PORT_MAX_FR_SIZE, new_mtu);
+ if (ret < 0) {
+ dev_err(ds->dev, "failed to change the mtu\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
+{
+ /* Frame max size is 9000 (= 0x2328) if
+ * jumbo frame support is enabled, PORT_JUMBO_EN bit will be enabled
+ * based on mtu in lan937x_change_mtu() API
+ */
+ return (FR_MAX_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN);
+}
+
+const struct dsa_switch_ops lan937x_switch_ops = {
+ .get_tag_protocol = lan937x_get_tag_protocol,
+ .setup = lan937x_setup,
+ .phy_read = lan937x_phy_read16,
+ .phy_write = lan937x_phy_write16,
+ .port_enable = ksz_enable_port,
+ .port_bridge_join = ksz_port_bridge_join,
+ .port_bridge_leave = ksz_port_bridge_leave,
+ .port_stp_state_set = lan937x_port_stp_state_set,
+ .port_fast_age = ksz_port_fast_age,
+ .port_max_mtu = lan937x_get_max_mtu,
+ .port_change_mtu = lan937x_change_mtu,
+};
+
+int lan937x_switch_register(struct ksz_device *dev)
+{
+ return ksz_switch_register(dev, &lan937x_dev_ops);
+}
+EXPORT_SYMBOL(lan937x_switch_register);
+
+MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@microchip.com>");
+MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h
new file mode 100644
index 000000000000..d0de595f6c4e
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_reg.h
@@ -0,0 +1,688 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip LAN937X switch register definitions
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#ifndef __LAN937X_REG_H
+#define __LAN937X_REG_H
+
+/* 0 - Operation */
+#define REG_CHIP_ID0__1 0x0000
+#define REG_CHIP_ID1__1 0x0001
+#define REG_CHIP_ID2__1 0x0002
+
+#define CHIP_ID_74 0x74
+#define CHIP_ID_73 0x73
+#define CHIP_ID_72 0x72
+#define CHIP_ID_71 0x71
+#define CHIP_ID_70 0x70
+
+#define REG_CHIP_ID3__1 0x0003
+
+#define REG_GLOBAL_CTRL_0 0x0007
+
+#define SW_PHY_REG_BLOCK BIT(7)
+#define SW_FAST_MODE BIT(3)
+#define SW_FAST_MODE_OVERRIDE BIT(2)
+
+#define REG_GLOBAL_OPTIONS 0x000F
+
+#define REG_SW_INT_STATUS__4 0x0010
+#define REG_SW_INT_MASK__4 0x0014
+
+#define LUE_INT BIT(31)
+#define TRIG_TS_INT BIT(30)
+#define APB_TIMEOUT_INT BIT(29)
+#define OVER_TEMP_INT BIT(28)
+#define HSR_INT BIT(27)
+#define PIO_INT BIT(26)
+#define POR_READY_INT BIT(25)
+
+#define SWITCH_INT_MASK \
+ (LUE_INT | TRIG_TS_INT | APB_TIMEOUT_INT | OVER_TEMP_INT | HSR_INT | \
+ PIO_INT | POR_READY_INT)
+
+#define REG_SW_PORT_INT_STATUS__4 0x0018
+#define REG_SW_PORT_INT_MASK__4 0x001C
+
+/* 1 - Global */
+#define REG_SW_GLOBAL_SERIAL_CTRL_0 0x0100
+
+#define SW_LITTLE_ENDIAN BIT(4)
+#define SPI_AUTO_EDGE_DETECTION BIT(1)
+#define SPI_CLOCK_OUT_RISING_EDGE BIT(0)
+
+#define REG_SW_GLOBAL_OUTPUT_CTRL__1 0x0103
+#define SW_CLK125_ENB BIT(1)
+#define SW_CLK25_ENB BIT(0)
+
+/* 2 - PHY */
+#define REG_SW_POWER_MANAGEMENT_CTRL 0x0201
+
+/* 3 - Operation Control */
+#define REG_SW_OPERATION 0x0300
+
+#define SW_DOUBLE_TAG BIT(7)
+#define SW_OVER_TEMP_ENABLE BIT(2)
+#define SW_RESET BIT(1)
+#define SW_START BIT(0)
+
+#define REG_SW_LUE_CTRL_0 0x0310
+#define SW_VLAN_ENABLE BIT(7)
+#define SW_DROP_INVALID_VID BIT(6)
+#define SW_AGE_CNT_M 0x7
+#define SW_AGE_CNT_S 3
+#define SW_RESV_MCAST_ENABLE BIT(2)
+
+#define REG_SW_LUE_CTRL_1 0x0311
+
+#define UNICAST_LEARN_DISABLE BIT(7)
+#define SW_FLUSH_STP_TABLE BIT(5)
+#define SW_FLUSH_MSTP_TABLE BIT(4)
+#define SW_SRC_ADDR_FILTER BIT(3)
+#define SW_AGING_ENABLE BIT(2)
+#define SW_FAST_AGING BIT(1)
+#define SW_LINK_AUTO_AGING BIT(0)
+
+#define REG_SW_LUE_CTRL_2 0x0312
+
+#define SW_MID_RANGE_AGE BIT(7)
+#define SW_LINK_DOWN_FLUSH BIT(6)
+#define SW_EGRESS_VLAN_FILTER_DYN BIT(5)
+#define SW_EGRESS_VLAN_FILTER_STA BIT(4)
+#define SW_FLUSH_OPTION_M 0x3
+#define SW_FLUSH_OPTION_S 2
+#define SW_FLUSH_OPTION_DYN_MAC 1
+#define SW_FLUSH_OPTION_STA_MAC 2
+#define SW_FLUSH_OPTION_BOTH 3
+
+#define REG_SW_LUE_CTRL_3 0x0313
+#define REG_SW_AGE_PERIOD__1 0x0313
+
+#define REG_SW_LUE_INT_STATUS__1 0x0314
+#define REG_SW_LUE_INT_MASK__1 0x0315
+
+#define LEARN_FAIL_INT BIT(2)
+#define WRITE_FAIL_INT BIT(0)
+
+#define LUE_INT_MASK (LEARN_FAIL_INT | WRITE_FAIL_INT)
+
+#define REG_SW_LUE_INDEX_0__2 0x0316
+
+#define ENTRY_INDEX_M 0x0FFF
+
+#define REG_SW_LUE_INDEX_1__2 0x0318
+
+#define FAIL_INDEX_M 0x03FF
+
+#define REG_SW_LUE_INDEX_2__2 0x031A
+
+#define REG_SW_STATIC_AVAIL_ENTRY__4 0x031C
+
+#define SW_INGRESS_FILTERING_NO_LEARN BIT(15)
+#define SW_STATIC_AVAIL_CNT 0x1FF
+
+#define REG_SW_AGE_PERIOD__2 0x0320
+#define SW_AGE_PERIOD_M 0xFFF
+
+#define REG_SW_LUE_UNK_UCAST_CTRL__2 0x0322
+#define REG_SW_LUE_UNK_CTRL_0__4 0x0322
+
+#define SW_UNK_UCAST_ENABLE BIT(15)
+#define SW_UNK_PORTS_M 0xFF
+
+#define REG_SW_LUE_UNK_MCAST_CTRL__2 0x0324
+#define SW_UNK_MCAST_ENABLE BIT(15)
+
+#define REG_SW_LUE_UNK_VID_CTRL__2 0x0326
+#define SW_UNK_VID_ENABLE BIT(15)
+
+#define SW_VLAN_FLUSH_PORTS_M 0xFF
+
+#define REG_SW_STATIC_ENTRY_LIMIT__4 0x032C
+
+#define REG_SW_MAC_CTRL_0 0x0330
+#define SW_NEW_BACKOFF BIT(7)
+#define SW_PAUSE_UNH_MODE BIT(1)
+#define SW_AGGR_BACKOFF BIT(0)
+
+#define REG_SW_MAC_CTRL_1 0x0331
+#define SW_SHORT_IFG BIT(7)
+#define MULTICAST_STORM_DISABLE BIT(6)
+#define SW_BACK_PRESSURE BIT(5)
+#define FAIR_FLOW_CTRL BIT(4)
+#define NO_EXC_COLLISION_DROP BIT(3)
+#define SW_LEGAL_PACKET_DISABLE BIT(1)
+#define SW_PASS_SHORT_FRAME BIT(0)
+
+#define REG_SW_MAC_CTRL_2 0x0332
+#define SW_REPLACE_VID BIT(3)
+#define BROADCAST_STORM_RATE_HI 0x07
+
+#define REG_SW_MAC_CTRL_3 0x0333
+#define BROADCAST_STORM_RATE_LO 0xFF
+#define BR_STORM_RATE 0x07FF
+
+#define REG_SW_MAC_CTRL_4 0x0334
+#define SW_PASS_PAUSE BIT(3)
+
+#define REG_SW_MAC_CTRL_5 0x0335
+#define SW_OUT_RATE_LIMIT_QUEUE_BASED BIT(3)
+
+#define REG_SW_MAC_CTRL_6 0x0336
+#define SW_MIB_COUNTER_FLUSH BIT(7)
+#define SW_MIB_COUNTER_FREEZE BIT(6)
+
+#define REG_SW_MRI_CTRL_0 0x0370
+#define SW_IGMP_SNOOP BIT(6)
+#define SW_IPV6_MLD_OPTION BIT(3)
+#define SW_IPV6_MLD_SNOOP BIT(2)
+#define SW_MIRROR_RX_TX BIT(0)
+
+#define REG_SW_MRI_CTRL_1__4 0x0374
+#define REG_SW_MRI_CTRL_2__4 0x0378
+#define REG_SW_CLASS_D_IP_CTRL__4 0x0374
+
+#define SW_CLASS_D_IP_ENABLE BIT(31)
+
+#define REG_SW_MRI_CTRL_8 0x0378
+#define SW_RED_COLOR_S 4
+#define SW_YELLOW_COLOR_S 2
+#define SW_GREEN_COLOR_S 0
+#define SW_COLOR_M 0x3
+
+#define REG_PTP_EVENT_PRIO_CTRL 0x037C
+#define REG_PTP_GENERAL_PRIO_CTRL 0x037D
+#define PTP_PRIO_ENABLE BIT(7)
+
+#define REG_SW_QM_CTRL__4 0x0390
+#define PRIO_SCHEME_SELECT_M KS_PRIO_M
+#define PRIO_SCHEME_SELECT_S 6
+#define PRIO_MAP_3_HI 0
+#define PRIO_MAP_2_HI 2
+#define PRIO_MAP_0_LO 3
+#define UNICAST_VLAN_BOUNDARY BIT(1)
+
+#define REG_SW_EEE_QM_CTRL__2 0x03C0
+#define REG_SW_EEE_TXQ_WAIT_TIME__2 0x03C2
+
+/* 4 - */
+#define REG_SW_VLAN_ENTRY__4 0x0400
+#define VLAN_VALID BIT(31)
+#define VLAN_FORWARD_OPTION BIT(27)
+#define VLAN_PRIO_M KS_PRIO_M
+#define VLAN_PRIO_S 24
+#define VLAN_MSTP_M 0x7
+#define VLAN_MSTP_S 12
+#define VLAN_FID_M 0x7F
+
+#define REG_SW_VLAN_ENTRY_UNTAG__4 0x0404
+#define REG_SW_VLAN_ENTRY_PORTS__4 0x0408
+#define REG_SW_VLAN_ENTRY_INDEX__2 0x040C
+
+#define VLAN_INDEX_M 0x0FFF
+
+#define REG_SW_VLAN_CTRL 0x040E
+#define VLAN_START BIT(7)
+#define VLAN_ACTION 0x3
+#define VLAN_WRITE 1
+#define VLAN_READ 2
+#define VLAN_CLEAR 3
+
+#define REG_SW_ALU_INDEX_0 0x0410
+#define ALU_FID_INDEX_S 16
+#define ALU_FID_SIZE 127
+#define ALU_MAC_ADDR_HI 0xFFFF
+
+#define REG_SW_ALU_INDEX_1 0x0414
+#define ALU_DIRECT_INDEX_M (BIT(12) - 1)
+
+#define REG_SW_ALU_CTRL__4 0x0418
+#define REG_SW_ALU_CTRL(num) (REG_SW_ALU_CTRL__4 + ((num) * 4))
+
+#define ALU_STA_DYN_CNT 2
+#define ALU_VALID_CNT_M (BIT(14) - 1)
+#define ALU_VALID_CNT_S 16
+#define ALU_START BIT(7)
+#define ALU_VALID BIT(6)
+#define ALU_VALID_OR_STOP BIT(5)
+#define ALU_DIRECT BIT(2)
+#define ALU_ACTION 0x3
+#define ALU_WRITE 1
+#define ALU_READ 2
+#define ALU_SEARCH 3
+
+#define REG_SW_ALU_STAT_CTRL__4 0x041C
+#define ALU_STAT_VALID_CNT_M (BIT(9) - 1)
+#define ALU_STAT_VALID_CNT_S 20
+#define ALU_STAT_INDEX_M (BIT(8) - 1)
+#define ALU_STAT_INDEX_S 8
+#define ALU_RESV_MCAST_INDEX_M (BIT(6) - 1)
+#define ALU_STAT_START BIT(7)
+#define ALU_STAT_VALID BIT(6)
+#define ALU_STAT_VALID_OR_STOP BIT(5)
+#define ALU_STAT_USE_FID BIT(4)
+#define ALU_STAT_DIRECT BIT(3)
+#define ALU_RESV_MCAST_ADDR BIT(2)
+#define ALU_STAT_ACTION 0x3
+#define ALU_STAT_WRITE 1
+#define ALU_STAT_READ 2
+#define ALU_STAT_SEARCH 3
+
+#define REG_SW_ALU_VAL_A 0x0420
+#define ALU_V_STATIC_VALID BIT(31)
+#define ALU_V_SRC_FILTER BIT(30)
+#define ALU_V_DST_FILTER BIT(29)
+#define ALU_V_PRIO_AGE_CNT_M (BIT(3) - 1)
+#define ALU_V_PRIO_AGE_CNT_S 26
+#define ALU_V_MSTP_M 0x7
+
+#define REG_SW_ALU_VAL_B 0x0424
+#define ALU_V_OVERRIDE BIT(31)
+#define ALU_V_USE_FID BIT(30)
+#define ALU_V_PORT_MAP 0xFF
+
+#define REG_SW_ALU_VAL_C 0x0428
+#define ALU_V_FID_M (BIT(16) - 1)
+#define ALU_V_FID_S 16
+#define ALU_V_MAC_ADDR_HI 0xFFFF
+
+#define REG_SW_ALU_VAL_D 0x042C
+
+#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12))
+
+#define REG_GLOBAL_RR_INDEX__1 0x0600
+
+/* VPHY */
+#define REG_VPHY_CTRL__2 0x0700
+#define REG_VPHY_STAT__2 0x0704
+#define REG_VPHY_ID_HI__2 0x0708
+#define REG_VPHY_ID_LO__2 0x070C
+#define REG_VPHY_AUTO_NEG__2 0x0710
+#define REG_VPHY_REMOTE_CAP__2 0x0714
+
+#define REG_VPHY_EXPANSION__2 0x0718
+
+#define REG_VPHY_M_CTRL__2 0x0724
+#define REG_VPHY_M_STAT__2 0x0728
+
+#define REG_VPHY_EXT_STAT__2 0x073C
+#define VPHY_EXT_1000_X_FULL BIT(15)
+#define VPHY_EXT_1000_X_HALF BIT(14)
+#define VPHY_EXT_1000_T_FULL BIT(13)
+#define VPHY_EXT_1000_T_HALF BIT(12)
+
+#define REG_VPHY_DEVAD_0__2 0x0740
+#define REG_VPHY_DEVAD_1__2 0x0744
+#define REG_VPHY_DEVAD_2__2 0x0748
+#define REG_VPHY_DEVAD_3__2 0x074C
+
+#define VPHY_DEVAD_UPDATE BIT(7)
+#define VPHY_DEVAD_M 0x1F
+#define VPHY_DEVAD_S 8
+
+#define REG_VPHY_SMI_ADDR__2 0x0750
+#define REG_VPHY_SMI_DATA_LO__2 0x0754
+#define REG_VPHY_SMI_DATA_HI__2 0x0758
+
+#define REG_VPHY_IND_ADDR__2 0x075C
+#define REG_VPHY_IND_DATA__2 0x0760
+#define REG_VPHY_IND_CTRL__2 0x0768
+
+#define VPHY_IND_WRITE BIT(1)
+#define VPHY_IND_BUSY BIT(0)
+
+#define REG_VPHY_SPECIAL_CTRL__2 0x077C
+#define VPHY_SMI_INDIRECT_ENABLE BIT(15)
+#define VPHY_SW_LOOPBACK BIT(14)
+#define VPHY_MDIO_INTERNAL_ENABLE BIT(13)
+#define VPHY_SPI_INDIRECT_ENABLE BIT(12)
+#define VPHY_PORT_MODE_M 0x3
+#define VPHY_PORT_MODE_S 8
+#define VPHY_MODE_RGMII 0
+#define VPHY_MODE_MII_PHY 1
+#define VPHY_MODE_SGMII 2
+#define VPHY_MODE_RMII_PHY 3
+#define VPHY_SW_COLLISION_TEST BIT(7)
+#define VPHY_SPEED_DUPLEX_STAT_M 0x7
+#define VPHY_SPEED_DUPLEX_STAT_S 2
+#define VPHY_SPEED_1000 BIT(4)
+#define VPHY_SPEED_100 BIT(3)
+#define VPHY_FULL_DUPLEX BIT(2)
+
+/* 0 - Operation */
+#define REG_PORT_DEFAULT_VID 0x0000
+
+#define REG_PORT_CUSTOM_VID 0x0002
+#define REG_PORT_PME_STATUS 0x0013
+
+#define REG_PORT_PME_CTRL 0x0017
+#define PME_WOL_MAGICPKT BIT(2)
+#define PME_WOL_LINKUP BIT(1)
+#define PME_WOL_ENERGY BIT(0)
+
+#define REG_PORT_INT_STATUS 0x001B
+#define REG_PORT_INT_MASK 0x001F
+
+#define PORT_TAS_INT BIT(5)
+#define PORT_SGMII_INT BIT(3)
+#define PORT_PTP_INT BIT(2)
+#define PORT_PHY_INT BIT(1)
+#define PORT_ACL_INT BIT(0)
+
+#define PORT_INT_MASK \
+ ( \
+ PORT_TAS_INT | \
+ PORT_SGMII_INT | PORT_PTP_INT | PORT_PHY_INT | PORT_ACL_INT)
+
+#define REG_PORT_CTRL_0 0x0020
+
+#define PORT_MAC_LOOPBACK BIT(7)
+#define PORT_MAC_REMOTE_LOOPBACK BIT(6)
+#define PORT_K2L_INSERT_ENABLE BIT(5)
+#define PORT_K2L_DEBUG_ENABLE BIT(4)
+#define PORT_TAIL_TAG_ENABLE BIT(2)
+#define PORT_QUEUE_SPLIT_ENABLE 0x3
+
+#define REG_PORT_CTRL_1 0x0021
+#define PORT_SRP_ENABLE 0x3
+
+#define REG_PORT_STATUS_0 0x0030
+#define PORT_INTF_SPEED_M 0x3
+#define PORT_INTF_SPEED_S 3
+#define PORT_INTF_FULL_DUPLEX BIT(2)
+
+#define REG_PORT_STATUS_1 0x0034
+
+/* 1 - PHY */
+#define REG_VPHY_SMI_ADDR 0x14
+#define REG_VPHY_SMI_DATA_LO 0x15
+#define REG_VPHY_SMI_DATA_HI 0x16
+
+#define REG_VPHY_SPECIAL_CTRL_STAT 0x1F
+
+#define REG_PORT_T1_PHY_CTRL_BASE 0x0100
+#define REG_PORT_TX_PHY_CTRL_BASE 0x0280
+#define REG_TX_PHY_CTRL_BASE 0x0980
+
+#define REG_PORT_PHY_1000_CTRL 0x0112
+#define PORT_AUTO_NEG_MANUAL BIT(12)
+#define PORT_AUTO_NEG_M BIT(11)
+#define PORT_AUTO_NEG_M_PREFERRED BIT(10)
+#define PORT_AUTO_NEG_1000BT_FD BIT(9)
+#define PORT_AUTO_NEG_1000BT BIT(8)
+
+#define REG_PORT_PHY_1000_STATUS 0x0114
+
+#define REG_PORT_PHY_RXER_COUNTER 0x012A
+#define REG_PORT_PHY_INT_ENABLE 0x0136
+#define REG_PORT_PHY_INT_STATUS 0x0137
+
+/* Same as PORT_PHY_LOOPBACK */
+#define PORT_PHY_PCS_LOOPBACK BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_2 0x013A
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_3 0x013C
+#define PORT_100BT_FIXED_LATENCY BIT(15)
+
+#define REG_PORT_PHY_PHY_CTRL 0x013E
+#define PORT_INT_PIN_HIGH BIT(14)
+#define PORT_ENABLE_JABBER BIT(9)
+#define PORT_STAT_SPEED_1000MBIT BIT(6)
+#define PORT_STAT_SPEED_100MBIT BIT(5)
+#define PORT_STAT_SPEED_10MBIT BIT(4)
+#define PORT_STAT_FULL_DUPLEX BIT(3)
+
+/* Same as PORT_PHY_STAT_M */
+#define PORT_STAT_M BIT(2)
+#define PORT_RESET BIT(1)
+#define PORT_LINK_STATUS_FAIL BIT(0)
+
+/* 3 - xMII */
+#define REG_PORT_XMII_CTRL_0 0x0300
+#define PORT_SGMII_SEL BIT(7)
+#define PORT_MII_FULL_DUPLEX BIT(6)
+#define PORT_MII_TX_FLOW_CTRL BIT(5)
+#define PORT_MII_100MBIT BIT(4)
+#define PORT_MII_RX_FLOW_CTRL BIT(3)
+#define PORT_GRXC_ENABLE BIT(0)
+
+#define REG_PORT_XMII_CTRL_1 0x0301
+#define PORT_MII_NOT_1GBIT BIT(6)
+#define PORT_MII_SEL_EDGE BIT(5)
+#define PORT_RGMII_ID_IG_ENABLE BIT(4)
+#define PORT_RGMII_ID_EG_ENABLE BIT(3)
+#define PORT_MII_MAC_MODE BIT(2)
+#define PORT_MII_SEL_M 0x3
+#define PORT_RGMII_SEL 0x0
+#define PORT_RMII_SEL 0x1
+#define PORT_MII_SEL 0x2
+
+#define REG_PORT_XMII_CTRL_2 0x0302
+#define PORT_RGMII_RX_STS_ENABLE BIT(0)
+
+#define REG_PORT_XMII_CTRL_3 0x0303
+#define PORT_DUPLEX_STATUS_FULL BIT(3)
+
+#define REG_PORT_XMII_CTRL_4 0x0304
+#define PORT_TX_TUNE_ADJ 0x3F80
+
+#define REG_PORT_XMII_CTRL_5 0x0306
+#define PORT_DLL_RESET BIT(15)
+#define PORT_RX_TUNE_ADJ 0x3F80
+
+#define PORT_TUNE_ADJ 0x3F80
+
+/* 4 - MAC */
+#define REG_PORT_MAC_CTRL_0 0x0400
+#define PORT_CHECK_LENGTH BIT(2)
+#define PORT_BROADCAST_STORM BIT(1)
+#define PORT_JUMBO_PACKET BIT(0)
+
+#define REG_PORT_MAC_CTRL_1 0x0401
+#define PORT_BACK_PRESSURE BIT(3)
+#define PORT_PASS_ALL BIT(0)
+
+#define REG_PORT_MAC_CTRL_2 0x0402
+#define PORT_100BT_EEE_DISABLE BIT(7)
+#define PORT_1000BT_EEE_DISABLE BIT(6)
+
+#define REG_PORT_MAC_IN_RATE_LIMIT 0x0403
+
+#define REG_PORT_MTU__2 0x0404
+#define PORT_RATE_LIMIT_M (BIT(7) - 1)
+
+/* 5 - MIB Counters */
+#define REG_PORT_MIB_CTRL_STAT 0x0500
+#define MIB_COUNTER_OVERFLOW BIT(31)
+#define MIB_COUNTER_VALID BIT(30)
+#define MIB_COUNTER_READ BIT(25)
+#define MIB_COUNTER_FLUSH_FREEZE BIT(24)
+#define MIB_COUNTER_INDEX_M (BIT(8) - 1)
+#define MIB_COUNTER_INDEX_S 16
+#define MIB_COUNTER_DATA_HI_M 0xF
+
+#define REG_PORT_MIB_DATA 0x0504
+
+/* 8 - Classification and Policing */
+#define REG_PORT_MRI_MIRROR_CTRL 0x0800
+#define PORT_MIRROR_RX BIT(6)
+#define PORT_MIRROR_TX BIT(5)
+#define PORT_MIRROR_SNIFFER BIT(1)
+
+#define REG_PORT_MRI_PRIO_CTRL 0x0801
+#define PORT_HIGHEST_PRIO BIT(7)
+#define PORT_OR_PRIO BIT(6)
+#define PORT_MAC_PRIO_ENABLE BIT(4)
+#define PORT_VLAN_PRIO_ENABLE BIT(3)
+#define PORT_802_1P_PRIO_ENABLE BIT(2)
+#define PORT_DIFFSERV_PRIO_ENABLE BIT(1)
+#define PORT_ACL_PRIO_ENABLE BIT(0)
+
+#define REG_PORT_MRI_MAC_CTRL 0x0802
+#define PORT_USER_PRIO_CEILING BIT(7)
+#define PORT_DROP_NON_VLAN BIT(4)
+#define PORT_DROP_TAG BIT(3)
+#define PORT_BASED_PRIO_M KS_PRIO_M
+#define PORT_BASED_PRIO_S 0
+
+#define REG_PORT_MRI_TC_MAP__4 0x0808
+
+/* 9 - Shaping */
+#define REG_PORT_MTI_QUEUE_INDEX__4 0x0900
+
+#define REG_PORT_MTI_QUEUE_CTRL_0__4 0x0904
+#define MTI_PVID_REPLACE BIT(0)
+
+#define REG_PORT_MTI_QUEUE_CTRL_0 0x0914
+
+/* A - QM */
+#define REG_PORT_QM_CTRL__4 0x0A00
+#define PORT_QM_DROP_PRIO_M 0x3
+
+#define REG_PORT_VLAN_MEMBERSHIP__4 0x0A04
+
+#define REG_PORT_QM_QUEUE_INDEX__4 0x0A08
+#define PORT_QM_QUEUE_INDEX_S 24
+#define PORT_QM_BURST_SIZE_S 16
+#define PORT_QM_MIN_RESV_SPACE_M (BIT(11) - 1)
+
+#define REG_PORT_QM_WATER_MARK__4 0x0A0C
+#define PORT_QM_HI_WATER_MARK_S 16
+#define PORT_QM_LO_WATER_MARK_S 0
+#define PORT_QM_WATER_MARK_M (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_0__4 0x0A10
+#define PORT_QM_TX_CNT_USED_S 0
+#define PORT_QM_TX_CNT_M (BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_1__4 0x0A14
+#define PORT_QM_TX_CNT_CALCULATED_S 16
+#define PORT_QM_TX_CNT_AVAIL_S 0
+
+/* B - LUE */
+#define REG_PORT_LUE_CTRL 0x0B00
+
+#define PORT_VLAN_LOOKUP_VID_0 BIT(7)
+#define PORT_INGRESS_FILTER BIT(6)
+#define PORT_DISCARD_NON_VID BIT(5)
+#define PORT_MAC_BASED_802_1X BIT(4)
+#define PORT_SRC_ADDR_FILTER BIT(3)
+
+#define REG_PORT_LUE_MSTP_INDEX 0x0B01
+
+#define REG_PORT_LUE_MSTP_STATE 0x0B04
+
+#define PORT_TX_ENABLE BIT(2)
+#define PORT_RX_ENABLE BIT(1)
+#define PORT_LEARN_DISABLE BIT(0)
+
+#define REG_PORT_LUE_LEARN_CNT__2 0x0B08
+
+#define REG_PORT_LUE_UNK_CTL0 0x0B0E
+#define REG_PORT_LUE_UNK_CTL1 0x0B10
+#define REG_PORT_LUE_UNK_VID_CTRL__2 0x0B12
+
+#define PORT_UNK_UCAST_MCAST_ENABLE BIT(15)
+#define PORT_UCAST_MCAST_MASK 0xFF
+#define PORT_UNK_VID_ENABLE BIT(15)
+
+#define PRIO_QUEUES 8
+#define RX_PRIO_QUEUES 8
+#define KS_PRIO_IN_REG 2
+#define TOTAL_PORT_NUM 8
+
+#define LAN937X_COUNTER_NUM 0x20
+#define TOTAL_LAN937X_COUNTER_NUM (LAN937X_COUNTER_NUM + 2 + 2)
+
+#define SWITCH_COUNTER_NUM LAN937X_COUNTER_NUM
+
+#define P_BCAST_STORM_CTRL REG_PORT_MAC_CTRL_0
+#define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL
+#define P_MIRROR_CTRL REG_PORT_MRI_MIRROR_CTRL
+#define P_STP_CTRL REG_PORT_LUE_MSTP_STATE
+#define P_PHY_CTRL REG_PORT_PHY_CTRL
+#define P_NEG_RESTART_CTRL REG_PORT_PHY_CTRL
+#define P_LINK_STATUS REG_PORT_PHY_STATUS
+#define P_SPEED_STATUS REG_PORT_PHY_PHY_CTRL
+#define P_RATE_LIMIT_CTRL REG_PORT_MAC_IN_RATE_LIMIT
+
+#define S_LINK_AGING_CTRL REG_SW_LUE_CTRL_1
+#define S_MIRROR_CTRL REG_SW_MRI_CTRL_0
+#define S_REPLACE_VID_CTRL REG_SW_MAC_CTRL_2
+#define S_802_1P_PRIO_CTRL REG_SW_MAC_802_1P_MAP_0
+#define S_TOS_PRIO_CTRL REG_SW_MAC_TOS_PRIO_0
+#define S_FLUSH_TABLE_CTRL REG_SW_LUE_CTRL_1
+
+#define REG_SWITCH_RESET REG_RESET_CTRL
+
+#define SW_FLUSH_DYN_MAC_TABLE SW_FLUSH_MSTP_TABLE
+
+#define MAX_TIMESTAMP_UNIT 2
+#define MAX_TRIG_UNIT 3
+#define MAX_TIMESTAMP_EVENT_UNIT 8
+#define MAX_GPIO 2
+#define MAX_CLOCK 2
+
+#define PTP_TRIG_UNIT_M (BIT(MAX_TRIG_UNIT) - 1)
+#define PTP_TS_UNIT_M (BIT(MAX_TIMESTAMP_UNIT) - 1)
+
+#define TAIL_TAG_PTP BIT(7)
+#define TAIL_TAG_NEXT_CHIP BIT(6)
+#define TAIL_TAG_K2L BIT(5)
+#define TAIL_TAG_PTP_1_STEP BIT(4)
+#define TAIL_TAG_PTP_P2P BIT(3)
+#define TAIL_TAG_RX_PORTS_M 0x7
+
+/* 148,800 frames * 67 ms / 100 */
+#define BR_STORM_VALUE 9969
+
+#define SW_CHECK_LENGTH BIT(3)
+
+#define FR_MIN_SIZE 1522
+#define FR_MAX_SIZE 9000
+
+#define PORT_JUMBO_EN BIT(0)
+#define PORT_FR_CHK_LENGTH BIT(2)
+#define PORT_MAX_FR_SIZE 0x404
+
+#define FR_SIZE_CPU_PORT 1540
+
+#define REG_PORT_CTRL_0 0x0020
+#define PORT_FULL_DUPLEX BIT(6)
+#define PORT_TX_FLOW_CTRL BIT(5)
+#define PORT_RX_FLOW_CTRL BIT(3)
+#define PORT_MAC_SPEED_100 BIT(4)
+
+#define PORT_QUEUE_SPLIT_ENABLE 0x3
+
+/* Get fid from vid, fid 0 is not used if vid is greater than 127 */
+#define LAN937X_GET_FID(vid) (((vid) % ALU_FID_SIZE) + 1)
+
+/* Driver set switch broadcast storm protection at 10% rate */
+#define BR_STORM_PROT_RATE 10
+
+#define MII_BMSR_100BASE_TX_FD BIT(14)
+
+#define PHY_LINK_UP 1
+#define PHY_LINK_DOWN 0
+
+/* The port number as per the datasheet */
+#define RGMII_2_PORT_NUM 5
+#define RGMII_1_PORT_NUM 6
+#define SGMII_PORT_NUM 4
+#define TXPHY_PORT_NUM 4
+
+#define GET_CHIP_ID_LSB(chip_id) (((chip_id) >> 8) & 0xff)
+#define LAN937X_RGMII_2_PORT (RGMII_2_PORT_NUM - 1)
+#define LAN937X_RGMII_1_PORT (RGMII_1_PORT_NUM - 1)
+#define LAN937X_SGMII_PORT (SGMII_PORT_NUM - 1)
+#define LAN937X_TXPHY_PORT (TXPHY_PORT_NUM - 1)
+#define LAN937X_TAG_LEN 2
+
+#define RGMII_1_TX_DELAY_2NS 2
+#define RGMII_2_TX_DELAY_2NS 0
+#define RGMII_1_RX_DELAY_2NS 0x1B
+#define RGMII_2_RX_DELAY_2NS 0x14
+
+#endif
diff --git a/drivers/net/dsa/microchip/lan937x_spi.c b/drivers/net/dsa/microchip/lan937x_spi.c
new file mode 100644
index 000000000000..a50dfcf27aff
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_spi.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver register access through SPI
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+
+#include "ksz_common.h"
+
+#define SPI_ADDR_SHIFT 24
+#define SPI_ADDR_ALIGN 3
+#define SPI_TURNAROUND_SHIFT 5
+
+KSZ_REGMAP_TABLE(lan937x, 32, SPI_ADDR_SHIFT, SPI_TURNAROUND_SHIFT,
+ SPI_ADDR_ALIGN);
+
+struct lan937x_chip_data {
+ u32 chip_id;
+ const char *dev_name;
+ int num_vlans;
+ int num_alus;
+ int num_statics;
+ int cpu_ports;
+ int port_cnt;
+};
+
+static const struct of_device_id lan937x_dt_ids[];
+
+static const struct lan937x_chip_data lan937x_switch_chips[] = {
+ {
+ .chip_id = 0x00937010,
+ .dev_name = "LAN9370",
+ .num_vlans = 4096,
+ .num_alus = 1024,
+ .num_statics = 256,
+ /* can be configured as cpu port */
+ .cpu_ports = 0x10,
+ /* total port count */
+ .port_cnt = 5,
+ },
+ {
+ .chip_id = 0x00937110,
+ .dev_name = "LAN9371",
+ .num_vlans = 4096,
+ .num_alus = 1024,
+ .num_statics = 256,
+ /* can be configured as cpu port */
+ .cpu_ports = 0x30,
+ /* total port count */
+ .port_cnt = 6,
+ },
+ {
+ .chip_id = 0x00937210,
+ .dev_name = "LAN9372",
+ .num_vlans = 4096,
+ .num_alus = 1024,
+ .num_statics = 256,
+ /* can be configured as cpu port */
+ .cpu_ports = 0x30,
+ /* total port count */
+ .port_cnt = 8,
+ },
+ {
+ .chip_id = 0x00937310,
+ .dev_name = "LAN9373",
+ .num_vlans = 4096,
+ .num_alus = 1024,
+ .num_statics = 256,
+ /* can be configured as cpu port */
+ .cpu_ports = 0x38,
+ /* total port count */
+ .port_cnt = 5,
+ },
+ {
+ .chip_id = 0x00937410,
+ .dev_name = "LAN9374",
+ .num_vlans = 4096,
+ .num_alus = 1024,
+ .num_statics = 256,
+ /* can be configured as cpu port */
+ .cpu_ports = 0x30,
+ /* total port count */
+ .port_cnt = 8,
+ },
+};
+
+static int lan937x_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config rc;
+ struct ksz_device *dev;
+ int i, ret;
+
+ dev = ksz_switch_alloc(&spi->dev, spi);
+ if (!dev)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(lan937x_regmap_config); i++) {
+ rc = lan937x_regmap_config[i];
+ rc.lock_arg = &dev->regmap_mutex;
+ dev->regmap[i] = devm_regmap_init_spi(spi, &rc);
+
+ if (IS_ERR(dev->regmap[i])) {
+ ret = PTR_ERR(dev->regmap[i]);
+ dev_err(&spi->dev,
+ "Failed to initialize regmap%i: %d\n",
+ lan937x_regmap_config[i].val_bits, ret);
+ return ret;
+ }
+ }
+
+ if (spi->dev.platform_data)
+ dev->pdata = spi->dev.platform_data;
+
+ ret = lan937x_switch_register(dev);
+ /* Main DSA driver may not be started yet. */
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, dev);
+
+ return 0;
+}
+
+int lan937x_check_device_id(struct ksz_device *dev)
+{
+ const struct lan937x_chip_data *dt_chip_data;
+ const struct of_device_id *match;
+ int i;
+
+ dt_chip_data = of_device_get_match_data(dev->dev);
+
+ if (!dt_chip_data)
+ return -EINVAL;
+
+ for (match = lan937x_dt_ids; match->compatible[0]; match++) {
+ const struct lan937x_chip_data *chip_data = match->data;
+
+ /* Check for chip id */
+ if (chip_data->chip_id != dev->chip_id)
+ continue;
+
+ /* Check for Device Tree and Chip ID */
+ if (dt_chip_data->chip_id != dev->chip_id) {
+ dev_err(dev->dev,
+ "Device tree specifies chip %s but found %s, please fix it!\n",
+ dt_chip_data->dev_name, chip_data->dev_name);
+ return -ENODEV;
+ }
+
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lan937x_switch_chips); i++) {
+ const struct lan937x_chip_data *chip = &lan937x_switch_chips[i];
+
+ if (dev->chip_id == chip->chip_id) {
+ dev->name = chip->dev_name;
+ dev->num_vlans = chip->num_vlans;
+ dev->num_alus = chip->num_alus;
+ dev->num_statics = chip->num_statics;
+ dev->port_cnt = chip->port_cnt;
+ dev->cpu_ports = chip->cpu_ports;
+ break;
+ }
+ }
+
+ /* no switch found */
+ if (!dev->port_cnt)
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL(lan937x_check_device_id);
+
+static void lan937x_spi_remove(struct spi_device *spi)
+{
+ struct ksz_device *dev = spi_get_drvdata(spi);
+
+ if (dev)
+ ksz_switch_remove(dev);
+
+ spi_set_drvdata(spi, NULL);
+}
+
+static void lan937x_spi_shutdown(struct spi_device *spi)
+{
+ struct ksz_device *dev = spi_get_drvdata(spi);
+
+ if (dev)
+ dsa_switch_shutdown(dev->ds);
+
+ spi_set_drvdata(spi, NULL);
+}
+
+static const struct of_device_id lan937x_dt_ids[] = {
+ { .compatible = "microchip,lan9370", .data = &lan937x_switch_chips[0] },
+ { .compatible = "microchip,lan9371", .data = &lan937x_switch_chips[1] },
+ { .compatible = "microchip,lan9372", .data = &lan937x_switch_chips[2] },
+ { .compatible = "microchip,lan9373", .data = &lan937x_switch_chips[3] },
+ { .compatible = "microchip,lan9374", .data = &lan937x_switch_chips[4] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lan937x_dt_ids);
+
+static const struct spi_device_id lan937x_spi_ids[] = {
+ { .name = "lan9370" },
+ { .name = "lan9371" },
+ { .name = "lan9372" },
+ { .name = "lan9373" },
+ { .name = "lan9374" },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, lan937x_spi_ids);
+
+static struct spi_driver lan937x_spi_driver = {
+ .driver = {
+ .name = "lan937x-switch",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(lan937x_dt_ids),
+ },
+ .probe = lan937x_spi_probe,
+ .remove = lan937x_spi_remove,
+ .shutdown = lan937x_spi_shutdown,
+ .id_table = lan937x_spi_ids,
+};
+
+module_spi_driver(lan937x_spi_driver);
+
+MODULE_ALIAS("spi:lan937x");
+
+MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@microchip.com>");
+MODULE_DESCRIPTION("Microchip LAN937x Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v11 net-next 05/10] net: dsa: microchip: add DSA support for microchip lan937x
2022-03-25 16:53 ` [RFC PATCH v11 net-next 05/10] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
@ 2022-04-08 23:25 ` Vladimir Oltean
0 siblings, 0 replies; 16+ messages in thread
From: Vladimir Oltean @ 2022-04-08 23:25 UTC (permalink / raw)
To: Prasanna Vengateshan
Cc: andrew, netdev, robh+dt, UNGLinuxDriver, woojung.huh, hkallweit1,
linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
devicetree, pabeni
On Fri, Mar 25, 2022 at 10:23:36PM +0530, Prasanna Vengateshan wrote:
> +static void lan937x_r_mib_stats64(struct ksz_device *dev, int port)
> +{
> + struct ksz_port_mib *mib = &dev->ports[port].mib;
> + struct rtnl_link_stats64 *s;
> + u64 *ctr = mib->counters;
> +
> + s = &mib->stats64;
> + spin_lock(&mib->stats64_lock);
I haven't looked at further patches yet to see if the situation improves
or not, but right now, this spin lock is useless, as you do not
implement .get_stats64.
> +
> + s->rx_packets = ctr[lan937x_mib_rx_mcast] +
> + ctr[lan937x_mib_rx_bcast] +
> + ctr[lan937x_mib_rx_ucast] +
> + ctr[lan937x_mib_rx_pause];
> +
> + s->tx_packets = ctr[lan937x_mib_tx_mcast] +
> + ctr[lan937x_mib_tx_bcast] +
> + ctr[lan937x_mib_tx_ucast] +
> + ctr[lan937x_mib_tx_pause];
> +
> + s->rx_bytes = ctr[lan937x_mib_rx_total];
> + s->tx_bytes = ctr[lan937x_mib_tx_total];
> +
> + s->rx_errors = ctr[lan937x_mib_rx_fragments] +
> + ctr[lan937x_mib_rx_jabbers] +
> + ctr[lan937x_mib_rx_sym_err] +
> + ctr[lan937x_mib_rx_align_err] +
> + ctr[lan937x_mib_rx_crc_err];
> +
> + s->tx_errors = ctr[lan937x_mib_tx_exc_col] +
> + ctr[lan937x_mib_tx_late_col];
> +
> + s->rx_dropped = ctr[lan937x_mib_rx_discard];
> + s->tx_dropped = ctr[lan937x_mib_tx_discard];
> + s->multicast = ctr[lan937x_mib_rx_mcast];
> +
> + s->collisions = ctr[lan937x_mib_tx_late_col] +
> + ctr[lan937x_mib_tx_single_col] +
> + ctr[lan937x_mib_tx_mult_col];
> +
> + s->rx_length_errors = ctr[lan937x_mib_rx_fragments] +
> + ctr[lan937x_mib_rx_jabbers];
> +
> + s->rx_crc_errors = ctr[lan937x_mib_rx_crc_err];
> + s->rx_frame_errors = ctr[lan937x_mib_rx_align_err];
> + s->tx_aborted_errors = ctr[lan937x_mib_tx_exc_col];
> + s->tx_window_errors = ctr[lan937x_mib_tx_late_col];
> +
> + spin_unlock(&mib->stats64_lock);
> +}
> +static int lan937x_init(struct ksz_device *dev)
> +{
> + int ret;
> +
> + ret = lan937x_switch_init(dev);
> + if (ret < 0) {
> + dev_err(dev->dev, "failed to initialize the switch");
> + return ret;
> + }
> +
> + /* enable Indirect Access from SPI to the VPHY registers */
> + ret = lan937x_enable_spi_indirect_access(dev);
Do you need to call this both from lan937x_init() and from lan937x_setup()?
> + if (ret < 0) {
> + dev_err(dev->dev, "failed to enable spi indirect access");
> + return ret;
> + }
> +
> + ret = lan937x_mdio_register(dev);
> + if (ret < 0) {
> + dev_err(dev->dev, "failed to register the mdio");
> + return ret;
> + }
> +
> + return 0;
> +}
> +static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
> + u8 state)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct ksz_port *p;
> + u8 data;
> +
> + lan937x_pread8(dev, port, P_STP_CTRL, &data);
This is a copy-paste of ksz8_port_stp_state_set() except for the use of
lan937x_pread8() instead of ksz_pread8(). But ksz_pread8() should work
too, since it calls dev->dev_ops->get_port_addr(port, offset) which you
translate into PORT_CTRL_ADDR(port, offset) which is exactly what
lan937x_pread8() does.
> + data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
> +
> + switch (state) {
> + case BR_STATE_DISABLED:
> + data |= PORT_LEARN_DISABLE;
> + break;
> + case BR_STATE_LISTENING:
> + data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
> + break;
> + case BR_STATE_LEARNING:
> + data |= PORT_RX_ENABLE;
> + break;
> + case BR_STATE_FORWARDING:
> + data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
> + break;
> + case BR_STATE_BLOCKING:
> + data |= PORT_LEARN_DISABLE;
> + break;
> + default:
> + dev_err(ds->dev, "invalid STP state: %d\n", state);
> + return;
> + }
> +
> + lan937x_pwrite8(dev, port, P_STP_CTRL, data);
> +
> + p = &dev->ports[port];
> + p->stp_state = state;
> +
> + ksz_update_port_member(dev, port);
> +}
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (4 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 05/10] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-04-08 23:41 ` Vladimir Oltean
2022-04-29 15:58 ` Russell King (Oracle)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 07/10] net: dsa: microchip: add support for ethtool port counters Prasanna Vengateshan
` (3 subsequent siblings)
9 siblings, 2 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
phylink_get_caps() is implemented and reused KSZ commmon API for
phylink_mac_link_down() operation
lan937x_phylink_mac_config configures the interface using
lan937x_mac_config and lan937x_phylink_mac_link_up configures
the speed/duplex/flow control.
Currently SGMII & in-band neg are not supported & it will be
added later.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
drivers/net/dsa/microchip/lan937x_dev.c | 158 +++++++++++++++++++++++
drivers/net/dsa/microchip/lan937x_dev.h | 5 +
drivers/net/dsa/microchip/lan937x_main.c | 66 ++++++++++
3 files changed, 229 insertions(+)
diff --git a/drivers/net/dsa/microchip/lan937x_dev.c b/drivers/net/dsa/microchip/lan937x_dev.c
index 784a24d0aa08..dfd93fa49939 100644
--- a/drivers/net/dsa/microchip/lan937x_dev.c
+++ b/drivers/net/dsa/microchip/lan937x_dev.c
@@ -449,6 +449,164 @@ int lan937x_internal_phy_read(struct ksz_device *dev, int addr, int reg,
return ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
}
+static void lan937x_config_gbit(struct ksz_device *dev, bool gbit, u8 *data)
+{
+ if (gbit)
+ *data &= ~PORT_MII_NOT_1GBIT;
+ else
+ *data |= PORT_MII_NOT_1GBIT;
+}
+
+static void lan937x_update_rgmii_tx_rx_delay(struct ksz_device *dev, int port,
+ bool is_tx)
+{
+ u16 data16;
+ int reg;
+ u8 val;
+
+ /* Apply different codes based on the ports as per characterization
+ * results
+ */
+ if (is_tx) {
+ reg = REG_PORT_XMII_CTRL_5;
+ val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_TX_DELAY_2NS :
+ RGMII_2_TX_DELAY_2NS;
+ } else {
+ reg = REG_PORT_XMII_CTRL_4;
+ val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_RX_DELAY_2NS :
+ RGMII_2_RX_DELAY_2NS;
+ }
+
+ lan937x_pread16(dev, port, reg, &data16);
+
+ /* clear tune Adjust */
+ data16 &= ~PORT_TUNE_ADJ;
+ data16 |= (val << 7);
+ lan937x_pwrite16(dev, port, reg, data16);
+
+ data16 |= PORT_DLL_RESET;
+ /* write DLL reset to take effect */
+ lan937x_pwrite16(dev, port, reg, data16);
+}
+
+static void lan937x_apply_rgmii_delay(struct ksz_device *dev, int port,
+ phy_interface_t interface, u8 val)
+{
+ struct ksz_port *p = &dev->ports[port];
+
+ /* Clear Ingress & Egress internal delay enabled bits */
+ val &= ~(PORT_RGMII_ID_EG_ENABLE | PORT_RGMII_ID_IG_ENABLE);
+
+ /* if the delay is 0, do not enable DLL */
+ if (p->rgmii_tx_val) {
+ lan937x_update_rgmii_tx_rx_delay(dev, port, true);
+ dev_info(dev->dev, "Applied rgmii tx delay for the port %d\n",
+ port);
+ val |= PORT_RGMII_ID_EG_ENABLE;
+ }
+
+ /* if the delay is 0, do not enable DLL */
+ if (p->rgmii_rx_val) {
+ lan937x_update_rgmii_tx_rx_delay(dev, port, false);
+ dev_info(dev->dev, "Applied rgmii rx delay for the port %d\n",
+ port);
+ val |= PORT_RGMII_ID_IG_ENABLE;
+ }
+
+ /* Enable RGMII internal delays */
+ lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, val);
+}
+
+void lan937x_mac_config(struct ksz_device *dev, int port,
+ phy_interface_t interface)
+{
+ u8 data8;
+
+ lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+
+ /* clear MII selection & set it based on interface later */
+ data8 &= ~PORT_MII_SEL_M;
+
+ /* configure MAC based on interface */
+ switch (interface) {
+ case PHY_INTERFACE_MODE_MII:
+ lan937x_config_gbit(dev, false, &data8);
+ data8 |= PORT_MII_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ lan937x_config_gbit(dev, false, &data8);
+ data8 |= PORT_RMII_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ lan937x_config_gbit(dev, true, &data8);
+ data8 |= PORT_RGMII_SEL;
+
+ /* Apply rgmii internal delay for the mac */
+ lan937x_apply_rgmii_delay(dev, port, interface, data8);
+
+ /* rgmii delay configuration is already applied above,
+ * hence return from here as no changes required
+ */
+ return;
+ default:
+ dev_err(dev->dev, "Unsupported interface '%s' for port %d\n",
+ phy_modes(interface), port);
+ return;
+ }
+
+ /* Write the updated value */
+ lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
+}
+
+void lan937x_config_interface(struct ksz_device *dev, int port,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ u8 xmii_ctrl0, xmii_ctrl1;
+
+ lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_0, &xmii_ctrl0);
+ lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &xmii_ctrl1);
+
+ switch (speed) {
+ case SPEED_1000:
+ lan937x_config_gbit(dev, true, &xmii_ctrl1);
+ break;
+ case SPEED_100:
+ lan937x_config_gbit(dev, false, &xmii_ctrl1);
+ xmii_ctrl0 |= PORT_MAC_SPEED_100;
+ break;
+ case SPEED_10:
+ lan937x_config_gbit(dev, false, &xmii_ctrl1);
+ xmii_ctrl0 &= ~PORT_MAC_SPEED_100;
+ break;
+ default:
+ dev_err(dev->dev, "Unsupported speed on port %d: %d\n",
+ port, speed);
+ return;
+ }
+
+ if (duplex)
+ xmii_ctrl0 |= PORT_FULL_DUPLEX;
+ else
+ xmii_ctrl0 &= ~PORT_FULL_DUPLEX;
+
+ if (tx_pause)
+ xmii_ctrl0 |= PORT_TX_FLOW_CTRL;
+ else
+ xmii_ctrl1 &= ~PORT_TX_FLOW_CTRL;
+
+ if (rx_pause)
+ xmii_ctrl0 |= PORT_RX_FLOW_CTRL;
+ else
+ xmii_ctrl0 &= ~PORT_RX_FLOW_CTRL;
+
+ lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_0, xmii_ctrl0);
+ lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, xmii_ctrl1);
+}
+
void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port)
{
struct dsa_switch *ds = dev->ds;
diff --git a/drivers/net/dsa/microchip/lan937x_dev.h b/drivers/net/dsa/microchip/lan937x_dev.h
index eba36912969a..a9b435594634 100644
--- a/drivers/net/dsa/microchip/lan937x_dev.h
+++ b/drivers/net/dsa/microchip/lan937x_dev.h
@@ -34,6 +34,11 @@ void lan937x_cfg_port_member(struct ksz_device *dev, int port,
u8 member);
void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port);
int lan937x_enable_spi_indirect_access(struct ksz_device *dev);
+void lan937x_config_interface(struct ksz_device *dev, int port,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause);
+void lan937x_mac_config(struct ksz_device *dev, int port,
+ phy_interface_t interface);
void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt);
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index b5164be297c8..c7b7b9b373d7 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -295,6 +295,68 @@ static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
return (FR_MAX_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN);
}
+static void lan937x_phylink_mac_config(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* Internal PHYs */
+ if (lan937x_is_internal_phy_port(dev, port))
+ return;
+
+ if (phylink_autoneg_inband(mode)) {
+ dev_err(ds->dev, "In-band AN not supported!\n");
+ return;
+ }
+
+ lan937x_mac_config(dev, port, state->interface);
+}
+
+static void lan937x_phylink_mac_link_up(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface,
+ struct phy_device *phydev,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* Internal PHYs */
+ if (lan937x_is_internal_phy_port(dev, port))
+ return;
+
+ lan937x_config_interface(dev, port, speed, duplex,
+ tx_pause, rx_pause);
+}
+
+static void lan937x_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* non legacy driver */
+ config->legacy_pre_march2020 = false;
+
+ config->mac_capabilities = MAC_100FD;
+
+ /* internal T1 PHY */
+ if (lan937x_is_internal_base_t1_phy_port(dev, port)) {
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ } else if (lan937x_is_rgmii_port(dev, port)) {
+ /* MII/RMII/RGMII ports */
+ config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_100HD | MAC_10 | MAC_1000FD;
+ phy_interface_set_rgmii(config->supported_interfaces);
+
+ __set_bit(PHY_INTERFACE_MODE_MII,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RMII,
+ config->supported_interfaces);
+ }
+}
+
const struct dsa_switch_ops lan937x_switch_ops = {
.get_tag_protocol = lan937x_get_tag_protocol,
.setup = lan937x_setup,
@@ -307,6 +369,10 @@ const struct dsa_switch_ops lan937x_switch_ops = {
.port_fast_age = ksz_port_fast_age,
.port_max_mtu = lan937x_get_max_mtu,
.port_change_mtu = lan937x_change_mtu,
+ .phylink_get_caps = lan937x_phylink_get_caps,
+ .phylink_mac_link_down = ksz_mac_link_down,
+ .phylink_mac_config = lan937x_phylink_mac_config,
+ .phylink_mac_link_up = lan937x_phylink_mac_link_up,
};
int lan937x_switch_register(struct ksz_device *dev)
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management
2022-03-25 16:53 ` [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
@ 2022-04-08 23:41 ` Vladimir Oltean
2022-04-29 15:58 ` Russell King (Oracle)
1 sibling, 0 replies; 16+ messages in thread
From: Vladimir Oltean @ 2022-04-08 23:41 UTC (permalink / raw)
To: Prasanna Vengateshan
Cc: andrew, netdev, robh+dt, UNGLinuxDriver, woojung.huh, hkallweit1,
linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
devicetree, pabeni
On Fri, Mar 25, 2022 at 10:23:37PM +0530, Prasanna Vengateshan wrote:
> phylink_get_caps() is implemented and reused KSZ commmon API for
> phylink_mac_link_down() operation
>
> lan937x_phylink_mac_config configures the interface using
> lan937x_mac_config and lan937x_phylink_mac_link_up configures
> the speed/duplex/flow control.
>
> Currently SGMII & in-band neg are not supported & it will be
> added later.
If SGMII is also an option, then what you're doing in
lan937x_parse_dt_rgmii_delay() looks wrong:
/* skip for internal ports */
if (lan937x_is_internal_phy_port(dev, p))
continue;
if (of_property_read_u32(port, "rx-internal-delay-ps", &val))
val = 0;
err = lan937x_set_rgmii_delay(dev, p, val, false);
if (err)
break;
Right now you assume that "isn't internal PHY" is the same as "is RGMII",
but this is not actually the future-proof way of doing things. Also
please consider that the driver you write now may end up booting on a DT
blob from the future. Do you do something sane when a port is configured
for a phy-mode you don't recognize? For one thing, you try to configure
RGMII delays on it, as far as I can see.
> +static void lan937x_phylink_get_caps(struct dsa_switch *ds, int port,
> + struct phylink_config *config)
> +{
> + struct ksz_device *dev = ds->priv;
> +
> + /* non legacy driver */
> + config->legacy_pre_march2020 = false;
> +
> + config->mac_capabilities = MAC_100FD;
> +
> + /* internal T1 PHY */
> + if (lan937x_is_internal_base_t1_phy_port(dev, port)) {
> + __set_bit(PHY_INTERFACE_MODE_INTERNAL,
> + config->supported_interfaces);
> + } else if (lan937x_is_rgmii_port(dev, port)) {
> + /* MII/RMII/RGMII ports */
> + config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
> + MAC_100HD | MAC_10 | MAC_1000FD;
> + phy_interface_set_rgmii(config->supported_interfaces);
> +
> + __set_bit(PHY_INTERFACE_MODE_MII,
> + config->supported_interfaces);
> + __set_bit(PHY_INTERFACE_MODE_RMII,
> + config->supported_interfaces);
> + }
No supported_interfaces for the 100base-TX internal PHY port? Does it
pass validation?
> +}
> +
> const struct dsa_switch_ops lan937x_switch_ops = {
> .get_tag_protocol = lan937x_get_tag_protocol,
> .setup = lan937x_setup,
> @@ -307,6 +369,10 @@ const struct dsa_switch_ops lan937x_switch_ops = {
> .port_fast_age = ksz_port_fast_age,
> .port_max_mtu = lan937x_get_max_mtu,
> .port_change_mtu = lan937x_change_mtu,
> + .phylink_get_caps = lan937x_phylink_get_caps,
> + .phylink_mac_link_down = ksz_mac_link_down,
> + .phylink_mac_config = lan937x_phylink_mac_config,
> + .phylink_mac_link_up = lan937x_phylink_mac_link_up,
> };
>
> int lan937x_switch_register(struct ksz_device *dev)
> --
> 2.30.2
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management
2022-03-25 16:53 ` [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
2022-04-08 23:41 ` Vladimir Oltean
@ 2022-04-29 15:58 ` Russell King (Oracle)
1 sibling, 0 replies; 16+ messages in thread
From: Russell King (Oracle) @ 2022-04-29 15:58 UTC (permalink / raw)
To: Prasanna Vengateshan
Cc: andrew, netdev, olteanv, robh+dt, UNGLinuxDriver, woojung.huh,
hkallweit1, davem, kuba, linux-kernel, vivien.didelot,
f.fainelli, devicetree, pabeni
On Fri, Mar 25, 2022 at 10:23:37PM +0530, Prasanna Vengateshan wrote:
> +static void lan937x_apply_rgmii_delay(struct ksz_device *dev, int port,
> + phy_interface_t interface, u8 val)
> +{
> + struct ksz_port *p = &dev->ports[port];
> +
> + /* Clear Ingress & Egress internal delay enabled bits */
> + val &= ~(PORT_RGMII_ID_EG_ENABLE | PORT_RGMII_ID_IG_ENABLE);
> +
> + /* if the delay is 0, do not enable DLL */
> + if (p->rgmii_tx_val) {
> + lan937x_update_rgmii_tx_rx_delay(dev, port, true);
> + dev_info(dev->dev, "Applied rgmii tx delay for the port %d\n",
> + port);
> + val |= PORT_RGMII_ID_EG_ENABLE;
> + }
> +
> + /* if the delay is 0, do not enable DLL */
> + if (p->rgmii_rx_val) {
> + lan937x_update_rgmii_tx_rx_delay(dev, port, false);
> + dev_info(dev->dev, "Applied rgmii rx delay for the port %d\n",
> + port);
> + val |= PORT_RGMII_ID_IG_ENABLE;
> + }
> +
> + /* Enable RGMII internal delays */
> + lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, val);
"interface" doesn't appear to be used in this function, do you need to
pass it?
Other than that, the patch looks good, thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 07/10] net: dsa: microchip: add support for ethtool port counters
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (5 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 06/10] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 08/10] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
` (2 subsequent siblings)
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
Added support for get_eth_**_stats() (phy/mac/ctrl) and
get_stats64()
Reused the KSZ common APIs for get_ethtool_stats() & get_sset_count()
along with relevant lan937x hooks for KSZ common layer and added
support for get_strings()
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/microchip/lan937x_main.c | 118 +++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index c7b7b9b373d7..51640234dda6 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -45,6 +45,20 @@ static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
return lan937x_internal_phy_write(dev, addr, reg, val);
}
+static void lan937x_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ u8 *buf)
+{
+ struct ksz_device *dev = ds->priv;
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < dev->mib_cnt; i++)
+ memcpy(buf + i * ETH_GSTRING_LEN, lan937x_mib_names[i].string,
+ ETH_GSTRING_LEN);
+}
+
static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
@@ -357,12 +371,116 @@ static void lan937x_phylink_get_caps(struct dsa_switch *ds, int port,
}
}
+static void lan937x_get_eth_phy_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *cnt;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ cnt = &mib->counters[lan937x_mib_rx_sym_err];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_rx_sym_err, NULL, cnt);
+
+ phy_stats->SymbolErrorDuringCarrier = *cnt;
+
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+static void lan937x_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *ctr = mib->counters;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ while (mib->cnt_ptr < dev->mib_cnt) {
+ lan937x_r_mib_pkt(dev, port, mib->cnt_ptr,
+ NULL, &mib->counters[mib->cnt_ptr]);
+ ++mib->cnt_ptr;
+ }
+
+ mac_stats->FramesTransmittedOK = ctr[lan937x_mib_tx_mcast] +
+ ctr[lan937x_mib_tx_bcast] +
+ ctr[lan937x_mib_tx_ucast] +
+ ctr[lan937x_mib_tx_pause];
+
+ mac_stats->SingleCollisionFrames = ctr[lan937x_mib_tx_single_col];
+ mac_stats->MultipleCollisionFrames = ctr[lan937x_mib_tx_mult_col];
+
+ mac_stats->FramesReceivedOK = ctr[lan937x_mib_rx_mcast] +
+ ctr[lan937x_mib_rx_bcast] +
+ ctr[lan937x_mib_rx_ucast] +
+ ctr[lan937x_mib_rx_pause];
+
+ mac_stats->FrameCheckSequenceErrors = ctr[lan937x_mib_rx_crc_err];
+ mac_stats->AlignmentErrors = ctr[lan937x_mib_rx_align_err];
+ mac_stats->OctetsTransmittedOK = ctr[lan937x_mib_tx_total];
+ mac_stats->FramesWithDeferredXmissions = ctr[lan937x_mib_tx_deferred];
+ mac_stats->LateCollisions = ctr[lan937x_mib_tx_late_col];
+ mac_stats->FramesAbortedDueToXSColls = ctr[lan937x_mib_tx_exc_col];
+ mac_stats->FramesLostDueToIntMACXmitError = ctr[lan937x_mib_tx_discard];
+
+ mac_stats->OctetsReceivedOK = ctr[lan937x_mib_rx_total];
+ mac_stats->FramesLostDueToIntMACRcvError = ctr[lan937x_mib_rx_discard];
+ mac_stats->MulticastFramesXmittedOK = ctr[lan937x_mib_tx_mcast];
+ mac_stats->BroadcastFramesXmittedOK = ctr[lan937x_mib_tx_bcast];
+
+ mac_stats->MulticastFramesReceivedOK = ctr[lan937x_mib_rx_mcast];
+ mac_stats->BroadcastFramesReceivedOK = ctr[lan937x_mib_rx_bcast];
+ mac_stats->InRangeLengthErrors = ctr[lan937x_mib_rx_fragments];
+
+ mib->cnt_ptr = 0;
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+static void lan937x_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_sts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *cnt;
+
+ mutex_lock(&mib->cnt_mutex);
+
+ cnt = &mib->counters[lan937x_mib_rx_pause];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_rx_pause, NULL, cnt);
+ ctrl_sts->MACControlFramesReceived = *cnt;
+
+ cnt = &mib->counters[lan937x_mib_tx_pause];
+ lan937x_r_mib_pkt(dev, port, lan937x_mib_tx_pause, NULL, cnt);
+ ctrl_sts->MACControlFramesTransmitted = *cnt;
+
+ mutex_unlock(&mib->cnt_mutex);
+}
+
+static void lan937x_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *s)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+
+ spin_lock(&mib->stats64_lock);
+ memcpy(s, &mib->stats64, sizeof(*s));
+ spin_unlock(&mib->stats64_lock);
+}
+
const struct dsa_switch_ops lan937x_switch_ops = {
.get_tag_protocol = lan937x_get_tag_protocol,
.setup = lan937x_setup,
.phy_read = lan937x_phy_read16,
.phy_write = lan937x_phy_write16,
.port_enable = ksz_enable_port,
+ .get_strings = lan937x_get_strings,
+ .get_ethtool_stats = ksz_get_ethtool_stats,
+ .get_sset_count = ksz_sset_count,
+ .get_eth_ctrl_stats = lan937x_get_eth_ctrl_stats,
+ .get_eth_mac_stats = lan937x_get_eth_mac_stats,
+ .get_eth_phy_stats = lan937x_get_eth_phy_stats,
+ .get_stats64 = lan937x_get_stats64,
.port_bridge_join = ksz_port_bridge_join,
.port_bridge_leave = ksz_port_bridge_leave,
.port_stp_state_set = lan937x_port_stp_state_set,
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 08/10] net: dsa: microchip: add support for port mirror operations
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (6 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 07/10] net: dsa: microchip: add support for ethtool port counters Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 09/10] net: dsa: microchip: add support for fdb and mdb management Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 10/10] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
Added support for port_mirror_add() and port_mirror_del operations
Sniffing is limited to one port & alert the user if any new
sniffing port is selected
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/microchip/lan937x_main.c | 83 ++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 51640234dda6..504b9a4edea2 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -98,6 +98,87 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
ksz_update_port_member(dev, port);
}
+static int lan937x_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress, struct netlink_ext_ack *extack)
+{
+ struct ksz_device *dev = ds->priv;
+ int ret, p;
+ u8 data;
+
+ /* Limit to one sniffer port
+ * Check if any of the port is already set for sniffing
+ * If yes, instruct the user to remove the previous entry & exit
+ */
+ for (p = 0; p < dev->port_cnt; p++) {
+ /* Skip the current sniffing port */
+ if (p == mirror->to_local_port)
+ continue;
+
+ ret = lan937x_pread8(dev, p, P_MIRROR_CTRL, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data & PORT_MIRROR_SNIFFER) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Sniffer port is already configured, delete existing rules & retry");
+ return -EBUSY;
+ }
+ }
+
+ /* Configure ingress/egress mirroring */
+ if (ingress)
+ ret = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX,
+ true);
+ else
+ ret = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX,
+ true);
+ if (ret < 0)
+ return ret;
+
+ /* Configure sniffer port as other ports do not have
+ * PORT_MIRROR_SNIFFER is set
+ */
+ ret = lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, true);
+ if (ret < 0)
+ return ret;
+
+ return lan937x_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+}
+
+static void lan937x_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct ksz_device *dev = ds->priv;
+ bool in_use = false;
+ u8 data;
+ int p;
+
+ /* clear ingress/egress mirroring port */
+ if (mirror->ingress)
+ lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX,
+ false);
+ else
+ lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX,
+ false);
+
+ /* Check if any of the port is still referring to sniffer port */
+ for (p = 0; p < dev->port_cnt; p++) {
+ lan937x_pread8(dev, p, P_MIRROR_CTRL, &data);
+
+ if ((data & (PORT_MIRROR_RX | PORT_MIRROR_TX))) {
+ in_use = true;
+ break;
+ }
+ }
+
+ /* delete sniffing if there are no other mirroring rule exist */
+ if (!in_use)
+ lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, false);
+}
+
static void lan937x_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -485,6 +566,8 @@ const struct dsa_switch_ops lan937x_switch_ops = {
.port_bridge_leave = ksz_port_bridge_leave,
.port_stp_state_set = lan937x_port_stp_state_set,
.port_fast_age = ksz_port_fast_age,
+ .port_mirror_add = lan937x_port_mirror_add,
+ .port_mirror_del = lan937x_port_mirror_del,
.port_max_mtu = lan937x_get_max_mtu,
.port_change_mtu = lan937x_change_mtu,
.phylink_get_caps = lan937x_phylink_get_caps,
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 09/10] net: dsa: microchip: add support for fdb and mdb management
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (7 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 08/10] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
2022-03-25 16:53 ` [RFC PATCH v11 net-next 10/10] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
Support for fdb_add, mdb_add, fdb_del, mdb_del and
fdb_dump operations. ALU1 and ALU2 are used for fdb operations.
fdb_add: find any existing entries and update the port map.
if ALU1 write is failed and attempt to write ALU2.
If ALU2 is also failed then exit. Clear WRITE_FAIL for both ALU1
& ALU2.
fdb_del: find the matching entry and clear the respective port
in the port map by writing the ALU tables
fdb_dump: read and dump 2 ALUs upto last entry. ALU_START bit is
used to find the last entry. If the read is timed out, then pass
the error message.
mdb_add: Find the empty slot in ALU and update the port map &
mac address by writing the ALU
mdb_del: find the matching entry and delete the respective port
in port map by writing the ALU
For MAC address, could not use upper_32_bits() & lower_32_bits()
as per Vladimir proposal since it gets accessed in terms of 16bits.
I tried to have common API to get 16bits based on index but shifting
seems to be straight-forward.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/microchip/lan937x_main.c | 515 +++++++++++++++++++++++
1 file changed, 515 insertions(+)
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 504b9a4edea2..5067da9488c1 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -17,6 +17,70 @@
#include "ksz_common.h"
#include "lan937x_dev.h"
+static u8 lan937x_get_fid(u16 vid)
+{
+ if (vid > ALU_FID_SIZE)
+ return LAN937X_GET_FID(vid);
+ else
+ return vid;
+}
+
+static int lan937x_read_table(struct ksz_device *dev, u32 *table)
+{
+ int ret;
+
+ /* read alu table */
+ ret = ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
+ if (ret < 0)
+ return ret;
+
+ return ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
+}
+
+static int lan937x_write_table(struct ksz_device *dev, u32 *table)
+{
+ int ret;
+
+ /* write alu table */
+ ret = ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
+ if (ret < 0)
+ return ret;
+
+ return ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
+}
+
+static int lan937x_wait_alu_ready(int alu, struct ksz_device *dev)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(dev->regmap[2], REG_SW_ALU_CTRL(alu),
+ val, !(val & ALU_START), 10, 1000);
+}
+
+static int lan937x_wait_alu_sta_ready(struct ksz_device *dev)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(dev->regmap[2], REG_SW_ALU_STAT_CTRL__4,
+ val, !(val & ALU_STAT_START), 10, 1000);
+}
+
static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
@@ -98,6 +162,452 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
ksz_update_port_member(dev, port);
}
+static int lan937x_port_fdb_add(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 fid = lan937x_get_fid(vid);
+ u32 alu_table[4];
+ int ret, i;
+ u32 data;
+ u8 val;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* Accessing two ALU tables through loop */
+ for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+ /* find any entry with mac & fid */
+ data = fid << ALU_FID_INDEX_S;
+ data |= ((addr[0] << 8) | addr[1]);
+
+ ret = ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+ if (ret < 0)
+ break;
+
+ data = ((addr[2] << 24) | (addr[3] << 16));
+ data |= ((addr[4] << 8) | addr[5]);
+
+ ret = ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+ if (ret < 0)
+ break;
+
+ /* start read operation */
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i),
+ ALU_READ | ALU_START);
+ if (ret < 0)
+ break;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_ready(i, dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU\n");
+ break;
+ }
+
+ /* read ALU entry */
+ ret = lan937x_read_table(dev, alu_table);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU\n");
+ break;
+ }
+
+ /* update ALU entry */
+ alu_table[0] = ALU_V_STATIC_VALID;
+
+ /* update port number */
+ alu_table[1] |= BIT(port);
+
+ if (fid)
+ alu_table[1] |= ALU_V_USE_FID;
+
+ alu_table[2] = (fid << ALU_V_FID_S);
+ alu_table[2] |= ((addr[0] << 8) | addr[1]);
+ alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
+ alu_table[3] |= ((addr[4] << 8) | addr[5]);
+
+ ret = lan937x_write_table(dev, alu_table);
+ if (ret < 0)
+ break;
+
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i),
+ (ALU_WRITE | ALU_START));
+ if (ret < 0)
+ break;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_ready(i, dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to write ALU\n");
+ break;
+ }
+
+ ret = ksz_read8(dev, REG_SW_LUE_INT_STATUS__1, &val);
+ if (ret < 0)
+ break;
+
+ /* ALU2 write failed */
+ if (val & WRITE_FAIL_INT && i == 1)
+ dev_err(dev->dev, "Failed to write ALU\n");
+
+ /* if ALU1 write is failed and attempt to write ALU2,
+ * otherwise exit. Clear Write fail for both ALU1 & ALU2
+ */
+ if (val & WRITE_FAIL_INT) {
+ /* Write to clear the Write Fail */
+ ret = ksz_write8(dev, REG_SW_LUE_INT_STATUS__1,
+ WRITE_FAIL_INT);
+ if (ret < 0)
+ break;
+ } else {
+ break;
+ }
+ }
+
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static int lan937x_port_fdb_del(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 fid = lan937x_get_fid(vid);
+ u32 alu_table[4];
+ int ret, i;
+ u32 data;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* Accessing two ALU tables through loop */
+ for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+ /* read any entry with mac & fid */
+ data = fid << ALU_FID_INDEX_S;
+ data |= ((addr[0] << 8) | addr[1]);
+ ret = ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+ if (ret < 0)
+ break;
+
+ data = ((addr[2] << 24) | (addr[3] << 16));
+ data |= ((addr[4] << 8) | addr[5]);
+ ret = ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+ if (ret < 0)
+ break;
+
+ /* start read operation */
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i),
+ (ALU_READ | ALU_START));
+ if (ret < 0)
+ break;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_ready(i, dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU\n");
+ break;
+ }
+
+ ret = ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
+ if (ret < 0)
+ break;
+
+ if (alu_table[0] & ALU_V_STATIC_VALID) {
+ /* read ALU entry */
+ ret = lan937x_read_table(dev, alu_table);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU table\n");
+ break;
+ }
+
+ /* clear forwarding port */
+ alu_table[1] &= ~BIT(port);
+
+ /* if there is no port to forward, clear table */
+ if ((alu_table[1] & ALU_V_PORT_MAP) == 0)
+ memset(&alu_table, 0, sizeof(alu_table));
+ } else {
+ memset(&alu_table, 0, sizeof(alu_table));
+ }
+
+ ret = lan937x_write_table(dev, alu_table);
+ if (ret < 0)
+ break;
+
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i),
+ (ALU_WRITE | ALU_START));
+ if (ret < 0)
+ break;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_ready(i, dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to delete ALU Entries\n");
+ break;
+ }
+ }
+
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static void lan937x_convert_alu(struct lan_alu_struct *alu, u32 *alu_table)
+{
+ alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
+ alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
+ alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
+ alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
+ ALU_V_PRIO_AGE_CNT_M;
+ alu->mstp = alu_table[0] & ALU_V_MSTP_M;
+
+ alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
+ alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
+ alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
+
+ alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
+
+ alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
+ alu->mac[1] = alu_table[2] & 0xFF;
+ alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
+ alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
+ alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
+ alu->mac[5] = alu_table[3] & 0xFF;
+}
+
+static int lan937x_port_fdb_dump(struct dsa_switch *ds, int port,
+ dsa_fdb_dump_cb_t *cb, void *data)
+{
+ struct ksz_device *dev = ds->priv;
+ struct lan_alu_struct alu;
+ u32 lan937x_data;
+ u32 alu_table[4];
+ int timeout;
+ int ret, i;
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* Accessing two ALU tables through loop */
+ for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+ /* start ALU search */
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i),
+ (ALU_START | ALU_SEARCH));
+ if (ret < 0)
+ goto exit;
+
+ do {
+ timeout = 1000;
+ do {
+ ret = ksz_read32(dev, REG_SW_ALU_CTRL(i),
+ &lan937x_data);
+ if (ret < 0)
+ goto exit;
+
+ if ((lan937x_data & ALU_VALID) ||
+ !(lan937x_data & ALU_START))
+ break;
+ usleep_range(1, 10);
+ } while (timeout-- > 0);
+
+ if (!timeout) {
+ dev_err(dev->dev, "Failed to search ALU\n");
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /* read ALU table */
+ ret = lan937x_read_table(dev, alu_table);
+ if (ret < 0)
+ goto exit;
+
+ lan937x_convert_alu(&alu, alu_table);
+
+ if (alu.port_forward & BIT(port)) {
+ ret = cb(alu.mac, alu.fid, alu.is_static, data);
+ if (ret)
+ goto exit;
+ }
+ } while (lan937x_data & ALU_START);
+
+exit:
+ /* stop ALU search & continue to next ALU if available */
+ ret = ksz_write32(dev, REG_SW_ALU_CTRL(i), 0);
+ }
+
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
+static int lan937x_port_mdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 fid = lan937x_get_fid(mdb->vid);
+ u32 static_table[4];
+ u32 mac_hi, mac_lo;
+ int index, ret;
+ u32 data;
+
+ mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+ mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+ mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* Access the entries in the table */
+ for (index = 0; index < dev->num_statics; index++) {
+ /* find empty slot first */
+ data = (index << ALU_STAT_INDEX_S) |
+ ALU_STAT_READ | ALU_STAT_START;
+
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_sta_ready(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU STATIC\n");
+ goto exit;
+ }
+
+ /* read ALU static table */
+ ret = lan937x_read_table(dev, static_table);
+ if (ret < 0)
+ goto exit;
+
+ if (static_table[0] & ALU_V_STATIC_VALID) {
+ /* check this has same fid & mac address */
+ if (((static_table[2] >> ALU_V_FID_S) == fid) &&
+ ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+ static_table[3] == mac_lo) {
+ /* found matching one */
+ break;
+ }
+ } else {
+ /* found empty one */
+ break;
+ }
+ }
+
+ /* no available entry */
+ if (index == dev->num_statics) {
+ ret = -ENOSPC;
+ goto exit;
+ }
+
+ /* add entry */
+ static_table[0] = ALU_V_STATIC_VALID;
+
+ static_table[1] |= BIT(port);
+ if (fid)
+ static_table[1] |= ALU_V_USE_FID;
+ static_table[2] = (fid << ALU_V_FID_S);
+ static_table[2] |= mac_hi;
+ static_table[3] = mac_lo;
+
+ ret = lan937x_write_table(dev, static_table);
+ if (ret < 0)
+ goto exit;
+
+ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_sta_ready(dev);
+ if (ret < 0)
+ dev_err(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+ return ret;
+}
+
+static int lan937x_port_mdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 fid = lan937x_get_fid(mdb->vid);
+ u32 static_table[4];
+ u32 mac_hi, mac_lo;
+ int index, ret;
+ u32 data;
+
+ mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+ mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+ mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+ mutex_lock(&dev->alu_mutex);
+
+ /* Access the entries in the table */
+ for (index = 0; index < dev->num_statics; index++) {
+ data = (index << ALU_STAT_INDEX_S) |
+ ALU_STAT_READ | ALU_STAT_START;
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_sta_ready(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to read ALU STATIC\n");
+ goto exit;
+ }
+
+ /* read ALU static table */
+ ret = lan937x_read_table(dev, static_table);
+ if (ret < 0)
+ goto exit;
+
+ if (static_table[0] & ALU_V_STATIC_VALID) {
+ /* check this has same fid & mac address */
+ if (((static_table[2] >> ALU_V_FID_S) == fid) &&
+ ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+ static_table[3] == mac_lo) {
+ /* found matching one */
+ break;
+ }
+ }
+ }
+
+ /* no available entry */
+ if (index == dev->num_statics)
+ goto exit;
+
+ /* clear port based on port arg */
+ static_table[1] &= ~BIT(port);
+
+ if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
+ /* delete entry */
+ memset(&static_table, 0, sizeof(static_table));
+ }
+
+ ret = lan937x_write_table(dev, static_table);
+ if (ret < 0)
+ goto exit;
+
+ data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be finished */
+ ret = lan937x_wait_alu_sta_ready(dev);
+ if (ret < 0)
+ dev_err(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
static int lan937x_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress, struct netlink_ext_ack *extack)
@@ -566,6 +1076,11 @@ const struct dsa_switch_ops lan937x_switch_ops = {
.port_bridge_leave = ksz_port_bridge_leave,
.port_stp_state_set = lan937x_port_stp_state_set,
.port_fast_age = ksz_port_fast_age,
+ .port_fdb_dump = lan937x_port_fdb_dump,
+ .port_fdb_add = lan937x_port_fdb_add,
+ .port_fdb_del = lan937x_port_fdb_del,
+ .port_mdb_add = lan937x_port_mdb_add,
+ .port_mdb_del = lan937x_port_mdb_del,
.port_mirror_add = lan937x_port_mirror_add,
.port_mirror_del = lan937x_port_mirror_del,
.port_max_mtu = lan937x_get_max_mtu,
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v11 net-next 10/10] net: dsa: microchip: add support for vlan operations
2022-03-25 16:53 [RFC PATCH v11 net-next 00/10] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
` (8 preceding siblings ...)
2022-03-25 16:53 ` [RFC PATCH v11 net-next 09/10] net: dsa: microchip: add support for fdb and mdb management Prasanna Vengateshan
@ 2022-03-25 16:53 ` Prasanna Vengateshan
9 siblings, 0 replies; 16+ messages in thread
From: Prasanna Vengateshan @ 2022-03-25 16:53 UTC (permalink / raw)
To: andrew, netdev, olteanv, robh+dt
Cc: UNGLinuxDriver, woojung.huh, hkallweit1, linux, davem, kuba,
linux-kernel, vivien.didelot, f.fainelli, devicetree, pabeni
Support for VLAN add, del, prepare and filtering operations.
The VLAN aware is a global setting. Mixed vlan filterings
are not supported. vlan_filtering_is_global is made as true
in lan937x_setup function.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/microchip/lan937x_main.c | 186 +++++++++++++++++++++++
1 file changed, 186 insertions(+)
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 5067da9488c1..f1489c5c7229 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -17,6 +17,14 @@
#include "ksz_common.h"
#include "lan937x_dev.h"
+static int lan937x_wait_vlan_ctrl_ready(struct ksz_device *dev)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(dev->regmap[0], REG_SW_VLAN_CTRL, val,
+ !(val & VLAN_START), 10, 1000);
+}
+
static u8 lan937x_get_fid(u16 vid)
{
if (vid > ALU_FID_SIZE)
@@ -25,6 +33,97 @@ static u8 lan937x_get_fid(u16 vid)
return vid;
}
+static int lan937x_get_vlan_table(struct ksz_device *dev, u16 vid,
+ struct lan937x_vlan *vlan_entry)
+{
+ u32 data;
+ int ret;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ ret = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be cleared */
+ ret = lan937x_wait_vlan_ctrl_ready(dev);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &data);
+ if (ret < 0)
+ goto exit;
+
+ vlan_entry->valid = !!(data & VLAN_VALID);
+ vlan_entry->fid = data & VLAN_FID_M;
+
+ ret = ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4,
+ &vlan_entry->untag_prtmap);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4,
+ &vlan_entry->fwd_map);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+ if (ret < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&dev->vlan_mutex);
+
+ return ret;
+}
+
+static int lan937x_set_vlan_table(struct ksz_device *dev, u16 vid,
+ struct lan937x_vlan *vlan_entry)
+{
+ u32 data;
+ int ret;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ data = vlan_entry->valid ? VLAN_VALID : 0;
+ data |= vlan_entry->fid;
+
+ ret = ksz_write32(dev, REG_SW_VLAN_ENTRY__4, data);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4,
+ vlan_entry->untag_prtmap);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_entry->fwd_map);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+ if (ret < 0)
+ goto exit;
+
+ ret = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
+ if (ret < 0)
+ goto exit;
+
+ /* wait to be cleared */
+ ret = lan937x_wait_vlan_ctrl_ready(dev);
+ if (ret < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&dev->vlan_mutex);
+
+ return ret;
+}
+
static int lan937x_read_table(struct ksz_device *dev, u32 *table)
{
int ret;
@@ -162,6 +261,90 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
ksz_update_port_member(dev, port);
}
+static int lan937x_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool flag,
+ struct netlink_ext_ack *extack)
+{
+ struct ksz_device *dev = ds->priv;
+
+ /* enable/disable VLAN mode, once enabled, look up process starts
+ * and then forwarding and discarding are done based on port
+ * membership of the VLAN table
+ */
+ return lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, flag);
+}
+
+static int lan937x_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+{
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ struct ksz_device *dev = ds->priv;
+ struct lan937x_vlan vlan_entry;
+ int ret;
+
+ ret = lan937x_get_vlan_table(dev, vlan->vid, &vlan_entry);
+ if (ret < 0) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to get vlan table");
+ return ret;
+ }
+
+ vlan_entry.fid = lan937x_get_fid(vlan->vid);
+ vlan_entry.valid = true;
+
+ /* set/clear switch port when updating vlan table registers */
+ if (untagged)
+ vlan_entry.untag_prtmap |= BIT(port);
+ else
+ vlan_entry.untag_prtmap &= ~BIT(port);
+
+ vlan_entry.fwd_map |= BIT(port);
+
+ ret = lan937x_set_vlan_table(dev, vlan->vid, &vlan_entry);
+ if (ret < 0) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to set vlan table");
+ return ret;
+ }
+
+ /* change PVID */
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+ ret = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID,
+ vlan->vid);
+ if (ret < 0) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to set pvid");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int lan937x_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct ksz_device *dev = ds->priv;
+ struct lan937x_vlan vlan_entry;
+ int ret;
+
+ ret = lan937x_get_vlan_table(dev, vlan->vid, &vlan_entry);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to get vlan table\n");
+ return ret;
+ }
+
+ /* clear port fwd map & untag entries*/
+ vlan_entry.fwd_map &= ~BIT(port);
+ vlan_entry.untag_prtmap &= ~BIT(port);
+
+ ret = lan937x_set_vlan_table(dev, vlan->vid, &vlan_entry);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to set vlan table\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int lan937x_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
@@ -1076,6 +1259,9 @@ const struct dsa_switch_ops lan937x_switch_ops = {
.port_bridge_leave = ksz_port_bridge_leave,
.port_stp_state_set = lan937x_port_stp_state_set,
.port_fast_age = ksz_port_fast_age,
+ .port_vlan_filtering = lan937x_port_vlan_filtering,
+ .port_vlan_add = lan937x_port_vlan_add,
+ .port_vlan_del = lan937x_port_vlan_del,
.port_fdb_dump = lan937x_port_fdb_dump,
.port_fdb_add = lan937x_port_fdb_add,
.port_fdb_del = lan937x_port_fdb_del,
--
2.30.2
^ permalink raw reply related [flat|nested] 16+ messages in thread