All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
@ 2020-12-17  7:51 ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This series provides the Microchip Sparx5 Switch Driver

The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
Ethernet ports and up to 200 Gbps of switching bandwidth.

It provides a rich set of Ethernet switching features such as hierarchical
QoS, hardware-based OAM  and service activation testing, protection
switching, IEEE 1588, and Synchronous Ethernet.

Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
MEF CE
2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
  classification in both ingress and egress.

Per-EVC features include advanced L3-aware classification, a rich set of
statistics, OAM for end-to-end performance monitoring, and dual-rate
policing and shaping.

Time sensitive networking (TSN) is supported through a comprehensive set of
features including frame preemption, cut-through, frame replication and
elimination for reliability, enhanced scheduling: credit-based shaping,
time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
and filtering.

Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
low-latency deterministic networking for Fronthaul, Carrier, and Industrial
Ethernet.

The Sparx5 switch family consists of following SKUs:

- VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
  port configurations:
  - 6 *10G
  - 16 * 2.5G + 2 * 10G
  - 24 * 1G + 4 * 10G

- VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
  port configurations:
  - 9 * 10G
  - 16 * 2.5G + 4 * 10G
  - 48 * 1G + 4 * 10G

- VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
  port configurations:
  - 12 * 10G
  - 16 * 2.5G + 8 * 10G
  - 48 * 1G + 8 * 10G

- VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
  port configurations:
  - 16 * 10G
  - 10 * 10G + 2 * 25G
  - 16 * 2.5G + 10 * 10G
  - 48 * 1G + 10 * 10G

- VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
  port configurations:
  - 20 * 10G
  - 8 * 25G

In addition, the device supports one 10/100/1000/2500/5000 Mbps
SGMII/SerDes node processor interface (NPI) Ethernet port.

The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.

- PCB134 main networking features:
  - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
  - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
    speed).
  - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
    (on-board VSC8211 PHY connected to Sparx5 through SGMII).

- PCB135 main networking features:
  - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
    connected to VSC7558 through QSGMII.
  - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
    each port connects to VSC7558 through SFI.
  - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
    speed.
  - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
    VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
    using a loopback add-on PCB)

This series provides support for:
  - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
    devices and media types.
  - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
    1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
  - SerDes configuration via the Sparx5 SerDes driver (see below).
  - Host mode providing register based injection and extraction.
  - Switch mode providing MAC/VLAN table learning and Layer2 switching
    offloaded to the Sparx5 switch.
  - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
    configuration and statistics via ethtool.

More support will be added at a later stage.

The Sparx5 Switch chip register model can be browsed here:
Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html

The series depends on the following series currently on their way
into the kernel:

- Sparx5 SerDes Driver
  Link: https://lore.kernel.org/r/20201211090541.157926-1-steen.hegelund@microchip.com/

- Serial GPIO Controller
  Link: https://lore.kernel.org/r/20201113145151.68900-1-lars.povlsen@microchip.com/

ChangeLog:
    v2:
        - The driver patch has been split into 6 patches by functionality
          like this:
            - the basic sparx5 driver
            - hostmode with phylink support
            - port module support
            - switching, vlan and mactable support
            - calendar bandwidth allocation support
            - ethtool configuration and statistics support
        - IO ranges have been collapsed into just 2 (the SerDes
          driver uses the area inbetween) and the driver uses an
          offset table to get the target instances.
        - register macros have been converted to functions
        - register_netdev() moved to the end of the switch initialization.
        - sparx5_update_port_stats: use reverse christmas tree
        - sparx5_get_sset_strings: copy individual strings
        - sparx5_port_open: updated to better use phylink: just call
          phylink_of_phy_connect directly
        - sparx5_destroy_netdev: always take the NL lock
        - sparx5_attr_stp_state_set: added learning state.
        - sparx5_phylink_mac_config: use phylink to provide the
          status for the devices phylink controls.
        - sparx5_get_1000basex_status: renamed to sparx5_get_dev2g5_status
          and corrected an error when combining the sync and link status
          information.
        - let phylink provide link status for cuPHYs and SFPs
        - corrected the pause mode status handling
        - use ethtool's get_link function directly
        - remove the use of the phy_validate function
        - sparx5_update_counter function: no longer inline
        - Removed the wrapper functions around the mactable mutex


Steen Hegelund (8):
  dt-bindings: net: sparx5: Add sparx5-switch bindings
  net: sparx5: add the basic sparx5 driver
  net: sparx5: add hostmode with phylink support
  net: sparx5: add port module support
  net: sparx5: add switching, vlan and mactable support
  net: sparx5: add calendar bandwidth allocation support
  net: sparx5: add ethtool configuration and statistics support
  arm64: dts: sparx5: Add the Sparx5 switch node

 .../bindings/net/microchip,sparx5-switch.yaml |  178 +
 arch/arm64/boot/dts/microchip/sparx5.dtsi     |   60 +
 .../dts/microchip/sparx5_pcb134_board.dtsi    |  424 +-
 .../dts/microchip/sparx5_pcb135_board.dtsi    |  602 ++-
 drivers/net/ethernet/microchip/Kconfig        |    2 +
 drivers/net/ethernet/microchip/Makefile       |    2 +
 drivers/net/ethernet/microchip/sparx5/Kconfig |    8 +
 .../net/ethernet/microchip/sparx5/Makefile    |   11 +
 .../microchip/sparx5/sparx5_calendar.c        |  595 +++
 .../microchip/sparx5/sparx5_ethtool.c         |  979 ++++
 .../microchip/sparx5/sparx5_mactable.c        |  502 +++
 .../ethernet/microchip/sparx5/sparx5_main.c   |  855 ++++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  372 ++
 .../microchip/sparx5/sparx5_main_regs.h       | 3922 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_netdev.c |  246 ++
 .../ethernet/microchip/sparx5/sparx5_packet.c |  279 ++
 .../microchip/sparx5/sparx5_phylink.c         |  193 +
 .../ethernet/microchip/sparx5/sparx5_port.c   | 1140 +++++
 .../ethernet/microchip/sparx5/sparx5_port.h   |   98 +
 .../microchip/sparx5/sparx5_switchdev.c       |  516 +++
 .../ethernet/microchip/sparx5/sparx5_vlan.c   |  223 +
 21 files changed, 11147 insertions(+), 60 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Kconfig
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Makefile
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c

--
2.29.2


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

* [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
@ 2020-12-17  7:51 ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This series provides the Microchip Sparx5 Switch Driver

The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
Ethernet ports and up to 200 Gbps of switching bandwidth.

It provides a rich set of Ethernet switching features such as hierarchical
QoS, hardware-based OAM  and service activation testing, protection
switching, IEEE 1588, and Synchronous Ethernet.

Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
MEF CE
2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
  classification in both ingress and egress.

Per-EVC features include advanced L3-aware classification, a rich set of
statistics, OAM for end-to-end performance monitoring, and dual-rate
policing and shaping.

Time sensitive networking (TSN) is supported through a comprehensive set of
features including frame preemption, cut-through, frame replication and
elimination for reliability, enhanced scheduling: credit-based shaping,
time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
and filtering.

Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
low-latency deterministic networking for Fronthaul, Carrier, and Industrial
Ethernet.

The Sparx5 switch family consists of following SKUs:

- VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
  port configurations:
  - 6 *10G
  - 16 * 2.5G + 2 * 10G
  - 24 * 1G + 4 * 10G

- VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
  port configurations:
  - 9 * 10G
  - 16 * 2.5G + 4 * 10G
  - 48 * 1G + 4 * 10G

- VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
  port configurations:
  - 12 * 10G
  - 16 * 2.5G + 8 * 10G
  - 48 * 1G + 8 * 10G

- VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
  port configurations:
  - 16 * 10G
  - 10 * 10G + 2 * 25G
  - 16 * 2.5G + 10 * 10G
  - 48 * 1G + 10 * 10G

- VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
  port configurations:
  - 20 * 10G
  - 8 * 25G

In addition, the device supports one 10/100/1000/2500/5000 Mbps
SGMII/SerDes node processor interface (NPI) Ethernet port.

The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.

- PCB134 main networking features:
  - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
  - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
    speed).
  - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
    (on-board VSC8211 PHY connected to Sparx5 through SGMII).

- PCB135 main networking features:
  - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
    connected to VSC7558 through QSGMII.
  - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
    each port connects to VSC7558 through SFI.
  - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
    speed.
  - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
    VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
    using a loopback add-on PCB)

This series provides support for:
  - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
    devices and media types.
  - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
    1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
  - SerDes configuration via the Sparx5 SerDes driver (see below).
  - Host mode providing register based injection and extraction.
  - Switch mode providing MAC/VLAN table learning and Layer2 switching
    offloaded to the Sparx5 switch.
  - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
    configuration and statistics via ethtool.

More support will be added at a later stage.

The Sparx5 Switch chip register model can be browsed here:
Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html

The series depends on the following series currently on their way
into the kernel:

- Sparx5 SerDes Driver
  Link: https://lore.kernel.org/r/20201211090541.157926-1-steen.hegelund@microchip.com/

- Serial GPIO Controller
  Link: https://lore.kernel.org/r/20201113145151.68900-1-lars.povlsen@microchip.com/

ChangeLog:
    v2:
        - The driver patch has been split into 6 patches by functionality
          like this:
            - the basic sparx5 driver
            - hostmode with phylink support
            - port module support
            - switching, vlan and mactable support
            - calendar bandwidth allocation support
            - ethtool configuration and statistics support
        - IO ranges have been collapsed into just 2 (the SerDes
          driver uses the area inbetween) and the driver uses an
          offset table to get the target instances.
        - register macros have been converted to functions
        - register_netdev() moved to the end of the switch initialization.
        - sparx5_update_port_stats: use reverse christmas tree
        - sparx5_get_sset_strings: copy individual strings
        - sparx5_port_open: updated to better use phylink: just call
          phylink_of_phy_connect directly
        - sparx5_destroy_netdev: always take the NL lock
        - sparx5_attr_stp_state_set: added learning state.
        - sparx5_phylink_mac_config: use phylink to provide the
          status for the devices phylink controls.
        - sparx5_get_1000basex_status: renamed to sparx5_get_dev2g5_status
          and corrected an error when combining the sync and link status
          information.
        - let phylink provide link status for cuPHYs and SFPs
        - corrected the pause mode status handling
        - use ethtool's get_link function directly
        - remove the use of the phy_validate function
        - sparx5_update_counter function: no longer inline
        - Removed the wrapper functions around the mactable mutex


Steen Hegelund (8):
  dt-bindings: net: sparx5: Add sparx5-switch bindings
  net: sparx5: add the basic sparx5 driver
  net: sparx5: add hostmode with phylink support
  net: sparx5: add port module support
  net: sparx5: add switching, vlan and mactable support
  net: sparx5: add calendar bandwidth allocation support
  net: sparx5: add ethtool configuration and statistics support
  arm64: dts: sparx5: Add the Sparx5 switch node

 .../bindings/net/microchip,sparx5-switch.yaml |  178 +
 arch/arm64/boot/dts/microchip/sparx5.dtsi     |   60 +
 .../dts/microchip/sparx5_pcb134_board.dtsi    |  424 +-
 .../dts/microchip/sparx5_pcb135_board.dtsi    |  602 ++-
 drivers/net/ethernet/microchip/Kconfig        |    2 +
 drivers/net/ethernet/microchip/Makefile       |    2 +
 drivers/net/ethernet/microchip/sparx5/Kconfig |    8 +
 .../net/ethernet/microchip/sparx5/Makefile    |   11 +
 .../microchip/sparx5/sparx5_calendar.c        |  595 +++
 .../microchip/sparx5/sparx5_ethtool.c         |  979 ++++
 .../microchip/sparx5/sparx5_mactable.c        |  502 +++
 .../ethernet/microchip/sparx5/sparx5_main.c   |  855 ++++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  372 ++
 .../microchip/sparx5/sparx5_main_regs.h       | 3922 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_netdev.c |  246 ++
 .../ethernet/microchip/sparx5/sparx5_packet.c |  279 ++
 .../microchip/sparx5/sparx5_phylink.c         |  193 +
 .../ethernet/microchip/sparx5/sparx5_port.c   | 1140 +++++
 .../ethernet/microchip/sparx5/sparx5_port.h   |   98 +
 .../microchip/sparx5/sparx5_switchdev.c       |  516 +++
 .../ethernet/microchip/sparx5/sparx5_vlan.c   |  223 +
 21 files changed, 11147 insertions(+), 60 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Kconfig
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Makefile
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c

--
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Document the Sparx5 switch device driver bindings

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../bindings/net/microchip,sparx5-switch.yaml | 178 ++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml

diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
new file mode 100644
index 000000000000..6e3ef8285e9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip Sparx5 Ethernet switch controller
+
+maintainers:
+  - Lars Povlsen <lars.povlsen@microchip.com>
+  - Steen Hegelund <steen.hegelund@microchip.com>
+
+description: |
+  The SparX-5 Enterprise Ethernet switch family provides a rich set of
+  Enterprise switching features such as advanced TCAM-based VLAN and
+  QoS processing enabling delivery of differentiated services, and
+  security through TCAM-based frame processing using versatile content
+  aware processor (VCAP).
+
+  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported
+  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K
+  IPv6 (S,G) multicast groups.
+
+  L3 security features include source guard and reverse path
+  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and
+  IP tunnels (IP over GRE/IP).
+
+  The SparX-5 switch family targets managed Layer 2 and Layer 3
+  equipment in SMB, SME, and Enterprise where high port count
+  1G/2.5G/5G/10G switching with 10G/25G aggregation links is required.
+
+properties:
+  $nodename:
+    pattern: "^switch@[0-9a-f]+$"
+
+  compatible:
+    const: microchip,sparx5-switch
+
+  reg:
+    minItems: 2
+
+  reg-names:
+    minItems: 2
+    items:
+      - const: devices
+      - const: gcb
+
+  interrupts:
+    maxItems: 1
+    description: Interrupt used for reception of packets to the CPU
+
+  ethernet-ports:
+    type: object
+    properties:
+      '#address-cells':
+        const: 1
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      "^port@[0-9]+$":
+        type: object
+        description: Switch ports
+
+        allOf:
+          - $ref: ethernet-controller.yaml#
+
+        properties:
+          reg:
+            description: Switch port number
+
+          max-speed:
+            maxItems: 1
+            description: Bandwidth allocated to this port
+
+          phys:
+            description: phandle of a Ethernet Serdes PHY
+
+          phy-handle:
+            description: phandle of a Ethernet PHY
+
+          phy-mode:
+            description: Interface between the serdes and the phy
+
+          sfp:
+            description: phandle of an SFP
+
+          managed:
+            maxItems: 1
+            description: SFP management
+
+        required:
+          - reg
+          - max-speed
+          - phys
+
+        oneOf:
+          - required:
+              - phy-handle
+              - phy-mode
+          - required:
+              - sfp
+              - managed
+
+        additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - ethernet-ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    switch: switch@600000000 {
+      compatible = "microchip,sparx5-switch";
+      reg =  <0x10000000 0x800000>,
+             <0x11010000 0x1b00000>;
+      reg-names = "devices", "gcb";
+
+      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+      ethernet-ports {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        port0: port@0 {
+          reg = <0>;
+          max-speed = <1000>;
+          phys = <&serdes 13>;
+          phy-handle = <&phy0>;
+          phy-mode = "qsgmii";
+        };
+        /* ... */
+        /* Then the 25G interfaces */
+        port60: port@60 {
+          reg = <60>;
+          max-speed = <25000>;
+          phys = <&serdes 29>;
+          sfp = <&sfp_eth60>;
+          managed = "in-band-status";
+        };
+        port61: port@61 {
+          reg = <61>;
+          max-speed = <25000>;
+          phys = <&serdes 30>;
+          sfp = <&sfp_eth61>;
+          managed = "in-band-status";
+        };
+        port62: port@62 {
+          reg = <62>;
+          max-speed = <25000>;
+          phys = <&serdes 31>;
+          sfp = <&sfp_eth62>;
+          managed = "in-band-status";
+        };
+        port63: port@63 {
+          reg = <63>;
+          max-speed = <25000>;
+          phys = <&serdes 32>;
+          sfp = <&sfp_eth63>;
+          managed = "in-band-status";
+        };
+        /* Finally the Management interface */
+        port64: port@64 {
+          reg = <64>;
+          max-speed = <1000>;
+          phys = <&serdes 0>;
+          phy-handle = <&phy64>;
+          phy-mode = "sgmii";
+        };
+      };
+    };
+
+...
-- 
2.29.2


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

* [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

Document the Sparx5 switch device driver bindings

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../bindings/net/microchip,sparx5-switch.yaml | 178 ++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml

diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
new file mode 100644
index 000000000000..6e3ef8285e9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip Sparx5 Ethernet switch controller
+
+maintainers:
+  - Lars Povlsen <lars.povlsen@microchip.com>
+  - Steen Hegelund <steen.hegelund@microchip.com>
+
+description: |
+  The SparX-5 Enterprise Ethernet switch family provides a rich set of
+  Enterprise switching features such as advanced TCAM-based VLAN and
+  QoS processing enabling delivery of differentiated services, and
+  security through TCAM-based frame processing using versatile content
+  aware processor (VCAP).
+
+  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported
+  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K
+  IPv6 (S,G) multicast groups.
+
+  L3 security features include source guard and reverse path
+  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and
+  IP tunnels (IP over GRE/IP).
+
+  The SparX-5 switch family targets managed Layer 2 and Layer 3
+  equipment in SMB, SME, and Enterprise where high port count
+  1G/2.5G/5G/10G switching with 10G/25G aggregation links is required.
+
+properties:
+  $nodename:
+    pattern: "^switch@[0-9a-f]+$"
+
+  compatible:
+    const: microchip,sparx5-switch
+
+  reg:
+    minItems: 2
+
+  reg-names:
+    minItems: 2
+    items:
+      - const: devices
+      - const: gcb
+
+  interrupts:
+    maxItems: 1
+    description: Interrupt used for reception of packets to the CPU
+
+  ethernet-ports:
+    type: object
+    properties:
+      '#address-cells':
+        const: 1
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      "^port@[0-9]+$":
+        type: object
+        description: Switch ports
+
+        allOf:
+          - $ref: ethernet-controller.yaml#
+
+        properties:
+          reg:
+            description: Switch port number
+
+          max-speed:
+            maxItems: 1
+            description: Bandwidth allocated to this port
+
+          phys:
+            description: phandle of a Ethernet Serdes PHY
+
+          phy-handle:
+            description: phandle of a Ethernet PHY
+
+          phy-mode:
+            description: Interface between the serdes and the phy
+
+          sfp:
+            description: phandle of an SFP
+
+          managed:
+            maxItems: 1
+            description: SFP management
+
+        required:
+          - reg
+          - max-speed
+          - phys
+
+        oneOf:
+          - required:
+              - phy-handle
+              - phy-mode
+          - required:
+              - sfp
+              - managed
+
+        additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - ethernet-ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    switch: switch@600000000 {
+      compatible = "microchip,sparx5-switch";
+      reg =  <0x10000000 0x800000>,
+             <0x11010000 0x1b00000>;
+      reg-names = "devices", "gcb";
+
+      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+      ethernet-ports {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        port0: port@0 {
+          reg = <0>;
+          max-speed = <1000>;
+          phys = <&serdes 13>;
+          phy-handle = <&phy0>;
+          phy-mode = "qsgmii";
+        };
+        /* ... */
+        /* Then the 25G interfaces */
+        port60: port@60 {
+          reg = <60>;
+          max-speed = <25000>;
+          phys = <&serdes 29>;
+          sfp = <&sfp_eth60>;
+          managed = "in-band-status";
+        };
+        port61: port@61 {
+          reg = <61>;
+          max-speed = <25000>;
+          phys = <&serdes 30>;
+          sfp = <&sfp_eth61>;
+          managed = "in-band-status";
+        };
+        port62: port@62 {
+          reg = <62>;
+          max-speed = <25000>;
+          phys = <&serdes 31>;
+          sfp = <&sfp_eth62>;
+          managed = "in-band-status";
+        };
+        port63: port@63 {
+          reg = <63>;
+          max-speed = <25000>;
+          phys = <&serdes 32>;
+          sfp = <&sfp_eth63>;
+          managed = "in-band-status";
+        };
+        /* Finally the Management interface */
+        port64: port@64 {
+          reg = <64>;
+          max-speed = <1000>;
+          phys = <&serdes 0>;
+          phy-handle = <&phy64>;
+          phy-mode = "sgmii";
+        };
+      };
+    };
+
+...
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-17  7:51 ` Steen Hegelund
  (?)
  (?)
@ 2020-12-17  7:51 ` Steen Hegelund
  2020-12-19 19:11     ` Andrew Lunn
  -1 siblings, 1 reply; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This adds the SwitchDev driver basic framework with switch device
detection and core clock configuration.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 drivers/net/ethernet/microchip/Kconfig        |    2 +
 drivers/net/ethernet/microchip/Makefile       |    2 +
 drivers/net/ethernet/microchip/sparx5/Kconfig |    8 +
 .../net/ethernet/microchip/sparx5/Makefile    |    8 +
 .../ethernet/microchip/sparx5/sparx5_main.c   |  733 +++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  282 ++
 .../microchip/sparx5/sparx5_main_regs.h       | 3922 +++++++++++++++++
 7 files changed, 4957 insertions(+)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Kconfig
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Makefile
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h

diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig
index d0f6dfe0dcf3..d54aa164c4e9 100644
--- a/drivers/net/ethernet/microchip/Kconfig
+++ b/drivers/net/ethernet/microchip/Kconfig
@@ -54,4 +54,6 @@ config LAN743X
 	  To compile this driver as a module, choose M here. The module will be
 	  called lan743x.
 
+source "drivers/net/ethernet/microchip/sparx5/Kconfig"
+
 endif # NET_VENDOR_MICROCHIP
diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile
index da603540ca57..c77dc0379bfd 100644
--- a/drivers/net/ethernet/microchip/Makefile
+++ b/drivers/net/ethernet/microchip/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o
 obj-$(CONFIG_LAN743X) += lan743x.o
 
 lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o
+
+obj-$(CONFIG_SPARX5_SWITCH) += sparx5/
diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig
new file mode 100644
index 000000000000..d2cb84372071
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/Kconfig
@@ -0,0 +1,8 @@
+config SPARX5_SWITCH
+	tristate "Sparx5 switch driver"
+	depends on NET_SWITCHDEV
+	depends on HAS_IOMEM
+	select PHYLINK
+	select PHY_SPARX5_SERDES
+	help
+	  This driver supports the Sparx5 network switch device.
diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
new file mode 100644
index 000000000000..41a31843d86f
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Microchip Sparx5 network device drivers.
+#
+
+obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
+
+sparx5-switch-objs  := sparx5_main.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
new file mode 100644
index 000000000000..67435fa45c7f
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ *
+ * The Sparx5 Chip Register Model can be browsed at this location:
+ * https://github.com/microchip-ung/sparx-5_reginfo
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <net/switchdev.h>
+#include <linux/etherdevice.h>
+#include <linux/io.h>
+#include <linux/printk.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "sparx5_main.h"
+
+/* Switch core reset/protect */
+#define RESET_PROT_STAT		0x84
+#define SYS_RST_PROT_VCORE	BIT(10)
+#define QLIM_WM(fraction) \
+	((SPX5_BUFFER_MEMORY / SPX5_BUFFER_CELL_SZ - 100) * (fraction) / 100)
+#define IO_RANGES               2
+
+struct sparx5_io_resource {
+	enum sparx5_target id;
+	phys_addr_t        offset;
+	int                range;
+};
+
+static struct sparx5_io_resource sparx5_iomap[] =  {
+	{ TARGET_DEV2G5,         0,         0 }, /* 0x610004000: dev2g5_0 */
+	{ TARGET_DEV5G,          0x4000,    0 }, /* 0x610008000: dev5g_0 */
+	{ TARGET_PCS5G_BR,       0x8000,    0 }, /* 0x61000c000: pcs5g_br_0 */
+	{ TARGET_DEV2G5 + 1,     0xc000,    0 }, /* 0x610010000: dev2g5_1 */
+	{ TARGET_DEV5G + 1,      0x10000,   0 }, /* 0x610014000: dev5g_1 */
+	{ TARGET_PCS5G_BR + 1,   0x14000,   0 }, /* 0x610018000: pcs5g_br_1 */
+	{ TARGET_DEV2G5 + 2,     0x18000,   0 }, /* 0x61001c000: dev2g5_2 */
+	{ TARGET_DEV5G + 2,      0x1c000,   0 }, /* 0x610020000: dev5g_2 */
+	{ TARGET_PCS5G_BR + 2,   0x20000,   0 }, /* 0x610024000: pcs5g_br_2 */
+	{ TARGET_DEV2G5 + 6,     0x24000,   0 }, /* 0x610028000: dev2g5_6 */
+	{ TARGET_DEV5G + 6,      0x28000,   0 }, /* 0x61002c000: dev5g_6 */
+	{ TARGET_PCS5G_BR + 6,   0x2c000,   0 }, /* 0x610030000: pcs5g_br_6 */
+	{ TARGET_DEV2G5 + 7,     0x30000,   0 }, /* 0x610034000: dev2g5_7 */
+	{ TARGET_DEV5G + 7,      0x34000,   0 }, /* 0x610038000: dev5g_7 */
+	{ TARGET_PCS5G_BR + 7,   0x38000,   0 }, /* 0x61003c000: pcs5g_br_7 */
+	{ TARGET_DEV2G5 + 8,     0x3c000,   0 }, /* 0x610040000: dev2g5_8 */
+	{ TARGET_DEV5G + 8,      0x40000,   0 }, /* 0x610044000: dev5g_8 */
+	{ TARGET_PCS5G_BR + 8,   0x44000,   0 }, /* 0x610048000: pcs5g_br_8 */
+	{ TARGET_DEV2G5 + 9,     0x48000,   0 }, /* 0x61004c000: dev2g5_9 */
+	{ TARGET_DEV5G + 9,      0x4c000,   0 }, /* 0x610050000: dev5g_9 */
+	{ TARGET_PCS5G_BR + 9,   0x50000,   0 }, /* 0x610054000: pcs5g_br_9 */
+	{ TARGET_DEV2G5 + 10,    0x54000,   0 }, /* 0x610058000: dev2g5_10 */
+	{ TARGET_DEV5G + 10,     0x58000,   0 }, /* 0x61005c000: dev5g_10 */
+	{ TARGET_PCS5G_BR + 10,  0x5c000,   0 }, /* 0x610060000: pcs5g_br_10 */
+	{ TARGET_DEV2G5 + 11,    0x60000,   0 }, /* 0x610064000: dev2g5_11 */
+	{ TARGET_DEV5G + 11,     0x64000,   0 }, /* 0x610068000: dev5g_11 */
+	{ TARGET_PCS5G_BR + 11,  0x68000,   0 }, /* 0x61006c000: pcs5g_br_11 */
+	{ TARGET_DEV2G5 + 12,    0x6c000,   0 }, /* 0x610070000: dev2g5_12 */
+	{ TARGET_DEV10G,         0x70000,   0 }, /* 0x610074000: dev10g_0 */
+	{ TARGET_PCS10G_BR,      0x74000,   0 }, /* 0x610078000: pcs10g_br_0 */
+	{ TARGET_DEV2G5 + 14,    0x78000,   0 }, /* 0x61007c000: dev2g5_14 */
+	{ TARGET_DEV10G + 2,     0x7c000,   0 }, /* 0x610080000: dev10g_2 */
+	{ TARGET_PCS10G_BR + 2,  0x80000,   0 }, /* 0x610084000: pcs10g_br_2 */
+	{ TARGET_DEV2G5 + 15,    0x84000,   0 }, /* 0x610088000: dev2g5_15 */
+	{ TARGET_DEV10G + 3,     0x88000,   0 }, /* 0x61008c000: dev10g_3 */
+	{ TARGET_PCS10G_BR + 3,  0x8c000,   0 }, /* 0x610090000: pcs10g_br_3 */
+	{ TARGET_DEV2G5 + 16,    0x90000,   0 }, /* 0x610094000: dev2g5_16 */
+	{ TARGET_DEV2G5 + 17,    0x94000,   0 }, /* 0x610098000: dev2g5_17 */
+	{ TARGET_DEV2G5 + 18,    0x98000,   0 }, /* 0x61009c000: dev2g5_18 */
+	{ TARGET_DEV2G5 + 19,    0x9c000,   0 }, /* 0x6100a0000: dev2g5_19 */
+	{ TARGET_DEV2G5 + 20,    0xa0000,   0 }, /* 0x6100a4000: dev2g5_20 */
+	{ TARGET_DEV2G5 + 21,    0xa4000,   0 }, /* 0x6100a8000: dev2g5_21 */
+	{ TARGET_DEV2G5 + 22,    0xa8000,   0 }, /* 0x6100ac000: dev2g5_22 */
+	{ TARGET_DEV2G5 + 23,    0xac000,   0 }, /* 0x6100b0000: dev2g5_23 */
+	{ TARGET_DEV2G5 + 32,    0xb0000,   0 }, /* 0x6100b4000: dev2g5_32 */
+	{ TARGET_DEV2G5 + 33,    0xb4000,   0 }, /* 0x6100b8000: dev2g5_33 */
+	{ TARGET_DEV2G5 + 34,    0xb8000,   0 }, /* 0x6100bc000: dev2g5_34 */
+	{ TARGET_DEV2G5 + 35,    0xbc000,   0 }, /* 0x6100c0000: dev2g5_35 */
+	{ TARGET_DEV2G5 + 36,    0xc0000,   0 }, /* 0x6100c4000: dev2g5_36 */
+	{ TARGET_DEV2G5 + 37,    0xc4000,   0 }, /* 0x6100c8000: dev2g5_37 */
+	{ TARGET_DEV2G5 + 38,    0xc8000,   0 }, /* 0x6100cc000: dev2g5_38 */
+	{ TARGET_DEV2G5 + 39,    0xcc000,   0 }, /* 0x6100d0000: dev2g5_39 */
+	{ TARGET_DEV2G5 + 40,    0xd0000,   0 }, /* 0x6100d4000: dev2g5_40 */
+	{ TARGET_DEV2G5 + 41,    0xd4000,   0 }, /* 0x6100d8000: dev2g5_41 */
+	{ TARGET_DEV2G5 + 42,    0xd8000,   0 }, /* 0x6100dc000: dev2g5_42 */
+	{ TARGET_DEV2G5 + 43,    0xdc000,   0 }, /* 0x6100e0000: dev2g5_43 */
+	{ TARGET_DEV2G5 + 44,    0xe0000,   0 }, /* 0x6100e4000: dev2g5_44 */
+	{ TARGET_DEV2G5 + 45,    0xe4000,   0 }, /* 0x6100e8000: dev2g5_45 */
+	{ TARGET_DEV2G5 + 46,    0xe8000,   0 }, /* 0x6100ec000: dev2g5_46 */
+	{ TARGET_DEV2G5 + 47,    0xec000,   0 }, /* 0x6100f0000: dev2g5_47 */
+	{ TARGET_DEV2G5 + 57,    0xf0000,   0 }, /* 0x6100f4000: dev2g5_57 */
+	{ TARGET_DEV25G + 1,     0xf4000,   0 }, /* 0x6100f8000: dev25g_1 */
+	{ TARGET_PCS25G_BR + 1,  0xf8000,   0 }, /* 0x6100fc000: pcs25g_br_1 */
+	{ TARGET_DEV2G5 + 59,    0x100000,  0 }, /* 0x610104000: dev2g5_59 */
+	{ TARGET_DEV25G + 3,     0x104000,  0 }, /* 0x610108000: dev25g_3 */
+	{ TARGET_PCS25G_BR + 3,  0x108000,  0 }, /* 0x61010c000: pcs25g_br_3 */
+	{ TARGET_DEV2G5 + 60,    0x110000,  0 }, /* 0x610114000: dev2g5_60 */
+	{ TARGET_DEV25G + 4,     0x114000,  0 }, /* 0x610118000: dev25g_4 */
+	{ TARGET_PCS25G_BR + 4,  0x118000,  0 }, /* 0x61011c000: pcs25g_br_4 */
+	{ TARGET_DEV2G5 + 64,    0x120000,  0 }, /* 0x610124000: dev2g5_64 */
+	{ TARGET_DEV5G + 12,     0x124000,  0 }, /* 0x610128000: dev5g_64 */
+	{ TARGET_PCS5G_BR + 12,  0x128000,  0 }, /* 0x61012c000: pcs5g_br_64 */
+	{ TARGET_PORT_CONF,      0x12c000,  0 }, /* 0x610130000: port_conf */
+	{ TARGET_DEV2G5 + 3,     0x400000,  0 }, /* 0x610404000: dev2g5_3 */
+	{ TARGET_DEV5G + 3,      0x404000,  0 }, /* 0x610408000: dev5g_3 */
+	{ TARGET_PCS5G_BR + 3,   0x408000,  0 }, /* 0x61040c000: pcs5g_br_3 */
+	{ TARGET_DEV2G5 + 4,     0x40c000,  0 }, /* 0x610410000: dev2g5_4 */
+	{ TARGET_DEV5G + 4,      0x410000,  0 }, /* 0x610414000: dev5g_4 */
+	{ TARGET_PCS5G_BR + 4,   0x414000,  0 }, /* 0x610418000: pcs5g_br_4 */
+	{ TARGET_DEV2G5 + 5,     0x418000,  0 }, /* 0x61041c000: dev2g5_5 */
+	{ TARGET_DEV5G + 5,      0x41c000,  0 }, /* 0x610420000: dev5g_5 */
+	{ TARGET_PCS5G_BR + 5,   0x420000,  0 }, /* 0x610424000: pcs5g_br_5 */
+	{ TARGET_DEV2G5 + 13,    0x424000,  0 }, /* 0x610428000: dev2g5_13 */
+	{ TARGET_DEV10G + 1,     0x428000,  0 }, /* 0x61042c000: dev10g_1 */
+	{ TARGET_PCS10G_BR + 1,  0x42c000,  0 }, /* 0x610430000: pcs10g_br_1 */
+	{ TARGET_DEV2G5 + 24,    0x430000,  0 }, /* 0x610434000: dev2g5_24 */
+	{ TARGET_DEV2G5 + 25,    0x434000,  0 }, /* 0x610438000: dev2g5_25 */
+	{ TARGET_DEV2G5 + 26,    0x438000,  0 }, /* 0x61043c000: dev2g5_26 */
+	{ TARGET_DEV2G5 + 27,    0x43c000,  0 }, /* 0x610440000: dev2g5_27 */
+	{ TARGET_DEV2G5 + 28,    0x440000,  0 }, /* 0x610444000: dev2g5_28 */
+	{ TARGET_DEV2G5 + 29,    0x444000,  0 }, /* 0x610448000: dev2g5_29 */
+	{ TARGET_DEV2G5 + 30,    0x448000,  0 }, /* 0x61044c000: dev2g5_30 */
+	{ TARGET_DEV2G5 + 31,    0x44c000,  0 }, /* 0x610450000: dev2g5_31 */
+	{ TARGET_DEV2G5 + 48,    0x450000,  0 }, /* 0x610454000: dev2g5_48 */
+	{ TARGET_DEV10G + 4,     0x454000,  0 }, /* 0x610458000: dev10g_4 */
+	{ TARGET_PCS10G_BR + 4,  0x458000,  0 }, /* 0x61045c000: pcs10g_br_4 */
+	{ TARGET_DEV2G5 + 49,    0x45c000,  0 }, /* 0x610460000: dev2g5_49 */
+	{ TARGET_DEV10G + 5,     0x460000,  0 }, /* 0x610464000: dev10g_5 */
+	{ TARGET_PCS10G_BR + 5,  0x464000,  0 }, /* 0x610468000: pcs10g_br_5 */
+	{ TARGET_DEV2G5 + 50,    0x468000,  0 }, /* 0x61046c000: dev2g5_50 */
+	{ TARGET_DEV10G + 6,     0x46c000,  0 }, /* 0x610470000: dev10g_6 */
+	{ TARGET_PCS10G_BR + 6,  0x470000,  0 }, /* 0x610474000: pcs10g_br_6 */
+	{ TARGET_DEV2G5 + 51,    0x474000,  0 }, /* 0x610478000: dev2g5_51 */
+	{ TARGET_DEV10G + 7,     0x478000,  0 }, /* 0x61047c000: dev10g_7 */
+	{ TARGET_PCS10G_BR + 7,  0x47c000,  0 }, /* 0x610480000: pcs10g_br_7 */
+	{ TARGET_DEV2G5 + 52,    0x480000,  0 }, /* 0x610484000: dev2g5_52 */
+	{ TARGET_DEV10G + 8,     0x484000,  0 }, /* 0x610488000: dev10g_8 */
+	{ TARGET_PCS10G_BR + 8,  0x488000,  0 }, /* 0x61048c000: pcs10g_br_8 */
+	{ TARGET_DEV2G5 + 53,    0x48c000,  0 }, /* 0x610490000: dev2g5_53 */
+	{ TARGET_DEV10G + 9,     0x490000,  0 }, /* 0x610494000: dev10g_9 */
+	{ TARGET_PCS10G_BR + 9,  0x494000,  0 }, /* 0x610498000: pcs10g_br_9 */
+	{ TARGET_DEV2G5 + 54,    0x498000,  0 }, /* 0x61049c000: dev2g5_54 */
+	{ TARGET_DEV10G + 10,    0x49c000,  0 }, /* 0x6104a0000: dev10g_10 */
+	{ TARGET_PCS10G_BR + 10, 0x4a0000,  0 }, /* 0x6104a4000: pcs10g_br_10 */
+	{ TARGET_DEV2G5 + 55,    0x4a4000,  0 }, /* 0x6104a8000: dev2g5_55 */
+	{ TARGET_DEV10G + 11,    0x4a8000,  0 }, /* 0x6104ac000: dev10g_11 */
+	{ TARGET_PCS10G_BR + 11, 0x4ac000,  0 }, /* 0x6104b0000: pcs10g_br_11 */
+	{ TARGET_DEV2G5 + 56,    0x4b0000,  0 }, /* 0x6104b4000: dev2g5_56 */
+	{ TARGET_DEV25G,         0x4b4000,  0 }, /* 0x6104b8000: dev25g_0 */
+	{ TARGET_PCS25G_BR,      0x4b8000,  0 }, /* 0x6104bc000: pcs25g_br_0 */
+	{ TARGET_DEV2G5 + 58,    0x4c0000,  0 }, /* 0x6104c4000: dev2g5_58 */
+	{ TARGET_DEV25G + 2,     0x4c4000,  0 }, /* 0x6104c8000: dev25g_2 */
+	{ TARGET_PCS25G_BR + 2,  0x4c8000,  0 }, /* 0x6104cc000: pcs25g_br_2 */
+	{ TARGET_DEV2G5 + 61,    0x4d0000,  0 }, /* 0x6104d4000: dev2g5_61 */
+	{ TARGET_DEV25G + 5,     0x4d4000,  0 }, /* 0x6104d8000: dev25g_5 */
+	{ TARGET_PCS25G_BR + 5,  0x4d8000,  0 }, /* 0x6104dc000: pcs25g_br_5 */
+	{ TARGET_DEV2G5 + 62,    0x4e0000,  0 }, /* 0x6104e4000: dev2g5_62 */
+	{ TARGET_DEV25G + 6,     0x4e4000,  0 }, /* 0x6104e8000: dev25g_6 */
+	{ TARGET_PCS25G_BR + 6,  0x4e8000,  0 }, /* 0x6104ec000: pcs25g_br_6 */
+	{ TARGET_DEV2G5 + 63,    0x4f0000,  0 }, /* 0x6104f4000: dev2g5_63 */
+	{ TARGET_DEV25G + 7,     0x4f4000,  0 }, /* 0x6104f8000: dev25g_7 */
+	{ TARGET_PCS25G_BR + 7,  0x4f8000,  0 }, /* 0x6104fc000: pcs25g_br_7 */
+	{ TARGET_DSM,            0x500000,  0 }, /* 0x610504000: dsm */
+	{ TARGET_ASM,            0x5fc000,  0 }, /* 0x610600000: asm */
+	{ TARGET_GCB,            0x100c000, 1 }, /* 0x611010000: gcb */
+	{ TARGET_QS,             0x102c000, 1 }, /* 0x611030000: qs */
+	{ TARGET_ANA_ACL,        0x104c000, 1 }, /* 0x611050000: ana_acl */
+	{ TARGET_LRN,            0x105c000, 1 }, /* 0x611060000: lrn */
+	{ TARGET_VCAP_SUPER,     0x107c000, 1 }, /* 0x611080000: vcap_super */
+	{ TARGET_QSYS,           0x109c000, 1 }, /* 0x6110a0000: qsys */
+	{ TARGET_QFWD,           0x10ac000, 1 }, /* 0x6110b0000: qfwd */
+	{ TARGET_XQS,            0x10bc000, 1 }, /* 0x6110c0000: xqs */
+	{ TARGET_CLKGEN,         0x10fc000, 1 }, /* 0x611100000: clkgen */
+	{ TARGET_ANA_AC_POL,     0x11fc000, 1 }, /* 0x611200000: ana_ac_pol */
+	{ TARGET_QRES,           0x127c000, 1 }, /* 0x611280000: qres */
+	{ TARGET_EACL,           0x12bc000, 1 }, /* 0x6112c0000: eacl */
+	{ TARGET_ANA_CL,         0x13fc000, 1 }, /* 0x611400000: ana_cl */
+	{ TARGET_ANA_L3,         0x147c000, 1 }, /* 0x611480000: ana_l3 */
+	{ TARGET_HSCH,           0x157c000, 1 }, /* 0x611580000: hsch */
+	{ TARGET_REW,            0x15fc000, 1 }, /* 0x611600000: rew */
+	{ TARGET_ANA_L2,         0x17fc000, 1 }, /* 0x611800000: ana_l2 */
+	{ TARGET_ANA_AC,         0x18fc000, 1 }, /* 0x611900000: ana_ac */
+	{ TARGET_VOP,            0x19fc000, 1 }, /* 0x611a00000: vop */
+};
+
+static int sparx5_create_targets(struct sparx5 *sparx5)
+{
+	int idx, jdx;
+	struct resource *iores[IO_RANGES];
+	void __iomem *iomem[IO_RANGES];
+	void __iomem *begin[IO_RANGES];
+	int range_id[IO_RANGES];
+
+	/* Check if done previously (deferred by serdes load) */
+	if (sparx5->regs[sparx5_iomap[0].id])
+		return 0;
+
+	for (idx = 0, jdx = 0; jdx < ARRAY_SIZE(sparx5_iomap); jdx++) {
+		struct sparx5_io_resource *iomap = &sparx5_iomap[jdx];
+
+		if (idx == iomap->range) {
+			range_id[idx] = jdx;
+			idx++;
+		}
+	}
+	for (idx = 0; idx < IO_RANGES; idx++) {
+		iores[idx] = platform_get_resource(sparx5->pdev, IORESOURCE_MEM, idx);
+		iomem[idx] = devm_ioremap(sparx5->dev,
+					  iores[idx]->start,
+					  iores[idx]->end - iores[idx]->start + 1);
+		if (IS_ERR(iomem[idx])) {
+			dev_err(sparx5->dev, "Unable to get switch registers: %s\n",
+				iores[idx]->name);
+			return PTR_ERR(iomem[idx]);
+		}
+		begin[idx] = iomem[idx] - sparx5_iomap[range_id[idx]].offset;
+	}
+	for (jdx = 0; jdx < ARRAY_SIZE(sparx5_iomap); jdx++) {
+		struct sparx5_io_resource *iomap = &sparx5_iomap[jdx];
+
+		sparx5->regs[iomap->id] = begin[iomap->range] + iomap->offset;
+	}
+	return 0;
+}
+
+static int sparx5_probe_port(struct sparx5 *sparx5,
+			     struct device_node *portnp,
+			     struct phy *serdes,
+			     u32 portno,
+			     struct sparx5_port_config *conf)
+{
+	struct sparx5_port *spx5_port;
+	struct net_device *ndev;
+	int err;
+
+	err = sparx5_create_targets(sparx5);
+	if (err)
+		return err;
+	spx5_port = netdev_priv(ndev);
+	spx5_port->of_node = portnp;
+	spx5_port->serdes = serdes;
+	spx5_port->pvid = NULL_VID;
+	spx5_port->signd_internal = true;
+	spx5_port->signd_active_high = true;
+	spx5_port->signd_enable = true;
+	spx5_port->flow_control = false;
+	spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
+	spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
+	spx5_port->custom_etype = 0x8880; /* Vitesse */
+	conf->portmode = conf->phy_mode;
+	spx5_port->conf.speed = SPEED_UNKNOWN;
+	spx5_port->conf.power_down = true;
+	sparx5->ports[portno] = spx5_port;
+	return 0;
+}
+
+static int sparx5_init_switchcore(struct sparx5 *sparx5)
+{
+	u32 value, pending, jdx, idx;
+	struct {
+		bool gazwrap;
+		void __iomem *init_reg;
+		u32  init_val;
+	} ram, ram_init_list[] = {
+		{false, spx5_reg_get(sparx5, ANA_AC_STAT_RESET),
+		 ANA_AC_STAT_RESET_RESET},
+		{false, spx5_reg_get(sparx5, ASM_STAT_CFG),
+		 ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
+		{true,  spx5_reg_get(sparx5, QSYS_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, REW_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, VOP_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, ANA_AC_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, ASM_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, EACL_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, VCAP_SUPER_RAM_INIT), 0},
+		{true,  spx5_reg_get(sparx5, DSM_RAM_INIT), 0}
+	};
+
+	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
+		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
+		 sparx5,
+		 EACL_POL_EACL_CFG);
+
+	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
+		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
+		 sparx5,
+		 EACL_POL_EACL_CFG);
+
+	/* Initialize memories, if not done already */
+	value = spx5_rd(sparx5, HSCH_RESET_CFG);
+
+	if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
+		for (idx = 0; idx < 10; idx++) {
+			pending = ARRAY_SIZE(ram_init_list);
+			for (jdx = 0; jdx < ARRAY_SIZE(ram_init_list); jdx++) {
+				ram = ram_init_list[jdx];
+				if (ram.gazwrap)
+					ram.init_val = QSYS_RAM_INIT_RAM_INIT;
+
+				if (idx == 0) {
+					writel(ram.init_val, ram.init_reg);
+				} else {
+					value = readl(ram.init_reg);
+					if ((value & ram.init_val) !=
+					    ram.init_val) {
+						pending--;
+					}
+				}
+			}
+			if (!pending)
+				break;
+			usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
+		}
+
+		if (pending > 0) {
+			/* Still initializing, should be complete in
+			 * less than 1ms
+			 */
+			dev_err(sparx5->dev, "Memory initialization error\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Reset counters */
+	spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
+	spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
+
+	/* Enable switch-core and queue system */
+	spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
+
+	return 0;
+}
+
+static int sparx5_init_coreclock(struct sparx5 *sparx5)
+{
+	u32 clk_div, clk_period, pol_upd_int, idx;
+	enum sparx5_core_clockfreq freq = sparx5->coreclock;
+
+	/* Verify if core clock frequency is supported on target.
+	 * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
+	 * freq. is used
+	 */
+	switch (sparx5->target_ct) {
+	case SPX5_TARGET_CT_7546:
+		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
+			freq = SPX5_CORE_CLOCK_250MHZ;
+		else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
+			freq = 0; /* Not supported */
+		break;
+	case SPX5_TARGET_CT_7549:
+	case SPX5_TARGET_CT_7552:
+	case SPX5_TARGET_CT_7556:
+		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
+			freq = SPX5_CORE_CLOCK_500MHZ;
+		else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
+			freq = 0; /* Not supported */
+		break;
+	case SPX5_TARGET_CT_7558:
+	case SPX5_TARGET_CT_7558TSN:
+		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
+			freq = SPX5_CORE_CLOCK_625MHZ;
+		else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
+			freq = 0; /* Not supported */
+		break;
+	case SPX5_TARGET_CT_7546TSN:
+		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
+			freq = SPX5_CORE_CLOCK_625MHZ;
+		break;
+	case SPX5_TARGET_CT_7549TSN:
+	case SPX5_TARGET_CT_7552TSN:
+	case SPX5_TARGET_CT_7556TSN:
+		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
+			freq = SPX5_CORE_CLOCK_625MHZ;
+		else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
+			freq = 0; /* Not supported */
+		break;
+	default:
+		dev_err(sparx5->dev, "Target (%#04x) not supported\n", sparx5->target_ct);
+		return -ENODEV;
+	}
+
+	switch (freq) {
+	case SPX5_CORE_CLOCK_250MHZ:
+		clk_div = 10;
+		pol_upd_int = 312;
+		break;
+	case SPX5_CORE_CLOCK_500MHZ:
+		clk_div = 5;
+		pol_upd_int = 624;
+		break;
+	case SPX5_CORE_CLOCK_625MHZ:
+		clk_div = 4;
+		pol_upd_int = 780;
+		break;
+	default:
+		dev_err(sparx5->dev, "%s: Frequency (%d) not supported on target (%#04x)\n",
+			__func__,
+			sparx5->coreclock, sparx5->target_ct);
+		return 0;
+	}
+
+	/* Update state with chosen frequency */
+	sparx5->coreclock = freq;
+
+	/* Configure the LCPLL */
+	spx5_rmw(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_SET(clk_div) |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_SET(0) |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_SET(0) |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_SET(0) |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_SET(0) |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_SET(1),
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA |
+		 CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA,
+		 sparx5,
+		 CLKGEN_LCPLL1_CORE_CLK_CFG);
+
+	clk_period = sparx5_clk_period(freq);
+
+	spx5_rmw(HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS_SET(clk_period / 100),
+		 HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS,
+		 sparx5,
+		 HSCH_SYS_CLK_PER);
+
+	spx5_rmw(ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS_SET(clk_period / 100),
+		 ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS,
+		 sparx5,
+		 ANA_AC_POL_BDLB_DLB_CTRL);
+
+	spx5_rmw(ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS_SET(clk_period / 100),
+		 ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS,
+		 sparx5,
+		 ANA_AC_POL_SLB_DLB_CTRL);
+
+	spx5_rmw(LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS_SET(clk_period / 100),
+		 LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS,
+		 sparx5,
+		 LRN_AUTOAGE_CFG_1);
+
+	for (idx = 0; idx < 3; idx++) {
+		spx5_rmw(GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(clk_period / 100),
+			 GCB_SIO_CLOCK_SYS_CLK_PERIOD,
+			 sparx5,
+			 GCB_SIO_CLOCK(idx));
+	}
+
+	spx5_rmw(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_SET
+		 ((256 * 1000) / clk_period),
+		 HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY,
+		 sparx5,
+		 HSCH_TAS_STATEMACHINE_CFG);
+
+	spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
+		 ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
+		 sparx5,
+		 ANA_AC_POL_POL_UPD_INT_CFG);
+
+	return 0;
+}
+
+static int sparx5_qlim_set(struct sparx5 *sparx5)
+{
+	u32 res, dp, prio;
+
+	for (res = 0; res < 2; res++) {
+		for (prio = 0; prio < 8; prio++)
+			spx5_wr(0xFFF, sparx5, QRES_RES_CFG(prio + 630 + res * 1024));
+
+		for (dp = 0; dp < 4; dp++)
+			spx5_wr(0xFFF, sparx5, QRES_RES_CFG(dp + 638 + res * 1024));
+	}
+
+	/* Set 80,90,95,100% of memory size for top watermarks */
+	spx5_wr(QLIM_WM(80), sparx5, XQS_QLIMIT_SHR_QLIM_CFG(0));
+	spx5_wr(QLIM_WM(90), sparx5, XQS_QLIMIT_SHR_CTOP_CFG(0));
+	spx5_wr(QLIM_WM(95), sparx5, XQS_QLIMIT_SHR_ATOP_CFG(0));
+	spx5_wr(QLIM_WM(100), sparx5, XQS_QLIMIT_SHR_TOP_CFG(0));
+
+	return 0;
+}
+
+static int sparx5_init(struct sparx5 *sparx5)
+{
+	u32 idx;
+
+	if (sparx5_create_targets(sparx5))
+		return -ENODEV;
+
+	/* Read chip ID to check CPU interface */
+	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
+
+	sparx5->target_ct = (enum spx5_target_chiptype)
+		GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
+
+	/* Initialize Switchcore and internal RAMs */
+	if (sparx5_init_switchcore(sparx5)) {
+		dev_err(sparx5->dev, "Switchcore initialization error\n");
+		return -EINVAL;
+	}
+
+	/* Initialize the LC-PLL (core clock) and set affected registers */
+	if (sparx5_init_coreclock(sparx5)) {
+		dev_err(sparx5->dev, "LC-PLL initialization error\n");
+		return -EINVAL;
+	}
+
+	/* Setup own UPSIDs */
+	for (idx = 0; idx < 3; idx++) {
+		spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
+		spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
+		spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
+		spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
+	}
+
+	/* Enable switch ports */
+	for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++) {
+		spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
+			 QFWD_SWITCH_PORT_MODE_PORT_ENA,
+			 sparx5,
+			 QFWD_SWITCH_PORT_MODE(idx));
+	}
+
+	/* CPU copy CPU pgids */
+	spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
+		sparx5, ANA_AC_PGID_MISC_CFG(PGID_CPU));
+	spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
+		sparx5, ANA_AC_PGID_MISC_CFG(PGID_BCAST));
+
+	/* Recalc injected frame FCS */
+	for (idx = SPX5_PORT_CPU_0; idx <= SPX5_PORT_CPU_1; idx++)
+		spx5_rmw(ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA_SET(1),
+			 ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
+			 sparx5, ANA_CL_FILTER_CTRL(idx));
+
+	/* Enable queue limitation watermarks */
+	sparx5_qlim_set(sparx5);
+
+	return 0;
+}
+
+/* Some boards needs to map the SGPIO for signal detect explicitly to the
+ * port module
+ */
+static void sparx5_board_init(struct sparx5 *sparx5)
+{
+	int idx;
+
+	if (!sparx5->sd_sgpio_remapping)
+		return;
+
+	/* Enable SGPIO Signal Detect remapping */
+	spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
+		 GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
+		 sparx5,
+		 GCB_HW_SGPIO_SD_CFG);
+
+	/* Refer to LOS SGPIO */
+	for (idx = 0; idx < SPX5_PORTS; idx++) {
+		if (sparx5->ports[idx]) {
+			if (sparx5->ports[idx]->conf.sd_sgpio != ~0) {
+				spx5_wr(sparx5->ports[idx]->conf.sd_sgpio,
+					sparx5,
+					GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
+			}
+		}
+	}
+}
+
+static int mchp_sparx5_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct sparx5 *sparx5;
+	struct device_node *ports, *portnp;
+	const u8 *mac_addr;
+	int err = 0;
+
+	if (!np && !pdev->dev.platform_data)
+		return -ENODEV;
+
+	sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5), GFP_KERNEL);
+	if (!sparx5)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, sparx5);
+	sparx5->pdev = pdev;
+	sparx5->dev = &pdev->dev;
+
+	/* Default values, some from DT */
+	sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
+
+	mac_addr = of_get_mac_address(np);
+	if (IS_ERR_OR_NULL(mac_addr)) {
+		dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
+		eth_random_addr(sparx5->base_mac);
+		sparx5->base_mac[5] = 0;
+	} else {
+		ether_addr_copy(sparx5->base_mac, mac_addr);
+	}
+
+	if (sparx5_init(sparx5)) {
+		dev_err(sparx5->dev, "Init failed\n");
+		return -ENODEV;
+	}
+	ports = of_get_child_by_name(np, "ethernet-ports");
+	if (!ports) {
+		dev_err(sparx5->dev, "no ethernet-ports child node found\n");
+		return -ENODEV;
+	}
+	sparx5->port_count = of_get_child_count(ports);
+
+	for_each_available_child_of_node(ports, portnp) {
+		struct sparx5_port_config config = {};
+		u32 portno;
+		struct phy *serdes;
+
+		err = of_property_read_u32(portnp, "reg", &portno);
+		if (err) {
+			dev_err(sparx5->dev, "port reg property error\n");
+			continue;
+		}
+		err = of_property_read_u32(portnp, "max-speed",
+					   &config.max_speed);
+		if (err) {
+			dev_err(sparx5->dev, "port max-speed property error\n");
+			continue;
+		}
+		config.speed = SPEED_UNKNOWN;
+		err = of_property_read_u32(portnp, "sd_sgpio", &config.sd_sgpio);
+		if (err)
+			config.sd_sgpio = ~0;
+		else
+			sparx5->sd_sgpio_remapping = true;
+		serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
+		if (IS_ERR(serdes)) {
+			err = PTR_ERR(serdes);
+			if (err != -EPROBE_DEFER)
+				dev_err(sparx5->dev,
+					"missing SerDes phys for port%d\n",
+					portno);
+			return err;
+		}
+
+		err = of_get_phy_mode(portnp, &config.phy_mode);
+		if (err)
+			config.power_down = true;
+		config.media_type = ETH_MEDIA_DAC;
+		config.serdes_reset = true;
+		config.portmode = config.phy_mode;
+		err = sparx5_probe_port(sparx5, portnp, serdes, portno, &config);
+		if (err) {
+			dev_err(sparx5->dev, "port probe error\n");
+			goto cleanup_ports;
+		}
+	}
+	sparx5_board_init(sparx5);
+
+cleanup_ports:
+	return err;
+}
+
+static const struct of_device_id mchp_sparx5_match[] = {
+	{ .compatible = "microchip,sparx5-switch" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mchp_sparx5_match);
+
+static struct platform_driver mchp_sparx5_driver = {
+	.probe = mchp_sparx5_probe,
+	.driver = {
+		.name = "sparx5-switch",
+		.of_match_table = mchp_sparx5_match,
+	},
+};
+
+module_platform_driver(mchp_sparx5_driver);
+
+static inline u32 sparx5_read_gcb_soft_rst(struct regmap *gcb_ctrl)
+{
+	u32 val;
+
+	regmap_read(gcb_ctrl, spx5_offset(GCB_SOFT_RST), &val);
+	return val;
+}
+
+static int __init sparx5_switch_reset(void)
+{
+	const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
+		*syscon_gcb = "microchip,sparx5-gcb-syscon";
+	struct regmap *cpu_ctrl, *gcb_ctrl;
+	u32 val;
+
+	cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
+	if (IS_ERR(cpu_ctrl)) {
+		pr_err("No '%s' syscon map\n", syscon_cpu);
+		return PTR_ERR(cpu_ctrl);
+	}
+
+	gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
+	if (IS_ERR(gcb_ctrl)) {
+		pr_err("No '%s' syscon map\n", syscon_gcb);
+		return PTR_ERR(gcb_ctrl);
+	}
+
+	/* Make sure the core is PROTECTED from reset */
+	regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
+			   SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
+
+	regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
+		     GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
+
+	return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl, val,
+				  GCB_SOFT_RST_SOFT_SWC_RST_GET(val) == 0,
+				  1, 100);
+}
+postcore_initcall(sparx5_switch_reset);
+
+MODULE_DESCRIPTION("Microchip Sparx5 switch driver");
+MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
new file mode 100644
index 000000000000..887b4f80b2db
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#ifndef __SPARX5_MAIN_H__
+#define __SPARX5_MAIN_H__
+
+#include <linux/types.h>
+#include <linux/phy/phy.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/if_vlan.h>
+#include <linux/bitmap.h>
+#include <linux/phylink.h>
+
+#include "sparx5_main_regs.h"
+
+/* Target chip type */
+enum spx5_target_chiptype {
+	SPX5_TARGET_CT_7546    = 0x7546,  /* SparX-5-64  Enterprise */
+	SPX5_TARGET_CT_7549    = 0x7549,  /* SparX-5-90  Enterprise */
+	SPX5_TARGET_CT_7552    = 0x7552,  /* SparX-5-128 Enterprise */
+	SPX5_TARGET_CT_7556    = 0x7556,  /* SparX-5-160 Enterprise */
+	SPX5_TARGET_CT_7558    = 0x7558,  /* SparX-5-200 Enterprise */
+	SPX5_TARGET_CT_7546TSN = 0x47546, /* SparX-5-64i Industrial */
+	SPX5_TARGET_CT_7549TSN = 0x47549, /* SparX-5-90i Industrial */
+	SPX5_TARGET_CT_7552TSN = 0x47552, /* SparX-5-128i Industrial */
+	SPX5_TARGET_CT_7556TSN = 0x47556, /* SparX-5-160i Industrial */
+	SPX5_TARGET_CT_7558TSN = 0x47558, /* SparX-5-200i Industrial */
+};
+
+enum sparx5_port_max_tags {
+	SPX5_PORT_MAX_TAGS_NONE,  /* No extra tags allowed */
+	SPX5_PORT_MAX_TAGS_ONE,   /* Single tag allowed */
+	SPX5_PORT_MAX_TAGS_TWO    /* Single and double tag allowed */
+};
+
+enum sparx5_vlan_port_type {
+	SPX5_VLAN_PORT_TYPE_UNAWARE, /* VLAN unaware port */
+	SPX5_VLAN_PORT_TYPE_C,       /* C-port */
+	SPX5_VLAN_PORT_TYPE_S,       /* S-port */
+	SPX5_VLAN_PORT_TYPE_S_CUSTOM /* S-port using custom type */
+};
+
+#define SPX5_PORTS             65
+#define SPX5_PORT_CPU          (SPX5_PORTS)  /* Next port is CPU port */
+#define SPX5_PORT_CPU_0        (SPX5_PORT_CPU + 0) /* CPU Port 65 */
+#define SPX5_PORT_CPU_1        (SPX5_PORT_CPU + 1) /* CPU Port 66 */
+#define SPX5_PORT_VD0          (SPX5_PORT_CPU + 2) /* VD0/Port 67 used for IPMC */
+#define SPX5_PORT_VD1          (SPX5_PORT_CPU + 3) /* VD1/Port 68 used for AFI/OAM */
+#define SPX5_PORT_VD2          (SPX5_PORT_CPU + 4) /* VD2/Port 69 used for IPinIP*/
+#define SPX5_PORTS_ALL         (SPX5_PORT_CPU + 5) /* Total number of ports */
+
+#define PGID_BASE              SPX5_PORTS /* Starts after port PGIDs */
+#define PGID_UC_FLOOD          (PGID_BASE + 0)
+#define PGID_MC_FLOOD          (PGID_BASE + 1)
+#define PGID_IPV4_MC_DATA      (PGID_BASE + 2)
+#define PGID_IPV4_MC_CTRL      (PGID_BASE + 3)
+#define PGID_IPV6_MC_DATA      (PGID_BASE + 4)
+#define PGID_IPV6_MC_CTRL      (PGID_BASE + 5)
+#define PGID_BCAST	       (PGID_BASE + 6)
+#define PGID_CPU	       (PGID_BASE + 7)
+
+#define IFH_LEN                9 /* 36 bytes */
+#define NULL_VID               0
+#define SPX5_MACT_PULL_DELAY   (2 * HZ)
+#define SPX5_STATS_CHECK_DELAY (2 * HZ)
+#define SPX5_PRIOS             8     /* Number of priority queues */
+#define SPX5_BUFFER_CELL_SZ    184   /* Cell size  */
+#define SPX5_BUFFER_MEMORY     4194280 /* 22795 words * 184 bytes */
+
+struct sparx5;
+
+struct sparx5_port_config      {
+	phy_interface_t          portmode;
+	u32                      max_speed;
+	u32                      speed;
+	int                      duplex;
+	enum ethernet_media_type media_type;
+	bool                     power_down;
+	bool                     autoneg;
+	u32                      pause;
+	bool                     serdes_reset;
+	phy_interface_t          phy_mode;
+	u32                      sd_sgpio;
+};
+
+struct sparx5_port {
+	struct net_device          *ndev;
+	struct sparx5              *sparx5;
+	struct device_node         *of_node;
+	struct phy                 *serdes;
+	struct sparx5_port_config  conf;
+	u16                        portno;
+	/* Ingress default VLAN (pvid) */
+	u16                        pvid;
+	/* Egress default VLAN (vid) */
+	u16                        vid;
+	bool                       signd_internal;
+	bool                       signd_active_high;
+	bool                       signd_enable;
+	bool                       flow_control;
+	enum sparx5_port_max_tags  max_vlan_tags;
+	enum sparx5_vlan_port_type vlan_type;
+	u32                        custom_etype;
+	u32                        ifh[IFH_LEN];
+	bool                       vlan_aware;
+};
+
+enum sparx5_core_clockfreq {
+	SPX5_CORE_CLOCK_DEFAULT,  /* Defaults to the highest supported frequency */
+	SPX5_CORE_CLOCK_250MHZ,   /* 250MHZ core clock frequency */
+	SPX5_CORE_CLOCK_500MHZ,   /* 500MHZ core clock frequency */
+	SPX5_CORE_CLOCK_625MHZ,   /* 625MHZ core clock frequency */
+};
+
+struct sparx5 {
+	struct platform_device                *pdev;
+	struct device                         *dev;
+	u32                                   chip_id;
+	enum spx5_target_chiptype             target_ct;
+	void __iomem                          *regs[NUM_TARGETS];
+	int                                   port_count;
+	struct mutex                          lock;      /* MAC reg lock */
+	/* port structures are in net device */
+	struct sparx5_port                    *ports[SPX5_PORTS];
+	enum sparx5_core_clockfreq            coreclock;
+	u8                                    base_mac[ETH_ALEN];
+	/* Board specifics */
+	bool                                  sd_sgpio_remapping;
+};
+
+/* Clock period in picoseconds */
+static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
+{
+	switch (cclock) {
+	case SPX5_CORE_CLOCK_250MHZ:
+		return 4000;
+	case SPX5_CORE_CLOCK_500MHZ:
+		return 2000;
+	case SPX5_CORE_CLOCK_625MHZ:
+	default:
+		return 1600;
+	}
+}
+
+/* Calculate raw offset */
+static inline __pure int spx5_offset(int id, int tinst, int tcnt,
+				     int gbase, int ginst,
+				     int gcnt, int gwidth,
+				     int raddr, int rinst,
+				     int rcnt, int rwidth)
+{
+#if defined(CONFIG_DEBUG_KERNEL)
+	WARN_ON((tinst) >= tcnt);
+	WARN_ON((ginst) >= gcnt);
+	WARN_ON((rinst) >= rcnt);
+#endif
+
+	return gbase + ((ginst) * gwidth) +
+		raddr + ((rinst) * rwidth);
+}
+
+/* Read, Write and modify registers content.
+ * The register definition macros start at the id
+ */
+static inline void __iomem *spx5_addr(void __iomem *base[],
+				      int id, int tinst, int tcnt,
+				      int gbase, int ginst,
+				      int gcnt, int gwidth,
+				      int raddr, int rinst,
+				      int rcnt, int rwidth)
+{
+#if defined(CONFIG_DEBUG_KERNEL)
+	WARN_ON((tinst) >= tcnt);
+	WARN_ON((ginst) >= gcnt);
+	WARN_ON((rinst) >= rcnt);
+#endif
+	return base[id + (tinst)] +
+		gbase + ((ginst) * gwidth) +
+		raddr + ((rinst) * rwidth);
+}
+
+static inline void __iomem *spx5_inst_addr(void __iomem *base,
+					   int gbase, int ginst,
+					   int gcnt, int gwidth,
+					   int raddr, int rinst,
+					   int rcnt, int rwidth)
+{
+#if defined(CONFIG_DEBUG_KERNEL)
+	WARN_ON((ginst) >= gcnt);
+	WARN_ON((rinst) >= rcnt);
+#endif
+	return base +
+		gbase + ((ginst) * gwidth) +
+		raddr + ((rinst) * rwidth);
+}
+
+static inline u32 spx5_rd(struct sparx5 *sparx5, int id, int tinst, int tcnt,
+			  int gbase, int ginst, int gcnt, int gwidth,
+			  int raddr, int rinst, int rcnt, int rwidth)
+{
+	return readl(spx5_addr(sparx5->regs, id, tinst, tcnt, gbase, ginst,
+			       gcnt, gwidth, raddr, rinst, rcnt, rwidth));
+}
+
+static inline u32 spx5_inst_rd(void __iomem *iomem, int id, int tinst, int tcnt,
+			       int gbase, int ginst, int gcnt, int gwidth,
+			       int raddr, int rinst, int rcnt, int rwidth)
+{
+	return readl(spx5_inst_addr(iomem, gbase, ginst,
+				     gcnt, gwidth, raddr, rinst, rcnt, rwidth));
+}
+
+static inline void spx5_wr(u32 val, struct sparx5 *sparx5,
+			   int id, int tinst, int tcnt,
+			   int gbase, int ginst, int gcnt, int gwidth,
+			   int raddr, int rinst, int rcnt, int rwidth)
+{
+	writel(val, spx5_addr(sparx5->regs, id, tinst, tcnt,
+			      gbase, ginst, gcnt, gwidth,
+			      raddr, rinst, rcnt, rwidth));
+}
+
+static inline void spx5_inst_wr(u32 val, void __iomem *iomem,
+				int id, int tinst, int tcnt,
+				int gbase, int ginst, int gcnt, int gwidth,
+				int raddr, int rinst, int rcnt, int rwidth)
+{
+	writel(val, spx5_inst_addr(iomem,
+				   gbase, ginst, gcnt, gwidth,
+				   raddr, rinst, rcnt, rwidth));
+}
+
+static inline void spx5_rmw(u32 val, u32 mask, struct sparx5 *sparx5,
+			    int id, int tinst, int tcnt,
+			    int gbase, int ginst, int gcnt, int gwidth,
+			    int raddr, int rinst, int rcnt, int rwidth)
+{
+	u32 nval;
+	void __iomem *addr =
+		spx5_addr(sparx5->regs, id, tinst, tcnt,
+			  gbase, ginst, gcnt, gwidth,
+			  raddr, rinst, rcnt, rwidth);
+	nval = readl(addr);
+	nval = (nval & ~mask) | (val & mask);
+	writel(nval, addr);
+}
+
+static inline void spx5_inst_rmw(u32 val, u32 mask, void __iomem *iomem,
+				 int id, int tinst, int tcnt,
+				 int gbase, int ginst, int gcnt, int gwidth,
+				 int raddr, int rinst, int rcnt, int rwidth)
+{
+	u32 nval;
+	void __iomem *addr =
+		spx5_inst_addr(iomem,
+			       gbase, ginst, gcnt, gwidth,
+			       raddr, rinst, rcnt, rwidth);
+	nval = readl(addr);
+	nval = (nval & ~mask) | (val & mask);
+	writel(nval, addr);
+}
+
+static inline void __iomem *spx5_inst_get(struct sparx5 *sparx5, int id, int tinst)
+{
+	return sparx5->regs[id + tinst];
+}
+
+static inline void __iomem *spx5_reg_get(struct sparx5 *sparx5,
+					 int id, int tinst, int tcnt,
+					 int gbase, int ginst, int gcnt, int gwidth,
+					 int raddr, int rinst, int rcnt, int rwidth)
+{
+	return spx5_addr(sparx5->regs, id, tinst, tcnt,
+			 gbase, ginst, gcnt, gwidth,
+			 raddr, rinst, rcnt, rwidth);
+}
+
+#endif	/* __SPARX5_MAIN_H__ */
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
new file mode 100644
index 000000000000..cad13b8290ca
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -0,0 +1,3922 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ * Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc.
+ */
+
+/* This file is autogenerated by cml-utils 2020-11-19 10:41:34 +0100.
+ * Commit ID: f34790e69dc252103e2cc3e85b1a5e4d9e3aa190
+ */
+
+#ifndef _SPARX5_MAIN_REGS_H_
+#define _SPARX5_MAIN_REGS_H_
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+
+enum sparx5_target {
+	TARGET_ANA_AC = 1,
+	TARGET_ANA_ACL = 2,
+	TARGET_ANA_AC_POL = 4,
+	TARGET_ANA_CL = 6,
+	TARGET_ANA_L2 = 7,
+	TARGET_ANA_L3 = 8,
+	TARGET_ASM = 9,
+	TARGET_CLKGEN = 11,
+	TARGET_DEV10G = 17,
+	TARGET_DEV25G = 29,
+	TARGET_DEV2G5 = 37,
+	TARGET_DEV5G = 102,
+	TARGET_DSM = 115,
+	TARGET_EACL = 116,
+	TARGET_GCB = 118,
+	TARGET_HSCH = 119,
+	TARGET_LRN = 122,
+	TARGET_PCS10G_BR = 132,
+	TARGET_PCS25G_BR = 144,
+	TARGET_PCS5G_BR = 160,
+	TARGET_PORT_CONF = 173,
+	TARGET_QFWD = 175,
+	TARGET_QRES = 176,
+	TARGET_QS = 177,
+	TARGET_QSYS = 178,
+	TARGET_REW = 179,
+	TARGET_VCAP_SUPER = 326,
+	TARGET_VOP = 327,
+	TARGET_XQS = 331,
+	NUM_TARGETS = 332
+};
+
+#define __REG(...)    __VA_ARGS__
+
+/*      ANA_AC:RAM_CTRL:RAM_INIT */
+#define ANA_AC_RAM_INIT           __REG(TARGET_ANA_AC, 0, 1, 839108, 0, 1, 4, 0, 0, 1, 4)
+
+#define ANA_AC_RAM_INIT_RAM_INIT                 BIT(1)
+#define ANA_AC_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(ANA_AC_RAM_INIT_RAM_INIT, x)
+#define ANA_AC_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(ANA_AC_RAM_INIT_RAM_INIT, x)
+
+#define ANA_AC_RAM_INIT_RAM_CFG_HOOK             BIT(0)
+#define ANA_AC_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(ANA_AC_RAM_INIT_RAM_CFG_HOOK, x)
+#define ANA_AC_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(ANA_AC_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      ANA_AC:PS_COMMON:OWN_UPSID */
+#define ANA_AC_OWN_UPSID(r)       __REG(TARGET_ANA_AC, 0, 1, 894472, 0, 1, 352, 52, r, 3, 4)
+
+#define ANA_AC_OWN_UPSID_OWN_UPSID               GENMASK(4, 0)
+#define ANA_AC_OWN_UPSID_OWN_UPSID_SET(x)\
+	FIELD_PREP(ANA_AC_OWN_UPSID_OWN_UPSID, x)
+#define ANA_AC_OWN_UPSID_OWN_UPSID_GET(x)\
+	FIELD_GET(ANA_AC_OWN_UPSID_OWN_UPSID, x)
+
+/*      ANA_AC:SRC:SRC_CFG */
+#define ANA_AC_SRC_CFG(g)         __REG(TARGET_ANA_AC, 0, 1, 849920, g, 102, 16, 0, 0, 1, 4)
+
+/*      ANA_AC:SRC:SRC_CFG1 */
+#define ANA_AC_SRC_CFG1(g)        __REG(TARGET_ANA_AC, 0, 1, 849920, g, 102, 16, 4, 0, 1, 4)
+
+/*      ANA_AC:SRC:SRC_CFG2 */
+#define ANA_AC_SRC_CFG2(g)        __REG(TARGET_ANA_AC, 0, 1, 849920, g, 102, 16, 8, 0, 1, 4)
+
+#define ANA_AC_SRC_CFG2_PORT_MASK2               BIT(0)
+#define ANA_AC_SRC_CFG2_PORT_MASK2_SET(x)\
+	FIELD_PREP(ANA_AC_SRC_CFG2_PORT_MASK2, x)
+#define ANA_AC_SRC_CFG2_PORT_MASK2_GET(x)\
+	FIELD_GET(ANA_AC_SRC_CFG2_PORT_MASK2, x)
+
+/*      ANA_AC:PGID:PGID_CFG */
+#define ANA_AC_PGID_CFG(g)        __REG(TARGET_ANA_AC, 0, 1, 786432, g, 3290, 16, 0, 0, 1, 4)
+
+/*      ANA_AC:PGID:PGID_CFG1 */
+#define ANA_AC_PGID_CFG1(g)       __REG(TARGET_ANA_AC, 0, 1, 786432, g, 3290, 16, 4, 0, 1, 4)
+
+/*      ANA_AC:PGID:PGID_CFG2 */
+#define ANA_AC_PGID_CFG2(g)       __REG(TARGET_ANA_AC, 0, 1, 786432, g, 3290, 16, 8, 0, 1, 4)
+
+#define ANA_AC_PGID_CFG2_PORT_MASK2              BIT(0)
+#define ANA_AC_PGID_CFG2_PORT_MASK2_SET(x)\
+	FIELD_PREP(ANA_AC_PGID_CFG2_PORT_MASK2, x)
+#define ANA_AC_PGID_CFG2_PORT_MASK2_GET(x)\
+	FIELD_GET(ANA_AC_PGID_CFG2_PORT_MASK2, x)
+
+/*      ANA_AC:PGID:PGID_MISC_CFG */
+#define ANA_AC_PGID_MISC_CFG(g)   __REG(TARGET_ANA_AC, 0, 1, 786432, g, 3290, 16, 12, 0, 1, 4)
+
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_QU         GENMASK(6, 4)
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_QU_SET(x)\
+	FIELD_PREP(ANA_AC_PGID_MISC_CFG_PGID_CPU_QU, x)
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_QU_GET(x)\
+	FIELD_GET(ANA_AC_PGID_MISC_CFG_PGID_CPU_QU, x)
+
+#define ANA_AC_PGID_MISC_CFG_STACK_TYPE_ENA      BIT(1)
+#define ANA_AC_PGID_MISC_CFG_STACK_TYPE_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_PGID_MISC_CFG_STACK_TYPE_ENA, x)
+#define ANA_AC_PGID_MISC_CFG_STACK_TYPE_ENA_GET(x)\
+	FIELD_GET(ANA_AC_PGID_MISC_CFG_STACK_TYPE_ENA, x)
+
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA   BIT(0)
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, x)
+#define ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_GET(x)\
+	FIELD_GET(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, x)
+
+/*      ANA_AC:STAT_GLOBAL_CFG_PORT:STAT_RESET */
+#define ANA_AC_STAT_RESET         __REG(TARGET_ANA_AC, 0, 1, 851552, 0, 1, 20, 16, 0, 1, 4)
+
+#define ANA_AC_STAT_RESET_RESET                  BIT(0)
+#define ANA_AC_STAT_RESET_RESET_SET(x)\
+	FIELD_PREP(ANA_AC_STAT_RESET_RESET, x)
+#define ANA_AC_STAT_RESET_RESET_GET(x)\
+	FIELD_GET(ANA_AC_STAT_RESET_RESET, x)
+
+/*      ANA_AC:STAT_CNT_CFG_PORT:STAT_LSB_CNT */
+#define ANA_AC_PORT_STAT_LSB_CNT(g, r) __REG(TARGET_ANA_AC, 0, 1, 843776, g, 70, 64, 20, r, 4, 4)
+
+/*      ANA_ACL:COMMON:OWN_UPSID */
+#define ANA_ACL_OWN_UPSID(r)      __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 580, r, 3, 4)
+
+#define ANA_ACL_OWN_UPSID_OWN_UPSID              GENMASK(4, 0)
+#define ANA_ACL_OWN_UPSID_OWN_UPSID_SET(x)\
+	FIELD_PREP(ANA_ACL_OWN_UPSID_OWN_UPSID, x)
+#define ANA_ACL_OWN_UPSID_OWN_UPSID_GET(x)\
+	FIELD_GET(ANA_ACL_OWN_UPSID_OWN_UPSID, x)
+
+/*      ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */
+#define ANA_AC_POL_POL_UPD_INT_CFG __REG(TARGET_ANA_AC_POL, 0, 1, 75968, 0, 1, 1160, 1148, 0, 1, 4)
+
+#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT   GENMASK(9, 0)
+#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(x)\
+	FIELD_PREP(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
+#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
+	FIELD_GET(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
+
+/*      ANA_AC_POL:COMMON_BDLB:DLB_CTRL */
+#define ANA_AC_POL_BDLB_DLB_CTRL  __REG(TARGET_ANA_AC_POL, 0, 1, 79048, 0, 1, 8, 0, 0, 1, 4)
+
+#define ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS GENMASK(26, 19)
+#define ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS_SET(x)\
+	FIELD_PREP(ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS, x)
+#define ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS_GET(x)\
+	FIELD_GET(ANA_AC_POL_BDLB_DLB_CTRL_CLK_PERIOD_01NS, x)
+
+#define ANA_AC_POL_BDLB_DLB_CTRL_BASE_TICK_CNT   GENMASK(18, 4)
+#define ANA_AC_POL_BDLB_DLB_CTRL_BASE_TICK_CNT_SET(x)\
+	FIELD_PREP(ANA_AC_POL_BDLB_DLB_CTRL_BASE_TICK_CNT, x)
+#define ANA_AC_POL_BDLB_DLB_CTRL_BASE_TICK_CNT_GET(x)\
+	FIELD_GET(ANA_AC_POL_BDLB_DLB_CTRL_BASE_TICK_CNT, x)
+
+#define ANA_AC_POL_BDLB_DLB_CTRL_LEAK_ENA        BIT(1)
+#define ANA_AC_POL_BDLB_DLB_CTRL_LEAK_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_POL_BDLB_DLB_CTRL_LEAK_ENA, x)
+#define ANA_AC_POL_BDLB_DLB_CTRL_LEAK_ENA_GET(x)\
+	FIELD_GET(ANA_AC_POL_BDLB_DLB_CTRL_LEAK_ENA, x)
+
+#define ANA_AC_POL_BDLB_DLB_CTRL_DLB_ADD_ENA     BIT(0)
+#define ANA_AC_POL_BDLB_DLB_CTRL_DLB_ADD_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_POL_BDLB_DLB_CTRL_DLB_ADD_ENA, x)
+#define ANA_AC_POL_BDLB_DLB_CTRL_DLB_ADD_ENA_GET(x)\
+	FIELD_GET(ANA_AC_POL_BDLB_DLB_CTRL_DLB_ADD_ENA, x)
+
+/*      ANA_AC_POL:COMMON_BUM_SLB:DLB_CTRL */
+#define ANA_AC_POL_SLB_DLB_CTRL   __REG(TARGET_ANA_AC_POL, 0, 1, 79056, 0, 1, 20, 0, 0, 1, 4)
+
+#define ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS  GENMASK(26, 19)
+#define ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS_SET(x)\
+	FIELD_PREP(ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS, x)
+#define ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS_GET(x)\
+	FIELD_GET(ANA_AC_POL_SLB_DLB_CTRL_CLK_PERIOD_01NS, x)
+
+#define ANA_AC_POL_SLB_DLB_CTRL_BASE_TICK_CNT    GENMASK(18, 4)
+#define ANA_AC_POL_SLB_DLB_CTRL_BASE_TICK_CNT_SET(x)\
+	FIELD_PREP(ANA_AC_POL_SLB_DLB_CTRL_BASE_TICK_CNT, x)
+#define ANA_AC_POL_SLB_DLB_CTRL_BASE_TICK_CNT_GET(x)\
+	FIELD_GET(ANA_AC_POL_SLB_DLB_CTRL_BASE_TICK_CNT, x)
+
+#define ANA_AC_POL_SLB_DLB_CTRL_LEAK_ENA         BIT(1)
+#define ANA_AC_POL_SLB_DLB_CTRL_LEAK_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_POL_SLB_DLB_CTRL_LEAK_ENA, x)
+#define ANA_AC_POL_SLB_DLB_CTRL_LEAK_ENA_GET(x)\
+	FIELD_GET(ANA_AC_POL_SLB_DLB_CTRL_LEAK_ENA, x)
+
+#define ANA_AC_POL_SLB_DLB_CTRL_DLB_ADD_ENA      BIT(0)
+#define ANA_AC_POL_SLB_DLB_CTRL_DLB_ADD_ENA_SET(x)\
+	FIELD_PREP(ANA_AC_POL_SLB_DLB_CTRL_DLB_ADD_ENA, x)
+#define ANA_AC_POL_SLB_DLB_CTRL_DLB_ADD_ENA_GET(x)\
+	FIELD_GET(ANA_AC_POL_SLB_DLB_CTRL_DLB_ADD_ENA, x)
+
+/*      ANA_CL:PORT:FILTER_CTRL */
+#define ANA_CL_FILTER_CTRL(g)     __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 4, 0, 1, 4)
+
+#define ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS    BIT(2)
+#define ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS, x)
+#define ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS_GET(x)\
+	FIELD_GET(ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS, x)
+
+#define ANA_CL_FILTER_CTRL_FILTER_NULL_MAC_DIS   BIT(1)
+#define ANA_CL_FILTER_CTRL_FILTER_NULL_MAC_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_FILTER_CTRL_FILTER_NULL_MAC_DIS, x)
+#define ANA_CL_FILTER_CTRL_FILTER_NULL_MAC_DIS_GET(x)\
+	FIELD_GET(ANA_CL_FILTER_CTRL_FILTER_NULL_MAC_DIS, x)
+
+#define ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA  BIT(0)
+#define ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA_SET(x)\
+	FIELD_PREP(ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA, x)
+#define ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA_GET(x)\
+	FIELD_GET(ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA, x)
+
+/*      ANA_CL:PORT:VLAN_FILTER_CTRL */
+#define ANA_CL_VLAN_FILTER_CTRL(g, r) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 8, r, 3, 4)
+
+#define ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA BIT(10)
+#define ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA, x)
+#define ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS    BIT(9)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_CTAG_DIS         BIT(8)
+#define ANA_CL_VLAN_FILTER_CTRL_CTAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_CTAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_CTAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_CTAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS    BIT(7)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST1_STAG_DIS BIT(6)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST1_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST1_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST1_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST1_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST2_STAG_DIS BIT(5)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST2_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST2_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST2_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST2_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST3_STAG_DIS BIT(4)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST3_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST3_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST3_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_PRIO_CUST3_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_STAG_DIS         BIT(3)
+#define ANA_CL_VLAN_FILTER_CTRL_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_CUST1_STAG_DIS   BIT(2)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST1_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_CUST1_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST1_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_CUST1_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_CUST2_STAG_DIS   BIT(1)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST2_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_CUST2_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST2_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_CUST2_STAG_DIS, x)
+
+#define ANA_CL_VLAN_FILTER_CTRL_CUST3_STAG_DIS   BIT(0)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST3_STAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_FILTER_CTRL_CUST3_STAG_DIS, x)
+#define ANA_CL_VLAN_FILTER_CTRL_CUST3_STAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_FILTER_CTRL_CUST3_STAG_DIS, x)
+
+/*      ANA_CL:PORT:ETAG_FILTER_CTRL */
+#define ANA_CL_ETAG_FILTER_CTRL(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 20, 0, 1, 4)
+
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_REQUIRED_ENA BIT(1)
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_REQUIRED_ENA_SET(x)\
+	FIELD_PREP(ANA_CL_ETAG_FILTER_CTRL_ETAG_REQUIRED_ENA, x)
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_REQUIRED_ENA_GET(x)\
+	FIELD_GET(ANA_CL_ETAG_FILTER_CTRL_ETAG_REQUIRED_ENA, x)
+
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_DIS         BIT(0)
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_ETAG_FILTER_CTRL_ETAG_DIS, x)
+#define ANA_CL_ETAG_FILTER_CTRL_ETAG_DIS_GET(x)\
+	FIELD_GET(ANA_CL_ETAG_FILTER_CTRL_ETAG_DIS, x)
+
+/*      ANA_CL:PORT:VLAN_CTRL */
+#define ANA_CL_VLAN_CTRL(g)       __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 32, 0, 1, 4)
+
+#define ANA_CL_VLAN_CTRL_PORT_VOE_TPID_AWARE_DIS GENMASK(30, 26)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_TPID_AWARE_DIS_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_VOE_TPID_AWARE_DIS, x)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_TPID_AWARE_DIS_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_VOE_TPID_AWARE_DIS, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_PCP    GENMASK(25, 23)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_PCP_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_PCP, x)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_PCP_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_PCP, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_DEI    BIT(22)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_DEI_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_DEI, x)
+#define ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_DEI_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_VOE_DEFAULT_DEI, x)
+
+#define ANA_CL_VLAN_CTRL_VLAN_PCP_DEI_TRANS_ENA  BIT(21)
+#define ANA_CL_VLAN_CTRL_VLAN_PCP_DEI_TRANS_ENA_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_VLAN_PCP_DEI_TRANS_ENA, x)
+#define ANA_CL_VLAN_CTRL_VLAN_PCP_DEI_TRANS_ENA_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_VLAN_PCP_DEI_TRANS_ENA, x)
+
+#define ANA_CL_VLAN_CTRL_VLAN_TAG_SEL            BIT(20)
+#define ANA_CL_VLAN_CTRL_VLAN_TAG_SEL_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_VLAN_TAG_SEL, x)
+#define ANA_CL_VLAN_CTRL_VLAN_TAG_SEL_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_VLAN_TAG_SEL, x)
+
+#define ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA          BIT(19)
+#define ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA, x)
+#define ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA, x)
+
+#define ANA_CL_VLAN_CTRL_VLAN_POP_CNT            GENMASK(18, 17)
+#define ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_VLAN_POP_CNT, x)
+#define ANA_CL_VLAN_CTRL_VLAN_POP_CNT_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_VLAN_POP_CNT, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_TAG_TYPE           BIT(16)
+#define ANA_CL_VLAN_CTRL_PORT_TAG_TYPE_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_TAG_TYPE, x)
+#define ANA_CL_VLAN_CTRL_PORT_TAG_TYPE_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_TAG_TYPE, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_PCP                GENMASK(15, 13)
+#define ANA_CL_VLAN_CTRL_PORT_PCP_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_PCP, x)
+#define ANA_CL_VLAN_CTRL_PORT_PCP_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_PCP, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_DEI                BIT(12)
+#define ANA_CL_VLAN_CTRL_PORT_DEI_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_DEI, x)
+#define ANA_CL_VLAN_CTRL_PORT_DEI_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_DEI, x)
+
+#define ANA_CL_VLAN_CTRL_PORT_VID                GENMASK(11, 0)
+#define ANA_CL_VLAN_CTRL_PORT_VID_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_PORT_VID, x)
+#define ANA_CL_VLAN_CTRL_PORT_VID_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_PORT_VID, x)
+
+/*      ANA_CL:PORT:VLAN_CTRL_2 */
+#define ANA_CL_VLAN_CTRL_2(g)     __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 36, 0, 1, 4)
+
+#define ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT         GENMASK(1, 0)
+#define ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT_SET(x)\
+	FIELD_PREP(ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT, x)
+#define ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT_GET(x)\
+	FIELD_GET(ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT, x)
+
+/*      ANA_CL:PORT:CAPTURE_BPDU_CFG */
+#define ANA_CL_CAPTURE_BPDU_CFG(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 196, 0, 1, 4)
+
+/*      ANA_CL:COMMON:OWN_UPSID */
+#define ANA_CL_OWN_UPSID(r)       __REG(TARGET_ANA_CL, 0, 1, 166912, 0, 1, 756, 0, r, 3, 4)
+
+#define ANA_CL_OWN_UPSID_OWN_UPSID               GENMASK(4, 0)
+#define ANA_CL_OWN_UPSID_OWN_UPSID_SET(x)\
+	FIELD_PREP(ANA_CL_OWN_UPSID_OWN_UPSID, x)
+#define ANA_CL_OWN_UPSID_OWN_UPSID_GET(x)\
+	FIELD_GET(ANA_CL_OWN_UPSID_OWN_UPSID, x)
+
+/*      ANA_L2:COMMON:AUTO_LRN_CFG */
+#define ANA_L2_AUTO_LRN_CFG       __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 24, 0, 1, 4)
+
+/*      ANA_L2:COMMON:AUTO_LRN_CFG1 */
+#define ANA_L2_AUTO_LRN_CFG1      __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 28, 0, 1, 4)
+
+/*      ANA_L2:COMMON:AUTO_LRN_CFG2 */
+#define ANA_L2_AUTO_LRN_CFG2      __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 32, 0, 1, 4)
+
+#define ANA_L2_AUTO_LRN_CFG2_AUTO_LRN_ENA2       BIT(0)
+#define ANA_L2_AUTO_LRN_CFG2_AUTO_LRN_ENA2_SET(x)\
+	FIELD_PREP(ANA_L2_AUTO_LRN_CFG2_AUTO_LRN_ENA2, x)
+#define ANA_L2_AUTO_LRN_CFG2_AUTO_LRN_ENA2_GET(x)\
+	FIELD_GET(ANA_L2_AUTO_LRN_CFG2_AUTO_LRN_ENA2, x)
+
+/*      ANA_L2:COMMON:OWN_UPSID */
+#define ANA_L2_OWN_UPSID(r)       __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 672, r, 3, 4)
+
+#define ANA_L2_OWN_UPSID_OWN_UPSID               GENMASK(4, 0)
+#define ANA_L2_OWN_UPSID_OWN_UPSID_SET(x)\
+	FIELD_PREP(ANA_L2_OWN_UPSID_OWN_UPSID, x)
+#define ANA_L2_OWN_UPSID_OWN_UPSID_GET(x)\
+	FIELD_GET(ANA_L2_OWN_UPSID_OWN_UPSID, x)
+
+/*      ANA_L3:COMMON:VLAN_CTRL */
+#define ANA_L3_VLAN_CTRL          __REG(TARGET_ANA_L3, 0, 1, 493632, 0, 1, 184, 4, 0, 1, 4)
+
+#define ANA_L3_VLAN_CTRL_VLAN_ENA                BIT(0)
+#define ANA_L3_VLAN_CTRL_VLAN_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CTRL_VLAN_ENA, x)
+#define ANA_L3_VLAN_CTRL_VLAN_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CTRL_VLAN_ENA, x)
+
+/*      ANA_L3:VLAN:VLAN_CFG */
+#define ANA_L3_VLAN_CFG(g)        __REG(TARGET_ANA_L3, 0, 1, 0, g, 5120, 64, 8, 0, 1, 4)
+
+#define ANA_L3_VLAN_CFG_VLAN_MSTP_PTR            GENMASK(30, 24)
+#define ANA_L3_VLAN_CFG_VLAN_MSTP_PTR_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_MSTP_PTR, x)
+#define ANA_L3_VLAN_CFG_VLAN_MSTP_PTR_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_MSTP_PTR, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_FID                 GENMASK(20, 8)
+#define ANA_L3_VLAN_CFG_VLAN_FID_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_FID, x)
+#define ANA_L3_VLAN_CFG_VLAN_FID_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_FID, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_IGR_FILTER_ENA      BIT(6)
+#define ANA_L3_VLAN_CFG_VLAN_IGR_FILTER_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_IGR_FILTER_ENA, x)
+#define ANA_L3_VLAN_CFG_VLAN_IGR_FILTER_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_IGR_FILTER_ENA, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_SEC_FWD_ENA         BIT(5)
+#define ANA_L3_VLAN_CFG_VLAN_SEC_FWD_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_SEC_FWD_ENA, x)
+#define ANA_L3_VLAN_CFG_VLAN_SEC_FWD_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_SEC_FWD_ENA, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_FLOOD_DIS           BIT(4)
+#define ANA_L3_VLAN_CFG_VLAN_FLOOD_DIS_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_FLOOD_DIS, x)
+#define ANA_L3_VLAN_CFG_VLAN_FLOOD_DIS_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_FLOOD_DIS, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_LRN_DIS             BIT(3)
+#define ANA_L3_VLAN_CFG_VLAN_LRN_DIS_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_LRN_DIS, x)
+#define ANA_L3_VLAN_CFG_VLAN_LRN_DIS_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_LRN_DIS, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_RLEG_ENA            BIT(2)
+#define ANA_L3_VLAN_CFG_VLAN_RLEG_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_RLEG_ENA, x)
+#define ANA_L3_VLAN_CFG_VLAN_RLEG_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_RLEG_ENA, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_PRIVATE_ENA         BIT(1)
+#define ANA_L3_VLAN_CFG_VLAN_PRIVATE_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_PRIVATE_ENA, x)
+#define ANA_L3_VLAN_CFG_VLAN_PRIVATE_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_PRIVATE_ENA, x)
+
+#define ANA_L3_VLAN_CFG_VLAN_MIRROR_ENA          BIT(0)
+#define ANA_L3_VLAN_CFG_VLAN_MIRROR_ENA_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_CFG_VLAN_MIRROR_ENA, x)
+#define ANA_L3_VLAN_CFG_VLAN_MIRROR_ENA_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_CFG_VLAN_MIRROR_ENA, x)
+
+/*      ANA_L3:VLAN:VLAN_MASK_CFG */
+#define ANA_L3_VLAN_MASK_CFG(g)   __REG(TARGET_ANA_L3, 0, 1, 0, g, 5120, 64, 16, 0, 1, 4)
+
+/*      ANA_L3:VLAN:VLAN_MASK_CFG1 */
+#define ANA_L3_VLAN_MASK_CFG1(g)  __REG(TARGET_ANA_L3, 0, 1, 0, g, 5120, 64, 20, 0, 1, 4)
+
+/*      ANA_L3:VLAN:VLAN_MASK_CFG2 */
+#define ANA_L3_VLAN_MASK_CFG2(g)  __REG(TARGET_ANA_L3, 0, 1, 0, g, 5120, 64, 24, 0, 1, 4)
+
+#define ANA_L3_VLAN_MASK_CFG2_VLAN_PORT_MASK2    BIT(0)
+#define ANA_L3_VLAN_MASK_CFG2_VLAN_PORT_MASK2_SET(x)\
+	FIELD_PREP(ANA_L3_VLAN_MASK_CFG2_VLAN_PORT_MASK2, x)
+#define ANA_L3_VLAN_MASK_CFG2_VLAN_PORT_MASK2_GET(x)\
+	FIELD_GET(ANA_L3_VLAN_MASK_CFG2_VLAN_PORT_MASK2, x)
+
+/*      ASM:DEV_STATISTICS:RX_IN_BYTES_CNT */
+#define ASM_RX_IN_BYTES_CNT(g)    __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 0, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SYMBOL_ERR_CNT */
+#define ASM_RX_SYMBOL_ERR_CNT(g)  __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 4, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_PAUSE_CNT */
+#define ASM_RX_PAUSE_CNT(g)       __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 8, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_OK_BYTES_CNT */
+#define ASM_RX_OK_BYTES_CNT(g)    __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 16, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_BAD_BYTES_CNT */
+#define ASM_RX_BAD_BYTES_CNT(g)   __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 20, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_UC_CNT */
+#define ASM_RX_UC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 24, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_MC_CNT */
+#define ASM_RX_MC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 28, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_BC_CNT */
+#define ASM_RX_BC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 32, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_CRC_ERR_CNT */
+#define ASM_RX_CRC_ERR_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 36, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_UNDERSIZE_CNT */
+#define ASM_RX_UNDERSIZE_CNT(g)   __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 40, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_FRAGMENTS_CNT */
+#define ASM_RX_FRAGMENTS_CNT(g)   __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 44, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_IN_RANGE_LEN_ERR_CNT */
+#define ASM_RX_IN_RANGE_LEN_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 48, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_OUT_OF_RANGE_LEN_ERR_CNT */
+#define ASM_RX_OUT_OF_RANGE_LEN_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 52, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_OVERSIZE_CNT */
+#define ASM_RX_OVERSIZE_CNT(g)    __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 56, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_JABBERS_CNT */
+#define ASM_RX_JABBERS_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 60, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE64_CNT */
+#define ASM_RX_SIZE64_CNT(g)      __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 64, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE65TO127_CNT */
+#define ASM_RX_SIZE65TO127_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 68, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE128TO255_CNT */
+#define ASM_RX_SIZE128TO255_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 72, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE256TO511_CNT */
+#define ASM_RX_SIZE256TO511_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 76, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE512TO1023_CNT */
+#define ASM_RX_SIZE512TO1023_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 80, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE1024TO1518_CNT */
+#define ASM_RX_SIZE1024TO1518_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 84, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:RX_SIZE1519TOMAX_CNT */
+#define ASM_RX_SIZE1519TOMAX_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 88, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_OUT_BYTES_CNT */
+#define ASM_TX_OUT_BYTES_CNT(g)   __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 96, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_PAUSE_CNT */
+#define ASM_TX_PAUSE_CNT(g)       __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 100, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_OK_BYTES_CNT */
+#define ASM_TX_OK_BYTES_CNT(g)    __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 104, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_UC_CNT */
+#define ASM_TX_UC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 108, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_MC_CNT */
+#define ASM_TX_MC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 112, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_BC_CNT */
+#define ASM_TX_BC_CNT(g)          __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 116, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE64_CNT */
+#define ASM_TX_SIZE64_CNT(g)      __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 120, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE65TO127_CNT */
+#define ASM_TX_SIZE65TO127_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 124, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE128TO255_CNT */
+#define ASM_TX_SIZE128TO255_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 128, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE256TO511_CNT */
+#define ASM_TX_SIZE256TO511_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 132, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE512TO1023_CNT */
+#define ASM_TX_SIZE512TO1023_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 136, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE1024TO1518_CNT */
+#define ASM_TX_SIZE1024TO1518_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 140, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_SIZE1519TOMAX_CNT */
+#define ASM_TX_SIZE1519TOMAX_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 144, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SYMBOL_ERR_CNT */
+#define ASM_PMAC_RX_SYMBOL_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 168, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_PAUSE_CNT */
+#define ASM_PMAC_RX_PAUSE_CNT(g)  __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 172, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_UNSUP_OPCODE_CNT */
+#define ASM_PMAC_RX_UNSUP_OPCODE_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 176, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_OK_BYTES_CNT */
+#define ASM_PMAC_RX_OK_BYTES_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 180, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_BAD_BYTES_CNT */
+#define ASM_PMAC_RX_BAD_BYTES_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 184, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_UC_CNT */
+#define ASM_PMAC_RX_UC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 188, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_MC_CNT */
+#define ASM_PMAC_RX_MC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 192, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_BC_CNT */
+#define ASM_PMAC_RX_BC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 196, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_CRC_ERR_CNT */
+#define ASM_PMAC_RX_CRC_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 200, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_UNDERSIZE_CNT */
+#define ASM_PMAC_RX_UNDERSIZE_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 204, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_FRAGMENTS_CNT */
+#define ASM_PMAC_RX_FRAGMENTS_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 208, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_IN_RANGE_LEN_ERR_CNT */
+#define ASM_PMAC_RX_IN_RANGE_LEN_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 212, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT */
+#define ASM_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 216, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_OVERSIZE_CNT */
+#define ASM_PMAC_RX_OVERSIZE_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 220, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_JABBERS_CNT */
+#define ASM_PMAC_RX_JABBERS_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 224, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE64_CNT */
+#define ASM_PMAC_RX_SIZE64_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 228, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE65TO127_CNT */
+#define ASM_PMAC_RX_SIZE65TO127_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 232, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE128TO255_CNT */
+#define ASM_PMAC_RX_SIZE128TO255_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 236, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE256TO511_CNT */
+#define ASM_PMAC_RX_SIZE256TO511_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 240, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE512TO1023_CNT */
+#define ASM_PMAC_RX_SIZE512TO1023_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 244, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE1024TO1518_CNT */
+#define ASM_PMAC_RX_SIZE1024TO1518_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 248, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_RX_SIZE1519TOMAX_CNT */
+#define ASM_PMAC_RX_SIZE1519TOMAX_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 252, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_PAUSE_CNT */
+#define ASM_PMAC_TX_PAUSE_CNT(g)  __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 256, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_OK_BYTES_CNT */
+#define ASM_PMAC_TX_OK_BYTES_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 260, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_UC_CNT */
+#define ASM_PMAC_TX_UC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 264, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_MC_CNT */
+#define ASM_PMAC_TX_MC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 268, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_BC_CNT */
+#define ASM_PMAC_TX_BC_CNT(g)     __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 272, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE64_CNT */
+#define ASM_PMAC_TX_SIZE64_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 276, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE65TO127_CNT */
+#define ASM_PMAC_TX_SIZE65TO127_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 280, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE128TO255_CNT */
+#define ASM_PMAC_TX_SIZE128TO255_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 284, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE256TO511_CNT */
+#define ASM_PMAC_TX_SIZE256TO511_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 288, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE512TO1023_CNT */
+#define ASM_PMAC_TX_SIZE512TO1023_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 292, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE1024TO1518_CNT */
+#define ASM_PMAC_TX_SIZE1024TO1518_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 296, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:PMAC_TX_SIZE1519TOMAX_CNT */
+#define ASM_PMAC_TX_SIZE1519TOMAX_CNT(g) __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 300, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_MULTI_COLL_CNT */
+#define ASM_TX_MULTI_COLL_CNT(g)  __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 328, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_LATE_COLL_CNT */
+#define ASM_TX_LATE_COLL_CNT(g)   __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 332, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_XCOLL_CNT */
+#define ASM_TX_XCOLL_CNT(g)       __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 336, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_DEFER_CNT */
+#define ASM_TX_DEFER_CNT(g)       __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 340, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_XDEFER_CNT */
+#define ASM_TX_XDEFER_CNT(g)      __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 344, 0, 1, 4)
+
+/*      ASM:DEV_STATISTICS:TX_BACKOFF1_CNT */
+#define ASM_TX_BACKOFF1_CNT(g)    __REG(TARGET_ASM, 0, 1, 0, g, 65, 512, 348, 0, 1, 4)
+
+/*      ASM:CFG:STAT_CFG */
+#define ASM_STAT_CFG              __REG(TARGET_ASM, 0, 1, 33280, 0, 1, 1088, 0, 0, 1, 4)
+
+#define ASM_STAT_CFG_STAT_CNT_CLR_SHOT           BIT(0)
+#define ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(x)\
+	FIELD_PREP(ASM_STAT_CFG_STAT_CNT_CLR_SHOT, x)
+#define ASM_STAT_CFG_STAT_CNT_CLR_SHOT_GET(x)\
+	FIELD_GET(ASM_STAT_CFG_STAT_CNT_CLR_SHOT, x)
+
+/*      ASM:CFG:PORT_CFG */
+#define ASM_PORT_CFG(r)           __REG(TARGET_ASM, 0, 1, 33280, 0, 1, 1088, 540, r, 67, 4)
+
+#define ASM_PORT_CFG_CSC_STAT_DIS                BIT(12)
+#define ASM_PORT_CFG_CSC_STAT_DIS_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_CSC_STAT_DIS, x)
+#define ASM_PORT_CFG_CSC_STAT_DIS_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_CSC_STAT_DIS, x)
+
+#define ASM_PORT_CFG_HIH_AFTER_PREAMBLE_ENA      BIT(11)
+#define ASM_PORT_CFG_HIH_AFTER_PREAMBLE_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_HIH_AFTER_PREAMBLE_ENA, x)
+#define ASM_PORT_CFG_HIH_AFTER_PREAMBLE_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_HIH_AFTER_PREAMBLE_ENA, x)
+
+#define ASM_PORT_CFG_IGN_TAXI_ABORT_ENA          BIT(10)
+#define ASM_PORT_CFG_IGN_TAXI_ABORT_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_IGN_TAXI_ABORT_ENA, x)
+#define ASM_PORT_CFG_IGN_TAXI_ABORT_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_IGN_TAXI_ABORT_ENA, x)
+
+#define ASM_PORT_CFG_NO_PREAMBLE_ENA             BIT(9)
+#define ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_NO_PREAMBLE_ENA, x)
+#define ASM_PORT_CFG_NO_PREAMBLE_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_NO_PREAMBLE_ENA, x)
+
+#define ASM_PORT_CFG_SKIP_PREAMBLE_ENA           BIT(8)
+#define ASM_PORT_CFG_SKIP_PREAMBLE_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_SKIP_PREAMBLE_ENA, x)
+#define ASM_PORT_CFG_SKIP_PREAMBLE_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_SKIP_PREAMBLE_ENA, x)
+
+#define ASM_PORT_CFG_FRM_AGING_DIS               BIT(7)
+#define ASM_PORT_CFG_FRM_AGING_DIS_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_FRM_AGING_DIS, x)
+#define ASM_PORT_CFG_FRM_AGING_DIS_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_FRM_AGING_DIS, x)
+
+#define ASM_PORT_CFG_PAD_ENA                     BIT(6)
+#define ASM_PORT_CFG_PAD_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_PAD_ENA, x)
+#define ASM_PORT_CFG_PAD_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_PAD_ENA, x)
+
+#define ASM_PORT_CFG_INJ_DISCARD_CFG             GENMASK(5, 4)
+#define ASM_PORT_CFG_INJ_DISCARD_CFG_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_INJ_DISCARD_CFG, x)
+#define ASM_PORT_CFG_INJ_DISCARD_CFG_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_INJ_DISCARD_CFG, x)
+
+#define ASM_PORT_CFG_INJ_FORMAT_CFG              GENMASK(3, 2)
+#define ASM_PORT_CFG_INJ_FORMAT_CFG_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_INJ_FORMAT_CFG, x)
+#define ASM_PORT_CFG_INJ_FORMAT_CFG_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_INJ_FORMAT_CFG, x)
+
+#define ASM_PORT_CFG_VSTAX2_AWR_ENA              BIT(1)
+#define ASM_PORT_CFG_VSTAX2_AWR_ENA_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_VSTAX2_AWR_ENA, x)
+#define ASM_PORT_CFG_VSTAX2_AWR_ENA_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_VSTAX2_AWR_ENA, x)
+
+#define ASM_PORT_CFG_PFRM_FLUSH                  BIT(0)
+#define ASM_PORT_CFG_PFRM_FLUSH_SET(x)\
+	FIELD_PREP(ASM_PORT_CFG_PFRM_FLUSH, x)
+#define ASM_PORT_CFG_PFRM_FLUSH_GET(x)\
+	FIELD_GET(ASM_PORT_CFG_PFRM_FLUSH, x)
+
+/*      ASM:RAM_CTRL:RAM_INIT */
+#define ASM_RAM_INIT              __REG(TARGET_ASM, 0, 1, 34832, 0, 1, 4, 0, 0, 1, 4)
+
+#define ASM_RAM_INIT_RAM_INIT                    BIT(1)
+#define ASM_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(ASM_RAM_INIT_RAM_INIT, x)
+#define ASM_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(ASM_RAM_INIT_RAM_INIT, x)
+
+#define ASM_RAM_INIT_RAM_CFG_HOOK                BIT(0)
+#define ASM_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(ASM_RAM_INIT_RAM_CFG_HOOK, x)
+#define ASM_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(ASM_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      CLKGEN:LCPLL1:LCPLL1_CORE_CLK_CFG */
+#define CLKGEN_LCPLL1_CORE_CLK_CFG __REG(TARGET_CLKGEN, 0, 1, 12, 0, 1, 36, 0, 0, 1, 4)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV  GENMASK(7, 0)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV, x)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV  GENMASK(10, 8)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV, x)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR  BIT(11)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR, x)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL  GENMASK(13, 12)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL, x)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA  BIT(14)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA, x)
+
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA  BIT(15)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_SET(x)\
+	FIELD_PREP(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA, x)
+#define CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_GET(x)\
+	FIELD_GET(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ENA_CFG */
+#define DEV10G_MAC_ENA_CFG(t)     __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 0, 0, 1, 4)
+
+#define DEV10G_MAC_ENA_CFG_RX_ENA                BIT(4)
+#define DEV10G_MAC_ENA_CFG_RX_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ENA_CFG_RX_ENA, x)
+#define DEV10G_MAC_ENA_CFG_RX_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ENA_CFG_RX_ENA, x)
+
+#define DEV10G_MAC_ENA_CFG_TX_ENA                BIT(0)
+#define DEV10G_MAC_ENA_CFG_TX_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ENA_CFG_TX_ENA, x)
+#define DEV10G_MAC_ENA_CFG_TX_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ENA_CFG_TX_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_MAXLEN_CFG */
+#define DEV10G_MAC_MAXLEN_CFG(t)  __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 8, 0, 1, 4)
+
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK    BIT(16)
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_SET(x)\
+	FIELD_PREP(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_GET(x)\
+	FIELD_GET(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN            GENMASK(15, 0)
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN_SET(x)\
+	FIELD_PREP(DEV10G_MAC_MAXLEN_CFG_MAX_LEN, x)
+#define DEV10G_MAC_MAXLEN_CFG_MAX_LEN_GET(x)\
+	FIELD_GET(DEV10G_MAC_MAXLEN_CFG_MAX_LEN, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_NUM_TAGS_CFG */
+#define DEV10G_MAC_NUM_TAGS_CFG(t) __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 12, 0, 1, 4)
+
+#define DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS         GENMASK(1, 0)
+#define DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS_SET(x)\
+	FIELD_PREP(DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS, x)
+#define DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS_GET(x)\
+	FIELD_GET(DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_TAGS_CFG */
+#define DEV10G_MAC_TAGS_CFG(t, r) __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 16, r, 3, 4)
+
+#define DEV10G_MAC_TAGS_CFG_TAG_ID               GENMASK(31, 16)
+#define DEV10G_MAC_TAGS_CFG_TAG_ID_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TAGS_CFG_TAG_ID, x)
+#define DEV10G_MAC_TAGS_CFG_TAG_ID_GET(x)\
+	FIELD_GET(DEV10G_MAC_TAGS_CFG_TAG_ID, x)
+
+#define DEV10G_MAC_TAGS_CFG_TAG_ENA              BIT(4)
+#define DEV10G_MAC_TAGS_CFG_TAG_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TAGS_CFG_TAG_ENA, x)
+#define DEV10G_MAC_TAGS_CFG_TAG_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_TAGS_CFG_TAG_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ADV_CHK_CFG */
+#define DEV10G_MAC_ADV_CHK_CFG(t) __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 28, 0, 1, 4)
+
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA   BIT(24)
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA   BIT(20)
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_SFD_CHK_ENA       BIT(16)
+#define DEV10G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS   BIT(12)
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_CHK_ENA       BIT(8)
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_OOR_ERR_ENA       BIT(4)
+#define DEV10G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+
+#define DEV10G_MAC_ADV_CHK_CFG_INR_ERR_ENA       BIT(0)
+#define DEV10G_MAC_ADV_CHK_CFG_INR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV10G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+#define DEV10G_MAC_ADV_CHK_CFG_INR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV10G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_TX_MONITOR_STICKY */
+#define DEV10G_MAC_TX_MONITOR_STICKY(t) __REG(TARGET_DEV10G, t, 12, 0, 0, 1, 60, 48, 0, 1, 4)
+
+#define DEV10G_MAC_TX_MONITOR_STICKY_LOCAL_ERR_STATE_STICKY BIT(4)
+#define DEV10G_MAC_TX_MONITOR_STICKY_LOCAL_ERR_STATE_STICKY_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TX_MONITOR_STICKY_LOCAL_ERR_STATE_STICKY, x)
+#define DEV10G_MAC_TX_MONITOR_STICKY_LOCAL_ERR_STATE_STICKY_GET(x)\
+	FIELD_GET(DEV10G_MAC_TX_MONITOR_STICKY_LOCAL_ERR_STATE_STICKY, x)
+
+#define DEV10G_MAC_TX_MONITOR_STICKY_REMOTE_ERR_STATE_STICKY BIT(3)
+#define DEV10G_MAC_TX_MONITOR_STICKY_REMOTE_ERR_STATE_STICKY_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TX_MONITOR_STICKY_REMOTE_ERR_STATE_STICKY, x)
+#define DEV10G_MAC_TX_MONITOR_STICKY_REMOTE_ERR_STATE_STICKY_GET(x)\
+	FIELD_GET(DEV10G_MAC_TX_MONITOR_STICKY_REMOTE_ERR_STATE_STICKY, x)
+
+#define DEV10G_MAC_TX_MONITOR_STICKY_LINK_INTERRUPTION_STATE_STICKY BIT(2)
+#define DEV10G_MAC_TX_MONITOR_STICKY_LINK_INTERRUPTION_STATE_STICKY_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TX_MONITOR_STICKY_LINK_INTERRUPTION_STATE_STICKY, x)
+#define DEV10G_MAC_TX_MONITOR_STICKY_LINK_INTERRUPTION_STATE_STICKY_GET(x)\
+	FIELD_GET(DEV10G_MAC_TX_MONITOR_STICKY_LINK_INTERRUPTION_STATE_STICKY, x)
+
+#define DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY BIT(1)
+#define DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY, x)
+#define DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY_GET(x)\
+	FIELD_GET(DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY, x)
+
+#define DEV10G_MAC_TX_MONITOR_STICKY_DIS_STATE_STICKY BIT(0)
+#define DEV10G_MAC_TX_MONITOR_STICKY_DIS_STATE_STICKY_SET(x)\
+	FIELD_PREP(DEV10G_MAC_TX_MONITOR_STICKY_DIS_STATE_STICKY, x)
+#define DEV10G_MAC_TX_MONITOR_STICKY_DIS_STATE_STICKY_GET(x)\
+	FIELD_GET(DEV10G_MAC_TX_MONITOR_STICKY_DIS_STATE_STICKY, x)
+
+/*      DEV10G:DEV_CFG_STATUS:DEV_RST_CTRL */
+#define DEV10G_DEV_RST_CTRL(t)    __REG(TARGET_DEV10G, t, 12, 436, 0, 1, 52, 0, 0, 1, 4)
+
+#define DEV10G_DEV_RST_CTRL_PARDET_MODE_ENA      BIT(28)
+#define DEV10G_DEV_RST_CTRL_PARDET_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+#define DEV10G_DEV_RST_CTRL_PARDET_MODE_ENA_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+
+#define DEV10G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS BIT(27)
+#define DEV10G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+#define DEV10G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+
+#define DEV10G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS GENMASK(26, 25)
+#define DEV10G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+#define DEV10G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+
+#define DEV10G_DEV_RST_CTRL_SERDES_SPEED_SEL     GENMASK(24, 23)
+#define DEV10G_DEV_RST_CTRL_SERDES_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+#define DEV10G_DEV_RST_CTRL_SERDES_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+
+#define DEV10G_DEV_RST_CTRL_SPEED_SEL            GENMASK(22, 20)
+#define DEV10G_DEV_RST_CTRL_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_SPEED_SEL, x)
+#define DEV10G_DEV_RST_CTRL_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_SPEED_SEL, x)
+
+#define DEV10G_DEV_RST_CTRL_PCS_TX_RST           BIT(12)
+#define DEV10G_DEV_RST_CTRL_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_PCS_TX_RST, x)
+#define DEV10G_DEV_RST_CTRL_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_PCS_TX_RST, x)
+
+#define DEV10G_DEV_RST_CTRL_PCS_RX_RST           BIT(8)
+#define DEV10G_DEV_RST_CTRL_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_PCS_RX_RST, x)
+#define DEV10G_DEV_RST_CTRL_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_PCS_RX_RST, x)
+
+#define DEV10G_DEV_RST_CTRL_MAC_TX_RST           BIT(4)
+#define DEV10G_DEV_RST_CTRL_MAC_TX_RST_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_MAC_TX_RST, x)
+#define DEV10G_DEV_RST_CTRL_MAC_TX_RST_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_MAC_TX_RST, x)
+
+#define DEV10G_DEV_RST_CTRL_MAC_RX_RST           BIT(0)
+#define DEV10G_DEV_RST_CTRL_MAC_RX_RST_SET(x)\
+	FIELD_PREP(DEV10G_DEV_RST_CTRL_MAC_RX_RST, x)
+#define DEV10G_DEV_RST_CTRL_MAC_RX_RST_GET(x)\
+	FIELD_GET(DEV10G_DEV_RST_CTRL_MAC_RX_RST, x)
+
+/*      DEV10G:PCS25G_CFG_STATUS:PCS25G_CFG */
+#define DEV10G_PCS25G_CFG(t)      __REG(TARGET_DEV10G, t, 12, 488, 0, 1, 32, 0, 0, 1, 4)
+
+#define DEV10G_PCS25G_CFG_PCS25G_ENA             BIT(0)
+#define DEV10G_PCS25G_CFG_PCS25G_ENA_SET(x)\
+	FIELD_PREP(DEV10G_PCS25G_CFG_PCS25G_ENA, x)
+#define DEV10G_PCS25G_CFG_PCS25G_ENA_GET(x)\
+	FIELD_GET(DEV10G_PCS25G_CFG_PCS25G_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ENA_CFG */
+#define DEV25G_MAC_ENA_CFG(t)     __REG(TARGET_DEV25G, t, 8, 0, 0, 1, 60, 0, 0, 1, 4)
+
+#define DEV25G_MAC_ENA_CFG_RX_ENA                BIT(4)
+#define DEV25G_MAC_ENA_CFG_RX_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ENA_CFG_RX_ENA, x)
+#define DEV25G_MAC_ENA_CFG_RX_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ENA_CFG_RX_ENA, x)
+
+#define DEV25G_MAC_ENA_CFG_TX_ENA                BIT(0)
+#define DEV25G_MAC_ENA_CFG_TX_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ENA_CFG_TX_ENA, x)
+#define DEV25G_MAC_ENA_CFG_TX_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ENA_CFG_TX_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_MAXLEN_CFG */
+#define DEV25G_MAC_MAXLEN_CFG(t)  __REG(TARGET_DEV25G, t, 8, 0, 0, 1, 60, 8, 0, 1, 4)
+
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK    BIT(16)
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_SET(x)\
+	FIELD_PREP(DEV25G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_GET(x)\
+	FIELD_GET(DEV25G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN            GENMASK(15, 0)
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN_SET(x)\
+	FIELD_PREP(DEV25G_MAC_MAXLEN_CFG_MAX_LEN, x)
+#define DEV25G_MAC_MAXLEN_CFG_MAX_LEN_GET(x)\
+	FIELD_GET(DEV25G_MAC_MAXLEN_CFG_MAX_LEN, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ADV_CHK_CFG */
+#define DEV25G_MAC_ADV_CHK_CFG(t) __REG(TARGET_DEV25G, t, 8, 0, 0, 1, 60, 28, 0, 1, 4)
+
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA   BIT(24)
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA   BIT(20)
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_SFD_CHK_ENA       BIT(16)
+#define DEV25G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS   BIT(12)
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_CHK_ENA       BIT(8)
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_OOR_ERR_ENA       BIT(4)
+#define DEV25G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+
+#define DEV25G_MAC_ADV_CHK_CFG_INR_ERR_ENA       BIT(0)
+#define DEV25G_MAC_ADV_CHK_CFG_INR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV25G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+#define DEV25G_MAC_ADV_CHK_CFG_INR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV25G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+
+/*      DEV10G:DEV_CFG_STATUS:DEV_RST_CTRL */
+#define DEV25G_DEV_RST_CTRL(t)    __REG(TARGET_DEV25G, t, 8, 436, 0, 1, 52, 0, 0, 1, 4)
+
+#define DEV25G_DEV_RST_CTRL_PARDET_MODE_ENA      BIT(28)
+#define DEV25G_DEV_RST_CTRL_PARDET_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+#define DEV25G_DEV_RST_CTRL_PARDET_MODE_ENA_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+
+#define DEV25G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS BIT(27)
+#define DEV25G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+#define DEV25G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+
+#define DEV25G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS GENMASK(26, 25)
+#define DEV25G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+#define DEV25G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+
+#define DEV25G_DEV_RST_CTRL_SERDES_SPEED_SEL     GENMASK(24, 23)
+#define DEV25G_DEV_RST_CTRL_SERDES_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+#define DEV25G_DEV_RST_CTRL_SERDES_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+
+#define DEV25G_DEV_RST_CTRL_SPEED_SEL            GENMASK(22, 20)
+#define DEV25G_DEV_RST_CTRL_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_SPEED_SEL, x)
+#define DEV25G_DEV_RST_CTRL_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_SPEED_SEL, x)
+
+#define DEV25G_DEV_RST_CTRL_PCS_TX_RST           BIT(12)
+#define DEV25G_DEV_RST_CTRL_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_PCS_TX_RST, x)
+#define DEV25G_DEV_RST_CTRL_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_PCS_TX_RST, x)
+
+#define DEV25G_DEV_RST_CTRL_PCS_RX_RST           BIT(8)
+#define DEV25G_DEV_RST_CTRL_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_PCS_RX_RST, x)
+#define DEV25G_DEV_RST_CTRL_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_PCS_RX_RST, x)
+
+#define DEV25G_DEV_RST_CTRL_MAC_TX_RST           BIT(4)
+#define DEV25G_DEV_RST_CTRL_MAC_TX_RST_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_MAC_TX_RST, x)
+#define DEV25G_DEV_RST_CTRL_MAC_TX_RST_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_MAC_TX_RST, x)
+
+#define DEV25G_DEV_RST_CTRL_MAC_RX_RST           BIT(0)
+#define DEV25G_DEV_RST_CTRL_MAC_RX_RST_SET(x)\
+	FIELD_PREP(DEV25G_DEV_RST_CTRL_MAC_RX_RST, x)
+#define DEV25G_DEV_RST_CTRL_MAC_RX_RST_GET(x)\
+	FIELD_GET(DEV25G_DEV_RST_CTRL_MAC_RX_RST, x)
+
+/*      DEV10G:PCS25G_CFG_STATUS:PCS25G_CFG */
+#define DEV25G_PCS25G_CFG(t)      __REG(TARGET_DEV25G, t, 8, 488, 0, 1, 32, 0, 0, 1, 4)
+
+#define DEV25G_PCS25G_CFG_PCS25G_ENA             BIT(0)
+#define DEV25G_PCS25G_CFG_PCS25G_ENA_SET(x)\
+	FIELD_PREP(DEV25G_PCS25G_CFG_PCS25G_ENA, x)
+#define DEV25G_PCS25G_CFG_PCS25G_ENA_GET(x)\
+	FIELD_GET(DEV25G_PCS25G_CFG_PCS25G_ENA, x)
+
+/*      DEV10G:PCS25G_CFG_STATUS:PCS25G_SD_CFG */
+#define DEV25G_PCS25G_SD_CFG(t)   __REG(TARGET_DEV25G, t, 8, 488, 0, 1, 32, 4, 0, 1, 4)
+
+#define DEV25G_PCS25G_SD_CFG_SD_SEL              BIT(8)
+#define DEV25G_PCS25G_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(DEV25G_PCS25G_SD_CFG_SD_SEL, x)
+#define DEV25G_PCS25G_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(DEV25G_PCS25G_SD_CFG_SD_SEL, x)
+
+#define DEV25G_PCS25G_SD_CFG_SD_POL              BIT(4)
+#define DEV25G_PCS25G_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(DEV25G_PCS25G_SD_CFG_SD_POL, x)
+#define DEV25G_PCS25G_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(DEV25G_PCS25G_SD_CFG_SD_POL, x)
+
+#define DEV25G_PCS25G_SD_CFG_SD_ENA              BIT(0)
+#define DEV25G_PCS25G_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(DEV25G_PCS25G_SD_CFG_SD_ENA, x)
+#define DEV25G_PCS25G_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(DEV25G_PCS25G_SD_CFG_SD_ENA, x)
+
+/*      DEV1G:DEV_CFG_STATUS:DEV_RST_CTRL */
+#define DEV2G5_DEV_RST_CTRL(t)    __REG(TARGET_DEV2G5, t, 65, 0, 0, 1, 36, 0, 0, 1, 4)
+
+#define DEV2G5_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS BIT(23)
+#define DEV2G5_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+#define DEV2G5_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+
+#define DEV2G5_DEV_RST_CTRL_SPEED_SEL            GENMASK(22, 20)
+#define DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_SPEED_SEL, x)
+#define DEV2G5_DEV_RST_CTRL_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_SPEED_SEL, x)
+
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_TX_RST       BIT(17)
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_USX_PCS_TX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_USX_PCS_TX_RST, x)
+
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_RX_RST       BIT(16)
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_USX_PCS_RX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_USX_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_USX_PCS_RX_RST, x)
+
+#define DEV2G5_DEV_RST_CTRL_PCS_TX_RST           BIT(12)
+#define DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_PCS_TX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_PCS_TX_RST, x)
+
+#define DEV2G5_DEV_RST_CTRL_PCS_RX_RST           BIT(8)
+#define DEV2G5_DEV_RST_CTRL_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_PCS_RX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_PCS_RX_RST, x)
+
+#define DEV2G5_DEV_RST_CTRL_MAC_TX_RST           BIT(4)
+#define DEV2G5_DEV_RST_CTRL_MAC_TX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_MAC_TX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_MAC_TX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_MAC_TX_RST, x)
+
+#define DEV2G5_DEV_RST_CTRL_MAC_RX_RST           BIT(0)
+#define DEV2G5_DEV_RST_CTRL_MAC_RX_RST_SET(x)\
+	FIELD_PREP(DEV2G5_DEV_RST_CTRL_MAC_RX_RST, x)
+#define DEV2G5_DEV_RST_CTRL_MAC_RX_RST_GET(x)\
+	FIELD_GET(DEV2G5_DEV_RST_CTRL_MAC_RX_RST, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_ENA_CFG */
+#define DEV2G5_MAC_ENA_CFG(t)     __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 0, 0, 1, 4)
+
+#define DEV2G5_MAC_ENA_CFG_RX_ENA                BIT(4)
+#define DEV2G5_MAC_ENA_CFG_RX_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_ENA_CFG_RX_ENA, x)
+#define DEV2G5_MAC_ENA_CFG_RX_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_ENA_CFG_RX_ENA, x)
+
+#define DEV2G5_MAC_ENA_CFG_TX_ENA                BIT(0)
+#define DEV2G5_MAC_ENA_CFG_TX_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_ENA_CFG_TX_ENA, x)
+#define DEV2G5_MAC_ENA_CFG_TX_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_ENA_CFG_TX_ENA, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_MODE_CFG */
+#define DEV2G5_MAC_MODE_CFG(t)    __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 4, 0, 1, 4)
+
+#define DEV2G5_MAC_MODE_CFG_FC_WORD_SYNC_ENA     BIT(8)
+#define DEV2G5_MAC_MODE_CFG_FC_WORD_SYNC_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_MODE_CFG_FC_WORD_SYNC_ENA, x)
+#define DEV2G5_MAC_MODE_CFG_FC_WORD_SYNC_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_MODE_CFG_FC_WORD_SYNC_ENA, x)
+
+#define DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA        BIT(4)
+#define DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA, x)
+#define DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA, x)
+
+#define DEV2G5_MAC_MODE_CFG_FDX_ENA              BIT(0)
+#define DEV2G5_MAC_MODE_CFG_FDX_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_MODE_CFG_FDX_ENA, x)
+#define DEV2G5_MAC_MODE_CFG_FDX_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_MODE_CFG_FDX_ENA, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_MAXLEN_CFG */
+#define DEV2G5_MAC_MAXLEN_CFG(t)  __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 8, 0, 1, 4)
+
+#define DEV2G5_MAC_MAXLEN_CFG_MAX_LEN            GENMASK(15, 0)
+#define DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN, x)
+#define DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_GET(x)\
+	FIELD_GET(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_TAGS_CFG */
+#define DEV2G5_MAC_TAGS_CFG(t)    __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 12, 0, 1, 4)
+
+#define DEV2G5_MAC_TAGS_CFG_TAG_ID               GENMASK(31, 16)
+#define DEV2G5_MAC_TAGS_CFG_TAG_ID_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG_TAG_ID, x)
+#define DEV2G5_MAC_TAGS_CFG_TAG_ID_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG_TAG_ID, x)
+
+#define DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA     BIT(3)
+#define DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x)
+#define DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x)
+
+#define DEV2G5_MAC_TAGS_CFG_PB_ENA               GENMASK(2, 1)
+#define DEV2G5_MAC_TAGS_CFG_PB_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG_PB_ENA, x)
+#define DEV2G5_MAC_TAGS_CFG_PB_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG_PB_ENA, x)
+
+#define DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA         BIT(0)
+#define DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA, x)
+#define DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_TAGS_CFG2 */
+#define DEV2G5_MAC_TAGS_CFG2(t)   __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 16, 0, 1, 4)
+
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID3             GENMASK(31, 16)
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID3_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG2_TAG_ID3, x)
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID3_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG2_TAG_ID3, x)
+
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID2             GENMASK(15, 0)
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID2_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_TAGS_CFG2_TAG_ID2, x)
+#define DEV2G5_MAC_TAGS_CFG2_TAG_ID2_GET(x)\
+	FIELD_GET(DEV2G5_MAC_TAGS_CFG2_TAG_ID2, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_ADV_CHK_CFG */
+#define DEV2G5_MAC_ADV_CHK_CFG(t) __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 20, 0, 1, 4)
+
+#define DEV2G5_MAC_ADV_CHK_CFG_LEN_DROP_ENA      BIT(0)
+#define DEV2G5_MAC_ADV_CHK_CFG_LEN_DROP_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_ADV_CHK_CFG_LEN_DROP_ENA, x)
+#define DEV2G5_MAC_ADV_CHK_CFG_LEN_DROP_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_ADV_CHK_CFG_LEN_DROP_ENA, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_IFG_CFG */
+#define DEV2G5_MAC_IFG_CFG(t)     __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 24, 0, 1, 4)
+
+#define DEV2G5_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK BIT(17)
+#define DEV2G5_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK, x)
+#define DEV2G5_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK_GET(x)\
+	FIELD_GET(DEV2G5_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK, x)
+
+#define DEV2G5_MAC_IFG_CFG_TX_IFG                GENMASK(12, 8)
+#define DEV2G5_MAC_IFG_CFG_TX_IFG_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_IFG_CFG_TX_IFG, x)
+#define DEV2G5_MAC_IFG_CFG_TX_IFG_GET(x)\
+	FIELD_GET(DEV2G5_MAC_IFG_CFG_TX_IFG, x)
+
+#define DEV2G5_MAC_IFG_CFG_RX_IFG2               GENMASK(7, 4)
+#define DEV2G5_MAC_IFG_CFG_RX_IFG2_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_IFG_CFG_RX_IFG2, x)
+#define DEV2G5_MAC_IFG_CFG_RX_IFG2_GET(x)\
+	FIELD_GET(DEV2G5_MAC_IFG_CFG_RX_IFG2, x)
+
+#define DEV2G5_MAC_IFG_CFG_RX_IFG1               GENMASK(3, 0)
+#define DEV2G5_MAC_IFG_CFG_RX_IFG1_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_IFG_CFG_RX_IFG1, x)
+#define DEV2G5_MAC_IFG_CFG_RX_IFG1_GET(x)\
+	FIELD_GET(DEV2G5_MAC_IFG_CFG_RX_IFG1, x)
+
+/*      DEV1G:MAC_CFG_STATUS:MAC_HDX_CFG */
+#define DEV2G5_MAC_HDX_CFG(t)     __REG(TARGET_DEV2G5, t, 65, 52, 0, 1, 36, 28, 0, 1, 4)
+
+#define DEV2G5_MAC_HDX_CFG_BYPASS_COL_SYNC       BIT(26)
+#define DEV2G5_MAC_HDX_CFG_BYPASS_COL_SYNC_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_HDX_CFG_BYPASS_COL_SYNC, x)
+#define DEV2G5_MAC_HDX_CFG_BYPASS_COL_SYNC_GET(x)\
+	FIELD_GET(DEV2G5_MAC_HDX_CFG_BYPASS_COL_SYNC, x)
+
+#define DEV2G5_MAC_HDX_CFG_SEED                  GENMASK(23, 16)
+#define DEV2G5_MAC_HDX_CFG_SEED_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_HDX_CFG_SEED, x)
+#define DEV2G5_MAC_HDX_CFG_SEED_GET(x)\
+	FIELD_GET(DEV2G5_MAC_HDX_CFG_SEED, x)
+
+#define DEV2G5_MAC_HDX_CFG_SEED_LOAD             BIT(12)
+#define DEV2G5_MAC_HDX_CFG_SEED_LOAD_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_HDX_CFG_SEED_LOAD, x)
+#define DEV2G5_MAC_HDX_CFG_SEED_LOAD_GET(x)\
+	FIELD_GET(DEV2G5_MAC_HDX_CFG_SEED_LOAD, x)
+
+#define DEV2G5_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA BIT(8)
+#define DEV2G5_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA, x)
+#define DEV2G5_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA_GET(x)\
+	FIELD_GET(DEV2G5_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA, x)
+
+#define DEV2G5_MAC_HDX_CFG_LATE_COL_POS          GENMASK(6, 0)
+#define DEV2G5_MAC_HDX_CFG_LATE_COL_POS_SET(x)\
+	FIELD_PREP(DEV2G5_MAC_HDX_CFG_LATE_COL_POS, x)
+#define DEV2G5_MAC_HDX_CFG_LATE_COL_POS_GET(x)\
+	FIELD_GET(DEV2G5_MAC_HDX_CFG_LATE_COL_POS, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_CFG */
+#define DEV2G5_PCS1G_CFG(t)       __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 0, 0, 1, 4)
+
+#define DEV2G5_PCS1G_CFG_LINK_STATUS_TYPE        BIT(4)
+#define DEV2G5_PCS1G_CFG_LINK_STATUS_TYPE_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_CFG_LINK_STATUS_TYPE, x)
+#define DEV2G5_PCS1G_CFG_LINK_STATUS_TYPE_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_CFG_LINK_STATUS_TYPE, x)
+
+#define DEV2G5_PCS1G_CFG_AN_LINK_CTRL_ENA        BIT(1)
+#define DEV2G5_PCS1G_CFG_AN_LINK_CTRL_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_CFG_AN_LINK_CTRL_ENA, x)
+#define DEV2G5_PCS1G_CFG_AN_LINK_CTRL_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_CFG_AN_LINK_CTRL_ENA, x)
+
+#define DEV2G5_PCS1G_CFG_PCS_ENA                 BIT(0)
+#define DEV2G5_PCS1G_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_CFG_PCS_ENA, x)
+#define DEV2G5_PCS1G_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_CFG_PCS_ENA, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_MODE_CFG */
+#define DEV2G5_PCS1G_MODE_CFG(t)  __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 4, 0, 1, 4)
+
+#define DEV2G5_PCS1G_MODE_CFG_UNIDIR_MODE_ENA    BIT(4)
+#define DEV2G5_PCS1G_MODE_CFG_UNIDIR_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_MODE_CFG_UNIDIR_MODE_ENA, x)
+#define DEV2G5_PCS1G_MODE_CFG_UNIDIR_MODE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_MODE_CFG_UNIDIR_MODE_ENA, x)
+
+#define DEV2G5_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA  BIT(1)
+#define DEV2G5_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x)
+#define DEV2G5_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x)
+
+#define DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA     BIT(0)
+#define DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA, x)
+#define DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_SD_CFG */
+#define DEV2G5_PCS1G_SD_CFG(t)    __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 8, 0, 1, 4)
+
+#define DEV2G5_PCS1G_SD_CFG_SD_SEL               BIT(8)
+#define DEV2G5_PCS1G_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_SD_CFG_SD_SEL, x)
+#define DEV2G5_PCS1G_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_SD_CFG_SD_SEL, x)
+
+#define DEV2G5_PCS1G_SD_CFG_SD_POL               BIT(4)
+#define DEV2G5_PCS1G_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_SD_CFG_SD_POL, x)
+#define DEV2G5_PCS1G_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_SD_CFG_SD_POL, x)
+
+#define DEV2G5_PCS1G_SD_CFG_SD_ENA               BIT(0)
+#define DEV2G5_PCS1G_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_SD_CFG_SD_ENA, x)
+#define DEV2G5_PCS1G_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_SD_CFG_SD_ENA, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_ANEG_CFG */
+#define DEV2G5_PCS1G_ANEG_CFG(t)  __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 12, 0, 1, 4)
+
+#define DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY        GENMASK(31, 16)
+#define DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY, x)
+#define DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY, x)
+
+#define DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA     BIT(8)
+#define DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA, x)
+#define DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA, x)
+
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT BIT(1)
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT, x)
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT, x)
+
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA           BIT(0)
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA, x)
+#define DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_LB_CFG */
+#define DEV2G5_PCS1G_LB_CFG(t)    __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 20, 0, 1, 4)
+
+#define DEV2G5_PCS1G_LB_CFG_RA_ENA               BIT(4)
+#define DEV2G5_PCS1G_LB_CFG_RA_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LB_CFG_RA_ENA, x)
+#define DEV2G5_PCS1G_LB_CFG_RA_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LB_CFG_RA_ENA, x)
+
+#define DEV2G5_PCS1G_LB_CFG_GMII_PHY_LB_ENA      BIT(1)
+#define DEV2G5_PCS1G_LB_CFG_GMII_PHY_LB_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LB_CFG_GMII_PHY_LB_ENA, x)
+#define DEV2G5_PCS1G_LB_CFG_GMII_PHY_LB_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LB_CFG_GMII_PHY_LB_ENA, x)
+
+#define DEV2G5_PCS1G_LB_CFG_TBI_HOST_LB_ENA      BIT(0)
+#define DEV2G5_PCS1G_LB_CFG_TBI_HOST_LB_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LB_CFG_TBI_HOST_LB_ENA, x)
+#define DEV2G5_PCS1G_LB_CFG_TBI_HOST_LB_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LB_CFG_TBI_HOST_LB_ENA, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_ANEG_STATUS */
+#define DEV2G5_PCS1G_ANEG_STATUS(t) __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 32, 0, 1, 4)
+
+#define DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY  GENMASK(31, 16)
+#define DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY, x)
+#define DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY, x)
+
+#define DEV2G5_PCS1G_ANEG_STATUS_PR              BIT(4)
+#define DEV2G5_PCS1G_ANEG_STATUS_PR_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_STATUS_PR, x)
+#define DEV2G5_PCS1G_ANEG_STATUS_PR_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_STATUS_PR, x)
+
+#define DEV2G5_PCS1G_ANEG_STATUS_PAGE_RX_STICKY  BIT(3)
+#define DEV2G5_PCS1G_ANEG_STATUS_PAGE_RX_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_STATUS_PAGE_RX_STICKY, x)
+#define DEV2G5_PCS1G_ANEG_STATUS_PAGE_RX_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_STATUS_PAGE_RX_STICKY, x)
+
+#define DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE   BIT(0)
+#define DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE, x)
+#define DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_LINK_STATUS */
+#define DEV2G5_PCS1G_LINK_STATUS(t) __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 40, 0, 1, 4)
+
+#define DEV2G5_PCS1G_LINK_STATUS_DELAY_VAR       GENMASK(15, 12)
+#define DEV2G5_PCS1G_LINK_STATUS_DELAY_VAR_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LINK_STATUS_DELAY_VAR, x)
+#define DEV2G5_PCS1G_LINK_STATUS_DELAY_VAR_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LINK_STATUS_DELAY_VAR, x)
+
+#define DEV2G5_PCS1G_LINK_STATUS_SIGNAL_DETECT   BIT(8)
+#define DEV2G5_PCS1G_LINK_STATUS_SIGNAL_DETECT_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LINK_STATUS_SIGNAL_DETECT, x)
+#define DEV2G5_PCS1G_LINK_STATUS_SIGNAL_DETECT_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LINK_STATUS_SIGNAL_DETECT, x)
+
+#define DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS     BIT(4)
+#define DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS, x)
+#define DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS, x)
+
+#define DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS     BIT(0)
+#define DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS, x)
+#define DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS, x)
+
+/*      DEV1G:PCS1G_CFG_STATUS:PCS1G_STICKY */
+#define DEV2G5_PCS1G_STICKY(t)    __REG(TARGET_DEV2G5, t, 65, 88, 0, 1, 68, 48, 0, 1, 4)
+
+#define DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY     BIT(4)
+#define DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY, x)
+#define DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY, x)
+
+#define DEV2G5_PCS1G_STICKY_OUT_OF_SYNC_STICKY   BIT(0)
+#define DEV2G5_PCS1G_STICKY_OUT_OF_SYNC_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS1G_STICKY_OUT_OF_SYNC_STICKY, x)
+#define DEV2G5_PCS1G_STICKY_OUT_OF_SYNC_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS1G_STICKY_OUT_OF_SYNC_STICKY, x)
+
+/*      DEV1G:PCS_FX100_CONFIGURATION:PCS_FX100_CFG */
+#define DEV2G5_PCS_FX100_CFG(t)   __REG(TARGET_DEV2G5, t, 65, 164, 0, 1, 4, 0, 0, 1, 4)
+
+#define DEV2G5_PCS_FX100_CFG_SD_SEL              BIT(26)
+#define DEV2G5_PCS_FX100_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_SD_SEL, x)
+#define DEV2G5_PCS_FX100_CFG_SD_SEL_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_SD_SEL, x)
+
+#define DEV2G5_PCS_FX100_CFG_SD_POL              BIT(25)
+#define DEV2G5_PCS_FX100_CFG_SD_POL_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_SD_POL, x)
+#define DEV2G5_PCS_FX100_CFG_SD_POL_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_SD_POL, x)
+
+#define DEV2G5_PCS_FX100_CFG_SD_ENA              BIT(24)
+#define DEV2G5_PCS_FX100_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_SD_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_SD_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_SD_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_LOOPBACK_ENA        BIT(20)
+#define DEV2G5_PCS_FX100_CFG_LOOPBACK_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_LOOPBACK_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_LOOPBACK_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_LOOPBACK_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_SWAP_MII_ENA        BIT(16)
+#define DEV2G5_PCS_FX100_CFG_SWAP_MII_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_SWAP_MII_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_SWAP_MII_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_SWAP_MII_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_RXBITSEL            GENMASK(15, 12)
+#define DEV2G5_PCS_FX100_CFG_RXBITSEL_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_RXBITSEL, x)
+#define DEV2G5_PCS_FX100_CFG_RXBITSEL_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_RXBITSEL, x)
+
+#define DEV2G5_PCS_FX100_CFG_SIGDET_CFG          GENMASK(10, 9)
+#define DEV2G5_PCS_FX100_CFG_SIGDET_CFG_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_SIGDET_CFG, x)
+#define DEV2G5_PCS_FX100_CFG_SIGDET_CFG_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_SIGDET_CFG, x)
+
+#define DEV2G5_PCS_FX100_CFG_LINKHYST_TM_ENA     BIT(8)
+#define DEV2G5_PCS_FX100_CFG_LINKHYST_TM_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_LINKHYST_TM_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_LINKHYST_TM_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_LINKHYST_TM_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_LINKHYSTTIMER       GENMASK(7, 4)
+#define DEV2G5_PCS_FX100_CFG_LINKHYSTTIMER_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_LINKHYSTTIMER, x)
+#define DEV2G5_PCS_FX100_CFG_LINKHYSTTIMER_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_LINKHYSTTIMER, x)
+
+#define DEV2G5_PCS_FX100_CFG_UNIDIR_MODE_ENA     BIT(3)
+#define DEV2G5_PCS_FX100_CFG_UNIDIR_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_UNIDIR_MODE_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_UNIDIR_MODE_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_UNIDIR_MODE_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_FEFCHK_ENA          BIT(2)
+#define DEV2G5_PCS_FX100_CFG_FEFCHK_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_FEFCHK_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_FEFCHK_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_FEFCHK_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_FEFGEN_ENA          BIT(1)
+#define DEV2G5_PCS_FX100_CFG_FEFGEN_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_FEFGEN_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_FEFGEN_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_FEFGEN_ENA, x)
+
+#define DEV2G5_PCS_FX100_CFG_PCS_ENA             BIT(0)
+#define DEV2G5_PCS_FX100_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_CFG_PCS_ENA, x)
+#define DEV2G5_PCS_FX100_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_CFG_PCS_ENA, x)
+
+/*      DEV1G:PCS_FX100_STATUS:PCS_FX100_STATUS */
+#define DEV2G5_PCS_FX100_STATUS(t) __REG(TARGET_DEV2G5, t, 65, 168, 0, 1, 4, 0, 0, 1, 4)
+
+#define DEV2G5_PCS_FX100_STATUS_EDGE_POS_PTP     GENMASK(11, 8)
+#define DEV2G5_PCS_FX100_STATUS_EDGE_POS_PTP_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_EDGE_POS_PTP, x)
+#define DEV2G5_PCS_FX100_STATUS_EDGE_POS_PTP_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_EDGE_POS_PTP, x)
+
+#define DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY BIT(7)
+#define DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY, x)
+#define DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY, x)
+
+#define DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY BIT(6)
+#define DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY, x)
+#define DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY, x)
+
+#define DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY BIT(5)
+#define DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY, x)
+#define DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY, x)
+
+#define DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY BIT(4)
+#define DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY, x)
+#define DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY, x)
+
+#define DEV2G5_PCS_FX100_STATUS_FEF_STATUS       BIT(2)
+#define DEV2G5_PCS_FX100_STATUS_FEF_STATUS_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_FEF_STATUS, x)
+#define DEV2G5_PCS_FX100_STATUS_FEF_STATUS_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_FEF_STATUS, x)
+
+#define DEV2G5_PCS_FX100_STATUS_SIGNAL_DETECT    BIT(1)
+#define DEV2G5_PCS_FX100_STATUS_SIGNAL_DETECT_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_SIGNAL_DETECT, x)
+#define DEV2G5_PCS_FX100_STATUS_SIGNAL_DETECT_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_SIGNAL_DETECT, x)
+
+#define DEV2G5_PCS_FX100_STATUS_SYNC_STATUS      BIT(0)
+#define DEV2G5_PCS_FX100_STATUS_SYNC_STATUS_SET(x)\
+	FIELD_PREP(DEV2G5_PCS_FX100_STATUS_SYNC_STATUS, x)
+#define DEV2G5_PCS_FX100_STATUS_SYNC_STATUS_GET(x)\
+	FIELD_GET(DEV2G5_PCS_FX100_STATUS_SYNC_STATUS, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ENA_CFG */
+#define DEV5G_MAC_ENA_CFG(t)      __REG(TARGET_DEV5G, t, 13, 0, 0, 1, 60, 0, 0, 1, 4)
+
+#define DEV5G_MAC_ENA_CFG_RX_ENA                 BIT(4)
+#define DEV5G_MAC_ENA_CFG_RX_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ENA_CFG_RX_ENA, x)
+#define DEV5G_MAC_ENA_CFG_RX_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ENA_CFG_RX_ENA, x)
+
+#define DEV5G_MAC_ENA_CFG_TX_ENA                 BIT(0)
+#define DEV5G_MAC_ENA_CFG_TX_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ENA_CFG_TX_ENA, x)
+#define DEV5G_MAC_ENA_CFG_TX_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ENA_CFG_TX_ENA, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_MAXLEN_CFG */
+#define DEV5G_MAC_MAXLEN_CFG(t)   __REG(TARGET_DEV5G, t, 13, 0, 0, 1, 60, 8, 0, 1, 4)
+
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK     BIT(16)
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_SET(x)\
+	FIELD_PREP(DEV5G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_GET(x)\
+	FIELD_GET(DEV5G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK, x)
+
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN             GENMASK(15, 0)
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN_SET(x)\
+	FIELD_PREP(DEV5G_MAC_MAXLEN_CFG_MAX_LEN, x)
+#define DEV5G_MAC_MAXLEN_CFG_MAX_LEN_GET(x)\
+	FIELD_GET(DEV5G_MAC_MAXLEN_CFG_MAX_LEN, x)
+
+/*      DEV10G:MAC_CFG_STATUS:MAC_ADV_CHK_CFG */
+#define DEV5G_MAC_ADV_CHK_CFG(t)  __REG(TARGET_DEV5G, t, 13, 0, 0, 1, 60, 28, 0, 1, 4)
+
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA    BIT(24)
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_EXT_EOP_CHK_ENA, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA    BIT(20)
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_EXT_SOP_CHK_ENA, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_SFD_CHK_ENA        BIT(16)
+#define DEV5G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_SFD_CHK_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_SFD_CHK_ENA, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS    BIT(12)
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_PRM_SHK_CHK_DIS, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_CHK_ENA        BIT(8)
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_PRM_CHK_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_PRM_CHK_ENA, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_OOR_ERR_ENA        BIT(4)
+#define DEV5G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_OOR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_OOR_ERR_ENA, x)
+
+#define DEV5G_MAC_ADV_CHK_CFG_INR_ERR_ENA        BIT(0)
+#define DEV5G_MAC_ADV_CHK_CFG_INR_ERR_ENA_SET(x)\
+	FIELD_PREP(DEV5G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+#define DEV5G_MAC_ADV_CHK_CFG_INR_ERR_ENA_GET(x)\
+	FIELD_GET(DEV5G_MAC_ADV_CHK_CFG_INR_ERR_ENA, x)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SYMBOL_ERR_CNT */
+#define DEV5G_RX_SYMBOL_ERR_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 0, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_PAUSE_CNT */
+#define DEV5G_RX_PAUSE_CNT(t)     __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 4, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_UC_CNT */
+#define DEV5G_RX_UC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 12, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_MC_CNT */
+#define DEV5G_RX_MC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 16, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_BC_CNT */
+#define DEV5G_RX_BC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 20, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_CRC_ERR_CNT */
+#define DEV5G_RX_CRC_ERR_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 24, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_UNDERSIZE_CNT */
+#define DEV5G_RX_UNDERSIZE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 28, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_FRAGMENTS_CNT */
+#define DEV5G_RX_FRAGMENTS_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 32, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_IN_RANGE_LEN_ERR_CNT */
+#define DEV5G_RX_IN_RANGE_LEN_ERR_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 36, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_OUT_OF_RANGE_LEN_ERR_CNT */
+#define DEV5G_RX_OUT_OF_RANGE_LEN_ERR_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 40, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_OVERSIZE_CNT */
+#define DEV5G_RX_OVERSIZE_CNT(t)  __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 44, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_JABBERS_CNT */
+#define DEV5G_RX_JABBERS_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 48, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE64_CNT */
+#define DEV5G_RX_SIZE64_CNT(t)    __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 52, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE65TO127_CNT */
+#define DEV5G_RX_SIZE65TO127_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 56, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE128TO255_CNT */
+#define DEV5G_RX_SIZE128TO255_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 60, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE256TO511_CNT */
+#define DEV5G_RX_SIZE256TO511_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 64, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE512TO1023_CNT */
+#define DEV5G_RX_SIZE512TO1023_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 68, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE1024TO1518_CNT */
+#define DEV5G_RX_SIZE1024TO1518_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 72, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:RX_SIZE1519TOMAX_CNT */
+#define DEV5G_RX_SIZE1519TOMAX_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 76, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_PAUSE_CNT */
+#define DEV5G_TX_PAUSE_CNT(t)     __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 84, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_UC_CNT */
+#define DEV5G_TX_UC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 88, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_MC_CNT */
+#define DEV5G_TX_MC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 92, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_BC_CNT */
+#define DEV5G_TX_BC_CNT(t)        __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 96, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE64_CNT */
+#define DEV5G_TX_SIZE64_CNT(t)    __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 100, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE65TO127_CNT */
+#define DEV5G_TX_SIZE65TO127_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 104, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE128TO255_CNT */
+#define DEV5G_TX_SIZE128TO255_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 108, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE256TO511_CNT */
+#define DEV5G_TX_SIZE256TO511_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 112, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE512TO1023_CNT */
+#define DEV5G_TX_SIZE512TO1023_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 116, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE1024TO1518_CNT */
+#define DEV5G_TX_SIZE1024TO1518_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 120, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:TX_SIZE1519TOMAX_CNT */
+#define DEV5G_TX_SIZE1519TOMAX_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 124, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SYMBOL_ERR_CNT */
+#define DEV5G_PMAC_RX_SYMBOL_ERR_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 148, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_PAUSE_CNT */
+#define DEV5G_PMAC_RX_PAUSE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 152, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_UNSUP_OPCODE_CNT */
+#define DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 156, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_UC_CNT */
+#define DEV5G_PMAC_RX_UC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 160, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_MC_CNT */
+#define DEV5G_PMAC_RX_MC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 164, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_BC_CNT */
+#define DEV5G_PMAC_RX_BC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 168, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_CRC_ERR_CNT */
+#define DEV5G_PMAC_RX_CRC_ERR_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 172, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_UNDERSIZE_CNT */
+#define DEV5G_PMAC_RX_UNDERSIZE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 176, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_FRAGMENTS_CNT */
+#define DEV5G_PMAC_RX_FRAGMENTS_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 180, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_IN_RANGE_LEN_ERR_CNT */
+#define DEV5G_PMAC_RX_IN_RANGE_LEN_ERR_CNT(t) __REG(TARGET_DEV5G,\
+					t, 13, 60, 0, 1, 312, 184, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT */
+#define DEV5G_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(t) __REG(TARGET_DEV5G,\
+					t, 13, 60, 0, 1, 312, 188, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_OVERSIZE_CNT */
+#define DEV5G_PMAC_RX_OVERSIZE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 192, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_JABBERS_CNT */
+#define DEV5G_PMAC_RX_JABBERS_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 196, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE64_CNT */
+#define DEV5G_PMAC_RX_SIZE64_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 200, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE65TO127_CNT */
+#define DEV5G_PMAC_RX_SIZE65TO127_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 204, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE128TO255_CNT */
+#define DEV5G_PMAC_RX_SIZE128TO255_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 208, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE256TO511_CNT */
+#define DEV5G_PMAC_RX_SIZE256TO511_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 212, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE512TO1023_CNT */
+#define DEV5G_PMAC_RX_SIZE512TO1023_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 216, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE1024TO1518_CNT */
+#define DEV5G_PMAC_RX_SIZE1024TO1518_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 220, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_RX_SIZE1519TOMAX_CNT */
+#define DEV5G_PMAC_RX_SIZE1519TOMAX_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 224, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_PAUSE_CNT */
+#define DEV5G_PMAC_TX_PAUSE_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 228, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_UC_CNT */
+#define DEV5G_PMAC_TX_UC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 232, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_MC_CNT */
+#define DEV5G_PMAC_TX_MC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 236, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_BC_CNT */
+#define DEV5G_PMAC_TX_BC_CNT(t)   __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 240, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE64_CNT */
+#define DEV5G_PMAC_TX_SIZE64_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 244, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE65TO127_CNT */
+#define DEV5G_PMAC_TX_SIZE65TO127_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 248, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE128TO255_CNT */
+#define DEV5G_PMAC_TX_SIZE128TO255_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 252, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE256TO511_CNT */
+#define DEV5G_PMAC_TX_SIZE256TO511_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 256, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE512TO1023_CNT */
+#define DEV5G_PMAC_TX_SIZE512TO1023_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 260, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE1024TO1518_CNT */
+#define DEV5G_PMAC_TX_SIZE1024TO1518_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 264, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_32BIT:PMAC_TX_SIZE1519TOMAX_CNT */
+#define DEV5G_PMAC_TX_SIZE1519TOMAX_CNT(t) __REG(TARGET_DEV5G, t, 13, 60, 0, 1, 312, 268, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:RX_IN_BYTES_CNT */
+#define DEV5G_RX_IN_BYTES_CNT(t)  __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 0, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:RX_OK_BYTES_CNT */
+#define DEV5G_RX_OK_BYTES_CNT(t)  __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 8, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:RX_BAD_BYTES_CNT */
+#define DEV5G_RX_BAD_BYTES_CNT(t) __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 16, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:TX_OUT_BYTES_CNT */
+#define DEV5G_TX_OUT_BYTES_CNT(t) __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 24, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:TX_OK_BYTES_CNT */
+#define DEV5G_TX_OK_BYTES_CNT(t)  __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 32, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:PMAC_RX_OK_BYTES_CNT */
+#define DEV5G_PMAC_RX_OK_BYTES_CNT(t) __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 40, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:PMAC_RX_BAD_BYTES_CNT */
+#define DEV5G_PMAC_RX_BAD_BYTES_CNT(t) __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 48, 0, 1, 4)
+
+/*      DEV10G:DEV_STATISTICS_40BIT:PMAC_TX_OK_BYTES_CNT */
+#define DEV5G_PMAC_TX_OK_BYTES_CNT(t) __REG(TARGET_DEV5G, t, 13, 372, 0, 1, 64, 56, 0, 1, 4)
+
+/*      DEV10G:DEV_CFG_STATUS:DEV_RST_CTRL */
+#define DEV5G_DEV_RST_CTRL(t)     __REG(TARGET_DEV5G, t, 13, 436, 0, 1, 52, 0, 0, 1, 4)
+
+#define DEV5G_DEV_RST_CTRL_PARDET_MODE_ENA       BIT(28)
+#define DEV5G_DEV_RST_CTRL_PARDET_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+#define DEV5G_DEV_RST_CTRL_PARDET_MODE_ENA_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_PARDET_MODE_ENA, x)
+
+#define DEV5G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS BIT(27)
+#define DEV5G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+#define DEV5G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_USXGMII_OSET_FILTER_DIS, x)
+
+#define DEV5G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS GENMASK(26, 25)
+#define DEV5G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+#define DEV5G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_MUXED_USXGMII_NETWORK_PORTS, x)
+
+#define DEV5G_DEV_RST_CTRL_SERDES_SPEED_SEL      GENMASK(24, 23)
+#define DEV5G_DEV_RST_CTRL_SERDES_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+#define DEV5G_DEV_RST_CTRL_SERDES_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_SERDES_SPEED_SEL, x)
+
+#define DEV5G_DEV_RST_CTRL_SPEED_SEL             GENMASK(22, 20)
+#define DEV5G_DEV_RST_CTRL_SPEED_SEL_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_SPEED_SEL, x)
+#define DEV5G_DEV_RST_CTRL_SPEED_SEL_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_SPEED_SEL, x)
+
+#define DEV5G_DEV_RST_CTRL_PCS_TX_RST            BIT(12)
+#define DEV5G_DEV_RST_CTRL_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_PCS_TX_RST, x)
+#define DEV5G_DEV_RST_CTRL_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_PCS_TX_RST, x)
+
+#define DEV5G_DEV_RST_CTRL_PCS_RX_RST            BIT(8)
+#define DEV5G_DEV_RST_CTRL_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_PCS_RX_RST, x)
+#define DEV5G_DEV_RST_CTRL_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_PCS_RX_RST, x)
+
+#define DEV5G_DEV_RST_CTRL_MAC_TX_RST            BIT(4)
+#define DEV5G_DEV_RST_CTRL_MAC_TX_RST_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_MAC_TX_RST, x)
+#define DEV5G_DEV_RST_CTRL_MAC_TX_RST_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_MAC_TX_RST, x)
+
+#define DEV5G_DEV_RST_CTRL_MAC_RX_RST            BIT(0)
+#define DEV5G_DEV_RST_CTRL_MAC_RX_RST_SET(x)\
+	FIELD_PREP(DEV5G_DEV_RST_CTRL_MAC_RX_RST, x)
+#define DEV5G_DEV_RST_CTRL_MAC_RX_RST_GET(x)\
+	FIELD_GET(DEV5G_DEV_RST_CTRL_MAC_RX_RST, x)
+
+/*      DSM:RAM_CTRL:RAM_INIT */
+#define DSM_RAM_INIT              __REG(TARGET_DSM, 0, 1, 0, 0, 1, 4, 0, 0, 1, 4)
+
+#define DSM_RAM_INIT_RAM_INIT                    BIT(1)
+#define DSM_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(DSM_RAM_INIT_RAM_INIT, x)
+#define DSM_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(DSM_RAM_INIT_RAM_INIT, x)
+
+#define DSM_RAM_INIT_RAM_CFG_HOOK                BIT(0)
+#define DSM_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(DSM_RAM_INIT_RAM_CFG_HOOK, x)
+#define DSM_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(DSM_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      DSM:CFG:BUF_CFG */
+#define DSM_BUF_CFG(r)            __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 0, r, 67, 4)
+
+#define DSM_BUF_CFG_CSC_STAT_DIS                 BIT(13)
+#define DSM_BUF_CFG_CSC_STAT_DIS_SET(x)\
+	FIELD_PREP(DSM_BUF_CFG_CSC_STAT_DIS, x)
+#define DSM_BUF_CFG_CSC_STAT_DIS_GET(x)\
+	FIELD_GET(DSM_BUF_CFG_CSC_STAT_DIS, x)
+
+#define DSM_BUF_CFG_AGING_ENA                    BIT(12)
+#define DSM_BUF_CFG_AGING_ENA_SET(x)\
+	FIELD_PREP(DSM_BUF_CFG_AGING_ENA, x)
+#define DSM_BUF_CFG_AGING_ENA_GET(x)\
+	FIELD_GET(DSM_BUF_CFG_AGING_ENA, x)
+
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS       BIT(11)
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_SET(x)\
+	FIELD_PREP(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS, x)
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS_GET(x)\
+	FIELD_GET(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_DIS, x)
+
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_TIMEOUT   GENMASK(10, 0)
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_TIMEOUT_SET(x)\
+	FIELD_PREP(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_TIMEOUT, x)
+#define DSM_BUF_CFG_UNDERFLOW_WATCHDOG_TIMEOUT_GET(x)\
+	FIELD_GET(DSM_BUF_CFG_UNDERFLOW_WATCHDOG_TIMEOUT, x)
+
+/*      DSM:CFG:DEV_TX_STOP_WM_CFG */
+#define DSM_DEV_TX_STOP_WM_CFG(r) __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 1360, r, 67, 4)
+
+#define DSM_DEV_TX_STOP_WM_CFG_FAST_STARTUP_ENA  BIT(9)
+#define DSM_DEV_TX_STOP_WM_CFG_FAST_STARTUP_ENA_SET(x)\
+	FIELD_PREP(DSM_DEV_TX_STOP_WM_CFG_FAST_STARTUP_ENA, x)
+#define DSM_DEV_TX_STOP_WM_CFG_FAST_STARTUP_ENA_GET(x)\
+	FIELD_GET(DSM_DEV_TX_STOP_WM_CFG_FAST_STARTUP_ENA, x)
+
+#define DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA BIT(8)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA_SET(x)\
+	FIELD_PREP(DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA, x)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA_GET(x)\
+	FIELD_GET(DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA, x)
+
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM    GENMASK(7, 1)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(x)\
+	FIELD_PREP(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM, x)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_GET(x)\
+	FIELD_GET(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM, x)
+
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR    BIT(0)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(x)\
+	FIELD_PREP(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR, x)
+#define DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_GET(x)\
+	FIELD_GET(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR, x)
+
+/*      DSM:CFG:RX_PAUSE_CFG */
+#define DSM_RX_PAUSE_CFG(r)       __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 1628, r, 67, 4)
+
+#define DSM_RX_PAUSE_CFG_RX_PAUSE_EN             BIT(1)
+#define DSM_RX_PAUSE_CFG_RX_PAUSE_EN_SET(x)\
+	FIELD_PREP(DSM_RX_PAUSE_CFG_RX_PAUSE_EN, x)
+#define DSM_RX_PAUSE_CFG_RX_PAUSE_EN_GET(x)\
+	FIELD_GET(DSM_RX_PAUSE_CFG_RX_PAUSE_EN, x)
+
+#define DSM_RX_PAUSE_CFG_FC_OBEY_LOCAL           BIT(0)
+#define DSM_RX_PAUSE_CFG_FC_OBEY_LOCAL_SET(x)\
+	FIELD_PREP(DSM_RX_PAUSE_CFG_FC_OBEY_LOCAL, x)
+#define DSM_RX_PAUSE_CFG_FC_OBEY_LOCAL_GET(x)\
+	FIELD_GET(DSM_RX_PAUSE_CFG_FC_OBEY_LOCAL, x)
+
+/*      DSM:CFG:MAC_CFG */
+#define DSM_MAC_CFG(r)            __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 2432, r, 67, 4)
+
+#define DSM_MAC_CFG_TX_PAUSE_VAL                 GENMASK(31, 16)
+#define DSM_MAC_CFG_TX_PAUSE_VAL_SET(x)\
+	FIELD_PREP(DSM_MAC_CFG_TX_PAUSE_VAL, x)
+#define DSM_MAC_CFG_TX_PAUSE_VAL_GET(x)\
+	FIELD_GET(DSM_MAC_CFG_TX_PAUSE_VAL, x)
+
+#define DSM_MAC_CFG_HDX_BACKPREASSURE            BIT(2)
+#define DSM_MAC_CFG_HDX_BACKPREASSURE_SET(x)\
+	FIELD_PREP(DSM_MAC_CFG_HDX_BACKPREASSURE, x)
+#define DSM_MAC_CFG_HDX_BACKPREASSURE_GET(x)\
+	FIELD_GET(DSM_MAC_CFG_HDX_BACKPREASSURE, x)
+
+#define DSM_MAC_CFG_SEND_PAUSE_FRM_TWICE         BIT(1)
+#define DSM_MAC_CFG_SEND_PAUSE_FRM_TWICE_SET(x)\
+	FIELD_PREP(DSM_MAC_CFG_SEND_PAUSE_FRM_TWICE, x)
+#define DSM_MAC_CFG_SEND_PAUSE_FRM_TWICE_GET(x)\
+	FIELD_GET(DSM_MAC_CFG_SEND_PAUSE_FRM_TWICE, x)
+
+#define DSM_MAC_CFG_TX_PAUSE_XON_XOFF            BIT(0)
+#define DSM_MAC_CFG_TX_PAUSE_XON_XOFF_SET(x)\
+	FIELD_PREP(DSM_MAC_CFG_TX_PAUSE_XON_XOFF, x)
+#define DSM_MAC_CFG_TX_PAUSE_XON_XOFF_GET(x)\
+	FIELD_GET(DSM_MAC_CFG_TX_PAUSE_XON_XOFF, x)
+
+/*      DSM:CFG:MAC_ADDR_BASE_HIGH_CFG */
+#define DSM_MAC_ADDR_BASE_HIGH_CFG(r) __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 2700, r, 65, 4)
+
+#define DSM_MAC_ADDR_BASE_HIGH_CFG_MAC_ADDR_HIGH GENMASK(23, 0)
+#define DSM_MAC_ADDR_BASE_HIGH_CFG_MAC_ADDR_HIGH_SET(x)\
+	FIELD_PREP(DSM_MAC_ADDR_BASE_HIGH_CFG_MAC_ADDR_HIGH, x)
+#define DSM_MAC_ADDR_BASE_HIGH_CFG_MAC_ADDR_HIGH_GET(x)\
+	FIELD_GET(DSM_MAC_ADDR_BASE_HIGH_CFG_MAC_ADDR_HIGH, x)
+
+/*      DSM:CFG:MAC_ADDR_BASE_LOW_CFG */
+#define DSM_MAC_ADDR_BASE_LOW_CFG(r) __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 2960, r, 65, 4)
+
+#define DSM_MAC_ADDR_BASE_LOW_CFG_MAC_ADDR_LOW   GENMASK(23, 0)
+#define DSM_MAC_ADDR_BASE_LOW_CFG_MAC_ADDR_LOW_SET(x)\
+	FIELD_PREP(DSM_MAC_ADDR_BASE_LOW_CFG_MAC_ADDR_LOW, x)
+#define DSM_MAC_ADDR_BASE_LOW_CFG_MAC_ADDR_LOW_GET(x)\
+	FIELD_GET(DSM_MAC_ADDR_BASE_LOW_CFG_MAC_ADDR_LOW, x)
+
+/*      DSM:CFG:TAXI_CAL_CFG */
+#define DSM_TAXI_CAL_CFG(r)       __REG(TARGET_DSM, 0, 1, 20, 0, 1, 3528, 3224, r, 9, 4)
+
+#define DSM_TAXI_CAL_CFG_CAL_IDX                 GENMASK(20, 15)
+#define DSM_TAXI_CAL_CFG_CAL_IDX_SET(x)\
+	FIELD_PREP(DSM_TAXI_CAL_CFG_CAL_IDX, x)
+#define DSM_TAXI_CAL_CFG_CAL_IDX_GET(x)\
+	FIELD_GET(DSM_TAXI_CAL_CFG_CAL_IDX, x)
+
+#define DSM_TAXI_CAL_CFG_CAL_CUR_LEN             GENMASK(14, 9)
+#define DSM_TAXI_CAL_CFG_CAL_CUR_LEN_SET(x)\
+	FIELD_PREP(DSM_TAXI_CAL_CFG_CAL_CUR_LEN, x)
+#define DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(x)\
+	FIELD_GET(DSM_TAXI_CAL_CFG_CAL_CUR_LEN, x)
+
+#define DSM_TAXI_CAL_CFG_CAL_CUR_VAL             GENMASK(8, 5)
+#define DSM_TAXI_CAL_CFG_CAL_CUR_VAL_SET(x)\
+	FIELD_PREP(DSM_TAXI_CAL_CFG_CAL_CUR_VAL, x)
+#define DSM_TAXI_CAL_CFG_CAL_CUR_VAL_GET(x)\
+	FIELD_GET(DSM_TAXI_CAL_CFG_CAL_CUR_VAL, x)
+
+#define DSM_TAXI_CAL_CFG_CAL_PGM_VAL             GENMASK(4, 1)
+#define DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(x)\
+	FIELD_PREP(DSM_TAXI_CAL_CFG_CAL_PGM_VAL, x)
+#define DSM_TAXI_CAL_CFG_CAL_PGM_VAL_GET(x)\
+	FIELD_GET(DSM_TAXI_CAL_CFG_CAL_PGM_VAL, x)
+
+#define DSM_TAXI_CAL_CFG_CAL_PGM_ENA             BIT(0)
+#define DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(x)\
+	FIELD_PREP(DSM_TAXI_CAL_CFG_CAL_PGM_ENA, x)
+#define DSM_TAXI_CAL_CFG_CAL_PGM_ENA_GET(x)\
+	FIELD_GET(DSM_TAXI_CAL_CFG_CAL_PGM_ENA, x)
+
+/*      EACL:POL_CFG:POL_EACL_CFG */
+#define EACL_POL_EACL_CFG         __REG(TARGET_EACL, 0, 1, 150608, 0, 1, 780, 768, 0, 1, 4)
+
+#define EACL_POL_EACL_CFG_EACL_CNT_MARKED_AS_DROPPED BIT(5)
+#define EACL_POL_EACL_CFG_EACL_CNT_MARKED_AS_DROPPED_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_CNT_MARKED_AS_DROPPED, x)
+#define EACL_POL_EACL_CFG_EACL_CNT_MARKED_AS_DROPPED_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_CNT_MARKED_AS_DROPPED, x)
+
+#define EACL_POL_EACL_CFG_EACL_ALLOW_FP_COPY     BIT(4)
+#define EACL_POL_EACL_CFG_EACL_ALLOW_FP_COPY_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_ALLOW_FP_COPY, x)
+#define EACL_POL_EACL_CFG_EACL_ALLOW_FP_COPY_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_ALLOW_FP_COPY, x)
+
+#define EACL_POL_EACL_CFG_EACL_ALLOW_CPU_COPY    BIT(3)
+#define EACL_POL_EACL_CFG_EACL_ALLOW_CPU_COPY_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_ALLOW_CPU_COPY, x)
+#define EACL_POL_EACL_CFG_EACL_ALLOW_CPU_COPY_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_ALLOW_CPU_COPY, x)
+
+#define EACL_POL_EACL_CFG_EACL_FORCE_CLOSE       BIT(2)
+#define EACL_POL_EACL_CFG_EACL_FORCE_CLOSE_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_FORCE_CLOSE, x)
+#define EACL_POL_EACL_CFG_EACL_FORCE_CLOSE_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_FORCE_CLOSE, x)
+
+#define EACL_POL_EACL_CFG_EACL_FORCE_OPEN        BIT(1)
+#define EACL_POL_EACL_CFG_EACL_FORCE_OPEN_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_FORCE_OPEN, x)
+#define EACL_POL_EACL_CFG_EACL_FORCE_OPEN_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_FORCE_OPEN, x)
+
+#define EACL_POL_EACL_CFG_EACL_FORCE_INIT        BIT(0)
+#define EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(x)\
+	FIELD_PREP(EACL_POL_EACL_CFG_EACL_FORCE_INIT, x)
+#define EACL_POL_EACL_CFG_EACL_FORCE_INIT_GET(x)\
+	FIELD_GET(EACL_POL_EACL_CFG_EACL_FORCE_INIT, x)
+
+/*      EACL:RAM_CTRL:RAM_INIT */
+#define EACL_RAM_INIT             __REG(TARGET_EACL, 0, 1, 118736, 0, 1, 4, 0, 0, 1, 4)
+
+#define EACL_RAM_INIT_RAM_INIT                   BIT(1)
+#define EACL_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(EACL_RAM_INIT_RAM_INIT, x)
+#define EACL_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(EACL_RAM_INIT_RAM_INIT, x)
+
+#define EACL_RAM_INIT_RAM_CFG_HOOK               BIT(0)
+#define EACL_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(EACL_RAM_INIT_RAM_CFG_HOOK, x)
+#define EACL_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(EACL_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      DEVCPU_GCB:CHIP_REGS:CHIP_ID */
+#define GCB_CHIP_ID               __REG(TARGET_GCB, 0, 1, 0, 0, 1, 424, 0, 0, 1, 4)
+
+#define GCB_CHIP_ID_REV_ID                       GENMASK(31, 28)
+#define GCB_CHIP_ID_REV_ID_SET(x)\
+	FIELD_PREP(GCB_CHIP_ID_REV_ID, x)
+#define GCB_CHIP_ID_REV_ID_GET(x)\
+	FIELD_GET(GCB_CHIP_ID_REV_ID, x)
+
+#define GCB_CHIP_ID_PART_ID                      GENMASK(27, 12)
+#define GCB_CHIP_ID_PART_ID_SET(x)\
+	FIELD_PREP(GCB_CHIP_ID_PART_ID, x)
+#define GCB_CHIP_ID_PART_ID_GET(x)\
+	FIELD_GET(GCB_CHIP_ID_PART_ID, x)
+
+#define GCB_CHIP_ID_MFG_ID                       GENMASK(11, 1)
+#define GCB_CHIP_ID_MFG_ID_SET(x)\
+	FIELD_PREP(GCB_CHIP_ID_MFG_ID, x)
+#define GCB_CHIP_ID_MFG_ID_GET(x)\
+	FIELD_GET(GCB_CHIP_ID_MFG_ID, x)
+
+#define GCB_CHIP_ID_ONE                          BIT(0)
+#define GCB_CHIP_ID_ONE_SET(x)\
+	FIELD_PREP(GCB_CHIP_ID_ONE, x)
+#define GCB_CHIP_ID_ONE_GET(x)\
+	FIELD_GET(GCB_CHIP_ID_ONE, x)
+
+/*      DEVCPU_GCB:CHIP_REGS:SOFT_RST */
+#define GCB_SOFT_RST              __REG(TARGET_GCB, 0, 1, 0, 0, 1, 424, 8, 0, 1, 4)
+
+#define GCB_SOFT_RST_SOFT_NON_CFG_RST            BIT(2)
+#define GCB_SOFT_RST_SOFT_NON_CFG_RST_SET(x)\
+	FIELD_PREP(GCB_SOFT_RST_SOFT_NON_CFG_RST, x)
+#define GCB_SOFT_RST_SOFT_NON_CFG_RST_GET(x)\
+	FIELD_GET(GCB_SOFT_RST_SOFT_NON_CFG_RST, x)
+
+#define GCB_SOFT_RST_SOFT_SWC_RST                BIT(1)
+#define GCB_SOFT_RST_SOFT_SWC_RST_SET(x)\
+	FIELD_PREP(GCB_SOFT_RST_SOFT_SWC_RST, x)
+#define GCB_SOFT_RST_SOFT_SWC_RST_GET(x)\
+	FIELD_GET(GCB_SOFT_RST_SOFT_SWC_RST, x)
+
+#define GCB_SOFT_RST_SOFT_CHIP_RST               BIT(0)
+#define GCB_SOFT_RST_SOFT_CHIP_RST_SET(x)\
+	FIELD_PREP(GCB_SOFT_RST_SOFT_CHIP_RST, x)
+#define GCB_SOFT_RST_SOFT_CHIP_RST_GET(x)\
+	FIELD_GET(GCB_SOFT_RST_SOFT_CHIP_RST, x)
+
+/*      DEVCPU_GCB:CHIP_REGS:HW_SGPIO_SD_CFG */
+#define GCB_HW_SGPIO_SD_CFG       __REG(TARGET_GCB, 0, 1, 0, 0, 1, 424, 20, 0, 1, 4)
+
+#define GCB_HW_SGPIO_SD_CFG_SD_HIGH_ENA          BIT(1)
+#define GCB_HW_SGPIO_SD_CFG_SD_HIGH_ENA_SET(x)\
+	FIELD_PREP(GCB_HW_SGPIO_SD_CFG_SD_HIGH_ENA, x)
+#define GCB_HW_SGPIO_SD_CFG_SD_HIGH_ENA_GET(x)\
+	FIELD_GET(GCB_HW_SGPIO_SD_CFG_SD_HIGH_ENA, x)
+
+#define GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL           BIT(0)
+#define GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL_SET(x)\
+	FIELD_PREP(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL, x)
+#define GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL_GET(x)\
+	FIELD_GET(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL, x)
+
+/*      DEVCPU_GCB:CHIP_REGS:HW_SGPIO_TO_SD_MAP_CFG */
+#define GCB_HW_SGPIO_TO_SD_MAP_CFG(r) __REG(TARGET_GCB, 0, 1, 0, 0, 1, 424, 24, r, 65, 4)
+
+#define GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL GENMASK(8, 0)
+#define GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL_SET(x)\
+	FIELD_PREP(GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL, x)
+#define GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL_GET(x)\
+	FIELD_GET(GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL, x)
+
+/*      DEVCPU_GCB:SIO_CTRL:SIO_CLOCK */
+#define GCB_SIO_CLOCK(g)          __REG(TARGET_GCB, 0, 1, 876, g, 3, 280, 20, 0, 1, 4)
+
+#define GCB_SIO_CLOCK_SIO_CLK_FREQ               GENMASK(19, 8)
+#define GCB_SIO_CLOCK_SIO_CLK_FREQ_SET(x)\
+	FIELD_PREP(GCB_SIO_CLOCK_SIO_CLK_FREQ, x)
+#define GCB_SIO_CLOCK_SIO_CLK_FREQ_GET(x)\
+	FIELD_GET(GCB_SIO_CLOCK_SIO_CLK_FREQ, x)
+
+#define GCB_SIO_CLOCK_SYS_CLK_PERIOD             GENMASK(7, 0)
+#define GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(x)\
+	FIELD_PREP(GCB_SIO_CLOCK_SYS_CLK_PERIOD, x)
+#define GCB_SIO_CLOCK_SYS_CLK_PERIOD_GET(x)\
+	FIELD_GET(GCB_SIO_CLOCK_SYS_CLK_PERIOD, x)
+
+/*      HSCH:HSCH_MISC:SYS_CLK_PER */
+#define HSCH_SYS_CLK_PER          __REG(TARGET_HSCH, 0, 1, 163104, 0, 1, 648, 640, 0, 1, 4)
+
+#define HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS       GENMASK(7, 0)
+#define HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS_SET(x)\
+	FIELD_PREP(HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS, x)
+#define HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS_GET(x)\
+	FIELD_GET(HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS, x)
+
+/*      HSCH:SYSTEM:FLUSH_CTRL */
+#define HSCH_FLUSH_CTRL           __REG(TARGET_HSCH, 0, 1, 184000, 0, 1, 312, 4, 0, 1, 4)
+
+#define HSCH_FLUSH_CTRL_FLUSH_ENA                BIT(27)
+#define HSCH_FLUSH_CTRL_FLUSH_ENA_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_ENA, x)
+#define HSCH_FLUSH_CTRL_FLUSH_ENA_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_ENA, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_SRC                BIT(26)
+#define HSCH_FLUSH_CTRL_FLUSH_SRC_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_SRC, x)
+#define HSCH_FLUSH_CTRL_FLUSH_SRC_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_SRC, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_DST                BIT(25)
+#define HSCH_FLUSH_CTRL_FLUSH_DST_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_DST, x)
+#define HSCH_FLUSH_CTRL_FLUSH_DST_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_DST, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_PORT               GENMASK(24, 18)
+#define HSCH_FLUSH_CTRL_FLUSH_PORT_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_PORT, x)
+#define HSCH_FLUSH_CTRL_FLUSH_PORT_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_PORT, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_QUEUE              BIT(17)
+#define HSCH_FLUSH_CTRL_FLUSH_QUEUE_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_QUEUE, x)
+#define HSCH_FLUSH_CTRL_FLUSH_QUEUE_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_QUEUE, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_SE                 BIT(16)
+#define HSCH_FLUSH_CTRL_FLUSH_SE_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_SE, x)
+#define HSCH_FLUSH_CTRL_FLUSH_SE_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_SE, x)
+
+#define HSCH_FLUSH_CTRL_FLUSH_HIER               GENMASK(15, 0)
+#define HSCH_FLUSH_CTRL_FLUSH_HIER_SET(x)\
+	FIELD_PREP(HSCH_FLUSH_CTRL_FLUSH_HIER, x)
+#define HSCH_FLUSH_CTRL_FLUSH_HIER_GET(x)\
+	FIELD_GET(HSCH_FLUSH_CTRL_FLUSH_HIER, x)
+
+/*      HSCH:SYSTEM:PORT_MODE */
+#define HSCH_PORT_MODE(r)         __REG(TARGET_HSCH, 0, 1, 184000, 0, 1, 312, 8, r, 70, 4)
+
+#define HSCH_PORT_MODE_DEQUEUE_DIS               BIT(4)
+#define HSCH_PORT_MODE_DEQUEUE_DIS_SET(x)\
+	FIELD_PREP(HSCH_PORT_MODE_DEQUEUE_DIS, x)
+#define HSCH_PORT_MODE_DEQUEUE_DIS_GET(x)\
+	FIELD_GET(HSCH_PORT_MODE_DEQUEUE_DIS, x)
+
+#define HSCH_PORT_MODE_AGE_DIS                   BIT(3)
+#define HSCH_PORT_MODE_AGE_DIS_SET(x)\
+	FIELD_PREP(HSCH_PORT_MODE_AGE_DIS, x)
+#define HSCH_PORT_MODE_AGE_DIS_GET(x)\
+	FIELD_GET(HSCH_PORT_MODE_AGE_DIS, x)
+
+#define HSCH_PORT_MODE_TRUNC_ENA                 BIT(2)
+#define HSCH_PORT_MODE_TRUNC_ENA_SET(x)\
+	FIELD_PREP(HSCH_PORT_MODE_TRUNC_ENA, x)
+#define HSCH_PORT_MODE_TRUNC_ENA_GET(x)\
+	FIELD_GET(HSCH_PORT_MODE_TRUNC_ENA, x)
+
+#define HSCH_PORT_MODE_EIR_REMARK_ENA            BIT(1)
+#define HSCH_PORT_MODE_EIR_REMARK_ENA_SET(x)\
+	FIELD_PREP(HSCH_PORT_MODE_EIR_REMARK_ENA, x)
+#define HSCH_PORT_MODE_EIR_REMARK_ENA_GET(x)\
+	FIELD_GET(HSCH_PORT_MODE_EIR_REMARK_ENA, x)
+
+#define HSCH_PORT_MODE_CPU_PRIO_MODE             BIT(0)
+#define HSCH_PORT_MODE_CPU_PRIO_MODE_SET(x)\
+	FIELD_PREP(HSCH_PORT_MODE_CPU_PRIO_MODE, x)
+#define HSCH_PORT_MODE_CPU_PRIO_MODE_GET(x)\
+	FIELD_GET(HSCH_PORT_MODE_CPU_PRIO_MODE, x)
+
+/*      HSCH:SYSTEM:OUTB_SHARE_ENA */
+#define HSCH_OUTB_SHARE_ENA(r)    __REG(TARGET_HSCH, 0, 1, 184000, 0, 1, 312, 288, r, 5, 4)
+
+#define HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA       GENMASK(7, 0)
+#define HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(x)\
+	FIELD_PREP(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA, x)
+#define HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_GET(x)\
+	FIELD_GET(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA, x)
+
+/*      HSCH:MMGT:RESET_CFG */
+#define HSCH_RESET_CFG            __REG(TARGET_HSCH, 0, 1, 162368, 0, 1, 16, 8, 0, 1, 4)
+
+#define HSCH_RESET_CFG_CORE_ENA                  BIT(0)
+#define HSCH_RESET_CFG_CORE_ENA_SET(x)\
+	FIELD_PREP(HSCH_RESET_CFG_CORE_ENA, x)
+#define HSCH_RESET_CFG_CORE_ENA_GET(x)\
+	FIELD_GET(HSCH_RESET_CFG_CORE_ENA, x)
+
+/*      HSCH:TAS_CONFIG:TAS_STATEMACHINE_CFG */
+#define HSCH_TAS_STATEMACHINE_CFG __REG(TARGET_HSCH, 0, 1, 162384, 0, 1, 12, 8, 0, 1, 4)
+
+#define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY    GENMASK(7, 0)
+#define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_SET(x)\
+	FIELD_PREP(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY, x)
+#define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_GET(x)\
+	FIELD_GET(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY, x)
+
+/*      LRN:COMMON:COMMON_ACCESS_CTRL */
+#define LRN_COMMON_ACCESS_CTRL    __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 0, 0, 1, 4)
+
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_COL GENMASK(21, 20)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_COL_SET(x)\
+	FIELD_PREP(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_COL, x)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_COL_GET(x)\
+	FIELD_GET(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_COL, x)
+
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_TYPE BIT(19)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_TYPE_SET(x)\
+	FIELD_PREP(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_TYPE, x)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_TYPE_GET(x)\
+	FIELD_GET(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_TYPE, x)
+
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW GENMASK(18, 5)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW_SET(x)\
+	FIELD_PREP(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW, x)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW_GET(x)\
+	FIELD_GET(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW, x)
+
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD    GENMASK(4, 1)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(x)\
+	FIELD_PREP(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD, x)
+#define LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_GET(x)\
+	FIELD_GET(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD, x)
+
+#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT BIT(0)
+#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(x)\
+	FIELD_PREP(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, x)
+#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(x)\
+	FIELD_GET(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, x)
+
+/*      LRN:COMMON:MAC_ACCESS_CFG_0 */
+#define LRN_MAC_ACCESS_CFG_0      __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 4, 0, 1, 4)
+
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_FID       GENMASK(28, 16)
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_FID_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_FID, x)
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_FID_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_FID, x)
+
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_MAC_MSB   GENMASK(15, 0)
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_MAC_MSB_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_MAC_MSB, x)
+#define LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_MAC_MSB_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_0_MAC_ENTRY_MAC_MSB, x)
+
+/*      LRN:COMMON:MAC_ACCESS_CFG_1 */
+#define LRN_MAC_ACCESS_CFG_1      __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 8, 0, 1, 4)
+
+/*      LRN:COMMON:MAC_ACCESS_CFG_2 */
+#define LRN_MAC_ACCESS_CFG_2      __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 12, 0, 1, 4)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_SRC_KILL_FWD BIT(28)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_SRC_KILL_FWD_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_SRC_KILL_FWD, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_SRC_KILL_FWD_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_SRC_KILL_FWD, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_NXT_LRN_ALL BIT(27)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_NXT_LRN_ALL_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_NXT_LRN_ALL, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_NXT_LRN_ALL_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_NXT_LRN_ALL, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_QU    GENMASK(26, 24)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_QU_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_QU, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_QU_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_QU, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_COPY  BIT(23)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_COPY_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_COPY, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_COPY_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_CPU_COPY, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLAN_IGNORE BIT(22)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLAN_IGNORE_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLAN_IGNORE, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLAN_IGNORE_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLAN_IGNORE, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_MIRROR    BIT(21)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_MIRROR_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_MIRROR, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_MIRROR_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_MIRROR, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_FLAG  GENMASK(20, 19)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_FLAG_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_FLAG, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_FLAG_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_FLAG, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_INTERVAL GENMASK(18, 17)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_INTERVAL_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_INTERVAL, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_INTERVAL_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_AGE_INTERVAL, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED    BIT(16)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD       BIT(15)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE GENMASK(14, 12)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE, x)
+
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR      GENMASK(11, 0)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR, x)
+#define LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR, x)
+
+/*      LRN:COMMON:MAC_ACCESS_CFG_3 */
+#define LRN_MAC_ACCESS_CFG_3      __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 16, 0, 1, 4)
+
+#define LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX GENMASK(10, 0)
+#define LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX_SET(x)\
+	FIELD_PREP(LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX, x)
+#define LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX_GET(x)\
+	FIELD_GET(LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX, x)
+
+/*      LRN:COMMON:SCAN_NEXT_CFG */
+#define LRN_SCAN_NEXT_CFG         __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 20, 0, 1, 4)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FLAG_UPDATE_SEL GENMASK(21, 19)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FLAG_UPDATE_SEL_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_AGE_FLAG_UPDATE_SEL, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FLAG_UPDATE_SEL_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_AGE_FLAG_UPDATE_SEL, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NXT_LRN_ALL_UPDATE_SEL GENMASK(18, 17)
+#define LRN_SCAN_NEXT_CFG_SCAN_NXT_LRN_ALL_UPDATE_SEL_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NXT_LRN_ALL_UPDATE_SEL, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NXT_LRN_ALL_UPDATE_SEL_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NXT_LRN_ALL_UPDATE_SEL, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FILTER_SEL    GENMASK(16, 15)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FILTER_SEL_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_AGE_FILTER_SEL, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_FILTER_SEL_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_AGE_FILTER_SEL, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_MOVE_FOUND_ENA BIT(14)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_MOVE_FOUND_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_MOVE_FOUND_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_MOVE_FOUND_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_MOVE_FOUND_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_NXT_LRN_ALL_FILTER_ENA BIT(13)
+#define LRN_SCAN_NEXT_CFG_NXT_LRN_ALL_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_NXT_LRN_ALL_FILTER_ENA, x)
+#define LRN_SCAN_NEXT_CFG_NXT_LRN_ALL_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_NXT_LRN_ALL_FILTER_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_USE_PORT_FILTER_ENA BIT(12)
+#define LRN_SCAN_NEXT_CFG_SCAN_USE_PORT_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_USE_PORT_FILTER_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_USE_PORT_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_USE_PORT_FILTER_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_REMOVE_FOUND_ENA BIT(11)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_REMOVE_FOUND_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_REMOVE_FOUND_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_REMOVE_FOUND_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_REMOVE_FOUND_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA BIT(10)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_INC_AGE_BITS_ENA BIT(9)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_INC_AGE_BITS_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_INC_AGE_BITS_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_INC_AGE_BITS_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_INC_AGE_BITS_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_AGED_ONLY_ENA BIT(8)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_AGED_ONLY_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_AGED_ONLY_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_AGED_ONLY_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_AGED_ONLY_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA BIT(7)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_INTERVAL_MASK GENMASK(6, 3)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_INTERVAL_MASK_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_SCAN_AGE_INTERVAL_MASK, x)
+#define LRN_SCAN_NEXT_CFG_SCAN_AGE_INTERVAL_MASK_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_SCAN_AGE_INTERVAL_MASK, x)
+
+#define LRN_SCAN_NEXT_CFG_ISDX_LIMIT_IDX_FILTER_ENA BIT(2)
+#define LRN_SCAN_NEXT_CFG_ISDX_LIMIT_IDX_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_ISDX_LIMIT_IDX_FILTER_ENA, x)
+#define LRN_SCAN_NEXT_CFG_ISDX_LIMIT_IDX_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_ISDX_LIMIT_IDX_FILTER_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_FID_FILTER_ENA         BIT(1)
+#define LRN_SCAN_NEXT_CFG_FID_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_FID_FILTER_ENA, x)
+#define LRN_SCAN_NEXT_CFG_FID_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_FID_FILTER_ENA, x)
+
+#define LRN_SCAN_NEXT_CFG_ADDR_FILTER_ENA        BIT(0)
+#define LRN_SCAN_NEXT_CFG_ADDR_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_ADDR_FILTER_ENA, x)
+#define LRN_SCAN_NEXT_CFG_ADDR_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_ADDR_FILTER_ENA, x)
+
+/*      LRN:COMMON:SCAN_NEXT_CFG_1 */
+#define LRN_SCAN_NEXT_CFG_1       __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 24, 0, 1, 4)
+
+#define LRN_SCAN_NEXT_CFG_1_PORT_MOVE_NEW_ADDR   GENMASK(30, 16)
+#define LRN_SCAN_NEXT_CFG_1_PORT_MOVE_NEW_ADDR_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_1_PORT_MOVE_NEW_ADDR, x)
+#define LRN_SCAN_NEXT_CFG_1_PORT_MOVE_NEW_ADDR_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_1_PORT_MOVE_NEW_ADDR, x)
+
+#define LRN_SCAN_NEXT_CFG_1_SCAN_ENTRY_ADDR_MASK GENMASK(14, 0)
+#define LRN_SCAN_NEXT_CFG_1_SCAN_ENTRY_ADDR_MASK_SET(x)\
+	FIELD_PREP(LRN_SCAN_NEXT_CFG_1_SCAN_ENTRY_ADDR_MASK, x)
+#define LRN_SCAN_NEXT_CFG_1_SCAN_ENTRY_ADDR_MASK_GET(x)\
+	FIELD_GET(LRN_SCAN_NEXT_CFG_1_SCAN_ENTRY_ADDR_MASK, x)
+
+/*      LRN:COMMON:AUTOAGE_CFG */
+#define LRN_AUTOAGE_CFG(r)        __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 36, r, 4, 4)
+
+#define LRN_AUTOAGE_CFG_UNIT_SIZE                GENMASK(29, 28)
+#define LRN_AUTOAGE_CFG_UNIT_SIZE_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_UNIT_SIZE, x)
+#define LRN_AUTOAGE_CFG_UNIT_SIZE_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_UNIT_SIZE, x)
+
+#define LRN_AUTOAGE_CFG_PERIOD_VAL               GENMASK(27, 0)
+#define LRN_AUTOAGE_CFG_PERIOD_VAL_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_PERIOD_VAL, x)
+#define LRN_AUTOAGE_CFG_PERIOD_VAL_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_PERIOD_VAL, x)
+
+/*      LRN:COMMON:AUTOAGE_CFG_1 */
+#define LRN_AUTOAGE_CFG_1         __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 52, 0, 1, 4)
+
+#define LRN_AUTOAGE_CFG_1_PAUSE_AUTO_AGE_ENA     BIT(25)
+#define LRN_AUTOAGE_CFG_1_PAUSE_AUTO_AGE_ENA_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_PAUSE_AUTO_AGE_ENA, x)
+#define LRN_AUTOAGE_CFG_1_PAUSE_AUTO_AGE_ENA_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_PAUSE_AUTO_AGE_ENA, x)
+
+#define LRN_AUTOAGE_CFG_1_CELLS_BETWEEN_ENTRY_SCAN GENMASK(24, 15)
+#define LRN_AUTOAGE_CFG_1_CELLS_BETWEEN_ENTRY_SCAN_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_CELLS_BETWEEN_ENTRY_SCAN, x)
+#define LRN_AUTOAGE_CFG_1_CELLS_BETWEEN_ENTRY_SCAN_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_CELLS_BETWEEN_ENTRY_SCAN, x)
+
+#define LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS        GENMASK(14, 7)
+#define LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS, x)
+#define LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_CLK_PERIOD_01NS, x)
+
+#define LRN_AUTOAGE_CFG_1_USE_PORT_FILTER_ENA    BIT(6)
+#define LRN_AUTOAGE_CFG_1_USE_PORT_FILTER_ENA_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_USE_PORT_FILTER_ENA, x)
+#define LRN_AUTOAGE_CFG_1_USE_PORT_FILTER_ENA_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_USE_PORT_FILTER_ENA, x)
+
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_SHOT     GENMASK(5, 2)
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_SHOT_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_SHOT, x)
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_SHOT_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_SHOT, x)
+
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_STOP_SHOT BIT(1)
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_STOP_SHOT_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_STOP_SHOT, x)
+#define LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_STOP_SHOT_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_FORCE_HW_SCAN_STOP_SHOT, x)
+
+#define LRN_AUTOAGE_CFG_1_FORCE_IDLE_ENA         BIT(0)
+#define LRN_AUTOAGE_CFG_1_FORCE_IDLE_ENA_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_1_FORCE_IDLE_ENA, x)
+#define LRN_AUTOAGE_CFG_1_FORCE_IDLE_ENA_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_1_FORCE_IDLE_ENA, x)
+
+/*      LRN:COMMON:AUTOAGE_CFG_2 */
+#define LRN_AUTOAGE_CFG_2         __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 56, 0, 1, 4)
+
+#define LRN_AUTOAGE_CFG_2_NEXT_ROW               GENMASK(17, 4)
+#define LRN_AUTOAGE_CFG_2_NEXT_ROW_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_2_NEXT_ROW, x)
+#define LRN_AUTOAGE_CFG_2_NEXT_ROW_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_2_NEXT_ROW, x)
+
+#define LRN_AUTOAGE_CFG_2_SCAN_ONGOING_STATUS    GENMASK(3, 0)
+#define LRN_AUTOAGE_CFG_2_SCAN_ONGOING_STATUS_SET(x)\
+	FIELD_PREP(LRN_AUTOAGE_CFG_2_SCAN_ONGOING_STATUS, x)
+#define LRN_AUTOAGE_CFG_2_SCAN_ONGOING_STATUS_GET(x)\
+	FIELD_GET(LRN_AUTOAGE_CFG_2_SCAN_ONGOING_STATUS, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_CFG */
+#define PCS10G_BR_PCS_CFG(t)      __REG(TARGET_PCS10G_BR, t, 12, 0, 0, 1, 56, 0, 0, 1, 4)
+
+#define PCS10G_BR_PCS_CFG_PCS_ENA                BIT(31)
+#define PCS10G_BR_PCS_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_PCS_ENA, x)
+#define PCS10G_BR_PCS_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_PCS_ENA, x)
+
+#define PCS10G_BR_PCS_CFG_PMA_LOOPBACK_ENA       BIT(30)
+#define PCS10G_BR_PCS_CFG_PMA_LOOPBACK_ENA_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+#define PCS10G_BR_PCS_CFG_PMA_LOOPBACK_ENA_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+
+#define PCS10G_BR_PCS_CFG_SH_CNT_MAX             GENMASK(29, 24)
+#define PCS10G_BR_PCS_CFG_SH_CNT_MAX_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_SH_CNT_MAX, x)
+#define PCS10G_BR_PCS_CFG_SH_CNT_MAX_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_SH_CNT_MAX, x)
+
+#define PCS10G_BR_PCS_CFG_RX_DATA_FLIP           BIT(18)
+#define PCS10G_BR_PCS_CFG_RX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_RX_DATA_FLIP, x)
+#define PCS10G_BR_PCS_CFG_RX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_RX_DATA_FLIP, x)
+
+#define PCS10G_BR_PCS_CFG_RESYNC_ENA             BIT(15)
+#define PCS10G_BR_PCS_CFG_RESYNC_ENA_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_RESYNC_ENA, x)
+#define PCS10G_BR_PCS_CFG_RESYNC_ENA_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_RESYNC_ENA, x)
+
+#define PCS10G_BR_PCS_CFG_LF_GEN_DIS             BIT(14)
+#define PCS10G_BR_PCS_CFG_LF_GEN_DIS_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_LF_GEN_DIS, x)
+#define PCS10G_BR_PCS_CFG_LF_GEN_DIS_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_LF_GEN_DIS, x)
+
+#define PCS10G_BR_PCS_CFG_RX_TEST_MODE           BIT(13)
+#define PCS10G_BR_PCS_CFG_RX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_RX_TEST_MODE, x)
+#define PCS10G_BR_PCS_CFG_RX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_RX_TEST_MODE, x)
+
+#define PCS10G_BR_PCS_CFG_RX_SCR_DISABLE         BIT(12)
+#define PCS10G_BR_PCS_CFG_RX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+#define PCS10G_BR_PCS_CFG_RX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+
+#define PCS10G_BR_PCS_CFG_TX_DATA_FLIP           BIT(7)
+#define PCS10G_BR_PCS_CFG_TX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_TX_DATA_FLIP, x)
+#define PCS10G_BR_PCS_CFG_TX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_TX_DATA_FLIP, x)
+
+#define PCS10G_BR_PCS_CFG_AN_LINK_CTRL_ENA       BIT(6)
+#define PCS10G_BR_PCS_CFG_AN_LINK_CTRL_ENA_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+#define PCS10G_BR_PCS_CFG_AN_LINK_CTRL_ENA_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+
+#define PCS10G_BR_PCS_CFG_TX_TEST_MODE           BIT(4)
+#define PCS10G_BR_PCS_CFG_TX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_TX_TEST_MODE, x)
+#define PCS10G_BR_PCS_CFG_TX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_TX_TEST_MODE, x)
+
+#define PCS10G_BR_PCS_CFG_TX_SCR_DISABLE         BIT(3)
+#define PCS10G_BR_PCS_CFG_TX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+#define PCS10G_BR_PCS_CFG_TX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_SD_CFG */
+#define PCS10G_BR_PCS_SD_CFG(t)   __REG(TARGET_PCS10G_BR, t, 12, 0, 0, 1, 56, 4, 0, 1, 4)
+
+#define PCS10G_BR_PCS_SD_CFG_SD_SEL              BIT(8)
+#define PCS10G_BR_PCS_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_SD_CFG_SD_SEL, x)
+#define PCS10G_BR_PCS_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_SD_CFG_SD_SEL, x)
+
+#define PCS10G_BR_PCS_SD_CFG_SD_POL              BIT(4)
+#define PCS10G_BR_PCS_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_SD_CFG_SD_POL, x)
+#define PCS10G_BR_PCS_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_SD_CFG_SD_POL, x)
+
+#define PCS10G_BR_PCS_SD_CFG_SD_ENA              BIT(0)
+#define PCS10G_BR_PCS_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(PCS10G_BR_PCS_SD_CFG_SD_ENA, x)
+#define PCS10G_BR_PCS_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(PCS10G_BR_PCS_SD_CFG_SD_ENA, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_CFG */
+#define PCS25G_BR_PCS_CFG(t)      __REG(TARGET_PCS25G_BR, t, 8, 0, 0, 1, 56, 0, 0, 1, 4)
+
+#define PCS25G_BR_PCS_CFG_PCS_ENA                BIT(31)
+#define PCS25G_BR_PCS_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_PCS_ENA, x)
+#define PCS25G_BR_PCS_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_PCS_ENA, x)
+
+#define PCS25G_BR_PCS_CFG_PMA_LOOPBACK_ENA       BIT(30)
+#define PCS25G_BR_PCS_CFG_PMA_LOOPBACK_ENA_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+#define PCS25G_BR_PCS_CFG_PMA_LOOPBACK_ENA_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+
+#define PCS25G_BR_PCS_CFG_SH_CNT_MAX             GENMASK(29, 24)
+#define PCS25G_BR_PCS_CFG_SH_CNT_MAX_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_SH_CNT_MAX, x)
+#define PCS25G_BR_PCS_CFG_SH_CNT_MAX_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_SH_CNT_MAX, x)
+
+#define PCS25G_BR_PCS_CFG_RX_DATA_FLIP           BIT(18)
+#define PCS25G_BR_PCS_CFG_RX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_RX_DATA_FLIP, x)
+#define PCS25G_BR_PCS_CFG_RX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_RX_DATA_FLIP, x)
+
+#define PCS25G_BR_PCS_CFG_RESYNC_ENA             BIT(15)
+#define PCS25G_BR_PCS_CFG_RESYNC_ENA_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_RESYNC_ENA, x)
+#define PCS25G_BR_PCS_CFG_RESYNC_ENA_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_RESYNC_ENA, x)
+
+#define PCS25G_BR_PCS_CFG_LF_GEN_DIS             BIT(14)
+#define PCS25G_BR_PCS_CFG_LF_GEN_DIS_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_LF_GEN_DIS, x)
+#define PCS25G_BR_PCS_CFG_LF_GEN_DIS_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_LF_GEN_DIS, x)
+
+#define PCS25G_BR_PCS_CFG_RX_TEST_MODE           BIT(13)
+#define PCS25G_BR_PCS_CFG_RX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_RX_TEST_MODE, x)
+#define PCS25G_BR_PCS_CFG_RX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_RX_TEST_MODE, x)
+
+#define PCS25G_BR_PCS_CFG_RX_SCR_DISABLE         BIT(12)
+#define PCS25G_BR_PCS_CFG_RX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+#define PCS25G_BR_PCS_CFG_RX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+
+#define PCS25G_BR_PCS_CFG_TX_DATA_FLIP           BIT(7)
+#define PCS25G_BR_PCS_CFG_TX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_TX_DATA_FLIP, x)
+#define PCS25G_BR_PCS_CFG_TX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_TX_DATA_FLIP, x)
+
+#define PCS25G_BR_PCS_CFG_AN_LINK_CTRL_ENA       BIT(6)
+#define PCS25G_BR_PCS_CFG_AN_LINK_CTRL_ENA_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+#define PCS25G_BR_PCS_CFG_AN_LINK_CTRL_ENA_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+
+#define PCS25G_BR_PCS_CFG_TX_TEST_MODE           BIT(4)
+#define PCS25G_BR_PCS_CFG_TX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_TX_TEST_MODE, x)
+#define PCS25G_BR_PCS_CFG_TX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_TX_TEST_MODE, x)
+
+#define PCS25G_BR_PCS_CFG_TX_SCR_DISABLE         BIT(3)
+#define PCS25G_BR_PCS_CFG_TX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+#define PCS25G_BR_PCS_CFG_TX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_SD_CFG */
+#define PCS25G_BR_PCS_SD_CFG(t)   __REG(TARGET_PCS25G_BR, t, 8, 0, 0, 1, 56, 4, 0, 1, 4)
+
+#define PCS25G_BR_PCS_SD_CFG_SD_SEL              BIT(8)
+#define PCS25G_BR_PCS_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_SD_CFG_SD_SEL, x)
+#define PCS25G_BR_PCS_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_SD_CFG_SD_SEL, x)
+
+#define PCS25G_BR_PCS_SD_CFG_SD_POL              BIT(4)
+#define PCS25G_BR_PCS_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_SD_CFG_SD_POL, x)
+#define PCS25G_BR_PCS_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_SD_CFG_SD_POL, x)
+
+#define PCS25G_BR_PCS_SD_CFG_SD_ENA              BIT(0)
+#define PCS25G_BR_PCS_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(PCS25G_BR_PCS_SD_CFG_SD_ENA, x)
+#define PCS25G_BR_PCS_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(PCS25G_BR_PCS_SD_CFG_SD_ENA, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_CFG */
+#define PCS5G_BR_PCS_CFG(t)       __REG(TARGET_PCS5G_BR, t, 13, 0, 0, 1, 56, 0, 0, 1, 4)
+
+#define PCS5G_BR_PCS_CFG_PCS_ENA                 BIT(31)
+#define PCS5G_BR_PCS_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_PCS_ENA, x)
+#define PCS5G_BR_PCS_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_PCS_ENA, x)
+
+#define PCS5G_BR_PCS_CFG_PMA_LOOPBACK_ENA        BIT(30)
+#define PCS5G_BR_PCS_CFG_PMA_LOOPBACK_ENA_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+#define PCS5G_BR_PCS_CFG_PMA_LOOPBACK_ENA_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_PMA_LOOPBACK_ENA, x)
+
+#define PCS5G_BR_PCS_CFG_SH_CNT_MAX              GENMASK(29, 24)
+#define PCS5G_BR_PCS_CFG_SH_CNT_MAX_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_SH_CNT_MAX, x)
+#define PCS5G_BR_PCS_CFG_SH_CNT_MAX_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_SH_CNT_MAX, x)
+
+#define PCS5G_BR_PCS_CFG_RX_DATA_FLIP            BIT(18)
+#define PCS5G_BR_PCS_CFG_RX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_RX_DATA_FLIP, x)
+#define PCS5G_BR_PCS_CFG_RX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_RX_DATA_FLIP, x)
+
+#define PCS5G_BR_PCS_CFG_RESYNC_ENA              BIT(15)
+#define PCS5G_BR_PCS_CFG_RESYNC_ENA_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_RESYNC_ENA, x)
+#define PCS5G_BR_PCS_CFG_RESYNC_ENA_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_RESYNC_ENA, x)
+
+#define PCS5G_BR_PCS_CFG_LF_GEN_DIS              BIT(14)
+#define PCS5G_BR_PCS_CFG_LF_GEN_DIS_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_LF_GEN_DIS, x)
+#define PCS5G_BR_PCS_CFG_LF_GEN_DIS_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_LF_GEN_DIS, x)
+
+#define PCS5G_BR_PCS_CFG_RX_TEST_MODE            BIT(13)
+#define PCS5G_BR_PCS_CFG_RX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_RX_TEST_MODE, x)
+#define PCS5G_BR_PCS_CFG_RX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_RX_TEST_MODE, x)
+
+#define PCS5G_BR_PCS_CFG_RX_SCR_DISABLE          BIT(12)
+#define PCS5G_BR_PCS_CFG_RX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+#define PCS5G_BR_PCS_CFG_RX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_RX_SCR_DISABLE, x)
+
+#define PCS5G_BR_PCS_CFG_TX_DATA_FLIP            BIT(7)
+#define PCS5G_BR_PCS_CFG_TX_DATA_FLIP_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_TX_DATA_FLIP, x)
+#define PCS5G_BR_PCS_CFG_TX_DATA_FLIP_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_TX_DATA_FLIP, x)
+
+#define PCS5G_BR_PCS_CFG_AN_LINK_CTRL_ENA        BIT(6)
+#define PCS5G_BR_PCS_CFG_AN_LINK_CTRL_ENA_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+#define PCS5G_BR_PCS_CFG_AN_LINK_CTRL_ENA_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_AN_LINK_CTRL_ENA, x)
+
+#define PCS5G_BR_PCS_CFG_TX_TEST_MODE            BIT(4)
+#define PCS5G_BR_PCS_CFG_TX_TEST_MODE_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_TX_TEST_MODE, x)
+#define PCS5G_BR_PCS_CFG_TX_TEST_MODE_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_TX_TEST_MODE, x)
+
+#define PCS5G_BR_PCS_CFG_TX_SCR_DISABLE          BIT(3)
+#define PCS5G_BR_PCS_CFG_TX_SCR_DISABLE_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+#define PCS5G_BR_PCS_CFG_TX_SCR_DISABLE_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_CFG_TX_SCR_DISABLE, x)
+
+/*      PCS_10GBASE_R:PCS_10GBR_CFG:PCS_SD_CFG */
+#define PCS5G_BR_PCS_SD_CFG(t)    __REG(TARGET_PCS5G_BR, t, 13, 0, 0, 1, 56, 4, 0, 1, 4)
+
+#define PCS5G_BR_PCS_SD_CFG_SD_SEL               BIT(8)
+#define PCS5G_BR_PCS_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_SD_CFG_SD_SEL, x)
+#define PCS5G_BR_PCS_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_SD_CFG_SD_SEL, x)
+
+#define PCS5G_BR_PCS_SD_CFG_SD_POL               BIT(4)
+#define PCS5G_BR_PCS_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_SD_CFG_SD_POL, x)
+#define PCS5G_BR_PCS_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_SD_CFG_SD_POL, x)
+
+#define PCS5G_BR_PCS_SD_CFG_SD_ENA               BIT(0)
+#define PCS5G_BR_PCS_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(PCS5G_BR_PCS_SD_CFG_SD_ENA, x)
+#define PCS5G_BR_PCS_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(PCS5G_BR_PCS_SD_CFG_SD_ENA, x)
+
+/*      PORT_CONF:HW_CFG:DEV5G_MODES */
+#define PORT_CONF_DEV5G_MODES     __REG(TARGET_PORT_CONF, 0, 1, 0, 0, 1, 24, 0, 0, 1, 4)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D0_MODE      BIT(0)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D0_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D0_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D0_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D0_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D1_MODE      BIT(1)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D1_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D1_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D1_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D1_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D2_MODE      BIT(2)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D2_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D2_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D2_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D2_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D3_MODE      BIT(3)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D3_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D3_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D3_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D3_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D4_MODE      BIT(4)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D4_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D4_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D4_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D4_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D5_MODE      BIT(5)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D5_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D5_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D5_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D5_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D6_MODE      BIT(6)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D6_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D6_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D6_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D6_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D7_MODE      BIT(7)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D7_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D7_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D7_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D7_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D8_MODE      BIT(8)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D8_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D8_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D8_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D8_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D9_MODE      BIT(9)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D9_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D9_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D9_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D9_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D10_MODE     BIT(10)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D10_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D10_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D10_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D10_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D11_MODE     BIT(11)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D11_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D11_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D11_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D11_MODE, x)
+
+#define PORT_CONF_DEV5G_MODES_DEV5G_D64_MODE     BIT(12)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D64_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV5G_MODES_DEV5G_D64_MODE, x)
+#define PORT_CONF_DEV5G_MODES_DEV5G_D64_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV5G_MODES_DEV5G_D64_MODE, x)
+
+/*      PORT_CONF:HW_CFG:DEV10G_MODES */
+#define PORT_CONF_DEV10G_MODES    __REG(TARGET_PORT_CONF, 0, 1, 0, 0, 1, 24, 4, 0, 1, 4)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D12_MODE   BIT(0)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D12_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D12_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D12_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D12_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D13_MODE   BIT(1)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D13_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D13_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D13_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D13_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D14_MODE   BIT(2)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D14_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D14_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D14_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D14_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D15_MODE   BIT(3)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D15_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D15_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D15_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D15_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D48_MODE   BIT(4)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D48_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D48_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D48_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D48_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D49_MODE   BIT(5)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D49_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D49_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D49_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D49_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D50_MODE   BIT(6)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D50_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D50_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D50_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D50_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D51_MODE   BIT(7)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D51_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D51_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D51_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D51_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D52_MODE   BIT(8)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D52_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D52_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D52_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D52_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D53_MODE   BIT(9)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D53_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D53_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D53_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D53_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D54_MODE   BIT(10)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D54_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D54_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D54_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D54_MODE, x)
+
+#define PORT_CONF_DEV10G_MODES_DEV10G_D55_MODE   BIT(11)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D55_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV10G_MODES_DEV10G_D55_MODE, x)
+#define PORT_CONF_DEV10G_MODES_DEV10G_D55_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV10G_MODES_DEV10G_D55_MODE, x)
+
+/*      PORT_CONF:HW_CFG:DEV25G_MODES */
+#define PORT_CONF_DEV25G_MODES    __REG(TARGET_PORT_CONF, 0, 1, 0, 0, 1, 24, 8, 0, 1, 4)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D56_MODE   BIT(0)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D56_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D56_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D56_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D56_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D57_MODE   BIT(1)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D57_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D57_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D57_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D57_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D58_MODE   BIT(2)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D58_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D58_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D58_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D58_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D59_MODE   BIT(3)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D59_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D59_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D59_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D59_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D60_MODE   BIT(4)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D60_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D60_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D60_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D60_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D61_MODE   BIT(5)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D61_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D61_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D61_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D61_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D62_MODE   BIT(6)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D62_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D62_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D62_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D62_MODE, x)
+
+#define PORT_CONF_DEV25G_MODES_DEV25G_D63_MODE   BIT(7)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D63_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_DEV25G_MODES_DEV25G_D63_MODE, x)
+#define PORT_CONF_DEV25G_MODES_DEV25G_D63_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_DEV25G_MODES_DEV25G_D63_MODE, x)
+
+/*      PORT_CONF:HW_CFG:QSGMII_ENA */
+#define PORT_CONF_QSGMII_ENA      __REG(TARGET_PORT_CONF, 0, 1, 0, 0, 1, 24, 12, 0, 1, 4)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_0        BIT(0)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_0_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_0, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_0_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_0, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_1        BIT(1)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_1_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_1, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_1_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_1, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_2        BIT(2)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_2_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_2, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_2_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_2, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_3        BIT(3)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_3_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_3, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_3_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_3, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_4        BIT(4)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_4_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_4, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_4_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_4, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_5        BIT(5)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_5_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_5, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_5_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_5, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_6        BIT(6)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_6_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_6, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_6_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_6, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_7        BIT(7)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_7_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_7, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_7_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_7, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_8        BIT(8)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_8_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_8, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_8_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_8, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_9        BIT(9)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_9_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_9, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_9_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_9, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_10       BIT(10)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_10_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_10, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_10_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_10, x)
+
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_11       BIT(11)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_11_SET(x)\
+	FIELD_PREP(PORT_CONF_QSGMII_ENA_QSGMII_ENA_11, x)
+#define PORT_CONF_QSGMII_ENA_QSGMII_ENA_11_GET(x)\
+	FIELD_GET(PORT_CONF_QSGMII_ENA_QSGMII_ENA_11, x)
+
+/*      PORT_CONF:USGMII_CFG_STAT:USGMII_CFG */
+#define PORT_CONF_USGMII_CFG(g)   __REG(TARGET_PORT_CONF, 0, 1, 72, g, 6, 8, 0, 0, 1, 4)
+
+#define PORT_CONF_USGMII_CFG_BYPASS_SCRAM        BIT(9)
+#define PORT_CONF_USGMII_CFG_BYPASS_SCRAM_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_BYPASS_SCRAM, x)
+#define PORT_CONF_USGMII_CFG_BYPASS_SCRAM_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_BYPASS_SCRAM, x)
+
+#define PORT_CONF_USGMII_CFG_BYPASS_DESCRAM      BIT(8)
+#define PORT_CONF_USGMII_CFG_BYPASS_DESCRAM_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_BYPASS_DESCRAM, x)
+#define PORT_CONF_USGMII_CFG_BYPASS_DESCRAM_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_BYPASS_DESCRAM, x)
+
+#define PORT_CONF_USGMII_CFG_FLIP_LANES          BIT(7)
+#define PORT_CONF_USGMII_CFG_FLIP_LANES_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_FLIP_LANES, x)
+#define PORT_CONF_USGMII_CFG_FLIP_LANES_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_FLIP_LANES, x)
+
+#define PORT_CONF_USGMII_CFG_SHYST_DIS           BIT(6)
+#define PORT_CONF_USGMII_CFG_SHYST_DIS_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_SHYST_DIS, x)
+#define PORT_CONF_USGMII_CFG_SHYST_DIS_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_SHYST_DIS, x)
+
+#define PORT_CONF_USGMII_CFG_E_DET_ENA           BIT(5)
+#define PORT_CONF_USGMII_CFG_E_DET_ENA_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_E_DET_ENA, x)
+#define PORT_CONF_USGMII_CFG_E_DET_ENA_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_E_DET_ENA, x)
+
+#define PORT_CONF_USGMII_CFG_USE_I1_ENA          BIT(4)
+#define PORT_CONF_USGMII_CFG_USE_I1_ENA_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_USE_I1_ENA, x)
+#define PORT_CONF_USGMII_CFG_USE_I1_ENA_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_USE_I1_ENA, x)
+
+#define PORT_CONF_USGMII_CFG_QUAD_MODE           BIT(1)
+#define PORT_CONF_USGMII_CFG_QUAD_MODE_SET(x)\
+	FIELD_PREP(PORT_CONF_USGMII_CFG_QUAD_MODE, x)
+#define PORT_CONF_USGMII_CFG_QUAD_MODE_GET(x)\
+	FIELD_GET(PORT_CONF_USGMII_CFG_QUAD_MODE, x)
+
+/*      QFWD:SYSTEM:SWITCH_PORT_MODE */
+#define QFWD_SWITCH_PORT_MODE(r)  __REG(TARGET_QFWD, 0, 1, 0, 0, 1, 340, 0, r, 70, 4)
+
+#define QFWD_SWITCH_PORT_MODE_PORT_ENA           BIT(19)
+#define QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_PORT_ENA, x)
+#define QFWD_SWITCH_PORT_MODE_PORT_ENA_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_PORT_ENA, x)
+
+#define QFWD_SWITCH_PORT_MODE_FWD_URGENCY        GENMASK(18, 10)
+#define QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_FWD_URGENCY, x)
+#define QFWD_SWITCH_PORT_MODE_FWD_URGENCY_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_FWD_URGENCY, x)
+
+#define QFWD_SWITCH_PORT_MODE_YEL_RSRVD          GENMASK(9, 6)
+#define QFWD_SWITCH_PORT_MODE_YEL_RSRVD_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_YEL_RSRVD, x)
+#define QFWD_SWITCH_PORT_MODE_YEL_RSRVD_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_YEL_RSRVD, x)
+
+#define QFWD_SWITCH_PORT_MODE_INGRESS_DROP_MODE  BIT(5)
+#define QFWD_SWITCH_PORT_MODE_INGRESS_DROP_MODE_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_INGRESS_DROP_MODE, x)
+#define QFWD_SWITCH_PORT_MODE_INGRESS_DROP_MODE_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_INGRESS_DROP_MODE, x)
+
+#define QFWD_SWITCH_PORT_MODE_IGR_NO_SHARING     BIT(4)
+#define QFWD_SWITCH_PORT_MODE_IGR_NO_SHARING_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_IGR_NO_SHARING, x)
+#define QFWD_SWITCH_PORT_MODE_IGR_NO_SHARING_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_IGR_NO_SHARING, x)
+
+#define QFWD_SWITCH_PORT_MODE_EGR_NO_SHARING     BIT(3)
+#define QFWD_SWITCH_PORT_MODE_EGR_NO_SHARING_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_EGR_NO_SHARING, x)
+#define QFWD_SWITCH_PORT_MODE_EGR_NO_SHARING_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_EGR_NO_SHARING, x)
+
+#define QFWD_SWITCH_PORT_MODE_EGRESS_DROP_MODE   BIT(2)
+#define QFWD_SWITCH_PORT_MODE_EGRESS_DROP_MODE_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_EGRESS_DROP_MODE, x)
+#define QFWD_SWITCH_PORT_MODE_EGRESS_DROP_MODE_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_EGRESS_DROP_MODE, x)
+
+#define QFWD_SWITCH_PORT_MODE_EGRESS_RSRV_DIS    BIT(1)
+#define QFWD_SWITCH_PORT_MODE_EGRESS_RSRV_DIS_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_EGRESS_RSRV_DIS, x)
+#define QFWD_SWITCH_PORT_MODE_EGRESS_RSRV_DIS_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_EGRESS_RSRV_DIS, x)
+
+#define QFWD_SWITCH_PORT_MODE_LEARNALL_MORE      BIT(0)
+#define QFWD_SWITCH_PORT_MODE_LEARNALL_MORE_SET(x)\
+	FIELD_PREP(QFWD_SWITCH_PORT_MODE_LEARNALL_MORE, x)
+#define QFWD_SWITCH_PORT_MODE_LEARNALL_MORE_GET(x)\
+	FIELD_GET(QFWD_SWITCH_PORT_MODE_LEARNALL_MORE, x)
+
+/*      QRES:RES_CTRL:RES_CFG */
+#define QRES_RES_CFG(g)           __REG(TARGET_QRES, 0, 1, 0, g, 5120, 16, 0, 0, 1, 4)
+
+#define QRES_RES_CFG_WM_HIGH                     GENMASK(11, 0)
+#define QRES_RES_CFG_WM_HIGH_SET(x)\
+	FIELD_PREP(QRES_RES_CFG_WM_HIGH, x)
+#define QRES_RES_CFG_WM_HIGH_GET(x)\
+	FIELD_GET(QRES_RES_CFG_WM_HIGH, x)
+
+/*      QRES:RES_CTRL:RES_STAT */
+#define QRES_RES_STAT(g)          __REG(TARGET_QRES, 0, 1, 0, g, 5120, 16, 4, 0, 1, 4)
+
+#define QRES_RES_STAT_MAXUSE                     GENMASK(20, 0)
+#define QRES_RES_STAT_MAXUSE_SET(x)\
+	FIELD_PREP(QRES_RES_STAT_MAXUSE, x)
+#define QRES_RES_STAT_MAXUSE_GET(x)\
+	FIELD_GET(QRES_RES_STAT_MAXUSE, x)
+
+/*      QRES:RES_CTRL:RES_STAT_CUR */
+#define QRES_RES_STAT_CUR(g)      __REG(TARGET_QRES, 0, 1, 0, g, 5120, 16, 8, 0, 1, 4)
+
+#define QRES_RES_STAT_CUR_INUSE                  GENMASK(20, 0)
+#define QRES_RES_STAT_CUR_INUSE_SET(x)\
+	FIELD_PREP(QRES_RES_STAT_CUR_INUSE, x)
+#define QRES_RES_STAT_CUR_INUSE_GET(x)\
+	FIELD_GET(QRES_RES_STAT_CUR_INUSE, x)
+
+/*      DEVCPU_QS:XTR:XTR_GRP_CFG */
+#define QS_XTR_GRP_CFG(r)         __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 0, r, 2, 4)
+
+#define QS_XTR_GRP_CFG_MODE                      GENMASK(3, 2)
+#define QS_XTR_GRP_CFG_MODE_SET(x)\
+	FIELD_PREP(QS_XTR_GRP_CFG_MODE, x)
+#define QS_XTR_GRP_CFG_MODE_GET(x)\
+	FIELD_GET(QS_XTR_GRP_CFG_MODE, x)
+
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS           BIT(1)
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(x)\
+	FIELD_PREP(QS_XTR_GRP_CFG_STATUS_WORD_POS, x)
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS_GET(x)\
+	FIELD_GET(QS_XTR_GRP_CFG_STATUS_WORD_POS, x)
+
+#define QS_XTR_GRP_CFG_BYTE_SWAP                 BIT(0)
+#define QS_XTR_GRP_CFG_BYTE_SWAP_SET(x)\
+	FIELD_PREP(QS_XTR_GRP_CFG_BYTE_SWAP, x)
+#define QS_XTR_GRP_CFG_BYTE_SWAP_GET(x)\
+	FIELD_GET(QS_XTR_GRP_CFG_BYTE_SWAP, x)
+
+/*      DEVCPU_QS:XTR:XTR_RD */
+#define QS_XTR_RD(r)              __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 8, r, 2, 4)
+
+/*      DEVCPU_QS:XTR:XTR_FLUSH */
+#define QS_XTR_FLUSH              __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 24, 0, 1, 4)
+
+#define QS_XTR_FLUSH_FLUSH                       GENMASK(1, 0)
+#define QS_XTR_FLUSH_FLUSH_SET(x)\
+	FIELD_PREP(QS_XTR_FLUSH_FLUSH, x)
+#define QS_XTR_FLUSH_FLUSH_GET(x)\
+	FIELD_GET(QS_XTR_FLUSH_FLUSH, x)
+
+/*      DEVCPU_QS:XTR:XTR_DATA_PRESENT */
+#define QS_XTR_DATA_PRESENT       __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 28, 0, 1, 4)
+
+#define QS_XTR_DATA_PRESENT_DATA_PRESENT         GENMASK(1, 0)
+#define QS_XTR_DATA_PRESENT_DATA_PRESENT_SET(x)\
+	FIELD_PREP(QS_XTR_DATA_PRESENT_DATA_PRESENT, x)
+#define QS_XTR_DATA_PRESENT_DATA_PRESENT_GET(x)\
+	FIELD_GET(QS_XTR_DATA_PRESENT_DATA_PRESENT, x)
+
+/*      DEVCPU_QS:INJ:INJ_GRP_CFG */
+#define QS_INJ_GRP_CFG(r)         __REG(TARGET_QS, 0, 1, 36, 0, 1, 40, 0, r, 2, 4)
+
+#define QS_INJ_GRP_CFG_MODE                      GENMASK(3, 2)
+#define QS_INJ_GRP_CFG_MODE_SET(x)\
+	FIELD_PREP(QS_INJ_GRP_CFG_MODE, x)
+#define QS_INJ_GRP_CFG_MODE_GET(x)\
+	FIELD_GET(QS_INJ_GRP_CFG_MODE, x)
+
+#define QS_INJ_GRP_CFG_BYTE_SWAP                 BIT(0)
+#define QS_INJ_GRP_CFG_BYTE_SWAP_SET(x)\
+	FIELD_PREP(QS_INJ_GRP_CFG_BYTE_SWAP, x)
+#define QS_INJ_GRP_CFG_BYTE_SWAP_GET(x)\
+	FIELD_GET(QS_INJ_GRP_CFG_BYTE_SWAP, x)
+
+/*      DEVCPU_QS:INJ:INJ_WR */
+#define QS_INJ_WR(r)              __REG(TARGET_QS, 0, 1, 36, 0, 1, 40, 8, r, 2, 4)
+
+/*      DEVCPU_QS:INJ:INJ_CTRL */
+#define QS_INJ_CTRL(r)            __REG(TARGET_QS, 0, 1, 36, 0, 1, 40, 16, r, 2, 4)
+
+#define QS_INJ_CTRL_GAP_SIZE                     GENMASK(24, 21)
+#define QS_INJ_CTRL_GAP_SIZE_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_GAP_SIZE, x)
+#define QS_INJ_CTRL_GAP_SIZE_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_GAP_SIZE, x)
+
+#define QS_INJ_CTRL_ABORT                        BIT(20)
+#define QS_INJ_CTRL_ABORT_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_ABORT, x)
+#define QS_INJ_CTRL_ABORT_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_ABORT, x)
+
+#define QS_INJ_CTRL_EOF                          BIT(19)
+#define QS_INJ_CTRL_EOF_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_EOF, x)
+#define QS_INJ_CTRL_EOF_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_EOF, x)
+
+#define QS_INJ_CTRL_SOF                          BIT(18)
+#define QS_INJ_CTRL_SOF_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_SOF, x)
+#define QS_INJ_CTRL_SOF_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_SOF, x)
+
+#define QS_INJ_CTRL_VLD_BYTES                    GENMASK(17, 16)
+#define QS_INJ_CTRL_VLD_BYTES_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_VLD_BYTES, x)
+#define QS_INJ_CTRL_VLD_BYTES_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_VLD_BYTES, x)
+
+/*      DEVCPU_QS:INJ:INJ_STATUS */
+#define QS_INJ_STATUS             __REG(TARGET_QS, 0, 1, 36, 0, 1, 40, 24, 0, 1, 4)
+
+#define QS_INJ_STATUS_WMARK_REACHED              GENMASK(5, 4)
+#define QS_INJ_STATUS_WMARK_REACHED_SET(x)\
+	FIELD_PREP(QS_INJ_STATUS_WMARK_REACHED, x)
+#define QS_INJ_STATUS_WMARK_REACHED_GET(x)\
+	FIELD_GET(QS_INJ_STATUS_WMARK_REACHED, x)
+
+#define QS_INJ_STATUS_FIFO_RDY                   GENMASK(3, 2)
+#define QS_INJ_STATUS_FIFO_RDY_SET(x)\
+	FIELD_PREP(QS_INJ_STATUS_FIFO_RDY, x)
+#define QS_INJ_STATUS_FIFO_RDY_GET(x)\
+	FIELD_GET(QS_INJ_STATUS_FIFO_RDY, x)
+
+#define QS_INJ_STATUS_INJ_IN_PROGRESS            GENMASK(1, 0)
+#define QS_INJ_STATUS_INJ_IN_PROGRESS_SET(x)\
+	FIELD_PREP(QS_INJ_STATUS_INJ_IN_PROGRESS, x)
+#define QS_INJ_STATUS_INJ_IN_PROGRESS_GET(x)\
+	FIELD_GET(QS_INJ_STATUS_INJ_IN_PROGRESS, x)
+
+/*      QSYS:PAUSE_CFG:PAUSE_CFG */
+#define QSYS_PAUSE_CFG(r)         __REG(TARGET_QSYS, 0, 1, 544, 0, 1, 1128, 0, r, 70, 4)
+
+#define QSYS_PAUSE_CFG_PAUSE_START               GENMASK(25, 14)
+#define QSYS_PAUSE_CFG_PAUSE_START_SET(x)\
+	FIELD_PREP(QSYS_PAUSE_CFG_PAUSE_START, x)
+#define QSYS_PAUSE_CFG_PAUSE_START_GET(x)\
+	FIELD_GET(QSYS_PAUSE_CFG_PAUSE_START, x)
+
+#define QSYS_PAUSE_CFG_PAUSE_STOP                GENMASK(13, 2)
+#define QSYS_PAUSE_CFG_PAUSE_STOP_SET(x)\
+	FIELD_PREP(QSYS_PAUSE_CFG_PAUSE_STOP, x)
+#define QSYS_PAUSE_CFG_PAUSE_STOP_GET(x)\
+	FIELD_GET(QSYS_PAUSE_CFG_PAUSE_STOP, x)
+
+#define QSYS_PAUSE_CFG_PAUSE_ENA                 BIT(1)
+#define QSYS_PAUSE_CFG_PAUSE_ENA_SET(x)\
+	FIELD_PREP(QSYS_PAUSE_CFG_PAUSE_ENA, x)
+#define QSYS_PAUSE_CFG_PAUSE_ENA_GET(x)\
+	FIELD_GET(QSYS_PAUSE_CFG_PAUSE_ENA, x)
+
+#define QSYS_PAUSE_CFG_AGGRESSIVE_TAILDROP_ENA   BIT(0)
+#define QSYS_PAUSE_CFG_AGGRESSIVE_TAILDROP_ENA_SET(x)\
+	FIELD_PREP(QSYS_PAUSE_CFG_AGGRESSIVE_TAILDROP_ENA, x)
+#define QSYS_PAUSE_CFG_AGGRESSIVE_TAILDROP_ENA_GET(x)\
+	FIELD_GET(QSYS_PAUSE_CFG_AGGRESSIVE_TAILDROP_ENA, x)
+
+/*      QSYS:PAUSE_CFG:ATOP */
+#define QSYS_ATOP(r)              __REG(TARGET_QSYS, 0, 1, 544, 0, 1, 1128, 284, r, 70, 4)
+
+#define QSYS_ATOP_ATOP                           GENMASK(11, 0)
+#define QSYS_ATOP_ATOP_SET(x)\
+	FIELD_PREP(QSYS_ATOP_ATOP, x)
+#define QSYS_ATOP_ATOP_GET(x)\
+	FIELD_GET(QSYS_ATOP_ATOP, x)
+
+/*      QSYS:PAUSE_CFG:FWD_PRESSURE */
+#define QSYS_FWD_PRESSURE(r)      __REG(TARGET_QSYS, 0, 1, 544, 0, 1, 1128, 564, r, 70, 4)
+
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE           GENMASK(11, 1)
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE_SET(x)\
+	FIELD_PREP(QSYS_FWD_PRESSURE_FWD_PRESSURE, x)
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE_GET(x)\
+	FIELD_GET(QSYS_FWD_PRESSURE_FWD_PRESSURE, x)
+
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS       BIT(0)
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS_SET(x)\
+	FIELD_PREP(QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS, x)
+#define QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS_GET(x)\
+	FIELD_GET(QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS, x)
+
+/*      QSYS:PAUSE_CFG:ATOP_TOT_CFG */
+#define QSYS_ATOP_TOT_CFG         __REG(TARGET_QSYS, 0, 1, 544, 0, 1, 1128, 844, 0, 1, 4)
+
+#define QSYS_ATOP_TOT_CFG_ATOP_TOT               GENMASK(11, 0)
+#define QSYS_ATOP_TOT_CFG_ATOP_TOT_SET(x)\
+	FIELD_PREP(QSYS_ATOP_TOT_CFG_ATOP_TOT, x)
+#define QSYS_ATOP_TOT_CFG_ATOP_TOT_GET(x)\
+	FIELD_GET(QSYS_ATOP_TOT_CFG_ATOP_TOT, x)
+
+/*      QSYS:CALCFG:CAL_AUTO */
+#define QSYS_CAL_AUTO(r)          __REG(TARGET_QSYS, 0, 1, 2304, 0, 1, 40, 0, r, 7, 4)
+
+#define QSYS_CAL_AUTO_CAL_AUTO                   GENMASK(29, 0)
+#define QSYS_CAL_AUTO_CAL_AUTO_SET(x)\
+	FIELD_PREP(QSYS_CAL_AUTO_CAL_AUTO, x)
+#define QSYS_CAL_AUTO_CAL_AUTO_GET(x)\
+	FIELD_GET(QSYS_CAL_AUTO_CAL_AUTO, x)
+
+/*      QSYS:CALCFG:CAL_CTRL */
+#define QSYS_CAL_CTRL             __REG(TARGET_QSYS, 0, 1, 2304, 0, 1, 40, 36, 0, 1, 4)
+
+#define QSYS_CAL_CTRL_CAL_MODE                   GENMASK(14, 11)
+#define QSYS_CAL_CTRL_CAL_MODE_SET(x)\
+	FIELD_PREP(QSYS_CAL_CTRL_CAL_MODE, x)
+#define QSYS_CAL_CTRL_CAL_MODE_GET(x)\
+	FIELD_GET(QSYS_CAL_CTRL_CAL_MODE, x)
+
+#define QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE        GENMASK(10, 1)
+#define QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(x)\
+	FIELD_PREP(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE, x)
+#define QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_GET(x)\
+	FIELD_GET(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE, x)
+
+#define QSYS_CAL_CTRL_CAL_AUTO_ERROR             BIT(0)
+#define QSYS_CAL_CTRL_CAL_AUTO_ERROR_SET(x)\
+	FIELD_PREP(QSYS_CAL_CTRL_CAL_AUTO_ERROR, x)
+#define QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(x)\
+	FIELD_GET(QSYS_CAL_CTRL_CAL_AUTO_ERROR, x)
+
+/*      QSYS:RAM_CTRL:RAM_INIT */
+#define QSYS_RAM_INIT             __REG(TARGET_QSYS, 0, 1, 2344, 0, 1, 4, 0, 0, 1, 4)
+
+#define QSYS_RAM_INIT_RAM_INIT                   BIT(1)
+#define QSYS_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(QSYS_RAM_INIT_RAM_INIT, x)
+#define QSYS_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(QSYS_RAM_INIT_RAM_INIT, x)
+
+#define QSYS_RAM_INIT_RAM_CFG_HOOK               BIT(0)
+#define QSYS_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(QSYS_RAM_INIT_RAM_CFG_HOOK, x)
+#define QSYS_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(QSYS_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      REW:COMMON:OWN_UPSID */
+#define REW_OWN_UPSID(r)          __REG(TARGET_REW, 0, 1, 387264, 0, 1, 1232, 0, r, 3, 4)
+
+#define REW_OWN_UPSID_OWN_UPSID                  GENMASK(4, 0)
+#define REW_OWN_UPSID_OWN_UPSID_SET(x)\
+	FIELD_PREP(REW_OWN_UPSID_OWN_UPSID, x)
+#define REW_OWN_UPSID_OWN_UPSID_GET(x)\
+	FIELD_GET(REW_OWN_UPSID_OWN_UPSID, x)
+
+/*      REW:PORT:PORT_VLAN_CFG */
+#define REW_PORT_VLAN_CFG(g)      __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 0, 0, 1, 4)
+
+#define REW_PORT_VLAN_CFG_PORT_PCP               GENMASK(15, 13)
+#define REW_PORT_VLAN_CFG_PORT_PCP_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_PCP, x)
+#define REW_PORT_VLAN_CFG_PORT_PCP_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_PCP, x)
+
+#define REW_PORT_VLAN_CFG_PORT_DEI               BIT(12)
+#define REW_PORT_VLAN_CFG_PORT_DEI_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_DEI, x)
+#define REW_PORT_VLAN_CFG_PORT_DEI_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_DEI, x)
+
+#define REW_PORT_VLAN_CFG_PORT_VID               GENMASK(11, 0)
+#define REW_PORT_VLAN_CFG_PORT_VID_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_VID, x)
+#define REW_PORT_VLAN_CFG_PORT_VID_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_VID, x)
+
+/*      REW:PORT:TAG_CTRL */
+#define REW_TAG_CTRL(g)           __REG(TARGET_REW, 0, 1, 360448, g, 70, 256, 132, 0, 1, 4)
+
+#define REW_TAG_CTRL_TAG_CFG_OBEY_WAS_TAGGED     BIT(13)
+#define REW_TAG_CTRL_TAG_CFG_OBEY_WAS_TAGGED_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_CFG_OBEY_WAS_TAGGED, x)
+#define REW_TAG_CTRL_TAG_CFG_OBEY_WAS_TAGGED_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_CFG_OBEY_WAS_TAGGED, x)
+
+#define REW_TAG_CTRL_TAG_CFG                     GENMASK(12, 11)
+#define REW_TAG_CTRL_TAG_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_CFG, x)
+#define REW_TAG_CTRL_TAG_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_CFG, x)
+
+#define REW_TAG_CTRL_TAG_TPID_CFG                GENMASK(10, 8)
+#define REW_TAG_CTRL_TAG_TPID_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_TPID_CFG, x)
+#define REW_TAG_CTRL_TAG_TPID_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_TPID_CFG, x)
+
+#define REW_TAG_CTRL_TAG_VID_CFG                 GENMASK(7, 6)
+#define REW_TAG_CTRL_TAG_VID_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_VID_CFG, x)
+#define REW_TAG_CTRL_TAG_VID_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_VID_CFG, x)
+
+#define REW_TAG_CTRL_TAG_PCP_CFG                 GENMASK(5, 3)
+#define REW_TAG_CTRL_TAG_PCP_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_PCP_CFG, x)
+#define REW_TAG_CTRL_TAG_PCP_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_PCP_CFG, x)
+
+#define REW_TAG_CTRL_TAG_DEI_CFG                 GENMASK(2, 0)
+#define REW_TAG_CTRL_TAG_DEI_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CTRL_TAG_DEI_CFG, x)
+#define REW_TAG_CTRL_TAG_DEI_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CTRL_TAG_DEI_CFG, x)
+
+/*      REW:RAM_CTRL:RAM_INIT */
+#define REW_RAM_INIT              __REG(TARGET_REW, 0, 1, 378696, 0, 1, 4, 0, 0, 1, 4)
+
+#define REW_RAM_INIT_RAM_INIT                    BIT(1)
+#define REW_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(REW_RAM_INIT_RAM_INIT, x)
+#define REW_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(REW_RAM_INIT_RAM_INIT, x)
+
+#define REW_RAM_INIT_RAM_CFG_HOOK                BIT(0)
+#define REW_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(REW_RAM_INIT_RAM_CFG_HOOK, x)
+#define REW_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(REW_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      VCAP_SUPER:RAM_CTRL:RAM_INIT */
+#define VCAP_SUPER_RAM_INIT       __REG(TARGET_VCAP_SUPER, 0, 1, 1120, 0, 1, 4, 0, 0, 1, 4)
+
+#define VCAP_SUPER_RAM_INIT_RAM_INIT             BIT(1)
+#define VCAP_SUPER_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(VCAP_SUPER_RAM_INIT_RAM_INIT, x)
+#define VCAP_SUPER_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(VCAP_SUPER_RAM_INIT_RAM_INIT, x)
+
+#define VCAP_SUPER_RAM_INIT_RAM_CFG_HOOK         BIT(0)
+#define VCAP_SUPER_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(VCAP_SUPER_RAM_INIT_RAM_CFG_HOOK, x)
+#define VCAP_SUPER_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(VCAP_SUPER_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      VOP:RAM_CTRL:RAM_INIT */
+#define VOP_RAM_INIT              __REG(TARGET_VOP, 0, 1, 279176, 0, 1, 4, 0, 0, 1, 4)
+
+#define VOP_RAM_INIT_RAM_INIT                    BIT(1)
+#define VOP_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(VOP_RAM_INIT_RAM_INIT, x)
+#define VOP_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(VOP_RAM_INIT_RAM_INIT, x)
+
+#define VOP_RAM_INIT_RAM_CFG_HOOK                BIT(0)
+#define VOP_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(VOP_RAM_INIT_RAM_CFG_HOOK, x)
+#define VOP_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(VOP_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      XQS:SYSTEM:STAT_CFG */
+#define XQS_STAT_CFG              __REG(TARGET_XQS, 0, 1, 6768, 0, 1, 872, 860, 0, 1, 4)
+
+#define XQS_STAT_CFG_STAT_CLEAR_SHOT             GENMASK(21, 18)
+#define XQS_STAT_CFG_STAT_CLEAR_SHOT_SET(x)\
+	FIELD_PREP(XQS_STAT_CFG_STAT_CLEAR_SHOT, x)
+#define XQS_STAT_CFG_STAT_CLEAR_SHOT_GET(x)\
+	FIELD_GET(XQS_STAT_CFG_STAT_CLEAR_SHOT, x)
+
+#define XQS_STAT_CFG_STAT_VIEW                   GENMASK(17, 5)
+#define XQS_STAT_CFG_STAT_VIEW_SET(x)\
+	FIELD_PREP(XQS_STAT_CFG_STAT_VIEW, x)
+#define XQS_STAT_CFG_STAT_VIEW_GET(x)\
+	FIELD_GET(XQS_STAT_CFG_STAT_VIEW, x)
+
+#define XQS_STAT_CFG_STAT_SRV_PKT_ONLY           BIT(4)
+#define XQS_STAT_CFG_STAT_SRV_PKT_ONLY_SET(x)\
+	FIELD_PREP(XQS_STAT_CFG_STAT_SRV_PKT_ONLY, x)
+#define XQS_STAT_CFG_STAT_SRV_PKT_ONLY_GET(x)\
+	FIELD_GET(XQS_STAT_CFG_STAT_SRV_PKT_ONLY, x)
+
+#define XQS_STAT_CFG_STAT_WRAP_DIS               GENMASK(3, 0)
+#define XQS_STAT_CFG_STAT_WRAP_DIS_SET(x)\
+	FIELD_PREP(XQS_STAT_CFG_STAT_WRAP_DIS, x)
+#define XQS_STAT_CFG_STAT_WRAP_DIS_GET(x)\
+	FIELD_GET(XQS_STAT_CFG_STAT_WRAP_DIS, x)
+
+/*      XQS:QLIMIT_SHR:QLIMIT_SHR_TOP_CFG */
+#define XQS_QLIMIT_SHR_TOP_CFG(g) __REG(TARGET_XQS, 0, 1, 7936, g, 4, 48, 0, 0, 1, 4)
+
+#define XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP    GENMASK(14, 0)
+#define XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP_SET(x)\
+	FIELD_PREP(XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP, x)
+#define XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP_GET(x)\
+	FIELD_GET(XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP, x)
+
+/*      XQS:QLIMIT_SHR:QLIMIT_SHR_ATOP_CFG */
+#define XQS_QLIMIT_SHR_ATOP_CFG(g) __REG(TARGET_XQS, 0, 1, 7936, g, 4, 48, 4, 0, 1, 4)
+
+#define XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP  GENMASK(14, 0)
+#define XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP_SET(x)\
+	FIELD_PREP(XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP, x)
+#define XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP_GET(x)\
+	FIELD_GET(XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP, x)
+
+/*      XQS:QLIMIT_SHR:QLIMIT_SHR_CTOP_CFG */
+#define XQS_QLIMIT_SHR_CTOP_CFG(g) __REG(TARGET_XQS, 0, 1, 7936, g, 4, 48, 8, 0, 1, 4)
+
+#define XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP  GENMASK(14, 0)
+#define XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP_SET(x)\
+	FIELD_PREP(XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP, x)
+#define XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP_GET(x)\
+	FIELD_GET(XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP, x)
+
+/*      XQS:QLIMIT_SHR:QLIMIT_SHR_QLIM_CFG */
+#define XQS_QLIMIT_SHR_QLIM_CFG(g) __REG(TARGET_XQS, 0, 1, 7936, g, 4, 48, 12, 0, 1, 4)
+
+#define XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM  GENMASK(14, 0)
+#define XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM_SET(x)\
+	FIELD_PREP(XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM, x)
+#define XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM_GET(x)\
+	FIELD_GET(XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM, x)
+
+/*      XQS:STAT:CNT */
+#define XQS_CNT(g)                __REG(TARGET_XQS, 0, 1, 0, g, 1024, 4, 0, 0, 1, 4)
+
+#endif /* _SPARX5_MAIN_REGS_H_ */
-- 
2.29.2


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

* [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This adds phylink support for ports and register base injection
and extraction.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   2 +-
 .../ethernet/microchip/sparx5/sparx5_main.c   |  68 +++++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  26 ++
 .../ethernet/microchip/sparx5/sparx5_netdev.c | 203 +++++++++++++
 .../ethernet/microchip/sparx5/sparx5_packet.c | 273 ++++++++++++++++++
 .../microchip/sparx5/sparx5_phylink.c         | 168 +++++++++++
 6 files changed, 739 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 41a31843d86f..19a593d17f4a 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,4 +5,4 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o
+sparx5-switch-objs  := sparx5_main.o sparx5_packet.o sparx5_netdev.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 67435fa45c7f..baa108cd99b2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -240,13 +240,20 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 			     u32 portno,
 			     struct sparx5_port_config *conf)
 {
+	phy_interface_t phy_mode = conf->phy_mode;
 	struct sparx5_port *spx5_port;
 	struct net_device *ndev;
+	struct phylink *phylink;
 	int err;
 
 	err = sparx5_create_targets(sparx5);
 	if (err)
 		return err;
+	ndev = sparx5_create_netdev(sparx5, portno);
+	if (IS_ERR(ndev)) {
+		dev_err(sparx5->dev, "Could not create net device: %02u\n", portno);
+		return PTR_ERR(ndev);
+	}
 	spx5_port = netdev_priv(ndev);
 	spx5_port->of_node = portnp;
 	spx5_port->serdes = serdes;
@@ -262,6 +269,24 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	spx5_port->conf.speed = SPEED_UNKNOWN;
 	spx5_port->conf.power_down = true;
 	sparx5->ports[portno] = spx5_port;
+	/* Create a phylink for PHY management.  Also handles SFPs */
+	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
+	spx5_port->phylink_config.type = PHYLINK_NETDEV;
+	spx5_port->phylink_config.pcs_poll = true;
+
+	/* phylink needs a valid interface mode to parse dt node */
+	if (phy_mode == PHY_INTERFACE_MODE_NA)
+		phy_mode = PHY_INTERFACE_MODE_10GBASER;
+
+	phylink = phylink_create(&spx5_port->phylink_config,
+				 of_fwnode_handle(portnp),
+				 phy_mode,
+				 &sparx5_phylink_mac_ops);
+	if (IS_ERR(phylink))
+		return PTR_ERR(phylink);
+
+	spx5_port->phylink = phylink;
+
 	return 0;
 }
 
@@ -336,6 +361,9 @@ static int sparx5_init_switchcore(struct sparx5 *sparx5)
 	spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
 	spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
 
+	/* Configure manual injection */
+	sparx5_manual_injection_mode(sparx5);
+
 	/* Enable switch-core and queue system */
 	spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
 
@@ -495,11 +523,22 @@ static int sparx5_qlim_set(struct sparx5 *sparx5)
 
 static int sparx5_init(struct sparx5 *sparx5)
 {
+	int irq, ret;
 	u32 idx;
 
 	if (sparx5_create_targets(sparx5))
 		return -ENODEV;
 
+	/* Hook xtr irq */
+	irq = platform_get_irq(sparx5->pdev, 0);
+	if (irq < 0)
+		return irq;
+	ret = devm_request_irq(sparx5->dev, irq,
+			       sparx5_xtr_handler, IRQF_SHARED,
+			       "sparx5-xtr", sparx5);
+	if (ret)
+		return ret;
+
 	/* Read chip ID to check CPU interface */
 	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
 
@@ -580,6 +619,18 @@ static void sparx5_board_init(struct sparx5 *sparx5)
 	}
 }
 
+static void sparx5_cleanup_ports(struct sparx5 *sparx5)
+{
+	int idx;
+
+	for (idx = 0; idx < SPX5_PORTS; ++idx) {
+		struct sparx5_port *port = sparx5->ports[idx];
+
+		if (port && port->ndev)
+			sparx5_destroy_netdev(sparx5, port);
+	}
+}
+
 static int mchp_sparx5_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -666,12 +717,28 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	err = sparx5_register_netdevs(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	sparx5_board_init(sparx5);
 
+	return err;
+
 cleanup_ports:
+	sparx5_cleanup_ports(sparx5);
 	return err;
 }
 
+static int mchp_sparx5_remove(struct platform_device *pdev)
+{
+	struct sparx5 *sparx5 = platform_get_drvdata(pdev);
+
+	sparx5_cleanup_ports(sparx5);
+
+	return 0;
+}
+
 static const struct of_device_id mchp_sparx5_match[] = {
 	{ .compatible = "microchip,sparx5-switch" },
 	{ }
@@ -680,6 +747,7 @@ MODULE_DEVICE_TABLE(of, mchp_sparx5_match);
 
 static struct platform_driver mchp_sparx5_driver = {
 	.probe = mchp_sparx5_probe,
+	.remove = mchp_sparx5_remove,
 	.driver = {
 		.name = "sparx5-switch",
 		.of_match_table = mchp_sparx5_match,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 887b4f80b2db..a92ebf339fe1 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -93,6 +93,9 @@ struct sparx5_port {
 	struct device_node         *of_node;
 	struct phy                 *serdes;
 	struct sparx5_port_config  conf;
+	struct phylink_config      phylink_config;
+	struct phylink             *phylink;
+	struct phylink_pcs         phylink_pcs;
 	u16                        portno;
 	/* Ingress default VLAN (pvid) */
 	u16                        pvid;
@@ -132,6 +135,27 @@ struct sparx5 {
 	bool                                  sd_sgpio_remapping;
 };
 
+/* sparx5_main.c */
+void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
+bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
+
+/* sparx5_packet.c */
+irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
+int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
+void sparx5_manual_injection_mode(struct sparx5 *sparx5);
+
+/* sparx5_netdev.c */
+bool sparx5_netdevice_check(const struct net_device *dev);
+struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
+int sparx5_register_netdevs(struct sparx5 *sparx5);
+void sparx5_destroy_netdev(struct sparx5 *sparx5, struct sparx5_port *port);
+
+/* Configuration */
+static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
+{
+	return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
+}
+
 /* Clock period in picoseconds */
 static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 {
@@ -146,6 +170,8 @@ static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 	}
 }
 
+extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
+
 /* Calculate raw offset */
 static inline __pure int spx5_offset(int id, int tinst, int tcnt,
 				     int gbase, int ginst,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
new file mode 100644
index 000000000000..6f9282e9d3f4
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+/* The IFH bit position of the first VSTAX bit. This is because the
+ * VSTAX bit positions in Data sheet is starting from zero.
+ */
+#define VSTAX 73
+
+static void ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width)
+{
+	u8 *ifh_hdr = ifh;
+	/* Calculate the Start IFH byte position of this IFH bit position */
+	u32 byte = (35 - (pos / 8));
+	/* Calculate the Start bit position in the Start IFH byte */
+	u32 bit  = (pos % 8);
+	u64 encode = GENMASK(bit + width - 1, bit) & (value << bit);
+
+	/* Max width is 5 bytes - 40 bits. In worst case this will
+	 * spread over 6 bytes - 48 bits
+	 */
+	compiletime_assert(width <= 40, "Unsupported width, must be <= 40");
+
+	/* The b0-b7 goes into the start IFH byte */
+	if (encode & 0xFF)
+		ifh_hdr[byte] |= (u8)((encode & 0xFF));
+	/* The b8-b15 goes into the next IFH byte */
+	if (encode & 0xFF00)
+		ifh_hdr[byte - 1] |= (u8)((encode & 0xFF00) >> 8);
+	/* The b16-b23 goes into the next IFH byte */
+	if (encode & 0xFF0000)
+		ifh_hdr[byte - 2] |= (u8)((encode & 0xFF0000) >> 16);
+	/* The b24-b31 goes into the next IFH byte */
+	if (encode & 0xFF000000)
+		ifh_hdr[byte - 3] |= (u8)((encode & 0xFF000000) >> 24);
+	/* The b32-b39 goes into the next IFH byte */
+	if (encode & 0xFF00000000)
+		ifh_hdr[byte - 4] |= (u8)((encode & 0xFF00000000) >> 32);
+	/* The b40-b47 goes into the next IFH byte */
+	if (encode & 0xFF0000000000)
+		ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40);
+}
+
+static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno)
+{
+	/* VSTAX.RSV = 1. MSBit must be 1 */
+	ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79,  1);
+	/* VSTAX.INGR_DROP_MODE = Enable. Don't make head-of-line blocking */
+	ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 55,  1);
+	/* MISC.CPU_MASK/DPORT = Destination port */
+	ifh_encode_bitfield(ifh_hdr, portno,   29, 8);
+	/* MISC.PIPELINE_PT */
+	ifh_encode_bitfield(ifh_hdr, 16,       37, 5);
+	/* MISC.PIPELINE_ACT */
+	ifh_encode_bitfield(ifh_hdr, 1,        42, 3);
+	/* FWD.SRC_PORT = CPU */
+	ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7);
+	/* FWD.SFLOW_ID (disable SFlow sampling) */
+	ifh_encode_bitfield(ifh_hdr, 124,      57, 7);
+	/* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */
+	ifh_encode_bitfield(ifh_hdr, 1,        67, 1);
+}
+
+static int sparx5_port_open(struct net_device *ndev)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	int err = 0;
+
+	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
+	if (err) {
+		netdev_err(ndev, "Could not attach to PHY\n");
+		return err;
+	}
+
+	phylink_start(port->phylink);
+
+	if (!ndev->phydev) {
+		/* power up serdes */
+		port->conf.power_down = false;
+		err = phy_power_on(port->serdes);
+		if (err)
+			netdev_err(ndev, "%s failed\n", __func__);
+	}
+
+	return err;
+}
+
+static int sparx5_port_stop(struct net_device *ndev)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	int err = 0;
+
+	phylink_stop(port->phylink);
+	phylink_disconnect_phy(port->phylink);
+
+	if (!ndev->phydev) {
+		port->conf.power_down = true;
+		err = phy_power_off(port->serdes);
+		if (err)
+			netdev_err(ndev, "%s failed\n", __func__);
+	}
+	return 0;
+}
+
+static int sparx5_port_get_phys_port_name(struct net_device *dev,
+					  char *buf, size_t len)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+
+	ret = snprintf(buf, len, "p%d", port->portno);
+	if (ret >= len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int sparx5_set_mac_address(struct net_device *dev, void *p)
+{
+	const struct sockaddr *addr = p;
+
+	/* Record the address */
+	ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+	return 0;
+}
+
+static const struct net_device_ops sparx5_port_netdev_ops = {
+	.ndo_open               = sparx5_port_open,
+	.ndo_stop               = sparx5_port_stop,
+	.ndo_start_xmit         = sparx5_port_xmit_impl,
+	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
+	.ndo_set_mac_address    = sparx5_set_mac_address,
+	.ndo_validate_addr      = eth_validate_addr,
+};
+
+bool sparx5_netdevice_check(const struct net_device *dev)
+{
+	return dev && (dev->netdev_ops == &sparx5_port_netdev_ops);
+}
+
+struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
+{
+	struct net_device *ndev;
+	struct sparx5_port *spx5_port;
+
+	ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port));
+	if (!ndev)
+		return ERR_PTR(-ENOMEM);
+
+	SET_NETDEV_DEV(ndev, sparx5->dev);
+	spx5_port = netdev_priv(ndev);
+	spx5_port->ndev = ndev;
+	spx5_port->sparx5 = sparx5;
+	spx5_port->portno = portno;
+	sparx5_set_port_ifh(spx5_port->ifh, portno);
+	snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
+
+	ether_setup(ndev);
+	ndev->netdev_ops = &sparx5_port_netdev_ops;
+	ndev->features |= NETIF_F_LLTX; /* software tx */
+
+	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
+	ndev->dev_addr[ETH_ALEN - 1] += portno + 1;
+
+	return ndev;
+}
+
+int sparx5_register_netdevs(struct sparx5 *sparx5)
+{
+	int portno;
+	int err;
+
+	for (portno = 0; portno < SPX5_PORTS; portno++)
+		if (sparx5->ports[portno]) {
+			err = devm_register_netdev(sparx5->dev,
+						   sparx5->ports[portno]->ndev);
+			if (err) {
+				dev_err(sparx5->dev,
+					"port: %02u: netdev registration failed\n",
+					portno);
+				return err;
+			}
+		}
+	return 0;
+}
+
+void sparx5_destroy_netdev(struct sparx5 *sparx5, struct sparx5_port *port)
+{
+	if (port->phylink) {
+		/* Disconnect the phy */
+		rtnl_lock();
+		sparx5_port_stop(port->ndev);
+		phylink_disconnect_phy(port->phylink);
+		rtnl_unlock();
+		phylink_destroy(port->phylink);
+		port->phylink = NULL;
+	}
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
new file mode 100644
index 000000000000..209eef5c6385
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+#define XTR_EOF_0     ntohl((__force __be32)0x80000000u)
+#define XTR_EOF_1     ntohl((__force __be32)0x80000001u)
+#define XTR_EOF_2     ntohl((__force __be32)0x80000002u)
+#define XTR_EOF_3     ntohl((__force __be32)0x80000003u)
+#define XTR_PRUNED    ntohl((__force __be32)0x80000004u)
+#define XTR_ABORT     ntohl((__force __be32)0x80000005u)
+#define XTR_ESCAPE    ntohl((__force __be32)0x80000006u)
+#define XTR_NOT_READY ntohl((__force __be32)0x80000007u)
+
+#define XTR_VALID_BYTES(x)      (4 - ((x) & 3))
+
+#define XTR_QUEUE     0
+#define INJ_QUEUE     0
+
+struct frame_info {
+	int src_port;
+};
+
+static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
+{
+	/* Start flush */
+	spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);
+
+	/* Allow to drain */
+	mdelay(1);
+
+	/* All Queues normal */
+	spx5_wr(0, sparx5, QS_XTR_FLUSH);
+}
+
+static void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)
+{
+	u8 *xtr_hdr = (u8 *)ifh;
+
+	/* FWD is bit 45-72 (28 bits), but we only read the 27 LSB for now */
+	u32 fwd =
+		((u32)xtr_hdr[27] << 24) |
+		((u32)xtr_hdr[28] << 16) |
+		((u32)xtr_hdr[29] <<  8) |
+		((u32)xtr_hdr[30] <<  0);
+	fwd = (fwd >> 5);
+	info->src_port = FIELD_GET(GENMASK(7, 1), fwd);
+}
+
+static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
+{
+	int i, byte_cnt = 0;
+	bool eof_flag = false, pruned_flag = false, abort_flag = false;
+	u32 ifh[IFH_LEN];
+	struct sk_buff *skb;
+	struct frame_info fi;
+	struct sparx5_port *port;
+	struct net_device *netdev;
+	u32 *rxbuf;
+
+	/* Get IFH */
+	for (i = 0; i < IFH_LEN; i++)
+		ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
+
+	/* Decode IFH (whats needed) */
+	sparx5_ifh_parse(ifh, &fi);
+
+	/* Map to port netdev */
+	port = fi.src_port < SPX5_PORTS ?
+		sparx5->ports[fi.src_port] : NULL;
+	if (!port || !port->ndev) {
+		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
+		sparx5_xtr_flush(sparx5, grp);
+		return;
+	}
+
+	/* Have netdev, get skb */
+	netdev = port->ndev;
+	skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
+	if (!skb) {
+		sparx5_xtr_flush(sparx5, grp);
+		dev_err(sparx5->dev, "No skb allocated\n");
+		return;
+	}
+	rxbuf = (u32 *)skb->data;
+
+	/* Now, pull frame data */
+	while (!eof_flag) {
+		u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
+		u32 cmp = val;
+
+		if (byte_swap)
+			cmp = ntohl((__force __be32)val);
+
+		switch (cmp) {
+		case XTR_NOT_READY:
+			break;
+		case XTR_ABORT:
+			/* No accompanying data */
+			abort_flag = true;
+			eof_flag = true;
+			break;
+		case XTR_EOF_0:
+		case XTR_EOF_1:
+		case XTR_EOF_2:
+		case XTR_EOF_3:
+			/* This assumes STATUS_WORD_POS == 1, Status
+			 * just after last data
+			 */
+			byte_cnt -= (4 - XTR_VALID_BYTES(val));
+			eof_flag = true;
+			break;
+		case XTR_PRUNED:
+			/* But get the last 4 bytes as well */
+			eof_flag = true;
+			pruned_flag = true;
+			fallthrough;
+		case XTR_ESCAPE:
+			*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
+			byte_cnt += 4;
+			rxbuf++;
+			break;
+		default:
+			*rxbuf = val;
+			byte_cnt += 4;
+			rxbuf++;
+		}
+	}
+
+	if (abort_flag || pruned_flag || !eof_flag) {
+		netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
+			   abort_flag, pruned_flag, eof_flag);
+		kfree_skb(skb);
+		return;
+	}
+
+	if (!netif_oper_up(netdev)) {
+		netdev_err(netdev, "Discarded frame: Interface not up\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Finish up skb */
+	skb_put(skb, byte_cnt - ETH_FCS_LEN);
+	eth_skb_pad(skb);
+	skb->protocol = eth_type_trans(skb, netdev);
+	netif_rx(skb);
+	netdev->stats.rx_bytes += skb->len;
+	netdev->stats.rx_packets++;
+}
+
+static int sparx5_inject(struct sparx5 *sparx5,
+			 u32 *ifh,
+			 struct sk_buff *skb)
+{
+	u32 val, w, count;
+	int grp = INJ_QUEUE;
+	u8 *buf;
+
+	val = spx5_rd(sparx5, QS_INJ_STATUS);
+	if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
+		pr_err("Injection: Queue not ready: 0x%lx\n",
+		       QS_INJ_STATUS_FIFO_RDY_GET(val));
+		return -1;
+	}
+
+	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
+		pr_err("Injection: Watermark reached: 0x%lx\n",
+		       QS_INJ_STATUS_WMARK_REACHED_GET(val));
+		return -1;
+	}
+
+	/* Indicate SOF */
+	spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
+		QS_INJ_CTRL_GAP_SIZE_SET(1),
+		sparx5, QS_INJ_CTRL(grp));
+
+	// Write the IFH to the chip.
+	for (w = 0; w < IFH_LEN; w++)
+		spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
+
+	/* Write words, round up */
+	count = ((skb->len + 3) / 4);
+	buf = skb->data;
+	for (w = 0; w < count; w++, buf += 4) {
+		val = get_unaligned((const u32 *)buf);
+		spx5_wr(val, sparx5, QS_INJ_WR(grp));
+	}
+
+	/* Add padding */
+	while (w < (60 / 4)) {
+		spx5_wr(0, sparx5, QS_INJ_WR(grp));
+		w++;
+	}
+
+	/* Indicate EOF and valid bytes in last word */
+	spx5_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |
+		QS_INJ_CTRL_VLD_BYTES_SET(skb->len < 60 ? 0 : skb->len % 4) |
+		QS_INJ_CTRL_EOF_SET(1),
+		sparx5, QS_INJ_CTRL(grp));
+
+	/* Add dummy CRC */
+	spx5_wr(0, sparx5, QS_INJ_WR(grp));
+	w++;
+
+	return NETDEV_TX_OK;
+}
+
+int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+	struct net_device_stats *stats = &dev->stats;
+	int ret;
+
+	ret = sparx5_inject(sparx5, port->ifh, skb);
+
+	if (ret == NETDEV_TX_OK) {
+		stats->tx_bytes += skb->len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_dropped++;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}
+
+void sparx5_manual_injection_mode(struct sparx5 *sparx5)
+{
+	const int byte_swap = 1;
+	int portno;
+
+	/* Change mode to manual extraction and injection */
+	spx5_wr(QS_XTR_GRP_CFG_MODE_SET(1) |
+		QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
+		QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
+		sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
+	spx5_wr(QS_INJ_GRP_CFG_MODE_SET(1) |
+		QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
+		sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
+
+	/* CPU ports capture setup */
+	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
+		/* ASM CPU port: No preamble, IFH, enable padding */
+		spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
+			ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
+			ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
+			sparx5, ASM_PORT_CFG(portno));
+	}
+
+	/* Reset WM cnt to unclog queued frames */
+	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++)
+		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
+			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
+			 sparx5,
+			 DSM_DEV_TX_STOP_WM_CFG(portno));
+}
+
+irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
+{
+	struct sparx5 *sparx5 = _sparx5;
+
+	/* Check data in queue */
+	while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
+		sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
new file mode 100644
index 000000000000..8166bdedaea1
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/phylink.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/sfp.h>
+
+#include "sparx5_main.h"
+
+static void sparx5_phylink_validate(struct phylink_config *config,
+				    unsigned long *supported,
+				    struct phylink_link_state *state)
+{
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	phylink_set(mask, Autoneg);
+	phylink_set_port_modes(mask);
+	phylink_set(mask, Pause);
+	phylink_set(mask, Asym_Pause);
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_10GBASER:
+	case PHY_INTERFACE_MODE_NA:
+		if (port->conf.max_speed == SPEED_25000 ||
+		    port->conf.max_speed == SPEED_10000) {
+			phylink_set(mask, 5000baseT_Full);
+			phylink_set(mask, 10000baseT_Full);
+			phylink_set(mask, 10000baseCR_Full);
+			phylink_set(mask, 10000baseSR_Full);
+			phylink_set(mask, 10000baseLR_Full);
+			phylink_set(mask, 10000baseLRM_Full);
+			phylink_set(mask, 10000baseER_Full);
+		}
+		if (port->conf.max_speed == SPEED_25000) {
+			phylink_set(mask, 25000baseCR_Full);
+			phylink_set(mask, 25000baseSR_Full);
+		}
+		if (state->interface != PHY_INTERFACE_MODE_NA)
+			break;
+		fallthrough;
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
+		phylink_set(mask, 10baseT_Half);
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Half);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 1000baseX_Full);
+		if (state->interface != PHY_INTERFACE_MODE_NA)
+			break;
+		fallthrough;
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+			phylink_set(mask, 1000baseT_Full);
+			phylink_set(mask, 1000baseX_Full);
+		}
+		if (state->interface == PHY_INTERFACE_MODE_2500BASEX ||
+		    state->interface == PHY_INTERFACE_MODE_NA) {
+			phylink_set(mask, 2500baseT_Full);
+			phylink_set(mask, 2500baseX_Full);
+		}
+		break;
+	default:
+		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+		return;
+	}
+	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_port_config *b)
+{
+	if (a->speed != b->speed ||
+	    a->portmode != b->portmode ||
+	    a->media_type != b->media_type)
+		return true;
+	return false;
+}
+
+static void sparx5_phylink_mac_config(struct phylink_config *config,
+				      unsigned int mode,
+				      const struct phylink_link_state *state)
+{
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_config conf;
+
+	conf = port->conf;
+	conf.power_down = false;
+	conf.portmode = state->interface;
+	conf.speed = state->speed;
+	conf.autoneg = state->an_enabled;
+
+	if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
+		if (state->speed == SPEED_UNKNOWN) {
+			/* When a SFP is plugged in we use capabilities to
+			 * default to the highest supported speed
+			 */
+			if (phylink_test(state->advertising, 25000baseSR_Full) ||
+			    phylink_test(state->advertising, 25000baseCR_Full))
+				conf.speed = SPEED_25000;
+			else if (state->interface == PHY_INTERFACE_MODE_10GBASER)
+				conf.speed = SPEED_10000;
+		} else if (state->speed == SPEED_2500) {
+			conf.portmode = PHY_INTERFACE_MODE_2500BASEX;
+		} else if (state->speed == SPEED_1000) {
+			conf.portmode = PHY_INTERFACE_MODE_1000BASEX;
+		}
+	}
+
+	if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
+		if (phylink_test(state->advertising, FIBRE))
+			conf.media_type = ETH_MEDIA_SR;
+		else
+			conf.media_type = ETH_MEDIA_DAC;
+	}
+
+	if (!port_conf_has_changed(&port->conf, &conf))
+		return;
+}
+
+static void sparx5_phylink_mac_link_up(struct phylink_config *config,
+				       struct phy_device *phy,
+				       unsigned int mode,
+				       phy_interface_t interface,
+				       int speed, int duplex,
+				       bool tx_pause, bool rx_pause)
+{
+	/* Currently not used */
+}
+
+static void sparx5_phylink_mac_link_state(struct phylink_config *config,
+					  struct phylink_link_state *state)
+{
+	state->link = true;
+	state->an_complete = true;
+	state->speed = SPEED_1000;
+	state->duplex = true;
+	state->pause = MLO_PAUSE_AN;
+}
+
+static void sparx5_phylink_mac_aneg_restart(struct phylink_config *config)
+{
+	/* Currently not used */
+}
+
+static void sparx5_phylink_mac_link_down(struct phylink_config *config,
+					 unsigned int mode,
+					 phy_interface_t interface)
+{
+	/* Currently not used */
+}
+
+const struct phylink_mac_ops sparx5_phylink_mac_ops = {
+	.validate = sparx5_phylink_validate,
+	.mac_pcs_get_state = sparx5_phylink_mac_link_state,
+	.mac_config = sparx5_phylink_mac_config,
+	.mac_an_restart = sparx5_phylink_mac_aneg_restart,
+	.mac_link_down = sparx5_phylink_mac_link_down,
+	.mac_link_up = sparx5_phylink_mac_link_up,
+};
-- 
2.29.2


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

* [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This adds phylink support for ports and register base injection
and extraction.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   2 +-
 .../ethernet/microchip/sparx5/sparx5_main.c   |  68 +++++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  26 ++
 .../ethernet/microchip/sparx5/sparx5_netdev.c | 203 +++++++++++++
 .../ethernet/microchip/sparx5/sparx5_packet.c | 273 ++++++++++++++++++
 .../microchip/sparx5/sparx5_phylink.c         | 168 +++++++++++
 6 files changed, 739 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 41a31843d86f..19a593d17f4a 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,4 +5,4 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o
+sparx5-switch-objs  := sparx5_main.o sparx5_packet.o sparx5_netdev.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 67435fa45c7f..baa108cd99b2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -240,13 +240,20 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 			     u32 portno,
 			     struct sparx5_port_config *conf)
 {
+	phy_interface_t phy_mode = conf->phy_mode;
 	struct sparx5_port *spx5_port;
 	struct net_device *ndev;
+	struct phylink *phylink;
 	int err;
 
 	err = sparx5_create_targets(sparx5);
 	if (err)
 		return err;
+	ndev = sparx5_create_netdev(sparx5, portno);
+	if (IS_ERR(ndev)) {
+		dev_err(sparx5->dev, "Could not create net device: %02u\n", portno);
+		return PTR_ERR(ndev);
+	}
 	spx5_port = netdev_priv(ndev);
 	spx5_port->of_node = portnp;
 	spx5_port->serdes = serdes;
@@ -262,6 +269,24 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	spx5_port->conf.speed = SPEED_UNKNOWN;
 	spx5_port->conf.power_down = true;
 	sparx5->ports[portno] = spx5_port;
+	/* Create a phylink for PHY management.  Also handles SFPs */
+	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
+	spx5_port->phylink_config.type = PHYLINK_NETDEV;
+	spx5_port->phylink_config.pcs_poll = true;
+
+	/* phylink needs a valid interface mode to parse dt node */
+	if (phy_mode == PHY_INTERFACE_MODE_NA)
+		phy_mode = PHY_INTERFACE_MODE_10GBASER;
+
+	phylink = phylink_create(&spx5_port->phylink_config,
+				 of_fwnode_handle(portnp),
+				 phy_mode,
+				 &sparx5_phylink_mac_ops);
+	if (IS_ERR(phylink))
+		return PTR_ERR(phylink);
+
+	spx5_port->phylink = phylink;
+
 	return 0;
 }
 
@@ -336,6 +361,9 @@ static int sparx5_init_switchcore(struct sparx5 *sparx5)
 	spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
 	spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
 
+	/* Configure manual injection */
+	sparx5_manual_injection_mode(sparx5);
+
 	/* Enable switch-core and queue system */
 	spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
 
@@ -495,11 +523,22 @@ static int sparx5_qlim_set(struct sparx5 *sparx5)
 
 static int sparx5_init(struct sparx5 *sparx5)
 {
+	int irq, ret;
 	u32 idx;
 
 	if (sparx5_create_targets(sparx5))
 		return -ENODEV;
 
+	/* Hook xtr irq */
+	irq = platform_get_irq(sparx5->pdev, 0);
+	if (irq < 0)
+		return irq;
+	ret = devm_request_irq(sparx5->dev, irq,
+			       sparx5_xtr_handler, IRQF_SHARED,
+			       "sparx5-xtr", sparx5);
+	if (ret)
+		return ret;
+
 	/* Read chip ID to check CPU interface */
 	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
 
@@ -580,6 +619,18 @@ static void sparx5_board_init(struct sparx5 *sparx5)
 	}
 }
 
+static void sparx5_cleanup_ports(struct sparx5 *sparx5)
+{
+	int idx;
+
+	for (idx = 0; idx < SPX5_PORTS; ++idx) {
+		struct sparx5_port *port = sparx5->ports[idx];
+
+		if (port && port->ndev)
+			sparx5_destroy_netdev(sparx5, port);
+	}
+}
+
 static int mchp_sparx5_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -666,12 +717,28 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	err = sparx5_register_netdevs(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	sparx5_board_init(sparx5);
 
+	return err;
+
 cleanup_ports:
+	sparx5_cleanup_ports(sparx5);
 	return err;
 }
 
+static int mchp_sparx5_remove(struct platform_device *pdev)
+{
+	struct sparx5 *sparx5 = platform_get_drvdata(pdev);
+
+	sparx5_cleanup_ports(sparx5);
+
+	return 0;
+}
+
 static const struct of_device_id mchp_sparx5_match[] = {
 	{ .compatible = "microchip,sparx5-switch" },
 	{ }
@@ -680,6 +747,7 @@ MODULE_DEVICE_TABLE(of, mchp_sparx5_match);
 
 static struct platform_driver mchp_sparx5_driver = {
 	.probe = mchp_sparx5_probe,
+	.remove = mchp_sparx5_remove,
 	.driver = {
 		.name = "sparx5-switch",
 		.of_match_table = mchp_sparx5_match,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 887b4f80b2db..a92ebf339fe1 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -93,6 +93,9 @@ struct sparx5_port {
 	struct device_node         *of_node;
 	struct phy                 *serdes;
 	struct sparx5_port_config  conf;
+	struct phylink_config      phylink_config;
+	struct phylink             *phylink;
+	struct phylink_pcs         phylink_pcs;
 	u16                        portno;
 	/* Ingress default VLAN (pvid) */
 	u16                        pvid;
@@ -132,6 +135,27 @@ struct sparx5 {
 	bool                                  sd_sgpio_remapping;
 };
 
+/* sparx5_main.c */
+void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
+bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
+
+/* sparx5_packet.c */
+irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
+int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
+void sparx5_manual_injection_mode(struct sparx5 *sparx5);
+
+/* sparx5_netdev.c */
+bool sparx5_netdevice_check(const struct net_device *dev);
+struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
+int sparx5_register_netdevs(struct sparx5 *sparx5);
+void sparx5_destroy_netdev(struct sparx5 *sparx5, struct sparx5_port *port);
+
+/* Configuration */
+static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
+{
+	return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
+}
+
 /* Clock period in picoseconds */
 static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 {
@@ -146,6 +170,8 @@ static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 	}
 }
 
+extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
+
 /* Calculate raw offset */
 static inline __pure int spx5_offset(int id, int tinst, int tcnt,
 				     int gbase, int ginst,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
new file mode 100644
index 000000000000..6f9282e9d3f4
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+/* The IFH bit position of the first VSTAX bit. This is because the
+ * VSTAX bit positions in Data sheet is starting from zero.
+ */
+#define VSTAX 73
+
+static void ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width)
+{
+	u8 *ifh_hdr = ifh;
+	/* Calculate the Start IFH byte position of this IFH bit position */
+	u32 byte = (35 - (pos / 8));
+	/* Calculate the Start bit position in the Start IFH byte */
+	u32 bit  = (pos % 8);
+	u64 encode = GENMASK(bit + width - 1, bit) & (value << bit);
+
+	/* Max width is 5 bytes - 40 bits. In worst case this will
+	 * spread over 6 bytes - 48 bits
+	 */
+	compiletime_assert(width <= 40, "Unsupported width, must be <= 40");
+
+	/* The b0-b7 goes into the start IFH byte */
+	if (encode & 0xFF)
+		ifh_hdr[byte] |= (u8)((encode & 0xFF));
+	/* The b8-b15 goes into the next IFH byte */
+	if (encode & 0xFF00)
+		ifh_hdr[byte - 1] |= (u8)((encode & 0xFF00) >> 8);
+	/* The b16-b23 goes into the next IFH byte */
+	if (encode & 0xFF0000)
+		ifh_hdr[byte - 2] |= (u8)((encode & 0xFF0000) >> 16);
+	/* The b24-b31 goes into the next IFH byte */
+	if (encode & 0xFF000000)
+		ifh_hdr[byte - 3] |= (u8)((encode & 0xFF000000) >> 24);
+	/* The b32-b39 goes into the next IFH byte */
+	if (encode & 0xFF00000000)
+		ifh_hdr[byte - 4] |= (u8)((encode & 0xFF00000000) >> 32);
+	/* The b40-b47 goes into the next IFH byte */
+	if (encode & 0xFF0000000000)
+		ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40);
+}
+
+static void sparx5_set_port_ifh(void *ifh_hdr, u16 portno)
+{
+	/* VSTAX.RSV = 1. MSBit must be 1 */
+	ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79,  1);
+	/* VSTAX.INGR_DROP_MODE = Enable. Don't make head-of-line blocking */
+	ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 55,  1);
+	/* MISC.CPU_MASK/DPORT = Destination port */
+	ifh_encode_bitfield(ifh_hdr, portno,   29, 8);
+	/* MISC.PIPELINE_PT */
+	ifh_encode_bitfield(ifh_hdr, 16,       37, 5);
+	/* MISC.PIPELINE_ACT */
+	ifh_encode_bitfield(ifh_hdr, 1,        42, 3);
+	/* FWD.SRC_PORT = CPU */
+	ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7);
+	/* FWD.SFLOW_ID (disable SFlow sampling) */
+	ifh_encode_bitfield(ifh_hdr, 124,      57, 7);
+	/* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */
+	ifh_encode_bitfield(ifh_hdr, 1,        67, 1);
+}
+
+static int sparx5_port_open(struct net_device *ndev)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	int err = 0;
+
+	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
+	if (err) {
+		netdev_err(ndev, "Could not attach to PHY\n");
+		return err;
+	}
+
+	phylink_start(port->phylink);
+
+	if (!ndev->phydev) {
+		/* power up serdes */
+		port->conf.power_down = false;
+		err = phy_power_on(port->serdes);
+		if (err)
+			netdev_err(ndev, "%s failed\n", __func__);
+	}
+
+	return err;
+}
+
+static int sparx5_port_stop(struct net_device *ndev)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	int err = 0;
+
+	phylink_stop(port->phylink);
+	phylink_disconnect_phy(port->phylink);
+
+	if (!ndev->phydev) {
+		port->conf.power_down = true;
+		err = phy_power_off(port->serdes);
+		if (err)
+			netdev_err(ndev, "%s failed\n", __func__);
+	}
+	return 0;
+}
+
+static int sparx5_port_get_phys_port_name(struct net_device *dev,
+					  char *buf, size_t len)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+
+	ret = snprintf(buf, len, "p%d", port->portno);
+	if (ret >= len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int sparx5_set_mac_address(struct net_device *dev, void *p)
+{
+	const struct sockaddr *addr = p;
+
+	/* Record the address */
+	ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+	return 0;
+}
+
+static const struct net_device_ops sparx5_port_netdev_ops = {
+	.ndo_open               = sparx5_port_open,
+	.ndo_stop               = sparx5_port_stop,
+	.ndo_start_xmit         = sparx5_port_xmit_impl,
+	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
+	.ndo_set_mac_address    = sparx5_set_mac_address,
+	.ndo_validate_addr      = eth_validate_addr,
+};
+
+bool sparx5_netdevice_check(const struct net_device *dev)
+{
+	return dev && (dev->netdev_ops == &sparx5_port_netdev_ops);
+}
+
+struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
+{
+	struct net_device *ndev;
+	struct sparx5_port *spx5_port;
+
+	ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port));
+	if (!ndev)
+		return ERR_PTR(-ENOMEM);
+
+	SET_NETDEV_DEV(ndev, sparx5->dev);
+	spx5_port = netdev_priv(ndev);
+	spx5_port->ndev = ndev;
+	spx5_port->sparx5 = sparx5;
+	spx5_port->portno = portno;
+	sparx5_set_port_ifh(spx5_port->ifh, portno);
+	snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
+
+	ether_setup(ndev);
+	ndev->netdev_ops = &sparx5_port_netdev_ops;
+	ndev->features |= NETIF_F_LLTX; /* software tx */
+
+	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
+	ndev->dev_addr[ETH_ALEN - 1] += portno + 1;
+
+	return ndev;
+}
+
+int sparx5_register_netdevs(struct sparx5 *sparx5)
+{
+	int portno;
+	int err;
+
+	for (portno = 0; portno < SPX5_PORTS; portno++)
+		if (sparx5->ports[portno]) {
+			err = devm_register_netdev(sparx5->dev,
+						   sparx5->ports[portno]->ndev);
+			if (err) {
+				dev_err(sparx5->dev,
+					"port: %02u: netdev registration failed\n",
+					portno);
+				return err;
+			}
+		}
+	return 0;
+}
+
+void sparx5_destroy_netdev(struct sparx5 *sparx5, struct sparx5_port *port)
+{
+	if (port->phylink) {
+		/* Disconnect the phy */
+		rtnl_lock();
+		sparx5_port_stop(port->ndev);
+		phylink_disconnect_phy(port->phylink);
+		rtnl_unlock();
+		phylink_destroy(port->phylink);
+		port->phylink = NULL;
+	}
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
new file mode 100644
index 000000000000..209eef5c6385
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+#define XTR_EOF_0     ntohl((__force __be32)0x80000000u)
+#define XTR_EOF_1     ntohl((__force __be32)0x80000001u)
+#define XTR_EOF_2     ntohl((__force __be32)0x80000002u)
+#define XTR_EOF_3     ntohl((__force __be32)0x80000003u)
+#define XTR_PRUNED    ntohl((__force __be32)0x80000004u)
+#define XTR_ABORT     ntohl((__force __be32)0x80000005u)
+#define XTR_ESCAPE    ntohl((__force __be32)0x80000006u)
+#define XTR_NOT_READY ntohl((__force __be32)0x80000007u)
+
+#define XTR_VALID_BYTES(x)      (4 - ((x) & 3))
+
+#define XTR_QUEUE     0
+#define INJ_QUEUE     0
+
+struct frame_info {
+	int src_port;
+};
+
+static void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp)
+{
+	/* Start flush */
+	spx5_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(grp)), sparx5, QS_XTR_FLUSH);
+
+	/* Allow to drain */
+	mdelay(1);
+
+	/* All Queues normal */
+	spx5_wr(0, sparx5, QS_XTR_FLUSH);
+}
+
+static void sparx5_ifh_parse(u32 *ifh, struct frame_info *info)
+{
+	u8 *xtr_hdr = (u8 *)ifh;
+
+	/* FWD is bit 45-72 (28 bits), but we only read the 27 LSB for now */
+	u32 fwd =
+		((u32)xtr_hdr[27] << 24) |
+		((u32)xtr_hdr[28] << 16) |
+		((u32)xtr_hdr[29] <<  8) |
+		((u32)xtr_hdr[30] <<  0);
+	fwd = (fwd >> 5);
+	info->src_port = FIELD_GET(GENMASK(7, 1), fwd);
+}
+
+static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
+{
+	int i, byte_cnt = 0;
+	bool eof_flag = false, pruned_flag = false, abort_flag = false;
+	u32 ifh[IFH_LEN];
+	struct sk_buff *skb;
+	struct frame_info fi;
+	struct sparx5_port *port;
+	struct net_device *netdev;
+	u32 *rxbuf;
+
+	/* Get IFH */
+	for (i = 0; i < IFH_LEN; i++)
+		ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
+
+	/* Decode IFH (whats needed) */
+	sparx5_ifh_parse(ifh, &fi);
+
+	/* Map to port netdev */
+	port = fi.src_port < SPX5_PORTS ?
+		sparx5->ports[fi.src_port] : NULL;
+	if (!port || !port->ndev) {
+		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
+		sparx5_xtr_flush(sparx5, grp);
+		return;
+	}
+
+	/* Have netdev, get skb */
+	netdev = port->ndev;
+	skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
+	if (!skb) {
+		sparx5_xtr_flush(sparx5, grp);
+		dev_err(sparx5->dev, "No skb allocated\n");
+		return;
+	}
+	rxbuf = (u32 *)skb->data;
+
+	/* Now, pull frame data */
+	while (!eof_flag) {
+		u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
+		u32 cmp = val;
+
+		if (byte_swap)
+			cmp = ntohl((__force __be32)val);
+
+		switch (cmp) {
+		case XTR_NOT_READY:
+			break;
+		case XTR_ABORT:
+			/* No accompanying data */
+			abort_flag = true;
+			eof_flag = true;
+			break;
+		case XTR_EOF_0:
+		case XTR_EOF_1:
+		case XTR_EOF_2:
+		case XTR_EOF_3:
+			/* This assumes STATUS_WORD_POS == 1, Status
+			 * just after last data
+			 */
+			byte_cnt -= (4 - XTR_VALID_BYTES(val));
+			eof_flag = true;
+			break;
+		case XTR_PRUNED:
+			/* But get the last 4 bytes as well */
+			eof_flag = true;
+			pruned_flag = true;
+			fallthrough;
+		case XTR_ESCAPE:
+			*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
+			byte_cnt += 4;
+			rxbuf++;
+			break;
+		default:
+			*rxbuf = val;
+			byte_cnt += 4;
+			rxbuf++;
+		}
+	}
+
+	if (abort_flag || pruned_flag || !eof_flag) {
+		netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
+			   abort_flag, pruned_flag, eof_flag);
+		kfree_skb(skb);
+		return;
+	}
+
+	if (!netif_oper_up(netdev)) {
+		netdev_err(netdev, "Discarded frame: Interface not up\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	/* Finish up skb */
+	skb_put(skb, byte_cnt - ETH_FCS_LEN);
+	eth_skb_pad(skb);
+	skb->protocol = eth_type_trans(skb, netdev);
+	netif_rx(skb);
+	netdev->stats.rx_bytes += skb->len;
+	netdev->stats.rx_packets++;
+}
+
+static int sparx5_inject(struct sparx5 *sparx5,
+			 u32 *ifh,
+			 struct sk_buff *skb)
+{
+	u32 val, w, count;
+	int grp = INJ_QUEUE;
+	u8 *buf;
+
+	val = spx5_rd(sparx5, QS_INJ_STATUS);
+	if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
+		pr_err("Injection: Queue not ready: 0x%lx\n",
+		       QS_INJ_STATUS_FIFO_RDY_GET(val));
+		return -1;
+	}
+
+	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
+		pr_err("Injection: Watermark reached: 0x%lx\n",
+		       QS_INJ_STATUS_WMARK_REACHED_GET(val));
+		return -1;
+	}
+
+	/* Indicate SOF */
+	spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
+		QS_INJ_CTRL_GAP_SIZE_SET(1),
+		sparx5, QS_INJ_CTRL(grp));
+
+	// Write the IFH to the chip.
+	for (w = 0; w < IFH_LEN; w++)
+		spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
+
+	/* Write words, round up */
+	count = ((skb->len + 3) / 4);
+	buf = skb->data;
+	for (w = 0; w < count; w++, buf += 4) {
+		val = get_unaligned((const u32 *)buf);
+		spx5_wr(val, sparx5, QS_INJ_WR(grp));
+	}
+
+	/* Add padding */
+	while (w < (60 / 4)) {
+		spx5_wr(0, sparx5, QS_INJ_WR(grp));
+		w++;
+	}
+
+	/* Indicate EOF and valid bytes in last word */
+	spx5_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |
+		QS_INJ_CTRL_VLD_BYTES_SET(skb->len < 60 ? 0 : skb->len % 4) |
+		QS_INJ_CTRL_EOF_SET(1),
+		sparx5, QS_INJ_CTRL(grp));
+
+	/* Add dummy CRC */
+	spx5_wr(0, sparx5, QS_INJ_WR(grp));
+	w++;
+
+	return NETDEV_TX_OK;
+}
+
+int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+	struct net_device_stats *stats = &dev->stats;
+	int ret;
+
+	ret = sparx5_inject(sparx5, port->ifh, skb);
+
+	if (ret == NETDEV_TX_OK) {
+		stats->tx_bytes += skb->len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_dropped++;
+	}
+
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}
+
+void sparx5_manual_injection_mode(struct sparx5 *sparx5)
+{
+	const int byte_swap = 1;
+	int portno;
+
+	/* Change mode to manual extraction and injection */
+	spx5_wr(QS_XTR_GRP_CFG_MODE_SET(1) |
+		QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(1) |
+		QS_XTR_GRP_CFG_BYTE_SWAP_SET(byte_swap),
+		sparx5, QS_XTR_GRP_CFG(XTR_QUEUE));
+	spx5_wr(QS_INJ_GRP_CFG_MODE_SET(1) |
+		QS_INJ_GRP_CFG_BYTE_SWAP_SET(byte_swap),
+		sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
+
+	/* CPU ports capture setup */
+	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
+		/* ASM CPU port: No preamble, IFH, enable padding */
+		spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
+			ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |
+			ASM_PORT_CFG_INJ_FORMAT_CFG_SET(1), /* 1 = IFH */
+			sparx5, ASM_PORT_CFG(portno));
+	}
+
+	/* Reset WM cnt to unclog queued frames */
+	for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++)
+		spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR_SET(1),
+			 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_CNT_CLR,
+			 sparx5,
+			 DSM_DEV_TX_STOP_WM_CFG(portno));
+}
+
+irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
+{
+	struct sparx5 *sparx5 = _sparx5;
+
+	/* Check data in queue */
+	while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
+		sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
new file mode 100644
index 000000000000..8166bdedaea1
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/phylink.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/sfp.h>
+
+#include "sparx5_main.h"
+
+static void sparx5_phylink_validate(struct phylink_config *config,
+				    unsigned long *supported,
+				    struct phylink_link_state *state)
+{
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	phylink_set(mask, Autoneg);
+	phylink_set_port_modes(mask);
+	phylink_set(mask, Pause);
+	phylink_set(mask, Asym_Pause);
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_10GBASER:
+	case PHY_INTERFACE_MODE_NA:
+		if (port->conf.max_speed == SPEED_25000 ||
+		    port->conf.max_speed == SPEED_10000) {
+			phylink_set(mask, 5000baseT_Full);
+			phylink_set(mask, 10000baseT_Full);
+			phylink_set(mask, 10000baseCR_Full);
+			phylink_set(mask, 10000baseSR_Full);
+			phylink_set(mask, 10000baseLR_Full);
+			phylink_set(mask, 10000baseLRM_Full);
+			phylink_set(mask, 10000baseER_Full);
+		}
+		if (port->conf.max_speed == SPEED_25000) {
+			phylink_set(mask, 25000baseCR_Full);
+			phylink_set(mask, 25000baseSR_Full);
+		}
+		if (state->interface != PHY_INTERFACE_MODE_NA)
+			break;
+		fallthrough;
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
+		phylink_set(mask, 10baseT_Half);
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Half);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 1000baseX_Full);
+		if (state->interface != PHY_INTERFACE_MODE_NA)
+			break;
+		fallthrough;
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+			phylink_set(mask, 1000baseT_Full);
+			phylink_set(mask, 1000baseX_Full);
+		}
+		if (state->interface == PHY_INTERFACE_MODE_2500BASEX ||
+		    state->interface == PHY_INTERFACE_MODE_NA) {
+			phylink_set(mask, 2500baseT_Full);
+			phylink_set(mask, 2500baseX_Full);
+		}
+		break;
+	default:
+		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+		return;
+	}
+	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_port_config *b)
+{
+	if (a->speed != b->speed ||
+	    a->portmode != b->portmode ||
+	    a->media_type != b->media_type)
+		return true;
+	return false;
+}
+
+static void sparx5_phylink_mac_config(struct phylink_config *config,
+				      unsigned int mode,
+				      const struct phylink_link_state *state)
+{
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_config conf;
+
+	conf = port->conf;
+	conf.power_down = false;
+	conf.portmode = state->interface;
+	conf.speed = state->speed;
+	conf.autoneg = state->an_enabled;
+
+	if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
+		if (state->speed == SPEED_UNKNOWN) {
+			/* When a SFP is plugged in we use capabilities to
+			 * default to the highest supported speed
+			 */
+			if (phylink_test(state->advertising, 25000baseSR_Full) ||
+			    phylink_test(state->advertising, 25000baseCR_Full))
+				conf.speed = SPEED_25000;
+			else if (state->interface == PHY_INTERFACE_MODE_10GBASER)
+				conf.speed = SPEED_10000;
+		} else if (state->speed == SPEED_2500) {
+			conf.portmode = PHY_INTERFACE_MODE_2500BASEX;
+		} else if (state->speed == SPEED_1000) {
+			conf.portmode = PHY_INTERFACE_MODE_1000BASEX;
+		}
+	}
+
+	if (state->interface == PHY_INTERFACE_MODE_10GBASER) {
+		if (phylink_test(state->advertising, FIBRE))
+			conf.media_type = ETH_MEDIA_SR;
+		else
+			conf.media_type = ETH_MEDIA_DAC;
+	}
+
+	if (!port_conf_has_changed(&port->conf, &conf))
+		return;
+}
+
+static void sparx5_phylink_mac_link_up(struct phylink_config *config,
+				       struct phy_device *phy,
+				       unsigned int mode,
+				       phy_interface_t interface,
+				       int speed, int duplex,
+				       bool tx_pause, bool rx_pause)
+{
+	/* Currently not used */
+}
+
+static void sparx5_phylink_mac_link_state(struct phylink_config *config,
+					  struct phylink_link_state *state)
+{
+	state->link = true;
+	state->an_complete = true;
+	state->speed = SPEED_1000;
+	state->duplex = true;
+	state->pause = MLO_PAUSE_AN;
+}
+
+static void sparx5_phylink_mac_aneg_restart(struct phylink_config *config)
+{
+	/* Currently not used */
+}
+
+static void sparx5_phylink_mac_link_down(struct phylink_config *config,
+					 unsigned int mode,
+					 phy_interface_t interface)
+{
+	/* Currently not used */
+}
+
+const struct phylink_mac_ops sparx5_phylink_mac_ops = {
+	.validate = sparx5_phylink_validate,
+	.mac_pcs_get_state = sparx5_phylink_mac_link_state,
+	.mac_config = sparx5_phylink_mac_config,
+	.mac_an_restart = sparx5_phylink_mac_aneg_restart,
+	.mac_link_down = sparx5_phylink_mac_link_down,
+	.mac_link_up = sparx5_phylink_mac_link_up,
+};
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 4/8] net: sparx5: add port module support
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This add configuration of the Sparx5 port module instances.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |    3 +-
 .../ethernet/microchip/sparx5/sparx5_main.c   |    9 +
 .../ethernet/microchip/sparx5/sparx5_netdev.c |   14 +-
 .../microchip/sparx5/sparx5_phylink.c         |   37 +-
 .../ethernet/microchip/sparx5/sparx5_port.c   | 1140 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_port.h   |   98 ++
 6 files changed, 1292 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.h

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 19a593d17f4a..9c14eec33fd7 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,4 +5,5 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o sparx5_packet.o sparx5_netdev.o sparx5_phylink.o
+sparx5-switch-objs  := sparx5_main.o sparx5_packet.o \
+ sparx5_netdev.o sparx5_port.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index baa108cd99b2..ea75f993f80e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 /* Switch core reset/protect */
 #define RESET_PROT_STAT		0x84
@@ -269,6 +270,14 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	spx5_port->conf.speed = SPEED_UNKNOWN;
 	spx5_port->conf.power_down = true;
 	sparx5->ports[portno] = spx5_port;
+
+	err = sparx5_port_init(sparx5, spx5_port, conf);
+	if (err) {
+		dev_err(sparx5->dev, "port init failed\n");
+		return err;
+	}
+	spx5_port->conf = *conf;
+
 	/* Create a phylink for PHY management.  Also handles SFPs */
 	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
 	spx5_port->phylink_config.type = PHYLINK_NETDEV;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index 6f9282e9d3f4..98decd1d6f57 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -5,6 +5,7 @@
  */
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 /* The IFH bit position of the first VSTAX bit. This is because the
  * VSTAX bit positions in Data sheet is starting from zero.
@@ -70,6 +71,7 @@ static int sparx5_port_open(struct net_device *ndev)
 	struct sparx5_port *port = netdev_priv(ndev);
 	int err = 0;
 
+	sparx5_port_enable(port, true);
 	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
 	if (err) {
 		netdev_err(ndev, "Could not attach to PHY\n");
@@ -81,7 +83,10 @@ static int sparx5_port_open(struct net_device *ndev)
 	if (!ndev->phydev) {
 		/* power up serdes */
 		port->conf.power_down = false;
-		err = phy_power_on(port->serdes);
+		if (port->conf.serdes_reset)
+			err = sparx5_serdes_set(port->sparx5, port, &port->conf);
+		else
+			err = phy_power_on(port->serdes);
 		if (err)
 			netdev_err(ndev, "%s failed\n", __func__);
 	}
@@ -94,12 +99,17 @@ static int sparx5_port_stop(struct net_device *ndev)
 	struct sparx5_port *port = netdev_priv(ndev);
 	int err = 0;
 
+	sparx5_port_enable(port, false);
 	phylink_stop(port->phylink);
 	phylink_disconnect_phy(port->phylink);
 
 	if (!ndev->phydev) {
+		/* power down serdes */
 		port->conf.power_down = true;
-		err = phy_power_off(port->serdes);
+		if (port->conf.serdes_reset)
+			err = sparx5_serdes_set(port->sparx5, port, &port->conf);
+		else
+			err = phy_power_off(port->serdes);
 		if (err)
 			netdev_err(ndev, "%s failed\n", __func__);
 	}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
index 8166bdedaea1..b474fc29e8df 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
@@ -11,6 +11,7 @@
 #include <linux/sfp.h>
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 static void sparx5_phylink_validate(struct phylink_config *config,
 				    unsigned long *supported,
@@ -91,6 +92,7 @@ static void sparx5_phylink_mac_config(struct phylink_config *config,
 {
 	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
 	struct sparx5_port_config conf;
+	int err = 0;
 
 	conf = port->conf;
 	conf.power_down = false;
@@ -124,6 +126,11 @@ static void sparx5_phylink_mac_config(struct phylink_config *config,
 
 	if (!port_conf_has_changed(&port->conf, &conf))
 		return;
+
+	/* Enable the PCS matching this interface type */
+	err = sparx5_port_pcs_set(port->sparx5, port, &conf);
+	if (err)
+		netdev_err(port->ndev, "port config failed: %d\n", err);
 }
 
 static void sparx5_phylink_mac_link_up(struct phylink_config *config,
@@ -133,17 +140,35 @@ static void sparx5_phylink_mac_link_up(struct phylink_config *config,
 				       int speed, int duplex,
 				       bool tx_pause, bool rx_pause)
 {
-	/* Currently not used */
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_config conf;
+	int err = 0;
+
+	conf = port->conf;
+	conf.duplex = duplex;
+	conf.pause = 0;
+	conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
+	conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
+	conf.speed = speed;
+
+	/* Configure the port to speed/duplex/pause */
+	err = sparx5_port_config(port->sparx5, port, &conf);
+	if (err)
+		netdev_err(port->ndev, "port config failed: %d\n", err);
 }
 
 static void sparx5_phylink_mac_link_state(struct phylink_config *config,
 					  struct phylink_link_state *state)
 {
-	state->link = true;
-	state->an_complete = true;
-	state->speed = SPEED_1000;
-	state->duplex = true;
-	state->pause = MLO_PAUSE_AN;
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_status status;
+
+	sparx5_get_port_status(port->sparx5, port, &status);
+	state->link = status.link && !status.link_down;
+	state->an_complete = status.an_complete;
+	state->speed = status.speed;
+	state->duplex = status.duplex;
+	state->pause = status.pause;
 }
 
 static void sparx5_phylink_mac_aneg_restart(struct phylink_config *config)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
new file mode 100644
index 000000000000..8c27a37346cc
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+
+#include "sparx5_main.h"
+#include "sparx5_port.h"
+
+#define SPX5_ETYPE_TAG_C     0x8100
+#define SPX5_ETYPE_TAG_S     0x88a8
+
+#define SPX5_WAIT_US         1000
+#define SPX5_WAIT_MAX_US     2000
+
+enum port_error {
+	SPX5_PERR_SPEED,
+	SPX5_PERR_IFTYPE,
+};
+
+static int sparx5_get_dev2g5_status(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_status *status)
+{
+	u32 value;
+	u32 lp_abil;
+	u32 portno = port->portno;
+
+	/* Get PCS Link down sticky */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_STICKY(portno));
+	status->link_down = DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY_GET(value);
+	if (status->link_down)	/* Clear the sticky */
+		spx5_wr(value, sparx5, DEV2G5_PCS1G_STICKY(portno));
+
+	/* Get both current Link and Sync status */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_LINK_STATUS(portno));
+	status->link = DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS_GET(value) &&
+		       DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS_GET(value);
+
+	if (port->conf.portmode == PHY_INTERFACE_MODE_1000BASEX)
+		status->speed = SPEED_1000;
+	else if (port->conf.portmode == PHY_INTERFACE_MODE_2500BASEX)
+		status->speed = SPEED_2500;
+
+	status->duplex = DUPLEX_FULL;
+
+	/* Get PCS ANEG status register */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_ANEG_STATUS(portno));
+	lp_abil = DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY_GET(value);
+
+	/* Aneg complete provides more information  */
+	if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
+		if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII) {
+			/* SGMII cisco aneg */
+			u32 spdvalue = ((lp_abil >> 10) & 3);
+
+			status->link = !!((lp_abil >> 15) == 1) && status->link;
+			status->an_complete = true;
+			status->duplex = (lp_abil >> 12) & 0x1 ?  DUPLEX_FULL : DUPLEX_HALF;
+			if (spdvalue == 0)
+				status->speed = SPEED_10;
+			else if (spdvalue == 1)
+				status->speed = SPEED_100;
+			else
+				status->speed = SPEED_1000;
+		} else {
+			/* Clause 37 Aneg */
+			status->link = !((lp_abil >> 12) & 3) && status->link;
+			status->an_complete = true;
+			status->duplex = ((lp_abil >> 5) & 1) ? DUPLEX_FULL : DUPLEX_UNKNOWN;
+			if ((lp_abil >> 8) & 1) /* symmetric pause */
+				status->pause = MLO_PAUSE_RX | MLO_PAUSE_TX;
+			if (lp_abil & (1 << 7)) /* asymmetric pause */
+				status->pause |= MLO_PAUSE_RX;
+		}
+	}
+	return 0;
+}
+
+static int sparx5_get_100fx_status(struct sparx5 *sparx5,
+				   struct sparx5_port *port,
+				   struct sparx5_port_status *status)
+{
+	u32 value, portno = port->portno;
+
+	/* Get the PCS status  */
+	value = spx5_rd(sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+
+	/* Link has been down if the are any error stickies */
+	status->link_down =
+		DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_FEF_STATUS_GET(value);
+
+	if (status->link_down) {
+		/* Clear the stickies and re-read */
+		spx5_wr(value, sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+		usleep_range(SPX5_WAIT_US, SPX5_WAIT_MAX_US);
+		value = spx5_rd(sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+	}
+	/* Link=1 if sync status=1 and no error stickies after a clear */
+	status->link =
+		DEV2G5_PCS_FX100_STATUS_SYNC_STATUS_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_FEF_STATUS_GET(value);
+	status->speed = SPEED_100;
+	status->duplex = DUPLEX_FULL;
+	return 0;
+}
+
+static int sparx5_get_sfi_status(struct sparx5 *sparx5,
+				 struct sparx5_port *port,
+				 struct sparx5_port_status *status)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(&port->conf);
+	u32 portno = port->portno;
+	u32 value, dev, tinst;
+	void __iomem *inst;
+
+	if (!high_speed_dev) {
+		netdev_err(port->ndev, "error: low speed and SFI mode\n");
+		return -EINVAL;
+	}
+
+	dev = sparx5_to_high_dev(portno);
+	tinst = sparx5_port_dev_index(portno);
+	inst = spx5_inst_get(sparx5, dev, tinst);
+
+	value = spx5_inst_rd(inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+	if (value != DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY) {
+		/* The link is or has been down. Clear the sticky bit */
+		status->link_down = 1;
+		spx5_inst_wr(0xffffffff, inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+		value = spx5_inst_rd(inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+	}
+	status->link = (value == DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY);
+	status->speed = port->conf.speed;
+	status->duplex = DUPLEX_FULL;
+	return 0;
+}
+
+/* Get link status of 100FX, 1000Base-X/in-band and SFI ports.
+ */
+int sparx5_get_port_status(struct sparx5 *sparx5,
+			   struct sparx5_port *port,
+			   struct sparx5_port_status *status)
+{
+	memset(status, 0, sizeof(*status));
+	status->speed = port->conf.speed;
+	if (port->conf.power_down) {
+		status->link = false;
+		return 0;
+	}
+	switch (port->conf.portmode) {
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (port->conf.speed == SPEED_100)
+			return sparx5_get_100fx_status(sparx5, port, status);
+		return sparx5_get_dev2g5_status(sparx5, port, status);
+	case PHY_INTERFACE_MODE_10GBASER:
+		return sparx5_get_sfi_status(sparx5, port, status);
+	case PHY_INTERFACE_MODE_NA:
+		return 0;
+	default:
+		netdev_err(port->ndev, "Status not supported");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int sparx5_port_error(struct sparx5_port *port,
+			     struct sparx5_port_config *conf,
+			     enum port_error errtype)
+{
+	switch (errtype) {
+	case SPX5_PERR_SPEED:
+		netdev_err(port->ndev,
+			   "Interface does not support speed: %u: for %s\n",
+			   conf->speed, phy_modes(conf->portmode));
+		break;
+	case SPX5_PERR_IFTYPE:
+		netdev_err(port->ndev,
+			   "Switch port does not support interface type: %s\n",
+			   phy_modes(conf->portmode));
+		break;
+	default:
+		netdev_err(port->ndev,
+			   "Interface configuration error\n");
+	}
+
+	return -EINVAL;
+}
+
+static int sparx5_port_verify_speed(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_config *conf)
+{
+	if ((sparx5_port_is_2g5(port->portno) &&
+	     conf->speed > SPEED_2500) ||
+	    (sparx5_port_is_5g(port->portno)  &&
+	     conf->speed > SPEED_5000) ||
+	    (sparx5_port_is_10g(port->portno) &&
+	     conf->speed > SPEED_10000))
+		return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+
+	switch (conf->portmode) {
+	case PHY_INTERFACE_MODE_NA:
+		return -EINVAL;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		if ((conf->speed != SPEED_100 && /* This is for 100BASE-FX */
+		     conf->speed != SPEED_1000) ||
+		    sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		if (sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (conf->speed != SPEED_2500 ||
+		    sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+		if (port->portno > 47)
+			return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+		fallthrough;
+	case PHY_INTERFACE_MODE_SGMII:
+		if (conf->speed != SPEED_1000 &&
+		    conf->speed != SPEED_100 &&
+		    conf->speed != SPEED_10 &&
+		    conf->speed != SPEED_2500)
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	case PHY_INTERFACE_MODE_10GBASER:
+		if ((conf->speed != SPEED_5000 &&
+		     conf->speed != SPEED_10000 &&
+		     conf->speed != SPEED_25000))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	default:
+		return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+	}
+	return 0;
+}
+
+static bool sparx5_dev_change(struct sparx5 *sparx5,
+			      struct sparx5_port *port,
+			      struct sparx5_port_config *conf)
+{
+	if (port->conf.portmode != conf->portmode)
+		if (port->conf.portmode == PHY_INTERFACE_MODE_10GBASER ||
+		    conf->portmode == PHY_INTERFACE_MODE_10GBASER)
+			return true;
+
+	return false;
+}
+
+static int sparx5_port_flush_poll(struct sparx5 *sparx5, u32 portno)
+{
+	u32  value, resource, prio, delay_cnt = 0;
+	char *mem = "";
+	bool poll_src = true;
+
+	/* Resource == 0: Memory tracked per source (SRC-MEM)
+	 * Resource == 1: Frame references tracked per source (SRC-REF)
+	 * Resource == 2: Memory tracked per destination (DST-MEM)
+	 * Resource == 3: Frame references tracked per destination. (DST-REF)
+	 */
+	while (1) {
+		bool empty = true;
+
+		for (resource = 0; resource < (poll_src ? 2 : 1); resource++) {
+			u32 base;
+
+			base = (resource == 0 ? 2048 : 0) + SPX5_PRIOS * portno;
+			for (prio = 0; prio < SPX5_PRIOS; prio++) {
+				value = spx5_rd(sparx5,
+						QRES_RES_STAT(base + prio));
+				if (value) {
+					mem = resource == 0 ?
+						"DST-MEM" : "SRC-MEM";
+					empty = false;
+				}
+			}
+		}
+
+		if (empty)
+			break;
+
+		if (delay_cnt++ == 2000) {
+			dev_err(sparx5->dev,
+				"Flush timeout port %u. %s queue not empty\n",
+				portno, mem);
+			return -EINVAL;
+		}
+
+		usleep_range(SPX5_WAIT_US, SPX5_WAIT_MAX_US);
+	}
+	return 0;
+}
+
+static int sparx5_port_disable(struct sparx5 *sparx5, struct sparx5_port *port, bool high_spd_dev)
+{
+	u32 dev               = high_spd_dev ? sparx5_to_high_dev(port->portno) : TARGET_DEV2G5;
+	u32 tinst             = high_spd_dev ? sparx5_port_dev_index(port->portno) : port->portno;
+	void __iomem *devinst = spx5_inst_get(sparx5, dev, tinst);
+	u32 spd               = port->conf.speed;
+	u32 spd_prm;
+	int err;
+
+	if (high_spd_dev) {
+		/* 1: Reset the PCS Rx clock domain  */
+		spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_RX_RST,
+			      DEV10G_DEV_RST_CTRL_PCS_RX_RST,
+			      devinst,
+			      DEV10G_DEV_RST_CTRL(0));
+
+		/* 2: Disable MAC frame reception */
+		spx5_inst_rmw(0,
+			      DEV10G_MAC_ENA_CFG_RX_ENA,
+			      devinst,
+			      DEV10G_MAC_ENA_CFG(0));
+	} else {
+		/* 1: Reset the PCS Rx clock domain  */
+		spx5_inst_rmw(DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+			      devinst,
+			      DEV2G5_DEV_RST_CTRL(0));
+		/* 2: Disable MAC frame reception */
+		spx5_inst_rmw(0,
+			      DEV2G5_MAC_ENA_CFG_RX_ENA,
+			      devinst,
+			      DEV2G5_MAC_ENA_CFG(0));
+	}
+	/* 3: Disable traffic being sent to or from switch port->portno */
+	spx5_rmw(0,
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+
+	/* 4: Disable dequeuing from the egress queues  */
+	spx5_rmw(HSCH_PORT_MODE_DEQUEUE_DIS,
+		 HSCH_PORT_MODE_DEQUEUE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* 5: Disable Flowcontrol */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_STOP_SET(0xFFF - 1),
+		 QSYS_PAUSE_CFG_PAUSE_STOP,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	spd_prm = spd == SPEED_10 ? 1000 : spd == SPEED_100 ? 100 : 10;
+	/* 6: Wait while the last frame is exiting the queues */
+	usleep_range(8 * spd_prm, 10 * spd_prm);
+
+	/* 7: Flush the queues accociated with the port->portno */
+	spx5_rmw(HSCH_FLUSH_CTRL_FLUSH_PORT_SET(port->portno) |
+		 HSCH_FLUSH_CTRL_FLUSH_DST_SET(1) |
+		 HSCH_FLUSH_CTRL_FLUSH_SRC_SET(1) |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA_SET(1),
+		 HSCH_FLUSH_CTRL_FLUSH_PORT |
+		 HSCH_FLUSH_CTRL_FLUSH_DST |
+		 HSCH_FLUSH_CTRL_FLUSH_SRC |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA,
+		 sparx5,
+		 HSCH_FLUSH_CTRL);
+
+	/* 8: Enable dequeuing from the egress queues */
+	spx5_rmw(0,
+		 HSCH_PORT_MODE_DEQUEUE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* 9: Wait until flushing is complete */
+	err = sparx5_port_flush_poll(sparx5, port->portno);
+	if (err)
+		return err;
+
+	/* 10: Reset the  MAC clock domain */
+	if (high_spd_dev) {
+		spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_TX_RST_SET(1) |
+			      DEV10G_DEV_RST_CTRL_MAC_RX_RST_SET(1) |
+			      DEV10G_DEV_RST_CTRL_MAC_TX_RST_SET(1),
+			      DEV10G_DEV_RST_CTRL_PCS_TX_RST |
+			      DEV10G_DEV_RST_CTRL_MAC_RX_RST |
+			      DEV10G_DEV_RST_CTRL_MAC_TX_RST,
+			      devinst,
+			      DEV10G_DEV_RST_CTRL(0));
+
+	} else {
+		spx5_inst_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(3)    |
+			      DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_MAC_TX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_MAC_RX_RST_SET(1),
+			      DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+			      DEV2G5_DEV_RST_CTRL_PCS_TX_RST |
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST |
+			      DEV2G5_DEV_RST_CTRL_MAC_TX_RST |
+			      DEV2G5_DEV_RST_CTRL_MAC_RX_RST,
+			      devinst,
+			      DEV2G5_DEV_RST_CTRL(0));
+	}
+	/* 11: Clear flushing */
+	spx5_rmw(HSCH_FLUSH_CTRL_FLUSH_PORT_SET(port->portno) |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA_SET(0),
+		 HSCH_FLUSH_CTRL_FLUSH_PORT |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA,
+		 sparx5,
+		 HSCH_FLUSH_CTRL);
+
+	if (high_spd_dev) {
+		u32 pcs = sparx5_to_pcs_dev(port->portno);
+		void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, tinst);
+
+		/* 12: Disable 5G/10G/25 BaseR PCS */
+		spx5_inst_rmw(PCS10G_BR_PCS_CFG_PCS_ENA_SET(0),
+			      PCS10G_BR_PCS_CFG_PCS_ENA,
+			      pcsinst,
+			      PCS10G_BR_PCS_CFG(0));
+
+		if (sparx5_port_is_25g(port->portno))
+			/* Disable 25G PCS */
+			spx5_rmw(DEV25G_PCS25G_CFG_PCS25G_ENA_SET(0),
+				 DEV25G_PCS25G_CFG_PCS25G_ENA,
+				 sparx5,
+				 DEV25G_PCS25G_CFG(tinst));
+	} else {
+		/* 12: Disable 1G/100fx PCS */
+		spx5_rmw(DEV2G5_PCS1G_CFG_PCS_ENA_SET(0),
+			 DEV2G5_PCS1G_CFG_PCS_ENA,
+			 sparx5,
+			 DEV2G5_PCS1G_CFG(port->portno));
+		spx5_rmw(DEV2G5_PCS_FX100_CFG_PCS_ENA_SET(0),
+			 DEV2G5_PCS_FX100_CFG_PCS_ENA,
+			 sparx5,
+			 DEV2G5_PCS_FX100_CFG(port->portno));
+	}
+
+	/* The port is now flushed and disabled  */
+	return 0;
+}
+
+static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
+			       u32 portno, u32 speed)
+{
+	u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
+	u32 mac_width  = 8;
+	u32 fifo_width = 16;
+	u32 addition   = 0;
+	u32 mac_per    = 6400, tmp1, tmp2, tmp3;
+	u32 taxi_dist[SPX5_PORTS_ALL] = {
+		6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
+		4, 4, 4, 4,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		4, 6, 8, 4, 6, 8, 6, 8,
+		2, 2, 2, 2, 2, 2, 2, 4, 2
+	};
+
+	switch (speed) {
+	case SPEED_25000:
+		return 0;
+	case SPEED_10000:
+		mac_per = 6400;
+		mac_width = 8;
+		addition = 1;
+		break;
+	case SPEED_5000:
+		mac_per = 12800;
+		mac_width = 8;
+		addition = 0;
+		break;
+	case SPEED_2500:
+		mac_per = 3200;
+		mac_width = 1;
+		addition = 0;
+		break;
+	case SPEED_1000:
+		mac_per =  8000;
+		mac_width = 1;
+		addition = 0;
+		break;
+	case SPEED_100:
+	case SPEED_10:
+		return 1;
+	default:
+		break;
+	}
+
+	tmp1 = 1000 * mac_width / fifo_width;
+	tmp2 = 3000 + ((12000 + 2 * taxi_dist[portno] * 1000)
+		       * sys_clk / mac_per);
+	tmp3 = tmp1 * tmp2 / 1000;
+	return  (tmp3 + 2000 + 999) / 1000 + addition;
+}
+
+/* Configure port muxing:
+ * QSGMII:     4x2G5 devices
+ */
+static int sparx5_port_mux_set(struct sparx5 *sparx5,
+			       struct sparx5_port *port,
+			       struct sparx5_port_config *conf)
+{
+	u32 inst;
+	u32 portno = port->portno;
+
+	if (port->conf.portmode == conf->portmode)
+		return 0; /* Nothing to do */
+
+	switch (conf->portmode) {
+	case PHY_INTERFACE_MODE_QSGMII: /* QSGMII: 4x2G5 devices. Mode Q'  */
+		inst = (portno - portno % 4) / 4;
+		spx5_rmw(BIT(inst),
+			 BIT(inst),
+			 sparx5,
+			 PORT_CONF_QSGMII_ENA);
+
+		if ((portno / 4 % 2) == 0) {
+			/* Affects d0-d3,d8-d11..d40-d43 */
+			spx5_rmw(PORT_CONF_USGMII_CFG_BYPASS_SCRAM_SET(1) |
+				 PORT_CONF_USGMII_CFG_BYPASS_DESCRAM_SET(1) |
+				 PORT_CONF_USGMII_CFG_QUAD_MODE_SET(1),
+				 PORT_CONF_USGMII_CFG_BYPASS_SCRAM |
+				 PORT_CONF_USGMII_CFG_BYPASS_DESCRAM |
+				 PORT_CONF_USGMII_CFG_QUAD_MODE,
+				 sparx5,
+				 PORT_CONF_USGMII_CFG((portno / 8)));
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
+				    struct sparx5_port *port)
+{
+	u32 etype;
+	enum sparx5_port_max_tags max_tags    = port->max_vlan_tags;
+	enum sparx5_vlan_port_type vlan_type  = port->vlan_type;
+	bool dotag          = max_tags != SPX5_PORT_MAX_TAGS_NONE;
+	int tag_ct          = max_tags == SPX5_PORT_MAX_TAGS_ONE ? 1 :
+			      max_tags == SPX5_PORT_MAX_TAGS_TWO ? 2 : 0;
+	bool dtag           = max_tags == SPX5_PORT_MAX_TAGS_TWO;
+	u32 dev             = sparx5_to_high_dev(port->portno);
+	u32 tinst           = sparx5_port_dev_index(port->portno);
+	void __iomem *inst  = spx5_inst_get(sparx5, dev, tinst);
+
+	etype = (vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ?
+		 port->custom_etype :
+		 vlan_type == SPX5_VLAN_PORT_TYPE_C ?
+		 SPX5_ETYPE_TAG_C : SPX5_ETYPE_TAG_S);
+
+	spx5_wr(DEV2G5_MAC_TAGS_CFG_TAG_ID_SET(etype) |
+		DEV2G5_MAC_TAGS_CFG_PB_ENA_SET(dtag) |
+		DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(dotag) |
+		DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(dotag),
+		sparx5,
+		DEV2G5_MAC_TAGS_CFG(port->portno));
+
+	if (sparx5_port_is_2g5(port->portno))
+		return 0;
+
+	spx5_inst_rmw(DEV10G_MAC_TAGS_CFG_TAG_ID_SET(etype) |
+		      DEV10G_MAC_TAGS_CFG_TAG_ENA_SET(dotag),
+		      DEV10G_MAC_TAGS_CFG_TAG_ID |
+		      DEV10G_MAC_TAGS_CFG_TAG_ENA,
+		      inst,
+		      DEV10G_MAC_TAGS_CFG(0, 0));
+
+	spx5_inst_rmw(DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS_SET(tag_ct),
+		      DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS,
+		      inst,
+		      DEV10G_MAC_NUM_TAGS_CFG(0));
+
+	spx5_inst_rmw(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_SET(dotag),
+		      DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK,
+		      inst,
+		      DEV10G_MAC_MAXLEN_CFG(0));
+	return 0;
+}
+
+static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)
+{
+	u32 clk_period_ps = 1600; /* 625Mhz for now */
+	u32 urg = 672000;
+
+	switch (speed) {
+	case SPEED_10:
+	case SPEED_100:
+	case SPEED_1000:
+		urg = 672000;
+		break;
+	case SPEED_2500:
+		urg = 270000;
+		break;
+	case SPEED_5000:
+		urg = 135000;
+		break;
+	case SPEED_10000:
+		urg = 67200;
+		break;
+	case SPEED_25000:
+		urg = 27000;
+		break;
+	}
+	return urg / clk_period_ps - 1;
+}
+
+static u16 sparx5_wm_enc(u16 value)
+{
+	if (value >= 2048)
+		return 2048 + value / 16;
+
+	return value;
+}
+
+static int sparx5_port_fc_setup(struct sparx5 *sparx5,
+				struct sparx5_port *port,
+				struct sparx5_port_config *conf)
+{
+	bool fc_obey = conf->pause & MLO_PAUSE_RX ? 1 : 0;
+	u32 pause_stop = 0xFFF - 1; /* FC generate disabled */
+
+	if (conf->pause & MLO_PAUSE_TX) {
+		int maxlen = ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN;
+
+		pause_stop = sparx5_wm_enc(4  * (maxlen / SPX5_BUFFER_CELL_SZ));
+	}
+
+	/* Set HDX flowcontrol */
+	spx5_rmw(DSM_MAC_CFG_HDX_BACKPREASSURE_SET(conf->duplex == DUPLEX_HALF),
+		 DSM_MAC_CFG_HDX_BACKPREASSURE,
+		 sparx5,
+		 DSM_MAC_CFG(port->portno));
+
+	/* Obey flowcontrol  */
+	spx5_rmw(DSM_RX_PAUSE_CFG_RX_PAUSE_EN_SET(fc_obey),
+		 DSM_RX_PAUSE_CFG_RX_PAUSE_EN,
+		 sparx5,
+		 DSM_RX_PAUSE_CFG(port->portno));
+
+	/* Disable forward pressure */
+	spx5_rmw(QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS_SET(fc_obey),
+		 QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS,
+		 sparx5,
+		 QSYS_FWD_PRESSURE(port->portno));
+
+	/* Generate pause frames */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_STOP_SET(pause_stop),
+		 QSYS_PAUSE_CFG_PAUSE_STOP,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	return 0;
+}
+
+static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
+{
+	if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37 aneg */
+		return ((1 << 14) | /* ack */
+		((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
+		((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
+		(1 << 5)); /* FDX only */
+
+	return 1; /* Enable SGMII Aneg */
+}
+
+int sparx5_serdes_set(struct sparx5 *sparx5,
+		      struct sparx5_port *port,
+		      struct sparx5_port_config *conf)
+{
+	union phy_configure_opts opts = {
+		.eth_serdes.speed = conf->speed,
+		.eth_serdes.media_type = conf->media_type,
+	};
+	int err;
+
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII &&
+	    ((port->portno % 4) != 0)) {
+		return 0;
+	}
+	err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, conf->portmode);
+	if (err)
+		return err;
+	if (conf->serdes_reset) {
+		err = phy_reset(port->serdes);
+		if (err)
+			return err;
+	}
+	/* Configure SerDes with port parameters */
+	err = phy_configure(port->serdes, &opts);
+	if (err)
+		return err;
+	conf->serdes_reset = false;
+	return err;
+}
+
+int sparx5_port_pcs_low_set(struct sparx5 *sparx5,
+			    struct sparx5_port *port,
+			    struct sparx5_port_config *conf)
+{
+	bool sgmii = false, inband_aneg = false;
+	int err;
+
+	if (sparx5_use_cu_phy(port)) {
+		sgmii = true; /* Phy is connnected to the MAC */
+	} else {
+		if (conf->portmode == PHY_INTERFACE_MODE_SGMII ||
+		    conf->portmode == PHY_INTERFACE_MODE_QSGMII)
+			inband_aneg = true; /* Cisco-SGMII in-band-aneg */
+		else if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX &&
+			 conf->autoneg)
+			inband_aneg = true; /* Clause-37 in-band-aneg */
+
+		err = sparx5_serdes_set(sparx5, port, conf);
+		if (err) {
+			pr_info("Could not serdes_set\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Choose SGMII or 1000BaseX/2500BaseX PCS mode */
+	spx5_rmw(DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(sgmii),
+		 DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA,
+		 sparx5,
+		 DEV2G5_PCS1G_MODE_CFG(port->portno));
+
+	/* Enable PCS */
+	spx5_wr(DEV2G5_PCS1G_CFG_PCS_ENA_SET(1),
+		sparx5,
+		DEV2G5_PCS1G_CFG(port->portno));
+
+	if (inband_aneg) {
+		u16 abil = sparx5_get_aneg_word(conf);
+
+		/* Enable in-band aneg */
+		spx5_wr(DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY_SET(abil) |
+			DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA_SET(1) |
+			DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA_SET(1) |
+			DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT_SET(1),
+			sparx5,
+			DEV2G5_PCS1G_ANEG_CFG(port->portno));
+	} else {
+		spx5_wr(0, sparx5, DEV2G5_PCS1G_ANEG_CFG(port->portno));
+	}
+
+	/* Take PCS out of reset */
+	spx5_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(2) |
+		 DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(0) |
+		 DEV2G5_DEV_RST_CTRL_PCS_RX_RST_SET(0),
+		 DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+		 DEV2G5_DEV_RST_CTRL_PCS_TX_RST |
+		 DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+		 sparx5,
+		 DEV2G5_DEV_RST_CTRL(port->portno));
+
+	return 0;
+}
+
+static int sparx5_port_pcs_high_set(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_config *conf)
+{
+	int err;
+	u32 dev               = sparx5_to_high_dev(port->portno);
+	u32 pix               = sparx5_port_dev_index(port->portno);
+	void __iomem *devinst = spx5_inst_get(sparx5, dev, pix);
+	u32 pcs               = sparx5_to_pcs_dev(port->portno);
+	void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, pix);
+	u32 clk_spd           = conf->speed == SPEED_5000 ? 1 : 0;
+
+	/*  SFI : No in-band-aneg. Speeds 5G/10G/25G */
+
+	err = sparx5_serdes_set(sparx5, port, conf);
+	if (err)
+		return -EINVAL;
+
+	if (conf->speed == SPEED_25000) {
+		/* Enable PCS for 25G device, speed 25G */
+		spx5_rmw(DEV25G_PCS25G_CFG_PCS25G_ENA_SET(1),
+			 DEV25G_PCS25G_CFG_PCS25G_ENA,
+			 sparx5,
+			 DEV25G_PCS25G_CFG(pix));
+	} else {
+		/* Enable PCS for 5G/10G/25G devices, speed 5G/10G */
+		spx5_inst_rmw(PCS10G_BR_PCS_CFG_PCS_ENA_SET(1),
+			      PCS10G_BR_PCS_CFG_PCS_ENA,
+			      pcsinst,
+			      PCS10G_BR_PCS_CFG(0));
+	}
+
+	/* Enable 5G/10G/25G MAC module */
+	spx5_inst_wr(DEV10G_MAC_ENA_CFG_RX_ENA_SET(1) |
+		     DEV10G_MAC_ENA_CFG_TX_ENA_SET(1),
+		     devinst,
+		     DEV10G_MAC_ENA_CFG(0));
+
+	/* Take the device out of reset */
+	spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_RX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_PCS_TX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_MAC_RX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_MAC_TX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_SPEED_SEL_SET(clk_spd),
+		      DEV10G_DEV_RST_CTRL_PCS_RX_RST |
+		      DEV10G_DEV_RST_CTRL_PCS_TX_RST |
+		      DEV10G_DEV_RST_CTRL_MAC_RX_RST |
+		      DEV10G_DEV_RST_CTRL_MAC_TX_RST |
+		      DEV10G_DEV_RST_CTRL_SPEED_SEL,
+		      devinst,
+		      DEV10G_DEV_RST_CTRL(0));
+
+	return 0;
+}
+
+/* Switch between 1G/2500 and 5G/10G/25G devices */
+static void sparx5_dev_switch(struct sparx5 *sparx5, int port, bool high_speed_dev)
+{
+	int bt_indx = BIT(sparx5_port_dev_index(port));
+
+	if (sparx5_port_is_5g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV5G_MODES);
+	} else if (sparx5_port_is_10g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV10G_MODES);
+	} else if (sparx5_port_is_25g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV25G_MODES);
+	}
+}
+
+/* Configure speed/duplex dependent registers */
+static int sparx5_port_config_low_set(struct sparx5 *sparx5,
+				      struct sparx5_port *port,
+				      struct sparx5_port_config *conf)
+{
+	bool fdx      = conf->duplex == DUPLEX_FULL;
+	int spd       = conf->speed;
+	u32 clk_spd   = spd == SPEED_10 ? 0 : spd == SPEED_100 ? 1 : 2;
+	u32 gig_mode  = spd == SPEED_1000 || spd == SPEED_2500;
+	u32 tx_gap    = spd == SPEED_1000 ? 4 : fdx ? 6 : 5;
+	u32 hdx_gap_1 = spd == SPEED_1000 ? 0 : spd == SPEED_100 ? 1 : 2;
+	u32 hdx_gap_2 = spd == SPEED_1000 ? 0 : spd == SPEED_100 ? 4 : 1;
+
+	/* GIG/FDX mode */
+	spx5_rmw(DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA_SET(gig_mode) |
+		 DEV2G5_MAC_MODE_CFG_FDX_ENA_SET(fdx),
+		 DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA |
+		 DEV2G5_MAC_MODE_CFG_FDX_ENA,
+		 sparx5,
+		 DEV2G5_MAC_MODE_CFG(port->portno));
+
+	/* Set MAC IFG Gaps */
+	spx5_wr(DEV2G5_MAC_IFG_CFG_TX_IFG_SET(tx_gap) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG1_SET(hdx_gap_1) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG2_SET(hdx_gap_2),
+		sparx5,
+		DEV2G5_MAC_IFG_CFG(port->portno));
+
+	/* Disabling frame aging when in HDX (due to HDX issue) */
+	spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(fdx == 0),
+		 HSCH_PORT_MODE_AGE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* Enable MAC module */
+	spx5_wr(DEV2G5_MAC_ENA_CFG_RX_ENA |
+		DEV2G5_MAC_ENA_CFG_TX_ENA,
+		sparx5,
+		DEV2G5_MAC_ENA_CFG(port->portno));
+
+	/* Select speed and take MAC out of reset */
+	spx5_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(clk_spd) |
+		 DEV2G5_DEV_RST_CTRL_MAC_TX_RST_SET(0) |
+		 DEV2G5_DEV_RST_CTRL_MAC_RX_RST_SET(0),
+		 DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+		 DEV2G5_DEV_RST_CTRL_MAC_TX_RST |
+		 DEV2G5_DEV_RST_CTRL_MAC_RX_RST,
+		 sparx5,
+		 DEV2G5_DEV_RST_CTRL(port->portno));
+
+	return 0;
+}
+
+int sparx5_port_pcs_set(struct sparx5 *sparx5,
+			struct sparx5_port *port,
+			struct sparx5_port_config *conf)
+
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(conf);
+	int err;
+
+	if (sparx5_dev_change(sparx5, port, conf)) {
+		/* switch device */
+		sparx5_dev_switch(sparx5, port->portno, high_speed_dev);
+
+		/* Disable the not-in-use device */
+		err = sparx5_port_disable(sparx5, port, !high_speed_dev);
+		if (err)
+			return err;
+	}
+	/* Disable the port before re-configuring */
+	err = sparx5_port_disable(sparx5, port, high_speed_dev);
+	if (err)
+		return -EINVAL;
+
+	if (high_speed_dev)
+		err = sparx5_port_pcs_high_set(sparx5, port, conf);
+	else
+		err = sparx5_port_pcs_low_set(sparx5, port, conf);
+
+	if (err)
+		return -EINVAL;
+
+	if (!sparx5_use_cu_phy(port)) {
+		/* Enable/disable 1G counters in ASM */
+		spx5_rmw(ASM_PORT_CFG_CSC_STAT_DIS_SET(high_speed_dev),
+			 ASM_PORT_CFG_CSC_STAT_DIS,
+			 sparx5,
+			 ASM_PORT_CFG(port->portno));
+
+		/* Enable/disable 1G counters in DSM */
+		spx5_rmw(DSM_BUF_CFG_CSC_STAT_DIS_SET(high_speed_dev),
+			 DSM_BUF_CFG_CSC_STAT_DIS,
+			 sparx5,
+			 DSM_BUF_CFG(port->portno));
+	}
+
+	port->conf = *conf;
+
+	return 0;
+}
+
+int sparx5_port_config(struct sparx5 *sparx5,
+		       struct sparx5_port *port,
+		       struct sparx5_port_config *conf)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(conf);
+	int err, urgency, stop_wm;
+
+	err = sparx5_port_verify_speed(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* high speed device is already configured */
+	if (!high_speed_dev)
+		sparx5_port_config_low_set(sparx5, port, conf);
+
+	/* Configure flow control */
+	err = sparx5_port_fc_setup(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* Set the DSM stop watermark */
+	stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf->speed);
+	spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
+		 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
+		 sparx5,
+		 DSM_DEV_TX_STOP_WM_CFG(port->portno));
+
+	/* Enable port forwarding */
+	urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
+	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
+		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA |
+		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+
+	/* Save the new values */
+	port->conf = *conf;
+
+	return 0;
+}
+
+/* Initialize port config to default */
+int sparx5_port_init(struct sparx5 *sparx5,
+		     struct sparx5_port *port,
+		     struct sparx5_port_config *conf)
+{
+	int err;
+	u32 devhigh           = sparx5_to_high_dev(port->portno);
+	u32 pix               = sparx5_port_dev_index(port->portno);
+	void __iomem *devinst = spx5_inst_get(sparx5, devhigh, pix);
+	u32 pcs               = sparx5_to_pcs_dev(port->portno);
+	void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, pix);
+	int maxlen            = ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN;
+	bool sd_pol           = port->signd_active_high;
+	bool sd_ena           = port->signd_enable;
+	bool sd_sel           = !port->signd_internal;
+	u32 atop              = sparx5_wm_enc(20 * (maxlen / SPX5_BUFFER_CELL_SZ));
+	u32 pause_start       = sparx5_wm_enc(6  * (maxlen / SPX5_BUFFER_CELL_SZ));
+	u32 pause_stop        = 0xFFF - 1; /* FC generate disabled */
+
+	/* Set the mux port mode  */
+	err = sparx5_port_mux_set(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* Configure MAC vlan awareness */
+	err = sparx5_port_max_tags_set(sparx5, port);
+	if (err)
+		return err;
+
+	/* Set Max Length */
+	spx5_rmw(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_SET(maxlen),
+		 DEV2G5_MAC_MAXLEN_CFG_MAX_LEN,
+		 sparx5,
+		 DEV2G5_MAC_MAXLEN_CFG(port->portno));
+
+	/* 1G/2G5: Signal Detect configuration */
+	spx5_wr(DEV2G5_PCS1G_SD_CFG_SD_POL_SET(sd_pol) |
+		DEV2G5_PCS1G_SD_CFG_SD_SEL_SET(sd_sel) |
+		DEV2G5_PCS1G_SD_CFG_SD_ENA_SET(sd_ena),
+		sparx5,
+		DEV2G5_PCS1G_SD_CFG(port->portno));
+
+	/* 100fx: Signal Detect configuration */
+	spx5_rmw(DEV2G5_PCS_FX100_CFG_SD_POL_SET(sd_pol) |
+		 DEV2G5_PCS_FX100_CFG_SD_SEL_SET(sd_sel) |
+		 DEV2G5_PCS_FX100_CFG_SD_ENA_SET(sd_ena),
+		 DEV2G5_PCS_FX100_CFG_SD_POL |
+		 DEV2G5_PCS_FX100_CFG_SD_SEL |
+		 DEV2G5_PCS_FX100_CFG_SD_ENA,
+		 sparx5,
+		 DEV2G5_PCS_FX100_CFG(port->portno));
+
+	/* Set Pause WM hysteresis */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_START_SET(pause_start) |
+		 QSYS_PAUSE_CFG_PAUSE_STOP_SET(pause_stop) |
+		 QSYS_PAUSE_CFG_PAUSE_ENA_SET(1),
+		 QSYS_PAUSE_CFG_PAUSE_START |
+		 QSYS_PAUSE_CFG_PAUSE_STOP |
+		 QSYS_PAUSE_CFG_PAUSE_ENA,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	/* Port ATOP. Frames are tail dropped when this WM is hit */
+	spx5_wr(QSYS_ATOP_ATOP_SET(atop),
+		sparx5,
+		QSYS_ATOP(port->portno));
+
+	/* Discard pause frame 01-80-C2-00-00-01 */
+	spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));
+
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII ||
+	    conf->portmode == PHY_INTERFACE_MODE_SGMII) {
+		err = sparx5_serdes_set(sparx5, port, conf);
+		if (err)
+			return err;
+
+		if (!sparx5_port_is_2g5(port->portno))
+			/* Enable shadow device */
+			spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA_SET(1),
+				 DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA,
+				 sparx5,
+				 DSM_DEV_TX_STOP_WM_CFG(port->portno));
+
+		sparx5_dev_switch(sparx5, port->portno, false);
+	}
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII) {
+		// All ports must be PCS enabled in QSGMII mode
+		spx5_rmw(DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(0),
+			 DEV2G5_DEV_RST_CTRL_PCS_TX_RST,
+			 sparx5,
+			 DEV2G5_DEV_RST_CTRL(port->portno));
+	}
+	/* Default IFGs for 1G */
+	spx5_wr(DEV2G5_MAC_IFG_CFG_TX_IFG_SET(6) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG1_SET(0) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG2_SET(0),
+		sparx5,
+		DEV2G5_MAC_IFG_CFG(port->portno));
+
+	if (sparx5_port_is_2g5(port->portno))
+		return 0; /* Low speed device only - return */
+
+	/* Now setup the high speed device */
+	if (conf->portmode == PHY_INTERFACE_MODE_NA)
+		conf->portmode = PHY_INTERFACE_MODE_10GBASER;
+
+	if (sparx5_is_high_speed_device(conf))
+		sparx5_dev_switch(sparx5, port->portno, true);
+
+	/* Set Max Length */
+	spx5_inst_rmw(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_SET(maxlen),
+		      DEV10G_MAC_MAXLEN_CFG_MAX_LEN,
+		      devinst,
+		      DEV10G_MAC_ENA_CFG(0));
+
+	/* Handle Signal Detect in 10G PCS */
+	spx5_inst_wr(PCS10G_BR_PCS_SD_CFG_SD_POL_SET(sd_pol) |
+		     PCS10G_BR_PCS_SD_CFG_SD_SEL_SET(sd_sel) |
+		     PCS10G_BR_PCS_SD_CFG_SD_ENA_SET(sd_ena),
+		     pcsinst,
+		     PCS10G_BR_PCS_SD_CFG(0));
+
+	if (sparx5_port_is_25g(port->portno)) {
+		/* Handle Signal Detect in 25G PCS */
+		spx5_wr(DEV25G_PCS25G_SD_CFG_SD_POL_SET(sd_pol) |
+			DEV25G_PCS25G_SD_CFG_SD_SEL_SET(sd_sel) |
+			DEV25G_PCS25G_SD_CFG_SD_ENA_SET(sd_ena),
+			sparx5,
+			DEV25G_PCS25G_SD_CFG(pix));
+	}
+
+	return 0;
+}
+
+void sparx5_port_enable(struct sparx5_port *port, bool enable)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	/* Enable port for frame transfer? */
+	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(enable),
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
new file mode 100644
index 000000000000..c2d931121ff2
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#ifndef __SPARX5_PORT_H__
+#define __SPARX5_PORT_H__
+
+#include "sparx5_main.h"
+
+static inline bool sparx5_port_is_2g5(int portno)
+{
+	return portno >= 16 && portno <= 47;
+}
+
+static inline bool sparx5_port_is_5g(int portno)
+{
+	return portno <= 11 || portno == 64;
+}
+
+static inline bool sparx5_port_is_10g(int portno)
+{
+	return (portno >= 12 && portno <= 15) || (portno >= 48 && portno <= 55);
+}
+
+static inline bool sparx5_port_is_25g(int portno)
+{
+	return portno >= 56 && portno <= 63;
+}
+
+static inline u32 sparx5_to_high_dev(int port)
+{
+	if (sparx5_port_is_5g(port))
+		return TARGET_DEV5G;
+	if (sparx5_port_is_10g(port))
+		return TARGET_DEV10G;
+	return TARGET_DEV25G;
+}
+
+static inline u32 sparx5_to_pcs_dev(int port)
+{
+	if (sparx5_port_is_5g(port))
+		return TARGET_PCS5G_BR;
+	if (sparx5_port_is_10g(port))
+		return TARGET_PCS10G_BR;
+	return TARGET_PCS25G_BR;
+}
+
+static inline int sparx5_port_dev_index(int port)
+{
+	if (sparx5_port_is_2g5(port))
+		return port;
+	if (sparx5_port_is_5g(port))
+		return (port <= 11 ? port : 12);
+	if (sparx5_port_is_10g(port))
+		return (port >= 12 && port <= 15) ?
+			port - 12 : port - 44;
+	return (port - 56);
+}
+
+static inline bool sparx5_is_high_speed_device(struct sparx5_port_config *conf)
+{
+	return conf->portmode == PHY_INTERFACE_MODE_10GBASER;
+}
+
+int sparx5_port_init(struct sparx5 *sparx5,
+		     struct sparx5_port *spx5_port,
+		     struct sparx5_port_config *conf);
+
+int sparx5_port_config(struct sparx5 *sparx5,
+		       struct sparx5_port *spx5_port,
+		       struct sparx5_port_config *conf);
+
+int sparx5_port_pcs_set(struct sparx5 *sparx5,
+			struct sparx5_port *port,
+			struct sparx5_port_config *conf);
+
+int sparx5_serdes_set(struct sparx5 *sparx5,
+		      struct sparx5_port *spx5_port,
+		      struct sparx5_port_config *conf);
+
+struct sparx5_port_status {
+	bool link;
+	bool link_down;
+	int  speed;
+	bool an_complete;
+	int  duplex;
+	int  pause;
+};
+
+int sparx5_get_port_status(struct sparx5 *sparx5,
+			   struct sparx5_port *port,
+			   struct sparx5_port_status *status);
+
+void sparx5_port_enable(struct sparx5_port *port, bool enable);
+
+#endif	/* __SPARX5_PORT_H__ */
-- 
2.29.2


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

* [RFC PATCH v2 4/8] net: sparx5: add port module support
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This add configuration of the Sparx5 port module instances.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |    3 +-
 .../ethernet/microchip/sparx5/sparx5_main.c   |    9 +
 .../ethernet/microchip/sparx5/sparx5_netdev.c |   14 +-
 .../microchip/sparx5/sparx5_phylink.c         |   37 +-
 .../ethernet/microchip/sparx5/sparx5_port.c   | 1140 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_port.h   |   98 ++
 6 files changed, 1292 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.h

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 19a593d17f4a..9c14eec33fd7 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,4 +5,5 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o sparx5_packet.o sparx5_netdev.o sparx5_phylink.o
+sparx5-switch-objs  := sparx5_main.o sparx5_packet.o \
+ sparx5_netdev.o sparx5_port.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index baa108cd99b2..ea75f993f80e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 /* Switch core reset/protect */
 #define RESET_PROT_STAT		0x84
@@ -269,6 +270,14 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	spx5_port->conf.speed = SPEED_UNKNOWN;
 	spx5_port->conf.power_down = true;
 	sparx5->ports[portno] = spx5_port;
+
+	err = sparx5_port_init(sparx5, spx5_port, conf);
+	if (err) {
+		dev_err(sparx5->dev, "port init failed\n");
+		return err;
+	}
+	spx5_port->conf = *conf;
+
 	/* Create a phylink for PHY management.  Also handles SFPs */
 	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
 	spx5_port->phylink_config.type = PHYLINK_NETDEV;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index 6f9282e9d3f4..98decd1d6f57 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -5,6 +5,7 @@
  */
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 /* The IFH bit position of the first VSTAX bit. This is because the
  * VSTAX bit positions in Data sheet is starting from zero.
@@ -70,6 +71,7 @@ static int sparx5_port_open(struct net_device *ndev)
 	struct sparx5_port *port = netdev_priv(ndev);
 	int err = 0;
 
+	sparx5_port_enable(port, true);
 	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
 	if (err) {
 		netdev_err(ndev, "Could not attach to PHY\n");
@@ -81,7 +83,10 @@ static int sparx5_port_open(struct net_device *ndev)
 	if (!ndev->phydev) {
 		/* power up serdes */
 		port->conf.power_down = false;
-		err = phy_power_on(port->serdes);
+		if (port->conf.serdes_reset)
+			err = sparx5_serdes_set(port->sparx5, port, &port->conf);
+		else
+			err = phy_power_on(port->serdes);
 		if (err)
 			netdev_err(ndev, "%s failed\n", __func__);
 	}
@@ -94,12 +99,17 @@ static int sparx5_port_stop(struct net_device *ndev)
 	struct sparx5_port *port = netdev_priv(ndev);
 	int err = 0;
 
+	sparx5_port_enable(port, false);
 	phylink_stop(port->phylink);
 	phylink_disconnect_phy(port->phylink);
 
 	if (!ndev->phydev) {
+		/* power down serdes */
 		port->conf.power_down = true;
-		err = phy_power_off(port->serdes);
+		if (port->conf.serdes_reset)
+			err = sparx5_serdes_set(port->sparx5, port, &port->conf);
+		else
+			err = phy_power_off(port->serdes);
 		if (err)
 			netdev_err(ndev, "%s failed\n", __func__);
 	}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
index 8166bdedaea1..b474fc29e8df 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
@@ -11,6 +11,7 @@
 #include <linux/sfp.h>
 
 #include "sparx5_main.h"
+#include "sparx5_port.h"
 
 static void sparx5_phylink_validate(struct phylink_config *config,
 				    unsigned long *supported,
@@ -91,6 +92,7 @@ static void sparx5_phylink_mac_config(struct phylink_config *config,
 {
 	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
 	struct sparx5_port_config conf;
+	int err = 0;
 
 	conf = port->conf;
 	conf.power_down = false;
@@ -124,6 +126,11 @@ static void sparx5_phylink_mac_config(struct phylink_config *config,
 
 	if (!port_conf_has_changed(&port->conf, &conf))
 		return;
+
+	/* Enable the PCS matching this interface type */
+	err = sparx5_port_pcs_set(port->sparx5, port, &conf);
+	if (err)
+		netdev_err(port->ndev, "port config failed: %d\n", err);
 }
 
 static void sparx5_phylink_mac_link_up(struct phylink_config *config,
@@ -133,17 +140,35 @@ static void sparx5_phylink_mac_link_up(struct phylink_config *config,
 				       int speed, int duplex,
 				       bool tx_pause, bool rx_pause)
 {
-	/* Currently not used */
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_config conf;
+	int err = 0;
+
+	conf = port->conf;
+	conf.duplex = duplex;
+	conf.pause = 0;
+	conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
+	conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
+	conf.speed = speed;
+
+	/* Configure the port to speed/duplex/pause */
+	err = sparx5_port_config(port->sparx5, port, &conf);
+	if (err)
+		netdev_err(port->ndev, "port config failed: %d\n", err);
 }
 
 static void sparx5_phylink_mac_link_state(struct phylink_config *config,
 					  struct phylink_link_state *state)
 {
-	state->link = true;
-	state->an_complete = true;
-	state->speed = SPEED_1000;
-	state->duplex = true;
-	state->pause = MLO_PAUSE_AN;
+	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
+	struct sparx5_port_status status;
+
+	sparx5_get_port_status(port->sparx5, port, &status);
+	state->link = status.link && !status.link_down;
+	state->an_complete = status.an_complete;
+	state->speed = status.speed;
+	state->duplex = status.duplex;
+	state->pause = status.pause;
 }
 
 static void sparx5_phylink_mac_aneg_restart(struct phylink_config *config)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
new file mode 100644
index 000000000000..8c27a37346cc
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+
+#include "sparx5_main.h"
+#include "sparx5_port.h"
+
+#define SPX5_ETYPE_TAG_C     0x8100
+#define SPX5_ETYPE_TAG_S     0x88a8
+
+#define SPX5_WAIT_US         1000
+#define SPX5_WAIT_MAX_US     2000
+
+enum port_error {
+	SPX5_PERR_SPEED,
+	SPX5_PERR_IFTYPE,
+};
+
+static int sparx5_get_dev2g5_status(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_status *status)
+{
+	u32 value;
+	u32 lp_abil;
+	u32 portno = port->portno;
+
+	/* Get PCS Link down sticky */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_STICKY(portno));
+	status->link_down = DEV2G5_PCS1G_STICKY_LINK_DOWN_STICKY_GET(value);
+	if (status->link_down)	/* Clear the sticky */
+		spx5_wr(value, sparx5, DEV2G5_PCS1G_STICKY(portno));
+
+	/* Get both current Link and Sync status */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_LINK_STATUS(portno));
+	status->link = DEV2G5_PCS1G_LINK_STATUS_LINK_STATUS_GET(value) &&
+		       DEV2G5_PCS1G_LINK_STATUS_SYNC_STATUS_GET(value);
+
+	if (port->conf.portmode == PHY_INTERFACE_MODE_1000BASEX)
+		status->speed = SPEED_1000;
+	else if (port->conf.portmode == PHY_INTERFACE_MODE_2500BASEX)
+		status->speed = SPEED_2500;
+
+	status->duplex = DUPLEX_FULL;
+
+	/* Get PCS ANEG status register */
+	value = spx5_rd(sparx5, DEV2G5_PCS1G_ANEG_STATUS(portno));
+	lp_abil = DEV2G5_PCS1G_ANEG_STATUS_LP_ADV_ABILITY_GET(value);
+
+	/* Aneg complete provides more information  */
+	if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
+		if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII) {
+			/* SGMII cisco aneg */
+			u32 spdvalue = ((lp_abil >> 10) & 3);
+
+			status->link = !!((lp_abil >> 15) == 1) && status->link;
+			status->an_complete = true;
+			status->duplex = (lp_abil >> 12) & 0x1 ?  DUPLEX_FULL : DUPLEX_HALF;
+			if (spdvalue == 0)
+				status->speed = SPEED_10;
+			else if (spdvalue == 1)
+				status->speed = SPEED_100;
+			else
+				status->speed = SPEED_1000;
+		} else {
+			/* Clause 37 Aneg */
+			status->link = !((lp_abil >> 12) & 3) && status->link;
+			status->an_complete = true;
+			status->duplex = ((lp_abil >> 5) & 1) ? DUPLEX_FULL : DUPLEX_UNKNOWN;
+			if ((lp_abil >> 8) & 1) /* symmetric pause */
+				status->pause = MLO_PAUSE_RX | MLO_PAUSE_TX;
+			if (lp_abil & (1 << 7)) /* asymmetric pause */
+				status->pause |= MLO_PAUSE_RX;
+		}
+	}
+	return 0;
+}
+
+static int sparx5_get_100fx_status(struct sparx5 *sparx5,
+				   struct sparx5_port *port,
+				   struct sparx5_port_status *status)
+{
+	u32 value, portno = port->portno;
+
+	/* Get the PCS status  */
+	value = spx5_rd(sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+
+	/* Link has been down if the are any error stickies */
+	status->link_down =
+		DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_GET(value) ||
+		DEV2G5_PCS_FX100_STATUS_FEF_STATUS_GET(value);
+
+	if (status->link_down) {
+		/* Clear the stickies and re-read */
+		spx5_wr(value, sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+		usleep_range(SPX5_WAIT_US, SPX5_WAIT_MAX_US);
+		value = spx5_rd(sparx5, DEV2G5_PCS_FX100_STATUS(portno));
+	}
+	/* Link=1 if sync status=1 and no error stickies after a clear */
+	status->link =
+		DEV2G5_PCS_FX100_STATUS_SYNC_STATUS_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_SYNC_LOST_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_FEF_FOUND_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_PCS_ERROR_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_SSD_ERROR_STICKY_GET(value) &&
+		!DEV2G5_PCS_FX100_STATUS_FEF_STATUS_GET(value);
+	status->speed = SPEED_100;
+	status->duplex = DUPLEX_FULL;
+	return 0;
+}
+
+static int sparx5_get_sfi_status(struct sparx5 *sparx5,
+				 struct sparx5_port *port,
+				 struct sparx5_port_status *status)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(&port->conf);
+	u32 portno = port->portno;
+	u32 value, dev, tinst;
+	void __iomem *inst;
+
+	if (!high_speed_dev) {
+		netdev_err(port->ndev, "error: low speed and SFI mode\n");
+		return -EINVAL;
+	}
+
+	dev = sparx5_to_high_dev(portno);
+	tinst = sparx5_port_dev_index(portno);
+	inst = spx5_inst_get(sparx5, dev, tinst);
+
+	value = spx5_inst_rd(inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+	if (value != DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY) {
+		/* The link is or has been down. Clear the sticky bit */
+		status->link_down = 1;
+		spx5_inst_wr(0xffffffff, inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+		value = spx5_inst_rd(inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
+	}
+	status->link = (value == DEV10G_MAC_TX_MONITOR_STICKY_IDLE_STATE_STICKY);
+	status->speed = port->conf.speed;
+	status->duplex = DUPLEX_FULL;
+	return 0;
+}
+
+/* Get link status of 100FX, 1000Base-X/in-band and SFI ports.
+ */
+int sparx5_get_port_status(struct sparx5 *sparx5,
+			   struct sparx5_port *port,
+			   struct sparx5_port_status *status)
+{
+	memset(status, 0, sizeof(*status));
+	status->speed = port->conf.speed;
+	if (port->conf.power_down) {
+		status->link = false;
+		return 0;
+	}
+	switch (port->conf.portmode) {
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
+	case PHY_INTERFACE_MODE_1000BASEX:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (port->conf.speed == SPEED_100)
+			return sparx5_get_100fx_status(sparx5, port, status);
+		return sparx5_get_dev2g5_status(sparx5, port, status);
+	case PHY_INTERFACE_MODE_10GBASER:
+		return sparx5_get_sfi_status(sparx5, port, status);
+	case PHY_INTERFACE_MODE_NA:
+		return 0;
+	default:
+		netdev_err(port->ndev, "Status not supported");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int sparx5_port_error(struct sparx5_port *port,
+			     struct sparx5_port_config *conf,
+			     enum port_error errtype)
+{
+	switch (errtype) {
+	case SPX5_PERR_SPEED:
+		netdev_err(port->ndev,
+			   "Interface does not support speed: %u: for %s\n",
+			   conf->speed, phy_modes(conf->portmode));
+		break;
+	case SPX5_PERR_IFTYPE:
+		netdev_err(port->ndev,
+			   "Switch port does not support interface type: %s\n",
+			   phy_modes(conf->portmode));
+		break;
+	default:
+		netdev_err(port->ndev,
+			   "Interface configuration error\n");
+	}
+
+	return -EINVAL;
+}
+
+static int sparx5_port_verify_speed(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_config *conf)
+{
+	if ((sparx5_port_is_2g5(port->portno) &&
+	     conf->speed > SPEED_2500) ||
+	    (sparx5_port_is_5g(port->portno)  &&
+	     conf->speed > SPEED_5000) ||
+	    (sparx5_port_is_10g(port->portno) &&
+	     conf->speed > SPEED_10000))
+		return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+
+	switch (conf->portmode) {
+	case PHY_INTERFACE_MODE_NA:
+		return -EINVAL;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		if ((conf->speed != SPEED_100 && /* This is for 100BASE-FX */
+		     conf->speed != SPEED_1000) ||
+		    sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		if (sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (conf->speed != SPEED_2500 ||
+		    sparx5_port_is_2g5(port->portno))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+		if (port->portno > 47)
+			return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+		fallthrough;
+	case PHY_INTERFACE_MODE_SGMII:
+		if (conf->speed != SPEED_1000 &&
+		    conf->speed != SPEED_100 &&
+		    conf->speed != SPEED_10 &&
+		    conf->speed != SPEED_2500)
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	case PHY_INTERFACE_MODE_10GBASER:
+		if ((conf->speed != SPEED_5000 &&
+		     conf->speed != SPEED_10000 &&
+		     conf->speed != SPEED_25000))
+			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
+		break;
+	default:
+		return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
+	}
+	return 0;
+}
+
+static bool sparx5_dev_change(struct sparx5 *sparx5,
+			      struct sparx5_port *port,
+			      struct sparx5_port_config *conf)
+{
+	if (port->conf.portmode != conf->portmode)
+		if (port->conf.portmode == PHY_INTERFACE_MODE_10GBASER ||
+		    conf->portmode == PHY_INTERFACE_MODE_10GBASER)
+			return true;
+
+	return false;
+}
+
+static int sparx5_port_flush_poll(struct sparx5 *sparx5, u32 portno)
+{
+	u32  value, resource, prio, delay_cnt = 0;
+	char *mem = "";
+	bool poll_src = true;
+
+	/* Resource == 0: Memory tracked per source (SRC-MEM)
+	 * Resource == 1: Frame references tracked per source (SRC-REF)
+	 * Resource == 2: Memory tracked per destination (DST-MEM)
+	 * Resource == 3: Frame references tracked per destination. (DST-REF)
+	 */
+	while (1) {
+		bool empty = true;
+
+		for (resource = 0; resource < (poll_src ? 2 : 1); resource++) {
+			u32 base;
+
+			base = (resource == 0 ? 2048 : 0) + SPX5_PRIOS * portno;
+			for (prio = 0; prio < SPX5_PRIOS; prio++) {
+				value = spx5_rd(sparx5,
+						QRES_RES_STAT(base + prio));
+				if (value) {
+					mem = resource == 0 ?
+						"DST-MEM" : "SRC-MEM";
+					empty = false;
+				}
+			}
+		}
+
+		if (empty)
+			break;
+
+		if (delay_cnt++ == 2000) {
+			dev_err(sparx5->dev,
+				"Flush timeout port %u. %s queue not empty\n",
+				portno, mem);
+			return -EINVAL;
+		}
+
+		usleep_range(SPX5_WAIT_US, SPX5_WAIT_MAX_US);
+	}
+	return 0;
+}
+
+static int sparx5_port_disable(struct sparx5 *sparx5, struct sparx5_port *port, bool high_spd_dev)
+{
+	u32 dev               = high_spd_dev ? sparx5_to_high_dev(port->portno) : TARGET_DEV2G5;
+	u32 tinst             = high_spd_dev ? sparx5_port_dev_index(port->portno) : port->portno;
+	void __iomem *devinst = spx5_inst_get(sparx5, dev, tinst);
+	u32 spd               = port->conf.speed;
+	u32 spd_prm;
+	int err;
+
+	if (high_spd_dev) {
+		/* 1: Reset the PCS Rx clock domain  */
+		spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_RX_RST,
+			      DEV10G_DEV_RST_CTRL_PCS_RX_RST,
+			      devinst,
+			      DEV10G_DEV_RST_CTRL(0));
+
+		/* 2: Disable MAC frame reception */
+		spx5_inst_rmw(0,
+			      DEV10G_MAC_ENA_CFG_RX_ENA,
+			      devinst,
+			      DEV10G_MAC_ENA_CFG(0));
+	} else {
+		/* 1: Reset the PCS Rx clock domain  */
+		spx5_inst_rmw(DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+			      devinst,
+			      DEV2G5_DEV_RST_CTRL(0));
+		/* 2: Disable MAC frame reception */
+		spx5_inst_rmw(0,
+			      DEV2G5_MAC_ENA_CFG_RX_ENA,
+			      devinst,
+			      DEV2G5_MAC_ENA_CFG(0));
+	}
+	/* 3: Disable traffic being sent to or from switch port->portno */
+	spx5_rmw(0,
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+
+	/* 4: Disable dequeuing from the egress queues  */
+	spx5_rmw(HSCH_PORT_MODE_DEQUEUE_DIS,
+		 HSCH_PORT_MODE_DEQUEUE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* 5: Disable Flowcontrol */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_STOP_SET(0xFFF - 1),
+		 QSYS_PAUSE_CFG_PAUSE_STOP,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	spd_prm = spd == SPEED_10 ? 1000 : spd == SPEED_100 ? 100 : 10;
+	/* 6: Wait while the last frame is exiting the queues */
+	usleep_range(8 * spd_prm, 10 * spd_prm);
+
+	/* 7: Flush the queues accociated with the port->portno */
+	spx5_rmw(HSCH_FLUSH_CTRL_FLUSH_PORT_SET(port->portno) |
+		 HSCH_FLUSH_CTRL_FLUSH_DST_SET(1) |
+		 HSCH_FLUSH_CTRL_FLUSH_SRC_SET(1) |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA_SET(1),
+		 HSCH_FLUSH_CTRL_FLUSH_PORT |
+		 HSCH_FLUSH_CTRL_FLUSH_DST |
+		 HSCH_FLUSH_CTRL_FLUSH_SRC |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA,
+		 sparx5,
+		 HSCH_FLUSH_CTRL);
+
+	/* 8: Enable dequeuing from the egress queues */
+	spx5_rmw(0,
+		 HSCH_PORT_MODE_DEQUEUE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* 9: Wait until flushing is complete */
+	err = sparx5_port_flush_poll(sparx5, port->portno);
+	if (err)
+		return err;
+
+	/* 10: Reset the  MAC clock domain */
+	if (high_spd_dev) {
+		spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_TX_RST_SET(1) |
+			      DEV10G_DEV_RST_CTRL_MAC_RX_RST_SET(1) |
+			      DEV10G_DEV_RST_CTRL_MAC_TX_RST_SET(1),
+			      DEV10G_DEV_RST_CTRL_PCS_TX_RST |
+			      DEV10G_DEV_RST_CTRL_MAC_RX_RST |
+			      DEV10G_DEV_RST_CTRL_MAC_TX_RST,
+			      devinst,
+			      DEV10G_DEV_RST_CTRL(0));
+
+	} else {
+		spx5_inst_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(3)    |
+			      DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_MAC_TX_RST_SET(1) |
+			      DEV2G5_DEV_RST_CTRL_MAC_RX_RST_SET(1),
+			      DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+			      DEV2G5_DEV_RST_CTRL_PCS_TX_RST |
+			      DEV2G5_DEV_RST_CTRL_PCS_RX_RST |
+			      DEV2G5_DEV_RST_CTRL_MAC_TX_RST |
+			      DEV2G5_DEV_RST_CTRL_MAC_RX_RST,
+			      devinst,
+			      DEV2G5_DEV_RST_CTRL(0));
+	}
+	/* 11: Clear flushing */
+	spx5_rmw(HSCH_FLUSH_CTRL_FLUSH_PORT_SET(port->portno) |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA_SET(0),
+		 HSCH_FLUSH_CTRL_FLUSH_PORT |
+		 HSCH_FLUSH_CTRL_FLUSH_ENA,
+		 sparx5,
+		 HSCH_FLUSH_CTRL);
+
+	if (high_spd_dev) {
+		u32 pcs = sparx5_to_pcs_dev(port->portno);
+		void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, tinst);
+
+		/* 12: Disable 5G/10G/25 BaseR PCS */
+		spx5_inst_rmw(PCS10G_BR_PCS_CFG_PCS_ENA_SET(0),
+			      PCS10G_BR_PCS_CFG_PCS_ENA,
+			      pcsinst,
+			      PCS10G_BR_PCS_CFG(0));
+
+		if (sparx5_port_is_25g(port->portno))
+			/* Disable 25G PCS */
+			spx5_rmw(DEV25G_PCS25G_CFG_PCS25G_ENA_SET(0),
+				 DEV25G_PCS25G_CFG_PCS25G_ENA,
+				 sparx5,
+				 DEV25G_PCS25G_CFG(tinst));
+	} else {
+		/* 12: Disable 1G/100fx PCS */
+		spx5_rmw(DEV2G5_PCS1G_CFG_PCS_ENA_SET(0),
+			 DEV2G5_PCS1G_CFG_PCS_ENA,
+			 sparx5,
+			 DEV2G5_PCS1G_CFG(port->portno));
+		spx5_rmw(DEV2G5_PCS_FX100_CFG_PCS_ENA_SET(0),
+			 DEV2G5_PCS_FX100_CFG_PCS_ENA,
+			 sparx5,
+			 DEV2G5_PCS_FX100_CFG(port->portno));
+	}
+
+	/* The port is now flushed and disabled  */
+	return 0;
+}
+
+static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
+			       u32 portno, u32 speed)
+{
+	u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
+	u32 mac_width  = 8;
+	u32 fifo_width = 16;
+	u32 addition   = 0;
+	u32 mac_per    = 6400, tmp1, tmp2, tmp3;
+	u32 taxi_dist[SPX5_PORTS_ALL] = {
+		6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
+		4, 4, 4, 4,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		11, 12, 13, 14, 15, 16, 17, 18,
+		4, 6, 8, 4, 6, 8, 6, 8,
+		2, 2, 2, 2, 2, 2, 2, 4, 2
+	};
+
+	switch (speed) {
+	case SPEED_25000:
+		return 0;
+	case SPEED_10000:
+		mac_per = 6400;
+		mac_width = 8;
+		addition = 1;
+		break;
+	case SPEED_5000:
+		mac_per = 12800;
+		mac_width = 8;
+		addition = 0;
+		break;
+	case SPEED_2500:
+		mac_per = 3200;
+		mac_width = 1;
+		addition = 0;
+		break;
+	case SPEED_1000:
+		mac_per =  8000;
+		mac_width = 1;
+		addition = 0;
+		break;
+	case SPEED_100:
+	case SPEED_10:
+		return 1;
+	default:
+		break;
+	}
+
+	tmp1 = 1000 * mac_width / fifo_width;
+	tmp2 = 3000 + ((12000 + 2 * taxi_dist[portno] * 1000)
+		       * sys_clk / mac_per);
+	tmp3 = tmp1 * tmp2 / 1000;
+	return  (tmp3 + 2000 + 999) / 1000 + addition;
+}
+
+/* Configure port muxing:
+ * QSGMII:     4x2G5 devices
+ */
+static int sparx5_port_mux_set(struct sparx5 *sparx5,
+			       struct sparx5_port *port,
+			       struct sparx5_port_config *conf)
+{
+	u32 inst;
+	u32 portno = port->portno;
+
+	if (port->conf.portmode == conf->portmode)
+		return 0; /* Nothing to do */
+
+	switch (conf->portmode) {
+	case PHY_INTERFACE_MODE_QSGMII: /* QSGMII: 4x2G5 devices. Mode Q'  */
+		inst = (portno - portno % 4) / 4;
+		spx5_rmw(BIT(inst),
+			 BIT(inst),
+			 sparx5,
+			 PORT_CONF_QSGMII_ENA);
+
+		if ((portno / 4 % 2) == 0) {
+			/* Affects d0-d3,d8-d11..d40-d43 */
+			spx5_rmw(PORT_CONF_USGMII_CFG_BYPASS_SCRAM_SET(1) |
+				 PORT_CONF_USGMII_CFG_BYPASS_DESCRAM_SET(1) |
+				 PORT_CONF_USGMII_CFG_QUAD_MODE_SET(1),
+				 PORT_CONF_USGMII_CFG_BYPASS_SCRAM |
+				 PORT_CONF_USGMII_CFG_BYPASS_DESCRAM |
+				 PORT_CONF_USGMII_CFG_QUAD_MODE,
+				 sparx5,
+				 PORT_CONF_USGMII_CFG((portno / 8)));
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
+				    struct sparx5_port *port)
+{
+	u32 etype;
+	enum sparx5_port_max_tags max_tags    = port->max_vlan_tags;
+	enum sparx5_vlan_port_type vlan_type  = port->vlan_type;
+	bool dotag          = max_tags != SPX5_PORT_MAX_TAGS_NONE;
+	int tag_ct          = max_tags == SPX5_PORT_MAX_TAGS_ONE ? 1 :
+			      max_tags == SPX5_PORT_MAX_TAGS_TWO ? 2 : 0;
+	bool dtag           = max_tags == SPX5_PORT_MAX_TAGS_TWO;
+	u32 dev             = sparx5_to_high_dev(port->portno);
+	u32 tinst           = sparx5_port_dev_index(port->portno);
+	void __iomem *inst  = spx5_inst_get(sparx5, dev, tinst);
+
+	etype = (vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ?
+		 port->custom_etype :
+		 vlan_type == SPX5_VLAN_PORT_TYPE_C ?
+		 SPX5_ETYPE_TAG_C : SPX5_ETYPE_TAG_S);
+
+	spx5_wr(DEV2G5_MAC_TAGS_CFG_TAG_ID_SET(etype) |
+		DEV2G5_MAC_TAGS_CFG_PB_ENA_SET(dtag) |
+		DEV2G5_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(dotag) |
+		DEV2G5_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(dotag),
+		sparx5,
+		DEV2G5_MAC_TAGS_CFG(port->portno));
+
+	if (sparx5_port_is_2g5(port->portno))
+		return 0;
+
+	spx5_inst_rmw(DEV10G_MAC_TAGS_CFG_TAG_ID_SET(etype) |
+		      DEV10G_MAC_TAGS_CFG_TAG_ENA_SET(dotag),
+		      DEV10G_MAC_TAGS_CFG_TAG_ID |
+		      DEV10G_MAC_TAGS_CFG_TAG_ENA,
+		      inst,
+		      DEV10G_MAC_TAGS_CFG(0, 0));
+
+	spx5_inst_rmw(DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS_SET(tag_ct),
+		      DEV10G_MAC_NUM_TAGS_CFG_NUM_TAGS,
+		      inst,
+		      DEV10G_MAC_NUM_TAGS_CFG(0));
+
+	spx5_inst_rmw(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK_SET(dotag),
+		      DEV10G_MAC_MAXLEN_CFG_MAX_LEN_TAG_CHK,
+		      inst,
+		      DEV10G_MAC_MAXLEN_CFG(0));
+	return 0;
+}
+
+static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)
+{
+	u32 clk_period_ps = 1600; /* 625Mhz for now */
+	u32 urg = 672000;
+
+	switch (speed) {
+	case SPEED_10:
+	case SPEED_100:
+	case SPEED_1000:
+		urg = 672000;
+		break;
+	case SPEED_2500:
+		urg = 270000;
+		break;
+	case SPEED_5000:
+		urg = 135000;
+		break;
+	case SPEED_10000:
+		urg = 67200;
+		break;
+	case SPEED_25000:
+		urg = 27000;
+		break;
+	}
+	return urg / clk_period_ps - 1;
+}
+
+static u16 sparx5_wm_enc(u16 value)
+{
+	if (value >= 2048)
+		return 2048 + value / 16;
+
+	return value;
+}
+
+static int sparx5_port_fc_setup(struct sparx5 *sparx5,
+				struct sparx5_port *port,
+				struct sparx5_port_config *conf)
+{
+	bool fc_obey = conf->pause & MLO_PAUSE_RX ? 1 : 0;
+	u32 pause_stop = 0xFFF - 1; /* FC generate disabled */
+
+	if (conf->pause & MLO_PAUSE_TX) {
+		int maxlen = ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN;
+
+		pause_stop = sparx5_wm_enc(4  * (maxlen / SPX5_BUFFER_CELL_SZ));
+	}
+
+	/* Set HDX flowcontrol */
+	spx5_rmw(DSM_MAC_CFG_HDX_BACKPREASSURE_SET(conf->duplex == DUPLEX_HALF),
+		 DSM_MAC_CFG_HDX_BACKPREASSURE,
+		 sparx5,
+		 DSM_MAC_CFG(port->portno));
+
+	/* Obey flowcontrol  */
+	spx5_rmw(DSM_RX_PAUSE_CFG_RX_PAUSE_EN_SET(fc_obey),
+		 DSM_RX_PAUSE_CFG_RX_PAUSE_EN,
+		 sparx5,
+		 DSM_RX_PAUSE_CFG(port->portno));
+
+	/* Disable forward pressure */
+	spx5_rmw(QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS_SET(fc_obey),
+		 QSYS_FWD_PRESSURE_FWD_PRESSURE_DIS,
+		 sparx5,
+		 QSYS_FWD_PRESSURE(port->portno));
+
+	/* Generate pause frames */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_STOP_SET(pause_stop),
+		 QSYS_PAUSE_CFG_PAUSE_STOP,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	return 0;
+}
+
+static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
+{
+	if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37 aneg */
+		return ((1 << 14) | /* ack */
+		((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
+		((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
+		(1 << 5)); /* FDX only */
+
+	return 1; /* Enable SGMII Aneg */
+}
+
+int sparx5_serdes_set(struct sparx5 *sparx5,
+		      struct sparx5_port *port,
+		      struct sparx5_port_config *conf)
+{
+	union phy_configure_opts opts = {
+		.eth_serdes.speed = conf->speed,
+		.eth_serdes.media_type = conf->media_type,
+	};
+	int err;
+
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII &&
+	    ((port->portno % 4) != 0)) {
+		return 0;
+	}
+	err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, conf->portmode);
+	if (err)
+		return err;
+	if (conf->serdes_reset) {
+		err = phy_reset(port->serdes);
+		if (err)
+			return err;
+	}
+	/* Configure SerDes with port parameters */
+	err = phy_configure(port->serdes, &opts);
+	if (err)
+		return err;
+	conf->serdes_reset = false;
+	return err;
+}
+
+int sparx5_port_pcs_low_set(struct sparx5 *sparx5,
+			    struct sparx5_port *port,
+			    struct sparx5_port_config *conf)
+{
+	bool sgmii = false, inband_aneg = false;
+	int err;
+
+	if (sparx5_use_cu_phy(port)) {
+		sgmii = true; /* Phy is connnected to the MAC */
+	} else {
+		if (conf->portmode == PHY_INTERFACE_MODE_SGMII ||
+		    conf->portmode == PHY_INTERFACE_MODE_QSGMII)
+			inband_aneg = true; /* Cisco-SGMII in-band-aneg */
+		else if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX &&
+			 conf->autoneg)
+			inband_aneg = true; /* Clause-37 in-band-aneg */
+
+		err = sparx5_serdes_set(sparx5, port, conf);
+		if (err) {
+			pr_info("Could not serdes_set\n");
+			return -EINVAL;
+		}
+	}
+
+	/* Choose SGMII or 1000BaseX/2500BaseX PCS mode */
+	spx5_rmw(DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(sgmii),
+		 DEV2G5_PCS1G_MODE_CFG_SGMII_MODE_ENA,
+		 sparx5,
+		 DEV2G5_PCS1G_MODE_CFG(port->portno));
+
+	/* Enable PCS */
+	spx5_wr(DEV2G5_PCS1G_CFG_PCS_ENA_SET(1),
+		sparx5,
+		DEV2G5_PCS1G_CFG(port->portno));
+
+	if (inband_aneg) {
+		u16 abil = sparx5_get_aneg_word(conf);
+
+		/* Enable in-band aneg */
+		spx5_wr(DEV2G5_PCS1G_ANEG_CFG_ADV_ABILITY_SET(abil) |
+			DEV2G5_PCS1G_ANEG_CFG_SW_RESOLVE_ENA_SET(1) |
+			DEV2G5_PCS1G_ANEG_CFG_ANEG_ENA_SET(1) |
+			DEV2G5_PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT_SET(1),
+			sparx5,
+			DEV2G5_PCS1G_ANEG_CFG(port->portno));
+	} else {
+		spx5_wr(0, sparx5, DEV2G5_PCS1G_ANEG_CFG(port->portno));
+	}
+
+	/* Take PCS out of reset */
+	spx5_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(2) |
+		 DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(0) |
+		 DEV2G5_DEV_RST_CTRL_PCS_RX_RST_SET(0),
+		 DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+		 DEV2G5_DEV_RST_CTRL_PCS_TX_RST |
+		 DEV2G5_DEV_RST_CTRL_PCS_RX_RST,
+		 sparx5,
+		 DEV2G5_DEV_RST_CTRL(port->portno));
+
+	return 0;
+}
+
+static int sparx5_port_pcs_high_set(struct sparx5 *sparx5,
+				    struct sparx5_port *port,
+				    struct sparx5_port_config *conf)
+{
+	int err;
+	u32 dev               = sparx5_to_high_dev(port->portno);
+	u32 pix               = sparx5_port_dev_index(port->portno);
+	void __iomem *devinst = spx5_inst_get(sparx5, dev, pix);
+	u32 pcs               = sparx5_to_pcs_dev(port->portno);
+	void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, pix);
+	u32 clk_spd           = conf->speed == SPEED_5000 ? 1 : 0;
+
+	/*  SFI : No in-band-aneg. Speeds 5G/10G/25G */
+
+	err = sparx5_serdes_set(sparx5, port, conf);
+	if (err)
+		return -EINVAL;
+
+	if (conf->speed == SPEED_25000) {
+		/* Enable PCS for 25G device, speed 25G */
+		spx5_rmw(DEV25G_PCS25G_CFG_PCS25G_ENA_SET(1),
+			 DEV25G_PCS25G_CFG_PCS25G_ENA,
+			 sparx5,
+			 DEV25G_PCS25G_CFG(pix));
+	} else {
+		/* Enable PCS for 5G/10G/25G devices, speed 5G/10G */
+		spx5_inst_rmw(PCS10G_BR_PCS_CFG_PCS_ENA_SET(1),
+			      PCS10G_BR_PCS_CFG_PCS_ENA,
+			      pcsinst,
+			      PCS10G_BR_PCS_CFG(0));
+	}
+
+	/* Enable 5G/10G/25G MAC module */
+	spx5_inst_wr(DEV10G_MAC_ENA_CFG_RX_ENA_SET(1) |
+		     DEV10G_MAC_ENA_CFG_TX_ENA_SET(1),
+		     devinst,
+		     DEV10G_MAC_ENA_CFG(0));
+
+	/* Take the device out of reset */
+	spx5_inst_rmw(DEV10G_DEV_RST_CTRL_PCS_RX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_PCS_TX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_MAC_RX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_MAC_TX_RST_SET(0) |
+		      DEV10G_DEV_RST_CTRL_SPEED_SEL_SET(clk_spd),
+		      DEV10G_DEV_RST_CTRL_PCS_RX_RST |
+		      DEV10G_DEV_RST_CTRL_PCS_TX_RST |
+		      DEV10G_DEV_RST_CTRL_MAC_RX_RST |
+		      DEV10G_DEV_RST_CTRL_MAC_TX_RST |
+		      DEV10G_DEV_RST_CTRL_SPEED_SEL,
+		      devinst,
+		      DEV10G_DEV_RST_CTRL(0));
+
+	return 0;
+}
+
+/* Switch between 1G/2500 and 5G/10G/25G devices */
+static void sparx5_dev_switch(struct sparx5 *sparx5, int port, bool high_speed_dev)
+{
+	int bt_indx = BIT(sparx5_port_dev_index(port));
+
+	if (sparx5_port_is_5g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV5G_MODES);
+	} else if (sparx5_port_is_10g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV10G_MODES);
+	} else if (sparx5_port_is_25g(port)) {
+		spx5_rmw(high_speed_dev ? 0 : bt_indx,
+			 bt_indx,
+			 sparx5,
+			 PORT_CONF_DEV25G_MODES);
+	}
+}
+
+/* Configure speed/duplex dependent registers */
+static int sparx5_port_config_low_set(struct sparx5 *sparx5,
+				      struct sparx5_port *port,
+				      struct sparx5_port_config *conf)
+{
+	bool fdx      = conf->duplex == DUPLEX_FULL;
+	int spd       = conf->speed;
+	u32 clk_spd   = spd == SPEED_10 ? 0 : spd == SPEED_100 ? 1 : 2;
+	u32 gig_mode  = spd == SPEED_1000 || spd == SPEED_2500;
+	u32 tx_gap    = spd == SPEED_1000 ? 4 : fdx ? 6 : 5;
+	u32 hdx_gap_1 = spd == SPEED_1000 ? 0 : spd == SPEED_100 ? 1 : 2;
+	u32 hdx_gap_2 = spd == SPEED_1000 ? 0 : spd == SPEED_100 ? 4 : 1;
+
+	/* GIG/FDX mode */
+	spx5_rmw(DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA_SET(gig_mode) |
+		 DEV2G5_MAC_MODE_CFG_FDX_ENA_SET(fdx),
+		 DEV2G5_MAC_MODE_CFG_GIGA_MODE_ENA |
+		 DEV2G5_MAC_MODE_CFG_FDX_ENA,
+		 sparx5,
+		 DEV2G5_MAC_MODE_CFG(port->portno));
+
+	/* Set MAC IFG Gaps */
+	spx5_wr(DEV2G5_MAC_IFG_CFG_TX_IFG_SET(tx_gap) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG1_SET(hdx_gap_1) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG2_SET(hdx_gap_2),
+		sparx5,
+		DEV2G5_MAC_IFG_CFG(port->portno));
+
+	/* Disabling frame aging when in HDX (due to HDX issue) */
+	spx5_rmw(HSCH_PORT_MODE_AGE_DIS_SET(fdx == 0),
+		 HSCH_PORT_MODE_AGE_DIS,
+		 sparx5,
+		 HSCH_PORT_MODE(port->portno));
+
+	/* Enable MAC module */
+	spx5_wr(DEV2G5_MAC_ENA_CFG_RX_ENA |
+		DEV2G5_MAC_ENA_CFG_TX_ENA,
+		sparx5,
+		DEV2G5_MAC_ENA_CFG(port->portno));
+
+	/* Select speed and take MAC out of reset */
+	spx5_rmw(DEV2G5_DEV_RST_CTRL_SPEED_SEL_SET(clk_spd) |
+		 DEV2G5_DEV_RST_CTRL_MAC_TX_RST_SET(0) |
+		 DEV2G5_DEV_RST_CTRL_MAC_RX_RST_SET(0),
+		 DEV2G5_DEV_RST_CTRL_SPEED_SEL |
+		 DEV2G5_DEV_RST_CTRL_MAC_TX_RST |
+		 DEV2G5_DEV_RST_CTRL_MAC_RX_RST,
+		 sparx5,
+		 DEV2G5_DEV_RST_CTRL(port->portno));
+
+	return 0;
+}
+
+int sparx5_port_pcs_set(struct sparx5 *sparx5,
+			struct sparx5_port *port,
+			struct sparx5_port_config *conf)
+
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(conf);
+	int err;
+
+	if (sparx5_dev_change(sparx5, port, conf)) {
+		/* switch device */
+		sparx5_dev_switch(sparx5, port->portno, high_speed_dev);
+
+		/* Disable the not-in-use device */
+		err = sparx5_port_disable(sparx5, port, !high_speed_dev);
+		if (err)
+			return err;
+	}
+	/* Disable the port before re-configuring */
+	err = sparx5_port_disable(sparx5, port, high_speed_dev);
+	if (err)
+		return -EINVAL;
+
+	if (high_speed_dev)
+		err = sparx5_port_pcs_high_set(sparx5, port, conf);
+	else
+		err = sparx5_port_pcs_low_set(sparx5, port, conf);
+
+	if (err)
+		return -EINVAL;
+
+	if (!sparx5_use_cu_phy(port)) {
+		/* Enable/disable 1G counters in ASM */
+		spx5_rmw(ASM_PORT_CFG_CSC_STAT_DIS_SET(high_speed_dev),
+			 ASM_PORT_CFG_CSC_STAT_DIS,
+			 sparx5,
+			 ASM_PORT_CFG(port->portno));
+
+		/* Enable/disable 1G counters in DSM */
+		spx5_rmw(DSM_BUF_CFG_CSC_STAT_DIS_SET(high_speed_dev),
+			 DSM_BUF_CFG_CSC_STAT_DIS,
+			 sparx5,
+			 DSM_BUF_CFG(port->portno));
+	}
+
+	port->conf = *conf;
+
+	return 0;
+}
+
+int sparx5_port_config(struct sparx5 *sparx5,
+		       struct sparx5_port *port,
+		       struct sparx5_port_config *conf)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(conf);
+	int err, urgency, stop_wm;
+
+	err = sparx5_port_verify_speed(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* high speed device is already configured */
+	if (!high_speed_dev)
+		sparx5_port_config_low_set(sparx5, port, conf);
+
+	/* Configure flow control */
+	err = sparx5_port_fc_setup(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* Set the DSM stop watermark */
+	stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf->speed);
+	spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
+		 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
+		 sparx5,
+		 DSM_DEV_TX_STOP_WM_CFG(port->portno));
+
+	/* Enable port forwarding */
+	urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
+	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
+		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA |
+		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+
+	/* Save the new values */
+	port->conf = *conf;
+
+	return 0;
+}
+
+/* Initialize port config to default */
+int sparx5_port_init(struct sparx5 *sparx5,
+		     struct sparx5_port *port,
+		     struct sparx5_port_config *conf)
+{
+	int err;
+	u32 devhigh           = sparx5_to_high_dev(port->portno);
+	u32 pix               = sparx5_port_dev_index(port->portno);
+	void __iomem *devinst = spx5_inst_get(sparx5, devhigh, pix);
+	u32 pcs               = sparx5_to_pcs_dev(port->portno);
+	void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, pix);
+	int maxlen            = ETH_DATA_LEN + ETH_HLEN + ETH_FCS_LEN;
+	bool sd_pol           = port->signd_active_high;
+	bool sd_ena           = port->signd_enable;
+	bool sd_sel           = !port->signd_internal;
+	u32 atop              = sparx5_wm_enc(20 * (maxlen / SPX5_BUFFER_CELL_SZ));
+	u32 pause_start       = sparx5_wm_enc(6  * (maxlen / SPX5_BUFFER_CELL_SZ));
+	u32 pause_stop        = 0xFFF - 1; /* FC generate disabled */
+
+	/* Set the mux port mode  */
+	err = sparx5_port_mux_set(sparx5, port, conf);
+	if (err)
+		return err;
+
+	/* Configure MAC vlan awareness */
+	err = sparx5_port_max_tags_set(sparx5, port);
+	if (err)
+		return err;
+
+	/* Set Max Length */
+	spx5_rmw(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_SET(maxlen),
+		 DEV2G5_MAC_MAXLEN_CFG_MAX_LEN,
+		 sparx5,
+		 DEV2G5_MAC_MAXLEN_CFG(port->portno));
+
+	/* 1G/2G5: Signal Detect configuration */
+	spx5_wr(DEV2G5_PCS1G_SD_CFG_SD_POL_SET(sd_pol) |
+		DEV2G5_PCS1G_SD_CFG_SD_SEL_SET(sd_sel) |
+		DEV2G5_PCS1G_SD_CFG_SD_ENA_SET(sd_ena),
+		sparx5,
+		DEV2G5_PCS1G_SD_CFG(port->portno));
+
+	/* 100fx: Signal Detect configuration */
+	spx5_rmw(DEV2G5_PCS_FX100_CFG_SD_POL_SET(sd_pol) |
+		 DEV2G5_PCS_FX100_CFG_SD_SEL_SET(sd_sel) |
+		 DEV2G5_PCS_FX100_CFG_SD_ENA_SET(sd_ena),
+		 DEV2G5_PCS_FX100_CFG_SD_POL |
+		 DEV2G5_PCS_FX100_CFG_SD_SEL |
+		 DEV2G5_PCS_FX100_CFG_SD_ENA,
+		 sparx5,
+		 DEV2G5_PCS_FX100_CFG(port->portno));
+
+	/* Set Pause WM hysteresis */
+	spx5_rmw(QSYS_PAUSE_CFG_PAUSE_START_SET(pause_start) |
+		 QSYS_PAUSE_CFG_PAUSE_STOP_SET(pause_stop) |
+		 QSYS_PAUSE_CFG_PAUSE_ENA_SET(1),
+		 QSYS_PAUSE_CFG_PAUSE_START |
+		 QSYS_PAUSE_CFG_PAUSE_STOP |
+		 QSYS_PAUSE_CFG_PAUSE_ENA,
+		 sparx5,
+		 QSYS_PAUSE_CFG(port->portno));
+
+	/* Port ATOP. Frames are tail dropped when this WM is hit */
+	spx5_wr(QSYS_ATOP_ATOP_SET(atop),
+		sparx5,
+		QSYS_ATOP(port->portno));
+
+	/* Discard pause frame 01-80-C2-00-00-01 */
+	spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));
+
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII ||
+	    conf->portmode == PHY_INTERFACE_MODE_SGMII) {
+		err = sparx5_serdes_set(sparx5, port, conf);
+		if (err)
+			return err;
+
+		if (!sparx5_port_is_2g5(port->portno))
+			/* Enable shadow device */
+			spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA_SET(1),
+				 DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA,
+				 sparx5,
+				 DSM_DEV_TX_STOP_WM_CFG(port->portno));
+
+		sparx5_dev_switch(sparx5, port->portno, false);
+	}
+	if (conf->portmode == PHY_INTERFACE_MODE_QSGMII) {
+		// All ports must be PCS enabled in QSGMII mode
+		spx5_rmw(DEV2G5_DEV_RST_CTRL_PCS_TX_RST_SET(0),
+			 DEV2G5_DEV_RST_CTRL_PCS_TX_RST,
+			 sparx5,
+			 DEV2G5_DEV_RST_CTRL(port->portno));
+	}
+	/* Default IFGs for 1G */
+	spx5_wr(DEV2G5_MAC_IFG_CFG_TX_IFG_SET(6) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG1_SET(0) |
+		DEV2G5_MAC_IFG_CFG_RX_IFG2_SET(0),
+		sparx5,
+		DEV2G5_MAC_IFG_CFG(port->portno));
+
+	if (sparx5_port_is_2g5(port->portno))
+		return 0; /* Low speed device only - return */
+
+	/* Now setup the high speed device */
+	if (conf->portmode == PHY_INTERFACE_MODE_NA)
+		conf->portmode = PHY_INTERFACE_MODE_10GBASER;
+
+	if (sparx5_is_high_speed_device(conf))
+		sparx5_dev_switch(sparx5, port->portno, true);
+
+	/* Set Max Length */
+	spx5_inst_rmw(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_SET(maxlen),
+		      DEV10G_MAC_MAXLEN_CFG_MAX_LEN,
+		      devinst,
+		      DEV10G_MAC_ENA_CFG(0));
+
+	/* Handle Signal Detect in 10G PCS */
+	spx5_inst_wr(PCS10G_BR_PCS_SD_CFG_SD_POL_SET(sd_pol) |
+		     PCS10G_BR_PCS_SD_CFG_SD_SEL_SET(sd_sel) |
+		     PCS10G_BR_PCS_SD_CFG_SD_ENA_SET(sd_ena),
+		     pcsinst,
+		     PCS10G_BR_PCS_SD_CFG(0));
+
+	if (sparx5_port_is_25g(port->portno)) {
+		/* Handle Signal Detect in 25G PCS */
+		spx5_wr(DEV25G_PCS25G_SD_CFG_SD_POL_SET(sd_pol) |
+			DEV25G_PCS25G_SD_CFG_SD_SEL_SET(sd_sel) |
+			DEV25G_PCS25G_SD_CFG_SD_ENA_SET(sd_ena),
+			sparx5,
+			DEV25G_PCS25G_SD_CFG(pix));
+	}
+
+	return 0;
+}
+
+void sparx5_port_enable(struct sparx5_port *port, bool enable)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	/* Enable port for frame transfer? */
+	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(enable),
+		 QFWD_SWITCH_PORT_MODE_PORT_ENA,
+		 sparx5,
+		 QFWD_SWITCH_PORT_MODE(port->portno));
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
new file mode 100644
index 000000000000..c2d931121ff2
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#ifndef __SPARX5_PORT_H__
+#define __SPARX5_PORT_H__
+
+#include "sparx5_main.h"
+
+static inline bool sparx5_port_is_2g5(int portno)
+{
+	return portno >= 16 && portno <= 47;
+}
+
+static inline bool sparx5_port_is_5g(int portno)
+{
+	return portno <= 11 || portno == 64;
+}
+
+static inline bool sparx5_port_is_10g(int portno)
+{
+	return (portno >= 12 && portno <= 15) || (portno >= 48 && portno <= 55);
+}
+
+static inline bool sparx5_port_is_25g(int portno)
+{
+	return portno >= 56 && portno <= 63;
+}
+
+static inline u32 sparx5_to_high_dev(int port)
+{
+	if (sparx5_port_is_5g(port))
+		return TARGET_DEV5G;
+	if (sparx5_port_is_10g(port))
+		return TARGET_DEV10G;
+	return TARGET_DEV25G;
+}
+
+static inline u32 sparx5_to_pcs_dev(int port)
+{
+	if (sparx5_port_is_5g(port))
+		return TARGET_PCS5G_BR;
+	if (sparx5_port_is_10g(port))
+		return TARGET_PCS10G_BR;
+	return TARGET_PCS25G_BR;
+}
+
+static inline int sparx5_port_dev_index(int port)
+{
+	if (sparx5_port_is_2g5(port))
+		return port;
+	if (sparx5_port_is_5g(port))
+		return (port <= 11 ? port : 12);
+	if (sparx5_port_is_10g(port))
+		return (port >= 12 && port <= 15) ?
+			port - 12 : port - 44;
+	return (port - 56);
+}
+
+static inline bool sparx5_is_high_speed_device(struct sparx5_port_config *conf)
+{
+	return conf->portmode == PHY_INTERFACE_MODE_10GBASER;
+}
+
+int sparx5_port_init(struct sparx5 *sparx5,
+		     struct sparx5_port *spx5_port,
+		     struct sparx5_port_config *conf);
+
+int sparx5_port_config(struct sparx5 *sparx5,
+		       struct sparx5_port *spx5_port,
+		       struct sparx5_port_config *conf);
+
+int sparx5_port_pcs_set(struct sparx5 *sparx5,
+			struct sparx5_port *port,
+			struct sparx5_port_config *conf);
+
+int sparx5_serdes_set(struct sparx5 *sparx5,
+		      struct sparx5_port *spx5_port,
+		      struct sparx5_port_config *conf);
+
+struct sparx5_port_status {
+	bool link;
+	bool link_down;
+	int  speed;
+	bool an_complete;
+	int  duplex;
+	int  pause;
+};
+
+int sparx5_get_port_status(struct sparx5 *sparx5,
+			   struct sparx5_port *port,
+			   struct sparx5_port_status *status);
+
+void sparx5_port_enable(struct sparx5_port *port, bool enable);
+
+#endif	/* __SPARX5_PORT_H__ */
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This adds SwitchDev support by hardware offloading the
SW bridge and setting up the Sparx5 MAC/VLAN tables, and listening
for MAC table updates.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   3 +-
 .../microchip/sparx5/sparx5_mactable.c        | 502 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |  32 ++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  51 ++
 .../ethernet/microchip/sparx5/sparx5_netdev.c |  31 ++
 .../ethernet/microchip/sparx5/sparx5_packet.c |   6 +
 .../microchip/sparx5/sparx5_switchdev.c       | 516 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_vlan.c   | 223 ++++++++
 8 files changed, 1363 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 9c14eec33fd7..32e0691e328a 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,5 +5,6 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o sparx5_packet.o \
+sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
+ sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
  sparx5_netdev.o sparx5_port.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
new file mode 100644
index 000000000000..a65a87cdba85
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <net/switchdev.h>
+#include <linux/iopoll.h>
+
+#include "sparx5_main.h"
+
+/* Commands for Mac Table Command register */
+#define MAC_CMD_LEARN         0 /* Insert (Learn) 1 entry */
+#define MAC_CMD_UNLEARN       1 /* Unlearn (Forget) 1 entry */
+#define MAC_CMD_LOOKUP        2 /* Look up 1 entry */
+#define MAC_CMD_READ          3 /* Read entry at Mac Table Index */
+#define MAC_CMD_WRITE         4 /* Write entry at Mac Table Index */
+#define MAC_CMD_SCAN          5 /* Scan (Age or find next) */
+#define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
+#define MAC_CMD_CLEAR_ALL     7 /* Delete all entries in table */
+
+/* Commands for MAC_ENTRY_ADDR_TYPE */
+#define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         0
+#define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
+#define  MAC_ENTRY_ADDR_TYPE_GLAG             2
+#define  MAC_ENTRY_ADDR_TYPE_MC_IDX           3
+
+#define TABLE_UPDATE_SLEEP_US 10
+#define TABLE_UPDATE_TIMEOUT_US 100000
+
+struct sparx5_mact_entry {
+	struct list_head list;
+	unsigned char mac[ETH_ALEN];
+	u32 flags;
+#define MAC_ENT_ALIVE	BIT(0)
+#define MAC_ENT_MOVED	BIT(1)
+#define MAC_ENT_LOCK	BIT(1)
+	u16 vid;
+	u16 port;
+};
+
+static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
+{
+	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
+}
+
+static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
+{
+	u32 val;
+
+	return readx_poll_timeout(sparx5_mact_get_status,
+		sparx5, val,
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
+		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
+}
+
+static void sparx5_mact_select(struct sparx5 *sparx5,
+			       const unsigned char mac[ETH_ALEN],
+			       u16 vid)
+{
+	u32 macl = 0, mach = 0;
+
+	/* Set the MAC address to handle and the vlan associated in a format
+	 * understood by the hardware.
+	 */
+	mach |= vid    << 16;
+	mach |= mac[0] << 8;
+	mach |= mac[1] << 0;
+	macl |= mac[2] << 24;
+	macl |= mac[3] << 16;
+	macl |= mac[4] << 8;
+	macl |= mac[5] << 0;
+
+	spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
+	spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
+}
+
+int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
+		      const unsigned char mac[ETH_ALEN], u16 vid)
+{
+	int addr, type, ret;
+
+	if (pgid < SPX5_PORTS) {
+		type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
+		addr = pgid % 32;
+		addr += (pgid / 32) << 5; /* Add upsid */
+	} else {
+		type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
+		addr = pgid - SPX5_PORTS;
+	}
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* MAC entry properties */
+	spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
+		sparx5, LRN_MAC_ACCESS_CFG_2);
+	spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
+
+	/*  Insert/learn new entry */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	return sparx5_mact_forget(sparx5, addr, port->pvid);
+}
+
+int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
+}
+
+static inline int sparx5_mact_get(struct sparx5 *sparx5,
+				  unsigned char mac[ETH_ALEN],
+				  u16 *vid, u32 *pcfg2)
+{
+	u32 mach, macl, cfg2;
+	int ret = -ENOENT;
+
+	cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
+	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
+		mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
+		macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
+		mac[0] = ((mach >> 8)  & 0xff);
+		mac[1] = ((mach >> 0)  & 0xff);
+		mac[2] = ((macl >> 24) & 0xff);
+		mac[3] = ((macl >> 16) & 0xff);
+		mac[4] = ((macl >> 8)  & 0xff);
+		mac[5] = ((macl >> 0)  & 0xff);
+		*vid = mach >> 16;
+		*pcfg2 = cfg2;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+bool sparx5_mact_getnext(struct sparx5 *sparx5,
+			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
+{
+	int ret;
+	u32 cfg2;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, *vid);
+
+	spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
+		LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
+		sparx5, LRN_SCAN_NEXT_CFG);
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
+		(MAC_CMD_FIND_SMALLEST) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+	if (ret == 0) {
+		ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
+		if (ret == 0)
+			*pcfg2 = cfg2;
+	}
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret == 0;
+}
+
+static int sparx5_mact_lookup(struct sparx5 *sparx5,
+			      const unsigned char mac[ETH_ALEN],
+			      u16 vid)
+{
+	int ret;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* Issue a lookup command */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+	if (ret)
+		goto out;
+
+	ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET
+		(spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2));
+
+out:
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+int sparx5_mact_forget(struct sparx5 *sparx5,
+		       const unsigned char mac[ETH_ALEN], u16 vid)
+{
+	int ret;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* Issue an unlearn command */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
+						  const unsigned char *mac,
+						  u16 vid, u16 port_index)
+{
+	struct sparx5_mact_entry *mact_entry;
+
+	mact_entry = devm_kzalloc(sparx5->dev,
+				  sizeof(*mact_entry), GFP_ATOMIC);
+	if (!mact_entry)
+		return NULL;
+
+	memcpy(mact_entry->mac, mac, ETH_ALEN);
+	mact_entry->vid = vid;
+	mact_entry->port = port_index;
+	return mact_entry;
+}
+
+static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
+						 const unsigned char *mac,
+						 u16 vid, u16 port_index)
+{
+	struct sparx5_mact_entry *mact_entry;
+	struct sparx5_mact_entry *res = NULL;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
+		if (mact_entry->vid == vid &&
+		    ether_addr_equal(mac, mact_entry->mac) &&
+		    mact_entry->port == port_index) {
+			res = mact_entry;
+			break;
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	return res;
+}
+
+static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
+				      const char *mac, u16 vid,
+				      struct net_device *dev, bool offloaded)
+{
+	struct switchdev_notifier_fdb_info info;
+
+	info.addr = mac;
+	info.vid = vid;
+	info.offloaded = offloaded;
+	call_switchdev_notifiers(type, dev, &info.info, NULL);
+}
+
+int sparx5_add_mact_entry(struct sparx5 *sparx5,
+			  struct sparx5_port *port,
+			  const unsigned char *addr, u16 vid)
+{
+	struct sparx5_mact_entry *mact_entry;
+	int ret;
+
+	ret = sparx5_mact_lookup(sparx5, addr, vid);
+	if (ret)
+		return 0;
+
+	/* In case the entry already exists, don't add it again to SW,
+	 * just update HW, but we need to look in the actual HW because
+	 * it is possible for an entry to be learn by HW and before the
+	 * mact thread to start the frame will reach CPU and the CPU will
+	 * add the entry but without the extern_learn flag.
+	 */
+	mact_entry = find_mact_entry(sparx5, addr, vid, port->portno);
+	if (mact_entry)
+		goto update_hw;
+
+	/* Add the entry in SW MAC table not to get the notification when
+	 * SW is pulling again
+	 */
+	mact_entry = alloc_mact_entry(sparx5, addr, vid, port->portno);
+	if (!mact_entry)
+		return -ENOMEM;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_add_tail(&mact_entry->list, &sparx5->mact_entries);
+	mutex_unlock(&sparx5->mact_lock);
+
+update_hw:
+	ret = sparx5_mact_learn(sparx5, port->portno, addr, vid);
+
+	/* New entry? */
+	if (mact_entry->flags == 0) {
+		mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
+		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid,
+					  port->ndev, true);
+	}
+
+	return ret;
+}
+
+int sparx5_del_mact_entry(struct sparx5 *sparx5,
+			  const unsigned char *addr,
+			  u16 vid)
+{
+	struct sparx5_mact_entry *mact_entry, *tmp;
+
+	/* Delete the entry in SW MAC table not to get the notification when
+	 * SW is pulling again
+	 */
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
+				 list) {
+		if ((vid == 0 || mact_entry->vid == vid) &&
+		    ether_addr_equal(addr, mact_entry->mac)) {
+			list_del(&mact_entry->list);
+			devm_kfree(sparx5->dev, mact_entry);
+
+			sparx5_mact_forget(sparx5, addr, mact_entry->vid);
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	return 0;
+}
+
+static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
+				     unsigned char mac[ETH_ALEN],
+				     u16 vid, u32 cfg2)
+{
+	struct sparx5_mact_entry *mact_entry;
+	u16 port;
+	bool found = false;
+
+	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
+	    MAC_ENTRY_ADDR_TYPE_UPSID_PN)
+		return;
+
+	port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
+	if (port >= SPX5_PORTS)
+		return;
+
+	if (!test_bit(port, sparx5->bridge_mask))
+		return;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
+		if (mact_entry->vid == vid &&
+		    ether_addr_equal(mac, mact_entry->mac)) {
+			found = true;
+			mact_entry->flags |= MAC_ENT_ALIVE;
+			if (mact_entry->port != port) {
+				dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
+					 mact_entry->port, port);
+				mact_entry->port = port;
+				mact_entry->flags |= MAC_ENT_MOVED;
+			}
+			/* Entry handled */
+			break;
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	if (found && !(mact_entry->flags & MAC_ENT_MOVED))
+		/* Present, not moved */
+		return;
+
+	if (!found) {
+		/* Entry not found - now add */
+		mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
+		if (!mact_entry)
+			return;
+
+		mact_entry->flags |= MAC_ENT_ALIVE;
+		mutex_lock(&sparx5->mact_lock);
+		list_add_tail(&mact_entry->list, &sparx5->mact_entries);
+		mutex_unlock(&sparx5->mact_lock);
+	}
+
+	/* New or moved entry - notify bridge */
+	sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
+				  mac, vid, sparx5->ports[port]->ndev,
+				  true);
+}
+
+void sparx5_mact_pull_work(struct work_struct *work)
+{
+	struct delayed_work *del_work = to_delayed_work(work);
+	struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
+					     mact_work);
+	struct sparx5_mact_entry *mact_entry, *tmp;
+	unsigned char mac[ETH_ALEN];
+	u16 vid;
+	u32 cfg2;
+	int ret;
+
+	/* Reset MAC entyry flags */
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
+		mact_entry->flags &= MAC_ENT_LOCK;
+	mutex_unlock(&sparx5->mact_lock);
+
+	/* MAIN mac address processing loop */
+	vid = 0;
+	memset(mac, 0, sizeof(mac));
+	do {
+		mutex_lock(&sparx5->lock);
+		sparx5_mact_select(sparx5, mac, vid);
+		spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
+			sparx5, LRN_SCAN_NEXT_CFG);
+		spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
+			(MAC_CMD_FIND_SMALLEST) |
+			LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+			sparx5, LRN_COMMON_ACCESS_CTRL);
+		ret = sparx5_mact_wait_for_completion(sparx5);
+		if (ret == 0)
+			ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
+		mutex_unlock(&sparx5->lock);
+		if (ret == 0)
+			sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
+	} while (ret == 0);
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
+				 list) {
+		/* If the entry is in HW or permanent, then skip */
+		if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
+			continue;
+
+		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
+					  mact_entry->mac, mact_entry->vid,
+					  sparx5->ports[mact_entry->port]->ndev,
+					  true);
+
+		list_del(&mact_entry->list);
+		devm_kfree(sparx5->dev, mact_entry);
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
+			   SPX5_MACT_PULL_DELAY);
+}
+
+void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
+{
+	int value = max(1, msecs / 10); /* unit 10 ms */
+
+	spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
+		 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
+		 LRN_AUTOAGE_CFG_UNIT_SIZE |
+		 LRN_AUTOAGE_CFG_PERIOD_VAL,
+		 sparx5,
+		 LRN_AUTOAGE_CFG(0));
+}
+
+void sparx5_mact_init(struct sparx5 *sparx5)
+{
+	mutex_init(&sparx5->lock);
+
+	mutex_lock(&sparx5->lock);
+
+	/*  Flush MAC table */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	if (sparx5_mact_wait_for_completion(sparx5) != 0)
+		dev_warn(sparx5->dev, "MAC flush error\n");
+
+	mutex_unlock(&sparx5->lock);
+
+	sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index ea75f993f80e..30940d9ee916 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -278,6 +278,9 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	}
 	spx5_port->conf = *conf;
 
+	/* Setup VLAN */
+	sparx5_vlan_port_setup(sparx5, spx5_port->portno);
+
 	/* Create a phylink for PHY management.  Also handles SFPs */
 	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
 	spx5_port->phylink_config.type = PHYLINK_NETDEV;
@@ -532,6 +535,7 @@ static int sparx5_qlim_set(struct sparx5 *sparx5)
 
 static int sparx5_init(struct sparx5 *sparx5)
 {
+	u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 	int irq, ret;
 	u32 idx;
 
@@ -582,6 +586,9 @@ static int sparx5_init(struct sparx5 *sparx5)
 			 QFWD_SWITCH_PORT_MODE(idx));
 	}
 
+	/* Init masks */
+	sparx5_update_fwd(sparx5);
+
 	/* CPU copy CPU pgids */
 	spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
 		sparx5, ANA_AC_PGID_MISC_CFG(PGID_CPU));
@@ -594,6 +601,15 @@ static int sparx5_init(struct sparx5 *sparx5)
 			 ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
 			 sparx5, ANA_CL_FILTER_CTRL(idx));
 
+	/* Init MAC table, ageing */
+	sparx5_mact_init(sparx5);
+
+	/* Setup VLANs */
+	sparx5_vlan_init(sparx5);
+
+	/* Add host mode BC address (points only to CPU) */
+	sparx5_mact_learn(sparx5, PGID_CPU, broadcast, NULL_VID);
+
 	/* Enable queue limitation watermarks */
 	sparx5_qlim_set(sparx5);
 
@@ -647,6 +663,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 	struct device_node *ports, *portnp;
 	const u8 *mac_addr;
 	int err = 0;
+	char queue_name[32];
 
 	if (!np && !pdev->dev.platform_data)
 		return -ENODEV;
@@ -726,11 +743,24 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	/* Init mact_sw struct */
+	mutex_init(&sparx5->mact_lock);
+	INIT_LIST_HEAD(&sparx5->mact_entries);
+	snprintf(queue_name, sizeof(queue_name), "%s-mact",
+		 dev_name(sparx5->dev));
+	sparx5->mact_queue = create_singlethread_workqueue(queue_name);
+	INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work);
+	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
+			   SPX5_MACT_PULL_DELAY);
+
 	err = sparx5_register_netdevs(sparx5);
 	if (err)
 		goto cleanup_ports;
 
 	sparx5_board_init(sparx5);
+	err = sparx5_register_notifier_blocks(sparx5);
+	if (err)
+		goto cleanup_ports;
 
 	return err;
 
@@ -744,6 +774,8 @@ static int mchp_sparx5_remove(struct platform_device *pdev)
 	struct sparx5 *sparx5 = platform_get_drvdata(pdev);
 
 	sparx5_cleanup_ports(sparx5);
+	/* Unregister netdevs */
+	sparx5_unregister_notifier_blocks(sparx5);
 
 	return 0;
 }
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index a92ebf339fe1..49657f9edbf3 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -130,7 +130,25 @@ struct sparx5 {
 	/* port structures are in net device */
 	struct sparx5_port                    *ports[SPX5_PORTS];
 	enum sparx5_core_clockfreq            coreclock;
+	/* Notifiers */
+	struct notifier_block                 netdevice_nb;
+	struct notifier_block                 switchdev_nb;
+	struct notifier_block                 switchdev_blocking_nb;
+	/* Switch state */
 	u8                                    base_mac[ETH_ALEN];
+	/* Associated bridge device (when bridged) */
+	struct net_device                     *hw_bridge_dev;
+	/* Bridged interfaces */
+	DECLARE_BITMAP(bridge_mask,           SPX5_PORTS);
+	DECLARE_BITMAP(bridge_fwd_mask,       SPX5_PORTS);
+	DECLARE_BITMAP(bridge_lrn_mask,       SPX5_PORTS);
+	DECLARE_BITMAP(vlan_mask[VLAN_N_VID], SPX5_PORTS);
+	/* SW MAC table */
+	struct list_head                      mact_entries;
+	/* mac table list (mact_entries) mutex */
+	struct mutex                          mact_lock;
+	struct delayed_work                   mact_work;
+	struct workqueue_struct               *mact_queue;
 	/* Board specifics */
 	bool                                  sd_sgpio_remapping;
 };
@@ -139,11 +157,44 @@ struct sparx5 {
 void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
 bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
 
+/* sparx5_switchdev.c */
+int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
+void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
+
 /* sparx5_packet.c */
 irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
 int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
 void sparx5_manual_injection_mode(struct sparx5 *sparx5);
 
+/* sparx5_mactable.c */
+void sparx5_mact_pull_work(struct work_struct *work);
+int sparx5_mact_learn(struct sparx5 *sparx5, int port,
+		      const unsigned char mac[ETH_ALEN], u16 vid);
+bool sparx5_mact_getnext(struct sparx5 *sparx5,
+			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2);
+int sparx5_mact_forget(struct sparx5 *sparx5,
+		       const unsigned char mac[ETH_ALEN], u16 vid);
+int sparx5_add_mact_entry(struct sparx5 *sparx5,
+			  struct sparx5_port *port,
+			  const unsigned char *addr, u16 vid);
+int sparx5_del_mact_entry(struct sparx5 *sparx5,
+			  const unsigned char *addr,
+			  u16 vid);
+int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr);
+int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr);
+void sparx5_set_ageing(struct sparx5 *sparx5, int msecs);
+void sparx5_mact_init(struct sparx5 *sparx5);
+
+/* sparx5_vlan.c */
+void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable);
+void sparx5_update_fwd(struct sparx5 *sparx5);
+void sparx5_vlan_init(struct sparx5 *sparx5);
+void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno);
+int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
+			bool untagged);
+int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
+void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index 98decd1d6f57..a0992443ab9f 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -116,6 +116,15 @@ static int sparx5_port_stop(struct net_device *ndev)
 	return 0;
 }
 
+static void sparx5_set_rx_mode(struct net_device *dev)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (!test_bit(port->portno, sparx5->bridge_mask))
+		__dev_mc_sync(dev, sparx5_mc_sync, sparx5_mc_unsync);
+}
+
 static int sparx5_port_get_phys_port_name(struct net_device *dev,
 					  char *buf, size_t len)
 {
@@ -131,21 +140,43 @@ static int sparx5_port_get_phys_port_name(struct net_device *dev,
 
 static int sparx5_set_mac_address(struct net_device *dev, void *p)
 {
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
 	const struct sockaddr *addr = p;
 
+	/* Remove current */
+	sparx5_mact_forget(sparx5, dev->dev_addr,  port->pvid);
+
+	/* Add new */
+	sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid);
+
 	/* Record the address */
 	ether_addr_copy(dev->dev_addr, addr->sa_data);
 
 	return 0;
 }
 
+static int sparx5_get_port_parent_id(struct net_device *dev,
+				     struct netdev_phys_item_id *ppid)
+{
+	struct sparx5_port *sparx5_port = netdev_priv(dev);
+	struct sparx5 *sparx5 = sparx5_port->sparx5;
+
+	ppid->id_len = sizeof(sparx5->base_mac);
+	memcpy(&ppid->id, &sparx5->base_mac, ppid->id_len);
+
+	return 0;
+}
+
 static const struct net_device_ops sparx5_port_netdev_ops = {
 	.ndo_open               = sparx5_port_open,
 	.ndo_stop               = sparx5_port_stop,
 	.ndo_start_xmit         = sparx5_port_xmit_impl,
+	.ndo_set_rx_mode        = sparx5_set_rx_mode,
 	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
 	.ndo_set_mac_address    = sparx5_set_mac_address,
 	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_get_port_parent_id = sparx5_get_port_parent_id,
 };
 
 bool sparx5_netdevice_check(const struct net_device *dev)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
index 209eef5c6385..c4b1d89d2204 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
@@ -143,6 +143,12 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
 		return;
 	}
 
+	/* Everything we see on an interface that is in the HW bridge
+	 * has already been forwarded
+	 */
+	if (test_bit(port->portno, sparx5->bridge_mask))
+		skb->offload_fwd_mark = 1;
+
 	/* Finish up skb */
 	skb_put(skb, byte_cnt - ETH_FCS_LEN);
 	eth_skb_pad(skb);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
new file mode 100644
index 000000000000..1059a187ae47
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/if_bridge.h>
+#include <net/switchdev.h>
+
+#include "sparx5_main.h"
+
+static struct workqueue_struct *sparx5_owq;
+
+struct sparx5_switchdev_event_work {
+	struct work_struct work;
+	struct switchdev_notifier_fdb_info fdb_info;
+	struct net_device *dev;
+	unsigned long event;
+};
+
+static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,
+					  unsigned long flags)
+{
+	sparx5_pgid_update_mask(port, PGID_MC_FLOOD, flags & BR_MCAST_FLOOD);
+}
+
+static void sparx5_attr_stp_state_set(struct sparx5_port *port,
+				      struct switchdev_trans *trans,
+				      u8 state)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (!test_bit(port->portno, sparx5->bridge_mask)) {
+		netdev_err(port->ndev,
+			   "Controlling non-bridged port %d?\n", port->portno);
+		return;
+	}
+
+	switch (state) {
+	case BR_STATE_FORWARDING:
+		set_bit(port->portno, sparx5->bridge_fwd_mask);
+		fallthrough;
+	case BR_STATE_LEARNING:
+		set_bit(port->portno, sparx5->bridge_lrn_mask);
+		break;
+
+	default:
+		/* All other states treated as blocking */
+		clear_bit(port->portno, sparx5->bridge_fwd_mask);
+		clear_bit(port->portno, sparx5->bridge_lrn_mask);
+		break;
+	}
+
+	/* apply the bridge_fwd_mask to all the ports */
+	sparx5_update_fwd(sparx5);
+}
+
+static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
+					unsigned long ageing_clock_t)
+{
+	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
+	u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
+
+	sparx5_set_ageing(port->sparx5, ageing_time);
+}
+
+static int sparx5_port_attr_set(struct net_device *dev,
+				const struct switchdev_attr *attr,
+				struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+
+	if (switchdev_trans_ph_prepare(trans))
+		return 0;
+
+	switch (attr->id) {
+	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);
+		break;
+	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+		sparx5_attr_stp_state_set(port, trans, attr->u.stp_state);
+		break;
+	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+		sparx5_port_attr_ageing_set(port, attr->u.ageing_time);
+		break;
+	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+		port->vlan_aware = attr->u.vlan_filtering;
+		sparx5_vlan_port_apply(port->sparx5, port);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int sparx5_port_bridge_join(struct sparx5_port *port,
+				   struct net_device *bridge)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
+		/* First bridged port */
+		sparx5->hw_bridge_dev = bridge;
+	else
+		if (sparx5->hw_bridge_dev != bridge)
+			/* This is adding the port to a second bridge, this is
+			 * unsupported
+			 */
+			return -ENODEV;
+
+	set_bit(port->portno, sparx5->bridge_mask);
+
+	/* Port enters in bridge mode therefor don't need to copy to CPU
+	 * frames for multicast in case the bridge is not requesting them
+	 */
+	__dev_mc_unsync(port->ndev, sparx5_mc_unsync);
+
+	return 0;
+}
+
+static void sparx5_port_bridge_leave(struct sparx5_port *port,
+				     struct net_device *bridge)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	clear_bit(port->portno, sparx5->bridge_mask);
+	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
+		sparx5->hw_bridge_dev = NULL;
+
+	/* Clear bridge vlan settings before updating the port settings */
+	port->vlan_aware = 0;
+	port->pvid = NULL_VID;
+	port->vid = NULL_VID;
+
+	/* Port enters in host more therefore restore mc list */
+	__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
+}
+
+static int sparx5_port_changeupper(struct net_device *dev,
+				   struct netdev_notifier_changeupper_info *info)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int err = 0;
+
+	if (netif_is_bridge_master(info->upper_dev)) {
+		if (info->linking)
+			err = sparx5_port_bridge_join(port, info->upper_dev);
+		else
+			sparx5_port_bridge_leave(port, info->upper_dev);
+
+		sparx5_vlan_port_apply(port->sparx5, port);
+	}
+
+	return err;
+}
+
+static int sparx5_port_add_addr(struct net_device *dev, bool up)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+	u16 vid = port->pvid;
+
+	if (up)
+		sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
+	else
+		sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
+
+	return 0;
+}
+
+static int sparx5_netdevice_port_event(struct net_device *dev,
+				       struct notifier_block *nb,
+				       unsigned long event, void *ptr)
+{
+	int err = 0;
+
+	if (!sparx5_netdevice_check(dev))
+		return 0;
+
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		err = sparx5_port_changeupper(dev, ptr);
+		break;
+	case NETDEV_PRE_UP:
+		err = sparx5_port_add_addr(dev, true);
+		break;
+	case NETDEV_DOWN:
+		err = sparx5_port_add_addr(dev, false);
+		break;
+	}
+
+	return err;
+}
+
+static int sparx5_netdevice_event(struct notifier_block *nb,
+				  unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	int ret = 0;
+
+	ret = sparx5_netdevice_port_event(dev, nb, event, ptr);
+
+	return notifier_from_errno(ret);
+}
+
+static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
+{
+	struct sparx5_switchdev_event_work *switchdev_work =
+		container_of(work, struct sparx5_switchdev_event_work, work);
+	struct net_device *dev = switchdev_work->dev;
+	struct switchdev_notifier_fdb_info *fdb_info;
+	struct sparx5_port *port;
+	struct sparx5 *sparx5;
+
+	rtnl_lock();
+	if (!sparx5_netdevice_check(dev))
+		goto out;
+
+	port = netdev_priv(dev);
+	sparx5 = port->sparx5;
+
+	fdb_info = &switchdev_work->fdb_info;
+
+	switch (switchdev_work->event) {
+	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		sparx5_add_mact_entry(sparx5, port, fdb_info->addr,
+				      fdb_info->vid);
+		break;
+	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid);
+		break;
+	}
+
+out:
+	rtnl_unlock();
+	kfree(switchdev_work->fdb_info.addr);
+	kfree(switchdev_work);
+	dev_put(dev);
+}
+
+static void sparx5_schedule_work(struct work_struct *work)
+{
+	queue_work(sparx5_owq, work);
+}
+
+static int sparx5_switchdev_event(struct notifier_block *unused,
+				  unsigned long event, void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	struct sparx5_switchdev_event_work *switchdev_work;
+	struct switchdev_notifier_fdb_info *fdb_info;
+	struct switchdev_notifier_info *info = ptr;
+	int err;
+
+	switch (event) {
+	case SWITCHDEV_PORT_ATTR_SET:
+		err = switchdev_handle_port_attr_set(dev, ptr,
+						     sparx5_netdevice_check,
+						     sparx5_port_attr_set);
+		return notifier_from_errno(err);
+	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		fallthrough;
+	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+		if (!switchdev_work)
+			return NOTIFY_BAD;
+
+		switchdev_work->dev = dev;
+		switchdev_work->event = event;
+
+		fdb_info = container_of(info,
+					struct switchdev_notifier_fdb_info,
+					info);
+		INIT_WORK(&switchdev_work->work,
+			  sparx5_switchdev_bridge_fdb_event_work);
+		memcpy(&switchdev_work->fdb_info, ptr,
+		       sizeof(switchdev_work->fdb_info));
+		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+		if (!switchdev_work->fdb_info.addr)
+			goto err_addr_alloc;
+
+		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
+				fdb_info->addr);
+		dev_hold(dev);
+
+		sparx5_schedule_work(&switchdev_work->work);
+		break;
+	}
+
+	return NOTIFY_DONE;
+err_addr_alloc:
+	kfree(switchdev_work);
+	return NOTIFY_BAD;
+}
+
+static void sparx5_sync_port_dev_addr(struct sparx5 *sparx5,
+				      struct sparx5_port *port,
+				      u16 vid, bool add)
+{
+	if (!port ||
+	    !test_bit(port->portno, sparx5->bridge_mask))
+		return; /* Skip null/host interfaces */
+
+	/* Bridge connects to vid? */
+	if (add) {
+		/* Add port MAC address from the VLAN */
+		sparx5_mact_learn(sparx5, PGID_CPU,
+				  port->ndev->dev_addr, vid);
+	} else {
+		/* Control port addr visibility depending on
+		 * port VLAN connectivity.
+		 */
+		if (test_bit(port->portno, sparx5->vlan_mask[vid]))
+			sparx5_mact_learn(sparx5, PGID_CPU,
+					  port->ndev->dev_addr, vid);
+		else
+			sparx5_mact_forget(sparx5,
+					   port->ndev->dev_addr, vid);
+	}
+}
+
+static void sparx5_sync_bridge_dev_addr(struct net_device *dev,
+					struct sparx5 *sparx5,
+					u16 vid, bool add)
+{
+	int i;
+
+	/* First, handle bridge address'es */
+	if (add) {
+		sparx5_mact_learn(sparx5, PGID_CPU, dev->dev_addr,
+				  vid);
+		sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
+				  vid);
+	} else {
+		sparx5_mact_forget(sparx5, dev->dev_addr, vid);
+		sparx5_mact_forget(sparx5, dev->broadcast, vid);
+	}
+
+	/* Now look at bridged ports */
+	for (i = 0; i < SPX5_PORTS; i++)
+		sparx5_sync_port_dev_addr(sparx5, sparx5->ports[i], vid, add);
+}
+
+static int sparx5_handle_port_vlan_add(struct net_device *dev,
+				       struct notifier_block *nb,
+				       const struct switchdev_obj_port_vlan *v,
+				       struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+	u16 vid;
+
+	if (switchdev_trans_ph_prepare(trans))
+		return 0;
+
+	if (netif_is_bridge_master(dev)) {
+		if (v->flags & BRIDGE_VLAN_INFO_BRENTRY) {
+			struct sparx5 *sparx5 =
+				container_of(nb, struct sparx5,
+					     switchdev_blocking_nb);
+
+			for (vid = v->vid_begin; vid <= v->vid_end; vid++)
+				sparx5_sync_bridge_dev_addr(dev, sparx5, vid, true);
+		}
+		return 0;
+	}
+
+	for (vid = v->vid_begin; vid <= v->vid_end; vid++) {
+		ret = sparx5_vlan_vid_add(port, vid,
+					  v->flags & BRIDGE_VLAN_INFO_PVID,
+					  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sparx5_handle_port_obj_add(struct net_device *dev,
+				      struct notifier_block *nb,
+				      struct switchdev_notifier_port_obj_info *info)
+{
+	const struct switchdev_obj *obj = info->obj;
+	int err;
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = sparx5_handle_port_vlan_add(dev, nb,
+						  SWITCHDEV_OBJ_PORT_VLAN(obj),
+						  info->trans);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	info->handled = true;
+	return err;
+}
+
+static int sparx5_handle_port_vlan_del(struct net_device *dev,
+				       struct notifier_block *nb,
+				       const struct switchdev_obj_port_vlan *v,
+				       struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+	u16 vid;
+
+	/* Master bridge? */
+	if (netif_is_bridge_master(dev)) {
+		struct sparx5 *sparx5 = container_of(nb, struct sparx5,
+						     switchdev_blocking_nb);
+
+		for (vid = v->vid_begin; vid <= v->vid_end; vid++)
+			sparx5_sync_bridge_dev_addr(dev, sparx5, vid, false);
+		return 0;
+	}
+
+	for (vid = v->vid_begin; vid <= v->vid_end; vid++) {
+		ret = sparx5_vlan_vid_del(port, vid);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sparx5_handle_port_obj_del(struct net_device *dev,
+				      struct notifier_block *nb,
+				      struct switchdev_notifier_port_obj_info *info)
+{
+	const struct switchdev_obj *obj = info->obj;
+	int err;
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = sparx5_handle_port_vlan_del(dev, nb,
+						  SWITCHDEV_OBJ_PORT_VLAN(obj),
+						  info->trans);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	info->handled = true;
+	return err;
+}
+
+static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
+					   unsigned long event,
+					   void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	int err;
+
+	switch (event) {
+	case SWITCHDEV_PORT_OBJ_ADD:
+		err = sparx5_handle_port_obj_add(dev, nb, ptr);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_OBJ_DEL:
+		err = sparx5_handle_port_obj_del(dev, nb, ptr);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_ATTR_SET:
+		err = switchdev_handle_port_attr_set(dev, ptr,
+						     sparx5_netdevice_check,
+						     sparx5_port_attr_set);
+		return notifier_from_errno(err);
+	}
+
+	return NOTIFY_DONE;
+}
+
+int sparx5_register_notifier_blocks(struct sparx5 *s5)
+{
+	int err;
+
+	s5->netdevice_nb.notifier_call = sparx5_netdevice_event;
+	err = register_netdevice_notifier(&s5->netdevice_nb);
+	if (err)
+		return err;
+
+	s5->switchdev_nb.notifier_call = sparx5_switchdev_event;
+	err = register_switchdev_notifier(&s5->switchdev_nb);
+	if (err)
+		goto err_switchdev_nb;
+
+	s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;
+	err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
+	if (err)
+		goto err_switchdev_blocking_nb;
+
+	sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
+	if (!sparx5_owq)
+		goto err_switchdev_blocking_nb;
+
+	return 0;
+
+err_switchdev_blocking_nb:
+	unregister_switchdev_notifier(&s5->switchdev_nb);
+err_switchdev_nb:
+	unregister_netdevice_notifier(&s5->netdevice_nb);
+
+	return err;
+}
+
+void sparx5_unregister_notifier_blocks(struct sparx5 *s5)
+{
+	destroy_workqueue(sparx5_owq);
+
+	unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
+	unregister_switchdev_notifier(&s5->switchdev_nb);
+	unregister_netdevice_notifier(&s5->netdevice_nb);
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
new file mode 100644
index 000000000000..a2ccb2fae945
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
+{
+	u32 mask[3];
+
+	/* Divide up mask in 32 bit words */
+	bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
+
+	/* Output mask to respective registers */
+	spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
+	spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
+	spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
+
+	return 0;
+}
+
+void sparx5_vlan_init(struct sparx5 *sparx5)
+{
+	u16 vid;
+
+	spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
+		 ANA_L3_VLAN_CTRL_VLAN_ENA,
+		 sparx5,
+		 ANA_L3_VLAN_CTRL);
+
+	/* Map VLAN = FID */
+	for (vid = NULL_VID; vid < VLAN_N_VID; vid++)
+		spx5_rmw(ANA_L3_VLAN_CFG_VLAN_FID_SET(vid),
+			 ANA_L3_VLAN_CFG_VLAN_FID,
+			 sparx5,
+			 ANA_L3_VLAN_CFG(vid));
+}
+
+void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
+{
+	struct sparx5_port *port = sparx5->ports[portno];
+
+	/* Configure PVID */
+	spx5_rmw(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(0) |
+		 ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid),
+		 ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA |
+		 ANA_CL_VLAN_CTRL_PORT_VID,
+		 sparx5,
+		 ANA_CL_VLAN_CTRL(port->portno));
+}
+
+int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
+			bool untagged)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	int ret;
+
+	/* Make the port a member of the VLAN */
+	set_bit(port->portno, sparx5->vlan_mask[vid]);
+	ret = sparx5_vlant_set_mask(sparx5, vid);
+	if (ret)
+		return ret;
+
+	/* Default ingress vlan classification */
+	if (pvid)
+		port->pvid = vid;
+
+	/* Untagged egress vlan clasification */
+	if (untagged && port->vid != vid) {
+		if (port->vid) {
+			netdev_err(port->ndev,
+				   "Port already has a native VLAN: %d\n",
+				   port->vid);
+			return -EBUSY;
+		}
+		port->vid = vid;
+	}
+
+	sparx5_vlan_port_apply(sparx5, port);
+
+	return 0;
+}
+
+int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	int ret;
+
+	/* 8021q removes VID 0 on module unload for all interfaces
+	 * with VLAN filtering feature. We need to keep it to receive
+	 * untagged traffic.
+	 */
+	if (vid == 0)
+		return 0;
+
+	/* Stop the port from being a member of the vlan */
+	clear_bit(port->portno, sparx5->vlan_mask[vid]);
+	ret = sparx5_vlant_set_mask(sparx5, vid);
+	if (ret)
+		return ret;
+
+	/* Ingress */
+	if (port->pvid == vid)
+		port->pvid = 0;
+
+	/* Egress */
+	if (port->vid == vid)
+		port->vid = 0;
+
+	sparx5_vlan_port_apply(sparx5, port);
+
+	return 0;
+}
+
+void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	u32 val, mask;
+
+	/* mask is spread across 3 registers x 32 bit */
+	if (port->portno < 32) {
+		mask = BIT(port->portno);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG(pgid));
+	} else if (port->portno < 64) {
+		mask = BIT(port->portno - 32);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG1(pgid));
+	} else if (port->portno < SPX5_PORTS) {
+		mask = BIT(port->portno - 64);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG2(pgid));
+	} else {
+		netdev_err(port->ndev, "Invalid port no: %d\n", port->portno);
+	}
+}
+
+void sparx5_update_fwd(struct sparx5 *sparx5)
+{
+	u32 mask[3];
+	DECLARE_BITMAP(workmask, SPX5_PORTS);
+	int port;
+
+	/* Divide up fwd mask in 32 bit words */
+	bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
+
+	/* Update flood masks */
+	for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
+		spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
+		spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
+		spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
+	}
+
+	/* Update SRC masks */
+	for (port = 0; port < SPX5_PORTS; port++) {
+		if (test_bit(port, sparx5->bridge_fwd_mask)) {
+			/* Allow to send to all bridged but self */
+			bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
+			clear_bit(port, workmask);
+			bitmap_to_arr32(mask, workmask, SPX5_PORTS);
+			spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
+			spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
+			spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
+		} else {
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
+		}
+	}
+
+	/* Learning enabled only for bridged ports */
+	bitmap_and(workmask, sparx5->bridge_fwd_mask,
+		   sparx5->bridge_lrn_mask, SPX5_PORTS);
+	bitmap_to_arr32(mask, workmask, SPX5_PORTS);
+
+	/* Apply learning mask */
+	spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
+	spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
+	spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
+}
+
+void sparx5_vlan_port_apply(struct sparx5 *sparx5,
+			    struct sparx5_port *port)
+
+{
+	u32 val;
+
+	/* Configure PVID, vlan aware */
+	val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) |
+		ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) |
+		ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid);
+	spx5_wr(val, sparx5, ANA_CL_VLAN_CTRL(port->portno));
+
+	val = 0;
+	if (port->vlan_aware && !port->pvid)
+		/* If port is vlan-aware and tagged, drop untagged and
+		 * priority tagged frames.
+		 */
+		val = ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(1) |
+			ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(1) |
+			ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(1);
+	spx5_wr(val, sparx5,
+		ANA_CL_VLAN_FILTER_CTRL(port->portno, 0));
+
+	/* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
+	val = REW_TAG_CTRL_TAG_TPID_CFG_SET(0);
+	if (port->vlan_aware) {
+		if (port->vid)
+			/* Tag all frames except when VID == DEFAULT_VLAN */
+			val |= REW_TAG_CTRL_TAG_CFG_SET(1);
+		else
+			val |= REW_TAG_CTRL_TAG_CFG_SET(3);
+	}
+	spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
+
+	/* Egress VID */
+	spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
+		 REW_PORT_VLAN_CFG_PORT_VID,
+		 sparx5,
+		 REW_PORT_VLAN_CFG(port->portno));
+}
-- 
2.29.2


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

* [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This adds SwitchDev support by hardware offloading the
SW bridge and setting up the Sparx5 MAC/VLAN tables, and listening
for MAC table updates.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   3 +-
 .../microchip/sparx5/sparx5_mactable.c        | 502 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |  32 ++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  51 ++
 .../ethernet/microchip/sparx5/sparx5_netdev.c |  31 ++
 .../ethernet/microchip/sparx5/sparx5_packet.c |   6 +
 .../microchip/sparx5/sparx5_switchdev.c       | 516 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_vlan.c   | 223 ++++++++
 8 files changed, 1363 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 9c14eec33fd7..32e0691e328a 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -5,5 +5,6 @@
 
 obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
-sparx5-switch-objs  := sparx5_main.o sparx5_packet.o \
+sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
+ sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
  sparx5_netdev.o sparx5_port.o sparx5_phylink.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
new file mode 100644
index 000000000000..a65a87cdba85
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
@@ -0,0 +1,502 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <net/switchdev.h>
+#include <linux/iopoll.h>
+
+#include "sparx5_main.h"
+
+/* Commands for Mac Table Command register */
+#define MAC_CMD_LEARN         0 /* Insert (Learn) 1 entry */
+#define MAC_CMD_UNLEARN       1 /* Unlearn (Forget) 1 entry */
+#define MAC_CMD_LOOKUP        2 /* Look up 1 entry */
+#define MAC_CMD_READ          3 /* Read entry at Mac Table Index */
+#define MAC_CMD_WRITE         4 /* Write entry at Mac Table Index */
+#define MAC_CMD_SCAN          5 /* Scan (Age or find next) */
+#define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
+#define MAC_CMD_CLEAR_ALL     7 /* Delete all entries in table */
+
+/* Commands for MAC_ENTRY_ADDR_TYPE */
+#define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         0
+#define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
+#define  MAC_ENTRY_ADDR_TYPE_GLAG             2
+#define  MAC_ENTRY_ADDR_TYPE_MC_IDX           3
+
+#define TABLE_UPDATE_SLEEP_US 10
+#define TABLE_UPDATE_TIMEOUT_US 100000
+
+struct sparx5_mact_entry {
+	struct list_head list;
+	unsigned char mac[ETH_ALEN];
+	u32 flags;
+#define MAC_ENT_ALIVE	BIT(0)
+#define MAC_ENT_MOVED	BIT(1)
+#define MAC_ENT_LOCK	BIT(1)
+	u16 vid;
+	u16 port;
+};
+
+static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
+{
+	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
+}
+
+static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
+{
+	u32 val;
+
+	return readx_poll_timeout(sparx5_mact_get_status,
+		sparx5, val,
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
+		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
+}
+
+static void sparx5_mact_select(struct sparx5 *sparx5,
+			       const unsigned char mac[ETH_ALEN],
+			       u16 vid)
+{
+	u32 macl = 0, mach = 0;
+
+	/* Set the MAC address to handle and the vlan associated in a format
+	 * understood by the hardware.
+	 */
+	mach |= vid    << 16;
+	mach |= mac[0] << 8;
+	mach |= mac[1] << 0;
+	macl |= mac[2] << 24;
+	macl |= mac[3] << 16;
+	macl |= mac[4] << 8;
+	macl |= mac[5] << 0;
+
+	spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
+	spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
+}
+
+int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
+		      const unsigned char mac[ETH_ALEN], u16 vid)
+{
+	int addr, type, ret;
+
+	if (pgid < SPX5_PORTS) {
+		type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
+		addr = pgid % 32;
+		addr += (pgid / 32) << 5; /* Add upsid */
+	} else {
+		type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
+		addr = pgid - SPX5_PORTS;
+	}
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* MAC entry properties */
+	spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
+		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
+		sparx5, LRN_MAC_ACCESS_CFG_2);
+	spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
+
+	/*  Insert/learn new entry */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	return sparx5_mact_forget(sparx5, addr, port->pvid);
+}
+
+int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
+}
+
+static inline int sparx5_mact_get(struct sparx5 *sparx5,
+				  unsigned char mac[ETH_ALEN],
+				  u16 *vid, u32 *pcfg2)
+{
+	u32 mach, macl, cfg2;
+	int ret = -ENOENT;
+
+	cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
+	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
+		mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
+		macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
+		mac[0] = ((mach >> 8)  & 0xff);
+		mac[1] = ((mach >> 0)  & 0xff);
+		mac[2] = ((macl >> 24) & 0xff);
+		mac[3] = ((macl >> 16) & 0xff);
+		mac[4] = ((macl >> 8)  & 0xff);
+		mac[5] = ((macl >> 0)  & 0xff);
+		*vid = mach >> 16;
+		*pcfg2 = cfg2;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+bool sparx5_mact_getnext(struct sparx5 *sparx5,
+			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
+{
+	int ret;
+	u32 cfg2;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, *vid);
+
+	spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
+		LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
+		sparx5, LRN_SCAN_NEXT_CFG);
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
+		(MAC_CMD_FIND_SMALLEST) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+	if (ret == 0) {
+		ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
+		if (ret == 0)
+			*pcfg2 = cfg2;
+	}
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret == 0;
+}
+
+static int sparx5_mact_lookup(struct sparx5 *sparx5,
+			      const unsigned char mac[ETH_ALEN],
+			      u16 vid)
+{
+	int ret;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* Issue a lookup command */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+	if (ret)
+		goto out;
+
+	ret = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET
+		(spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2));
+
+out:
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+int sparx5_mact_forget(struct sparx5 *sparx5,
+		       const unsigned char mac[ETH_ALEN], u16 vid)
+{
+	int ret;
+
+	mutex_lock(&sparx5->lock);
+
+	sparx5_mact_select(sparx5, mac, vid);
+
+	/* Issue an unlearn command */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	ret = sparx5_mact_wait_for_completion(sparx5);
+
+	mutex_unlock(&sparx5->lock);
+
+	return ret;
+}
+
+static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
+						  const unsigned char *mac,
+						  u16 vid, u16 port_index)
+{
+	struct sparx5_mact_entry *mact_entry;
+
+	mact_entry = devm_kzalloc(sparx5->dev,
+				  sizeof(*mact_entry), GFP_ATOMIC);
+	if (!mact_entry)
+		return NULL;
+
+	memcpy(mact_entry->mac, mac, ETH_ALEN);
+	mact_entry->vid = vid;
+	mact_entry->port = port_index;
+	return mact_entry;
+}
+
+static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
+						 const unsigned char *mac,
+						 u16 vid, u16 port_index)
+{
+	struct sparx5_mact_entry *mact_entry;
+	struct sparx5_mact_entry *res = NULL;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
+		if (mact_entry->vid == vid &&
+		    ether_addr_equal(mac, mact_entry->mac) &&
+		    mact_entry->port == port_index) {
+			res = mact_entry;
+			break;
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	return res;
+}
+
+static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
+				      const char *mac, u16 vid,
+				      struct net_device *dev, bool offloaded)
+{
+	struct switchdev_notifier_fdb_info info;
+
+	info.addr = mac;
+	info.vid = vid;
+	info.offloaded = offloaded;
+	call_switchdev_notifiers(type, dev, &info.info, NULL);
+}
+
+int sparx5_add_mact_entry(struct sparx5 *sparx5,
+			  struct sparx5_port *port,
+			  const unsigned char *addr, u16 vid)
+{
+	struct sparx5_mact_entry *mact_entry;
+	int ret;
+
+	ret = sparx5_mact_lookup(sparx5, addr, vid);
+	if (ret)
+		return 0;
+
+	/* In case the entry already exists, don't add it again to SW,
+	 * just update HW, but we need to look in the actual HW because
+	 * it is possible for an entry to be learn by HW and before the
+	 * mact thread to start the frame will reach CPU and the CPU will
+	 * add the entry but without the extern_learn flag.
+	 */
+	mact_entry = find_mact_entry(sparx5, addr, vid, port->portno);
+	if (mact_entry)
+		goto update_hw;
+
+	/* Add the entry in SW MAC table not to get the notification when
+	 * SW is pulling again
+	 */
+	mact_entry = alloc_mact_entry(sparx5, addr, vid, port->portno);
+	if (!mact_entry)
+		return -ENOMEM;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_add_tail(&mact_entry->list, &sparx5->mact_entries);
+	mutex_unlock(&sparx5->mact_lock);
+
+update_hw:
+	ret = sparx5_mact_learn(sparx5, port->portno, addr, vid);
+
+	/* New entry? */
+	if (mact_entry->flags == 0) {
+		mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
+		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid,
+					  port->ndev, true);
+	}
+
+	return ret;
+}
+
+int sparx5_del_mact_entry(struct sparx5 *sparx5,
+			  const unsigned char *addr,
+			  u16 vid)
+{
+	struct sparx5_mact_entry *mact_entry, *tmp;
+
+	/* Delete the entry in SW MAC table not to get the notification when
+	 * SW is pulling again
+	 */
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
+				 list) {
+		if ((vid == 0 || mact_entry->vid == vid) &&
+		    ether_addr_equal(addr, mact_entry->mac)) {
+			list_del(&mact_entry->list);
+			devm_kfree(sparx5->dev, mact_entry);
+
+			sparx5_mact_forget(sparx5, addr, mact_entry->vid);
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	return 0;
+}
+
+static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
+				     unsigned char mac[ETH_ALEN],
+				     u16 vid, u32 cfg2)
+{
+	struct sparx5_mact_entry *mact_entry;
+	u16 port;
+	bool found = false;
+
+	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
+	    MAC_ENTRY_ADDR_TYPE_UPSID_PN)
+		return;
+
+	port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
+	if (port >= SPX5_PORTS)
+		return;
+
+	if (!test_bit(port, sparx5->bridge_mask))
+		return;
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
+		if (mact_entry->vid == vid &&
+		    ether_addr_equal(mac, mact_entry->mac)) {
+			found = true;
+			mact_entry->flags |= MAC_ENT_ALIVE;
+			if (mact_entry->port != port) {
+				dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
+					 mact_entry->port, port);
+				mact_entry->port = port;
+				mact_entry->flags |= MAC_ENT_MOVED;
+			}
+			/* Entry handled */
+			break;
+		}
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	if (found && !(mact_entry->flags & MAC_ENT_MOVED))
+		/* Present, not moved */
+		return;
+
+	if (!found) {
+		/* Entry not found - now add */
+		mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
+		if (!mact_entry)
+			return;
+
+		mact_entry->flags |= MAC_ENT_ALIVE;
+		mutex_lock(&sparx5->mact_lock);
+		list_add_tail(&mact_entry->list, &sparx5->mact_entries);
+		mutex_unlock(&sparx5->mact_lock);
+	}
+
+	/* New or moved entry - notify bridge */
+	sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
+				  mac, vid, sparx5->ports[port]->ndev,
+				  true);
+}
+
+void sparx5_mact_pull_work(struct work_struct *work)
+{
+	struct delayed_work *del_work = to_delayed_work(work);
+	struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
+					     mact_work);
+	struct sparx5_mact_entry *mact_entry, *tmp;
+	unsigned char mac[ETH_ALEN];
+	u16 vid;
+	u32 cfg2;
+	int ret;
+
+	/* Reset MAC entyry flags */
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
+		mact_entry->flags &= MAC_ENT_LOCK;
+	mutex_unlock(&sparx5->mact_lock);
+
+	/* MAIN mac address processing loop */
+	vid = 0;
+	memset(mac, 0, sizeof(mac));
+	do {
+		mutex_lock(&sparx5->lock);
+		sparx5_mact_select(sparx5, mac, vid);
+		spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
+			sparx5, LRN_SCAN_NEXT_CFG);
+		spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
+			(MAC_CMD_FIND_SMALLEST) |
+			LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+			sparx5, LRN_COMMON_ACCESS_CTRL);
+		ret = sparx5_mact_wait_for_completion(sparx5);
+		if (ret == 0)
+			ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
+		mutex_unlock(&sparx5->lock);
+		if (ret == 0)
+			sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
+	} while (ret == 0);
+
+	mutex_lock(&sparx5->mact_lock);
+	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
+				 list) {
+		/* If the entry is in HW or permanent, then skip */
+		if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
+			continue;
+
+		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
+					  mact_entry->mac, mact_entry->vid,
+					  sparx5->ports[mact_entry->port]->ndev,
+					  true);
+
+		list_del(&mact_entry->list);
+		devm_kfree(sparx5->dev, mact_entry);
+	}
+	mutex_unlock(&sparx5->mact_lock);
+
+	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
+			   SPX5_MACT_PULL_DELAY);
+}
+
+void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
+{
+	int value = max(1, msecs / 10); /* unit 10 ms */
+
+	spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
+		 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
+		 LRN_AUTOAGE_CFG_UNIT_SIZE |
+		 LRN_AUTOAGE_CFG_PERIOD_VAL,
+		 sparx5,
+		 LRN_AUTOAGE_CFG(0));
+}
+
+void sparx5_mact_init(struct sparx5 *sparx5)
+{
+	mutex_init(&sparx5->lock);
+
+	mutex_lock(&sparx5->lock);
+
+	/*  Flush MAC table */
+	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
+		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
+		sparx5, LRN_COMMON_ACCESS_CTRL);
+
+	if (sparx5_mact_wait_for_completion(sparx5) != 0)
+		dev_warn(sparx5->dev, "MAC flush error\n");
+
+	mutex_unlock(&sparx5->lock);
+
+	sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index ea75f993f80e..30940d9ee916 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -278,6 +278,9 @@ static int sparx5_probe_port(struct sparx5 *sparx5,
 	}
 	spx5_port->conf = *conf;
 
+	/* Setup VLAN */
+	sparx5_vlan_port_setup(sparx5, spx5_port->portno);
+
 	/* Create a phylink for PHY management.  Also handles SFPs */
 	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
 	spx5_port->phylink_config.type = PHYLINK_NETDEV;
@@ -532,6 +535,7 @@ static int sparx5_qlim_set(struct sparx5 *sparx5)
 
 static int sparx5_init(struct sparx5 *sparx5)
 {
+	u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 	int irq, ret;
 	u32 idx;
 
@@ -582,6 +586,9 @@ static int sparx5_init(struct sparx5 *sparx5)
 			 QFWD_SWITCH_PORT_MODE(idx));
 	}
 
+	/* Init masks */
+	sparx5_update_fwd(sparx5);
+
 	/* CPU copy CPU pgids */
 	spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
 		sparx5, ANA_AC_PGID_MISC_CFG(PGID_CPU));
@@ -594,6 +601,15 @@ static int sparx5_init(struct sparx5 *sparx5)
 			 ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
 			 sparx5, ANA_CL_FILTER_CTRL(idx));
 
+	/* Init MAC table, ageing */
+	sparx5_mact_init(sparx5);
+
+	/* Setup VLANs */
+	sparx5_vlan_init(sparx5);
+
+	/* Add host mode BC address (points only to CPU) */
+	sparx5_mact_learn(sparx5, PGID_CPU, broadcast, NULL_VID);
+
 	/* Enable queue limitation watermarks */
 	sparx5_qlim_set(sparx5);
 
@@ -647,6 +663,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 	struct device_node *ports, *portnp;
 	const u8 *mac_addr;
 	int err = 0;
+	char queue_name[32];
 
 	if (!np && !pdev->dev.platform_data)
 		return -ENODEV;
@@ -726,11 +743,24 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	/* Init mact_sw struct */
+	mutex_init(&sparx5->mact_lock);
+	INIT_LIST_HEAD(&sparx5->mact_entries);
+	snprintf(queue_name, sizeof(queue_name), "%s-mact",
+		 dev_name(sparx5->dev));
+	sparx5->mact_queue = create_singlethread_workqueue(queue_name);
+	INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work);
+	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
+			   SPX5_MACT_PULL_DELAY);
+
 	err = sparx5_register_netdevs(sparx5);
 	if (err)
 		goto cleanup_ports;
 
 	sparx5_board_init(sparx5);
+	err = sparx5_register_notifier_blocks(sparx5);
+	if (err)
+		goto cleanup_ports;
 
 	return err;
 
@@ -744,6 +774,8 @@ static int mchp_sparx5_remove(struct platform_device *pdev)
 	struct sparx5 *sparx5 = platform_get_drvdata(pdev);
 
 	sparx5_cleanup_ports(sparx5);
+	/* Unregister netdevs */
+	sparx5_unregister_notifier_blocks(sparx5);
 
 	return 0;
 }
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index a92ebf339fe1..49657f9edbf3 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -130,7 +130,25 @@ struct sparx5 {
 	/* port structures are in net device */
 	struct sparx5_port                    *ports[SPX5_PORTS];
 	enum sparx5_core_clockfreq            coreclock;
+	/* Notifiers */
+	struct notifier_block                 netdevice_nb;
+	struct notifier_block                 switchdev_nb;
+	struct notifier_block                 switchdev_blocking_nb;
+	/* Switch state */
 	u8                                    base_mac[ETH_ALEN];
+	/* Associated bridge device (when bridged) */
+	struct net_device                     *hw_bridge_dev;
+	/* Bridged interfaces */
+	DECLARE_BITMAP(bridge_mask,           SPX5_PORTS);
+	DECLARE_BITMAP(bridge_fwd_mask,       SPX5_PORTS);
+	DECLARE_BITMAP(bridge_lrn_mask,       SPX5_PORTS);
+	DECLARE_BITMAP(vlan_mask[VLAN_N_VID], SPX5_PORTS);
+	/* SW MAC table */
+	struct list_head                      mact_entries;
+	/* mac table list (mact_entries) mutex */
+	struct mutex                          mact_lock;
+	struct delayed_work                   mact_work;
+	struct workqueue_struct               *mact_queue;
 	/* Board specifics */
 	bool                                  sd_sgpio_remapping;
 };
@@ -139,11 +157,44 @@ struct sparx5 {
 void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
 bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
 
+/* sparx5_switchdev.c */
+int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
+void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
+
 /* sparx5_packet.c */
 irqreturn_t sparx5_xtr_handler(int irq, void *_priv);
 int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev);
 void sparx5_manual_injection_mode(struct sparx5 *sparx5);
 
+/* sparx5_mactable.c */
+void sparx5_mact_pull_work(struct work_struct *work);
+int sparx5_mact_learn(struct sparx5 *sparx5, int port,
+		      const unsigned char mac[ETH_ALEN], u16 vid);
+bool sparx5_mact_getnext(struct sparx5 *sparx5,
+			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2);
+int sparx5_mact_forget(struct sparx5 *sparx5,
+		       const unsigned char mac[ETH_ALEN], u16 vid);
+int sparx5_add_mact_entry(struct sparx5 *sparx5,
+			  struct sparx5_port *port,
+			  const unsigned char *addr, u16 vid);
+int sparx5_del_mact_entry(struct sparx5 *sparx5,
+			  const unsigned char *addr,
+			  u16 vid);
+int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr);
+int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr);
+void sparx5_set_ageing(struct sparx5 *sparx5, int msecs);
+void sparx5_mact_init(struct sparx5 *sparx5);
+
+/* sparx5_vlan.c */
+void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable);
+void sparx5_update_fwd(struct sparx5 *sparx5);
+void sparx5_vlan_init(struct sparx5 *sparx5);
+void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno);
+int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
+			bool untagged);
+int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
+void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index 98decd1d6f57..a0992443ab9f 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -116,6 +116,15 @@ static int sparx5_port_stop(struct net_device *ndev)
 	return 0;
 }
 
+static void sparx5_set_rx_mode(struct net_device *dev)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (!test_bit(port->portno, sparx5->bridge_mask))
+		__dev_mc_sync(dev, sparx5_mc_sync, sparx5_mc_unsync);
+}
+
 static int sparx5_port_get_phys_port_name(struct net_device *dev,
 					  char *buf, size_t len)
 {
@@ -131,21 +140,43 @@ static int sparx5_port_get_phys_port_name(struct net_device *dev,
 
 static int sparx5_set_mac_address(struct net_device *dev, void *p)
 {
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
 	const struct sockaddr *addr = p;
 
+	/* Remove current */
+	sparx5_mact_forget(sparx5, dev->dev_addr,  port->pvid);
+
+	/* Add new */
+	sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid);
+
 	/* Record the address */
 	ether_addr_copy(dev->dev_addr, addr->sa_data);
 
 	return 0;
 }
 
+static int sparx5_get_port_parent_id(struct net_device *dev,
+				     struct netdev_phys_item_id *ppid)
+{
+	struct sparx5_port *sparx5_port = netdev_priv(dev);
+	struct sparx5 *sparx5 = sparx5_port->sparx5;
+
+	ppid->id_len = sizeof(sparx5->base_mac);
+	memcpy(&ppid->id, &sparx5->base_mac, ppid->id_len);
+
+	return 0;
+}
+
 static const struct net_device_ops sparx5_port_netdev_ops = {
 	.ndo_open               = sparx5_port_open,
 	.ndo_stop               = sparx5_port_stop,
 	.ndo_start_xmit         = sparx5_port_xmit_impl,
+	.ndo_set_rx_mode        = sparx5_set_rx_mode,
 	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
 	.ndo_set_mac_address    = sparx5_set_mac_address,
 	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_get_port_parent_id = sparx5_get_port_parent_id,
 };
 
 bool sparx5_netdevice_check(const struct net_device *dev)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
index 209eef5c6385..c4b1d89d2204 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
@@ -143,6 +143,12 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
 		return;
 	}
 
+	/* Everything we see on an interface that is in the HW bridge
+	 * has already been forwarded
+	 */
+	if (test_bit(port->portno, sparx5->bridge_mask))
+		skb->offload_fwd_mark = 1;
+
 	/* Finish up skb */
 	skb_put(skb, byte_cnt - ETH_FCS_LEN);
 	eth_skb_pad(skb);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
new file mode 100644
index 000000000000..1059a187ae47
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/if_bridge.h>
+#include <net/switchdev.h>
+
+#include "sparx5_main.h"
+
+static struct workqueue_struct *sparx5_owq;
+
+struct sparx5_switchdev_event_work {
+	struct work_struct work;
+	struct switchdev_notifier_fdb_info fdb_info;
+	struct net_device *dev;
+	unsigned long event;
+};
+
+static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,
+					  unsigned long flags)
+{
+	sparx5_pgid_update_mask(port, PGID_MC_FLOOD, flags & BR_MCAST_FLOOD);
+}
+
+static void sparx5_attr_stp_state_set(struct sparx5_port *port,
+				      struct switchdev_trans *trans,
+				      u8 state)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (!test_bit(port->portno, sparx5->bridge_mask)) {
+		netdev_err(port->ndev,
+			   "Controlling non-bridged port %d?\n", port->portno);
+		return;
+	}
+
+	switch (state) {
+	case BR_STATE_FORWARDING:
+		set_bit(port->portno, sparx5->bridge_fwd_mask);
+		fallthrough;
+	case BR_STATE_LEARNING:
+		set_bit(port->portno, sparx5->bridge_lrn_mask);
+		break;
+
+	default:
+		/* All other states treated as blocking */
+		clear_bit(port->portno, sparx5->bridge_fwd_mask);
+		clear_bit(port->portno, sparx5->bridge_lrn_mask);
+		break;
+	}
+
+	/* apply the bridge_fwd_mask to all the ports */
+	sparx5_update_fwd(sparx5);
+}
+
+static void sparx5_port_attr_ageing_set(struct sparx5_port *port,
+					unsigned long ageing_clock_t)
+{
+	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
+	u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
+
+	sparx5_set_ageing(port->sparx5, ageing_time);
+}
+
+static int sparx5_port_attr_set(struct net_device *dev,
+				const struct switchdev_attr *attr,
+				struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+
+	if (switchdev_trans_ph_prepare(trans))
+		return 0;
+
+	switch (attr->id) {
+	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		sparx5_port_attr_bridge_flags(port, attr->u.brport_flags);
+		break;
+	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+		sparx5_attr_stp_state_set(port, trans, attr->u.stp_state);
+		break;
+	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+		sparx5_port_attr_ageing_set(port, attr->u.ageing_time);
+		break;
+	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+		port->vlan_aware = attr->u.vlan_filtering;
+		sparx5_vlan_port_apply(port->sparx5, port);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int sparx5_port_bridge_join(struct sparx5_port *port,
+				   struct net_device *bridge)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
+		/* First bridged port */
+		sparx5->hw_bridge_dev = bridge;
+	else
+		if (sparx5->hw_bridge_dev != bridge)
+			/* This is adding the port to a second bridge, this is
+			 * unsupported
+			 */
+			return -ENODEV;
+
+	set_bit(port->portno, sparx5->bridge_mask);
+
+	/* Port enters in bridge mode therefor don't need to copy to CPU
+	 * frames for multicast in case the bridge is not requesting them
+	 */
+	__dev_mc_unsync(port->ndev, sparx5_mc_unsync);
+
+	return 0;
+}
+
+static void sparx5_port_bridge_leave(struct sparx5_port *port,
+				     struct net_device *bridge)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+
+	clear_bit(port->portno, sparx5->bridge_mask);
+	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
+		sparx5->hw_bridge_dev = NULL;
+
+	/* Clear bridge vlan settings before updating the port settings */
+	port->vlan_aware = 0;
+	port->pvid = NULL_VID;
+	port->vid = NULL_VID;
+
+	/* Port enters in host more therefore restore mc list */
+	__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
+}
+
+static int sparx5_port_changeupper(struct net_device *dev,
+				   struct netdev_notifier_changeupper_info *info)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int err = 0;
+
+	if (netif_is_bridge_master(info->upper_dev)) {
+		if (info->linking)
+			err = sparx5_port_bridge_join(port, info->upper_dev);
+		else
+			sparx5_port_bridge_leave(port, info->upper_dev);
+
+		sparx5_vlan_port_apply(port->sparx5, port);
+	}
+
+	return err;
+}
+
+static int sparx5_port_add_addr(struct net_device *dev, bool up)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	struct sparx5 *sparx5 = port->sparx5;
+	u16 vid = port->pvid;
+
+	if (up)
+		sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
+	else
+		sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
+
+	return 0;
+}
+
+static int sparx5_netdevice_port_event(struct net_device *dev,
+				       struct notifier_block *nb,
+				       unsigned long event, void *ptr)
+{
+	int err = 0;
+
+	if (!sparx5_netdevice_check(dev))
+		return 0;
+
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		err = sparx5_port_changeupper(dev, ptr);
+		break;
+	case NETDEV_PRE_UP:
+		err = sparx5_port_add_addr(dev, true);
+		break;
+	case NETDEV_DOWN:
+		err = sparx5_port_add_addr(dev, false);
+		break;
+	}
+
+	return err;
+}
+
+static int sparx5_netdevice_event(struct notifier_block *nb,
+				  unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	int ret = 0;
+
+	ret = sparx5_netdevice_port_event(dev, nb, event, ptr);
+
+	return notifier_from_errno(ret);
+}
+
+static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
+{
+	struct sparx5_switchdev_event_work *switchdev_work =
+		container_of(work, struct sparx5_switchdev_event_work, work);
+	struct net_device *dev = switchdev_work->dev;
+	struct switchdev_notifier_fdb_info *fdb_info;
+	struct sparx5_port *port;
+	struct sparx5 *sparx5;
+
+	rtnl_lock();
+	if (!sparx5_netdevice_check(dev))
+		goto out;
+
+	port = netdev_priv(dev);
+	sparx5 = port->sparx5;
+
+	fdb_info = &switchdev_work->fdb_info;
+
+	switch (switchdev_work->event) {
+	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		sparx5_add_mact_entry(sparx5, port, fdb_info->addr,
+				      fdb_info->vid);
+		break;
+	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		sparx5_del_mact_entry(sparx5, fdb_info->addr, fdb_info->vid);
+		break;
+	}
+
+out:
+	rtnl_unlock();
+	kfree(switchdev_work->fdb_info.addr);
+	kfree(switchdev_work);
+	dev_put(dev);
+}
+
+static void sparx5_schedule_work(struct work_struct *work)
+{
+	queue_work(sparx5_owq, work);
+}
+
+static int sparx5_switchdev_event(struct notifier_block *unused,
+				  unsigned long event, void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	struct sparx5_switchdev_event_work *switchdev_work;
+	struct switchdev_notifier_fdb_info *fdb_info;
+	struct switchdev_notifier_info *info = ptr;
+	int err;
+
+	switch (event) {
+	case SWITCHDEV_PORT_ATTR_SET:
+		err = switchdev_handle_port_attr_set(dev, ptr,
+						     sparx5_netdevice_check,
+						     sparx5_port_attr_set);
+		return notifier_from_errno(err);
+	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		fallthrough;
+	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+		if (!switchdev_work)
+			return NOTIFY_BAD;
+
+		switchdev_work->dev = dev;
+		switchdev_work->event = event;
+
+		fdb_info = container_of(info,
+					struct switchdev_notifier_fdb_info,
+					info);
+		INIT_WORK(&switchdev_work->work,
+			  sparx5_switchdev_bridge_fdb_event_work);
+		memcpy(&switchdev_work->fdb_info, ptr,
+		       sizeof(switchdev_work->fdb_info));
+		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+		if (!switchdev_work->fdb_info.addr)
+			goto err_addr_alloc;
+
+		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
+				fdb_info->addr);
+		dev_hold(dev);
+
+		sparx5_schedule_work(&switchdev_work->work);
+		break;
+	}
+
+	return NOTIFY_DONE;
+err_addr_alloc:
+	kfree(switchdev_work);
+	return NOTIFY_BAD;
+}
+
+static void sparx5_sync_port_dev_addr(struct sparx5 *sparx5,
+				      struct sparx5_port *port,
+				      u16 vid, bool add)
+{
+	if (!port ||
+	    !test_bit(port->portno, sparx5->bridge_mask))
+		return; /* Skip null/host interfaces */
+
+	/* Bridge connects to vid? */
+	if (add) {
+		/* Add port MAC address from the VLAN */
+		sparx5_mact_learn(sparx5, PGID_CPU,
+				  port->ndev->dev_addr, vid);
+	} else {
+		/* Control port addr visibility depending on
+		 * port VLAN connectivity.
+		 */
+		if (test_bit(port->portno, sparx5->vlan_mask[vid]))
+			sparx5_mact_learn(sparx5, PGID_CPU,
+					  port->ndev->dev_addr, vid);
+		else
+			sparx5_mact_forget(sparx5,
+					   port->ndev->dev_addr, vid);
+	}
+}
+
+static void sparx5_sync_bridge_dev_addr(struct net_device *dev,
+					struct sparx5 *sparx5,
+					u16 vid, bool add)
+{
+	int i;
+
+	/* First, handle bridge address'es */
+	if (add) {
+		sparx5_mact_learn(sparx5, PGID_CPU, dev->dev_addr,
+				  vid);
+		sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
+				  vid);
+	} else {
+		sparx5_mact_forget(sparx5, dev->dev_addr, vid);
+		sparx5_mact_forget(sparx5, dev->broadcast, vid);
+	}
+
+	/* Now look at bridged ports */
+	for (i = 0; i < SPX5_PORTS; i++)
+		sparx5_sync_port_dev_addr(sparx5, sparx5->ports[i], vid, add);
+}
+
+static int sparx5_handle_port_vlan_add(struct net_device *dev,
+				       struct notifier_block *nb,
+				       const struct switchdev_obj_port_vlan *v,
+				       struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+	u16 vid;
+
+	if (switchdev_trans_ph_prepare(trans))
+		return 0;
+
+	if (netif_is_bridge_master(dev)) {
+		if (v->flags & BRIDGE_VLAN_INFO_BRENTRY) {
+			struct sparx5 *sparx5 =
+				container_of(nb, struct sparx5,
+					     switchdev_blocking_nb);
+
+			for (vid = v->vid_begin; vid <= v->vid_end; vid++)
+				sparx5_sync_bridge_dev_addr(dev, sparx5, vid, true);
+		}
+		return 0;
+	}
+
+	for (vid = v->vid_begin; vid <= v->vid_end; vid++) {
+		ret = sparx5_vlan_vid_add(port, vid,
+					  v->flags & BRIDGE_VLAN_INFO_PVID,
+					  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sparx5_handle_port_obj_add(struct net_device *dev,
+				      struct notifier_block *nb,
+				      struct switchdev_notifier_port_obj_info *info)
+{
+	const struct switchdev_obj *obj = info->obj;
+	int err;
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = sparx5_handle_port_vlan_add(dev, nb,
+						  SWITCHDEV_OBJ_PORT_VLAN(obj),
+						  info->trans);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	info->handled = true;
+	return err;
+}
+
+static int sparx5_handle_port_vlan_del(struct net_device *dev,
+				       struct notifier_block *nb,
+				       const struct switchdev_obj_port_vlan *v,
+				       struct switchdev_trans *trans)
+{
+	struct sparx5_port *port = netdev_priv(dev);
+	int ret;
+	u16 vid;
+
+	/* Master bridge? */
+	if (netif_is_bridge_master(dev)) {
+		struct sparx5 *sparx5 = container_of(nb, struct sparx5,
+						     switchdev_blocking_nb);
+
+		for (vid = v->vid_begin; vid <= v->vid_end; vid++)
+			sparx5_sync_bridge_dev_addr(dev, sparx5, vid, false);
+		return 0;
+	}
+
+	for (vid = v->vid_begin; vid <= v->vid_end; vid++) {
+		ret = sparx5_vlan_vid_del(port, vid);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sparx5_handle_port_obj_del(struct net_device *dev,
+				      struct notifier_block *nb,
+				      struct switchdev_notifier_port_obj_info *info)
+{
+	const struct switchdev_obj *obj = info->obj;
+	int err;
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = sparx5_handle_port_vlan_del(dev, nb,
+						  SWITCHDEV_OBJ_PORT_VLAN(obj),
+						  info->trans);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	info->handled = true;
+	return err;
+}
+
+static int sparx5_switchdev_blocking_event(struct notifier_block *nb,
+					   unsigned long event,
+					   void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	int err;
+
+	switch (event) {
+	case SWITCHDEV_PORT_OBJ_ADD:
+		err = sparx5_handle_port_obj_add(dev, nb, ptr);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_OBJ_DEL:
+		err = sparx5_handle_port_obj_del(dev, nb, ptr);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_ATTR_SET:
+		err = switchdev_handle_port_attr_set(dev, ptr,
+						     sparx5_netdevice_check,
+						     sparx5_port_attr_set);
+		return notifier_from_errno(err);
+	}
+
+	return NOTIFY_DONE;
+}
+
+int sparx5_register_notifier_blocks(struct sparx5 *s5)
+{
+	int err;
+
+	s5->netdevice_nb.notifier_call = sparx5_netdevice_event;
+	err = register_netdevice_notifier(&s5->netdevice_nb);
+	if (err)
+		return err;
+
+	s5->switchdev_nb.notifier_call = sparx5_switchdev_event;
+	err = register_switchdev_notifier(&s5->switchdev_nb);
+	if (err)
+		goto err_switchdev_nb;
+
+	s5->switchdev_blocking_nb.notifier_call = sparx5_switchdev_blocking_event;
+	err = register_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
+	if (err)
+		goto err_switchdev_blocking_nb;
+
+	sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
+	if (!sparx5_owq)
+		goto err_switchdev_blocking_nb;
+
+	return 0;
+
+err_switchdev_blocking_nb:
+	unregister_switchdev_notifier(&s5->switchdev_nb);
+err_switchdev_nb:
+	unregister_netdevice_notifier(&s5->netdevice_nb);
+
+	return err;
+}
+
+void sparx5_unregister_notifier_blocks(struct sparx5 *s5)
+{
+	destroy_workqueue(sparx5_owq);
+
+	unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
+	unregister_switchdev_notifier(&s5->switchdev_nb);
+	unregister_netdevice_notifier(&s5->netdevice_nb);
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
new file mode 100644
index 000000000000..a2ccb2fae945
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include "sparx5_main.h"
+
+static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
+{
+	u32 mask[3];
+
+	/* Divide up mask in 32 bit words */
+	bitmap_to_arr32(mask, sparx5->vlan_mask[vid], SPX5_PORTS);
+
+	/* Output mask to respective registers */
+	spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
+	spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
+	spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
+
+	return 0;
+}
+
+void sparx5_vlan_init(struct sparx5 *sparx5)
+{
+	u16 vid;
+
+	spx5_rmw(ANA_L3_VLAN_CTRL_VLAN_ENA_SET(1),
+		 ANA_L3_VLAN_CTRL_VLAN_ENA,
+		 sparx5,
+		 ANA_L3_VLAN_CTRL);
+
+	/* Map VLAN = FID */
+	for (vid = NULL_VID; vid < VLAN_N_VID; vid++)
+		spx5_rmw(ANA_L3_VLAN_CFG_VLAN_FID_SET(vid),
+			 ANA_L3_VLAN_CFG_VLAN_FID,
+			 sparx5,
+			 ANA_L3_VLAN_CFG(vid));
+}
+
+void sparx5_vlan_port_setup(struct sparx5 *sparx5, int portno)
+{
+	struct sparx5_port *port = sparx5->ports[portno];
+
+	/* Configure PVID */
+	spx5_rmw(ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(0) |
+		 ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid),
+		 ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA |
+		 ANA_CL_VLAN_CTRL_PORT_VID,
+		 sparx5,
+		 ANA_CL_VLAN_CTRL(port->portno));
+}
+
+int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
+			bool untagged)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	int ret;
+
+	/* Make the port a member of the VLAN */
+	set_bit(port->portno, sparx5->vlan_mask[vid]);
+	ret = sparx5_vlant_set_mask(sparx5, vid);
+	if (ret)
+		return ret;
+
+	/* Default ingress vlan classification */
+	if (pvid)
+		port->pvid = vid;
+
+	/* Untagged egress vlan clasification */
+	if (untagged && port->vid != vid) {
+		if (port->vid) {
+			netdev_err(port->ndev,
+				   "Port already has a native VLAN: %d\n",
+				   port->vid);
+			return -EBUSY;
+		}
+		port->vid = vid;
+	}
+
+	sparx5_vlan_port_apply(sparx5, port);
+
+	return 0;
+}
+
+int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	int ret;
+
+	/* 8021q removes VID 0 on module unload for all interfaces
+	 * with VLAN filtering feature. We need to keep it to receive
+	 * untagged traffic.
+	 */
+	if (vid == 0)
+		return 0;
+
+	/* Stop the port from being a member of the vlan */
+	clear_bit(port->portno, sparx5->vlan_mask[vid]);
+	ret = sparx5_vlant_set_mask(sparx5, vid);
+	if (ret)
+		return ret;
+
+	/* Ingress */
+	if (port->pvid == vid)
+		port->pvid = 0;
+
+	/* Egress */
+	if (port->vid == vid)
+		port->vid = 0;
+
+	sparx5_vlan_port_apply(sparx5, port);
+
+	return 0;
+}
+
+void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
+{
+	struct sparx5 *sparx5 = port->sparx5;
+	u32 val, mask;
+
+	/* mask is spread across 3 registers x 32 bit */
+	if (port->portno < 32) {
+		mask = BIT(port->portno);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG(pgid));
+	} else if (port->portno < 64) {
+		mask = BIT(port->portno - 32);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG1(pgid));
+	} else if (port->portno < SPX5_PORTS) {
+		mask = BIT(port->portno - 64);
+		val = enable ? mask : 0;
+		spx5_rmw(val, mask, sparx5, ANA_AC_PGID_CFG2(pgid));
+	} else {
+		netdev_err(port->ndev, "Invalid port no: %d\n", port->portno);
+	}
+}
+
+void sparx5_update_fwd(struct sparx5 *sparx5)
+{
+	u32 mask[3];
+	DECLARE_BITMAP(workmask, SPX5_PORTS);
+	int port;
+
+	/* Divide up fwd mask in 32 bit words */
+	bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
+
+	/* Update flood masks */
+	for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
+		spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
+		spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
+		spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
+	}
+
+	/* Update SRC masks */
+	for (port = 0; port < SPX5_PORTS; port++) {
+		if (test_bit(port, sparx5->bridge_fwd_mask)) {
+			/* Allow to send to all bridged but self */
+			bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
+			clear_bit(port, workmask);
+			bitmap_to_arr32(mask, workmask, SPX5_PORTS);
+			spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
+			spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
+			spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
+		} else {
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
+			spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
+		}
+	}
+
+	/* Learning enabled only for bridged ports */
+	bitmap_and(workmask, sparx5->bridge_fwd_mask,
+		   sparx5->bridge_lrn_mask, SPX5_PORTS);
+	bitmap_to_arr32(mask, workmask, SPX5_PORTS);
+
+	/* Apply learning mask */
+	spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
+	spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
+	spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
+}
+
+void sparx5_vlan_port_apply(struct sparx5 *sparx5,
+			    struct sparx5_port *port)
+
+{
+	u32 val;
+
+	/* Configure PVID, vlan aware */
+	val = ANA_CL_VLAN_CTRL_VLAN_AWARE_ENA_SET(port->vlan_aware) |
+		ANA_CL_VLAN_CTRL_VLAN_POP_CNT_SET(port->vlan_aware) |
+		ANA_CL_VLAN_CTRL_PORT_VID_SET(port->pvid);
+	spx5_wr(val, sparx5, ANA_CL_VLAN_CTRL(port->portno));
+
+	val = 0;
+	if (port->vlan_aware && !port->pvid)
+		/* If port is vlan-aware and tagged, drop untagged and
+		 * priority tagged frames.
+		 */
+		val = ANA_CL_VLAN_FILTER_CTRL_TAG_REQUIRED_ENA_SET(1) |
+			ANA_CL_VLAN_FILTER_CTRL_PRIO_CTAG_DIS_SET(1) |
+			ANA_CL_VLAN_FILTER_CTRL_PRIO_STAG_DIS_SET(1);
+	spx5_wr(val, sparx5,
+		ANA_CL_VLAN_FILTER_CTRL(port->portno, 0));
+
+	/* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
+	val = REW_TAG_CTRL_TAG_TPID_CFG_SET(0);
+	if (port->vlan_aware) {
+		if (port->vid)
+			/* Tag all frames except when VID == DEFAULT_VLAN */
+			val |= REW_TAG_CTRL_TAG_CFG_SET(1);
+		else
+			val |= REW_TAG_CTRL_TAG_CFG_SET(3);
+	}
+	spx5_wr(val, sparx5, REW_TAG_CTRL(port->portno));
+
+	/* Egress VID */
+	spx5_rmw(REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
+		 REW_PORT_VLAN_CFG_PORT_VID,
+		 sparx5,
+		 REW_PORT_VLAN_CFG(port->portno));
+}
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 6/8] net: sparx5: add calendar bandwidth allocation support
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This configures the Sparx5 calendars according to the bandwidth
requested in the Device Tree nodes.
It also checks if the total requested bandwidth is within the
specs of the detected Sparx5 models limits.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   2 +-
 .../microchip/sparx5/sparx5_calendar.c        | 595 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |   8 +
 .../ethernet/microchip/sparx5/sparx5_main.h   |   4 +
 4 files changed, 608 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 32e0691e328a..17654ab90ce1 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -7,4 +7,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
 sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
  sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
- sparx5_netdev.o sparx5_port.o sparx5_phylink.o
+ sparx5_netdev.o sparx5_port.o sparx5_phylink.o sparx5_calendar.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
new file mode 100644
index 000000000000..932c897c72a7
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "sparx5_main.h"
+
+/* QSYS calendar information */
+#define SPX5_PORTS_PER_CALREG          10  /* Ports mapped in a calendar register */
+#define SPX5_CALBITS_PER_PORT          3   /* Bit per port in calendar register */
+
+/* DSM calendar information */
+#define SPX5_DSM_CAL_LEN               64
+#define SPX5_DSM_CAL_EMPTY             0xFFFF
+#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
+#define SPX5_DSM_CAL_TAXIS             8
+#define SPX5_DSM_CAL_BW_LOSS           553
+
+#define SPX5_TAXI_PORT_MAX             70
+
+#define SPEED_12500                    12500
+
+/* Maps from taxis to port numbers */
+static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = {
+	{57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23},
+	{58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31},
+	{59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39},
+	{60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47},
+	{61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+};
+
+struct sparx5_calendar_data {
+	u32 schedule[SPX5_DSM_CAL_LEN];
+	u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 new_slots[SPX5_DSM_CAL_LEN];
+	u32 temp_sched[SPX5_DSM_CAL_LEN];
+	u32 indices[SPX5_DSM_CAL_LEN];
+	u32 short_list[SPX5_DSM_CAL_LEN];
+	u32 long_list[SPX5_DSM_CAL_LEN];
+};
+
+static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
+{
+	switch (sparx5->target_ct) {
+	case SPX5_TARGET_CT_7546:
+	case SPX5_TARGET_CT_7546TSN:
+		return 65000;
+	case SPX5_TARGET_CT_7549:
+	case SPX5_TARGET_CT_7549TSN:
+		return 91000;
+	case SPX5_TARGET_CT_7552:
+	case SPX5_TARGET_CT_7552TSN:
+		return 129000;
+	case SPX5_TARGET_CT_7556:
+	case SPX5_TARGET_CT_7556TSN:
+		return 161000;
+	case SPX5_TARGET_CT_7558:
+	case SPX5_TARGET_CT_7558TSN:
+		return 201000;
+	default:
+		return 0;
+	}
+}
+
+/* This is used in calendar configuration */
+enum sparx5_cal_bw {
+	SPX5_CAL_SPEED_NONE = 0,
+	SPX5_CAL_SPEED_1G   = 1,
+	SPX5_CAL_SPEED_2G5  = 2,
+	SPX5_CAL_SPEED_5G   = 3,
+	SPX5_CAL_SPEED_10G  = 4,
+	SPX5_CAL_SPEED_25G  = 5,
+	SPX5_CAL_SPEED_0G5  = 6,
+	SPX5_CAL_SPEED_12G5 = 7
+};
+
+static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
+{
+	switch (cclock) {
+	case SPX5_CORE_CLOCK_250MHZ: return 83000; /* 250000 / 3 */
+	case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */
+	case SPX5_CORE_CLOCK_625MHZ: return  208000; /* 625000 / 3 */
+	default: return 0;
+	}
+	return 0;
+}
+
+static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
+{
+	switch (speed) {
+	case SPX5_CAL_SPEED_1G:   return 1000;
+	case SPX5_CAL_SPEED_2G5:  return 2500;
+	case SPX5_CAL_SPEED_5G:   return 5000;
+	case SPX5_CAL_SPEED_10G:  return 10000;
+	case SPX5_CAL_SPEED_25G:  return 25000;
+	case SPX5_CAL_SPEED_0G5:  return 500;
+	case SPX5_CAL_SPEED_12G5: return 12500;
+	default: return 0;
+	}
+}
+
+static u32 sparx5_bandwidth_to_calendar(u32 bw)
+{
+	switch (bw) {
+	case SPEED_10:      return SPX5_CAL_SPEED_0G5;
+	case SPEED_100:     return SPX5_CAL_SPEED_0G5;
+	case SPEED_1000:    return SPX5_CAL_SPEED_1G;
+	case SPEED_2500:    return SPX5_CAL_SPEED_2G5;
+	case SPEED_5000:    return SPX5_CAL_SPEED_5G;
+	case SPEED_10000:   return SPX5_CAL_SPEED_10G;
+	case SPEED_12500:   return SPX5_CAL_SPEED_12G5;
+	case SPEED_25000:   return SPX5_CAL_SPEED_25G;
+	case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G;
+	default:            return SPX5_CAL_SPEED_NONE;
+	}
+}
+
+static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
+						    u32 portno)
+{
+	struct sparx5_port *port;
+
+	if (portno >= SPX5_PORTS) {
+		/* Internal ports */
+		if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
+			/* Equals 1.25G */
+			return SPX5_CAL_SPEED_2G5;
+		} else if (portno == SPX5_PORT_VD0) {
+			/* IPMC only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		} else if (portno == SPX5_PORT_VD1) {
+			/* OAM only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		} else if (portno == SPX5_PORT_VD2) {
+			/* IPinIP gets only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		}
+		/* not in port map */
+		return SPX5_CAL_SPEED_NONE;
+	}
+	/* Front ports - may be used */
+	port = sparx5->ports[portno];
+	if (!port)
+		return SPX5_CAL_SPEED_NONE;
+	return sparx5_bandwidth_to_calendar(port->conf.max_speed);
+}
+
+/* Auto configure the QSYS calendar based on port configuration */
+int sparx5_config_auto_calendar(struct sparx5 *sparx5)
+{
+	u32 cal[7], value, idx, portno;
+	u32 max_core_bw;
+	u32 total_bw = 0, used_port_bw = 0;
+	int err = 0;
+	enum sparx5_cal_bw spd;
+
+	memset(cal, 0, sizeof(cal));
+
+	max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock);
+	if (max_core_bw == 0) {
+		dev_err(sparx5->dev, "Core clock not supported");
+		return -EINVAL;
+	}
+
+	/* Setup the calendar with the bandwidth to each port */
+	for (portno = 0; portno < SPX5_PORTS_ALL; portno++) {
+		u64 reg, offset, this_bw;
+
+		spd = sparx5_get_port_cal_speed(sparx5, portno);
+		if (spd == SPX5_CAL_SPEED_NONE)
+			continue;
+
+		this_bw = sparx5_cal_speed_to_value(spd);
+		if (portno < SPX5_PORTS)
+			used_port_bw += this_bw;
+		else
+			/* Internal ports are granted half the value */
+			this_bw = this_bw / 2;
+		total_bw += this_bw;
+		reg = portno;
+		offset = do_div(reg, SPX5_PORTS_PER_CALREG);
+		cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT);
+	}
+
+	if (used_port_bw > sparx5_target_bandwidth(sparx5)) {
+		dev_err(sparx5->dev,
+			"Port BW %u above target BW %u\n",
+			used_port_bw, sparx5_target_bandwidth(sparx5));
+		return -EINVAL;
+	}
+
+	if (total_bw > max_core_bw) {
+		dev_err(sparx5->dev,
+			"Total BW %u above switch core BW %u\n",
+			total_bw, max_core_bw);
+		return -EINVAL;
+	}
+
+	/* Halt the calendar while changing it */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
+		 QSYS_CAL_CTRL_CAL_MODE,
+		 sparx5, QSYS_CAL_CTRL);
+
+	/* Assign port bandwidth to auto calendar */
+	for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
+		spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
+
+	/* Increase grant rate of all ports to account for
+	 * core clock ppm deviations
+	 */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */
+		 QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
+		 sparx5,
+		 QSYS_CAL_CTRL);
+
+	/* Grant idle usage to VD 0-2 */
+	for (idx = 2; idx < 5; idx++)
+		spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12),
+			sparx5,
+			HSCH_OUTB_SHARE_ENA(idx));
+
+	/* Enable Auto mode */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
+		 QSYS_CAL_CTRL_CAL_MODE,
+		 sparx5, QSYS_CAL_CTRL);
+
+	/* Verify successful calendar config */
+	value = spx5_rd(sparx5, QSYS_CAL_CTRL);
+	if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) {
+		dev_err(sparx5->dev, "QSYS calendar error\n");
+		err = -EINVAL;
+	}
+	return err;
+}
+
+static u32 sparx5_dsm_exb_gcd(u32 a, u32 b)
+{
+	if (b == 0)
+		return a;
+	return sparx5_dsm_exb_gcd(b, a % b);
+}
+
+static u32 sparx5_dsm_cal_len(u32 *cal)
+{
+	u32 idx = 0, len = 0;
+
+	while (idx < SPX5_DSM_CAL_LEN) {
+		if (cal[idx] != SPX5_DSM_CAL_EMPTY)
+			len++;
+		idx++;
+	}
+	return len;
+}
+
+static u32 sparx5_dsm_cp_cal(u32 *sched)
+{
+	u32 idx = 0, tmp;
+
+	while (idx < SPX5_DSM_CAL_LEN) {
+		if (sched[idx] != SPX5_DSM_CAL_EMPTY) {
+			tmp = sched[idx];
+			sched[idx] = SPX5_DSM_CAL_EMPTY;
+			return tmp;
+		}
+		idx++;
+	}
+	return SPX5_DSM_CAL_EMPTY;
+}
+
+static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
+				    struct sparx5_calendar_data *data)
+{
+	bool slow_mode;
+	u32 gcd, idx, sum, min, factor;
+	u32 num_of_slots, slot_spd, empty_slots;
+	u32 taxi_bw, clk_period_ps;
+
+	clk_period_ps = sparx5_clk_period(sparx5->coreclock);
+	taxi_bw = 128 * 1000000 / clk_period_ps;
+	slow_mode = !!(clk_period_ps > 2000);
+	memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi],
+	       sizeof(data->taxi_ports));
+
+	for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
+		data->new_slots[idx] = SPX5_DSM_CAL_EMPTY;
+		data->schedule[idx] = SPX5_DSM_CAL_EMPTY;
+		data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY;
+	}
+	/* Default empty calendar */
+	data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
+
+	/* Map ports to taxi positions */
+	for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
+		u32 portno = data->taxi_ports[idx];
+
+		if (portno < SPX5_TAXI_PORT_MAX) {
+			data->taxi_speeds[idx] = sparx5_cal_speed_to_value
+				(sparx5_get_port_cal_speed(sparx5, portno));
+		} else {
+			data->taxi_speeds[idx] = 0;
+		}
+	}
+
+	sum = 0;
+	min = 25000;
+	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
+		u32 jdx;
+
+		sum += data->taxi_speeds[idx];
+		if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min)
+			min = data->taxi_speeds[idx];
+		gcd = min;
+		for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++)
+			gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]);
+	}
+	if (sum == 0) /* Empty calendar */
+		return 0;
+	/* Make room for overhead traffic */
+	factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS);
+
+	if (sum * factor > (taxi_bw * 1000)) {
+		dev_err(sparx5->dev,
+			"Taxi %u, Requested BW %u above available BW %u\n",
+			taxi, sum, taxi_bw);
+		return -EINVAL;
+	}
+	for (idx = 0; idx < 4; idx++) {
+		u32 raw_spd;
+
+		if (idx == 0)
+			raw_spd = gcd / 5;
+		else if (idx == 1)
+			raw_spd = gcd / 2;
+		else if (idx == 2)
+			raw_spd = gcd;
+		else
+			raw_spd = min;
+		slot_spd = raw_spd * factor / 1000;
+		num_of_slots = taxi_bw / slot_spd;
+		if (num_of_slots <= 64)
+			break;
+	}
+
+	num_of_slots = num_of_slots > 64 ? 64 : num_of_slots;
+	slot_spd = taxi_bw / num_of_slots;
+
+	sum = 0;
+	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
+		u32 spd = data->taxi_speeds[idx];
+		u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000;
+
+		if (adjusted_speed > 0) {
+			data->avg_dist[idx] = (128 * 1000000 * 10) /
+				(adjusted_speed * clk_period_ps);
+		} else {
+			data->avg_dist[idx] = -1;
+		}
+		data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000;
+		if (spd != 25000 && (spd != 10000 || !slow_mode)) {
+			if (num_of_slots < (5 * data->dev_slots[idx])) {
+				dev_err(sparx5->dev,
+					"Taxi %u, speed %u, Low slot sep.\n",
+					taxi, spd);
+				return -EINVAL;
+			}
+		}
+		sum += data->dev_slots[idx];
+		if (sum > num_of_slots) {
+			dev_err(sparx5->dev,
+				"Taxi %u with overhead factor %u\n",
+				taxi, factor);
+			return -EINVAL;
+		}
+	}
+
+	empty_slots = num_of_slots - sum;
+
+	for (idx = 0; idx < empty_slots; idx++)
+		data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
+
+	for (idx = 1; idx < num_of_slots; idx++) {
+		u32 indices_len = 0;
+		u32 slot, jdx, kdx, ts;
+		s32 cnt;
+		u32 num_of_old_slots, num_of_new_slots, tgt_score;
+
+		for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) {
+			if (data->dev_slots[slot] == idx) {
+				data->indices[indices_len] = slot;
+				indices_len++;
+			}
+		}
+		if (indices_len == 0)
+			continue;
+		kdx = 0;
+		for (slot = 0; slot < idx; slot++) {
+			for (jdx = 0; jdx < indices_len; jdx++, kdx++)
+				data->new_slots[kdx] = data->indices[jdx];
+		}
+
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY)
+				break;
+		}
+
+		num_of_old_slots =  slot;
+		num_of_new_slots =  kdx;
+		cnt = 0;
+		ts = 0;
+
+		if (num_of_new_slots > num_of_old_slots) {
+			memcpy(data->short_list, data->schedule,
+			       sizeof(data->short_list));
+			memcpy(data->long_list, data->new_slots,
+			       sizeof(data->long_list));
+			tgt_score = 100000 * num_of_old_slots /
+				num_of_new_slots;
+		} else {
+			memcpy(data->short_list, data->new_slots,
+			       sizeof(data->short_list));
+			memcpy(data->long_list, data->schedule,
+			       sizeof(data->long_list));
+			tgt_score = 100000 * num_of_new_slots /
+				num_of_old_slots;
+		}
+
+		while (sparx5_dsm_cal_len(data->short_list) > 0 ||
+		       sparx5_dsm_cal_len(data->long_list) > 0) {
+			u32 act = 0;
+
+			if (sparx5_dsm_cal_len(data->short_list) > 0) {
+				data->temp_sched[ts] =
+					sparx5_dsm_cp_cal(data->short_list);
+				ts++;
+				cnt += 100000;
+				act = 1;
+			}
+			while (sparx5_dsm_cal_len(data->long_list) > 0 &&
+			       cnt > 0) {
+				data->temp_sched[ts] =
+					sparx5_dsm_cp_cal(data->long_list);
+				ts++;
+				cnt -= tgt_score;
+				act = 1;
+			}
+			if (act == 0) {
+				dev_err(sparx5->dev,
+					"Error in DSM calendar calculation\n");
+				return -EINVAL;
+			}
+		}
+
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY)
+				break;
+		}
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			data->schedule[slot] = data->temp_sched[slot];
+			data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY;
+			data->new_slots[slot] = SPX5_DSM_CAL_EMPTY;
+		}
+	}
+	return 0;
+}
+
+static int sparx5_dsm_calendar_check(struct sparx5 *sparx5,
+				     struct sparx5_calendar_data *data)
+{
+	u32 num_of_slots, idx, port;
+	int cnt, max_dist;
+	u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN];
+	u32 cal_length = sparx5_dsm_cal_len(data->schedule);
+
+	for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) {
+		num_of_slots = 0;
+		max_dist = data->avg_dist[port];
+		for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
+			slot_indices[idx] = SPX5_DSM_CAL_EMPTY;
+			distances[idx] = SPX5_DSM_CAL_EMPTY;
+		}
+
+		for (idx = 0; idx < cal_length; idx++) {
+			if (data->schedule[idx] == port) {
+				slot_indices[num_of_slots] = idx;
+				num_of_slots++;
+			}
+		}
+
+		slot_indices[num_of_slots] = slot_indices[0] + cal_length;
+
+		for (idx = 0; idx < num_of_slots; idx++) {
+			distances[idx] = (slot_indices[idx + 1] -
+					  slot_indices[idx]) * 10;
+		}
+
+		for (idx = 0; idx < num_of_slots; idx++) {
+			u32 jdx, kdx;
+
+			cnt = distances[idx] - max_dist;
+			if (cnt < 0)
+				cnt = -cnt;
+			kdx = 0;
+			for (jdx = (idx + 1) % num_of_slots;
+			     jdx != idx;
+			     jdx = (jdx + 1) % num_of_slots, kdx++) {
+				cnt =  cnt + distances[jdx] - max_dist;
+				if (cnt < 0)
+					cnt = -cnt;
+				if (cnt > max_dist)
+					goto check_err;
+			}
+		}
+	}
+	return 0;
+check_err:
+	dev_err(sparx5->dev,
+		"Port %u: distance %u above limit %d\n",
+		port, cnt, max_dist);
+	return -EINVAL;
+}
+
+static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
+				      struct sparx5_calendar_data *data)
+{
+	u32 idx;
+	u32 cal_len = sparx5_dsm_cal_len(data->schedule), len;
+
+	spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1),
+		sparx5,
+		DSM_TAXI_CAL_CFG(taxi));
+	for (idx = 0; idx < cal_len; idx++) {
+		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx),
+			 DSM_TAXI_CAL_CFG_CAL_IDX,
+			 sparx5,
+			 DSM_TAXI_CAL_CFG(taxi));
+		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]),
+			 DSM_TAXI_CAL_CFG_CAL_PGM_VAL,
+			 sparx5,
+			 DSM_TAXI_CAL_CFG(taxi));
+	}
+	spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0),
+		sparx5,
+		DSM_TAXI_CAL_CFG(taxi));
+	len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5,
+						       DSM_TAXI_CAL_CFG(taxi)));
+	if (len != cal_len - 1)
+		goto update_err;
+	return 0;
+update_err:
+	dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len);
+	return -EINVAL;
+}
+
+/* Configure the DSM calendar based on port configuration */
+int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
+{
+	int taxi;
+	struct sparx5_calendar_data *data;
+	int err = 0;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) {
+		err = sparx5_dsm_calendar_calc(sparx5, taxi, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar calculation failed\n");
+			goto cal_out;
+		}
+		err = sparx5_dsm_calendar_check(sparx5, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar check failed\n");
+			goto cal_out;
+		}
+		err = sparx5_dsm_calendar_update(sparx5, taxi, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar update failed\n");
+			goto cal_out;
+		}
+	}
+cal_out:
+	kfree(data);
+	return err;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 30940d9ee916..2b42c72dac72 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -743,6 +743,14 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	err = sparx5_config_auto_calendar(sparx5);
+	if (err)
+		goto cleanup_ports;
+
+	err = sparx5_config_dsm_calendar(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	/* Init mact_sw struct */
 	mutex_init(&sparx5->mact_lock);
 	INIT_LIST_HEAD(&sparx5->mact_entries);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 49657f9edbf3..e59e1962f7d4 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -195,6 +195,10 @@ int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
 int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
 void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
 
+/* sparx5_calendar.c */
+int sparx5_config_auto_calendar(struct sparx5 *sparx5);
+int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
-- 
2.29.2


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

* [RFC PATCH v2 6/8] net: sparx5: add calendar bandwidth allocation support
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This configures the Sparx5 calendars according to the bandwidth
requested in the Device Tree nodes.
It also checks if the total requested bandwidth is within the
specs of the detected Sparx5 models limits.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   2 +-
 .../microchip/sparx5/sparx5_calendar.c        | 595 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |   8 +
 .../ethernet/microchip/sparx5/sparx5_main.h   |   4 +
 4 files changed, 608 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 32e0691e328a..17654ab90ce1 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -7,4 +7,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
 sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
  sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
- sparx5_netdev.o sparx5_port.o sparx5_phylink.o
+ sparx5_netdev.o sparx5_port.o sparx5_phylink.o sparx5_calendar.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
new file mode 100644
index 000000000000..932c897c72a7
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "sparx5_main.h"
+
+/* QSYS calendar information */
+#define SPX5_PORTS_PER_CALREG          10  /* Ports mapped in a calendar register */
+#define SPX5_CALBITS_PER_PORT          3   /* Bit per port in calendar register */
+
+/* DSM calendar information */
+#define SPX5_DSM_CAL_LEN               64
+#define SPX5_DSM_CAL_EMPTY             0xFFFF
+#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
+#define SPX5_DSM_CAL_TAXIS             8
+#define SPX5_DSM_CAL_BW_LOSS           553
+
+#define SPX5_TAXI_PORT_MAX             70
+
+#define SPEED_12500                    12500
+
+/* Maps from taxis to port numbers */
+static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI] = {
+	{57, 12, 0, 1, 2, 16, 17, 18, 19, 20, 21, 22, 23},
+	{58, 13, 3, 4, 5, 24, 25, 26, 27, 28, 29, 30, 31},
+	{59, 14, 6, 7, 8, 32, 33, 34, 35, 36, 37, 38, 39},
+	{60, 15, 9, 10, 11, 40, 41, 42, 43, 44, 45, 46, 47},
+	{61, 48, 49, 50, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{62, 51, 52, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{56, 63, 54, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+	{64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+};
+
+struct sparx5_calendar_data {
+	u32 schedule[SPX5_DSM_CAL_LEN];
+	u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
+	u32 new_slots[SPX5_DSM_CAL_LEN];
+	u32 temp_sched[SPX5_DSM_CAL_LEN];
+	u32 indices[SPX5_DSM_CAL_LEN];
+	u32 short_list[SPX5_DSM_CAL_LEN];
+	u32 long_list[SPX5_DSM_CAL_LEN];
+};
+
+static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
+{
+	switch (sparx5->target_ct) {
+	case SPX5_TARGET_CT_7546:
+	case SPX5_TARGET_CT_7546TSN:
+		return 65000;
+	case SPX5_TARGET_CT_7549:
+	case SPX5_TARGET_CT_7549TSN:
+		return 91000;
+	case SPX5_TARGET_CT_7552:
+	case SPX5_TARGET_CT_7552TSN:
+		return 129000;
+	case SPX5_TARGET_CT_7556:
+	case SPX5_TARGET_CT_7556TSN:
+		return 161000;
+	case SPX5_TARGET_CT_7558:
+	case SPX5_TARGET_CT_7558TSN:
+		return 201000;
+	default:
+		return 0;
+	}
+}
+
+/* This is used in calendar configuration */
+enum sparx5_cal_bw {
+	SPX5_CAL_SPEED_NONE = 0,
+	SPX5_CAL_SPEED_1G   = 1,
+	SPX5_CAL_SPEED_2G5  = 2,
+	SPX5_CAL_SPEED_5G   = 3,
+	SPX5_CAL_SPEED_10G  = 4,
+	SPX5_CAL_SPEED_25G  = 5,
+	SPX5_CAL_SPEED_0G5  = 6,
+	SPX5_CAL_SPEED_12G5 = 7
+};
+
+static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
+{
+	switch (cclock) {
+	case SPX5_CORE_CLOCK_250MHZ: return 83000; /* 250000 / 3 */
+	case SPX5_CORE_CLOCK_500MHZ: return 166000; /* 500000 / 3 */
+	case SPX5_CORE_CLOCK_625MHZ: return  208000; /* 625000 / 3 */
+	default: return 0;
+	}
+	return 0;
+}
+
+static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
+{
+	switch (speed) {
+	case SPX5_CAL_SPEED_1G:   return 1000;
+	case SPX5_CAL_SPEED_2G5:  return 2500;
+	case SPX5_CAL_SPEED_5G:   return 5000;
+	case SPX5_CAL_SPEED_10G:  return 10000;
+	case SPX5_CAL_SPEED_25G:  return 25000;
+	case SPX5_CAL_SPEED_0G5:  return 500;
+	case SPX5_CAL_SPEED_12G5: return 12500;
+	default: return 0;
+	}
+}
+
+static u32 sparx5_bandwidth_to_calendar(u32 bw)
+{
+	switch (bw) {
+	case SPEED_10:      return SPX5_CAL_SPEED_0G5;
+	case SPEED_100:     return SPX5_CAL_SPEED_0G5;
+	case SPEED_1000:    return SPX5_CAL_SPEED_1G;
+	case SPEED_2500:    return SPX5_CAL_SPEED_2G5;
+	case SPEED_5000:    return SPX5_CAL_SPEED_5G;
+	case SPEED_10000:   return SPX5_CAL_SPEED_10G;
+	case SPEED_12500:   return SPX5_CAL_SPEED_12G5;
+	case SPEED_25000:   return SPX5_CAL_SPEED_25G;
+	case SPEED_UNKNOWN: return SPX5_CAL_SPEED_1G;
+	default:            return SPX5_CAL_SPEED_NONE;
+	}
+}
+
+static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
+						    u32 portno)
+{
+	struct sparx5_port *port;
+
+	if (portno >= SPX5_PORTS) {
+		/* Internal ports */
+		if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
+			/* Equals 1.25G */
+			return SPX5_CAL_SPEED_2G5;
+		} else if (portno == SPX5_PORT_VD0) {
+			/* IPMC only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		} else if (portno == SPX5_PORT_VD1) {
+			/* OAM only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		} else if (portno == SPX5_PORT_VD2) {
+			/* IPinIP gets only idle BW */
+			return SPX5_CAL_SPEED_NONE;
+		}
+		/* not in port map */
+		return SPX5_CAL_SPEED_NONE;
+	}
+	/* Front ports - may be used */
+	port = sparx5->ports[portno];
+	if (!port)
+		return SPX5_CAL_SPEED_NONE;
+	return sparx5_bandwidth_to_calendar(port->conf.max_speed);
+}
+
+/* Auto configure the QSYS calendar based on port configuration */
+int sparx5_config_auto_calendar(struct sparx5 *sparx5)
+{
+	u32 cal[7], value, idx, portno;
+	u32 max_core_bw;
+	u32 total_bw = 0, used_port_bw = 0;
+	int err = 0;
+	enum sparx5_cal_bw spd;
+
+	memset(cal, 0, sizeof(cal));
+
+	max_core_bw = sparx5_clk_to_bandwidth(sparx5->coreclock);
+	if (max_core_bw == 0) {
+		dev_err(sparx5->dev, "Core clock not supported");
+		return -EINVAL;
+	}
+
+	/* Setup the calendar with the bandwidth to each port */
+	for (portno = 0; portno < SPX5_PORTS_ALL; portno++) {
+		u64 reg, offset, this_bw;
+
+		spd = sparx5_get_port_cal_speed(sparx5, portno);
+		if (spd == SPX5_CAL_SPEED_NONE)
+			continue;
+
+		this_bw = sparx5_cal_speed_to_value(spd);
+		if (portno < SPX5_PORTS)
+			used_port_bw += this_bw;
+		else
+			/* Internal ports are granted half the value */
+			this_bw = this_bw / 2;
+		total_bw += this_bw;
+		reg = portno;
+		offset = do_div(reg, SPX5_PORTS_PER_CALREG);
+		cal[reg] |= spd << (offset * SPX5_CALBITS_PER_PORT);
+	}
+
+	if (used_port_bw > sparx5_target_bandwidth(sparx5)) {
+		dev_err(sparx5->dev,
+			"Port BW %u above target BW %u\n",
+			used_port_bw, sparx5_target_bandwidth(sparx5));
+		return -EINVAL;
+	}
+
+	if (total_bw > max_core_bw) {
+		dev_err(sparx5->dev,
+			"Total BW %u above switch core BW %u\n",
+			total_bw, max_core_bw);
+		return -EINVAL;
+	}
+
+	/* Halt the calendar while changing it */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
+		 QSYS_CAL_CTRL_CAL_MODE,
+		 sparx5, QSYS_CAL_CTRL);
+
+	/* Assign port bandwidth to auto calendar */
+	for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
+		spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
+
+	/* Increase grant rate of all ports to account for
+	 * core clock ppm deviations
+	 */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE_SET(671), /* 672->671 */
+		 QSYS_CAL_CTRL_CAL_AUTO_GRANT_RATE,
+		 sparx5,
+		 QSYS_CAL_CTRL);
+
+	/* Grant idle usage to VD 0-2 */
+	for (idx = 2; idx < 5; idx++)
+		spx5_wr(HSCH_OUTB_SHARE_ENA_OUTB_SHARE_ENA_SET(12),
+			sparx5,
+			HSCH_OUTB_SHARE_ENA(idx));
+
+	/* Enable Auto mode */
+	spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(8),
+		 QSYS_CAL_CTRL_CAL_MODE,
+		 sparx5, QSYS_CAL_CTRL);
+
+	/* Verify successful calendar config */
+	value = spx5_rd(sparx5, QSYS_CAL_CTRL);
+	if (QSYS_CAL_CTRL_CAL_AUTO_ERROR_GET(value)) {
+		dev_err(sparx5->dev, "QSYS calendar error\n");
+		err = -EINVAL;
+	}
+	return err;
+}
+
+static u32 sparx5_dsm_exb_gcd(u32 a, u32 b)
+{
+	if (b == 0)
+		return a;
+	return sparx5_dsm_exb_gcd(b, a % b);
+}
+
+static u32 sparx5_dsm_cal_len(u32 *cal)
+{
+	u32 idx = 0, len = 0;
+
+	while (idx < SPX5_DSM_CAL_LEN) {
+		if (cal[idx] != SPX5_DSM_CAL_EMPTY)
+			len++;
+		idx++;
+	}
+	return len;
+}
+
+static u32 sparx5_dsm_cp_cal(u32 *sched)
+{
+	u32 idx = 0, tmp;
+
+	while (idx < SPX5_DSM_CAL_LEN) {
+		if (sched[idx] != SPX5_DSM_CAL_EMPTY) {
+			tmp = sched[idx];
+			sched[idx] = SPX5_DSM_CAL_EMPTY;
+			return tmp;
+		}
+		idx++;
+	}
+	return SPX5_DSM_CAL_EMPTY;
+}
+
+static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
+				    struct sparx5_calendar_data *data)
+{
+	bool slow_mode;
+	u32 gcd, idx, sum, min, factor;
+	u32 num_of_slots, slot_spd, empty_slots;
+	u32 taxi_bw, clk_period_ps;
+
+	clk_period_ps = sparx5_clk_period(sparx5->coreclock);
+	taxi_bw = 128 * 1000000 / clk_period_ps;
+	slow_mode = !!(clk_period_ps > 2000);
+	memcpy(data->taxi_ports, &sparx5_taxi_ports[taxi],
+	       sizeof(data->taxi_ports));
+
+	for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
+		data->new_slots[idx] = SPX5_DSM_CAL_EMPTY;
+		data->schedule[idx] = SPX5_DSM_CAL_EMPTY;
+		data->temp_sched[idx] = SPX5_DSM_CAL_EMPTY;
+	}
+	/* Default empty calendar */
+	data->schedule[0] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
+
+	/* Map ports to taxi positions */
+	for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
+		u32 portno = data->taxi_ports[idx];
+
+		if (portno < SPX5_TAXI_PORT_MAX) {
+			data->taxi_speeds[idx] = sparx5_cal_speed_to_value
+				(sparx5_get_port_cal_speed(sparx5, portno));
+		} else {
+			data->taxi_speeds[idx] = 0;
+		}
+	}
+
+	sum = 0;
+	min = 25000;
+	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
+		u32 jdx;
+
+		sum += data->taxi_speeds[idx];
+		if (data->taxi_speeds[idx] && data->taxi_speeds[idx] < min)
+			min = data->taxi_speeds[idx];
+		gcd = min;
+		for (jdx = 0; jdx < ARRAY_SIZE(data->taxi_speeds); jdx++)
+			gcd = sparx5_dsm_exb_gcd(gcd, data->taxi_speeds[jdx]);
+	}
+	if (sum == 0) /* Empty calendar */
+		return 0;
+	/* Make room for overhead traffic */
+	factor = 100 * 100 * 1000 / (100 * 100 - SPX5_DSM_CAL_BW_LOSS);
+
+	if (sum * factor > (taxi_bw * 1000)) {
+		dev_err(sparx5->dev,
+			"Taxi %u, Requested BW %u above available BW %u\n",
+			taxi, sum, taxi_bw);
+		return -EINVAL;
+	}
+	for (idx = 0; idx < 4; idx++) {
+		u32 raw_spd;
+
+		if (idx == 0)
+			raw_spd = gcd / 5;
+		else if (idx == 1)
+			raw_spd = gcd / 2;
+		else if (idx == 2)
+			raw_spd = gcd;
+		else
+			raw_spd = min;
+		slot_spd = raw_spd * factor / 1000;
+		num_of_slots = taxi_bw / slot_spd;
+		if (num_of_slots <= 64)
+			break;
+	}
+
+	num_of_slots = num_of_slots > 64 ? 64 : num_of_slots;
+	slot_spd = taxi_bw / num_of_slots;
+
+	sum = 0;
+	for (idx = 0; idx < ARRAY_SIZE(data->taxi_speeds); idx++) {
+		u32 spd = data->taxi_speeds[idx];
+		u32 adjusted_speed = data->taxi_speeds[idx] * factor / 1000;
+
+		if (adjusted_speed > 0) {
+			data->avg_dist[idx] = (128 * 1000000 * 10) /
+				(adjusted_speed * clk_period_ps);
+		} else {
+			data->avg_dist[idx] = -1;
+		}
+		data->dev_slots[idx] = ((spd * factor / slot_spd) + 999) / 1000;
+		if (spd != 25000 && (spd != 10000 || !slow_mode)) {
+			if (num_of_slots < (5 * data->dev_slots[idx])) {
+				dev_err(sparx5->dev,
+					"Taxi %u, speed %u, Low slot sep.\n",
+					taxi, spd);
+				return -EINVAL;
+			}
+		}
+		sum += data->dev_slots[idx];
+		if (sum > num_of_slots) {
+			dev_err(sparx5->dev,
+				"Taxi %u with overhead factor %u\n",
+				taxi, factor);
+			return -EINVAL;
+		}
+	}
+
+	empty_slots = num_of_slots - sum;
+
+	for (idx = 0; idx < empty_slots; idx++)
+		data->schedule[idx] = SPX5_DSM_CAL_MAX_DEVS_PER_TAXI;
+
+	for (idx = 1; idx < num_of_slots; idx++) {
+		u32 indices_len = 0;
+		u32 slot, jdx, kdx, ts;
+		s32 cnt;
+		u32 num_of_old_slots, num_of_new_slots, tgt_score;
+
+		for (slot = 0; slot < ARRAY_SIZE(data->dev_slots); slot++) {
+			if (data->dev_slots[slot] == idx) {
+				data->indices[indices_len] = slot;
+				indices_len++;
+			}
+		}
+		if (indices_len == 0)
+			continue;
+		kdx = 0;
+		for (slot = 0; slot < idx; slot++) {
+			for (jdx = 0; jdx < indices_len; jdx++, kdx++)
+				data->new_slots[kdx] = data->indices[jdx];
+		}
+
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			if (data->schedule[slot] == SPX5_DSM_CAL_EMPTY)
+				break;
+		}
+
+		num_of_old_slots =  slot;
+		num_of_new_slots =  kdx;
+		cnt = 0;
+		ts = 0;
+
+		if (num_of_new_slots > num_of_old_slots) {
+			memcpy(data->short_list, data->schedule,
+			       sizeof(data->short_list));
+			memcpy(data->long_list, data->new_slots,
+			       sizeof(data->long_list));
+			tgt_score = 100000 * num_of_old_slots /
+				num_of_new_slots;
+		} else {
+			memcpy(data->short_list, data->new_slots,
+			       sizeof(data->short_list));
+			memcpy(data->long_list, data->schedule,
+			       sizeof(data->long_list));
+			tgt_score = 100000 * num_of_new_slots /
+				num_of_old_slots;
+		}
+
+		while (sparx5_dsm_cal_len(data->short_list) > 0 ||
+		       sparx5_dsm_cal_len(data->long_list) > 0) {
+			u32 act = 0;
+
+			if (sparx5_dsm_cal_len(data->short_list) > 0) {
+				data->temp_sched[ts] =
+					sparx5_dsm_cp_cal(data->short_list);
+				ts++;
+				cnt += 100000;
+				act = 1;
+			}
+			while (sparx5_dsm_cal_len(data->long_list) > 0 &&
+			       cnt > 0) {
+				data->temp_sched[ts] =
+					sparx5_dsm_cp_cal(data->long_list);
+				ts++;
+				cnt -= tgt_score;
+				act = 1;
+			}
+			if (act == 0) {
+				dev_err(sparx5->dev,
+					"Error in DSM calendar calculation\n");
+				return -EINVAL;
+			}
+		}
+
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			if (data->temp_sched[slot] == SPX5_DSM_CAL_EMPTY)
+				break;
+		}
+		for (slot = 0; slot < SPX5_DSM_CAL_LEN; slot++) {
+			data->schedule[slot] = data->temp_sched[slot];
+			data->temp_sched[slot] = SPX5_DSM_CAL_EMPTY;
+			data->new_slots[slot] = SPX5_DSM_CAL_EMPTY;
+		}
+	}
+	return 0;
+}
+
+static int sparx5_dsm_calendar_check(struct sparx5 *sparx5,
+				     struct sparx5_calendar_data *data)
+{
+	u32 num_of_slots, idx, port;
+	int cnt, max_dist;
+	u32 slot_indices[SPX5_DSM_CAL_LEN], distances[SPX5_DSM_CAL_LEN];
+	u32 cal_length = sparx5_dsm_cal_len(data->schedule);
+
+	for (port = 0; port < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; port++) {
+		num_of_slots = 0;
+		max_dist = data->avg_dist[port];
+		for (idx = 0; idx < SPX5_DSM_CAL_LEN; idx++) {
+			slot_indices[idx] = SPX5_DSM_CAL_EMPTY;
+			distances[idx] = SPX5_DSM_CAL_EMPTY;
+		}
+
+		for (idx = 0; idx < cal_length; idx++) {
+			if (data->schedule[idx] == port) {
+				slot_indices[num_of_slots] = idx;
+				num_of_slots++;
+			}
+		}
+
+		slot_indices[num_of_slots] = slot_indices[0] + cal_length;
+
+		for (idx = 0; idx < num_of_slots; idx++) {
+			distances[idx] = (slot_indices[idx + 1] -
+					  slot_indices[idx]) * 10;
+		}
+
+		for (idx = 0; idx < num_of_slots; idx++) {
+			u32 jdx, kdx;
+
+			cnt = distances[idx] - max_dist;
+			if (cnt < 0)
+				cnt = -cnt;
+			kdx = 0;
+			for (jdx = (idx + 1) % num_of_slots;
+			     jdx != idx;
+			     jdx = (jdx + 1) % num_of_slots, kdx++) {
+				cnt =  cnt + distances[jdx] - max_dist;
+				if (cnt < 0)
+					cnt = -cnt;
+				if (cnt > max_dist)
+					goto check_err;
+			}
+		}
+	}
+	return 0;
+check_err:
+	dev_err(sparx5->dev,
+		"Port %u: distance %u above limit %d\n",
+		port, cnt, max_dist);
+	return -EINVAL;
+}
+
+static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
+				      struct sparx5_calendar_data *data)
+{
+	u32 idx;
+	u32 cal_len = sparx5_dsm_cal_len(data->schedule), len;
+
+	spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(1),
+		sparx5,
+		DSM_TAXI_CAL_CFG(taxi));
+	for (idx = 0; idx < cal_len; idx++) {
+		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_IDX_SET(idx),
+			 DSM_TAXI_CAL_CFG_CAL_IDX,
+			 sparx5,
+			 DSM_TAXI_CAL_CFG(taxi));
+		spx5_rmw(DSM_TAXI_CAL_CFG_CAL_PGM_VAL_SET(data->schedule[idx]),
+			 DSM_TAXI_CAL_CFG_CAL_PGM_VAL,
+			 sparx5,
+			 DSM_TAXI_CAL_CFG(taxi));
+	}
+	spx5_wr(DSM_TAXI_CAL_CFG_CAL_PGM_ENA_SET(0),
+		sparx5,
+		DSM_TAXI_CAL_CFG(taxi));
+	len = DSM_TAXI_CAL_CFG_CAL_CUR_LEN_GET(spx5_rd(sparx5,
+						       DSM_TAXI_CAL_CFG(taxi)));
+	if (len != cal_len - 1)
+		goto update_err;
+	return 0;
+update_err:
+	dev_err(sparx5->dev, "Incorrect calendar length: %u\n", len);
+	return -EINVAL;
+}
+
+/* Configure the DSM calendar based on port configuration */
+int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
+{
+	int taxi;
+	struct sparx5_calendar_data *data;
+	int err = 0;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) {
+		err = sparx5_dsm_calendar_calc(sparx5, taxi, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar calculation failed\n");
+			goto cal_out;
+		}
+		err = sparx5_dsm_calendar_check(sparx5, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar check failed\n");
+			goto cal_out;
+		}
+		err = sparx5_dsm_calendar_update(sparx5, taxi, data);
+		if (err) {
+			dev_err(sparx5->dev, "DSM calendar update failed\n");
+			goto cal_out;
+		}
+	}
+cal_out:
+	kfree(data);
+	return err;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 30940d9ee916..2b42c72dac72 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -743,6 +743,14 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 			goto cleanup_ports;
 		}
 	}
+	err = sparx5_config_auto_calendar(sparx5);
+	if (err)
+		goto cleanup_ports;
+
+	err = sparx5_config_dsm_calendar(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	/* Init mact_sw struct */
 	mutex_init(&sparx5->mact_lock);
 	INIT_LIST_HEAD(&sparx5->mact_entries);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 49657f9edbf3..e59e1962f7d4 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -195,6 +195,10 @@ int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
 int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
 void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
 
+/* sparx5_calendar.c */
+int sparx5_config_auto_calendar(struct sparx5 *sparx5);
+int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 7/8] net: sparx5: add ethtool configuration and statistics support
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This adds statistic counters for the network interfaces provided
by the driver.  It also adds CPU port counters (which are not
exposed by ethtool).
This also adds support for configuring the network interface
parameters via ethtool: speed, duplex, aneg etc.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   3 +-
 .../microchip/sparx5/sparx5_ethtool.c         | 979 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |   5 +
 .../ethernet/microchip/sparx5/sparx5_main.h   |  17 +-
 .../ethernet/microchip/sparx5/sparx5_netdev.c |   2 +
 5 files changed, 1001 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 17654ab90ce1..de3155cd582c 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
 sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
  sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
- sparx5_netdev.o sparx5_port.o sparx5_phylink.o sparx5_calendar.o
+ sparx5_ethtool.o sparx5_netdev.o sparx5_port.o \
+ sparx5_phylink.o sparx5_calendar.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
new file mode 100644
index 000000000000..f72f2126fea2
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
@@ -0,0 +1,979 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/ethtool.h>
+
+#include "sparx5_main.h"
+#include "sparx5_port.h"
+
+/* Add a potentially wrapping 32 bit value to a 64 bit counter */
+static void sparx5_update_counter(u64 *cnt, u32 val)
+{
+	if (val < (*cnt & U32_MAX))
+		*cnt += (u64)1 << 32; /* value has wrapped */
+
+	*cnt = (*cnt & ~(u64)U32_MAX) + val;
+}
+
+/* Get a set of Queue System statistics */
+static void sparx5_xqs_prio_stats(struct sparx5 *sparx5,
+				  u32 addr,
+				  u64 *stats)
+{
+	int idx;
+
+	for (idx = 0; idx < 2 * SPX5_PRIOS; ++idx, ++addr, ++stats)
+		sparx5_update_counter(stats, spx5_rd(sparx5, XQS_CNT(addr)));
+}
+
+#define SPX5_STAT_GET(sname)        portstats[spx5_stats_##sname]
+#define SPX5_STAT_SUM(sname)        (portstats[spx5_stats_##sname] + \
+				     portstats[spx5_stats_pmac_##sname])
+#define SPX5_STAT_XQS_PRIOS_COUNTER_SUM(sname)    \
+	(portstats[spx5_stats_green_p0_##sname] + \
+	portstats[spx5_stats_green_p1_##sname] +  \
+	portstats[spx5_stats_green_p2_##sname] +  \
+	portstats[spx5_stats_green_p3_##sname] +  \
+	portstats[spx5_stats_green_p4_##sname] +  \
+	portstats[spx5_stats_green_p5_##sname] +  \
+	portstats[spx5_stats_green_p6_##sname] +  \
+	portstats[spx5_stats_green_p7_##sname] +  \
+	portstats[spx5_stats_yellow_p0_##sname] + \
+	portstats[spx5_stats_yellow_p1_##sname] + \
+	portstats[spx5_stats_yellow_p2_##sname] + \
+	portstats[spx5_stats_yellow_p3_##sname] + \
+	portstats[spx5_stats_yellow_p4_##sname] + \
+	portstats[spx5_stats_yellow_p5_##sname] + \
+	portstats[spx5_stats_yellow_p6_##sname] + \
+	portstats[spx5_stats_yellow_p7_##sname])
+
+enum sparx5_stats_entry {
+	spx5_stats_rx_in_bytes,
+	spx5_stats_rx_symbol_err,
+	spx5_stats_rx_pause,
+	spx5_stats_rx_unsup_opcode,
+	spx5_stats_rx_ok_bytes,
+	spx5_stats_rx_bad_bytes,
+	spx5_stats_rx_unicast,
+	spx5_stats_rx_multicast,
+	spx5_stats_rx_broadcast,
+	spx5_stats_rx_crc_err,
+	spx5_stats_rx_undersize,
+	spx5_stats_rx_fragments,
+	spx5_stats_rx_inrangelen_err,
+	spx5_stats_rx_outofrangelen_err,
+	spx5_stats_rx_oversize,
+	spx5_stats_rx_jabbers,
+	spx5_stats_rx_size64,
+	spx5_stats_rx_size65_127,
+	spx5_stats_rx_size128_255,
+	spx5_stats_rx_size256_511,
+	spx5_stats_rx_size512_1023,
+	spx5_stats_rx_size1024_1518,
+	spx5_stats_rx_size1519_max,
+	spx5_stats_pmac_rx_symbol_err,
+	spx5_stats_pmac_rx_pause,
+	spx5_stats_pmac_rx_unsup_opcode,
+	spx5_stats_pmac_rx_ok_bytes,
+	spx5_stats_pmac_rx_bad_bytes,
+	spx5_stats_pmac_rx_unicast,
+	spx5_stats_pmac_rx_multicast,
+	spx5_stats_pmac_rx_broadcast,
+	spx5_stats_pmac_rx_crc_err,
+	spx5_stats_pmac_rx_undersize,
+	spx5_stats_pmac_rx_fragments,
+	spx5_stats_pmac_rx_inrangelen_err,
+	spx5_stats_pmac_rx_outofrangelen_err,
+	spx5_stats_pmac_rx_oversize,
+	spx5_stats_pmac_rx_jabbers,
+	spx5_stats_pmac_rx_size64,
+	spx5_stats_pmac_rx_size65_127,
+	spx5_stats_pmac_rx_size128_255,
+	spx5_stats_pmac_rx_size256_511,
+	spx5_stats_pmac_rx_size512_1023,
+	spx5_stats_pmac_rx_size1024_1518,
+	spx5_stats_pmac_rx_size1519_max,
+	spx5_stats_green_p0_rx_fwd,
+	spx5_stats_green_p1_rx_fwd,
+	spx5_stats_green_p2_rx_fwd,
+	spx5_stats_green_p3_rx_fwd,
+	spx5_stats_green_p4_rx_fwd,
+	spx5_stats_green_p5_rx_fwd,
+	spx5_stats_green_p6_rx_fwd,
+	spx5_stats_green_p7_rx_fwd,
+	spx5_stats_yellow_p0_rx_fwd,
+	spx5_stats_yellow_p1_rx_fwd,
+	spx5_stats_yellow_p2_rx_fwd,
+	spx5_stats_yellow_p3_rx_fwd,
+	spx5_stats_yellow_p4_rx_fwd,
+	spx5_stats_yellow_p5_rx_fwd,
+	spx5_stats_yellow_p6_rx_fwd,
+	spx5_stats_yellow_p7_rx_fwd,
+	spx5_stats_green_p0_rx_port_drop,
+	spx5_stats_green_p1_rx_port_drop,
+	spx5_stats_green_p2_rx_port_drop,
+	spx5_stats_green_p3_rx_port_drop,
+	spx5_stats_green_p4_rx_port_drop,
+	spx5_stats_green_p5_rx_port_drop,
+	spx5_stats_green_p6_rx_port_drop,
+	spx5_stats_green_p7_rx_port_drop,
+	spx5_stats_yellow_p0_rx_port_drop,
+	spx5_stats_yellow_p1_rx_port_drop,
+	spx5_stats_yellow_p2_rx_port_drop,
+	spx5_stats_yellow_p3_rx_port_drop,
+	spx5_stats_yellow_p4_rx_port_drop,
+	spx5_stats_yellow_p5_rx_port_drop,
+	spx5_stats_yellow_p6_rx_port_drop,
+	spx5_stats_yellow_p7_rx_port_drop,
+	spx5_stats_rx_local_drop,
+	spx5_stats_rx_port_policer_drop,
+	spx5_stats_tx_out_bytes,
+	spx5_stats_tx_pause,
+	spx5_stats_tx_ok_bytes,
+	spx5_stats_tx_unicast,
+	spx5_stats_tx_multicast,
+	spx5_stats_tx_broadcast,
+	spx5_stats_tx_size64,
+	spx5_stats_tx_size65_127,
+	spx5_stats_tx_size128_255,
+	spx5_stats_tx_size256_511,
+	spx5_stats_tx_size512_1023,
+	spx5_stats_tx_size1024_1518,
+	spx5_stats_tx_size1519_max,
+	spx5_stats_tx_multi_coll,
+	spx5_stats_tx_late_coll,
+	spx5_stats_tx_xcoll,
+	spx5_stats_tx_defer,
+	spx5_stats_tx_xdefer,
+	spx5_stats_tx_backoff1,
+	spx5_stats_pmac_tx_pause,
+	spx5_stats_pmac_tx_ok_bytes,
+	spx5_stats_pmac_tx_unicast,
+	spx5_stats_pmac_tx_multicast,
+	spx5_stats_pmac_tx_broadcast,
+	spx5_stats_pmac_tx_size64,
+	spx5_stats_pmac_tx_size65_127,
+	spx5_stats_pmac_tx_size128_255,
+	spx5_stats_pmac_tx_size256_511,
+	spx5_stats_pmac_tx_size512_1023,
+	spx5_stats_pmac_tx_size1024_1518,
+	spx5_stats_pmac_tx_size1519_max,
+	spx5_stats_green_p0_tx_port,
+	spx5_stats_green_p1_tx_port,
+	spx5_stats_green_p2_tx_port,
+	spx5_stats_green_p3_tx_port,
+	spx5_stats_green_p4_tx_port,
+	spx5_stats_green_p5_tx_port,
+	spx5_stats_green_p6_tx_port,
+	spx5_stats_green_p7_tx_port,
+	spx5_stats_yellow_p0_tx_port,
+	spx5_stats_yellow_p1_tx_port,
+	spx5_stats_yellow_p2_tx_port,
+	spx5_stats_yellow_p3_tx_port,
+	spx5_stats_yellow_p4_tx_port,
+	spx5_stats_yellow_p5_tx_port,
+	spx5_stats_yellow_p6_tx_port,
+	spx5_stats_yellow_p7_tx_port,
+	spx5_stats_tx_local_drop,
+};
+
+static const char *const sparx5_stats_layout[] = {
+	"rx_in_bytes",
+	"rx_symbol_err",
+	"rx_pause",
+	"rx_unsup_opcode",
+	"rx_ok_bytes",
+	"rx_bad_bytes",
+	"rx_unicast",
+	"rx_multicast",
+	"rx_broadcast",
+	"rx_crc_err",
+	"rx_undersize",
+	"rx_fragments",
+	"rx_inrangelen_err",
+	"rx_outofrangelen_err",
+	"rx_oversize",
+	"rx_jabbers",
+	"rx_size64",
+	"rx_size65_127",
+	"rx_size128_255",
+	"rx_size256_511",
+	"rx_size512_1023",
+	"rx_size1024_1518",
+	"rx_size1519_max",
+	"pmac_rx_symbol_err",
+	"pmac_rx_pause",
+	"pmac_rx_unsup_opcode",
+	"pmac_rx_ok_bytes",
+	"pmac_rx_bad_bytes",
+	"pmac_rx_unicast",
+	"pmac_rx_multicast",
+	"pmac_rx_broadcast",
+	"pmac_rx_crc_err",
+	"pmac_rx_undersize",
+	"pmac_rx_fragments",
+	"pmac_rx_inrangelen_err",
+	"pmac_rx_outofrangelen_err",
+	"pmac_rx_oversize",
+	"pmac_rx_jabbers",
+	"pmac_rx_size64",
+	"pmac_rx_size65_127",
+	"pmac_rx_size128_255",
+	"pmac_rx_size256_511",
+	"pmac_rx_size512_1023",
+	"pmac_rx_size1024_1518",
+	"pmac_rx_size1519_max",
+	"rx_fwd_green_p0_q",
+	"rx_fwd_green_p1_q",
+	"rx_fwd_green_p2_q",
+	"rx_fwd_green_p3_q",
+	"rx_fwd_green_p4_q",
+	"rx_fwd_green_p5_q",
+	"rx_fwd_green_p6_q",
+	"rx_fwd_green_p7_q",
+	"rx_fwd_yellow_p0_q",
+	"rx_fwd_yellow_p1_q",
+	"rx_fwd_yellow_p2_q",
+	"rx_fwd_yellow_p3_q",
+	"rx_fwd_yellow_p4_q",
+	"rx_fwd_yellow_p5_q",
+	"rx_fwd_yellow_p6_q",
+	"rx_fwd_yellow_p7_q",
+	"rx_port_drop_green_p0_q",
+	"rx_port_drop_green_p1_q",
+	"rx_port_drop_green_p2_q",
+	"rx_port_drop_green_p3_q",
+	"rx_port_drop_green_p4_q",
+	"rx_port_drop_green_p5_q",
+	"rx_port_drop_green_p6_q",
+	"rx_port_drop_green_p7_q",
+	"rx_port_drop_yellow_p0_q",
+	"rx_port_drop_yellow_p1_q",
+	"rx_port_drop_yellow_p2_q",
+	"rx_port_drop_yellow_p3_q",
+	"rx_port_drop_yellow_p4_q",
+	"rx_port_drop_yellow_p5_q",
+	"rx_port_drop_yellow_p6_q",
+	"rx_port_drop_yellow_p7_q",
+	"rx_local_drop",
+	"rx_port_policer_drop",
+	"tx_out_bytes",
+	"tx_pause",
+	"tx_ok_bytes",
+	"tx_unicast",
+	"tx_multicast",
+	"tx_broadcast",
+	"tx_size64",
+	"tx_size65_127",
+	"tx_size128_255",
+	"tx_size256_511",
+	"tx_size512_1023",
+	"tx_size1024_1518",
+	"tx_size1519_max",
+	"tx_multi_coll",
+	"tx_late_coll",
+	"tx_xcoll",
+	"tx_defer",
+	"tx_xdefer",
+	"tx_backoff1",
+	"pmac_tx_pause",
+	"pmac_tx_ok_bytes",
+	"pmac_tx_unicast",
+	"pmac_tx_multicast",
+	"pmac_tx_broadcast",
+	"pmac_tx_size64",
+	"pmac_tx_size65_127",
+	"pmac_tx_size128_255",
+	"pmac_tx_size256_511",
+	"pmac_tx_size512_1023",
+	"pmac_tx_size1024_1518",
+	"pmac_tx_size1519_max",
+	"tx_port_green_p0_q",
+	"tx_port_green_p1_q",
+	"tx_port_green_p2_q",
+	"tx_port_green_p3_q",
+	"tx_port_green_p4_q",
+	"tx_port_green_p5_q",
+	"tx_port_green_p6_q",
+	"tx_port_green_p7_q",
+	"tx_port_yellow_p0_q",
+	"tx_port_yellow_p1_q",
+	"tx_port_yellow_p2_q",
+	"tx_port_yellow_p3_q",
+	"tx_port_yellow_p4_q",
+	"tx_port_yellow_p5_q",
+	"tx_port_yellow_p6_q",
+	"tx_port_yellow_p7_q",
+	"tx_local_drop",
+};
+
+/* Device Statistics */
+static void sparx5_get_device_stats(struct sparx5 *sparx5, int portno)
+{
+	u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+	u32 dev = sparx5_to_high_dev(portno);
+	u32 tinst = sparx5_port_dev_index(portno);
+	void __iomem *inst = spx5_inst_get(sparx5, dev, tinst);
+
+	sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_IN_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SYMBOL_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_BAD_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_CRC_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_UNDERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_FRAGMENTS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OVERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_JABBERS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SYMBOL_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_BAD_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_CRC_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNDERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_FRAGMENTS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OVERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_JABBERS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_xqs_prio_stats(sparx5,
+			      0,
+			      &portstats[spx5_stats_green_p0_rx_fwd]);
+	sparx5_xqs_prio_stats(sparx5,
+			      16,
+			      &portstats[spx5_stats_green_p0_rx_port_drop]);
+	sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(32)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+			      spx5_rd(sparx5,
+				      ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_OUT_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_xqs_prio_stats(sparx5,
+			      256,
+			      &portstats[spx5_stats_green_p0_tx_port]);
+	sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+/* ASM Statistics */
+static void sparx5_get_asm_stats(struct sparx5 *sparx5, int portno)
+{
+	u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+	u32 dev = TARGET_ASM;
+	void __iomem *inst = spx5_inst_get(sparx5, dev, 0);
+
+	sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_IN_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SYMBOL_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_RX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_BAD_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_CRC_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+			      spx5_inst_rd(inst,
+					   ASM_RX_UNDERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+			      spx5_inst_rd(inst,
+					   ASM_RX_FRAGMENTS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OVERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   ASM_RX_JABBERS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SYMBOL_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_BAD_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_CRC_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNDERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_FRAGMENTS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OVERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_JABBERS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_xqs_prio_stats(sparx5,
+			      0,
+			      &portstats[spx5_stats_green_p0_rx_fwd]);
+	sparx5_xqs_prio_stats(sparx5,
+			      16,
+			      &portstats[spx5_stats_green_p0_rx_port_drop]);
+	sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(32)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+			      spx5_rd(sparx5,
+				      ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_TX_OUT_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_TX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_TX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multi_coll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_MULTI_COLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_late_coll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_LATE_COLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_xcoll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_XCOLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_defer],
+			      spx5_inst_rd(inst,
+					   ASM_TX_DEFER_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_xdefer],
+			      spx5_inst_rd(inst,
+					   ASM_TX_XDEFER_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_backoff1],
+			      spx5_inst_rd(inst,
+					   ASM_TX_BACKOFF1_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_xqs_prio_stats(sparx5,
+			      256,
+			      &portstats[spx5_stats_green_p0_tx_port]);
+	sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+static void sparx5_update_port_stats(struct sparx5 *sparx5, int portno)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(&sparx5->ports[portno]->conf);
+
+	/* Set XQS port number */
+	spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno), sparx5, XQS_STAT_CFG);
+	if (high_speed_dev)
+		sparx5_get_device_stats(sparx5, portno);
+	else
+		sparx5_get_asm_stats(sparx5, portno);
+}
+
+static void sparx5_update_stats(struct sparx5 *sparx5)
+{
+	int idx;
+
+	mutex_lock(&sparx5->stats_lock);
+	for (idx = 0; idx < SPX5_PORTS; idx++)
+		if (sparx5->ports[idx])
+			sparx5_update_port_stats(sparx5, idx);
+	mutex_unlock(&sparx5->stats_lock);
+}
+
+static void sparx5_check_stats_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct sparx5 *sparx5 = container_of(dwork, struct sparx5,
+					     stats_work);
+
+	sparx5_update_stats(sparx5);
+
+	queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+			   SPX5_STATS_CHECK_DELAY);
+}
+
+static int sparx5_get_sset_count(struct net_device *ndev, int sset)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+
+	if (sset != ETH_SS_STATS)
+		return -EOPNOTSUPP;
+	return sparx5->num_stats;
+}
+
+static void sparx5_get_sset_strings(struct net_device *ndev, u32 sset, u8 *data)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+	int idx;
+
+	if (sset != ETH_SS_STATS)
+		return;
+
+	for (idx = 0; idx < sparx5->num_stats; idx++)
+		strncpy(data + idx * ETH_GSTRING_LEN,
+			sparx5->stats_layout[idx], ETH_GSTRING_LEN);
+}
+
+static void sparx5_get_stats(struct net_device *ndev,
+			     struct ethtool_stats *stats, u64 *data)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+	u64 *portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+	int idx;
+
+	/* check and update now (all ports) */
+	sparx5_update_stats(sparx5);
+
+	/* Copy port counters to the ethtool buffer */
+	for (idx = 0; idx < sparx5->num_stats; idx++)
+		*data++ = portstats[idx];
+}
+
+void sparx5_get_stats64(struct net_device *ndev,
+			struct rtnl_link_stats64 *stats)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5 *sparx5 = port->sparx5;
+	u64 *portstats;
+
+	if (!sparx5->stats)
+		return;		/* Not initialized yet */
+
+	portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+
+	mutex_lock(&sparx5->stats_lock);
+
+	stats->rx_errors = SPX5_STAT_SUM(rx_crc_err) +
+		SPX5_STAT_SUM(rx_undersize) + SPX5_STAT_SUM(rx_oversize) +
+		SPX5_STAT_SUM(rx_outofrangelen_err) +
+		SPX5_STAT_SUM(rx_symbol_err) + SPX5_STAT_SUM(rx_jabbers) +
+		SPX5_STAT_SUM(rx_fragments);
+
+	stats->rx_bytes = SPX5_STAT_SUM(rx_ok_bytes) +
+		SPX5_STAT_SUM(rx_bad_bytes);
+
+	stats->rx_packets = SPX5_STAT_SUM(rx_unicast) +
+		SPX5_STAT_SUM(rx_multicast) + SPX5_STAT_SUM(rx_broadcast) +
+		stats->rx_errors;
+
+	stats->multicast = SPX5_STAT_SUM(rx_unicast);
+
+	stats->rx_dropped = SPX5_STAT_GET(rx_port_policer_drop) +
+		SPX5_STAT_XQS_PRIOS_COUNTER_SUM(rx_port_drop);
+
+	/* Get Tx stats */
+	stats->tx_bytes = SPX5_STAT_SUM(tx_ok_bytes);
+
+	stats->tx_packets = SPX5_STAT_SUM(tx_unicast) +
+		SPX5_STAT_SUM(tx_multicast) + SPX5_STAT_SUM(tx_broadcast);
+
+	stats->tx_dropped = SPX5_STAT_GET(tx_local_drop);
+
+	stats->collisions = SPX5_STAT_GET(tx_multi_coll) +
+		SPX5_STAT_GET(tx_late_coll) +
+		SPX5_STAT_GET(tx_xcoll) +
+		SPX5_STAT_GET(tx_backoff1);
+
+	mutex_unlock(&sparx5->stats_lock);
+}
+
+static int sparx5_get_link_settings(struct net_device *ndev,
+				    struct ethtool_link_ksettings *cmd)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+
+	return phylink_ethtool_ksettings_get(port->phylink, cmd);
+}
+
+static int sparx5_set_link_settings(struct net_device *ndev,
+				    const struct ethtool_link_ksettings *cmd)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+
+	return phylink_ethtool_ksettings_set(port->phylink, cmd);
+}
+
+const struct ethtool_ops sparx5_ethtool_ops = {
+	.get_sset_count         = sparx5_get_sset_count,
+	.get_strings            = sparx5_get_sset_strings,
+	.get_ethtool_stats      = sparx5_get_stats,
+	.get_link_ksettings	= sparx5_get_link_settings,
+	.set_link_ksettings	= sparx5_set_link_settings,
+	.get_link               = ethtool_op_get_link,
+};
+
+int sparx_stats_init(struct sparx5 *sparx5)
+{
+	char queue_name[32];
+	int portno;
+
+	sparx5->stats_layout = sparx5_stats_layout;
+	sparx5->num_stats = ARRAY_SIZE(sparx5_stats_layout);
+	sparx5->stats = devm_kcalloc(sparx5->dev,
+				     SPX5_PORTS_ALL * sparx5->num_stats,
+				     sizeof(u64), GFP_KERNEL);
+	if (!sparx5->stats)
+		return -ENOMEM;
+
+	for (portno = 0; portno < SPX5_PORTS; portno++)
+		if (sparx5->ports[portno]) {
+			/* Clear Queue System counters */
+			spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno) |
+				XQS_STAT_CFG_STAT_CLEAR_SHOT_SET(3), sparx5, XQS_STAT_CFG);
+		}
+
+	mutex_init(&sparx5->stats_lock);
+	snprintf(queue_name, sizeof(queue_name), "%s-stats",
+		 dev_name(sparx5->dev));
+	sparx5->stats_queue = create_singlethread_workqueue(queue_name);
+	INIT_DELAYED_WORK(&sparx5->stats_work, sparx5_check_stats_work);
+	queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+			   SPX5_STATS_CHECK_DELAY);
+
+	return 0;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 2b42c72dac72..e1e2384512bc 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -751,6 +751,11 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 	if (err)
 		goto cleanup_ports;
 
+	/* Init stats */
+	err = sparx_stats_init(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	/* Init mact_sw struct */
 	mutex_init(&sparx5->mact_lock);
 	INIT_LIST_HEAD(&sparx5->mact_entries);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index e59e1962f7d4..1d1b3b516244 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -130,6 +130,14 @@ struct sparx5 {
 	/* port structures are in net device */
 	struct sparx5_port                    *ports[SPX5_PORTS];
 	enum sparx5_core_clockfreq            coreclock;
+	/* Statistics */
+	u32                                   num_stats;
+	const char * const                    *stats_layout;
+	u64                                   *stats;
+	/* Workqueue for reading stats */
+	struct mutex                          stats_lock;
+	struct delayed_work                   stats_work;
+	struct workqueue_struct               *stats_queue;
 	/* Notifiers */
 	struct notifier_block                 netdevice_nb;
 	struct notifier_block                 switchdev_nb;
@@ -153,10 +161,6 @@ struct sparx5 {
 	bool                                  sd_sgpio_remapping;
 };
 
-/* sparx5_main.c */
-void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
-bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
-
 /* sparx5_switchdev.c */
 int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
 void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
@@ -199,6 +203,10 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
 int sparx5_config_auto_calendar(struct sparx5 *sparx5);
 int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
 
+/* sparx5_ethtool.c */
+void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats);
+int sparx_stats_init(struct sparx5 *sparx5);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
@@ -226,6 +234,7 @@ static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 }
 
 extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
+extern const struct ethtool_ops sparx5_ethtool_ops;
 
 /* Calculate raw offset */
 static inline __pure int spx5_offset(int id, int tinst, int tcnt,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index a0992443ab9f..3b7cfbb14f2e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -176,6 +176,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = {
 	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
 	.ndo_set_mac_address    = sparx5_set_mac_address,
 	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_get_stats64        = sparx5_get_stats64,
 	.ndo_get_port_parent_id = sparx5_get_port_parent_id,
 };
 
@@ -203,6 +204,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
 
 	ether_setup(ndev);
 	ndev->netdev_ops = &sparx5_port_netdev_ops;
+	ndev->ethtool_ops = &sparx5_ethtool_ops;
 	ndev->features |= NETIF_F_LLTX; /* software tx */
 
 	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
-- 
2.29.2


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

* [RFC PATCH v2 7/8] net: sparx5: add ethtool configuration and statistics support
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This adds statistic counters for the network interfaces provided
by the driver.  It also adds CPU port counters (which are not
exposed by ethtool).
This also adds support for configuring the network interface
parameters via ethtool: speed, duplex, aneg etc.

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonasson@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   3 +-
 .../microchip/sparx5/sparx5_ethtool.c         | 979 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |   5 +
 .../ethernet/microchip/sparx5/sparx5_main.h   |  17 +-
 .../ethernet/microchip/sparx5/sparx5_netdev.c |   2 +
 5 files changed, 1001 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile
index 17654ab90ce1..de3155cd582c 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
 sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
  sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
- sparx5_netdev.o sparx5_port.o sparx5_phylink.o sparx5_calendar.o
+ sparx5_ethtool.o sparx5_netdev.o sparx5_port.o \
+ sparx5_phylink.o sparx5_calendar.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
new file mode 100644
index 000000000000..f72f2126fea2
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
@@ -0,0 +1,979 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/ethtool.h>
+
+#include "sparx5_main.h"
+#include "sparx5_port.h"
+
+/* Add a potentially wrapping 32 bit value to a 64 bit counter */
+static void sparx5_update_counter(u64 *cnt, u32 val)
+{
+	if (val < (*cnt & U32_MAX))
+		*cnt += (u64)1 << 32; /* value has wrapped */
+
+	*cnt = (*cnt & ~(u64)U32_MAX) + val;
+}
+
+/* Get a set of Queue System statistics */
+static void sparx5_xqs_prio_stats(struct sparx5 *sparx5,
+				  u32 addr,
+				  u64 *stats)
+{
+	int idx;
+
+	for (idx = 0; idx < 2 * SPX5_PRIOS; ++idx, ++addr, ++stats)
+		sparx5_update_counter(stats, spx5_rd(sparx5, XQS_CNT(addr)));
+}
+
+#define SPX5_STAT_GET(sname)        portstats[spx5_stats_##sname]
+#define SPX5_STAT_SUM(sname)        (portstats[spx5_stats_##sname] + \
+				     portstats[spx5_stats_pmac_##sname])
+#define SPX5_STAT_XQS_PRIOS_COUNTER_SUM(sname)    \
+	(portstats[spx5_stats_green_p0_##sname] + \
+	portstats[spx5_stats_green_p1_##sname] +  \
+	portstats[spx5_stats_green_p2_##sname] +  \
+	portstats[spx5_stats_green_p3_##sname] +  \
+	portstats[spx5_stats_green_p4_##sname] +  \
+	portstats[spx5_stats_green_p5_##sname] +  \
+	portstats[spx5_stats_green_p6_##sname] +  \
+	portstats[spx5_stats_green_p7_##sname] +  \
+	portstats[spx5_stats_yellow_p0_##sname] + \
+	portstats[spx5_stats_yellow_p1_##sname] + \
+	portstats[spx5_stats_yellow_p2_##sname] + \
+	portstats[spx5_stats_yellow_p3_##sname] + \
+	portstats[spx5_stats_yellow_p4_##sname] + \
+	portstats[spx5_stats_yellow_p5_##sname] + \
+	portstats[spx5_stats_yellow_p6_##sname] + \
+	portstats[spx5_stats_yellow_p7_##sname])
+
+enum sparx5_stats_entry {
+	spx5_stats_rx_in_bytes,
+	spx5_stats_rx_symbol_err,
+	spx5_stats_rx_pause,
+	spx5_stats_rx_unsup_opcode,
+	spx5_stats_rx_ok_bytes,
+	spx5_stats_rx_bad_bytes,
+	spx5_stats_rx_unicast,
+	spx5_stats_rx_multicast,
+	spx5_stats_rx_broadcast,
+	spx5_stats_rx_crc_err,
+	spx5_stats_rx_undersize,
+	spx5_stats_rx_fragments,
+	spx5_stats_rx_inrangelen_err,
+	spx5_stats_rx_outofrangelen_err,
+	spx5_stats_rx_oversize,
+	spx5_stats_rx_jabbers,
+	spx5_stats_rx_size64,
+	spx5_stats_rx_size65_127,
+	spx5_stats_rx_size128_255,
+	spx5_stats_rx_size256_511,
+	spx5_stats_rx_size512_1023,
+	spx5_stats_rx_size1024_1518,
+	spx5_stats_rx_size1519_max,
+	spx5_stats_pmac_rx_symbol_err,
+	spx5_stats_pmac_rx_pause,
+	spx5_stats_pmac_rx_unsup_opcode,
+	spx5_stats_pmac_rx_ok_bytes,
+	spx5_stats_pmac_rx_bad_bytes,
+	spx5_stats_pmac_rx_unicast,
+	spx5_stats_pmac_rx_multicast,
+	spx5_stats_pmac_rx_broadcast,
+	spx5_stats_pmac_rx_crc_err,
+	spx5_stats_pmac_rx_undersize,
+	spx5_stats_pmac_rx_fragments,
+	spx5_stats_pmac_rx_inrangelen_err,
+	spx5_stats_pmac_rx_outofrangelen_err,
+	spx5_stats_pmac_rx_oversize,
+	spx5_stats_pmac_rx_jabbers,
+	spx5_stats_pmac_rx_size64,
+	spx5_stats_pmac_rx_size65_127,
+	spx5_stats_pmac_rx_size128_255,
+	spx5_stats_pmac_rx_size256_511,
+	spx5_stats_pmac_rx_size512_1023,
+	spx5_stats_pmac_rx_size1024_1518,
+	spx5_stats_pmac_rx_size1519_max,
+	spx5_stats_green_p0_rx_fwd,
+	spx5_stats_green_p1_rx_fwd,
+	spx5_stats_green_p2_rx_fwd,
+	spx5_stats_green_p3_rx_fwd,
+	spx5_stats_green_p4_rx_fwd,
+	spx5_stats_green_p5_rx_fwd,
+	spx5_stats_green_p6_rx_fwd,
+	spx5_stats_green_p7_rx_fwd,
+	spx5_stats_yellow_p0_rx_fwd,
+	spx5_stats_yellow_p1_rx_fwd,
+	spx5_stats_yellow_p2_rx_fwd,
+	spx5_stats_yellow_p3_rx_fwd,
+	spx5_stats_yellow_p4_rx_fwd,
+	spx5_stats_yellow_p5_rx_fwd,
+	spx5_stats_yellow_p6_rx_fwd,
+	spx5_stats_yellow_p7_rx_fwd,
+	spx5_stats_green_p0_rx_port_drop,
+	spx5_stats_green_p1_rx_port_drop,
+	spx5_stats_green_p2_rx_port_drop,
+	spx5_stats_green_p3_rx_port_drop,
+	spx5_stats_green_p4_rx_port_drop,
+	spx5_stats_green_p5_rx_port_drop,
+	spx5_stats_green_p6_rx_port_drop,
+	spx5_stats_green_p7_rx_port_drop,
+	spx5_stats_yellow_p0_rx_port_drop,
+	spx5_stats_yellow_p1_rx_port_drop,
+	spx5_stats_yellow_p2_rx_port_drop,
+	spx5_stats_yellow_p3_rx_port_drop,
+	spx5_stats_yellow_p4_rx_port_drop,
+	spx5_stats_yellow_p5_rx_port_drop,
+	spx5_stats_yellow_p6_rx_port_drop,
+	spx5_stats_yellow_p7_rx_port_drop,
+	spx5_stats_rx_local_drop,
+	spx5_stats_rx_port_policer_drop,
+	spx5_stats_tx_out_bytes,
+	spx5_stats_tx_pause,
+	spx5_stats_tx_ok_bytes,
+	spx5_stats_tx_unicast,
+	spx5_stats_tx_multicast,
+	spx5_stats_tx_broadcast,
+	spx5_stats_tx_size64,
+	spx5_stats_tx_size65_127,
+	spx5_stats_tx_size128_255,
+	spx5_stats_tx_size256_511,
+	spx5_stats_tx_size512_1023,
+	spx5_stats_tx_size1024_1518,
+	spx5_stats_tx_size1519_max,
+	spx5_stats_tx_multi_coll,
+	spx5_stats_tx_late_coll,
+	spx5_stats_tx_xcoll,
+	spx5_stats_tx_defer,
+	spx5_stats_tx_xdefer,
+	spx5_stats_tx_backoff1,
+	spx5_stats_pmac_tx_pause,
+	spx5_stats_pmac_tx_ok_bytes,
+	spx5_stats_pmac_tx_unicast,
+	spx5_stats_pmac_tx_multicast,
+	spx5_stats_pmac_tx_broadcast,
+	spx5_stats_pmac_tx_size64,
+	spx5_stats_pmac_tx_size65_127,
+	spx5_stats_pmac_tx_size128_255,
+	spx5_stats_pmac_tx_size256_511,
+	spx5_stats_pmac_tx_size512_1023,
+	spx5_stats_pmac_tx_size1024_1518,
+	spx5_stats_pmac_tx_size1519_max,
+	spx5_stats_green_p0_tx_port,
+	spx5_stats_green_p1_tx_port,
+	spx5_stats_green_p2_tx_port,
+	spx5_stats_green_p3_tx_port,
+	spx5_stats_green_p4_tx_port,
+	spx5_stats_green_p5_tx_port,
+	spx5_stats_green_p6_tx_port,
+	spx5_stats_green_p7_tx_port,
+	spx5_stats_yellow_p0_tx_port,
+	spx5_stats_yellow_p1_tx_port,
+	spx5_stats_yellow_p2_tx_port,
+	spx5_stats_yellow_p3_tx_port,
+	spx5_stats_yellow_p4_tx_port,
+	spx5_stats_yellow_p5_tx_port,
+	spx5_stats_yellow_p6_tx_port,
+	spx5_stats_yellow_p7_tx_port,
+	spx5_stats_tx_local_drop,
+};
+
+static const char *const sparx5_stats_layout[] = {
+	"rx_in_bytes",
+	"rx_symbol_err",
+	"rx_pause",
+	"rx_unsup_opcode",
+	"rx_ok_bytes",
+	"rx_bad_bytes",
+	"rx_unicast",
+	"rx_multicast",
+	"rx_broadcast",
+	"rx_crc_err",
+	"rx_undersize",
+	"rx_fragments",
+	"rx_inrangelen_err",
+	"rx_outofrangelen_err",
+	"rx_oversize",
+	"rx_jabbers",
+	"rx_size64",
+	"rx_size65_127",
+	"rx_size128_255",
+	"rx_size256_511",
+	"rx_size512_1023",
+	"rx_size1024_1518",
+	"rx_size1519_max",
+	"pmac_rx_symbol_err",
+	"pmac_rx_pause",
+	"pmac_rx_unsup_opcode",
+	"pmac_rx_ok_bytes",
+	"pmac_rx_bad_bytes",
+	"pmac_rx_unicast",
+	"pmac_rx_multicast",
+	"pmac_rx_broadcast",
+	"pmac_rx_crc_err",
+	"pmac_rx_undersize",
+	"pmac_rx_fragments",
+	"pmac_rx_inrangelen_err",
+	"pmac_rx_outofrangelen_err",
+	"pmac_rx_oversize",
+	"pmac_rx_jabbers",
+	"pmac_rx_size64",
+	"pmac_rx_size65_127",
+	"pmac_rx_size128_255",
+	"pmac_rx_size256_511",
+	"pmac_rx_size512_1023",
+	"pmac_rx_size1024_1518",
+	"pmac_rx_size1519_max",
+	"rx_fwd_green_p0_q",
+	"rx_fwd_green_p1_q",
+	"rx_fwd_green_p2_q",
+	"rx_fwd_green_p3_q",
+	"rx_fwd_green_p4_q",
+	"rx_fwd_green_p5_q",
+	"rx_fwd_green_p6_q",
+	"rx_fwd_green_p7_q",
+	"rx_fwd_yellow_p0_q",
+	"rx_fwd_yellow_p1_q",
+	"rx_fwd_yellow_p2_q",
+	"rx_fwd_yellow_p3_q",
+	"rx_fwd_yellow_p4_q",
+	"rx_fwd_yellow_p5_q",
+	"rx_fwd_yellow_p6_q",
+	"rx_fwd_yellow_p7_q",
+	"rx_port_drop_green_p0_q",
+	"rx_port_drop_green_p1_q",
+	"rx_port_drop_green_p2_q",
+	"rx_port_drop_green_p3_q",
+	"rx_port_drop_green_p4_q",
+	"rx_port_drop_green_p5_q",
+	"rx_port_drop_green_p6_q",
+	"rx_port_drop_green_p7_q",
+	"rx_port_drop_yellow_p0_q",
+	"rx_port_drop_yellow_p1_q",
+	"rx_port_drop_yellow_p2_q",
+	"rx_port_drop_yellow_p3_q",
+	"rx_port_drop_yellow_p4_q",
+	"rx_port_drop_yellow_p5_q",
+	"rx_port_drop_yellow_p6_q",
+	"rx_port_drop_yellow_p7_q",
+	"rx_local_drop",
+	"rx_port_policer_drop",
+	"tx_out_bytes",
+	"tx_pause",
+	"tx_ok_bytes",
+	"tx_unicast",
+	"tx_multicast",
+	"tx_broadcast",
+	"tx_size64",
+	"tx_size65_127",
+	"tx_size128_255",
+	"tx_size256_511",
+	"tx_size512_1023",
+	"tx_size1024_1518",
+	"tx_size1519_max",
+	"tx_multi_coll",
+	"tx_late_coll",
+	"tx_xcoll",
+	"tx_defer",
+	"tx_xdefer",
+	"tx_backoff1",
+	"pmac_tx_pause",
+	"pmac_tx_ok_bytes",
+	"pmac_tx_unicast",
+	"pmac_tx_multicast",
+	"pmac_tx_broadcast",
+	"pmac_tx_size64",
+	"pmac_tx_size65_127",
+	"pmac_tx_size128_255",
+	"pmac_tx_size256_511",
+	"pmac_tx_size512_1023",
+	"pmac_tx_size1024_1518",
+	"pmac_tx_size1519_max",
+	"tx_port_green_p0_q",
+	"tx_port_green_p1_q",
+	"tx_port_green_p2_q",
+	"tx_port_green_p3_q",
+	"tx_port_green_p4_q",
+	"tx_port_green_p5_q",
+	"tx_port_green_p6_q",
+	"tx_port_green_p7_q",
+	"tx_port_yellow_p0_q",
+	"tx_port_yellow_p1_q",
+	"tx_port_yellow_p2_q",
+	"tx_port_yellow_p3_q",
+	"tx_port_yellow_p4_q",
+	"tx_port_yellow_p5_q",
+	"tx_port_yellow_p6_q",
+	"tx_port_yellow_p7_q",
+	"tx_local_drop",
+};
+
+/* Device Statistics */
+static void sparx5_get_device_stats(struct sparx5 *sparx5, int portno)
+{
+	u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+	u32 dev = sparx5_to_high_dev(portno);
+	u32 tinst = sparx5_port_dev_index(portno);
+	void __iomem *inst = spx5_inst_get(sparx5, dev, tinst);
+
+	sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_IN_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SYMBOL_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_BAD_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_CRC_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_UNDERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_FRAGMENTS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_OVERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_JABBERS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_RX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SYMBOL_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_BAD_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_CRC_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_UNDERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_FRAGMENTS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_OVERSIZE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_JABBERS_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_RX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_xqs_prio_stats(sparx5,
+			      0,
+			      &portstats[spx5_stats_green_p0_rx_fwd]);
+	sparx5_xqs_prio_stats(sparx5,
+			      16,
+			      &portstats[spx5_stats_green_p0_rx_port_drop]);
+	sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(32)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+			      spx5_rd(sparx5,
+				      ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_OUT_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_TX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_PAUSE_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_OK_BYTES_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_UC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_MC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_BC_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE64_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE65TO127_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE128TO255_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE256TO511_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE512TO1023_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE1024TO1518_CNT(tinst)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   DEV5G_PMAC_TX_SIZE1519TOMAX_CNT(tinst)));
+	sparx5_xqs_prio_stats(sparx5,
+			      256,
+			      &portstats[spx5_stats_green_p0_tx_port]);
+	sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+/* ASM Statistics */
+static void sparx5_get_asm_stats(struct sparx5 *sparx5, int portno)
+{
+	u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+	u32 dev = TARGET_ASM;
+	void __iomem *inst = spx5_inst_get(sparx5, dev, 0);
+
+	sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_IN_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SYMBOL_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_RX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_RX_BAD_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_RX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_CRC_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+			      spx5_inst_rd(inst,
+					   ASM_RX_UNDERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+			      spx5_inst_rd(inst,
+					   ASM_RX_FRAGMENTS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+			      spx5_inst_rd(inst,
+					   ASM_RX_OVERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   ASM_RX_JABBERS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_RX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SYMBOL_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_BAD_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_CRC_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_UNDERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_FRAGMENTS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_OVERSIZE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_JABBERS_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_RX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_xqs_prio_stats(sparx5,
+			      0,
+			      &portstats[spx5_stats_green_p0_rx_fwd]);
+	sparx5_xqs_prio_stats(sparx5,
+			      16,
+			      &portstats[spx5_stats_green_p0_rx_port_drop]);
+	sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(32)));
+	sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+			      spx5_rd(sparx5,
+				      ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_TX_OUT_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_TX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_TX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_TX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_TX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_multi_coll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_MULTI_COLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_late_coll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_LATE_COLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_xcoll],
+			      spx5_inst_rd(inst,
+					   ASM_TX_XCOLL_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_defer],
+			      spx5_inst_rd(inst,
+					   ASM_TX_DEFER_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_xdefer],
+			      spx5_inst_rd(inst,
+					   ASM_TX_XDEFER_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_tx_backoff1],
+			      spx5_inst_rd(inst,
+					   ASM_TX_BACKOFF1_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_PAUSE_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_OK_BYTES_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_UC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_MC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_BC_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE64_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE65TO127_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE128TO255_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE256TO511_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE512TO1023_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE1024TO1518_CNT(portno)));
+	sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+			      spx5_inst_rd(inst,
+					   ASM_PMAC_TX_SIZE1519TOMAX_CNT(portno)));
+	sparx5_xqs_prio_stats(sparx5,
+			      256,
+			      &portstats[spx5_stats_green_p0_tx_port]);
+	sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+			      spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+static void sparx5_update_port_stats(struct sparx5 *sparx5, int portno)
+{
+	bool high_speed_dev = sparx5_is_high_speed_device(&sparx5->ports[portno]->conf);
+
+	/* Set XQS port number */
+	spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno), sparx5, XQS_STAT_CFG);
+	if (high_speed_dev)
+		sparx5_get_device_stats(sparx5, portno);
+	else
+		sparx5_get_asm_stats(sparx5, portno);
+}
+
+static void sparx5_update_stats(struct sparx5 *sparx5)
+{
+	int idx;
+
+	mutex_lock(&sparx5->stats_lock);
+	for (idx = 0; idx < SPX5_PORTS; idx++)
+		if (sparx5->ports[idx])
+			sparx5_update_port_stats(sparx5, idx);
+	mutex_unlock(&sparx5->stats_lock);
+}
+
+static void sparx5_check_stats_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct sparx5 *sparx5 = container_of(dwork, struct sparx5,
+					     stats_work);
+
+	sparx5_update_stats(sparx5);
+
+	queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+			   SPX5_STATS_CHECK_DELAY);
+}
+
+static int sparx5_get_sset_count(struct net_device *ndev, int sset)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+
+	if (sset != ETH_SS_STATS)
+		return -EOPNOTSUPP;
+	return sparx5->num_stats;
+}
+
+static void sparx5_get_sset_strings(struct net_device *ndev, u32 sset, u8 *data)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+	int idx;
+
+	if (sset != ETH_SS_STATS)
+		return;
+
+	for (idx = 0; idx < sparx5->num_stats; idx++)
+		strncpy(data + idx * ETH_GSTRING_LEN,
+			sparx5->stats_layout[idx], ETH_GSTRING_LEN);
+}
+
+static void sparx5_get_stats(struct net_device *ndev,
+			     struct ethtool_stats *stats, u64 *data)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5  *sparx5 = port->sparx5;
+	u64 *portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+	int idx;
+
+	/* check and update now (all ports) */
+	sparx5_update_stats(sparx5);
+
+	/* Copy port counters to the ethtool buffer */
+	for (idx = 0; idx < sparx5->num_stats; idx++)
+		*data++ = portstats[idx];
+}
+
+void sparx5_get_stats64(struct net_device *ndev,
+			struct rtnl_link_stats64 *stats)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+	struct sparx5 *sparx5 = port->sparx5;
+	u64 *portstats;
+
+	if (!sparx5->stats)
+		return;		/* Not initialized yet */
+
+	portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+
+	mutex_lock(&sparx5->stats_lock);
+
+	stats->rx_errors = SPX5_STAT_SUM(rx_crc_err) +
+		SPX5_STAT_SUM(rx_undersize) + SPX5_STAT_SUM(rx_oversize) +
+		SPX5_STAT_SUM(rx_outofrangelen_err) +
+		SPX5_STAT_SUM(rx_symbol_err) + SPX5_STAT_SUM(rx_jabbers) +
+		SPX5_STAT_SUM(rx_fragments);
+
+	stats->rx_bytes = SPX5_STAT_SUM(rx_ok_bytes) +
+		SPX5_STAT_SUM(rx_bad_bytes);
+
+	stats->rx_packets = SPX5_STAT_SUM(rx_unicast) +
+		SPX5_STAT_SUM(rx_multicast) + SPX5_STAT_SUM(rx_broadcast) +
+		stats->rx_errors;
+
+	stats->multicast = SPX5_STAT_SUM(rx_unicast);
+
+	stats->rx_dropped = SPX5_STAT_GET(rx_port_policer_drop) +
+		SPX5_STAT_XQS_PRIOS_COUNTER_SUM(rx_port_drop);
+
+	/* Get Tx stats */
+	stats->tx_bytes = SPX5_STAT_SUM(tx_ok_bytes);
+
+	stats->tx_packets = SPX5_STAT_SUM(tx_unicast) +
+		SPX5_STAT_SUM(tx_multicast) + SPX5_STAT_SUM(tx_broadcast);
+
+	stats->tx_dropped = SPX5_STAT_GET(tx_local_drop);
+
+	stats->collisions = SPX5_STAT_GET(tx_multi_coll) +
+		SPX5_STAT_GET(tx_late_coll) +
+		SPX5_STAT_GET(tx_xcoll) +
+		SPX5_STAT_GET(tx_backoff1);
+
+	mutex_unlock(&sparx5->stats_lock);
+}
+
+static int sparx5_get_link_settings(struct net_device *ndev,
+				    struct ethtool_link_ksettings *cmd)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+
+	return phylink_ethtool_ksettings_get(port->phylink, cmd);
+}
+
+static int sparx5_set_link_settings(struct net_device *ndev,
+				    const struct ethtool_link_ksettings *cmd)
+{
+	struct sparx5_port *port = netdev_priv(ndev);
+
+	return phylink_ethtool_ksettings_set(port->phylink, cmd);
+}
+
+const struct ethtool_ops sparx5_ethtool_ops = {
+	.get_sset_count         = sparx5_get_sset_count,
+	.get_strings            = sparx5_get_sset_strings,
+	.get_ethtool_stats      = sparx5_get_stats,
+	.get_link_ksettings	= sparx5_get_link_settings,
+	.set_link_ksettings	= sparx5_set_link_settings,
+	.get_link               = ethtool_op_get_link,
+};
+
+int sparx_stats_init(struct sparx5 *sparx5)
+{
+	char queue_name[32];
+	int portno;
+
+	sparx5->stats_layout = sparx5_stats_layout;
+	sparx5->num_stats = ARRAY_SIZE(sparx5_stats_layout);
+	sparx5->stats = devm_kcalloc(sparx5->dev,
+				     SPX5_PORTS_ALL * sparx5->num_stats,
+				     sizeof(u64), GFP_KERNEL);
+	if (!sparx5->stats)
+		return -ENOMEM;
+
+	for (portno = 0; portno < SPX5_PORTS; portno++)
+		if (sparx5->ports[portno]) {
+			/* Clear Queue System counters */
+			spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno) |
+				XQS_STAT_CFG_STAT_CLEAR_SHOT_SET(3), sparx5, XQS_STAT_CFG);
+		}
+
+	mutex_init(&sparx5->stats_lock);
+	snprintf(queue_name, sizeof(queue_name), "%s-stats",
+		 dev_name(sparx5->dev));
+	sparx5->stats_queue = create_singlethread_workqueue(queue_name);
+	INIT_DELAYED_WORK(&sparx5->stats_work, sparx5_check_stats_work);
+	queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+			   SPX5_STATS_CHECK_DELAY);
+
+	return 0;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 2b42c72dac72..e1e2384512bc 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -751,6 +751,11 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
 	if (err)
 		goto cleanup_ports;
 
+	/* Init stats */
+	err = sparx_stats_init(sparx5);
+	if (err)
+		goto cleanup_ports;
+
 	/* Init mact_sw struct */
 	mutex_init(&sparx5->mact_lock);
 	INIT_LIST_HEAD(&sparx5->mact_entries);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index e59e1962f7d4..1d1b3b516244 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -130,6 +130,14 @@ struct sparx5 {
 	/* port structures are in net device */
 	struct sparx5_port                    *ports[SPX5_PORTS];
 	enum sparx5_core_clockfreq            coreclock;
+	/* Statistics */
+	u32                                   num_stats;
+	const char * const                    *stats_layout;
+	u64                                   *stats;
+	/* Workqueue for reading stats */
+	struct mutex                          stats_lock;
+	struct delayed_work                   stats_work;
+	struct workqueue_struct               *stats_queue;
 	/* Notifiers */
 	struct notifier_block                 netdevice_nb;
 	struct notifier_block                 switchdev_nb;
@@ -153,10 +161,6 @@ struct sparx5 {
 	bool                                  sd_sgpio_remapping;
 };
 
-/* sparx5_main.c */
-void sparx5_update_cpuport_stats(struct sparx5 *sparx5, int portno);
-bool sparx5_is_cpuport_stat(struct sparx5 *sparx5, int idx);
-
 /* sparx5_switchdev.c */
 int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
 void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
@@ -199,6 +203,10 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
 int sparx5_config_auto_calendar(struct sparx5 *sparx5);
 int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
 
+/* sparx5_ethtool.c */
+void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats);
+int sparx_stats_init(struct sparx5 *sparx5);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
@@ -226,6 +234,7 @@ static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
 }
 
 extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
+extern const struct ethtool_ops sparx5_ethtool_ops;
 
 /* Calculate raw offset */
 static inline __pure int spx5_offset(int id, int tinst, int tcnt,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index a0992443ab9f..3b7cfbb14f2e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -176,6 +176,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = {
 	.ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
 	.ndo_set_mac_address    = sparx5_set_mac_address,
 	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_get_stats64        = sparx5_get_stats64,
 	.ndo_get_port_parent_id = sparx5_get_port_parent_id,
 };
 
@@ -203,6 +204,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
 
 	ether_setup(ndev);
 	ndev->netdev_ops = &sparx5_port_netdev_ops;
+	ndev->ethtool_ops = &sparx5_ethtool_ops;
 	ndev->features |= NETIF_F_LLTX; /* software tx */
 
 	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-17  7:51   ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List
  Cc: Steen Hegelund, Andrew Lunn, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

This provides switchdev support for the Microship Sparx5 PCB134 and PCB135
reference boards.

This commit depends on the following series currently on their way
into the kernel:

- Sparx5 SerDes Driver
  Link: https://lore.kernel.org/r/20201211090541.157926-1-steen.hegelund@microchip.com/

- Serial GPIO Controller
  Link: https://lore.kernel.org/r/20201113145151.68900-1-lars.povlsen@microchip.com/

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 arch/arm64/boot/dts/microchip/sparx5.dtsi     |  60 ++
 .../dts/microchip/sparx5_pcb134_board.dtsi    | 424 ++++++++++--
 .../dts/microchip/sparx5_pcb135_board.dtsi    | 602 +++++++++++++++++-
 3 files changed, 1026 insertions(+), 60 deletions(-)

diff --git a/arch/arm64/boot/dts/microchip/sparx5.dtsi b/arch/arm64/boot/dts/microchip/sparx5.dtsi
index e480238489fa..085ab5a4fa50 100644
--- a/arch/arm64/boot/dts/microchip/sparx5.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5.dtsi
@@ -267,6 +267,21 @@ emmc_pins: emmc-pins {
 					"GPIO_46", "GPIO_47";
 				function = "emmc";
 			};
+
+			miim1_pins: miim1-pins {
+				pins = "GPIO_56", "GPIO_57";
+				function = "miim";
+			};
+
+			miim2_pins: miim2-pins {
+				pins = "GPIO_58", "GPIO_59";
+				function = "miim";
+			};
+
+			miim3_pins: miim3-pins {
+				pins = "GPIO_52", "GPIO_53";
+				function = "miim";
+			};
 		};
 
 		sgpio0: gpio@61101036c {
@@ -379,6 +394,44 @@ tmon0: tmon@610508110 {
 			clocks = <&ahb_clk>;
 		};
 
+		mdio0: mdio@6110102b0 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102b0 0x24>;
+		};
+
+		mdio1: mdio@6110102d4 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim1_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102d4 0x24>;
+		};
+
+		mdio2: mdio@6110102f8 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim2_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102d4 0x24>;
+		};
+
+		mdio3: mdio@61101031c {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim3_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x1101031c 0x24>;
+		};
+
 		serdes: serdes@10808000 {
 			compatible = "microchip,sparx5-serdes";
 			#phy-cells = <1>;
@@ -386,5 +439,12 @@ serdes: serdes@10808000 {
 			reg = <0x6 0x10808000 0x5d0000>;
 		};
 
+		switch: switch@610004000 {
+			compatible = "microchip,sparx5-switch";
+			reg =	<0x6 0x10004000 0x800000>,
+				<0x6 0x11010000 0x1b00000>;
+			reg-names = "devices", "gcb";
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		};
 	};
 };
diff --git a/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi b/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
index 6820579448d0..efa35cbcef49 100644
--- a/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
@@ -7,30 +7,6 @@
 #include "sparx5_pcb_common.dtsi"
 
 /{
-	aliases {
-	    i2c0   = &i2c0;
-	    i2c100 = &i2c100;
-	    i2c101 = &i2c101;
-	    i2c102 = &i2c102;
-	    i2c103 = &i2c103;
-	    i2c104 = &i2c104;
-	    i2c105 = &i2c105;
-	    i2c106 = &i2c106;
-	    i2c107 = &i2c107;
-	    i2c108 = &i2c108;
-	    i2c109 = &i2c109;
-	    i2c110 = &i2c110;
-	    i2c111 = &i2c111;
-	    i2c112 = &i2c112;
-	    i2c113 = &i2c113;
-	    i2c114 = &i2c114;
-	    i2c115 = &i2c115;
-	    i2c116 = &i2c116;
-	    i2c117 = &i2c117;
-	    i2c118 = &i2c118;
-	    i2c119 = &i2c119;
-	};
-
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
@@ -328,6 +304,11 @@ gpio@1 {
 	};
 };
 
+&sgpio2 {
+	status = "okay";
+	microchip,sgpio-port-ranges = <0 0>, <11 31>;
+};
+
 &gpio {
 	i2cmux_pins_i: i2cmux-pins-i {
 	       pins = "GPIO_16", "GPIO_17", "GPIO_18", "GPIO_19",
@@ -415,9 +396,9 @@ i2c0_emux: i2c0-emux@0 {
 
 &i2c0_imux {
 	pinctrl-names =
-		"i2c100", "i2c101", "i2c102", "i2c103",
-		"i2c104", "i2c105", "i2c106", "i2c107",
-		"i2c108", "i2c109", "i2c110", "i2c111", "idle";
+		"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
+		"i2c_sfp5", "i2c_sfp6", "i2c_sfp7", "i2c_sfp8",
+		"i2c_sfp9", "i2c_sfp10", "i2c_sfp11", "i2c_sfp12", "idle";
 	pinctrl-0 = <&i2cmux_0>;
 	pinctrl-1 = <&i2cmux_1>;
 	pinctrl-2 = <&i2cmux_2>;
@@ -431,62 +412,62 @@ &i2c0_imux {
 	pinctrl-10 = <&i2cmux_10>;
 	pinctrl-11 = <&i2cmux_11>;
 	pinctrl-12 = <&i2cmux_pins_i>;
-	i2c100: i2c_sfp1 {
+	i2c_sfp1: i2c_sfp1 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c101: i2c_sfp2 {
+	i2c_sfp2: i2c_sfp2 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c102: i2c_sfp3 {
+	i2c_sfp3: i2c_sfp3 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c103: i2c_sfp4 {
+	i2c_sfp4: i2c_sfp4 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c104: i2c_sfp5 {
+	i2c_sfp5: i2c_sfp5 {
 		reg = <0x4>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c105: i2c_sfp6 {
+	i2c_sfp6: i2c_sfp6 {
 		reg = <0x5>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c106: i2c_sfp7 {
+	i2c_sfp7: i2c_sfp7 {
 		reg = <0x6>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c107: i2c_sfp8 {
+	i2c_sfp8: i2c_sfp8 {
 		reg = <0x7>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c108: i2c_sfp9 {
+	i2c_sfp9: i2c_sfp9 {
 		reg = <0x8>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c109: i2c_sfp10 {
+	i2c_sfp10: i2c_sfp10 {
 		reg = <0x9>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c110: i2c_sfp11 {
+	i2c_sfp11: i2c_sfp11 {
 		reg = <0xa>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c111: i2c_sfp12 {
+	i2c_sfp12: i2c_sfp12 {
 		reg = <0xb>;
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -499,44 +480,393 @@ &gpio 60 GPIO_ACTIVE_HIGH
 		     &gpio 61 GPIO_ACTIVE_HIGH
 		     &gpio 54 GPIO_ACTIVE_HIGH>;
 	idle-state = <0x8>;
-	i2c112: i2c_sfp13 {
+	i2c_sfp13: i2c_sfp13 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c113: i2c_sfp14 {
+	i2c_sfp14: i2c_sfp14 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c114: i2c_sfp15 {
+	i2c_sfp15: i2c_sfp15 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c115: i2c_sfp16 {
+	i2c_sfp16: i2c_sfp16 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c116: i2c_sfp17 {
+	i2c_sfp17: i2c_sfp17 {
 		reg = <0x4>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c117: i2c_sfp18 {
+	i2c_sfp18: i2c_sfp18 {
 		reg = <0x5>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c118: i2c_sfp19 {
+	i2c_sfp19: i2c_sfp19 {
 		reg = <0x6>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c119: i2c_sfp20 {
+	i2c_sfp20: i2c_sfp20 {
 		reg = <0x7>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
 };
+
+&mdio3 {
+	status = "ok";
+	phy64: ethernet-phy@64 {
+		reg = <28>;
+	};
+};
+
+&axi {
+	sfp_eth12: sfp-eth12 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp1>;
+		tx-disable-gpios = <&sgpio_out2 11 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 11 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 11 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 12 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth13: sfp-eth13 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp2>;
+		tx-disable-gpios = <&sgpio_out2 12 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 12 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 12 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 13 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth14: sfp-eth14 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp3>;
+		tx-disable-gpios = <&sgpio_out2 13 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 13 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 13 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 14 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth15: sfp-eth15 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp4>;
+		tx-disable-gpios = <&sgpio_out2 14 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 14 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 14 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 15 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth48: sfp-eth48 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp5>;
+		tx-disable-gpios = <&sgpio_out2 15 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 15 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 15 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 16 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth49: sfp-eth49 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp6>;
+		tx-disable-gpios = <&sgpio_out2 16 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 16 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 16 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 17 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth50: sfp-eth50 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp7>;
+		tx-disable-gpios = <&sgpio_out2 17 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 17 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 17 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 18 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth51: sfp-eth51 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp8>;
+		tx-disable-gpios = <&sgpio_out2 18 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 18 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 18 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 19 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth52: sfp-eth52 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp9>;
+		tx-disable-gpios = <&sgpio_out2 19 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 19 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 19 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 20 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth53: sfp-eth53 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp10>;
+		tx-disable-gpios = <&sgpio_out2 20 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 20 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 20 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 21 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth54: sfp-eth54 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp11>;
+		tx-disable-gpios = <&sgpio_out2 21 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 21 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 21 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 22 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth55: sfp-eth55 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp12>;
+		tx-disable-gpios = <&sgpio_out2 22 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 22 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 22 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 23 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth56: sfp-eth56 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp13>;
+		tx-disable-gpios = <&sgpio_out2 23 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 23 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 23 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 24 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth57: sfp-eth57 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp14>;
+		tx-disable-gpios = <&sgpio_out2 24 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 24 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 24 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 25 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth58: sfp-eth58 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp15>;
+		tx-disable-gpios = <&sgpio_out2 25 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 25 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 25 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 26 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth59: sfp-eth59 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp16>;
+		tx-disable-gpios = <&sgpio_out2 26 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 26 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 26 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 27 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth60: sfp-eth60 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp17>;
+		tx-disable-gpios = <&sgpio_out2 27 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 27 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 27 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth61: sfp-eth61 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp18>;
+		tx-disable-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 28 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 28 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth62: sfp-eth62 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp19>;
+		tx-disable-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 29 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 29 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth63: sfp-eth63 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp20>;
+		tx-disable-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 30 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 30 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&switch {
+	ethernet-ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		/* 10G SFPs */
+		port12: port@12 {
+			reg = <12>;
+			max-speed = <10000>;
+			phys = <&serdes 13>;
+			sfp = <&sfp_eth12>;
+			sd_sgpio = <301>;
+			managed = "in-band-status";
+		};
+		port13: port@13 {
+			reg = <13>;
+			/* Example: CU SFP, 1G speed */
+			max-speed = <10000>;
+			phys = <&serdes 14>;
+			sfp = <&sfp_eth13>;
+			sd_sgpio = <305>;
+			managed = "in-band-status";
+		};
+		port14: port@14 {
+			reg = <14>;
+			max-speed = <10000>;
+			phys = <&serdes 15>;
+			sfp = <&sfp_eth14>;
+			sd_sgpio = <309>;
+			managed = "in-band-status";
+		};
+		port15: port@15 {
+			reg = <15>;
+			max-speed = <10000>;
+			phys = <&serdes 16>;
+			sfp = <&sfp_eth15>;
+			sd_sgpio = <313>;
+			managed = "in-band-status";
+		};
+		port48: port@48 {
+			reg = <48>;
+			max-speed = <10000>;
+			phys = <&serdes 17>;
+			sfp = <&sfp_eth48>;
+			sd_sgpio = <317>;
+			managed = "in-band-status";
+		};
+		port49: port@49 {
+			reg = <49>;
+			max-speed = <10000>;
+			phys = <&serdes 18>;
+			sfp = <&sfp_eth49>;
+			sd_sgpio = <321>;
+			managed = "in-band-status";
+		};
+		port50: port@50 {
+			reg = <50>;
+			max-speed = <10000>;
+			phys = <&serdes 19>;
+			sfp = <&sfp_eth50>;
+			sd_sgpio = <325>;
+			managed = "in-band-status";
+		};
+		port51: port@51 {
+			reg = <51>;
+			max-speed = <10000>;
+			phys = <&serdes 20>;
+			sfp = <&sfp_eth51>;
+			sd_sgpio = <329>;
+			managed = "in-band-status";
+		};
+		port52: port@52 {
+			reg = <52>;
+			max-speed = <10000>;
+			phys = <&serdes 21>;
+			sfp = <&sfp_eth52>;
+			sd_sgpio = <333>;
+			managed = "in-band-status";
+		};
+		port53: port@53 {
+			reg = <53>;
+			max-speed = <10000>;
+			phys = <&serdes 22>;
+			sfp = <&sfp_eth53>;
+			sd_sgpio = <337>;
+			managed = "in-band-status";
+		};
+		port54: port@54 {
+			reg = <54>;
+			max-speed = <10000>;
+			phys = <&serdes 23>;
+			sfp = <&sfp_eth54>;
+			sd_sgpio = <341>;
+			managed = "in-band-status";
+		};
+		port55: port@55 {
+			reg = <55>;
+			max-speed = <10000>;
+			phys = <&serdes 24>;
+			sfp = <&sfp_eth55>;
+			sd_sgpio = <345>;
+			managed = "in-band-status";
+		};
+		/* 25G SFPs */
+		port56: port@56 {
+			reg = <56>;
+			max-speed = <10000>;
+			phys = <&serdes 25>;
+			sfp = <&sfp_eth56>;
+			sd_sgpio = <349>;
+			managed = "in-band-status";
+		};
+		port57: port@57 {
+			reg = <57>;
+			max-speed = <10000>;
+			phys = <&serdes 26>;
+			sfp = <&sfp_eth57>;
+			sd_sgpio = <353>;
+			managed = "in-band-status";
+		};
+		port58: port@58 {
+			reg = <58>;
+			max-speed = <10000>;
+			phys = <&serdes 27>;
+			sfp = <&sfp_eth58>;
+			sd_sgpio = <357>;
+			managed = "in-band-status";
+		};
+		port59: port@59 {
+			reg = <59>;
+			max-speed = <10000>;
+			phys = <&serdes 28>;
+			sfp = <&sfp_eth59>;
+			sd_sgpio = <361>;
+			managed = "in-band-status";
+		};
+		port60: port@60 {
+			reg = <60>;
+			max-speed = <10000>;
+			phys = <&serdes 29>;
+			sfp = <&sfp_eth60>;
+			sd_sgpio = <365>;
+			managed = "in-band-status";
+		};
+		port61: port@61 {
+			reg = <61>;
+			max-speed = <10000>;
+			phys = <&serdes 30>;
+			sfp = <&sfp_eth61>;
+			sd_sgpio = <369>;
+			managed = "in-band-status";
+		};
+		port62: port@62 {
+			reg = <62>;
+			max-speed = <10000>;
+			phys = <&serdes 31>;
+			sfp = <&sfp_eth62>;
+			sd_sgpio = <373>;
+			managed = "in-band-status";
+		};
+		port63: port@63 {
+			reg = <63>;
+			max-speed = <10000>;
+			phys = <&serdes 32>;
+			sfp = <&sfp_eth63>;
+			sd_sgpio = <377>;
+			managed = "in-band-status";
+		};
+		/* Finally the Management interface */
+		port64: port@64 {
+			reg = <64>;
+			max-speed = <1000>;
+			phys = <&serdes 0>;
+			phy-handle = <&phy64>;
+			phy-mode = "sgmii";
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi b/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
index e28c6dd16377..69e136c7bb9c 100644
--- a/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
@@ -7,14 +7,6 @@
 #include "sparx5_pcb_common.dtsi"
 
 /{
-	aliases {
-	    i2c0   = &i2c0;
-	    i2c152 = &i2c152;
-	    i2c153 = &i2c153;
-	    i2c154 = &i2c154;
-	    i2c155 = &i2c155;
-	};
-
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
@@ -138,6 +130,11 @@ gpio@1 {
 	};
 };
 
+&sgpio2 {
+	status = "okay";
+	microchip,sgpio-port-ranges = <0 0>, <16 18>, <28 31>;
+};
+
 &axi {
 	i2c0_imux: i2c0-imux@0 {
 		compatible = "i2c-mux-pinctrl";
@@ -149,31 +146,610 @@ i2c0_imux: i2c0-imux@0 {
 
 &i2c0_imux {
 	pinctrl-names =
-		"i2c152", "i2c153", "i2c154", "i2c155",
+		"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
 		"idle";
 	pinctrl-0 = <&i2cmux_s29>;
 	pinctrl-1 = <&i2cmux_s30>;
 	pinctrl-2 = <&i2cmux_s31>;
 	pinctrl-3 = <&i2cmux_s32>;
 	pinctrl-4 = <&i2cmux_pins_i>;
-	i2c152: i2c_sfp1 {
+	i2c_sfp1: i2c_sfp1 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c153: i2c_sfp2 {
+	i2c_sfp2: i2c_sfp2 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c154: i2c_sfp3 {
+	i2c_sfp3: i2c_sfp3 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c155: i2c_sfp4 {
+	i2c_sfp4: i2c_sfp4 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
 };
+
+&axi {
+	sfp_eth60: sfp-eth60 {
+		compatible	   = "sff,sfp";
+		i2c-bus            = <&i2c_sfp1>;
+		tx-disable-gpios   = <&sgpio_out2 28 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 28 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 28 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth61: sfp-eth61 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp2>;
+		tx-disable-gpios   = <&sgpio_out2 29 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 29 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 29 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth62: sfp-eth62 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp3>;
+		tx-disable-gpios   = <&sgpio_out2 30 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 30 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 30 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth63: sfp-eth63 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp4>;
+		tx-disable-gpios   = <&sgpio_out2 31 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 31 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 31 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 31 2 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&mdio0 {
+	status = "ok";
+	phy0: ethernet-phy@0 {
+		reg = <0>;
+	};
+	phy1: ethernet-phy@1 {
+		reg = <1>;
+	};
+	phy2: ethernet-phy@2 {
+		reg = <2>;
+	};
+	phy3: ethernet-phy@3 {
+		reg = <3>;
+	};
+	phy4: ethernet-phy@4 {
+		reg = <4>;
+	};
+	phy5: ethernet-phy@5 {
+		reg = <5>;
+	};
+	phy6: ethernet-phy@6 {
+		reg = <6>;
+	};
+	phy7: ethernet-phy@7 {
+		reg = <7>;
+	};
+	phy8: ethernet-phy@8 {
+		reg = <8>;
+	};
+	phy9: ethernet-phy@9 {
+		reg = <9>;
+	};
+	phy10: ethernet-phy@10 {
+		reg = <10>;
+	};
+	phy11: ethernet-phy@11 {
+		reg = <11>;
+	};
+	phy12: ethernet-phy@12 {
+		reg = <12>;
+	};
+	phy13: ethernet-phy@13 {
+		reg = <13>;
+	};
+	phy14: ethernet-phy@14 {
+		reg = <14>;
+	};
+	phy15: ethernet-phy@15 {
+		reg = <15>;
+	};
+	phy16: ethernet-phy@16 {
+		reg = <16>;
+	};
+	phy17: ethernet-phy@17 {
+		reg = <17>;
+	};
+	phy18: ethernet-phy@18 {
+		reg = <18>;
+	};
+	phy19: ethernet-phy@19 {
+		reg = <19>;
+	};
+	phy20: ethernet-phy@20 {
+		reg = <20>;
+	};
+	phy21: ethernet-phy@21 {
+		reg = <21>;
+	};
+	phy22: ethernet-phy@22 {
+		reg = <22>;
+	};
+	phy23: ethernet-phy@23 {
+		reg = <23>;
+	};
+};
+
+&mdio1 {
+	status = "ok";
+	phy24: ethernet-phy@24 {
+		reg = <0>;
+	};
+	phy25: ethernet-phy@25 {
+		reg = <1>;
+	};
+	phy26: ethernet-phy@26 {
+		reg = <2>;
+	};
+	phy27: ethernet-phy@27 {
+		reg = <3>;
+	};
+	phy28: ethernet-phy@28 {
+		reg = <4>;
+	};
+	phy29: ethernet-phy@29 {
+		reg = <5>;
+	};
+	phy30: ethernet-phy@30 {
+		reg = <6>;
+	};
+	phy31: ethernet-phy@31 {
+		reg = <7>;
+	};
+	phy32: ethernet-phy@32 {
+		reg = <8>;
+	};
+	phy33: ethernet-phy@33 {
+		reg = <9>;
+	};
+	phy34: ethernet-phy@34 {
+		reg = <10>;
+	};
+	phy35: ethernet-phy@35 {
+		reg = <11>;
+	};
+	phy36: ethernet-phy@36 {
+		reg = <12>;
+	};
+	phy37: ethernet-phy@37 {
+		reg = <13>;
+	};
+	phy38: ethernet-phy@38 {
+		reg = <14>;
+	};
+	phy39: ethernet-phy@39 {
+		reg = <15>;
+	};
+	phy40: ethernet-phy@40 {
+		reg = <16>;
+	};
+	phy41: ethernet-phy@41 {
+		reg = <17>;
+	};
+	phy42: ethernet-phy@42 {
+		reg = <18>;
+	};
+	phy43: ethernet-phy@43 {
+		reg = <19>;
+	};
+	phy44: ethernet-phy@44 {
+		reg = <20>;
+	};
+	phy45: ethernet-phy@45 {
+		reg = <21>;
+	};
+	phy46: ethernet-phy@46 {
+		reg = <22>;
+	};
+	phy47: ethernet-phy@47 {
+		reg = <23>;
+	};
+};
+
+&mdio3 {
+	status = "ok";
+	phy64: ethernet-phy@64 {
+		reg = <28>;
+	};
+};
+
+&switch {
+	ethernet-ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port0: port@0 {
+			reg = <0>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy0>;
+			phy-mode = "qsgmii";
+		};
+		port1: port@1 {
+			reg = <1>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy1>;
+			phy-mode = "qsgmii";
+		};
+		port2: port@2 {
+			reg = <2>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy2>;
+			phy-mode = "qsgmii";
+		};
+		port3: port@3 {
+			reg = <3>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy3>;
+			phy-mode = "qsgmii";
+		};
+		port4: port@4 {
+			reg = <4>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy4>;
+			phy-mode = "qsgmii";
+		};
+		port5: port@5 {
+			reg = <5>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy5>;
+			phy-mode = "qsgmii";
+		};
+		port6: port@6 {
+			reg = <6>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy6>;
+			phy-mode = "qsgmii";
+		};
+		port7: port@7 {
+			reg = <7>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy7>;
+			phy-mode = "qsgmii";
+		};
+		port8: port@8 {
+			reg = <8>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy8>;
+			phy-mode = "qsgmii";
+		};
+		port9: port@9 {
+			reg = <9>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy9>;
+			phy-mode = "qsgmii";
+		};
+		port10: port@10 {
+			reg = <10>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy10>;
+			phy-mode = "qsgmii";
+		};
+		port11: port@11 {
+			reg = <11>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy11>;
+			phy-mode = "qsgmii";
+		};
+		port12: port@12 {
+			reg = <12>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy12>;
+			phy-mode = "qsgmii";
+		};
+		port13: port@13 {
+			reg = <13>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy13>;
+			phy-mode = "qsgmii";
+		};
+		port14: port@14 {
+			reg = <14>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy14>;
+			phy-mode = "qsgmii";
+		};
+		port15: port@15 {
+			reg = <15>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy15>;
+			phy-mode = "qsgmii";
+		};
+		port16: port@16 {
+			reg = <16>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy16>;
+			phy-mode = "qsgmii";
+		};
+		port17: port@17 {
+			reg = <17>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy17>;
+			phy-mode = "qsgmii";
+		};
+		port18: port@18 {
+			reg = <18>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy18>;
+			phy-mode = "qsgmii";
+		};
+		port19: port@19 {
+			reg = <19>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy19>;
+			phy-mode = "qsgmii";
+		};
+		port20: port@20 {
+			reg = <20>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy20>;
+			phy-mode = "qsgmii";
+		};
+		port21: port@21 {
+			reg = <21>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy21>;
+			phy-mode = "qsgmii";
+		};
+		port22: port@22 {
+			reg = <22>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy22>;
+			phy-mode = "qsgmii";
+		};
+		port23: port@23 {
+			reg = <23>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy23>;
+			phy-mode = "qsgmii";
+		};
+		port24: port@24 {
+			reg = <24>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy24>;
+			phy-mode = "qsgmii";
+		};
+		port25: port@25 {
+			reg = <25>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy25>;
+			phy-mode = "qsgmii";
+		};
+		port26: port@26 {
+			reg = <26>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy26>;
+			phy-mode = "qsgmii";
+		};
+		port27: port@27 {
+			reg = <27>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy27>;
+			phy-mode = "qsgmii";
+		};
+		port28: port@28 {
+			reg = <28>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy28>;
+			phy-mode = "qsgmii";
+		};
+		port29: port@29 {
+			reg = <29>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy29>;
+			phy-mode = "qsgmii";
+		};
+		port30: port@30 {
+			reg = <30>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy30>;
+			phy-mode = "qsgmii";
+		};
+		port31: port@31 {
+			reg = <31>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy31>;
+			phy-mode = "qsgmii";
+		};
+		port32: port@32 {
+			reg = <32>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy32>;
+			phy-mode = "qsgmii";
+		};
+		port33: port@33 {
+			reg = <33>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy33>;
+			phy-mode = "qsgmii";
+		};
+		port34: port@34 {
+			reg = <34>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy34>;
+			phy-mode = "qsgmii";
+		};
+		port35: port@35 {
+			reg = <35>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy35>;
+			phy-mode = "qsgmii";
+		};
+		port36: port@36 {
+			reg = <36>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy36>;
+			phy-mode = "qsgmii";
+		};
+		port37: port@37 {
+			reg = <37>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy37>;
+			phy-mode = "qsgmii";
+		};
+		port38: port@38 {
+			reg = <38>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy38>;
+			phy-mode = "qsgmii";
+		};
+		port39: port@39 {
+			reg = <39>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy39>;
+			phy-mode = "qsgmii";
+		};
+		port40: port@40 {
+			reg = <40>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy40>;
+			phy-mode = "qsgmii";
+		};
+		port41: port@41 {
+			reg = <41>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy41>;
+			phy-mode = "qsgmii";
+		};
+		port42: port@42 {
+			reg = <42>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy42>;
+			phy-mode = "qsgmii";
+		};
+		port43: port@43 {
+			reg = <43>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy43>;
+			phy-mode = "qsgmii";
+		};
+		port44: port@44 {
+			reg = <44>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy44>;
+			phy-mode = "qsgmii";
+		};
+		port45: port@45 {
+			reg = <45>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy45>;
+			phy-mode = "qsgmii";
+		};
+		port46: port@46 {
+			reg = <46>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy46>;
+			phy-mode = "qsgmii";
+		};
+		port47: port@47 {
+			reg = <47>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy47>;
+			phy-mode = "qsgmii";
+		};
+		/* Then the 25G interfaces */
+		port60: port@60 {
+			reg = <60>;
+			max-speed = <25000>;
+			phys = <&serdes 29>;
+			sfp = <&sfp_eth60>;
+			managed = "in-band-status";
+		};
+		port61: port@61 {
+			reg = <61>;
+			max-speed = <25000>;
+			phys = <&serdes 30>;
+			sfp = <&sfp_eth61>;
+			managed = "in-band-status";
+		};
+		port62: port@62 {
+			reg = <62>;
+			max-speed = <25000>;
+			phys = <&serdes 31>;
+			sfp = <&sfp_eth62>;
+			managed = "in-band-status";
+		};
+		port63: port@63 {
+			reg = <63>;
+			max-speed = <25000>;
+			phys = <&serdes 32>;
+			sfp = <&sfp_eth63>;
+			managed = "in-band-status";
+		};
+		/* Finally the Management interface */
+		port64: port@64 {
+			reg = <64>;
+			max-speed = <1000>;
+			phys = <&serdes 0>;
+			phy-handle = <&phy64>;
+			phy-mode = "sgmii";
+		};
+	};
+};
-- 
2.29.2


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

* [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
@ 2020-12-17  7:51   ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-17  7:51 UTC (permalink / raw)
  To: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Masahiro Yamada, Lars Povlsen

This provides switchdev support for the Microship Sparx5 PCB134 and PCB135
reference boards.

This commit depends on the following series currently on their way
into the kernel:

- Sparx5 SerDes Driver
  Link: https://lore.kernel.org/r/20201211090541.157926-1-steen.hegelund@microchip.com/

- Serial GPIO Controller
  Link: https://lore.kernel.org/r/20201113145151.68900-1-lars.povlsen@microchip.com/

Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
---
 arch/arm64/boot/dts/microchip/sparx5.dtsi     |  60 ++
 .../dts/microchip/sparx5_pcb134_board.dtsi    | 424 ++++++++++--
 .../dts/microchip/sparx5_pcb135_board.dtsi    | 602 +++++++++++++++++-
 3 files changed, 1026 insertions(+), 60 deletions(-)

diff --git a/arch/arm64/boot/dts/microchip/sparx5.dtsi b/arch/arm64/boot/dts/microchip/sparx5.dtsi
index e480238489fa..085ab5a4fa50 100644
--- a/arch/arm64/boot/dts/microchip/sparx5.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5.dtsi
@@ -267,6 +267,21 @@ emmc_pins: emmc-pins {
 					"GPIO_46", "GPIO_47";
 				function = "emmc";
 			};
+
+			miim1_pins: miim1-pins {
+				pins = "GPIO_56", "GPIO_57";
+				function = "miim";
+			};
+
+			miim2_pins: miim2-pins {
+				pins = "GPIO_58", "GPIO_59";
+				function = "miim";
+			};
+
+			miim3_pins: miim3-pins {
+				pins = "GPIO_52", "GPIO_53";
+				function = "miim";
+			};
 		};
 
 		sgpio0: gpio@61101036c {
@@ -379,6 +394,44 @@ tmon0: tmon@610508110 {
 			clocks = <&ahb_clk>;
 		};
 
+		mdio0: mdio@6110102b0 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102b0 0x24>;
+		};
+
+		mdio1: mdio@6110102d4 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim1_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102d4 0x24>;
+		};
+
+		mdio2: mdio@6110102f8 {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim2_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x110102d4 0x24>;
+		};
+
+		mdio3: mdio@61101031c {
+			compatible = "mscc,ocelot-miim";
+			status = "disabled";
+			pinctrl-0 = <&miim3_pins>;
+			pinctrl-names = "default";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x6 0x1101031c 0x24>;
+		};
+
 		serdes: serdes@10808000 {
 			compatible = "microchip,sparx5-serdes";
 			#phy-cells = <1>;
@@ -386,5 +439,12 @@ serdes: serdes@10808000 {
 			reg = <0x6 0x10808000 0x5d0000>;
 		};
 
+		switch: switch@610004000 {
+			compatible = "microchip,sparx5-switch";
+			reg =	<0x6 0x10004000 0x800000>,
+				<0x6 0x11010000 0x1b00000>;
+			reg-names = "devices", "gcb";
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		};
 	};
 };
diff --git a/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi b/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
index 6820579448d0..efa35cbcef49 100644
--- a/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5_pcb134_board.dtsi
@@ -7,30 +7,6 @@
 #include "sparx5_pcb_common.dtsi"
 
 /{
-	aliases {
-	    i2c0   = &i2c0;
-	    i2c100 = &i2c100;
-	    i2c101 = &i2c101;
-	    i2c102 = &i2c102;
-	    i2c103 = &i2c103;
-	    i2c104 = &i2c104;
-	    i2c105 = &i2c105;
-	    i2c106 = &i2c106;
-	    i2c107 = &i2c107;
-	    i2c108 = &i2c108;
-	    i2c109 = &i2c109;
-	    i2c110 = &i2c110;
-	    i2c111 = &i2c111;
-	    i2c112 = &i2c112;
-	    i2c113 = &i2c113;
-	    i2c114 = &i2c114;
-	    i2c115 = &i2c115;
-	    i2c116 = &i2c116;
-	    i2c117 = &i2c117;
-	    i2c118 = &i2c118;
-	    i2c119 = &i2c119;
-	};
-
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
@@ -328,6 +304,11 @@ gpio@1 {
 	};
 };
 
+&sgpio2 {
+	status = "okay";
+	microchip,sgpio-port-ranges = <0 0>, <11 31>;
+};
+
 &gpio {
 	i2cmux_pins_i: i2cmux-pins-i {
 	       pins = "GPIO_16", "GPIO_17", "GPIO_18", "GPIO_19",
@@ -415,9 +396,9 @@ i2c0_emux: i2c0-emux@0 {
 
 &i2c0_imux {
 	pinctrl-names =
-		"i2c100", "i2c101", "i2c102", "i2c103",
-		"i2c104", "i2c105", "i2c106", "i2c107",
-		"i2c108", "i2c109", "i2c110", "i2c111", "idle";
+		"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
+		"i2c_sfp5", "i2c_sfp6", "i2c_sfp7", "i2c_sfp8",
+		"i2c_sfp9", "i2c_sfp10", "i2c_sfp11", "i2c_sfp12", "idle";
 	pinctrl-0 = <&i2cmux_0>;
 	pinctrl-1 = <&i2cmux_1>;
 	pinctrl-2 = <&i2cmux_2>;
@@ -431,62 +412,62 @@ &i2c0_imux {
 	pinctrl-10 = <&i2cmux_10>;
 	pinctrl-11 = <&i2cmux_11>;
 	pinctrl-12 = <&i2cmux_pins_i>;
-	i2c100: i2c_sfp1 {
+	i2c_sfp1: i2c_sfp1 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c101: i2c_sfp2 {
+	i2c_sfp2: i2c_sfp2 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c102: i2c_sfp3 {
+	i2c_sfp3: i2c_sfp3 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c103: i2c_sfp4 {
+	i2c_sfp4: i2c_sfp4 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c104: i2c_sfp5 {
+	i2c_sfp5: i2c_sfp5 {
 		reg = <0x4>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c105: i2c_sfp6 {
+	i2c_sfp6: i2c_sfp6 {
 		reg = <0x5>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c106: i2c_sfp7 {
+	i2c_sfp7: i2c_sfp7 {
 		reg = <0x6>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c107: i2c_sfp8 {
+	i2c_sfp8: i2c_sfp8 {
 		reg = <0x7>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c108: i2c_sfp9 {
+	i2c_sfp9: i2c_sfp9 {
 		reg = <0x8>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c109: i2c_sfp10 {
+	i2c_sfp10: i2c_sfp10 {
 		reg = <0x9>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c110: i2c_sfp11 {
+	i2c_sfp11: i2c_sfp11 {
 		reg = <0xa>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c111: i2c_sfp12 {
+	i2c_sfp12: i2c_sfp12 {
 		reg = <0xb>;
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -499,44 +480,393 @@ &gpio 60 GPIO_ACTIVE_HIGH
 		     &gpio 61 GPIO_ACTIVE_HIGH
 		     &gpio 54 GPIO_ACTIVE_HIGH>;
 	idle-state = <0x8>;
-	i2c112: i2c_sfp13 {
+	i2c_sfp13: i2c_sfp13 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c113: i2c_sfp14 {
+	i2c_sfp14: i2c_sfp14 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c114: i2c_sfp15 {
+	i2c_sfp15: i2c_sfp15 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c115: i2c_sfp16 {
+	i2c_sfp16: i2c_sfp16 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c116: i2c_sfp17 {
+	i2c_sfp17: i2c_sfp17 {
 		reg = <0x4>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c117: i2c_sfp18 {
+	i2c_sfp18: i2c_sfp18 {
 		reg = <0x5>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c118: i2c_sfp19 {
+	i2c_sfp19: i2c_sfp19 {
 		reg = <0x6>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c119: i2c_sfp20 {
+	i2c_sfp20: i2c_sfp20 {
 		reg = <0x7>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
 };
+
+&mdio3 {
+	status = "ok";
+	phy64: ethernet-phy@64 {
+		reg = <28>;
+	};
+};
+
+&axi {
+	sfp_eth12: sfp-eth12 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp1>;
+		tx-disable-gpios = <&sgpio_out2 11 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 11 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 11 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 12 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth13: sfp-eth13 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp2>;
+		tx-disable-gpios = <&sgpio_out2 12 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 12 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 12 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 13 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth14: sfp-eth14 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp3>;
+		tx-disable-gpios = <&sgpio_out2 13 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 13 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 13 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 14 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth15: sfp-eth15 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp4>;
+		tx-disable-gpios = <&sgpio_out2 14 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 14 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 14 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 15 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth48: sfp-eth48 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp5>;
+		tx-disable-gpios = <&sgpio_out2 15 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 15 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 15 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 16 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth49: sfp-eth49 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp6>;
+		tx-disable-gpios = <&sgpio_out2 16 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 16 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 16 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 17 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth50: sfp-eth50 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp7>;
+		tx-disable-gpios = <&sgpio_out2 17 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 17 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 17 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 18 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth51: sfp-eth51 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp8>;
+		tx-disable-gpios = <&sgpio_out2 18 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 18 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 18 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 19 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth52: sfp-eth52 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp9>;
+		tx-disable-gpios = <&sgpio_out2 19 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 19 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 19 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 20 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth53: sfp-eth53 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp10>;
+		tx-disable-gpios = <&sgpio_out2 20 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 20 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 20 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 21 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth54: sfp-eth54 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp11>;
+		tx-disable-gpios = <&sgpio_out2 21 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 21 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 21 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 22 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth55: sfp-eth55 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp12>;
+		tx-disable-gpios = <&sgpio_out2 22 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 22 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 22 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 23 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth56: sfp-eth56 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp13>;
+		tx-disable-gpios = <&sgpio_out2 23 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 23 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 23 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 24 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth57: sfp-eth57 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp14>;
+		tx-disable-gpios = <&sgpio_out2 24 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 24 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 24 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 25 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth58: sfp-eth58 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp15>;
+		tx-disable-gpios = <&sgpio_out2 25 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 25 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 25 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 26 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth59: sfp-eth59 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp16>;
+		tx-disable-gpios = <&sgpio_out2 26 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 26 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 26 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 27 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth60: sfp-eth60 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp17>;
+		tx-disable-gpios = <&sgpio_out2 27 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 27 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 27 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth61: sfp-eth61 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp18>;
+		tx-disable-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 28 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 28 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth62: sfp-eth62 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp19>;
+		tx-disable-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 29 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 29 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth63: sfp-eth63 {
+		compatible       = "sff,sfp";
+		i2c-bus          = <&i2c_sfp20>;
+		tx-disable-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_LOW>;
+		los-gpios        = <&sgpio_in2 30 1 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios   = <&sgpio_in2 30 2 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios   = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&switch {
+	ethernet-ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		/* 10G SFPs */
+		port12: port@12 {
+			reg = <12>;
+			max-speed = <10000>;
+			phys = <&serdes 13>;
+			sfp = <&sfp_eth12>;
+			sd_sgpio = <301>;
+			managed = "in-band-status";
+		};
+		port13: port@13 {
+			reg = <13>;
+			/* Example: CU SFP, 1G speed */
+			max-speed = <10000>;
+			phys = <&serdes 14>;
+			sfp = <&sfp_eth13>;
+			sd_sgpio = <305>;
+			managed = "in-band-status";
+		};
+		port14: port@14 {
+			reg = <14>;
+			max-speed = <10000>;
+			phys = <&serdes 15>;
+			sfp = <&sfp_eth14>;
+			sd_sgpio = <309>;
+			managed = "in-band-status";
+		};
+		port15: port@15 {
+			reg = <15>;
+			max-speed = <10000>;
+			phys = <&serdes 16>;
+			sfp = <&sfp_eth15>;
+			sd_sgpio = <313>;
+			managed = "in-band-status";
+		};
+		port48: port@48 {
+			reg = <48>;
+			max-speed = <10000>;
+			phys = <&serdes 17>;
+			sfp = <&sfp_eth48>;
+			sd_sgpio = <317>;
+			managed = "in-band-status";
+		};
+		port49: port@49 {
+			reg = <49>;
+			max-speed = <10000>;
+			phys = <&serdes 18>;
+			sfp = <&sfp_eth49>;
+			sd_sgpio = <321>;
+			managed = "in-band-status";
+		};
+		port50: port@50 {
+			reg = <50>;
+			max-speed = <10000>;
+			phys = <&serdes 19>;
+			sfp = <&sfp_eth50>;
+			sd_sgpio = <325>;
+			managed = "in-band-status";
+		};
+		port51: port@51 {
+			reg = <51>;
+			max-speed = <10000>;
+			phys = <&serdes 20>;
+			sfp = <&sfp_eth51>;
+			sd_sgpio = <329>;
+			managed = "in-band-status";
+		};
+		port52: port@52 {
+			reg = <52>;
+			max-speed = <10000>;
+			phys = <&serdes 21>;
+			sfp = <&sfp_eth52>;
+			sd_sgpio = <333>;
+			managed = "in-band-status";
+		};
+		port53: port@53 {
+			reg = <53>;
+			max-speed = <10000>;
+			phys = <&serdes 22>;
+			sfp = <&sfp_eth53>;
+			sd_sgpio = <337>;
+			managed = "in-band-status";
+		};
+		port54: port@54 {
+			reg = <54>;
+			max-speed = <10000>;
+			phys = <&serdes 23>;
+			sfp = <&sfp_eth54>;
+			sd_sgpio = <341>;
+			managed = "in-band-status";
+		};
+		port55: port@55 {
+			reg = <55>;
+			max-speed = <10000>;
+			phys = <&serdes 24>;
+			sfp = <&sfp_eth55>;
+			sd_sgpio = <345>;
+			managed = "in-band-status";
+		};
+		/* 25G SFPs */
+		port56: port@56 {
+			reg = <56>;
+			max-speed = <10000>;
+			phys = <&serdes 25>;
+			sfp = <&sfp_eth56>;
+			sd_sgpio = <349>;
+			managed = "in-band-status";
+		};
+		port57: port@57 {
+			reg = <57>;
+			max-speed = <10000>;
+			phys = <&serdes 26>;
+			sfp = <&sfp_eth57>;
+			sd_sgpio = <353>;
+			managed = "in-band-status";
+		};
+		port58: port@58 {
+			reg = <58>;
+			max-speed = <10000>;
+			phys = <&serdes 27>;
+			sfp = <&sfp_eth58>;
+			sd_sgpio = <357>;
+			managed = "in-band-status";
+		};
+		port59: port@59 {
+			reg = <59>;
+			max-speed = <10000>;
+			phys = <&serdes 28>;
+			sfp = <&sfp_eth59>;
+			sd_sgpio = <361>;
+			managed = "in-band-status";
+		};
+		port60: port@60 {
+			reg = <60>;
+			max-speed = <10000>;
+			phys = <&serdes 29>;
+			sfp = <&sfp_eth60>;
+			sd_sgpio = <365>;
+			managed = "in-band-status";
+		};
+		port61: port@61 {
+			reg = <61>;
+			max-speed = <10000>;
+			phys = <&serdes 30>;
+			sfp = <&sfp_eth61>;
+			sd_sgpio = <369>;
+			managed = "in-band-status";
+		};
+		port62: port@62 {
+			reg = <62>;
+			max-speed = <10000>;
+			phys = <&serdes 31>;
+			sfp = <&sfp_eth62>;
+			sd_sgpio = <373>;
+			managed = "in-band-status";
+		};
+		port63: port@63 {
+			reg = <63>;
+			max-speed = <10000>;
+			phys = <&serdes 32>;
+			sfp = <&sfp_eth63>;
+			sd_sgpio = <377>;
+			managed = "in-band-status";
+		};
+		/* Finally the Management interface */
+		port64: port@64 {
+			reg = <64>;
+			max-speed = <1000>;
+			phys = <&serdes 0>;
+			phy-handle = <&phy64>;
+			phy-mode = "sgmii";
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi b/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
index e28c6dd16377..69e136c7bb9c 100644
--- a/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
+++ b/arch/arm64/boot/dts/microchip/sparx5_pcb135_board.dtsi
@@ -7,14 +7,6 @@
 #include "sparx5_pcb_common.dtsi"
 
 /{
-	aliases {
-	    i2c0   = &i2c0;
-	    i2c152 = &i2c152;
-	    i2c153 = &i2c153;
-	    i2c154 = &i2c154;
-	    i2c155 = &i2c155;
-	};
-
 	gpio-restart {
 		compatible = "gpio-restart";
 		gpios = <&gpio 37 GPIO_ACTIVE_LOW>;
@@ -138,6 +130,11 @@ gpio@1 {
 	};
 };
 
+&sgpio2 {
+	status = "okay";
+	microchip,sgpio-port-ranges = <0 0>, <16 18>, <28 31>;
+};
+
 &axi {
 	i2c0_imux: i2c0-imux@0 {
 		compatible = "i2c-mux-pinctrl";
@@ -149,31 +146,610 @@ i2c0_imux: i2c0-imux@0 {
 
 &i2c0_imux {
 	pinctrl-names =
-		"i2c152", "i2c153", "i2c154", "i2c155",
+		"i2c_sfp1", "i2c_sfp2", "i2c_sfp3", "i2c_sfp4",
 		"idle";
 	pinctrl-0 = <&i2cmux_s29>;
 	pinctrl-1 = <&i2cmux_s30>;
 	pinctrl-2 = <&i2cmux_s31>;
 	pinctrl-3 = <&i2cmux_s32>;
 	pinctrl-4 = <&i2cmux_pins_i>;
-	i2c152: i2c_sfp1 {
+	i2c_sfp1: i2c_sfp1 {
 		reg = <0x0>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c153: i2c_sfp2 {
+	i2c_sfp2: i2c_sfp2 {
 		reg = <0x1>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c154: i2c_sfp3 {
+	i2c_sfp3: i2c_sfp3 {
 		reg = <0x2>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
-	i2c155: i2c_sfp4 {
+	i2c_sfp4: i2c_sfp4 {
 		reg = <0x3>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
 };
+
+&axi {
+	sfp_eth60: sfp-eth60 {
+		compatible	   = "sff,sfp";
+		i2c-bus            = <&i2c_sfp1>;
+		tx-disable-gpios   = <&sgpio_out2 28 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 28 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 28 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 28 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 28 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth61: sfp-eth61 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp2>;
+		tx-disable-gpios   = <&sgpio_out2 29 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 29 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 29 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 29 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 29 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth62: sfp-eth62 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp3>;
+		tx-disable-gpios   = <&sgpio_out2 30 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 30 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 30 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 30 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 30 2 GPIO_ACTIVE_HIGH>;
+	};
+	sfp_eth63: sfp-eth63 {
+		compatible         = "sff,sfp";
+		i2c-bus            = <&i2c_sfp4>;
+		tx-disable-gpios   = <&sgpio_out2 31 0 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&sgpio_out2 31 1 GPIO_ACTIVE_HIGH>;
+		los-gpios          = <&sgpio_in2 31 0 GPIO_ACTIVE_HIGH>;
+		mod-def0-gpios     = <&sgpio_in2 31 1 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios     = <&sgpio_in2 31 2 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&mdio0 {
+	status = "ok";
+	phy0: ethernet-phy@0 {
+		reg = <0>;
+	};
+	phy1: ethernet-phy@1 {
+		reg = <1>;
+	};
+	phy2: ethernet-phy@2 {
+		reg = <2>;
+	};
+	phy3: ethernet-phy@3 {
+		reg = <3>;
+	};
+	phy4: ethernet-phy@4 {
+		reg = <4>;
+	};
+	phy5: ethernet-phy@5 {
+		reg = <5>;
+	};
+	phy6: ethernet-phy@6 {
+		reg = <6>;
+	};
+	phy7: ethernet-phy@7 {
+		reg = <7>;
+	};
+	phy8: ethernet-phy@8 {
+		reg = <8>;
+	};
+	phy9: ethernet-phy@9 {
+		reg = <9>;
+	};
+	phy10: ethernet-phy@10 {
+		reg = <10>;
+	};
+	phy11: ethernet-phy@11 {
+		reg = <11>;
+	};
+	phy12: ethernet-phy@12 {
+		reg = <12>;
+	};
+	phy13: ethernet-phy@13 {
+		reg = <13>;
+	};
+	phy14: ethernet-phy@14 {
+		reg = <14>;
+	};
+	phy15: ethernet-phy@15 {
+		reg = <15>;
+	};
+	phy16: ethernet-phy@16 {
+		reg = <16>;
+	};
+	phy17: ethernet-phy@17 {
+		reg = <17>;
+	};
+	phy18: ethernet-phy@18 {
+		reg = <18>;
+	};
+	phy19: ethernet-phy@19 {
+		reg = <19>;
+	};
+	phy20: ethernet-phy@20 {
+		reg = <20>;
+	};
+	phy21: ethernet-phy@21 {
+		reg = <21>;
+	};
+	phy22: ethernet-phy@22 {
+		reg = <22>;
+	};
+	phy23: ethernet-phy@23 {
+		reg = <23>;
+	};
+};
+
+&mdio1 {
+	status = "ok";
+	phy24: ethernet-phy@24 {
+		reg = <0>;
+	};
+	phy25: ethernet-phy@25 {
+		reg = <1>;
+	};
+	phy26: ethernet-phy@26 {
+		reg = <2>;
+	};
+	phy27: ethernet-phy@27 {
+		reg = <3>;
+	};
+	phy28: ethernet-phy@28 {
+		reg = <4>;
+	};
+	phy29: ethernet-phy@29 {
+		reg = <5>;
+	};
+	phy30: ethernet-phy@30 {
+		reg = <6>;
+	};
+	phy31: ethernet-phy@31 {
+		reg = <7>;
+	};
+	phy32: ethernet-phy@32 {
+		reg = <8>;
+	};
+	phy33: ethernet-phy@33 {
+		reg = <9>;
+	};
+	phy34: ethernet-phy@34 {
+		reg = <10>;
+	};
+	phy35: ethernet-phy@35 {
+		reg = <11>;
+	};
+	phy36: ethernet-phy@36 {
+		reg = <12>;
+	};
+	phy37: ethernet-phy@37 {
+		reg = <13>;
+	};
+	phy38: ethernet-phy@38 {
+		reg = <14>;
+	};
+	phy39: ethernet-phy@39 {
+		reg = <15>;
+	};
+	phy40: ethernet-phy@40 {
+		reg = <16>;
+	};
+	phy41: ethernet-phy@41 {
+		reg = <17>;
+	};
+	phy42: ethernet-phy@42 {
+		reg = <18>;
+	};
+	phy43: ethernet-phy@43 {
+		reg = <19>;
+	};
+	phy44: ethernet-phy@44 {
+		reg = <20>;
+	};
+	phy45: ethernet-phy@45 {
+		reg = <21>;
+	};
+	phy46: ethernet-phy@46 {
+		reg = <22>;
+	};
+	phy47: ethernet-phy@47 {
+		reg = <23>;
+	};
+};
+
+&mdio3 {
+	status = "ok";
+	phy64: ethernet-phy@64 {
+		reg = <28>;
+	};
+};
+
+&switch {
+	ethernet-ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port0: port@0 {
+			reg = <0>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy0>;
+			phy-mode = "qsgmii";
+		};
+		port1: port@1 {
+			reg = <1>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy1>;
+			phy-mode = "qsgmii";
+		};
+		port2: port@2 {
+			reg = <2>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy2>;
+			phy-mode = "qsgmii";
+		};
+		port3: port@3 {
+			reg = <3>;
+			max-speed = <1000>;
+			phys = <&serdes 13>;
+			phy-handle = <&phy3>;
+			phy-mode = "qsgmii";
+		};
+		port4: port@4 {
+			reg = <4>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy4>;
+			phy-mode = "qsgmii";
+		};
+		port5: port@5 {
+			reg = <5>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy5>;
+			phy-mode = "qsgmii";
+		};
+		port6: port@6 {
+			reg = <6>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy6>;
+			phy-mode = "qsgmii";
+		};
+		port7: port@7 {
+			reg = <7>;
+			max-speed = <1000>;
+			phys = <&serdes 14>;
+			phy-handle = <&phy7>;
+			phy-mode = "qsgmii";
+		};
+		port8: port@8 {
+			reg = <8>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy8>;
+			phy-mode = "qsgmii";
+		};
+		port9: port@9 {
+			reg = <9>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy9>;
+			phy-mode = "qsgmii";
+		};
+		port10: port@10 {
+			reg = <10>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy10>;
+			phy-mode = "qsgmii";
+		};
+		port11: port@11 {
+			reg = <11>;
+			max-speed = <1000>;
+			phys = <&serdes 15>;
+			phy-handle = <&phy11>;
+			phy-mode = "qsgmii";
+		};
+		port12: port@12 {
+			reg = <12>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy12>;
+			phy-mode = "qsgmii";
+		};
+		port13: port@13 {
+			reg = <13>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy13>;
+			phy-mode = "qsgmii";
+		};
+		port14: port@14 {
+			reg = <14>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy14>;
+			phy-mode = "qsgmii";
+		};
+		port15: port@15 {
+			reg = <15>;
+			max-speed = <1000>;
+			phys = <&serdes 16>;
+			phy-handle = <&phy15>;
+			phy-mode = "qsgmii";
+		};
+		port16: port@16 {
+			reg = <16>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy16>;
+			phy-mode = "qsgmii";
+		};
+		port17: port@17 {
+			reg = <17>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy17>;
+			phy-mode = "qsgmii";
+		};
+		port18: port@18 {
+			reg = <18>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy18>;
+			phy-mode = "qsgmii";
+		};
+		port19: port@19 {
+			reg = <19>;
+			max-speed = <1000>;
+			phys = <&serdes 17>;
+			phy-handle = <&phy19>;
+			phy-mode = "qsgmii";
+		};
+		port20: port@20 {
+			reg = <20>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy20>;
+			phy-mode = "qsgmii";
+		};
+		port21: port@21 {
+			reg = <21>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy21>;
+			phy-mode = "qsgmii";
+		};
+		port22: port@22 {
+			reg = <22>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy22>;
+			phy-mode = "qsgmii";
+		};
+		port23: port@23 {
+			reg = <23>;
+			max-speed = <1000>;
+			phys = <&serdes 18>;
+			phy-handle = <&phy23>;
+			phy-mode = "qsgmii";
+		};
+		port24: port@24 {
+			reg = <24>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy24>;
+			phy-mode = "qsgmii";
+		};
+		port25: port@25 {
+			reg = <25>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy25>;
+			phy-mode = "qsgmii";
+		};
+		port26: port@26 {
+			reg = <26>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy26>;
+			phy-mode = "qsgmii";
+		};
+		port27: port@27 {
+			reg = <27>;
+			max-speed = <1000>;
+			phys = <&serdes 19>;
+			phy-handle = <&phy27>;
+			phy-mode = "qsgmii";
+		};
+		port28: port@28 {
+			reg = <28>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy28>;
+			phy-mode = "qsgmii";
+		};
+		port29: port@29 {
+			reg = <29>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy29>;
+			phy-mode = "qsgmii";
+		};
+		port30: port@30 {
+			reg = <30>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy30>;
+			phy-mode = "qsgmii";
+		};
+		port31: port@31 {
+			reg = <31>;
+			max-speed = <1000>;
+			phys = <&serdes 20>;
+			phy-handle = <&phy31>;
+			phy-mode = "qsgmii";
+		};
+		port32: port@32 {
+			reg = <32>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy32>;
+			phy-mode = "qsgmii";
+		};
+		port33: port@33 {
+			reg = <33>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy33>;
+			phy-mode = "qsgmii";
+		};
+		port34: port@34 {
+			reg = <34>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy34>;
+			phy-mode = "qsgmii";
+		};
+		port35: port@35 {
+			reg = <35>;
+			max-speed = <1000>;
+			phys = <&serdes 21>;
+			phy-handle = <&phy35>;
+			phy-mode = "qsgmii";
+		};
+		port36: port@36 {
+			reg = <36>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy36>;
+			phy-mode = "qsgmii";
+		};
+		port37: port@37 {
+			reg = <37>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy37>;
+			phy-mode = "qsgmii";
+		};
+		port38: port@38 {
+			reg = <38>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy38>;
+			phy-mode = "qsgmii";
+		};
+		port39: port@39 {
+			reg = <39>;
+			max-speed = <1000>;
+			phys = <&serdes 22>;
+			phy-handle = <&phy39>;
+			phy-mode = "qsgmii";
+		};
+		port40: port@40 {
+			reg = <40>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy40>;
+			phy-mode = "qsgmii";
+		};
+		port41: port@41 {
+			reg = <41>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy41>;
+			phy-mode = "qsgmii";
+		};
+		port42: port@42 {
+			reg = <42>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy42>;
+			phy-mode = "qsgmii";
+		};
+		port43: port@43 {
+			reg = <43>;
+			max-speed = <1000>;
+			phys = <&serdes 23>;
+			phy-handle = <&phy43>;
+			phy-mode = "qsgmii";
+		};
+		port44: port@44 {
+			reg = <44>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy44>;
+			phy-mode = "qsgmii";
+		};
+		port45: port@45 {
+			reg = <45>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy45>;
+			phy-mode = "qsgmii";
+		};
+		port46: port@46 {
+			reg = <46>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy46>;
+			phy-mode = "qsgmii";
+		};
+		port47: port@47 {
+			reg = <47>;
+			max-speed = <1000>;
+			phys = <&serdes 24>;
+			phy-handle = <&phy47>;
+			phy-mode = "qsgmii";
+		};
+		/* Then the 25G interfaces */
+		port60: port@60 {
+			reg = <60>;
+			max-speed = <25000>;
+			phys = <&serdes 29>;
+			sfp = <&sfp_eth60>;
+			managed = "in-band-status";
+		};
+		port61: port@61 {
+			reg = <61>;
+			max-speed = <25000>;
+			phys = <&serdes 30>;
+			sfp = <&sfp_eth61>;
+			managed = "in-band-status";
+		};
+		port62: port@62 {
+			reg = <62>;
+			max-speed = <25000>;
+			phys = <&serdes 31>;
+			sfp = <&sfp_eth62>;
+			managed = "in-band-status";
+		};
+		port63: port@63 {
+			reg = <63>;
+			max-speed = <25000>;
+			phys = <&serdes 32>;
+			sfp = <&sfp_eth63>;
+			managed = "in-band-status";
+		};
+		/* Finally the Management interface */
+		port64: port@64 {
+			reg = <64>;
+			max-speed = <1000>;
+			phys = <&serdes 0>;
+			phy-handle = <&phy64>;
+			phy-mode = "sgmii";
+		};
+	};
+};
-- 
2.29.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-19 17:54     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 17:54 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>

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

    Andrew

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-19 17:54     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 17:54 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Device Tree List, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, Rob Herring,
	linux-arm-kernel, Mark Einon, Jakub Kicinski, David S. Miller,
	Lars Povlsen

On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>

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

    Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-17  7:51 ` [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver Steen Hegelund
@ 2020-12-19 19:11     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 19:11 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

On Thu, Dec 17, 2020 at 08:51:28AM +0100, Steen Hegelund wrote:

> +static struct sparx5_io_resource sparx5_iomap[] =  {

This could be made const i think,.

> +	{ TARGET_DEV2G5,         0,         0 }, /* 0x610004000: dev2g5_0 */
> +	{ TARGET_DEV5G,          0x4000,    0 }, /* 0x610008000: dev5g_0 */
> +	{ TARGET_PCS5G_BR,       0x8000,    0 }, /* 0x61000c000: pcs5g_br_0 */
> +	{ TARGET_DEV2G5 + 1,     0xc000,    0 }, /* 0x610010000: dev2g5_1 */

> +static int sparx5_create_targets(struct sparx5 *sparx5)
> +{
> +	int idx, jdx;
> +	struct resource *iores[IO_RANGES];
> +	void __iomem *iomem[IO_RANGES];
> +	void __iomem *begin[IO_RANGES];
> +	int range_id[IO_RANGES];

Reverse Christmas tree. idx, jdx need to come last.

> +
> +	/* Check if done previously (deferred by serdes load) */
> +	if (sparx5->regs[sparx5_iomap[0].id])
> +		return 0;

Could you explain this a bit more. Do you mean -EPROBE_DEFER?

> +static int sparx5_probe_port(struct sparx5 *sparx5,
> +			     struct device_node *portnp,
> +			     struct phy *serdes,
> +			     u32 portno,
> +			     struct sparx5_port_config *conf)
> +{
> +	struct sparx5_port *spx5_port;
> +	struct net_device *ndev;
> +	int err;
> +
> +	err = sparx5_create_targets(sparx5);
> +	if (err)
> +		return err;

This sees odd here. Don't sparx5_create_targets() create all the
targets, where as this creates one specific port? Seems like
sparx5_create_targets() should be in the devices as a whole probe, not
the port probe.

> +	spx5_port = netdev_priv(ndev);
> +	spx5_port->of_node = portnp;
> +	spx5_port->serdes = serdes;
> +	spx5_port->pvid = NULL_VID;
> +	spx5_port->signd_internal = true;
> +	spx5_port->signd_active_high = true;
> +	spx5_port->signd_enable = true;
> +	spx5_port->flow_control = false;
> +	spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
> +	spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
> +	spx5_port->custom_etype = 0x8880; /* Vitesse */
> +	conf->portmode = conf->phy_mode;
> +	spx5_port->conf.speed = SPEED_UNKNOWN;
> +	spx5_port->conf.power_down = true;
> +	sparx5->ports[portno] = spx5_port;
> +	return 0;

I'm also not sure this has the correct name. This does not look like a
typical probe function.


> +}
> +
> +static int sparx5_init_switchcore(struct sparx5 *sparx5)
> +{
> +	u32 value, pending, jdx, idx;
> +	struct {
> +		bool gazwrap;
> +		void __iomem *init_reg;
> +		u32  init_val;
> +	} ram, ram_init_list[] = {
> +		{false, spx5_reg_get(sparx5, ANA_AC_STAT_RESET),
> +		 ANA_AC_STAT_RESET_RESET},
> +		{false, spx5_reg_get(sparx5, ASM_STAT_CFG),
> +		 ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
> +		{true,  spx5_reg_get(sparx5, QSYS_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, REW_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, VOP_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, ANA_AC_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, ASM_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, EACL_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, VCAP_SUPER_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, DSM_RAM_INIT), 0}
> +	};

Looks like this could be const as well. And this does not really fit
reverse christmas tree.

> +
> +	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
> +		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> +		 sparx5,
> +		 EACL_POL_EACL_CFG);
> +
> +	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
> +		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> +		 sparx5,
> +		 EACL_POL_EACL_CFG);
> +
> +	/* Initialize memories, if not done already */
> +	value = spx5_rd(sparx5, HSCH_RESET_CFG);
> +
> +	if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
> +		for (idx = 0; idx < 10; idx++) {
> +			pending = ARRAY_SIZE(ram_init_list);
> +			for (jdx = 0; jdx < ARRAY_SIZE(ram_init_list); jdx++) {
> +				ram = ram_init_list[jdx];
> +				if (ram.gazwrap)
> +					ram.init_val = QSYS_RAM_INIT_RAM_INIT;
> +
> +				if (idx == 0) {
> +					writel(ram.init_val, ram.init_reg);
> +				} else {
> +					value = readl(ram.init_reg);
> +					if ((value & ram.init_val) !=
> +					    ram.init_val) {
> +						pending--;
> +					}
> +				}
> +			}
> +			if (!pending)
> +				break;
> +			usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
> +		}

You are getting pretty deeply nested here. Might be better to pull
this out into a helpers.

> +
> +		if (pending > 0) {
> +			/* Still initializing, should be complete in
> +			 * less than 1ms
> +			 */
> +			dev_err(sparx5->dev, "Memory initialization error\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Reset counters */
> +	spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
> +	spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
> +
> +	/* Enable switch-core and queue system */
> +	spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
> +
> +	return 0;
> +}
> +
> +static int sparx5_init_coreclock(struct sparx5 *sparx5)
> +{
> +	u32 clk_div, clk_period, pol_upd_int, idx;
> +	enum sparx5_core_clockfreq freq = sparx5->coreclock;

More reverse christmas tree. Please review the whole driver.

> +
> +	/* Verify if core clock frequency is supported on target.
> +	 * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
> +	 * freq. is used
> +	 */
> +	switch (sparx5->target_ct) {
> +	case SPX5_TARGET_CT_7546:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_250MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7549:
> +	case SPX5_TARGET_CT_7552:
> +	case SPX5_TARGET_CT_7556:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_500MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7558:
> +	case SPX5_TARGET_CT_7558TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7546TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		break;
> +	case SPX5_TARGET_CT_7549TSN:
> +	case SPX5_TARGET_CT_7552TSN:
> +	case SPX5_TARGET_CT_7556TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	default:
> +		dev_err(sparx5->dev, "Target (%#04x) not supported\n", sparx5->target_ct);

netdev is staying with 80 character lines. Please fold this, here and
every where else, where possible. The exception is, you should not
split a string.

> +		return -ENODEV;
> +	}
> +
> +	switch (freq) {
> +	case SPX5_CORE_CLOCK_250MHZ:
> +		clk_div = 10;
> +		pol_upd_int = 312;
> +		break;
> +	case SPX5_CORE_CLOCK_500MHZ:
> +		clk_div = 5;
> +		pol_upd_int = 624;
> +		break;
> +	case SPX5_CORE_CLOCK_625MHZ:
> +		clk_div = 4;
> +		pol_upd_int = 780;
> +		break;
> +	default:
> +		dev_err(sparx5->dev, "%s: Frequency (%d) not supported on target (%#04x)\n",
> +			__func__,
> +			sparx5->coreclock, sparx5->target_ct);
> +		return 0;

-EINVAL? Or is it not fatal to use an unsupported frequency?

> +static int sparx5_init(struct sparx5 *sparx5)
> +{
> +	u32 idx;
> +
> +	if (sparx5_create_targets(sparx5))
> +		return -ENODEV;

Hum, sparx5_create_targets() again?

> +
> +	/* Read chip ID to check CPU interface */
> +	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> +
> +	sparx5->target_ct = (enum spx5_target_chiptype)
> +		GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> +
> +	/* Initialize Switchcore and internal RAMs */
> +	if (sparx5_init_switchcore(sparx5)) {
> +		dev_err(sparx5->dev, "Switchcore initialization error\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Initialize the LC-PLL (core clock) and set affected registers */
> +	if (sparx5_init_coreclock(sparx5)) {
> +		dev_err(sparx5->dev, "LC-PLL initialization error\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Setup own UPSIDs */
> +	for (idx = 0; idx < 3; idx++) {
> +		spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
> +	}
> +
> +	/* Enable switch ports */
> +	for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++) {
> +		spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
> +			 QFWD_SWITCH_PORT_MODE_PORT_ENA,
> +			 sparx5,
> +			 QFWD_SWITCH_PORT_MODE(idx));
> +	}

What happens when you enable the ports? Why is this here, and not in
the port specific open call?

> +/* Some boards needs to map the SGPIO for signal detect explicitly to the
> + * port module
> + */
> +static void sparx5_board_init(struct sparx5 *sparx5)
> +{
> +	int idx;
> +
> +	if (!sparx5->sd_sgpio_remapping)
> +		return;
> +
> +	/* Enable SGPIO Signal Detect remapping */
> +	spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> +		 GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> +		 sparx5,
> +		 GCB_HW_SGPIO_SD_CFG);
> +
> +	/* Refer to LOS SGPIO */
> +	for (idx = 0; idx < SPX5_PORTS; idx++) {
> +		if (sparx5->ports[idx]) {
> +			if (sparx5->ports[idx]->conf.sd_sgpio != ~0) {
> +				spx5_wr(sparx5->ports[idx]->conf.sd_sgpio,
> +					sparx5,
> +					GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> +			}
> +		}
> +	}
> +}

I've not looked at how you do SFP integration yet. Is this the LOS
from the SFP socket? Is there a Linux GPIO controller exported by this
driver, so the SFP driver can use the GPIOs?

> +
> +static int mchp_sparx5_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct sparx5 *sparx5;
> +	struct device_node *ports, *portnp;
> +	const u8 *mac_addr;
> +	int err = 0;
> +
> +	if (!np && !pdev->dev.platform_data)
> +		return -ENODEV;
> +
> +	sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5), GFP_KERNEL);
> +	if (!sparx5)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, sparx5);
> +	sparx5->pdev = pdev;
> +	sparx5->dev = &pdev->dev;
> +
> +	/* Default values, some from DT */
> +	sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> +
> +	mac_addr = of_get_mac_address(np);
> +	if (IS_ERR_OR_NULL(mac_addr)) {
> +		dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
> +		eth_random_addr(sparx5->base_mac);
> +		sparx5->base_mac[5] = 0;
> +	} else {
> +		ether_addr_copy(sparx5->base_mac, mac_addr);
> +	}

The binding document does not say anything about a MAC address at the
top level. What is this used for?

+
> +	if (sparx5_init(sparx5)) {
> +		dev_err(sparx5->dev, "Init failed\n");
> +		return -ENODEV;
> +	}
> +	ports = of_get_child_by_name(np, "ethernet-ports");
> +	if (!ports) {
> +		dev_err(sparx5->dev, "no ethernet-ports child node found\n");
> +		return -ENODEV;
> +	}
> +	sparx5->port_count = of_get_child_count(ports);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct sparx5_port_config config = {};
> +		u32 portno;
> +		struct phy *serdes;
> +
> +		err = of_property_read_u32(portnp, "reg", &portno);
> +		if (err) {
> +			dev_err(sparx5->dev, "port reg property error\n");
> +			continue;
> +		}
> +		err = of_property_read_u32(portnp, "max-speed",
> +					   &config.max_speed);
> +		if (err) {
> +			dev_err(sparx5->dev, "port max-speed property error\n");
> +			continue;
> +		}
> +		config.speed = SPEED_UNKNOWN;
> +		err = of_property_read_u32(portnp, "sd_sgpio", &config.sd_sgpio);

Not in the binding documentation. I think i need to withdraw my Reviewed-by :-(

> +		if (err)
> +			config.sd_sgpio = ~0;
> +		else
> +			sparx5->sd_sgpio_remapping = true;
> +		serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err != -EPROBE_DEFER)
> +				dev_err(sparx5->dev,
> +					"missing SerDes phys for port%d\n",
> +					portno);
> +			return err;
> +		}
> +
> +		err = of_get_phy_mode(portnp, &config.phy_mode);
> +		if (err)
> +			config.power_down = true;

You should indicate in the binding it is optional. And what happens
when it is missing.

> +		config.media_type = ETH_MEDIA_DAC;
> +		config.serdes_reset = true;
> +		config.portmode = config.phy_mode;
> +		err = sparx5_probe_port(sparx5, portnp, serdes, portno, &config);
> +		if (err) {
> +			dev_err(sparx5->dev, "port probe error\n");
> +			goto cleanup_ports;
> +		}
> +	}
> +	sparx5_board_init(sparx5);
> +
> +cleanup_ports:
> +	return err;

Seems missed named, no cleanup.

> +static int __init sparx5_switch_reset(void)
> +{
> +	const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> +		*syscon_gcb = "microchip,sparx5-gcb-syscon";
> +	struct regmap *cpu_ctrl, *gcb_ctrl;
> +	u32 val;
> +
> +	cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> +	if (IS_ERR(cpu_ctrl)) {
> +		pr_err("No '%s' syscon map\n", syscon_cpu);
> +		return PTR_ERR(cpu_ctrl);
> +	}
> +
> +	gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> +	if (IS_ERR(gcb_ctrl)) {
> +		pr_err("No '%s' syscon map\n", syscon_gcb);
> +		return PTR_ERR(gcb_ctrl);
> +	}
> +
> +	/* Make sure the core is PROTECTED from reset */
> +	regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> +			   SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> +
> +	regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> +		     GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> +
> +	return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl, val,
> +				  GCB_SOFT_RST_SOFT_SWC_RST_GET(val) == 0,
> +				  1, 100);
> +}
> +postcore_initcall(sparx5_switch_reset);

That is pretty unusual. Why cannot this be done at probe time?

> +/* Clock period in picoseconds */
> +static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
> +{
> +	switch (cclock) {
> +	case SPX5_CORE_CLOCK_250MHZ:
> +		return 4000;
> +	case SPX5_CORE_CLOCK_500MHZ:
> +		return 2000;
> +	case SPX5_CORE_CLOCK_625MHZ:
> +	default:
> +		return 1600;
> +	}
> +}

Is this something which is used in the hot path?

> --- /dev/null
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> @@ -0,0 +1,3922 @@
> +/* SPDX-License-Identifier: GPL-2.0+
> + * Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc.
> + */
> +
> +/* This file is autogenerated by cml-utils 2020-11-19 10:41:34 +0100.
> + * Commit ID: f34790e69dc252103e2cc3e85b1a5e4d9e3aa190
> + */

How reproducible this is generation process? If you have to run it
again, will it keep the same order of lines?

       Andrew 

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-19 19:11     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 19:11 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

On Thu, Dec 17, 2020 at 08:51:28AM +0100, Steen Hegelund wrote:

> +static struct sparx5_io_resource sparx5_iomap[] =  {

This could be made const i think,.

> +	{ TARGET_DEV2G5,         0,         0 }, /* 0x610004000: dev2g5_0 */
> +	{ TARGET_DEV5G,          0x4000,    0 }, /* 0x610008000: dev5g_0 */
> +	{ TARGET_PCS5G_BR,       0x8000,    0 }, /* 0x61000c000: pcs5g_br_0 */
> +	{ TARGET_DEV2G5 + 1,     0xc000,    0 }, /* 0x610010000: dev2g5_1 */

> +static int sparx5_create_targets(struct sparx5 *sparx5)
> +{
> +	int idx, jdx;
> +	struct resource *iores[IO_RANGES];
> +	void __iomem *iomem[IO_RANGES];
> +	void __iomem *begin[IO_RANGES];
> +	int range_id[IO_RANGES];

Reverse Christmas tree. idx, jdx need to come last.

> +
> +	/* Check if done previously (deferred by serdes load) */
> +	if (sparx5->regs[sparx5_iomap[0].id])
> +		return 0;

Could you explain this a bit more. Do you mean -EPROBE_DEFER?

> +static int sparx5_probe_port(struct sparx5 *sparx5,
> +			     struct device_node *portnp,
> +			     struct phy *serdes,
> +			     u32 portno,
> +			     struct sparx5_port_config *conf)
> +{
> +	struct sparx5_port *spx5_port;
> +	struct net_device *ndev;
> +	int err;
> +
> +	err = sparx5_create_targets(sparx5);
> +	if (err)
> +		return err;

This sees odd here. Don't sparx5_create_targets() create all the
targets, where as this creates one specific port? Seems like
sparx5_create_targets() should be in the devices as a whole probe, not
the port probe.

> +	spx5_port = netdev_priv(ndev);
> +	spx5_port->of_node = portnp;
> +	spx5_port->serdes = serdes;
> +	spx5_port->pvid = NULL_VID;
> +	spx5_port->signd_internal = true;
> +	spx5_port->signd_active_high = true;
> +	spx5_port->signd_enable = true;
> +	spx5_port->flow_control = false;
> +	spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
> +	spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
> +	spx5_port->custom_etype = 0x8880; /* Vitesse */
> +	conf->portmode = conf->phy_mode;
> +	spx5_port->conf.speed = SPEED_UNKNOWN;
> +	spx5_port->conf.power_down = true;
> +	sparx5->ports[portno] = spx5_port;
> +	return 0;

I'm also not sure this has the correct name. This does not look like a
typical probe function.


> +}
> +
> +static int sparx5_init_switchcore(struct sparx5 *sparx5)
> +{
> +	u32 value, pending, jdx, idx;
> +	struct {
> +		bool gazwrap;
> +		void __iomem *init_reg;
> +		u32  init_val;
> +	} ram, ram_init_list[] = {
> +		{false, spx5_reg_get(sparx5, ANA_AC_STAT_RESET),
> +		 ANA_AC_STAT_RESET_RESET},
> +		{false, spx5_reg_get(sparx5, ASM_STAT_CFG),
> +		 ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
> +		{true,  spx5_reg_get(sparx5, QSYS_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, REW_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, VOP_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, ANA_AC_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, ASM_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, EACL_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, VCAP_SUPER_RAM_INIT), 0},
> +		{true,  spx5_reg_get(sparx5, DSM_RAM_INIT), 0}
> +	};

Looks like this could be const as well. And this does not really fit
reverse christmas tree.

> +
> +	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
> +		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> +		 sparx5,
> +		 EACL_POL_EACL_CFG);
> +
> +	spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
> +		 EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> +		 sparx5,
> +		 EACL_POL_EACL_CFG);
> +
> +	/* Initialize memories, if not done already */
> +	value = spx5_rd(sparx5, HSCH_RESET_CFG);
> +
> +	if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
> +		for (idx = 0; idx < 10; idx++) {
> +			pending = ARRAY_SIZE(ram_init_list);
> +			for (jdx = 0; jdx < ARRAY_SIZE(ram_init_list); jdx++) {
> +				ram = ram_init_list[jdx];
> +				if (ram.gazwrap)
> +					ram.init_val = QSYS_RAM_INIT_RAM_INIT;
> +
> +				if (idx == 0) {
> +					writel(ram.init_val, ram.init_reg);
> +				} else {
> +					value = readl(ram.init_reg);
> +					if ((value & ram.init_val) !=
> +					    ram.init_val) {
> +						pending--;
> +					}
> +				}
> +			}
> +			if (!pending)
> +				break;
> +			usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
> +		}

You are getting pretty deeply nested here. Might be better to pull
this out into a helpers.

> +
> +		if (pending > 0) {
> +			/* Still initializing, should be complete in
> +			 * less than 1ms
> +			 */
> +			dev_err(sparx5->dev, "Memory initialization error\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Reset counters */
> +	spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5, ANA_AC_STAT_RESET);
> +	spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5, ASM_STAT_CFG);
> +
> +	/* Enable switch-core and queue system */
> +	spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5, HSCH_RESET_CFG);
> +
> +	return 0;
> +}
> +
> +static int sparx5_init_coreclock(struct sparx5 *sparx5)
> +{
> +	u32 clk_div, clk_period, pol_upd_int, idx;
> +	enum sparx5_core_clockfreq freq = sparx5->coreclock;

More reverse christmas tree. Please review the whole driver.

> +
> +	/* Verify if core clock frequency is supported on target.
> +	 * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
> +	 * freq. is used
> +	 */
> +	switch (sparx5->target_ct) {
> +	case SPX5_TARGET_CT_7546:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_250MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7549:
> +	case SPX5_TARGET_CT_7552:
> +	case SPX5_TARGET_CT_7556:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_500MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7558:
> +	case SPX5_TARGET_CT_7558TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	case SPX5_TARGET_CT_7546TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		break;
> +	case SPX5_TARGET_CT_7549TSN:
> +	case SPX5_TARGET_CT_7552TSN:
> +	case SPX5_TARGET_CT_7556TSN:
> +		if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> +			freq = SPX5_CORE_CLOCK_625MHZ;
> +		else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
> +			freq = 0; /* Not supported */
> +		break;
> +	default:
> +		dev_err(sparx5->dev, "Target (%#04x) not supported\n", sparx5->target_ct);

netdev is staying with 80 character lines. Please fold this, here and
every where else, where possible. The exception is, you should not
split a string.

> +		return -ENODEV;
> +	}
> +
> +	switch (freq) {
> +	case SPX5_CORE_CLOCK_250MHZ:
> +		clk_div = 10;
> +		pol_upd_int = 312;
> +		break;
> +	case SPX5_CORE_CLOCK_500MHZ:
> +		clk_div = 5;
> +		pol_upd_int = 624;
> +		break;
> +	case SPX5_CORE_CLOCK_625MHZ:
> +		clk_div = 4;
> +		pol_upd_int = 780;
> +		break;
> +	default:
> +		dev_err(sparx5->dev, "%s: Frequency (%d) not supported on target (%#04x)\n",
> +			__func__,
> +			sparx5->coreclock, sparx5->target_ct);
> +		return 0;

-EINVAL? Or is it not fatal to use an unsupported frequency?

> +static int sparx5_init(struct sparx5 *sparx5)
> +{
> +	u32 idx;
> +
> +	if (sparx5_create_targets(sparx5))
> +		return -ENODEV;

Hum, sparx5_create_targets() again?

> +
> +	/* Read chip ID to check CPU interface */
> +	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> +
> +	sparx5->target_ct = (enum spx5_target_chiptype)
> +		GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> +
> +	/* Initialize Switchcore and internal RAMs */
> +	if (sparx5_init_switchcore(sparx5)) {
> +		dev_err(sparx5->dev, "Switchcore initialization error\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Initialize the LC-PLL (core clock) and set affected registers */
> +	if (sparx5_init_coreclock(sparx5)) {
> +		dev_err(sparx5->dev, "LC-PLL initialization error\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Setup own UPSIDs */
> +	for (idx = 0; idx < 3; idx++) {
> +		spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
> +		spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
> +	}
> +
> +	/* Enable switch ports */
> +	for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++) {
> +		spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
> +			 QFWD_SWITCH_PORT_MODE_PORT_ENA,
> +			 sparx5,
> +			 QFWD_SWITCH_PORT_MODE(idx));
> +	}

What happens when you enable the ports? Why is this here, and not in
the port specific open call?

> +/* Some boards needs to map the SGPIO for signal detect explicitly to the
> + * port module
> + */
> +static void sparx5_board_init(struct sparx5 *sparx5)
> +{
> +	int idx;
> +
> +	if (!sparx5->sd_sgpio_remapping)
> +		return;
> +
> +	/* Enable SGPIO Signal Detect remapping */
> +	spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> +		 GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> +		 sparx5,
> +		 GCB_HW_SGPIO_SD_CFG);
> +
> +	/* Refer to LOS SGPIO */
> +	for (idx = 0; idx < SPX5_PORTS; idx++) {
> +		if (sparx5->ports[idx]) {
> +			if (sparx5->ports[idx]->conf.sd_sgpio != ~0) {
> +				spx5_wr(sparx5->ports[idx]->conf.sd_sgpio,
> +					sparx5,
> +					GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> +			}
> +		}
> +	}
> +}

I've not looked at how you do SFP integration yet. Is this the LOS
from the SFP socket? Is there a Linux GPIO controller exported by this
driver, so the SFP driver can use the GPIOs?

> +
> +static int mchp_sparx5_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct sparx5 *sparx5;
> +	struct device_node *ports, *portnp;
> +	const u8 *mac_addr;
> +	int err = 0;
> +
> +	if (!np && !pdev->dev.platform_data)
> +		return -ENODEV;
> +
> +	sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5), GFP_KERNEL);
> +	if (!sparx5)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, sparx5);
> +	sparx5->pdev = pdev;
> +	sparx5->dev = &pdev->dev;
> +
> +	/* Default values, some from DT */
> +	sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> +
> +	mac_addr = of_get_mac_address(np);
> +	if (IS_ERR_OR_NULL(mac_addr)) {
> +		dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
> +		eth_random_addr(sparx5->base_mac);
> +		sparx5->base_mac[5] = 0;
> +	} else {
> +		ether_addr_copy(sparx5->base_mac, mac_addr);
> +	}

The binding document does not say anything about a MAC address at the
top level. What is this used for?

+
> +	if (sparx5_init(sparx5)) {
> +		dev_err(sparx5->dev, "Init failed\n");
> +		return -ENODEV;
> +	}
> +	ports = of_get_child_by_name(np, "ethernet-ports");
> +	if (!ports) {
> +		dev_err(sparx5->dev, "no ethernet-ports child node found\n");
> +		return -ENODEV;
> +	}
> +	sparx5->port_count = of_get_child_count(ports);
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct sparx5_port_config config = {};
> +		u32 portno;
> +		struct phy *serdes;
> +
> +		err = of_property_read_u32(portnp, "reg", &portno);
> +		if (err) {
> +			dev_err(sparx5->dev, "port reg property error\n");
> +			continue;
> +		}
> +		err = of_property_read_u32(portnp, "max-speed",
> +					   &config.max_speed);
> +		if (err) {
> +			dev_err(sparx5->dev, "port max-speed property error\n");
> +			continue;
> +		}
> +		config.speed = SPEED_UNKNOWN;
> +		err = of_property_read_u32(portnp, "sd_sgpio", &config.sd_sgpio);

Not in the binding documentation. I think i need to withdraw my Reviewed-by :-(

> +		if (err)
> +			config.sd_sgpio = ~0;
> +		else
> +			sparx5->sd_sgpio_remapping = true;
> +		serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err != -EPROBE_DEFER)
> +				dev_err(sparx5->dev,
> +					"missing SerDes phys for port%d\n",
> +					portno);
> +			return err;
> +		}
> +
> +		err = of_get_phy_mode(portnp, &config.phy_mode);
> +		if (err)
> +			config.power_down = true;

You should indicate in the binding it is optional. And what happens
when it is missing.

> +		config.media_type = ETH_MEDIA_DAC;
> +		config.serdes_reset = true;
> +		config.portmode = config.phy_mode;
> +		err = sparx5_probe_port(sparx5, portnp, serdes, portno, &config);
> +		if (err) {
> +			dev_err(sparx5->dev, "port probe error\n");
> +			goto cleanup_ports;
> +		}
> +	}
> +	sparx5_board_init(sparx5);
> +
> +cleanup_ports:
> +	return err;

Seems missed named, no cleanup.

> +static int __init sparx5_switch_reset(void)
> +{
> +	const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> +		*syscon_gcb = "microchip,sparx5-gcb-syscon";
> +	struct regmap *cpu_ctrl, *gcb_ctrl;
> +	u32 val;
> +
> +	cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> +	if (IS_ERR(cpu_ctrl)) {
> +		pr_err("No '%s' syscon map\n", syscon_cpu);
> +		return PTR_ERR(cpu_ctrl);
> +	}
> +
> +	gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> +	if (IS_ERR(gcb_ctrl)) {
> +		pr_err("No '%s' syscon map\n", syscon_gcb);
> +		return PTR_ERR(gcb_ctrl);
> +	}
> +
> +	/* Make sure the core is PROTECTED from reset */
> +	regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> +			   SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> +
> +	regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> +		     GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> +
> +	return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl, val,
> +				  GCB_SOFT_RST_SOFT_SWC_RST_GET(val) == 0,
> +				  1, 100);
> +}
> +postcore_initcall(sparx5_switch_reset);

That is pretty unusual. Why cannot this be done at probe time?

> +/* Clock period in picoseconds */
> +static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
> +{
> +	switch (cclock) {
> +	case SPX5_CORE_CLOCK_250MHZ:
> +		return 4000;
> +	case SPX5_CORE_CLOCK_500MHZ:
> +		return 2000;
> +	case SPX5_CORE_CLOCK_625MHZ:
> +	default:
> +		return 1600;
> +	}
> +}

Is this something which is used in the hot path?

> --- /dev/null
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> @@ -0,0 +1,3922 @@
> +/* SPDX-License-Identifier: GPL-2.0+
> + * Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc.
> + */
> +
> +/* This file is autogenerated by cml-utils 2020-11-19 10:41:34 +0100.
> + * Commit ID: f34790e69dc252103e2cc3e85b1a5e4d9e3aa190
> + */

How reproducible this is generation process? If you have to run it
again, will it keep the same order of lines?

       Andrew 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-19 19:51     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 19:51 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

> +	/* Create a phylink for PHY management.  Also handles SFPs */
> +	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> +	spx5_port->phylink_config.type = PHYLINK_NETDEV;
> +	spx5_port->phylink_config.pcs_poll = true;
> +
> +	/* phylink needs a valid interface mode to parse dt node */
> +	if (phy_mode == PHY_INTERFACE_MODE_NA)
> +		phy_mode = PHY_INTERFACE_MODE_10GBASER;

Maybe just enforce a valid value in DT?

> +/* Configuration */
> +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> +{
> +	return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> +}

That is a rather odd definition of copper.

> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> new file mode 100644
> index 000000000000..6f9282e9d3f4
> --- /dev/null
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> @@ -0,0 +1,203 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
> + */
> +
> +#include "sparx5_main.h"

I don't actually know what is preferred here, but very few drivers
i've reviewed put all the required headers into another header
file. They normally list them in each .c file.

> +static int sparx5_port_open(struct net_device *ndev)
> +{
> +	struct sparx5_port *port = netdev_priv(ndev);
> +	int err = 0;
> +
> +	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
> +	if (err) {
> +		netdev_err(ndev, "Could not attach to PHY\n");
> +		return err;
> +	}
> +
> +	phylink_start(port->phylink);
> +
> +	if (!ndev->phydev) {

Humm. When is ndev->phydev set? I don't think phylink ever sets it.

> +		/* power up serdes */
> +		port->conf.power_down = false;
> +		err = phy_power_on(port->serdes);
> +		if (err)
> +			netdev_err(ndev, "%s failed\n", __func__);
> +	}
> +
> +	return err;
> +}

> +struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
> +{
> +	struct net_device *ndev;
> +	struct sparx5_port *spx5_port;
> +
> +	ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port));
> +	if (!ndev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	SET_NETDEV_DEV(ndev, sparx5->dev);
> +	spx5_port = netdev_priv(ndev);
> +	spx5_port->ndev = ndev;
> +	spx5_port->sparx5 = sparx5;
> +	spx5_port->portno = portno;
> +	sparx5_set_port_ifh(spx5_port->ifh, portno);
> +	snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
> +
> +	ether_setup(ndev);

devm_alloc_etherdev() should of already called ether_setup().

> +	ndev->netdev_ops = &sparx5_port_netdev_ops;
> +	ndev->features |= NETIF_F_LLTX; /* software tx */
> +
> +	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
> +	ndev->dev_addr[ETH_ALEN - 1] += portno + 1;

That will cause some surprises with wrap around. Use eth_addr_inc()

> +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
> +{
> +	int i, byte_cnt = 0;
> +	bool eof_flag = false, pruned_flag = false, abort_flag = false;
> +	u32 ifh[IFH_LEN];
> +	struct sk_buff *skb;
> +	struct frame_info fi;
> +	struct sparx5_port *port;
> +	struct net_device *netdev;
> +	u32 *rxbuf;
> +
> +	/* Get IFH */
> +	for (i = 0; i < IFH_LEN; i++)
> +		ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> +
> +	/* Decode IFH (whats needed) */
> +	sparx5_ifh_parse(ifh, &fi);
> +
> +	/* Map to port netdev */
> +	port = fi.src_port < SPX5_PORTS ?
> +		sparx5->ports[fi.src_port] : NULL;
> +	if (!port || !port->ndev) {
> +		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
> +		sparx5_xtr_flush(sparx5, grp);
> +		return;
> +	}
> +
> +	/* Have netdev, get skb */
> +	netdev = port->ndev;
> +	skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> +	if (!skb) {
> +		sparx5_xtr_flush(sparx5, grp);
> +		dev_err(sparx5->dev, "No skb allocated\n");
> +		return;
> +	}
> +	rxbuf = (u32 *)skb->data;
> +
> +	/* Now, pull frame data */
> +	while (!eof_flag) {
> +		u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> +		u32 cmp = val;
> +
> +		if (byte_swap)
> +			cmp = ntohl((__force __be32)val);
> +
> +		switch (cmp) {
> +		case XTR_NOT_READY:
> +			break;
> +		case XTR_ABORT:
> +			/* No accompanying data */
> +			abort_flag = true;
> +			eof_flag = true;
> +			break;
> +		case XTR_EOF_0:
> +		case XTR_EOF_1:
> +		case XTR_EOF_2:
> +		case XTR_EOF_3:
> +			/* This assumes STATUS_WORD_POS == 1, Status
> +			 * just after last data
> +			 */
> +			byte_cnt -= (4 - XTR_VALID_BYTES(val));
> +			eof_flag = true;
> +			break;
> +		case XTR_PRUNED:
> +			/* But get the last 4 bytes as well */
> +			eof_flag = true;
> +			pruned_flag = true;
> +			fallthrough;
> +		case XTR_ESCAPE:
> +			*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> +			byte_cnt += 4;
> +			rxbuf++;
> +			break;
> +		default:
> +			*rxbuf = val;
> +			byte_cnt += 4;
> +			rxbuf++;
> +		}
> +	}
> +
> +	if (abort_flag || pruned_flag || !eof_flag) {
> +		netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
> +			   abort_flag, pruned_flag, eof_flag);
> +		kfree_skb(skb);
> +		return;
> +	}
> +
> +	if (!netif_oper_up(netdev)) {
> +		netdev_err(netdev, "Discarded frame: Interface not up\n");
> +		kfree_skb(skb);
> +		return;
> +	}

Why is it sending frames when it is not up?

> +static int sparx5_inject(struct sparx5 *sparx5,
> +			 u32 *ifh,
> +			 struct sk_buff *skb)
> +{
> +	u32 val, w, count;
> +	int grp = INJ_QUEUE;
> +	u8 *buf;
> +
> +	val = spx5_rd(sparx5, QS_INJ_STATUS);
> +	if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
> +		pr_err("Injection: Queue not ready: 0x%lx\n",
> +		       QS_INJ_STATUS_FIFO_RDY_GET(val));
> +		return -1;

Always use -ESOMETHING.

> +	}
> +
> +	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
> +		pr_err("Injection: Watermark reached: 0x%lx\n",
> +		       QS_INJ_STATUS_WMARK_REACHED_GET(val));
> +		return -1;
> +	}
> +
> +	/* Indicate SOF */
> +	spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
> +		QS_INJ_CTRL_GAP_SIZE_SET(1),
> +		sparx5, QS_INJ_CTRL(grp));
> +
> +	// Write the IFH to the chip.
> +	for (w = 0; w < IFH_LEN; w++)
> +		spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
> +
> +	/* Write words, round up */
> +	count = ((skb->len + 3) / 4);
> +	buf = skb->data;
> +	for (w = 0; w < count; w++, buf += 4) {
> +		val = get_unaligned((const u32 *)buf);
> +		spx5_wr(val, sparx5, QS_INJ_WR(grp));
> +	}

No DMA? What sort of performance do you get? Enough for the odd BPDU,
IGMP frame etc, but i guess you don't want any real bulk data to be
sent this way?

> +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> +{
> +	struct sparx5 *sparx5 = _sparx5;
> +
> +	/* Check data in queue */
> +	while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> +		sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> +
> +	return IRQ_HANDLED;
> +}

Is there any sort of limit how many times this will loop? If somebody
is blasting 10Gbps at the CPU, will it ever get out of this loop?

   Andrew

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-19 19:51     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 19:51 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

> +	/* Create a phylink for PHY management.  Also handles SFPs */
> +	spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> +	spx5_port->phylink_config.type = PHYLINK_NETDEV;
> +	spx5_port->phylink_config.pcs_poll = true;
> +
> +	/* phylink needs a valid interface mode to parse dt node */
> +	if (phy_mode == PHY_INTERFACE_MODE_NA)
> +		phy_mode = PHY_INTERFACE_MODE_10GBASER;

Maybe just enforce a valid value in DT?

> +/* Configuration */
> +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> +{
> +	return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> +}

That is a rather odd definition of copper.

> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> new file mode 100644
> index 000000000000..6f9282e9d3f4
> --- /dev/null
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> @@ -0,0 +1,203 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
> + */
> +
> +#include "sparx5_main.h"

I don't actually know what is preferred here, but very few drivers
i've reviewed put all the required headers into another header
file. They normally list them in each .c file.

> +static int sparx5_port_open(struct net_device *ndev)
> +{
> +	struct sparx5_port *port = netdev_priv(ndev);
> +	int err = 0;
> +
> +	err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
> +	if (err) {
> +		netdev_err(ndev, "Could not attach to PHY\n");
> +		return err;
> +	}
> +
> +	phylink_start(port->phylink);
> +
> +	if (!ndev->phydev) {

Humm. When is ndev->phydev set? I don't think phylink ever sets it.

> +		/* power up serdes */
> +		port->conf.power_down = false;
> +		err = phy_power_on(port->serdes);
> +		if (err)
> +			netdev_err(ndev, "%s failed\n", __func__);
> +	}
> +
> +	return err;
> +}

> +struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
> +{
> +	struct net_device *ndev;
> +	struct sparx5_port *spx5_port;
> +
> +	ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port));
> +	if (!ndev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	SET_NETDEV_DEV(ndev, sparx5->dev);
> +	spx5_port = netdev_priv(ndev);
> +	spx5_port->ndev = ndev;
> +	spx5_port->sparx5 = sparx5;
> +	spx5_port->portno = portno;
> +	sparx5_set_port_ifh(spx5_port->ifh, portno);
> +	snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
> +
> +	ether_setup(ndev);

devm_alloc_etherdev() should of already called ether_setup().

> +	ndev->netdev_ops = &sparx5_port_netdev_ops;
> +	ndev->features |= NETIF_F_LLTX; /* software tx */
> +
> +	ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
> +	ndev->dev_addr[ETH_ALEN - 1] += portno + 1;

That will cause some surprises with wrap around. Use eth_addr_inc()

> +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
> +{
> +	int i, byte_cnt = 0;
> +	bool eof_flag = false, pruned_flag = false, abort_flag = false;
> +	u32 ifh[IFH_LEN];
> +	struct sk_buff *skb;
> +	struct frame_info fi;
> +	struct sparx5_port *port;
> +	struct net_device *netdev;
> +	u32 *rxbuf;
> +
> +	/* Get IFH */
> +	for (i = 0; i < IFH_LEN; i++)
> +		ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> +
> +	/* Decode IFH (whats needed) */
> +	sparx5_ifh_parse(ifh, &fi);
> +
> +	/* Map to port netdev */
> +	port = fi.src_port < SPX5_PORTS ?
> +		sparx5->ports[fi.src_port] : NULL;
> +	if (!port || !port->ndev) {
> +		dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
> +		sparx5_xtr_flush(sparx5, grp);
> +		return;
> +	}
> +
> +	/* Have netdev, get skb */
> +	netdev = port->ndev;
> +	skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> +	if (!skb) {
> +		sparx5_xtr_flush(sparx5, grp);
> +		dev_err(sparx5->dev, "No skb allocated\n");
> +		return;
> +	}
> +	rxbuf = (u32 *)skb->data;
> +
> +	/* Now, pull frame data */
> +	while (!eof_flag) {
> +		u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> +		u32 cmp = val;
> +
> +		if (byte_swap)
> +			cmp = ntohl((__force __be32)val);
> +
> +		switch (cmp) {
> +		case XTR_NOT_READY:
> +			break;
> +		case XTR_ABORT:
> +			/* No accompanying data */
> +			abort_flag = true;
> +			eof_flag = true;
> +			break;
> +		case XTR_EOF_0:
> +		case XTR_EOF_1:
> +		case XTR_EOF_2:
> +		case XTR_EOF_3:
> +			/* This assumes STATUS_WORD_POS == 1, Status
> +			 * just after last data
> +			 */
> +			byte_cnt -= (4 - XTR_VALID_BYTES(val));
> +			eof_flag = true;
> +			break;
> +		case XTR_PRUNED:
> +			/* But get the last 4 bytes as well */
> +			eof_flag = true;
> +			pruned_flag = true;
> +			fallthrough;
> +		case XTR_ESCAPE:
> +			*rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> +			byte_cnt += 4;
> +			rxbuf++;
> +			break;
> +		default:
> +			*rxbuf = val;
> +			byte_cnt += 4;
> +			rxbuf++;
> +		}
> +	}
> +
> +	if (abort_flag || pruned_flag || !eof_flag) {
> +		netdev_err(netdev, "Discarded frame: abort:%d pruned:%d eof:%d\n",
> +			   abort_flag, pruned_flag, eof_flag);
> +		kfree_skb(skb);
> +		return;
> +	}
> +
> +	if (!netif_oper_up(netdev)) {
> +		netdev_err(netdev, "Discarded frame: Interface not up\n");
> +		kfree_skb(skb);
> +		return;
> +	}

Why is it sending frames when it is not up?

> +static int sparx5_inject(struct sparx5 *sparx5,
> +			 u32 *ifh,
> +			 struct sk_buff *skb)
> +{
> +	u32 val, w, count;
> +	int grp = INJ_QUEUE;
> +	u8 *buf;
> +
> +	val = spx5_rd(sparx5, QS_INJ_STATUS);
> +	if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
> +		pr_err("Injection: Queue not ready: 0x%lx\n",
> +		       QS_INJ_STATUS_FIFO_RDY_GET(val));
> +		return -1;

Always use -ESOMETHING.

> +	}
> +
> +	if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
> +		pr_err("Injection: Watermark reached: 0x%lx\n",
> +		       QS_INJ_STATUS_WMARK_REACHED_GET(val));
> +		return -1;
> +	}
> +
> +	/* Indicate SOF */
> +	spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
> +		QS_INJ_CTRL_GAP_SIZE_SET(1),
> +		sparx5, QS_INJ_CTRL(grp));
> +
> +	// Write the IFH to the chip.
> +	for (w = 0; w < IFH_LEN; w++)
> +		spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
> +
> +	/* Write words, round up */
> +	count = ((skb->len + 3) / 4);
> +	buf = skb->data;
> +	for (w = 0; w < count; w++, buf += 4) {
> +		val = get_unaligned((const u32 *)buf);
> +		spx5_wr(val, sparx5, QS_INJ_WR(grp));
> +	}

No DMA? What sort of performance do you get? Enough for the odd BPDU,
IGMP frame etc, but i guess you don't want any real bulk data to be
sent this way?

> +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> +{
> +	struct sparx5 *sparx5 = _sparx5;
> +
> +	/* Check data in queue */
> +	while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> +		sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> +
> +	return IRQ_HANDLED;
> +}

Is there any sort of limit how many times this will loop? If somebody
is blasting 10Gbps at the CPU, will it ever get out of this loop?

   Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-19 20:24     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 20:24 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

> +		port13: port@13 {
> +			reg = <13>;
> +			/* Example: CU SFP, 1G speed */
> +			max-speed = <10000>;

One too many 0's for 1G.

> +		/* 25G SFPs */
> +		port56: port@56 {
> +			reg = <56>;
> +			max-speed = <10000>;

Why limit a 25G SFP to 10G?

    Andrew

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
@ 2020-12-19 20:24     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 20:24 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Device Tree List, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, Rob Herring,
	linux-arm-kernel, Mark Einon, Jakub Kicinski, David S. Miller,
	Lars Povlsen

> +		port13: port@13 {
> +			reg = <13>;
> +			/* Example: CU SFP, 1G speed */
> +			max-speed = <10000>;

One too many 0's for 1G.

> +		/* 25G SFPs */
> +		port56: port@56 {
> +			reg = <56>;
> +			max-speed = <10000>;

Why limit a 25G SFP to 10G?

    Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 7/8] net: sparx5: add ethtool configuration and statistics support
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-19 23:31     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 23:31 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

> +void sparx5_get_stats64(struct net_device *ndev,
> +			struct rtnl_link_stats64 *stats)
> +{
> +	struct sparx5_port *port = netdev_priv(ndev);
> +	struct sparx5 *sparx5 = port->sparx5;
> +	u64 *portstats;
> +
> +	if (!sparx5->stats)
> +		return;		/* Not initialized yet */
> +
> +	portstats = &sparx5->stats[port->portno * sparx5->num_stats];
> +
> +	mutex_lock(&sparx5->stats_lock);

There was a big discussion about stats64 not being able to take a lock
a few weeks ago. You probably want to go read the thread. The aim was
to allow sleeping, but i don't know if that as been achieved yet.

   Andrew

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

* Re: [RFC PATCH v2 7/8] net: sparx5: add ethtool configuration and statistics support
@ 2020-12-19 23:31     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-19 23:31 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

> +void sparx5_get_stats64(struct net_device *ndev,
> +			struct rtnl_link_stats64 *stats)
> +{
> +	struct sparx5_port *port = netdev_priv(ndev);
> +	struct sparx5 *sparx5 = port->sparx5;
> +	u64 *portstats;
> +
> +	if (!sparx5->stats)
> +		return;		/* Not initialized yet */
> +
> +	portstats = &sparx5->stats[port->portno * sparx5->num_stats];
> +
> +	mutex_lock(&sparx5->stats_lock);

There was a big discussion about stats64 not being able to take a lock
a few weeks ago. You probably want to go read the thread. The aim was
to allow sleeping, but i don't know if that as been achieved yet.

   Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-20 23:35     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-20 23:35 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

> +	/* Aneg complete provides more information  */
> +	if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
> +		if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII) {
> +			/* SGMII cisco aneg */
> +			u32 spdvalue = ((lp_abil >> 10) & 3);

			u32 spdvalue = lp_abil & LPA_SGMII_SPD_MASK;

> +
> +			status->link = !!((lp_abil >> 15) == 1) && status->link;

Maybe

			status->link = !!((lp_abil & LPA_SGMII_LINK) && status->link;

> +			status->an_complete = true;
> +			status->duplex = (lp_abil >> 12) & 0x1 ?  DUPLEX_FULL : DUPLEX_HALF;

			status->duplex = (lp_abil & LPA_SGMII_FULL_DUPLEX) ?  DUPLEX_FULL : DUPLEX_HALF;


> +			if (spdvalue == LPA_SGMII_10)
> +				status->speed = SPEED_10;
> +			else if (spdvalue == LPA_SGMII_100)
> +				status->speed = SPEED_100;
> +			else
> +				status->speed = SPEED_1000;

I wonder if there is a helper for this?


> +		} else {
> +			/* Clause 37 Aneg */
> +			status->link = !((lp_abil >> 12) & 3) && status->link;
> +			status->an_complete = true;
> +			status->duplex = ((lp_abil >> 5) & 1) ? DUPLEX_FULL : DUPLEX_UNKNOWN;
> +			if ((lp_abil >> 8) & 1) /* symmetric pause */
> +				status->pause = MLO_PAUSE_RX | MLO_PAUSE_TX;
> +			if (lp_abil & (1 << 7)) /* asymmetric pause */
> +				status->pause |= MLO_PAUSE_RX;
> +		}

Please check if there are any standard #defines you can use for
this. Russell King has done some work for clause 37. Maybe there is
some code in phy_driver.c you can use? phylink_decode_sgmii_word()

> +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> +				    struct sparx5_port *port,
> +				    struct sparx5_port_config *conf)
> +{
> +	case PHY_INTERFACE_MODE_SGMII:
> +		if (conf->speed != SPEED_1000 &&
> +		    conf->speed != SPEED_100 &&
> +		    conf->speed != SPEED_10 &&
> +		    conf->speed != SPEED_2500)
> +			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);

Is it really SGMII over clocked at 2500? Or 2500BaseX?

> +static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
> +			       u32 portno, u32 speed)
> +{
> +	u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
> +	u32 mac_width  = 8;
> +	u32 fifo_width = 16;
> +	u32 addition   = 0;
> +	u32 mac_per    = 6400, tmp1, tmp2, tmp3;
> +	u32 taxi_dist[SPX5_PORTS_ALL] = {

const. As it is at the moment, it gets copied onto the stack, so it
can be modified. Const i guess prevents that copy?

> +		6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
> +		4, 4, 4, 4,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		4, 6, 8, 4, 6, 8, 6, 8,
> +		2, 2, 2, 2, 2, 2, 2, 4, 2
> +	};

> +static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)

What is urg? 

> +static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
> +{
> +	if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37 aneg */
> +		return ((1 << 14) | /* ack */
> +		((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
> +		((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
> +		(1 << 5)); /* FDX only */

ADVERTISE_LPACK, ADVERTISE_PAUSE_ASYM, ADVERTISE_PAUSE_CAP, ADVERTISE_1000XFULL?

> +int sparx5_port_config(struct sparx5 *sparx5,
> +		       struct sparx5_port *port,
> +		       struct sparx5_port_config *conf)
> +{
> +	bool high_speed_dev = sparx5_is_high_speed_device(conf);
> +	int err, urgency, stop_wm;
> +
> +	err = sparx5_port_verify_speed(sparx5, port, conf);
> +	if (err)
> +		return err;
> +
> +	/* high speed device is already configured */
> +	if (!high_speed_dev)
> +		sparx5_port_config_low_set(sparx5, port, conf);
> +
> +	/* Configure flow control */
> +	err = sparx5_port_fc_setup(sparx5, port, conf);
> +	if (err)
> +		return err;
> +
> +	/* Set the DSM stop watermark */
> +	stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf->speed);
> +	spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
> +		 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
> +		 sparx5,
> +		 DSM_DEV_TX_STOP_WM_CFG(port->portno));
> +
> +	/* Enable port forwarding */
> +	urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
> +	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
> +		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
> +		 QFWD_SWITCH_PORT_MODE_PORT_ENA |
> +		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
> +		 sparx5,
> +		 QFWD_SWITCH_PORT_MODE(port->portno));

What does it mean by port forwarding? By default, packets should only
go to the CPU, until the port is added to a bridge. I've not thought
much about L3, since DSA so far only has L2 switches, but i guess you
don't need to enable L3 forwarding until a route out the port has been
added?

> +/* Initialize port config to default */
> +int sparx5_port_init(struct sparx5 *sparx5,
> +		     struct sparx5_port *port,
> +		     struct sparx5_port_config *conf)
> +{
> +	/* Discard pause frame 01-80-C2-00-00-01 */
> +	spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));

The comment is about pause frames, but the macro contain BPDU?

    Andrew

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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
@ 2020-12-20 23:35     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-20 23:35 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

> +	/* Aneg complete provides more information  */
> +	if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
> +		if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII) {
> +			/* SGMII cisco aneg */
> +			u32 spdvalue = ((lp_abil >> 10) & 3);

			u32 spdvalue = lp_abil & LPA_SGMII_SPD_MASK;

> +
> +			status->link = !!((lp_abil >> 15) == 1) && status->link;

Maybe

			status->link = !!((lp_abil & LPA_SGMII_LINK) && status->link;

> +			status->an_complete = true;
> +			status->duplex = (lp_abil >> 12) & 0x1 ?  DUPLEX_FULL : DUPLEX_HALF;

			status->duplex = (lp_abil & LPA_SGMII_FULL_DUPLEX) ?  DUPLEX_FULL : DUPLEX_HALF;


> +			if (spdvalue == LPA_SGMII_10)
> +				status->speed = SPEED_10;
> +			else if (spdvalue == LPA_SGMII_100)
> +				status->speed = SPEED_100;
> +			else
> +				status->speed = SPEED_1000;

I wonder if there is a helper for this?


> +		} else {
> +			/* Clause 37 Aneg */
> +			status->link = !((lp_abil >> 12) & 3) && status->link;
> +			status->an_complete = true;
> +			status->duplex = ((lp_abil >> 5) & 1) ? DUPLEX_FULL : DUPLEX_UNKNOWN;
> +			if ((lp_abil >> 8) & 1) /* symmetric pause */
> +				status->pause = MLO_PAUSE_RX | MLO_PAUSE_TX;
> +			if (lp_abil & (1 << 7)) /* asymmetric pause */
> +				status->pause |= MLO_PAUSE_RX;
> +		}

Please check if there are any standard #defines you can use for
this. Russell King has done some work for clause 37. Maybe there is
some code in phy_driver.c you can use? phylink_decode_sgmii_word()

> +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> +				    struct sparx5_port *port,
> +				    struct sparx5_port_config *conf)
> +{
> +	case PHY_INTERFACE_MODE_SGMII:
> +		if (conf->speed != SPEED_1000 &&
> +		    conf->speed != SPEED_100 &&
> +		    conf->speed != SPEED_10 &&
> +		    conf->speed != SPEED_2500)
> +			return sparx5_port_error(port, conf, SPX5_PERR_SPEED);

Is it really SGMII over clocked at 2500? Or 2500BaseX?

> +static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
> +			       u32 portno, u32 speed)
> +{
> +	u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
> +	u32 mac_width  = 8;
> +	u32 fifo_width = 16;
> +	u32 addition   = 0;
> +	u32 mac_per    = 6400, tmp1, tmp2, tmp3;
> +	u32 taxi_dist[SPX5_PORTS_ALL] = {

const. As it is at the moment, it gets copied onto the stack, so it
can be modified. Const i guess prevents that copy?

> +		6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
> +		4, 4, 4, 4,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		11, 12, 13, 14, 15, 16, 17, 18,
> +		4, 6, 8, 4, 6, 8, 6, 8,
> +		2, 2, 2, 2, 2, 2, 2, 4, 2
> +	};

> +static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)

What is urg? 

> +static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
> +{
> +	if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37 aneg */
> +		return ((1 << 14) | /* ack */
> +		((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
> +		((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
> +		(1 << 5)); /* FDX only */

ADVERTISE_LPACK, ADVERTISE_PAUSE_ASYM, ADVERTISE_PAUSE_CAP, ADVERTISE_1000XFULL?

> +int sparx5_port_config(struct sparx5 *sparx5,
> +		       struct sparx5_port *port,
> +		       struct sparx5_port_config *conf)
> +{
> +	bool high_speed_dev = sparx5_is_high_speed_device(conf);
> +	int err, urgency, stop_wm;
> +
> +	err = sparx5_port_verify_speed(sparx5, port, conf);
> +	if (err)
> +		return err;
> +
> +	/* high speed device is already configured */
> +	if (!high_speed_dev)
> +		sparx5_port_config_low_set(sparx5, port, conf);
> +
> +	/* Configure flow control */
> +	err = sparx5_port_fc_setup(sparx5, port, conf);
> +	if (err)
> +		return err;
> +
> +	/* Set the DSM stop watermark */
> +	stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf->speed);
> +	spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
> +		 DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
> +		 sparx5,
> +		 DSM_DEV_TX_STOP_WM_CFG(port->portno));
> +
> +	/* Enable port forwarding */
> +	urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
> +	spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
> +		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
> +		 QFWD_SWITCH_PORT_MODE_PORT_ENA |
> +		 QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
> +		 sparx5,
> +		 QFWD_SWITCH_PORT_MODE(port->portno));

What does it mean by port forwarding? By default, packets should only
go to the CPU, until the port is added to a bridge. I've not thought
much about L3, since DSA so far only has L2 switches, but i guess you
don't need to enable L3 forwarding until a route out the port has been
added?

> +/* Initialize port config to default */
> +int sparx5_port_init(struct sparx5 *sparx5,
> +		     struct sparx5_port *port,
> +		     struct sparx5_port_config *conf)
> +{
> +	/* Discard pause frame 01-80-C2-00-00-01 */
> +	spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));

The comment is about pause frames, but the macro contain BPDU?

    Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-21  0:25     ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-21  0:25 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> +
> +static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
> +{
> +	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
> +}
> +
> +static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
> +{
> +	u32 val;
> +
> +	return readx_poll_timeout(sparx5_mact_get_status,
> +		sparx5, val,
> +		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
> +		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
> +}

No inline functions in C files please.

> +void sparx5_mact_init(struct sparx5 *sparx5)
> +{
> +	mutex_init(&sparx5->lock);
> +
> +	mutex_lock(&sparx5->lock);
> +
> +	/*  Flush MAC table */
> +	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
> +		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
> +		sparx5, LRN_COMMON_ACCESS_CTRL);
> +
> +	if (sparx5_mact_wait_for_completion(sparx5) != 0)
> +		dev_warn(sparx5->dev, "MAC flush error\n");
> +
> +	mutex_unlock(&sparx5->lock);

It always seems odd to me, when you initialise a mutex, and then
immediately take it. Who are you locking against? I'm not saying it is
wrong though, especially if you have code in spx5_wr() and spx5_rd()
which check the lock is actually taken. I've found a number of locking
bugs in mv88e6xxx by having such checks.

> +
> +	sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */

BR_DEFAULT_AGEING_TIME is 300 seconds. Is this the same thing?

> +static int sparx5_port_bridge_join(struct sparx5_port *port,
> +				   struct net_device *bridge)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +
> +	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
> +		/* First bridged port */
> +		sparx5->hw_bridge_dev = bridge;
> +	else
> +		if (sparx5->hw_bridge_dev != bridge)
> +			/* This is adding the port to a second bridge, this is
> +			 * unsupported
> +			 */
> +			return -ENODEV;

Just checking my understanding. You have a 64 port switch, which only
supports a single bridge?

-EOPNOTSUPP seems like a better return code.

> +
> +	set_bit(port->portno, sparx5->bridge_mask);
> +
> +	/* Port enters in bridge mode therefor don't need to copy to CPU
> +	 * frames for multicast in case the bridge is not requesting them
> +	 */
> +	__dev_mc_unsync(port->ndev, sparx5_mc_unsync);

Did you copy that from the mellanox driver? I think in DSA we take the
opposite approach. Multicast/broadcast goes to the CPU until the CPU
says it does not want it.

> +static void sparx5_port_bridge_leave(struct sparx5_port *port,
> +				     struct net_device *bridge)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +
> +	clear_bit(port->portno, sparx5->bridge_mask);
> +	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
> +		sparx5->hw_bridge_dev = NULL;
> +
> +	/* Clear bridge vlan settings before updating the port settings */
> +	port->vlan_aware = 0;
> +	port->pvid = NULL_VID;
> +	port->vid = NULL_VID;
> +
> +	/* Port enters in host more therefore restore mc list */

s/more/mode

> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
> @@ -0,0 +1,223 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
> + */
> +
> +#include "sparx5_main.h"
> +
> +static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)

Is the t in vlant typ0?

> +int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
> +			bool untagged)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +	int ret;
> +
> +	/* Make the port a member of the VLAN */
> +	set_bit(port->portno, sparx5->vlan_mask[vid]);
> +	ret = sparx5_vlant_set_mask(sparx5, vid);
> +	if (ret)
> +		return ret;
> +
> +	/* Default ingress vlan classification */
> +	if (pvid)
> +		port->pvid = vid;
> +
> +	/* Untagged egress vlan clasification */

classification

> +	if (untagged && port->vid != vid) {
> +		if (port->vid) {
> +			netdev_err(port->ndev,
> +				   "Port already has a native VLAN: %d\n",
> +				   port->vid);
> +			return -EBUSY;
> +		}
> +		port->vid = vid;
> +	}
> +
> +	sparx5_vlan_port_apply(sparx5, port);
> +
> +	return 0;
> +}


> +void sparx5_update_fwd(struct sparx5 *sparx5)
> +{
> +	u32 mask[3];
> +	DECLARE_BITMAP(workmask, SPX5_PORTS);
> +	int port;
> +
> +	/* Divide up fwd mask in 32 bit words */
> +	bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
> +
> +	/* Update flood masks */
> +	for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
> +		spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
> +		spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
> +		spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
> +	}
> +
> +	/* Update SRC masks */
> +	for (port = 0; port < SPX5_PORTS; port++) {
> +		if (test_bit(port, sparx5->bridge_fwd_mask)) {
> +			/* Allow to send to all bridged but self */
> +			bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
> +			clear_bit(port, workmask);
> +			bitmap_to_arr32(mask, workmask, SPX5_PORTS);
> +			spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
> +			spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
> +			spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
> +		} else {
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
> +		}

Humm, interesting. This seems to control what other ports a port can
send to. That is one of the basic features you need for supporting
multiple bridges. So i assume your problems is you cannot partition
the MAC table?

    Andrew

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

* Re: [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
@ 2020-12-21  0:25     ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-21  0:25 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
> +
> +static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
> +{
> +	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
> +}
> +
> +static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
> +{
> +	u32 val;
> +
> +	return readx_poll_timeout(sparx5_mact_get_status,
> +		sparx5, val,
> +		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
> +		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
> +}

No inline functions in C files please.

> +void sparx5_mact_init(struct sparx5 *sparx5)
> +{
> +	mutex_init(&sparx5->lock);
> +
> +	mutex_lock(&sparx5->lock);
> +
> +	/*  Flush MAC table */
> +	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
> +		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
> +		sparx5, LRN_COMMON_ACCESS_CTRL);
> +
> +	if (sparx5_mact_wait_for_completion(sparx5) != 0)
> +		dev_warn(sparx5->dev, "MAC flush error\n");
> +
> +	mutex_unlock(&sparx5->lock);

It always seems odd to me, when you initialise a mutex, and then
immediately take it. Who are you locking against? I'm not saying it is
wrong though, especially if you have code in spx5_wr() and spx5_rd()
which check the lock is actually taken. I've found a number of locking
bugs in mv88e6xxx by having such checks.

> +
> +	sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */

BR_DEFAULT_AGEING_TIME is 300 seconds. Is this the same thing?

> +static int sparx5_port_bridge_join(struct sparx5_port *port,
> +				   struct net_device *bridge)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +
> +	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
> +		/* First bridged port */
> +		sparx5->hw_bridge_dev = bridge;
> +	else
> +		if (sparx5->hw_bridge_dev != bridge)
> +			/* This is adding the port to a second bridge, this is
> +			 * unsupported
> +			 */
> +			return -ENODEV;

Just checking my understanding. You have a 64 port switch, which only
supports a single bridge?

-EOPNOTSUPP seems like a better return code.

> +
> +	set_bit(port->portno, sparx5->bridge_mask);
> +
> +	/* Port enters in bridge mode therefor don't need to copy to CPU
> +	 * frames for multicast in case the bridge is not requesting them
> +	 */
> +	__dev_mc_unsync(port->ndev, sparx5_mc_unsync);

Did you copy that from the mellanox driver? I think in DSA we take the
opposite approach. Multicast/broadcast goes to the CPU until the CPU
says it does not want it.

> +static void sparx5_port_bridge_leave(struct sparx5_port *port,
> +				     struct net_device *bridge)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +
> +	clear_bit(port->portno, sparx5->bridge_mask);
> +	if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
> +		sparx5->hw_bridge_dev = NULL;
> +
> +	/* Clear bridge vlan settings before updating the port settings */
> +	port->vlan_aware = 0;
> +	port->pvid = NULL_VID;
> +	port->vid = NULL_VID;
> +
> +	/* Port enters in host more therefore restore mc list */

s/more/mode

> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
> @@ -0,0 +1,223 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Microchip Sparx5 Switch driver
> + *
> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
> + */
> +
> +#include "sparx5_main.h"
> +
> +static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)

Is the t in vlant typ0?

> +int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
> +			bool untagged)
> +{
> +	struct sparx5 *sparx5 = port->sparx5;
> +	int ret;
> +
> +	/* Make the port a member of the VLAN */
> +	set_bit(port->portno, sparx5->vlan_mask[vid]);
> +	ret = sparx5_vlant_set_mask(sparx5, vid);
> +	if (ret)
> +		return ret;
> +
> +	/* Default ingress vlan classification */
> +	if (pvid)
> +		port->pvid = vid;
> +
> +	/* Untagged egress vlan clasification */

classification

> +	if (untagged && port->vid != vid) {
> +		if (port->vid) {
> +			netdev_err(port->ndev,
> +				   "Port already has a native VLAN: %d\n",
> +				   port->vid);
> +			return -EBUSY;
> +		}
> +		port->vid = vid;
> +	}
> +
> +	sparx5_vlan_port_apply(sparx5, port);
> +
> +	return 0;
> +}


> +void sparx5_update_fwd(struct sparx5 *sparx5)
> +{
> +	u32 mask[3];
> +	DECLARE_BITMAP(workmask, SPX5_PORTS);
> +	int port;
> +
> +	/* Divide up fwd mask in 32 bit words */
> +	bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
> +
> +	/* Update flood masks */
> +	for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
> +		spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
> +		spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
> +		spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
> +	}
> +
> +	/* Update SRC masks */
> +	for (port = 0; port < SPX5_PORTS; port++) {
> +		if (test_bit(port, sparx5->bridge_fwd_mask)) {
> +			/* Allow to send to all bridged but self */
> +			bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
> +			clear_bit(port, workmask);
> +			bitmap_to_arr32(mask, workmask, SPX5_PORTS);
> +			spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
> +			spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
> +			spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
> +		} else {
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
> +			spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
> +		}

Humm, interesting. This seems to control what other ports a port can
send to. That is one of the basic features you need for supporting
multiple bridges. So i assume your problems is you cannot partition
the MAC table?

    Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-21  0:55     ` Florian Fainelli
  -1 siblings, 0 replies; 75+ messages in thread
From: Florian Fainelli @ 2020-12-21  0:55 UTC (permalink / raw)
  To: Steen Hegelund, David S. Miller, Jakub Kicinski, Rob Herring,
	Device Tree List
  Cc: Andrew Lunn, Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel



On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> ---

[snip]

> +          max-speed:
> +            maxItems: 1
> +            description: Bandwidth allocated to this port
> +
> +          phys:
> +            description: phandle of a Ethernet Serdes PHY
> +
> +          phy-handle:
> +            description: phandle of a Ethernet PHY
> +
> +          phy-mode:
> +            description: Interface between the serdes and the phy

Can you specify this pertains to the Serdes and Ethernet PHY?
-- 
Florian

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-21  0:55     ` Florian Fainelli
  0 siblings, 0 replies; 75+ messages in thread
From: Florian Fainelli @ 2020-12-21  0:55 UTC (permalink / raw)
  To: Steen Hegelund, David S. Miller, Jakub Kicinski, Rob Herring,
	Device Tree List
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Lars Povlsen



On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> ---

[snip]

> +          max-speed:
> +            maxItems: 1
> +            description: Bandwidth allocated to this port
> +
> +          phys:
> +            description: phandle of a Ethernet Serdes PHY
> +
> +          phy-handle:
> +            description: phandle of a Ethernet PHY
> +
> +          phy-mode:
> +            description: Interface between the serdes and the phy

Can you specify this pertains to the Serdes and Ethernet PHY?
-- 
Florian

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
  2020-12-17  7:51 ` Steen Hegelund
@ 2020-12-21  0:58   ` Florian Fainelli
  -1 siblings, 0 replies; 75+ messages in thread
From: Florian Fainelli @ 2020-12-21  0:58 UTC (permalink / raw)
  To: Steen Hegelund, David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel



On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> This series provides the Microchip Sparx5 Switch Driver
> 
> The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
> Ethernet ports and up to 200 Gbps of switching bandwidth.
> 
> It provides a rich set of Ethernet switching features such as hierarchical
> QoS, hardware-based OAM  and service activation testing, protection
> switching, IEEE 1588, and Synchronous Ethernet.
> 
> Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
> MEF CE
> 2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
>   classification in both ingress and egress.
> 
> Per-EVC features include advanced L3-aware classification, a rich set of
> statistics, OAM for end-to-end performance monitoring, and dual-rate
> policing and shaping.
> 
> Time sensitive networking (TSN) is supported through a comprehensive set of
> features including frame preemption, cut-through, frame replication and
> elimination for reliability, enhanced scheduling: credit-based shaping,
> time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
> and filtering.
> 
> Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
> low-latency deterministic networking for Fronthaul, Carrier, and Industrial
> Ethernet.
> 
> The Sparx5 switch family consists of following SKUs:
> 
> - VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
>   port configurations:
>   - 6 *10G
>   - 16 * 2.5G + 2 * 10G
>   - 24 * 1G + 4 * 10G
> 
> - VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
>   port configurations:
>   - 9 * 10G
>   - 16 * 2.5G + 4 * 10G
>   - 48 * 1G + 4 * 10G
> 
> - VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
>   port configurations:
>   - 12 * 10G
>   - 16 * 2.5G + 8 * 10G
>   - 48 * 1G + 8 * 10G
> 
> - VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
>   port configurations:
>   - 16 * 10G
>   - 10 * 10G + 2 * 25G
>   - 16 * 2.5G + 10 * 10G
>   - 48 * 1G + 10 * 10G
> 
> - VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
>   port configurations:
>   - 20 * 10G
>   - 8 * 25G
> 
> In addition, the device supports one 10/100/1000/2500/5000 Mbps
> SGMII/SerDes node processor interface (NPI) Ethernet port.
> 
> The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.
> 
> - PCB134 main networking features:
>   - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
>   - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
>     speed).
>   - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
>     (on-board VSC8211 PHY connected to Sparx5 through SGMII).
> 
> - PCB135 main networking features:
>   - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
>     connected to VSC7558 through QSGMII.
>   - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
>     each port connects to VSC7558 through SFI.
>   - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
>     speed.
>   - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
>     VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
>     using a loopback add-on PCB)
> 
> This series provides support for:
>   - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
>     devices and media types.
>   - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
>     1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
>   - SerDes configuration via the Sparx5 SerDes driver (see below).
>   - Host mode providing register based injection and extraction.
>   - Switch mode providing MAC/VLAN table learning and Layer2 switching
>     offloaded to the Sparx5 switch.
>   - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
>     configuration and statistics via ethtool.
> 
> More support will be added at a later stage.
> 
> The Sparx5 Switch chip register model can be browsed here:
> Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html

Out of curiosity, what tool was used to generate the register
information page? It looks really neat and well organized.
-- 
Florian

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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
@ 2020-12-21  0:58   ` Florian Fainelli
  0 siblings, 0 replies; 75+ messages in thread
From: Florian Fainelli @ 2020-12-21  0:58 UTC (permalink / raw)
  To: Steen Hegelund, David S. Miller, Jakub Kicinski
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Lars Povlsen



On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> This series provides the Microchip Sparx5 Switch Driver
> 
> The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
> Ethernet ports and up to 200 Gbps of switching bandwidth.
> 
> It provides a rich set of Ethernet switching features such as hierarchical
> QoS, hardware-based OAM  and service activation testing, protection
> switching, IEEE 1588, and Synchronous Ethernet.
> 
> Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
> MEF CE
> 2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
>   classification in both ingress and egress.
> 
> Per-EVC features include advanced L3-aware classification, a rich set of
> statistics, OAM for end-to-end performance monitoring, and dual-rate
> policing and shaping.
> 
> Time sensitive networking (TSN) is supported through a comprehensive set of
> features including frame preemption, cut-through, frame replication and
> elimination for reliability, enhanced scheduling: credit-based shaping,
> time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
> and filtering.
> 
> Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
> low-latency deterministic networking for Fronthaul, Carrier, and Industrial
> Ethernet.
> 
> The Sparx5 switch family consists of following SKUs:
> 
> - VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
>   port configurations:
>   - 6 *10G
>   - 16 * 2.5G + 2 * 10G
>   - 24 * 1G + 4 * 10G
> 
> - VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
>   port configurations:
>   - 9 * 10G
>   - 16 * 2.5G + 4 * 10G
>   - 48 * 1G + 4 * 10G
> 
> - VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
>   port configurations:
>   - 12 * 10G
>   - 16 * 2.5G + 8 * 10G
>   - 48 * 1G + 8 * 10G
> 
> - VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
>   port configurations:
>   - 16 * 10G
>   - 10 * 10G + 2 * 25G
>   - 16 * 2.5G + 10 * 10G
>   - 48 * 1G + 10 * 10G
> 
> - VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
>   port configurations:
>   - 20 * 10G
>   - 8 * 25G
> 
> In addition, the device supports one 10/100/1000/2500/5000 Mbps
> SGMII/SerDes node processor interface (NPI) Ethernet port.
> 
> The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.
> 
> - PCB134 main networking features:
>   - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
>   - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
>     speed).
>   - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
>     (on-board VSC8211 PHY connected to Sparx5 through SGMII).
> 
> - PCB135 main networking features:
>   - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
>     connected to VSC7558 through QSGMII.
>   - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
>     each port connects to VSC7558 through SFI.
>   - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
>     speed.
>   - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
>     VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
>     using a loopback add-on PCB)
> 
> This series provides support for:
>   - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
>     devices and media types.
>   - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
>     1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
>   - SerDes configuration via the Sparx5 SerDes driver (see below).
>   - Host mode providing register based injection and extraction.
>   - Switch mode providing MAC/VLAN table learning and Layer2 switching
>     offloaded to the Sparx5 switch.
>   - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
>     configuration and statistics via ethtool.
> 
> More support will be added at a later stage.
> 
> The Sparx5 Switch chip register model can be browsed here:
> Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html

Out of curiosity, what tool was used to generate the register
information page? It looks really neat and well organized.
-- 
Florian

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-21  0:55     ` Florian Fainelli
@ 2020-12-21 10:00       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-21 10:00 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List,
	Andrew Lunn, Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On Sun, 2020-12-20 at 16:55 -0800, Florian Fainelli wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> > Document the Sparx5 switch device driver bindings
> > 
> > Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> > Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> > ---
> 
> [snip]
> 
> > +          max-speed:
> > +            maxItems: 1
> > +            description: Bandwidth allocated to this port
> > +
> > +          phys:
> > +            description: phandle of a Ethernet Serdes PHY
> > +
> > +          phy-handle:
> > +            description: phandle of a Ethernet PHY
> > +
> > +          phy-mode:
> > +            description: Interface between the serdes and the phy
> 
> Can you specify this pertains to the Serdes and Ethernet PHY?
Hi Florian,

Yes: I will clarify that phy-mode is for the optional Ethernet cuPHY.

Thanks for your comments
Steen

> --
> Florian



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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-21 10:00       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-21 10:00 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Device Tree List, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, Rob Herring,
	linux-arm-kernel, Mark Einon, Jakub Kicinski, Bjarni Jonasson,
	David S. Miller, Lars Povlsen

On Sun, 2020-12-20 at 16:55 -0800, Florian Fainelli wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On 12/16/2020 11:51 PM, Steen Hegelund wrote:
> > Document the Sparx5 switch device driver bindings
> > 
> > Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> > Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> > ---
> 
> [snip]
> 
> > +          max-speed:
> > +            maxItems: 1
> > +            description: Bandwidth allocated to this port
> > +
> > +          phys:
> > +            description: phandle of a Ethernet Serdes PHY
> > +
> > +          phy-handle:
> > +            description: phandle of a Ethernet PHY
> > +
> > +          phy-mode:
> > +            description: Interface between the serdes and the phy
> 
> Can you specify this pertains to the Serdes and Ethernet PHY?
Hi Florian,

Yes: I will clarify that phy-mode is for the optional Ethernet cuPHY.

Thanks for your comments
Steen

> --
> Florian



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
  2020-12-21  0:58   ` Florian Fainelli
@ 2020-12-21 14:31     ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-21 14:31 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Andrew Lunn, Russell King, Lars Povlsen, David S. Miller,
	Jakub Kicinski, Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Hi Florian,

On Sun, 2020-12-20 at 16:58 -0800, Florian Fainelli wrote:
> > 
> > The Sparx5 Switch chip register model can be browsed here:
> > Link:  
> > https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html
> 
> Out of curiosity, what tool was used to generate the register
> information page? It looks really neat and well organized.

It is an in-house tool that is used in our so-called VML-flow
(Versatile Markup Language), so it is not out in the open yet.

The same model file is used internally in many ways - but exposing it
to the public is something we have not tried before, and having this
view is so much nicer that the usual datasheet, I find...

And thanks for the kind words - I passed them on to the author.

BR
Steen
> --
> Florian





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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
@ 2020-12-21 14:31     ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-21 14:31 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Jakub Kicinski, David S. Miller, Lars Povlsen

Hi Florian,

On Sun, 2020-12-20 at 16:58 -0800, Florian Fainelli wrote:
> > 
> > The Sparx5 Switch chip register model can be browsed here:
> > Link:  
> > https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html
> 
> Out of curiosity, what tool was used to generate the register
> information page? It looks really neat and well organized.

It is an in-house tool that is used in our so-called VML-flow
(Versatile Markup Language), so it is not out in the open yet.

The same model file is used internally in many ways - but exposing it
to the public is something we have not tried before, and having this
view is so much nicer that the usual datasheet, I find...

And thanks for the kind words - I passed them on to the author.

BR
Steen
> --
> Florian





_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-17  7:51   ` Steen Hegelund
@ 2020-12-21 21:40     ` Rob Herring
  -1 siblings, 0 replies; 75+ messages in thread
From: Rob Herring @ 2020-12-21 21:40 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Device Tree List, Andrew Lunn,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> ---
>  .../bindings/net/microchip,sparx5-switch.yaml | 178 ++++++++++++++++++
>  1 file changed, 178 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> 
> diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> new file mode 100644
> index 000000000000..6e3ef8285e9a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> @@ -0,0 +1,178 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Microchip Sparx5 Ethernet switch controller
> +
> +maintainers:
> +  - Lars Povlsen <lars.povlsen@microchip.com>
> +  - Steen Hegelund <steen.hegelund@microchip.com>
> +
> +description: |
> +  The SparX-5 Enterprise Ethernet switch family provides a rich set of
> +  Enterprise switching features such as advanced TCAM-based VLAN and
> +  QoS processing enabling delivery of differentiated services, and
> +  security through TCAM-based frame processing using versatile content
> +  aware processor (VCAP).
> +
> +  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported
> +  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K
> +  IPv6 (S,G) multicast groups.
> +
> +  L3 security features include source guard and reverse path
> +  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and
> +  IP tunnels (IP over GRE/IP).
> +
> +  The SparX-5 switch family targets managed Layer 2 and Layer 3
> +  equipment in SMB, SME, and Enterprise where high port count
> +  1G/2.5G/5G/10G switching with 10G/25G aggregation links is required.
> +
> +properties:
> +  $nodename:
> +    pattern: "^switch@[0-9a-f]+$"
> +
> +  compatible:
> +    const: microchip,sparx5-switch
> +
> +  reg:
> +    minItems: 2
> +
> +  reg-names:
> +    minItems: 2

This is the default based on 'items' length.

> +    items:
> +      - const: devices
> +      - const: gcb
> +
> +  interrupts:
> +    maxItems: 1
> +    description: Interrupt used for reception of packets to the CPU
> +
> +  ethernet-ports:
> +    type: object
> +    properties:
> +      '#address-cells':
> +        const: 1
> +      '#size-cells':
> +        const: 0
> +
> +    patternProperties:
> +      "^port@[0-9]+$":
> +        type: object
> +        description: Switch ports
> +
> +        allOf:
> +          - $ref: ethernet-controller.yaml#
> +
> +        properties:
> +          reg:
> +            description: Switch port number
> +
> +          max-speed:
> +            maxItems: 1

Is that an array?

> +            description: Bandwidth allocated to this port
> +
> +          phys:

How many? (maxItems)

> +            description: phandle of a Ethernet Serdes PHY
> +
> +          phy-handle:
> +            description: phandle of a Ethernet PHY
> +
> +          phy-mode:
> +            description: Interface between the serdes and the phy

The whole set of modes defined is supported?

> +
> +          sfp:
> +            description: phandle of an SFP
> +
> +          managed:
> +            maxItems: 1

An array?

> +            description: SFP management
> +
> +        required:
> +          - reg
> +          - max-speed
> +          - phys
> +
> +        oneOf:
> +          - required:
> +              - phy-handle
> +              - phy-mode
> +          - required:
> +              - sfp
> +              - managed
> +
> +        additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - interrupts
> +  - ethernet-ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    switch: switch@600000000 {
> +      compatible = "microchip,sparx5-switch";
> +      reg =  <0x10000000 0x800000>,
> +             <0x11010000 0x1b00000>;
> +      reg-names = "devices", "gcb";
> +
> +      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
> +      ethernet-ports {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        port0: port@0 {
> +          reg = <0>;
> +          max-speed = <1000>;
> +          phys = <&serdes 13>;
> +          phy-handle = <&phy0>;
> +          phy-mode = "qsgmii";
> +        };
> +        /* ... */
> +        /* Then the 25G interfaces */
> +        port60: port@60 {
> +          reg = <60>;
> +          max-speed = <25000>;
> +          phys = <&serdes 29>;
> +          sfp = <&sfp_eth60>;
> +          managed = "in-band-status";
> +        };
> +        port61: port@61 {
> +          reg = <61>;
> +          max-speed = <25000>;
> +          phys = <&serdes 30>;
> +          sfp = <&sfp_eth61>;
> +          managed = "in-band-status";
> +        };
> +        port62: port@62 {
> +          reg = <62>;
> +          max-speed = <25000>;
> +          phys = <&serdes 31>;
> +          sfp = <&sfp_eth62>;
> +          managed = "in-band-status";
> +        };
> +        port63: port@63 {
> +          reg = <63>;
> +          max-speed = <25000>;
> +          phys = <&serdes 32>;
> +          sfp = <&sfp_eth63>;
> +          managed = "in-band-status";
> +        };
> +        /* Finally the Management interface */
> +        port64: port@64 {
> +          reg = <64>;
> +          max-speed = <1000>;
> +          phys = <&serdes 0>;
> +          phy-handle = <&phy64>;
> +          phy-mode = "sgmii";
> +        };
> +      };
> +    };
> +
> +...
> -- 
> 2.29.2
> 

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-21 21:40     ` Rob Herring
  0 siblings, 0 replies; 75+ messages in thread
From: Rob Herring @ 2020-12-21 21:40 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Device Tree List, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Jakub Kicinski, Bjarni Jonasson, David S. Miller,
	Lars Povlsen

On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> Document the Sparx5 switch device driver bindings
> 
> Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> ---
>  .../bindings/net/microchip,sparx5-switch.yaml | 178 ++++++++++++++++++
>  1 file changed, 178 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> 
> diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> new file mode 100644
> index 000000000000..6e3ef8285e9a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> @@ -0,0 +1,178 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Microchip Sparx5 Ethernet switch controller
> +
> +maintainers:
> +  - Lars Povlsen <lars.povlsen@microchip.com>
> +  - Steen Hegelund <steen.hegelund@microchip.com>
> +
> +description: |
> +  The SparX-5 Enterprise Ethernet switch family provides a rich set of
> +  Enterprise switching features such as advanced TCAM-based VLAN and
> +  QoS processing enabling delivery of differentiated services, and
> +  security through TCAM-based frame processing using versatile content
> +  aware processor (VCAP).
> +
> +  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is supported
> +  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K IPv4/3K
> +  IPv6 (S,G) multicast groups.
> +
> +  L3 security features include source guard and reverse path
> +  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite and
> +  IP tunnels (IP over GRE/IP).
> +
> +  The SparX-5 switch family targets managed Layer 2 and Layer 3
> +  equipment in SMB, SME, and Enterprise where high port count
> +  1G/2.5G/5G/10G switching with 10G/25G aggregation links is required.
> +
> +properties:
> +  $nodename:
> +    pattern: "^switch@[0-9a-f]+$"
> +
> +  compatible:
> +    const: microchip,sparx5-switch
> +
> +  reg:
> +    minItems: 2
> +
> +  reg-names:
> +    minItems: 2

This is the default based on 'items' length.

> +    items:
> +      - const: devices
> +      - const: gcb
> +
> +  interrupts:
> +    maxItems: 1
> +    description: Interrupt used for reception of packets to the CPU
> +
> +  ethernet-ports:
> +    type: object
> +    properties:
> +      '#address-cells':
> +        const: 1
> +      '#size-cells':
> +        const: 0
> +
> +    patternProperties:
> +      "^port@[0-9]+$":
> +        type: object
> +        description: Switch ports
> +
> +        allOf:
> +          - $ref: ethernet-controller.yaml#
> +
> +        properties:
> +          reg:
> +            description: Switch port number
> +
> +          max-speed:
> +            maxItems: 1

Is that an array?

> +            description: Bandwidth allocated to this port
> +
> +          phys:

How many? (maxItems)

> +            description: phandle of a Ethernet Serdes PHY
> +
> +          phy-handle:
> +            description: phandle of a Ethernet PHY
> +
> +          phy-mode:
> +            description: Interface between the serdes and the phy

The whole set of modes defined is supported?

> +
> +          sfp:
> +            description: phandle of an SFP
> +
> +          managed:
> +            maxItems: 1

An array?

> +            description: SFP management
> +
> +        required:
> +          - reg
> +          - max-speed
> +          - phys
> +
> +        oneOf:
> +          - required:
> +              - phy-handle
> +              - phy-mode
> +          - required:
> +              - sfp
> +              - managed
> +
> +        additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - interrupts
> +  - ethernet-ports
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    switch: switch@600000000 {
> +      compatible = "microchip,sparx5-switch";
> +      reg =  <0x10000000 0x800000>,
> +             <0x11010000 0x1b00000>;
> +      reg-names = "devices", "gcb";
> +
> +      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
> +      ethernet-ports {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        port0: port@0 {
> +          reg = <0>;
> +          max-speed = <1000>;
> +          phys = <&serdes 13>;
> +          phy-handle = <&phy0>;
> +          phy-mode = "qsgmii";
> +        };
> +        /* ... */
> +        /* Then the 25G interfaces */
> +        port60: port@60 {
> +          reg = <60>;
> +          max-speed = <25000>;
> +          phys = <&serdes 29>;
> +          sfp = <&sfp_eth60>;
> +          managed = "in-band-status";
> +        };
> +        port61: port@61 {
> +          reg = <61>;
> +          max-speed = <25000>;
> +          phys = <&serdes 30>;
> +          sfp = <&sfp_eth61>;
> +          managed = "in-band-status";
> +        };
> +        port62: port@62 {
> +          reg = <62>;
> +          max-speed = <25000>;
> +          phys = <&serdes 31>;
> +          sfp = <&sfp_eth62>;
> +          managed = "in-band-status";
> +        };
> +        port63: port@63 {
> +          reg = <63>;
> +          max-speed = <25000>;
> +          phys = <&serdes 32>;
> +          sfp = <&sfp_eth63>;
> +          managed = "in-band-status";
> +        };
> +        /* Finally the Management interface */
> +        port64: port@64 {
> +          reg = <64>;
> +          max-speed = <1000>;
> +          phys = <&serdes 0>;
> +          phy-handle = <&phy64>;
> +          phy-mode = "sgmii";
> +        };
> +      };
> +    };
> +
> +...
> -- 
> 2.29.2
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
  2020-12-21 21:40     ` Rob Herring
@ 2020-12-22  7:30       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22  7:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: David S. Miller, Jakub Kicinski, Device Tree List, Andrew Lunn,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

Hi Rob,

On Mon, 2020-12-21 at 14:40 -0700, Rob Herring wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> > Document the Sparx5 switch device driver bindings
> > 
> > Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> > Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> > ---
> >  .../bindings/net/microchip,sparx5-switch.yaml | 178
> > ++++++++++++++++++
> >  1 file changed, 178 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> > 
> > diff --git
> > a/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > b/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > new file mode 100644
> > index 000000000000..6e3ef8285e9a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > @@ -0,0 +1,178 @@
> > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > +%YAML 1.2
> > +---
> > +$id: 
> > http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Microchip Sparx5 Ethernet switch controller
> > +
> > +maintainers:
> > +  - Lars Povlsen <lars.povlsen@microchip.com>
> > +  - Steen Hegelund <steen.hegelund@microchip.com>
> > +
> > +description: |
> > +  The SparX-5 Enterprise Ethernet switch family provides a rich
> > set of
> > +  Enterprise switching features such as advanced TCAM-based VLAN
> > and
> > +  QoS processing enabling delivery of differentiated services, and
> > +  security through TCAM-based frame processing using versatile
> > content
> > +  aware processor (VCAP).
> > +
> > +  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is
> > supported
> > +  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K
> > IPv4/3K
> > +  IPv6 (S,G) multicast groups.
> > +
> > +  L3 security features include source guard and reverse path
> > +  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite
> > and
> > +  IP tunnels (IP over GRE/IP).
> > +
> > +  The SparX-5 switch family targets managed Layer 2 and Layer 3
> > +  equipment in SMB, SME, and Enterprise where high port count
> > +  1G/2.5G/5G/10G switching with 10G/25G aggregation links is
> > required.
> > +
> > +properties:
> > +  $nodename:
> > +    pattern: "^switch@[0-9a-f]+$"
> > +
> > +  compatible:
> > +    const: microchip,sparx5-switch
> > +
> > +  reg:
> > +    minItems: 2
> > +
> > +  reg-names:
> > +    minItems: 2
> 
> This is the default based on 'items' length.

Does that mean that I should omit minItems here?

> 
> > +    items:
> > +      - const: devices
> > +      - const: gcb
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +    description: Interrupt used for reception of packets to the
> > CPU
> > +
> > +  ethernet-ports:
> > +    type: object
> > +    properties:
> > +      '#address-cells':
> > +        const: 1
> > +      '#size-cells':
> > +        const: 0
> > +
> > +    patternProperties:
> > +      "^port@[0-9]+$":
> > +        type: object
> > +        description: Switch ports
> > +
> > +        allOf:
> > +          - $ref: ethernet-controller.yaml#
> > +
> > +        properties:
> > +          reg:
> > +            description: Switch port number
> > +
> > +          max-speed:
> > +            maxItems: 1
> 
> Is that an array?

No it is just a single value.
> 
> > +            description: Bandwidth allocated to this port
> > +
> > +          phys:
> 
> How many? (maxItems)

I will add "maxItems: 1"

> 
> > +            description: phandle of a Ethernet Serdes PHY
> > +
> > +          phy-handle:
> > +            description: phandle of a Ethernet PHY
> > +
> > +          phy-mode:
> > +            description: Interface between the serdes and the phy
> 
> The whole set of modes defined is supported?

This driver does not impose any limits on phy-mode.  It is passed on to
the phy, so all modes are supported as I see it.

> 
> > +
> > +          sfp:
> > +            description: phandle of an SFP
> > +
> > +          managed:
> > +            maxItems: 1
> 
> An array?

No just a single item.


Thanks for your comments.

BR
Steen

> 
> > +            description: SFP management
> > +
> > +        required:
> > +          - reg
> > +          - max-speed
> > +          - phys
> > +
> > +        oneOf:
> > +          - required:
> > +              - phy-handle
> > +              - phy-mode
> > +          - required:
> > +              - sfp
> > +              - managed
> > +
> > +        additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - reg-names
> > +  - interrupts
> > +  - ethernet-ports
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +    switch: switch@600000000 {
> > +      compatible = "microchip,sparx5-switch";
> > +      reg =  <0x10000000 0x800000>,
> > +             <0x11010000 0x1b00000>;
> > +      reg-names = "devices", "gcb";
> > +
> > +      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
> > +      ethernet-ports {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        port0: port@0 {
> > +          reg = <0>;
> > +          max-speed = <1000>;
> > +          phys = <&serdes 13>;
> > +          phy-handle = <&phy0>;
> > +          phy-mode = "qsgmii";
> > +        };
> > +        /* ... */
> > +        /* Then the 25G interfaces */
> > +        port60: port@60 {
> > +          reg = <60>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 29>;
> > +          sfp = <&sfp_eth60>;
> > +          managed = "in-band-status";
> > +        };
> > +        port61: port@61 {
> > +          reg = <61>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 30>;
> > +          sfp = <&sfp_eth61>;
> > +          managed = "in-band-status";
> > +        };
> > +        port62: port@62 {
> > +          reg = <62>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 31>;
> > +          sfp = <&sfp_eth62>;
> > +          managed = "in-band-status";
> > +        };
> > +        port63: port@63 {
> > +          reg = <63>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 32>;
> > +          sfp = <&sfp_eth63>;
> > +          managed = "in-band-status";
> > +        };
> > +        /* Finally the Management interface */
> > +        port64: port@64 {
> > +          reg = <64>;
> > +          max-speed = <1000>;
> > +          phys = <&serdes 0>;
> > +          phy-handle = <&phy64>;
> > +          phy-mode = "sgmii";
> > +        };
> > +      };
> > +    };
> > +
> > +...
> > --
> > 2.29.2
> > 



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

* Re: [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings
@ 2020-12-22  7:30       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22  7:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Device Tree List, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, linux-arm-kernel,
	Mark Einon, Jakub Kicinski, Bjarni Jonasson, David S. Miller,
	Lars Povlsen

Hi Rob,

On Mon, 2020-12-21 at 14:40 -0700, Rob Herring wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On Thu, Dec 17, 2020 at 08:51:27AM +0100, Steen Hegelund wrote:
> > Document the Sparx5 switch device driver bindings
> > 
> > Signed-off-by: Steen Hegelund <steen.hegelund@microchip.com>
> > Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> > ---
> >  .../bindings/net/microchip,sparx5-switch.yaml | 178
> > ++++++++++++++++++
> >  1 file changed, 178 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
> > 
> > diff --git
> > a/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > b/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > new file mode 100644
> > index 000000000000..6e3ef8285e9a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/microchip,sparx5-
> > switch.yaml
> > @@ -0,0 +1,178 @@
> > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > +%YAML 1.2
> > +---
> > +$id: 
> > http://devicetree.org/schemas/net/microchip,sparx5-switch.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Microchip Sparx5 Ethernet switch controller
> > +
> > +maintainers:
> > +  - Lars Povlsen <lars.povlsen@microchip.com>
> > +  - Steen Hegelund <steen.hegelund@microchip.com>
> > +
> > +description: |
> > +  The SparX-5 Enterprise Ethernet switch family provides a rich
> > set of
> > +  Enterprise switching features such as advanced TCAM-based VLAN
> > and
> > +  QoS processing enabling delivery of differentiated services, and
> > +  security through TCAM-based frame processing using versatile
> > content
> > +  aware processor (VCAP).
> > +
> > +  IPv4/IPv6 Layer 3 (L3) unicast and multicast routing is
> > supported
> > +  with up to 18K IPv4/9K IPv6 unicast LPM entries and up to 9K
> > IPv4/3K
> > +  IPv6 (S,G) multicast groups.
> > +
> > +  L3 security features include source guard and reverse path
> > +  forwarding (uRPF) tasks. Additional L3 features include VRF-Lite
> > and
> > +  IP tunnels (IP over GRE/IP).
> > +
> > +  The SparX-5 switch family targets managed Layer 2 and Layer 3
> > +  equipment in SMB, SME, and Enterprise where high port count
> > +  1G/2.5G/5G/10G switching with 10G/25G aggregation links is
> > required.
> > +
> > +properties:
> > +  $nodename:
> > +    pattern: "^switch@[0-9a-f]+$"
> > +
> > +  compatible:
> > +    const: microchip,sparx5-switch
> > +
> > +  reg:
> > +    minItems: 2
> > +
> > +  reg-names:
> > +    minItems: 2
> 
> This is the default based on 'items' length.

Does that mean that I should omit minItems here?

> 
> > +    items:
> > +      - const: devices
> > +      - const: gcb
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +    description: Interrupt used for reception of packets to the
> > CPU
> > +
> > +  ethernet-ports:
> > +    type: object
> > +    properties:
> > +      '#address-cells':
> > +        const: 1
> > +      '#size-cells':
> > +        const: 0
> > +
> > +    patternProperties:
> > +      "^port@[0-9]+$":
> > +        type: object
> > +        description: Switch ports
> > +
> > +        allOf:
> > +          - $ref: ethernet-controller.yaml#
> > +
> > +        properties:
> > +          reg:
> > +            description: Switch port number
> > +
> > +          max-speed:
> > +            maxItems: 1
> 
> Is that an array?

No it is just a single value.
> 
> > +            description: Bandwidth allocated to this port
> > +
> > +          phys:
> 
> How many? (maxItems)

I will add "maxItems: 1"

> 
> > +            description: phandle of a Ethernet Serdes PHY
> > +
> > +          phy-handle:
> > +            description: phandle of a Ethernet PHY
> > +
> > +          phy-mode:
> > +            description: Interface between the serdes and the phy
> 
> The whole set of modes defined is supported?

This driver does not impose any limits on phy-mode.  It is passed on to
the phy, so all modes are supported as I see it.

> 
> > +
> > +          sfp:
> > +            description: phandle of an SFP
> > +
> > +          managed:
> > +            maxItems: 1
> 
> An array?

No just a single item.


Thanks for your comments.

BR
Steen

> 
> > +            description: SFP management
> > +
> > +        required:
> > +          - reg
> > +          - max-speed
> > +          - phys
> > +
> > +        oneOf:
> > +          - required:
> > +              - phy-handle
> > +              - phy-mode
> > +          - required:
> > +              - sfp
> > +              - managed
> > +
> > +        additionalProperties: false
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - reg-names
> > +  - interrupts
> > +  - ethernet-ports
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +    switch: switch@600000000 {
> > +      compatible = "microchip,sparx5-switch";
> > +      reg =  <0x10000000 0x800000>,
> > +             <0x11010000 0x1b00000>;
> > +      reg-names = "devices", "gcb";
> > +
> > +      interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
> > +      ethernet-ports {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        port0: port@0 {
> > +          reg = <0>;
> > +          max-speed = <1000>;
> > +          phys = <&serdes 13>;
> > +          phy-handle = <&phy0>;
> > +          phy-mode = "qsgmii";
> > +        };
> > +        /* ... */
> > +        /* Then the 25G interfaces */
> > +        port60: port@60 {
> > +          reg = <60>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 29>;
> > +          sfp = <&sfp_eth60>;
> > +          managed = "in-band-status";
> > +        };
> > +        port61: port@61 {
> > +          reg = <61>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 30>;
> > +          sfp = <&sfp_eth61>;
> > +          managed = "in-band-status";
> > +        };
> > +        port62: port@62 {
> > +          reg = <62>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 31>;
> > +          sfp = <&sfp_eth62>;
> > +          managed = "in-band-status";
> > +        };
> > +        port63: port@63 {
> > +          reg = <63>;
> > +          max-speed = <25000>;
> > +          phys = <&serdes 32>;
> > +          sfp = <&sfp_eth63>;
> > +          managed = "in-band-status";
> > +        };
> > +        /* Finally the Management interface */
> > +        port64: port@64 {
> > +          reg = <64>;
> > +          max-speed = <1000>;
> > +          phys = <&serdes 0>;
> > +          phy-handle = <&phy64>;
> > +          phy-mode = "sgmii";
> > +        };
> > +      };
> > +    };
> > +
> > +...
> > --
> > 2.29.2
> > 



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-19 19:51     ` Andrew Lunn
@ 2020-12-22  9:46       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22  9:46 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Hi Andrew,

On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> > +     /* Create a phylink for PHY management.  Also handles SFPs */
> > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> > +     spx5_port->phylink_co
> > nfig.type = PHYLINK_NETDEV;
> > +     spx5_port->phylink_config.pcs_poll = true;
> > +
> > +     /* phylink needs a valid interface mode to parse dt node */
> > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
> > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
> 
> Maybe just enforce a valid value in DT?

Maybe I need to clarify that you must choose between an Ethernet cuPHY
or an SFP, so it is optional.
> 
> > +/* Configuration */
> > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> > +{
> > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> > +}
> 
> That is a rather odd definition of copper.

Should I rather use a bool property to select between the two options
(cuPHY or SFP)?

> 
> > diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > new file mode 100644
> > index 000000000000..6f9282e9d3f4
> > --- /dev/null
> > +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > @@ -0,0 +1,203 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/* Microchip Sparx5 Switch driver
> > + *
> > + * Copyright (c) 2020 Microchip Technology Inc. and its
> > subsidiaries.
> > + */
> > +
> > +#include "sparx5_main.h"
> 
> I don't actually know what is preferred here, but very few drivers
> i've reviewed put all the required headers into another header
> file. They normally list them in each .c file.

I will look at reworking this. 

> 
> > +static int sparx5_port_open(struct net_device *ndev)
> > +{
> > +     struct sparx5_port *port = netdev_priv(ndev);
> > +     int err = 0;
> > +
> > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
> > 0);
> > +     if (err) {
> > +             netdev_err(ndev, "Could not attach to PHY\n");
> > +             return err;
> > +     }
> > +
> > +     phylink_start(port->phylink);
> > +
> > +     if (!ndev->phydev) {
> 
> Humm. When is ndev->phydev set? I don't think phylink ever sets it.

Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
the phydev.

> 
> > +             /* power up serdes */
> > +             port->conf.power_down = false;
> > +             err = phy_power_on(port->serdes);
> > +             if (err)
> > +                     netdev_err(ndev, "%s failed\n", __func__);
> > +     }
> > +
> > +     return err;
> > +}
> 
> > +struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32
> > portno)
> > +{
> > +     struct net_device *ndev;
> > +     struct sparx5_port *spx5_port;
> > +
> > +     ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct
> > sparx5_port));
> > +     if (!ndev)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     SET_NETDEV_DEV(ndev, sparx5->dev);
> > +     spx5_port = netdev_priv(ndev);
> > +     spx5_port->ndev = ndev;
> > +     spx5_port->sparx5 = sparx5;
> > +     spx5_port->portno = portno;
> > +     sparx5_set_port_ifh(spx5_port->ifh, portno);
> > +     snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
> > +
> > +     ether_setup(ndev);
> 
> devm_alloc_etherdev() should of already called ether_setup().

Ah - yes it is the setup(dev) call in alloc_netdev_mqs. I will remove
that then.
> 
> > +     ndev->netdev_ops = &sparx5_port_netdev_ops;
> > +     ndev->features |= NETIF_F_LLTX; /* software tx */
> > +
> > +     ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
> > +     ndev->dev_addr[ETH_ALEN - 1] += portno + 1;
> 
> That will cause some surprises with wrap around. Use eth_addr_inc()

OK - will do.

> 
> > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
> > byte_swap)
> > +{
> > +     int i, byte_cnt = 0;
> > +     bool eof_flag = false, pruned_flag = false, abort_flag =
> > false;
> > +     u32 ifh[IFH_LEN];
> > +     struct sk_buff *skb;
> > +     struct frame_info fi;
> > +     struct sparx5_port *port;
> > +     struct net_device *netdev;
> > +     u32 *rxbuf;
> > +
> > +     /* Get IFH */
> > +     for (i = 0; i < IFH_LEN; i++)
> > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +
> > +     /* Decode IFH (whats needed) */
> > +     sparx5_ifh_parse(ifh, &fi);
> > +
> > +     /* Map to port netdev */
> > +     port = fi.src_port < SPX5_PORTS ?
> > +             sparx5->ports[fi.src_port] : NULL;
> > +     if (!port || !port->ndev) {
> > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
> > fi.src_port);
> > +             sparx5_xtr_flush(sparx5, grp);
> > +             return;
> > +     }
> > +
> > +     /* Have netdev, get skb */
> > +     netdev = port->ndev;
> > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> > +     if (!skb) {
> > +             sparx5_xtr_flush(sparx5, grp);
> > +             dev_err(sparx5->dev, "No skb allocated\n");
> > +             return;
> > +     }
> > +     rxbuf = (u32 *)skb->data;
> > +
> > +     /* Now, pull frame data */
> > +     while (!eof_flag) {
> > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +             u32 cmp = val;
> > +
> > +             if (byte_swap)
> > +                     cmp = ntohl((__force __be32)val);
> > +
> > +             switch (cmp) {
> > +             case XTR_NOT_READY:
> > +                     break;
> > +             case XTR_ABORT:
> > +                     /* No accompanying data */
> > +                     abort_flag = true;
> > +                     eof_flag = true;
> > +                     break;
> > +             case XTR_EOF_0:
> > +             case XTR_EOF_1:
> > +             case XTR_EOF_2:
> > +             case XTR_EOF_3:
> > +                     /* This assumes STATUS_WORD_POS == 1, Status
> > +                      * just after last data
> > +                      */
> > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
> > +                     eof_flag = true;
> > +                     break;
> > +             case XTR_PRUNED:
> > +                     /* But get the last 4 bytes as well */
> > +                     eof_flag = true;
> > +                     pruned_flag = true;
> > +                     fallthrough;
> > +             case XTR_ESCAPE:
> > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +                     byte_cnt += 4;
> > +                     rxbuf++;
> > +                     break;
> > +             default:
> > +                     *rxbuf = val;
> > +                     byte_cnt += 4;
> > +                     rxbuf++;
> > +             }
> > +     }
> > +
> > +     if (abort_flag || pruned_flag || !eof_flag) {
> > +             netdev_err(netdev, "Discarded frame: abort:%d
> > pruned:%d eof:%d\n",
> > +                        abort_flag, pruned_flag, eof_flag);
> > +             kfree_skb(skb);
> > +             return;
> > +     }
> > +
> > +     if (!netif_oper_up(netdev)) {
> > +             netdev_err(netdev, "Discarded frame: Interface not
> > up\n");
> > +             kfree_skb(skb);
> > +             return;
> > +     }
> 
> Why is it sending frames when it is not up?

This is intended for received frames. A situation where the lower
layers have been enabled correctly but not the port.

> 
> > +static int sparx5_inject(struct sparx5 *sparx5,
> > +                      u32 *ifh,
> > +                      struct sk_buff *skb)
> > +{
> > +     u32 val, w, count;
> > +     int grp = INJ_QUEUE;
> > +     u8 *buf;
> > +
> > +     val = spx5_rd(sparx5, QS_INJ_STATUS);
> > +     if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
> > +             pr_err("Injection: Queue not ready: 0x%lx\n",
> > +                    QS_INJ_STATUS_FIFO_RDY_GET(val));
> > +             return -1;
> 
> Always use -ESOMETHING.

Yes.

> 
> > +     }
> > +
> > +     if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
> > +             pr_err("Injection: Watermark reached: 0x%lx\n",
> > +                    QS_INJ_STATUS_WMARK_REACHED_GET(val));
> > +             return -1;
> > +     }
> > +
> > +     /* Indicate SOF */
> > +     spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
> > +             QS_INJ_CTRL_GAP_SIZE_SET(1),
> > +             sparx5, QS_INJ_CTRL(grp));
> > +
> > +     // Write the IFH to the chip.
> > +     for (w = 0; w < IFH_LEN; w++)
> > +             spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
> > +
> > +     /* Write words, round up */
> > +     count = ((skb->len + 3) / 4);
> > +     buf = skb->data;
> > +     for (w = 0; w < count; w++, buf += 4) {
> > +             val = get_unaligned((const u32 *)buf);
> > +             spx5_wr(val, sparx5, QS_INJ_WR(grp));
> > +     }
> 
> No DMA? What sort of performance do you get? Enough for the odd BPDU,
> IGMP frame etc, but i guess you don't want any real bulk data to be
> sent this way?

Yes the register based injection/extration is not going to be fast, but
the FDMA and its driver is being sent later as separate series to keep
the size of this review down.

> 
> > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> > +{
> > +     struct sparx5 *sparx5 = _sparx5;
> > +
> > +     /* Check data in queue */
> > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> > +
> > +     return IRQ_HANDLED;
> > +}
> 
> Is there any sort of limit how many times this will loop? If somebody
> is blasting 10Gbps at the CPU, will it ever get out of this loop?

Hmmm, not at the moment but this is because the FDMA driver is intended
to be used in these scenarios.

> 
>    Andrew

Thanks for your comments

BR
Steen


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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-22  9:46       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22  9:46 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

Hi Andrew,

On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> > +     /* Create a phylink for PHY management.  Also handles SFPs */
> > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> > +     spx5_port->phylink_co
> > nfig.type = PHYLINK_NETDEV;
> > +     spx5_port->phylink_config.pcs_poll = true;
> > +
> > +     /* phylink needs a valid interface mode to parse dt node */
> > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
> > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
> 
> Maybe just enforce a valid value in DT?

Maybe I need to clarify that you must choose between an Ethernet cuPHY
or an SFP, so it is optional.
> 
> > +/* Configuration */
> > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> > +{
> > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> > +}
> 
> That is a rather odd definition of copper.

Should I rather use a bool property to select between the two options
(cuPHY or SFP)?

> 
> > diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > new file mode 100644
> > index 000000000000..6f9282e9d3f4
> > --- /dev/null
> > +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
> > @@ -0,0 +1,203 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/* Microchip Sparx5 Switch driver
> > + *
> > + * Copyright (c) 2020 Microchip Technology Inc. and its
> > subsidiaries.
> > + */
> > +
> > +#include "sparx5_main.h"
> 
> I don't actually know what is preferred here, but very few drivers
> i've reviewed put all the required headers into another header
> file. They normally list them in each .c file.

I will look at reworking this. 

> 
> > +static int sparx5_port_open(struct net_device *ndev)
> > +{
> > +     struct sparx5_port *port = netdev_priv(ndev);
> > +     int err = 0;
> > +
> > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
> > 0);
> > +     if (err) {
> > +             netdev_err(ndev, "Could not attach to PHY\n");
> > +             return err;
> > +     }
> > +
> > +     phylink_start(port->phylink);
> > +
> > +     if (!ndev->phydev) {
> 
> Humm. When is ndev->phydev set? I don't think phylink ever sets it.

Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
the phydev.

> 
> > +             /* power up serdes */
> > +             port->conf.power_down = false;
> > +             err = phy_power_on(port->serdes);
> > +             if (err)
> > +                     netdev_err(ndev, "%s failed\n", __func__);
> > +     }
> > +
> > +     return err;
> > +}
> 
> > +struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32
> > portno)
> > +{
> > +     struct net_device *ndev;
> > +     struct sparx5_port *spx5_port;
> > +
> > +     ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct
> > sparx5_port));
> > +     if (!ndev)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     SET_NETDEV_DEV(ndev, sparx5->dev);
> > +     spx5_port = netdev_priv(ndev);
> > +     spx5_port->ndev = ndev;
> > +     spx5_port->sparx5 = sparx5;
> > +     spx5_port->portno = portno;
> > +     sparx5_set_port_ifh(spx5_port->ifh, portno);
> > +     snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
> > +
> > +     ether_setup(ndev);
> 
> devm_alloc_etherdev() should of already called ether_setup().

Ah - yes it is the setup(dev) call in alloc_netdev_mqs. I will remove
that then.
> 
> > +     ndev->netdev_ops = &sparx5_port_netdev_ops;
> > +     ndev->features |= NETIF_F_LLTX; /* software tx */
> > +
> > +     ether_addr_copy(ndev->dev_addr, sparx5->base_mac);
> > +     ndev->dev_addr[ETH_ALEN - 1] += portno + 1;
> 
> That will cause some surprises with wrap around. Use eth_addr_inc()

OK - will do.

> 
> > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
> > byte_swap)
> > +{
> > +     int i, byte_cnt = 0;
> > +     bool eof_flag = false, pruned_flag = false, abort_flag =
> > false;
> > +     u32 ifh[IFH_LEN];
> > +     struct sk_buff *skb;
> > +     struct frame_info fi;
> > +     struct sparx5_port *port;
> > +     struct net_device *netdev;
> > +     u32 *rxbuf;
> > +
> > +     /* Get IFH */
> > +     for (i = 0; i < IFH_LEN; i++)
> > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +
> > +     /* Decode IFH (whats needed) */
> > +     sparx5_ifh_parse(ifh, &fi);
> > +
> > +     /* Map to port netdev */
> > +     port = fi.src_port < SPX5_PORTS ?
> > +             sparx5->ports[fi.src_port] : NULL;
> > +     if (!port || !port->ndev) {
> > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
> > fi.src_port);
> > +             sparx5_xtr_flush(sparx5, grp);
> > +             return;
> > +     }
> > +
> > +     /* Have netdev, get skb */
> > +     netdev = port->ndev;
> > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> > +     if (!skb) {
> > +             sparx5_xtr_flush(sparx5, grp);
> > +             dev_err(sparx5->dev, "No skb allocated\n");
> > +             return;
> > +     }
> > +     rxbuf = (u32 *)skb->data;
> > +
> > +     /* Now, pull frame data */
> > +     while (!eof_flag) {
> > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +             u32 cmp = val;
> > +
> > +             if (byte_swap)
> > +                     cmp = ntohl((__force __be32)val);
> > +
> > +             switch (cmp) {
> > +             case XTR_NOT_READY:
> > +                     break;
> > +             case XTR_ABORT:
> > +                     /* No accompanying data */
> > +                     abort_flag = true;
> > +                     eof_flag = true;
> > +                     break;
> > +             case XTR_EOF_0:
> > +             case XTR_EOF_1:
> > +             case XTR_EOF_2:
> > +             case XTR_EOF_3:
> > +                     /* This assumes STATUS_WORD_POS == 1, Status
> > +                      * just after last data
> > +                      */
> > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
> > +                     eof_flag = true;
> > +                     break;
> > +             case XTR_PRUNED:
> > +                     /* But get the last 4 bytes as well */
> > +                     eof_flag = true;
> > +                     pruned_flag = true;
> > +                     fallthrough;
> > +             case XTR_ESCAPE:
> > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> > +                     byte_cnt += 4;
> > +                     rxbuf++;
> > +                     break;
> > +             default:
> > +                     *rxbuf = val;
> > +                     byte_cnt += 4;
> > +                     rxbuf++;
> > +             }
> > +     }
> > +
> > +     if (abort_flag || pruned_flag || !eof_flag) {
> > +             netdev_err(netdev, "Discarded frame: abort:%d
> > pruned:%d eof:%d\n",
> > +                        abort_flag, pruned_flag, eof_flag);
> > +             kfree_skb(skb);
> > +             return;
> > +     }
> > +
> > +     if (!netif_oper_up(netdev)) {
> > +             netdev_err(netdev, "Discarded frame: Interface not
> > up\n");
> > +             kfree_skb(skb);
> > +             return;
> > +     }
> 
> Why is it sending frames when it is not up?

This is intended for received frames. A situation where the lower
layers have been enabled correctly but not the port.

> 
> > +static int sparx5_inject(struct sparx5 *sparx5,
> > +                      u32 *ifh,
> > +                      struct sk_buff *skb)
> > +{
> > +     u32 val, w, count;
> > +     int grp = INJ_QUEUE;
> > +     u8 *buf;
> > +
> > +     val = spx5_rd(sparx5, QS_INJ_STATUS);
> > +     if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp))) {
> > +             pr_err("Injection: Queue not ready: 0x%lx\n",
> > +                    QS_INJ_STATUS_FIFO_RDY_GET(val));
> > +             return -1;
> 
> Always use -ESOMETHING.

Yes.

> 
> > +     }
> > +
> > +     if (QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)) {
> > +             pr_err("Injection: Watermark reached: 0x%lx\n",
> > +                    QS_INJ_STATUS_WMARK_REACHED_GET(val));
> > +             return -1;
> > +     }
> > +
> > +     /* Indicate SOF */
> > +     spx5_wr(QS_INJ_CTRL_SOF_SET(1) |
> > +             QS_INJ_CTRL_GAP_SIZE_SET(1),
> > +             sparx5, QS_INJ_CTRL(grp));
> > +
> > +     // Write the IFH to the chip.
> > +     for (w = 0; w < IFH_LEN; w++)
> > +             spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp));
> > +
> > +     /* Write words, round up */
> > +     count = ((skb->len + 3) / 4);
> > +     buf = skb->data;
> > +     for (w = 0; w < count; w++, buf += 4) {
> > +             val = get_unaligned((const u32 *)buf);
> > +             spx5_wr(val, sparx5, QS_INJ_WR(grp));
> > +     }
> 
> No DMA? What sort of performance do you get? Enough for the odd BPDU,
> IGMP frame etc, but i guess you don't want any real bulk data to be
> sent this way?

Yes the register based injection/extration is not going to be fast, but
the FDMA and its driver is being sent later as separate series to keep
the size of this review down.

> 
> > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> > +{
> > +     struct sparx5 *sparx5 = _sparx5;
> > +
> > +     /* Check data in queue */
> > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> > +
> > +     return IRQ_HANDLED;
> > +}
> 
> Is there any sort of limit how many times this will loop? If somebody
> is blasting 10Gbps at the CPU, will it ever get out of this loop?

Hmmm, not at the moment but this is because the FDMA driver is intended
to be used in these scenarios.

> 
>    Andrew

Thanks for your comments

BR
Steen


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
  2020-12-21  0:58   ` Florian Fainelli
@ 2020-12-22 11:29     ` Lars Povlsen
  -1 siblings, 0 replies; 75+ messages in thread
From: Lars Povlsen @ 2020-12-22 11:29 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Steen Hegelund, David S. Miller, Jakub Kicinski, Andrew Lunn,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel, Torkil.Oelgaard,
	Ioannis.Kotleas


Florian Fainelli writes:

> On 12/16/2020 11:51 PM, Steen Hegelund wrote:
>> This series provides the Microchip Sparx5 Switch Driver
>>
>> The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
>> Ethernet ports and up to 200 Gbps of switching bandwidth.
>>
>> It provides a rich set of Ethernet switching features such as hierarchical
>> QoS, hardware-based OAM  and service activation testing, protection
>> switching, IEEE 1588, and Synchronous Ethernet.
>>
>> Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
>> MEF CE
>> 2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
>>   classification in both ingress and egress.
>>
>> Per-EVC features include advanced L3-aware classification, a rich set of
>> statistics, OAM for end-to-end performance monitoring, and dual-rate
>> policing and shaping.
>>
>> Time sensitive networking (TSN) is supported through a comprehensive set of
>> features including frame preemption, cut-through, frame replication and
>> elimination for reliability, enhanced scheduling: credit-based shaping,
>> time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
>> and filtering.
>>
>> Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
>> low-latency deterministic networking for Fronthaul, Carrier, and Industrial
>> Ethernet.
>>
>> The Sparx5 switch family consists of following SKUs:
>>
>> - VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 6 *10G
>>   - 16 * 2.5G + 2 * 10G
>>   - 24 * 1G + 4 * 10G
>>
>> - VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 9 * 10G
>>   - 16 * 2.5G + 4 * 10G
>>   - 48 * 1G + 4 * 10G
>>
>> - VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 12 * 10G
>>   - 16 * 2.5G + 8 * 10G
>>   - 48 * 1G + 8 * 10G
>>
>> - VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 16 * 10G
>>   - 10 * 10G + 2 * 25G
>>   - 16 * 2.5G + 10 * 10G
>>   - 48 * 1G + 10 * 10G
>>
>> - VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 20 * 10G
>>   - 8 * 25G
>>
>> In addition, the device supports one 10/100/1000/2500/5000 Mbps
>> SGMII/SerDes node processor interface (NPI) Ethernet port.
>>
>> The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.
>>
>> - PCB134 main networking features:
>>   - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
>>   - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
>>     speed).
>>   - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
>>     (on-board VSC8211 PHY connected to Sparx5 through SGMII).
>>
>> - PCB135 main networking features:
>>   - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
>>     connected to VSC7558 through QSGMII.
>>   - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
>>     each port connects to VSC7558 through SFI.
>>   - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
>>     speed.
>>   - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
>>     VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
>>     using a loopback add-on PCB)
>>
>> This series provides support for:
>>   - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
>>     devices and media types.
>>   - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
>>     1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
>>   - SerDes configuration via the Sparx5 SerDes driver (see below).
>>   - Host mode providing register based injection and extraction.
>>   - Switch mode providing MAC/VLAN table learning and Layer2 switching
>>     offloaded to the Sparx5 switch.
>>   - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
>>     configuration and statistics via ethtool.
>>
>> More support will be added at a later stage.
>>
>> The Sparx5 Switch chip register model can be browsed here:
>> Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html
>
> Out of curiosity, what tool was used to generate the register
> information page? It looks really neat and well organized.

Florian,

It is an in-house tool. The input data is in a proprietary XML-like
format.

We're pleased that you like it, we do too. We are also pleased that
being a Microchip entity, we can actually make this kind of information
public.

I'll pass your praise on.

---Lars

--
Lars Povlsen,
Microchip

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

* Re: [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver
@ 2020-12-22 11:29     ` Lars Povlsen
  0 siblings, 0 replies; 75+ messages in thread
From: Lars Povlsen @ 2020-12-22 11:29 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Andrew Lunn, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Steen Hegelund,
	Russell King, Microchip Linux Driver Support, Torkil.Oelgaard,
	linux-arm-kernel, Ioannis.Kotleas, Mark Einon, Jakub Kicinski,
	Masahiro Yamada, David S. Miller, Lars Povlsen


Florian Fainelli writes:

> On 12/16/2020 11:51 PM, Steen Hegelund wrote:
>> This series provides the Microchip Sparx5 Switch Driver
>>
>> The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
>> Ethernet ports and up to 200 Gbps of switching bandwidth.
>>
>> It provides a rich set of Ethernet switching features such as hierarchical
>> QoS, hardware-based OAM  and service activation testing, protection
>> switching, IEEE 1588, and Synchronous Ethernet.
>>
>> Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
>> MEF CE
>> 2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
>>   classification in both ingress and egress.
>>
>> Per-EVC features include advanced L3-aware classification, a rich set of
>> statistics, OAM for end-to-end performance monitoring, and dual-rate
>> policing and shaping.
>>
>> Time sensitive networking (TSN) is supported through a comprehensive set of
>> features including frame preemption, cut-through, frame replication and
>> elimination for reliability, enhanced scheduling: credit-based shaping,
>> time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
>> and filtering.
>>
>> Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
>> low-latency deterministic networking for Fronthaul, Carrier, and Industrial
>> Ethernet.
>>
>> The Sparx5 switch family consists of following SKUs:
>>
>> - VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 6 *10G
>>   - 16 * 2.5G + 2 * 10G
>>   - 24 * 1G + 4 * 10G
>>
>> - VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 9 * 10G
>>   - 16 * 2.5G + 4 * 10G
>>   - 48 * 1G + 4 * 10G
>>
>> - VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 12 * 10G
>>   - 16 * 2.5G + 8 * 10G
>>   - 48 * 1G + 8 * 10G
>>
>> - VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 16 * 10G
>>   - 10 * 10G + 2 * 25G
>>   - 16 * 2.5G + 10 * 10G
>>   - 48 * 1G + 10 * 10G
>>
>> - VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
>>   port configurations:
>>   - 20 * 10G
>>   - 8 * 25G
>>
>> In addition, the device supports one 10/100/1000/2500/5000 Mbps
>> SGMII/SerDes node processor interface (NPI) Ethernet port.
>>
>> The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.
>>
>> - PCB134 main networking features:
>>   - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
>>   - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
>>     speed).
>>   - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
>>     (on-board VSC8211 PHY connected to Sparx5 through SGMII).
>>
>> - PCB135 main networking features:
>>   - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
>>     connected to VSC7558 through QSGMII.
>>   - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
>>     each port connects to VSC7558 through SFI.
>>   - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
>>     speed.
>>   - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
>>     VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
>>     using a loopback add-on PCB)
>>
>> This series provides support for:
>>   - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
>>     devices and media types.
>>   - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
>>     1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
>>   - SerDes configuration via the Sparx5 SerDes driver (see below).
>>   - Host mode providing register based injection and extraction.
>>   - Switch mode providing MAC/VLAN table learning and Layer2 switching
>>     offloaded to the Sparx5 switch.
>>   - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
>>     configuration and statistics via ethtool.
>>
>> More support will be added at a later stage.
>>
>> The Sparx5 Switch chip register model can be browsed here:
>> Link: https://microchip-ung.github.io/sparx-5_reginfo/reginfo_sparx-5.html
>
> Out of curiosity, what tool was used to generate the register
> information page? It looks really neat and well organized.

Florian,

It is an in-house tool. The input data is in a proprietary XML-like
format.

We're pleased that you like it, we do too. We are also pleased that
being a Microchip entity, we can actually make this kind of information
public.

I'll pass your praise on.

---Lars

--
Lars Povlsen,
Microchip

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-19 19:11     ` Andrew Lunn
@ 2020-12-22 13:50       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22 13:50 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Hi Andrew,

On Sat, 2020-12-19 at 20:11 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On Thu, Dec 17, 2020 at 08:51:28AM +0100, Steen Hegelund wrote:
> 
> > +static struct sparx5_io_resource sparx5_iomap[] =  {
> 
> This could be made const i think,.

Yes

> 
> > +     { TARGET_DEV2G5,         0,         0 }, /* 0x610004000:
> > dev2g5_0 */
> > +     { TARGET_DEV5G,          0x4000,    0 }, /* 0x610008000:
> > dev5g_0 */
> > +     { TARGET_PCS5G_BR,       0x8000,    0 }, /* 0x61000c000:
> > pcs5g_br_0 */
> > +     { TARGET_DEV2G5 + 1,     0xc000,    0 }, /* 0x610010000:
> > dev2g5_1 */
> 
> > +static int sparx5_create_targets(struct sparx5 *sparx5)
> > +{
> > +     int idx, jdx;
> > +     struct resource *iores[IO_RANGES];
> > +     void __iomem *iomem[IO_RANGES];
> > +     void __iomem *begin[IO_RANGES];
> > +     int range_id[IO_RANGES];
> 
> Reverse Christmas tree. idx, jdx need to come last.

Yes - I will check the entire file for RCT...
> 
> > +
> > +     /* Check if done previously (deferred by serdes load) */
> > +     if (sparx5->regs[sparx5_iomap[0].id])
> > +             return 0;
> 
> Could you explain this a bit more. Do you mean -EPROBE_DEFER?

Yes that was the intended usage. I will change the startup flow to try
to avoid checking this-
> 
> > +static int sparx5_probe_port(struct sparx5 *sparx5,
> > +                          struct device_node *portnp,
> > +                          struct phy *serdes,
> > +                          u32 portno,
> > +                          struct sparx5_port_config *conf)
> > +{
> > +     struct sparx5_port *spx5_port;
> > +     struct net_device *ndev;
> > +     int err;
> > +
> > +     err = sparx5_create_targets(sparx5);
> > +     if (err)
> > +             return err;
> 
> This sees odd here. Don't sparx5_create_targets() create all the
> targets, where as this creates one specific port? Seems like
> sparx5_create_targets() should be in the devices as a whole probe,
> not
> the port probe.

You are right - the name does not really fit (anymore). I will rework
this.
> 
> > +     spx5_port = netdev_priv(ndev);
> > +     spx5_port->of_node = portnp;
> > +     spx5_port->serdes = serdes;
> > +     spx5_port->pvid = NULL_VID;
> > +     spx5_port->signd_internal = true;
> > +     spx5_port->signd_active_high = true;
> > +     spx5_port->signd_enable = true;
> > +     spx5_port->flow_control = false;
> > +     spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
> > +     spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
> > +     spx5_port->custom_etype = 0x8880; /* Vitesse */
> > +     conf->portmode = conf->phy_mode;
> > +     spx5_port->conf.speed = SPEED_UNKNOWN;
> > +     spx5_port->conf.power_down = true;
> > +     sparx5->ports[portno] = spx5_port;
> > +     return 0;
> 
> I'm also not sure this has the correct name. This does not look like
> a
> typical probe function.

Agree.
> 
> 
> > +}
> > +
> > +static int sparx5_init_switchcore(struct sparx5 *sparx5)
> > +{
> > +     u32 value, pending, jdx, idx;
> > +     struct {
> > +             bool gazwrap;
> > +             void __iomem *init_reg;
> > +             u32  init_val;
> > +     } ram, ram_init_list[] = {
> > +             {false, spx5_reg_get(sparx5, ANA_AC_STAT_RESET),
> > +              ANA_AC_STAT_RESET_RESET},
> > +             {false, spx5_reg_get(sparx5, ASM_STAT_CFG),
> > +              ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
> > +             {true,  spx5_reg_get(sparx5, QSYS_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, REW_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, VOP_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, ANA_AC_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, ASM_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, EACL_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, VCAP_SUPER_RAM_INIT),
> > 0},
> > +             {true,  spx5_reg_get(sparx5, DSM_RAM_INIT), 0}
> > +     };
> 
> Looks like this could be const as well. And this does not really fit
> reverse christmas tree.

I will update this.
> 
> > +
> > +     spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
> > +              EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> > +              sparx5,
> > +              EACL_POL_EACL_CFG);
> > +
> > +     spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
> > +              EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> > +              sparx5,
> > +              EACL_POL_EACL_CFG);
> > +
> > +     /* Initialize memories, if not done already */
> > +     value = spx5_rd(sparx5, HSCH_RESET_CFG);
> > +
> > +     if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
> > +             for (idx = 0; idx < 10; idx++) {
> > +                     pending = ARRAY_SIZE(ram_init_list);
> > +                     for (jdx = 0; jdx <
> > ARRAY_SIZE(ram_init_list); jdx++) {
> > +                             ram = ram_init_list[jdx];
> > +                             if (ram.gazwrap)
> > +                                     ram.init_val =
> > QSYS_RAM_INIT_RAM_INIT;
> > +
> > +                             if (idx == 0) {
> > +                                     writel(ram.init_val,
> > ram.init_reg);
> > +                             } else {
> > +                                     value = readl(ram.init_reg);
> > +                                     if ((value & ram.init_val) !=
> > +                                         ram.init_val) {
> > +                                             pending--;
> > +                                     }
> > +                             }
> > +                     }
> > +                     if (!pending)
> > +                             break;
> > +                     usleep_range(USEC_PER_MSEC, 2 *
> > USEC_PER_MSEC);
> > +             }
> 
> You are getting pretty deeply nested here. Might be better to pull
> this out into a helpers.

Yes.

> 
> > +
> > +             if (pending > 0) {
> > +                     /* Still initializing, should be complete in
> > +                      * less than 1ms
> > +                      */
> > +                     dev_err(sparx5->dev, "Memory initialization
> > error\n");
> > +                     return -EINVAL;
> > +             }
> > +     }
> > +
> > +     /* Reset counters */
> > +     spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5,
> > ANA_AC_STAT_RESET);
> > +     spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5,
> > ASM_STAT_CFG);
> > +
> > +     /* Enable switch-core and queue system */
> > +     spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5,
> > HSCH_RESET_CFG);
> > +
> > +     return 0;
> > +}
> > +
> > +static int sparx5_init_coreclock(struct sparx5 *sparx5)
> > +{
> > +     u32 clk_div, clk_period, pol_upd_int, idx;
> > +     enum sparx5_core_clockfreq freq = sparx5->coreclock;
> 
> More reverse christmas tree. Please review the whole driver.

I will do that.

> 
> > +
> > +     /* Verify if core clock frequency is supported on target.
> > +      * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
> > +      * freq. is used
> > +      */
> > +     switch (sparx5->target_ct) {
> > +     case SPX5_TARGET_CT_7546:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_250MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7549:
> > +     case SPX5_TARGET_CT_7552:
> > +     case SPX5_TARGET_CT_7556:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_500MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7558:
> > +     case SPX5_TARGET_CT_7558TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7546TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             break;
> > +     case SPX5_TARGET_CT_7549TSN:
> > +     case SPX5_TARGET_CT_7552TSN:
> > +     case SPX5_TARGET_CT_7556TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     default:
> > +             dev_err(sparx5->dev, "Target (%#04x) not
> > supported\n", sparx5->target_ct);
> 
> netdev is staying with 80 character lines. Please fold this, here and
> every where else, where possible. The exception is, you should not
> split a string.

Will do.

> 
> > +             return -ENODEV;
> > +     }
> > +
> > +     switch (freq) {
> > +     case SPX5_CORE_CLOCK_250MHZ:
> > +             clk_div = 10;
> > +             pol_upd_int = 312;
> > +             break;
> > +     case SPX5_CORE_CLOCK_500MHZ:
> > +             clk_div = 5;
> > +             pol_upd_int = 624;
> > +             break;
> > +     case SPX5_CORE_CLOCK_625MHZ:
> > +             clk_div = 4;
> > +             pol_upd_int = 780;
> > +             break;
> > +     default:
> > +             dev_err(sparx5->dev, "%s: Frequency (%d) not
> > supported on target (%#04x)\n",
> > +                     __func__,
> > +                     sparx5->coreclock, sparx5->target_ct);
> > +             return 0;
> 
> -EINVAL? Or is it not fatal to use an unsupported frequency?

Yes - it should be fatal.
> 
> > +static int sparx5_init(struct sparx5 *sparx5)
> > +{
> > +     u32 idx;
> > +
> > +     if (sparx5_create_targets(sparx5))
> > +             return -ENODEV;
> 
> Hum, sparx5_create_targets() again?

Yes that was due to the PROBE_DEFER - but I will go over this again.

> 
> > +
> > +     /* Read chip ID to check CPU interface */
> > +     sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> > +
> > +     sparx5->target_ct = (enum spx5_target_chiptype)
> > +             GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> > +
> > +     /* Initialize Switchcore and internal RAMs */
> > +     if (sparx5_init_switchcore(sparx5)) {
> > +             dev_err(sparx5->dev, "Switchcore initialization
> > error\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Initialize the LC-PLL (core clock) and set affected
> > registers */
> > +     if (sparx5_init_coreclock(sparx5)) {
> > +             dev_err(sparx5->dev, "LC-PLL initialization
> > error\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Setup own UPSIDs */
> > +     for (idx = 0; idx < 3; idx++) {
> > +             spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
> > +     }
> > +
> > +     /* Enable switch ports */
> > +     for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++) {
> > +             spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
> > +                      QFWD_SWITCH_PORT_MODE_PORT_ENA,
> > +                      sparx5,
> > +                      QFWD_SWITCH_PORT_MODE(idx));
> > +     }
> 
> What happens when you enable the ports? Why is this here, and not in
> the port specific open call?

The comment is not correct.  This is just enabling the CPU ports, so it
belongs with the other switch core initialization. I will update the
comment.

> 
> > +/* Some boards needs to map the SGPIO for signal detect explicitly
> > to the
> > + * port module
> > + */
> > +static void sparx5_board_init(struct sparx5 *sparx5)
> > +{
> > +     int idx;
> > +
> > +     if (!sparx5->sd_sgpio_remapping)
> > +             return;
> > +
> > +     /* Enable SGPIO Signal Detect remapping */
> > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > +              sparx5,
> > +              GCB_HW_SGPIO_SD_CFG);
> > +
> > +     /* Refer to LOS SGPIO */
> > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
> > +             if (sparx5->ports[idx]) {
> > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
> > {
> > +                             spx5_wr(sparx5->ports[idx]-
> > >conf.sd_sgpio,
> > +                                     sparx5,
> > +                                    
> > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> > +                     }
> > +             }
> > +     }
> > +}
> 
> I've not looked at how you do SFP integration yet. Is this the LOS
> from the SFP socket? Is there a Linux GPIO controller exported by
> this
> driver, so the SFP driver can use the GPIOs?

Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
location of the LOS, and use this by default without any driver
configuration.
But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
are not located in the expected position, so we have this board
remapping function to handle that aspect.

> 
> > +
> > +static int mchp_sparx5_probe(struct platform_device *pdev)
> > +{
> > +     struct device_node *np = pdev->dev.of_node;
> > +     struct sparx5 *sparx5;
> > +     struct device_node *ports, *portnp;
> > +     const u8 *mac_addr;
> > +     int err = 0;
> > +
> > +     if (!np && !pdev->dev.platform_data)
> > +             return -ENODEV;
> > +
> > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
> > GFP_KERNEL);
> > +     if (!sparx5)
> > +             return -ENOMEM;
> > +
> > +     platform_set_drvdata(pdev, sparx5);
> > +     sparx5->pdev = pdev;
> > +     sparx5->dev = &pdev->dev;
> > +
> > +     /* Default values, some from DT */
> > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> > +
> > +     mac_addr = of_get_mac_address(np);
> > +     if (IS_ERR_OR_NULL(mac_addr)) {
> > +             dev_info(sparx5->dev, "MAC addr was not set, use
> > random MAC\n");
> > +             eth_random_addr(sparx5->base_mac);
> > +             sparx5->base_mac[5] = 0;
> > +     } else {
> > +             ether_addr_copy(sparx5->base_mac, mac_addr);
> > +     }
> 
> The binding document does not say anything about a MAC address at the
> top level. What is this used for?

This the base MAC address used for generating the the switch NI's MAC
addresses.
> 
> +
> > +     if (sparx5_init(sparx5)) {
> > +             dev_err(sparx5->dev, "Init failed\n");
> > +             return -ENODEV;
> > +     }
> > +     ports = of_get_child_by_name(np, "ethernet-ports");
> > +     if (!ports) {
> > +             dev_err(sparx5->dev, "no ethernet-ports child node
> > found\n");
> > +             return -ENODEV;
> > +     }
> > +     sparx5->port_count = of_get_child_count(ports);
> > +
> > +     for_each_available_child_of_node(ports, portnp) {
> > +             struct sparx5_port_config config = {};
> > +             u32 portno;
> > +             struct phy *serdes;
> > +
> > +             err = of_property_read_u32(portnp, "reg", &portno);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port reg property
> > error\n");
> > +                     continue;
> > +             }
> > +             err = of_property_read_u32(portnp, "max-speed",
> > +                                        &config.max_speed);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port max-speed property
> > error\n");
> > +                     continue;
> > +             }
> > +             config.speed = SPEED_UNKNOWN;
> > +             err = of_property_read_u32(portnp, "sd_sgpio",
> > &config.sd_sgpio);
> 
> Not in the binding documentation. I think i need to withdraw my
> Reviewed-by :-(

Ooops - yes that is a mistake that these 2 items were not included.

> 
> > +             if (err)
> > +                     config.sd_sgpio = ~0;
> > +             else
> > +                     sparx5->sd_sgpio_remapping = true;
> > +             serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> > +             if (IS_ERR(serdes)) {
> > +                     err = PTR_ERR(serdes);
> > +                     if (err != -EPROBE_DEFER)
> > +                             dev_err(sparx5->dev,
> > +                                     "missing SerDes phys for
> > port%d\n",
> > +                                     portno);
> > +                     return err;
> > +             }
> > +
> > +             err = of_get_phy_mode(portnp, &config.phy_mode);
> > +             if (err)
> > +                     config.power_down = true;
> 
> You should indicate in the binding it is optional. And what happens
> when it is missing.

Will update the description.

> 
> > +             config.media_type = ETH_MEDIA_DAC;
> > +             config.serdes_reset = true;
> > +             config.portmode = config.phy_mode;
> > +             err = sparx5_probe_port(sparx5, portnp, serdes,
> > portno, &config);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port probe error\n");
> > +                     goto cleanup_ports;
> > +             }
> > +     }
> > +     sparx5_board_init(sparx5);
> > +
> > +cleanup_ports:
> > +     return err;
> 
> Seems missed named, no cleanup.

Ah - this comes later (as the driver was split in functional groups for
reviewing). I hope this is OK, as it is only temporary - I could add a
comment to that effect.

> 
> > +static int __init sparx5_switch_reset(void)
> > +{
> > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
> > +     struct regmap *cpu_ctrl, *gcb_ctrl;
> > +     u32 val;
> > +
> > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> > +     if (IS_ERR(cpu_ctrl)) {
> > +             pr_err("No '%s' syscon map\n", syscon_cpu);
> > +             return PTR_ERR(cpu_ctrl);
> > +     }
> > +
> > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> > +     if (IS_ERR(gcb_ctrl)) {
> > +             pr_err("No '%s' syscon map\n", syscon_gcb);
> > +             return PTR_ERR(gcb_ctrl);
> > +     }
> > +
> > +     /* Make sure the core is PROTECTED from reset */
> > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> > +
> > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> > +
> > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
> > val,
> > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
> > == 0,
> > +                               1, 100);
> > +}
> > +postcore_initcall(sparx5_switch_reset);
> 
> That is pretty unusual. Why cannot this be done at probe time?

The problem is that the switch core reset also affects (reset) the
SGPIO controller.

We tried to put this in the reset driver, but it was rejected. If the
reset is done at probe time, the SGPIO driver may already have
initialized state.

The switch core reset will then reset all SGPIO registers. 

> 
> > +/* Clock period in picoseconds */
> > +static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq
> > cclock)
> > +{
> > +     switch (cclock) {
> > +     case SPX5_CORE_CLOCK_250MHZ:
> > +             return 4000;
> > +     case SPX5_CORE_CLOCK_500MHZ:
> > +             return 2000;
> > +     case SPX5_CORE_CLOCK_625MHZ:
> > +     default:
> > +             return 1600;
> > +     }
> > +}
> 
> Is this something which is used in the hot path?

No - so maybe this should just be a regular function?
> 
> > --- /dev/null
> > +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> > @@ -0,0 +1,3922 @@
> > +/* SPDX-License-Identifier: GPL-2.0+
> > + * Microchip Sparx5 Switch driver
> > + *
> > + * Copyright (c) 2020 Microchip Technology Inc.
> > + */
> > +
> > +/* This file is autogenerated by cml-utils 2020-11-19 10:41:34
> > +0100.
> > + * Commit ID: f34790e69dc252103e2cc3e85b1a5e4d9e3aa190
> > + */
> 
> How reproducible this is generation process? If you have to run it
> again, will it keep the same order of lines?

As long as the CML (Chip Markup Language) file has not changed
(documentation fields not considered), this is reproducible. The tool
parses the XML nodes in a deterministic order.


> 
>        Andrew

Thanks for your comments
/Steen



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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-22 13:50       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-22 13:50 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

Hi Andrew,

On Sat, 2020-12-19 at 20:11 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> On Thu, Dec 17, 2020 at 08:51:28AM +0100, Steen Hegelund wrote:
> 
> > +static struct sparx5_io_resource sparx5_iomap[] =  {
> 
> This could be made const i think,.

Yes

> 
> > +     { TARGET_DEV2G5,         0,         0 }, /* 0x610004000:
> > dev2g5_0 */
> > +     { TARGET_DEV5G,          0x4000,    0 }, /* 0x610008000:
> > dev5g_0 */
> > +     { TARGET_PCS5G_BR,       0x8000,    0 }, /* 0x61000c000:
> > pcs5g_br_0 */
> > +     { TARGET_DEV2G5 + 1,     0xc000,    0 }, /* 0x610010000:
> > dev2g5_1 */
> 
> > +static int sparx5_create_targets(struct sparx5 *sparx5)
> > +{
> > +     int idx, jdx;
> > +     struct resource *iores[IO_RANGES];
> > +     void __iomem *iomem[IO_RANGES];
> > +     void __iomem *begin[IO_RANGES];
> > +     int range_id[IO_RANGES];
> 
> Reverse Christmas tree. idx, jdx need to come last.

Yes - I will check the entire file for RCT...
> 
> > +
> > +     /* Check if done previously (deferred by serdes load) */
> > +     if (sparx5->regs[sparx5_iomap[0].id])
> > +             return 0;
> 
> Could you explain this a bit more. Do you mean -EPROBE_DEFER?

Yes that was the intended usage. I will change the startup flow to try
to avoid checking this-
> 
> > +static int sparx5_probe_port(struct sparx5 *sparx5,
> > +                          struct device_node *portnp,
> > +                          struct phy *serdes,
> > +                          u32 portno,
> > +                          struct sparx5_port_config *conf)
> > +{
> > +     struct sparx5_port *spx5_port;
> > +     struct net_device *ndev;
> > +     int err;
> > +
> > +     err = sparx5_create_targets(sparx5);
> > +     if (err)
> > +             return err;
> 
> This sees odd here. Don't sparx5_create_targets() create all the
> targets, where as this creates one specific port? Seems like
> sparx5_create_targets() should be in the devices as a whole probe,
> not
> the port probe.

You are right - the name does not really fit (anymore). I will rework
this.
> 
> > +     spx5_port = netdev_priv(ndev);
> > +     spx5_port->of_node = portnp;
> > +     spx5_port->serdes = serdes;
> > +     spx5_port->pvid = NULL_VID;
> > +     spx5_port->signd_internal = true;
> > +     spx5_port->signd_active_high = true;
> > +     spx5_port->signd_enable = true;
> > +     spx5_port->flow_control = false;
> > +     spx5_port->max_vlan_tags = SPX5_PORT_MAX_TAGS_NONE;
> > +     spx5_port->vlan_type = SPX5_VLAN_PORT_TYPE_UNAWARE;
> > +     spx5_port->custom_etype = 0x8880; /* Vitesse */
> > +     conf->portmode = conf->phy_mode;
> > +     spx5_port->conf.speed = SPEED_UNKNOWN;
> > +     spx5_port->conf.power_down = true;
> > +     sparx5->ports[portno] = spx5_port;
> > +     return 0;
> 
> I'm also not sure this has the correct name. This does not look like
> a
> typical probe function.

Agree.
> 
> 
> > +}
> > +
> > +static int sparx5_init_switchcore(struct sparx5 *sparx5)
> > +{
> > +     u32 value, pending, jdx, idx;
> > +     struct {
> > +             bool gazwrap;
> > +             void __iomem *init_reg;
> > +             u32  init_val;
> > +     } ram, ram_init_list[] = {
> > +             {false, spx5_reg_get(sparx5, ANA_AC_STAT_RESET),
> > +              ANA_AC_STAT_RESET_RESET},
> > +             {false, spx5_reg_get(sparx5, ASM_STAT_CFG),
> > +              ASM_STAT_CFG_STAT_CNT_CLR_SHOT},
> > +             {true,  spx5_reg_get(sparx5, QSYS_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, REW_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, VOP_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, ANA_AC_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, ASM_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, EACL_RAM_INIT), 0},
> > +             {true,  spx5_reg_get(sparx5, VCAP_SUPER_RAM_INIT),
> > 0},
> > +             {true,  spx5_reg_get(sparx5, DSM_RAM_INIT), 0}
> > +     };
> 
> Looks like this could be const as well. And this does not really fit
> reverse christmas tree.

I will update this.
> 
> > +
> > +     spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(1),
> > +              EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> > +              sparx5,
> > +              EACL_POL_EACL_CFG);
> > +
> > +     spx5_rmw(EACL_POL_EACL_CFG_EACL_FORCE_INIT_SET(0),
> > +              EACL_POL_EACL_CFG_EACL_FORCE_INIT,
> > +              sparx5,
> > +              EACL_POL_EACL_CFG);
> > +
> > +     /* Initialize memories, if not done already */
> > +     value = spx5_rd(sparx5, HSCH_RESET_CFG);
> > +
> > +     if (!(value & HSCH_RESET_CFG_CORE_ENA)) {
> > +             for (idx = 0; idx < 10; idx++) {
> > +                     pending = ARRAY_SIZE(ram_init_list);
> > +                     for (jdx = 0; jdx <
> > ARRAY_SIZE(ram_init_list); jdx++) {
> > +                             ram = ram_init_list[jdx];
> > +                             if (ram.gazwrap)
> > +                                     ram.init_val =
> > QSYS_RAM_INIT_RAM_INIT;
> > +
> > +                             if (idx == 0) {
> > +                                     writel(ram.init_val,
> > ram.init_reg);
> > +                             } else {
> > +                                     value = readl(ram.init_reg);
> > +                                     if ((value & ram.init_val) !=
> > +                                         ram.init_val) {
> > +                                             pending--;
> > +                                     }
> > +                             }
> > +                     }
> > +                     if (!pending)
> > +                             break;
> > +                     usleep_range(USEC_PER_MSEC, 2 *
> > USEC_PER_MSEC);
> > +             }
> 
> You are getting pretty deeply nested here. Might be better to pull
> this out into a helpers.

Yes.

> 
> > +
> > +             if (pending > 0) {
> > +                     /* Still initializing, should be complete in
> > +                      * less than 1ms
> > +                      */
> > +                     dev_err(sparx5->dev, "Memory initialization
> > error\n");
> > +                     return -EINVAL;
> > +             }
> > +     }
> > +
> > +     /* Reset counters */
> > +     spx5_wr(ANA_AC_STAT_RESET_RESET_SET(1), sparx5,
> > ANA_AC_STAT_RESET);
> > +     spx5_wr(ASM_STAT_CFG_STAT_CNT_CLR_SHOT_SET(1), sparx5,
> > ASM_STAT_CFG);
> > +
> > +     /* Enable switch-core and queue system */
> > +     spx5_wr(HSCH_RESET_CFG_CORE_ENA_SET(1), sparx5,
> > HSCH_RESET_CFG);
> > +
> > +     return 0;
> > +}
> > +
> > +static int sparx5_init_coreclock(struct sparx5 *sparx5)
> > +{
> > +     u32 clk_div, clk_period, pol_upd_int, idx;
> > +     enum sparx5_core_clockfreq freq = sparx5->coreclock;
> 
> More reverse christmas tree. Please review the whole driver.

I will do that.

> 
> > +
> > +     /* Verify if core clock frequency is supported on target.
> > +      * If 'VTSS_CORE_CLOCK_DEFAULT' then the highest supported
> > +      * freq. is used
> > +      */
> > +     switch (sparx5->target_ct) {
> > +     case SPX5_TARGET_CT_7546:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_250MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_250MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7549:
> > +     case SPX5_TARGET_CT_7552:
> > +     case SPX5_TARGET_CT_7556:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_500MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_500MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7558:
> > +     case SPX5_TARGET_CT_7558TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             else if (sparx5->coreclock != SPX5_CORE_CLOCK_625MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     case SPX5_TARGET_CT_7546TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             break;
> > +     case SPX5_TARGET_CT_7549TSN:
> > +     case SPX5_TARGET_CT_7552TSN:
> > +     case SPX5_TARGET_CT_7556TSN:
> > +             if (sparx5->coreclock == SPX5_CORE_CLOCK_DEFAULT)
> > +                     freq = SPX5_CORE_CLOCK_625MHZ;
> > +             else if (sparx5->coreclock == SPX5_CORE_CLOCK_250MHZ)
> > +                     freq = 0; /* Not supported */
> > +             break;
> > +     default:
> > +             dev_err(sparx5->dev, "Target (%#04x) not
> > supported\n", sparx5->target_ct);
> 
> netdev is staying with 80 character lines. Please fold this, here and
> every where else, where possible. The exception is, you should not
> split a string.

Will do.

> 
> > +             return -ENODEV;
> > +     }
> > +
> > +     switch (freq) {
> > +     case SPX5_CORE_CLOCK_250MHZ:
> > +             clk_div = 10;
> > +             pol_upd_int = 312;
> > +             break;
> > +     case SPX5_CORE_CLOCK_500MHZ:
> > +             clk_div = 5;
> > +             pol_upd_int = 624;
> > +             break;
> > +     case SPX5_CORE_CLOCK_625MHZ:
> > +             clk_div = 4;
> > +             pol_upd_int = 780;
> > +             break;
> > +     default:
> > +             dev_err(sparx5->dev, "%s: Frequency (%d) not
> > supported on target (%#04x)\n",
> > +                     __func__,
> > +                     sparx5->coreclock, sparx5->target_ct);
> > +             return 0;
> 
> -EINVAL? Or is it not fatal to use an unsupported frequency?

Yes - it should be fatal.
> 
> > +static int sparx5_init(struct sparx5 *sparx5)
> > +{
> > +     u32 idx;
> > +
> > +     if (sparx5_create_targets(sparx5))
> > +             return -ENODEV;
> 
> Hum, sparx5_create_targets() again?

Yes that was due to the PROBE_DEFER - but I will go over this again.

> 
> > +
> > +     /* Read chip ID to check CPU interface */
> > +     sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> > +
> > +     sparx5->target_ct = (enum spx5_target_chiptype)
> > +             GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> > +
> > +     /* Initialize Switchcore and internal RAMs */
> > +     if (sparx5_init_switchcore(sparx5)) {
> > +             dev_err(sparx5->dev, "Switchcore initialization
> > error\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Initialize the LC-PLL (core clock) and set affected
> > registers */
> > +     if (sparx5_init_coreclock(sparx5)) {
> > +             dev_err(sparx5->dev, "LC-PLL initialization
> > error\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Setup own UPSIDs */
> > +     for (idx = 0; idx < 3; idx++) {
> > +             spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
> > +             spx5_wr(idx, sparx5, REW_OWN_UPSID(idx));
> > +     }
> > +
> > +     /* Enable switch ports */
> > +     for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++) {
> > +             spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
> > +                      QFWD_SWITCH_PORT_MODE_PORT_ENA,
> > +                      sparx5,
> > +                      QFWD_SWITCH_PORT_MODE(idx));
> > +     }
> 
> What happens when you enable the ports? Why is this here, and not in
> the port specific open call?

The comment is not correct.  This is just enabling the CPU ports, so it
belongs with the other switch core initialization. I will update the
comment.

> 
> > +/* Some boards needs to map the SGPIO for signal detect explicitly
> > to the
> > + * port module
> > + */
> > +static void sparx5_board_init(struct sparx5 *sparx5)
> > +{
> > +     int idx;
> > +
> > +     if (!sparx5->sd_sgpio_remapping)
> > +             return;
> > +
> > +     /* Enable SGPIO Signal Detect remapping */
> > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > +              sparx5,
> > +              GCB_HW_SGPIO_SD_CFG);
> > +
> > +     /* Refer to LOS SGPIO */
> > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
> > +             if (sparx5->ports[idx]) {
> > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
> > {
> > +                             spx5_wr(sparx5->ports[idx]-
> > >conf.sd_sgpio,
> > +                                     sparx5,
> > +                                    
> > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> > +                     }
> > +             }
> > +     }
> > +}
> 
> I've not looked at how you do SFP integration yet. Is this the LOS
> from the SFP socket? Is there a Linux GPIO controller exported by
> this
> driver, so the SFP driver can use the GPIOs?

Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
location of the LOS, and use this by default without any driver
configuration.
But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
are not located in the expected position, so we have this board
remapping function to handle that aspect.

> 
> > +
> > +static int mchp_sparx5_probe(struct platform_device *pdev)
> > +{
> > +     struct device_node *np = pdev->dev.of_node;
> > +     struct sparx5 *sparx5;
> > +     struct device_node *ports, *portnp;
> > +     const u8 *mac_addr;
> > +     int err = 0;
> > +
> > +     if (!np && !pdev->dev.platform_data)
> > +             return -ENODEV;
> > +
> > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
> > GFP_KERNEL);
> > +     if (!sparx5)
> > +             return -ENOMEM;
> > +
> > +     platform_set_drvdata(pdev, sparx5);
> > +     sparx5->pdev = pdev;
> > +     sparx5->dev = &pdev->dev;
> > +
> > +     /* Default values, some from DT */
> > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> > +
> > +     mac_addr = of_get_mac_address(np);
> > +     if (IS_ERR_OR_NULL(mac_addr)) {
> > +             dev_info(sparx5->dev, "MAC addr was not set, use
> > random MAC\n");
> > +             eth_random_addr(sparx5->base_mac);
> > +             sparx5->base_mac[5] = 0;
> > +     } else {
> > +             ether_addr_copy(sparx5->base_mac, mac_addr);
> > +     }
> 
> The binding document does not say anything about a MAC address at the
> top level. What is this used for?

This the base MAC address used for generating the the switch NI's MAC
addresses.
> 
> +
> > +     if (sparx5_init(sparx5)) {
> > +             dev_err(sparx5->dev, "Init failed\n");
> > +             return -ENODEV;
> > +     }
> > +     ports = of_get_child_by_name(np, "ethernet-ports");
> > +     if (!ports) {
> > +             dev_err(sparx5->dev, "no ethernet-ports child node
> > found\n");
> > +             return -ENODEV;
> > +     }
> > +     sparx5->port_count = of_get_child_count(ports);
> > +
> > +     for_each_available_child_of_node(ports, portnp) {
> > +             struct sparx5_port_config config = {};
> > +             u32 portno;
> > +             struct phy *serdes;
> > +
> > +             err = of_property_read_u32(portnp, "reg", &portno);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port reg property
> > error\n");
> > +                     continue;
> > +             }
> > +             err = of_property_read_u32(portnp, "max-speed",
> > +                                        &config.max_speed);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port max-speed property
> > error\n");
> > +                     continue;
> > +             }
> > +             config.speed = SPEED_UNKNOWN;
> > +             err = of_property_read_u32(portnp, "sd_sgpio",
> > &config.sd_sgpio);
> 
> Not in the binding documentation. I think i need to withdraw my
> Reviewed-by :-(

Ooops - yes that is a mistake that these 2 items were not included.

> 
> > +             if (err)
> > +                     config.sd_sgpio = ~0;
> > +             else
> > +                     sparx5->sd_sgpio_remapping = true;
> > +             serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> > +             if (IS_ERR(serdes)) {
> > +                     err = PTR_ERR(serdes);
> > +                     if (err != -EPROBE_DEFER)
> > +                             dev_err(sparx5->dev,
> > +                                     "missing SerDes phys for
> > port%d\n",
> > +                                     portno);
> > +                     return err;
> > +             }
> > +
> > +             err = of_get_phy_mode(portnp, &config.phy_mode);
> > +             if (err)
> > +                     config.power_down = true;
> 
> You should indicate in the binding it is optional. And what happens
> when it is missing.

Will update the description.

> 
> > +             config.media_type = ETH_MEDIA_DAC;
> > +             config.serdes_reset = true;
> > +             config.portmode = config.phy_mode;
> > +             err = sparx5_probe_port(sparx5, portnp, serdes,
> > portno, &config);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port probe error\n");
> > +                     goto cleanup_ports;
> > +             }
> > +     }
> > +     sparx5_board_init(sparx5);
> > +
> > +cleanup_ports:
> > +     return err;
> 
> Seems missed named, no cleanup.

Ah - this comes later (as the driver was split in functional groups for
reviewing). I hope this is OK, as it is only temporary - I could add a
comment to that effect.

> 
> > +static int __init sparx5_switch_reset(void)
> > +{
> > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
> > +     struct regmap *cpu_ctrl, *gcb_ctrl;
> > +     u32 val;
> > +
> > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> > +     if (IS_ERR(cpu_ctrl)) {
> > +             pr_err("No '%s' syscon map\n", syscon_cpu);
> > +             return PTR_ERR(cpu_ctrl);
> > +     }
> > +
> > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> > +     if (IS_ERR(gcb_ctrl)) {
> > +             pr_err("No '%s' syscon map\n", syscon_gcb);
> > +             return PTR_ERR(gcb_ctrl);
> > +     }
> > +
> > +     /* Make sure the core is PROTECTED from reset */
> > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> > +
> > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> > +
> > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
> > val,
> > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
> > == 0,
> > +                               1, 100);
> > +}
> > +postcore_initcall(sparx5_switch_reset);
> 
> That is pretty unusual. Why cannot this be done at probe time?

The problem is that the switch core reset also affects (reset) the
SGPIO controller.

We tried to put this in the reset driver, but it was rejected. If the
reset is done at probe time, the SGPIO driver may already have
initialized state.

The switch core reset will then reset all SGPIO registers. 

> 
> > +/* Clock period in picoseconds */
> > +static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq
> > cclock)
> > +{
> > +     switch (cclock) {
> > +     case SPX5_CORE_CLOCK_250MHZ:
> > +             return 4000;
> > +     case SPX5_CORE_CLOCK_500MHZ:
> > +             return 2000;
> > +     case SPX5_CORE_CLOCK_625MHZ:
> > +     default:
> > +             return 1600;
> > +     }
> > +}
> 
> Is this something which is used in the hot path?

No - so maybe this should just be a regular function?
> 
> > --- /dev/null
> > +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> > @@ -0,0 +1,3922 @@
> > +/* SPDX-License-Identifier: GPL-2.0+
> > + * Microchip Sparx5 Switch driver
> > + *
> > + * Copyright (c) 2020 Microchip Technology Inc.
> > + */
> > +
> > +/* This file is autogenerated by cml-utils 2020-11-19 10:41:34
> > +0100.
> > + * Commit ID: f34790e69dc252103e2cc3e85b1a5e4d9e3aa190
> > + */
> 
> How reproducible this is generation process? If you have to run it
> again, will it keep the same order of lines?

As long as the CML (Chip Markup Language) file has not changed
(documentation fields not considered), this is reproducible. The tool
parses the XML nodes in a deterministic order.


> 
>        Andrew

Thanks for your comments
/Steen



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-22  9:46       ` Steen Hegelund
@ 2020-12-22 14:41         ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 14:41 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

On Tue, Dec 22, 2020 at 10:46:12AM +0100, Steen Hegelund wrote:
> Hi Andrew,
> 
> On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you
> > know the content is safe
> > 
> > > +     /* Create a phylink for PHY management.  Also handles SFPs */
> > > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> > > +     spx5_port->phylink_co
> > > nfig.type = PHYLINK_NETDEV;
> > > +     spx5_port->phylink_config.pcs_poll = true;
> > > +
> > > +     /* phylink needs a valid interface mode to parse dt node */
> > > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
> > > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
> > 
> > Maybe just enforce a valid value in DT?
> 
> Maybe I need to clarify that you must choose between an Ethernet cuPHY
> or an SFP, so it is optional.

But you also need to watch out for somebody putting a copper modules
in an SFP port. phylink will then set the mode to SGMII for a 1G
copper module, etc.

> > > +/* Configuration */
> > > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> > > +{
> > > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> > > +}
> > 
> > That is a rather odd definition of copper.
> 
> Should I rather use a bool property to select between the two options
> (cuPHY or SFP)?

I guess what you are trying to indicate is between a hard wired Copper
PHY and an SFP cage? You have some sort of MII switch which allows the
MAC to be connected to either the QSGMII PHY, or an SFP cage? But
since the SFP cage could be populated with a copper PHY, and PHYLINK
will then instantiate a phylib copper PHY driver for it, looking at
phy_mode is not reliable. You need a property which selects the port,
not the technology.

> > > +static int sparx5_port_open(struct net_device *ndev)
> > > +{
> > > +     struct sparx5_port *port = netdev_priv(ndev);
> > > +     int err = 0;
> > > +
> > > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
> > > 0);
> > > +     if (err) {
> > > +             netdev_err(ndev, "Could not attach to PHY\n");
> > > +             return err;
> > > +     }
> > > +
> > > +     phylink_start(port->phylink);
> > > +
> > > +     if (!ndev->phydev) {
> > 
> > Humm. When is ndev->phydev set? I don't think phylink ever sets it.
> 
> Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
> the phydev.

Ah, O.K. But watch out for a copper SFP module!

> > > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
> > > byte_swap)
> > > +{
> > > +     int i, byte_cnt = 0;
> > > +     bool eof_flag = false, pruned_flag = false, abort_flag =
> > > false;
> > > +     u32 ifh[IFH_LEN];
> > > +     struct sk_buff *skb;
> > > +     struct frame_info fi;
> > > +     struct sparx5_port *port;
> > > +     struct net_device *netdev;
> > > +     u32 *rxbuf;
> > > +
> > > +     /* Get IFH */
> > > +     for (i = 0; i < IFH_LEN; i++)
> > > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +
> > > +     /* Decode IFH (whats needed) */
> > > +     sparx5_ifh_parse(ifh, &fi);
> > > +
> > > +     /* Map to port netdev */
> > > +     port = fi.src_port < SPX5_PORTS ?
> > > +             sparx5->ports[fi.src_port] : NULL;
> > > +     if (!port || !port->ndev) {
> > > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
> > > fi.src_port);
> > > +             sparx5_xtr_flush(sparx5, grp);
> > > +             return;
> > > +     }
> > > +
> > > +     /* Have netdev, get skb */
> > > +     netdev = port->ndev;
> > > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> > > +     if (!skb) {
> > > +             sparx5_xtr_flush(sparx5, grp);
> > > +             dev_err(sparx5->dev, "No skb allocated\n");
> > > +             return;
> > > +     }
> > > +     rxbuf = (u32 *)skb->data;
> > > +
> > > +     /* Now, pull frame data */
> > > +     while (!eof_flag) {
> > > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +             u32 cmp = val;
> > > +
> > > +             if (byte_swap)
> > > +                     cmp = ntohl((__force __be32)val);
> > > +
> > > +             switch (cmp) {
> > > +             case XTR_NOT_READY:
> > > +                     break;
> > > +             case XTR_ABORT:
> > > +                     /* No accompanying data */
> > > +                     abort_flag = true;
> > > +                     eof_flag = true;
> > > +                     break;
> > > +             case XTR_EOF_0:
> > > +             case XTR_EOF_1:
> > > +             case XTR_EOF_2:
> > > +             case XTR_EOF_3:
> > > +                     /* This assumes STATUS_WORD_POS == 1, Status
> > > +                      * just after last data
> > > +                      */
> > > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
> > > +                     eof_flag = true;
> > > +                     break;
> > > +             case XTR_PRUNED:
> > > +                     /* But get the last 4 bytes as well */
> > > +                     eof_flag = true;
> > > +                     pruned_flag = true;
> > > +                     fallthrough;
> > > +             case XTR_ESCAPE:
> > > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +                     byte_cnt += 4;
> > > +                     rxbuf++;
> > > +                     break;
> > > +             default:
> > > +                     *rxbuf = val;
> > > +                     byte_cnt += 4;
> > > +                     rxbuf++;
> > > +             }
> > > +     }
> > > +
> > > +     if (abort_flag || pruned_flag || !eof_flag) {
> > > +             netdev_err(netdev, "Discarded frame: abort:%d
> > > pruned:%d eof:%d\n",
> > > +                        abort_flag, pruned_flag, eof_flag);
> > > +             kfree_skb(skb);
> > > +             return;
> > > +     }
> > > +
> > > +     if (!netif_oper_up(netdev)) {
> > > +             netdev_err(netdev, "Discarded frame: Interface not
> > > up\n");
> > > +             kfree_skb(skb);
> > > +             return;
> > > +     }
> > 
> > Why is it sending frames when it is not up?
> 
> This is intended for received frames. A situation where the lower
> layers have been enabled correctly but not the port.

But why should that happen? It suggests you have the order wrong. The
lower level should only be enabled once the port is opened.

> > No DMA? What sort of performance do you get? Enough for the odd BPDU,
> > IGMP frame etc, but i guess you don't want any real bulk data to be
> > sent this way?
> 
> Yes the register based injection/extration is not going to be fast, but
> the FDMA and its driver is being sent later as separate series to keep
> the size of this review down.

FDMA?

I need a bit more background here, just to make use this should be a
pure switchdev driver and not a DSA driver.

> 
> > 
> > > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> > > +{
> > > +     struct sparx5 *sparx5 = _sparx5;
> > > +
> > > +     /* Check data in queue */
> > > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> > > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> > > +
> > > +     return IRQ_HANDLED;
> > > +}
> > 
> > Is there any sort of limit how many times this will loop? If somebody
> > is blasting 10Gbps at the CPU, will it ever get out of this loop?
> 
> Hmmm, not at the moment but this is because the FDMA driver is intended
> to be used in these scenarios.

So throwing out an idea, which might be terrible. How about limiting
it to 64 loops, the same as the NAPI poll? That might allow the
machine to get some work done before the next interrupt? Does the
hardware do interrupt coalescing? But is this is going to be quickly
thrown away and replaced with FDMA, don't spend too much time on it.

	 Andrew

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-22 14:41         ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 14:41 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

On Tue, Dec 22, 2020 at 10:46:12AM +0100, Steen Hegelund wrote:
> Hi Andrew,
> 
> On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you
> > know the content is safe
> > 
> > > +     /* Create a phylink for PHY management.  Also handles SFPs */
> > > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
> > > +     spx5_port->phylink_co
> > > nfig.type = PHYLINK_NETDEV;
> > > +     spx5_port->phylink_config.pcs_poll = true;
> > > +
> > > +     /* phylink needs a valid interface mode to parse dt node */
> > > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
> > > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
> > 
> > Maybe just enforce a valid value in DT?
> 
> Maybe I need to clarify that you must choose between an Ethernet cuPHY
> or an SFP, so it is optional.

But you also need to watch out for somebody putting a copper modules
in an SFP port. phylink will then set the mode to SGMII for a 1G
copper module, etc.

> > > +/* Configuration */
> > > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
> > > +{
> > > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
> > > +}
> > 
> > That is a rather odd definition of copper.
> 
> Should I rather use a bool property to select between the two options
> (cuPHY or SFP)?

I guess what you are trying to indicate is between a hard wired Copper
PHY and an SFP cage? You have some sort of MII switch which allows the
MAC to be connected to either the QSGMII PHY, or an SFP cage? But
since the SFP cage could be populated with a copper PHY, and PHYLINK
will then instantiate a phylib copper PHY driver for it, looking at
phy_mode is not reliable. You need a property which selects the port,
not the technology.

> > > +static int sparx5_port_open(struct net_device *ndev)
> > > +{
> > > +     struct sparx5_port *port = netdev_priv(ndev);
> > > +     int err = 0;
> > > +
> > > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
> > > 0);
> > > +     if (err) {
> > > +             netdev_err(ndev, "Could not attach to PHY\n");
> > > +             return err;
> > > +     }
> > > +
> > > +     phylink_start(port->phylink);
> > > +
> > > +     if (!ndev->phydev) {
> > 
> > Humm. When is ndev->phydev set? I don't think phylink ever sets it.
> 
> Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
> the phydev.

Ah, O.K. But watch out for a copper SFP module!

> > > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
> > > byte_swap)
> > > +{
> > > +     int i, byte_cnt = 0;
> > > +     bool eof_flag = false, pruned_flag = false, abort_flag =
> > > false;
> > > +     u32 ifh[IFH_LEN];
> > > +     struct sk_buff *skb;
> > > +     struct frame_info fi;
> > > +     struct sparx5_port *port;
> > > +     struct net_device *netdev;
> > > +     u32 *rxbuf;
> > > +
> > > +     /* Get IFH */
> > > +     for (i = 0; i < IFH_LEN; i++)
> > > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +
> > > +     /* Decode IFH (whats needed) */
> > > +     sparx5_ifh_parse(ifh, &fi);
> > > +
> > > +     /* Map to port netdev */
> > > +     port = fi.src_port < SPX5_PORTS ?
> > > +             sparx5->ports[fi.src_port] : NULL;
> > > +     if (!port || !port->ndev) {
> > > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
> > > fi.src_port);
> > > +             sparx5_xtr_flush(sparx5, grp);
> > > +             return;
> > > +     }
> > > +
> > > +     /* Have netdev, get skb */
> > > +     netdev = port->ndev;
> > > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
> > > +     if (!skb) {
> > > +             sparx5_xtr_flush(sparx5, grp);
> > > +             dev_err(sparx5->dev, "No skb allocated\n");
> > > +             return;
> > > +     }
> > > +     rxbuf = (u32 *)skb->data;
> > > +
> > > +     /* Now, pull frame data */
> > > +     while (!eof_flag) {
> > > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +             u32 cmp = val;
> > > +
> > > +             if (byte_swap)
> > > +                     cmp = ntohl((__force __be32)val);
> > > +
> > > +             switch (cmp) {
> > > +             case XTR_NOT_READY:
> > > +                     break;
> > > +             case XTR_ABORT:
> > > +                     /* No accompanying data */
> > > +                     abort_flag = true;
> > > +                     eof_flag = true;
> > > +                     break;
> > > +             case XTR_EOF_0:
> > > +             case XTR_EOF_1:
> > > +             case XTR_EOF_2:
> > > +             case XTR_EOF_3:
> > > +                     /* This assumes STATUS_WORD_POS == 1, Status
> > > +                      * just after last data
> > > +                      */
> > > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
> > > +                     eof_flag = true;
> > > +                     break;
> > > +             case XTR_PRUNED:
> > > +                     /* But get the last 4 bytes as well */
> > > +                     eof_flag = true;
> > > +                     pruned_flag = true;
> > > +                     fallthrough;
> > > +             case XTR_ESCAPE:
> > > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
> > > +                     byte_cnt += 4;
> > > +                     rxbuf++;
> > > +                     break;
> > > +             default:
> > > +                     *rxbuf = val;
> > > +                     byte_cnt += 4;
> > > +                     rxbuf++;
> > > +             }
> > > +     }
> > > +
> > > +     if (abort_flag || pruned_flag || !eof_flag) {
> > > +             netdev_err(netdev, "Discarded frame: abort:%d
> > > pruned:%d eof:%d\n",
> > > +                        abort_flag, pruned_flag, eof_flag);
> > > +             kfree_skb(skb);
> > > +             return;
> > > +     }
> > > +
> > > +     if (!netif_oper_up(netdev)) {
> > > +             netdev_err(netdev, "Discarded frame: Interface not
> > > up\n");
> > > +             kfree_skb(skb);
> > > +             return;
> > > +     }
> > 
> > Why is it sending frames when it is not up?
> 
> This is intended for received frames. A situation where the lower
> layers have been enabled correctly but not the port.

But why should that happen? It suggests you have the order wrong. The
lower level should only be enabled once the port is opened.

> > No DMA? What sort of performance do you get? Enough for the odd BPDU,
> > IGMP frame etc, but i guess you don't want any real bulk data to be
> > sent this way?
> 
> Yes the register based injection/extration is not going to be fast, but
> the FDMA and its driver is being sent later as separate series to keep
> the size of this review down.

FDMA?

I need a bit more background here, just to make use this should be a
pure switchdev driver and not a DSA driver.

> 
> > 
> > > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
> > > +{
> > > +     struct sparx5 *sparx5 = _sparx5;
> > > +
> > > +     /* Check data in queue */
> > > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
> > > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
> > > +
> > > +     return IRQ_HANDLED;
> > > +}
> > 
> > Is there any sort of limit how many times this will loop? If somebody
> > is blasting 10Gbps at the CPU, will it ever get out of this loop?
> 
> Hmmm, not at the moment but this is because the FDMA driver is intended
> to be used in these scenarios.

So throwing out an idea, which might be terrible. How about limiting
it to 64 loops, the same as the NAPI poll? That might allow the
machine to get some work done before the next interrupt? Does the
hardware do interrupt coalescing? But is this is going to be quickly
thrown away and replaced with FDMA, don't spend too much time on it.

	 Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
  2020-12-20 23:35     ` Andrew Lunn
@ 2020-12-22 14:55       ` Bjarni Jonasson
  -1 siblings, 0 replies; 75+ messages in thread
From: Bjarni Jonasson @ 2020-12-22 14:55 UTC (permalink / raw)
  To: Andrew Lunn, Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On Mon, 2020-12-21 at 00:35 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> > +     /* Aneg complete provides more information  */
> > +     if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
> > +             if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII)
> > {
> > +                     /* SGMII cisco aneg */
> > +                     u32 spdvalue = ((lp_abil >> 10) & 3);
> 
>                         u32 spdvalue = lp_abil & LPA_SGMII_SPD_MASK;
> 
> > +
> > +                     status->link = !!((lp_abil >> 15) == 1) &&
> > status->link;
> 
> Maybe
> 
>                         status->link = !!((lp_abil & LPA_SGMII_LINK)
> && status->link;
> 
> > +                     status->an_complete = true;
> > +                     status->duplex = (lp_abil >> 12) & 0x1
> > ?  DUPLEX_FULL : DUPLEX_HALF;
> 
>                         status->duplex = (lp_abil &
> LPA_SGMII_FULL_DUPLEX) ?  DUPLEX_FULL : DUPLEX_HALF;

Yes, changed to use the SGMII macroes.

> 
> 
> > +                     if (spdvalue == LPA_SGMII_10)
> > +                             status->speed = SPEED_10;
> > +                     else if (spdvalue == LPA_SGMII_100)
> > +                             status->speed = SPEED_100;
> > +                     else
> > +                             status->speed = SPEED_1000;
> 
> I wonder if there is a helper for this?
> 
> 
> > +             } else {
> > +                     /* Clause 37 Aneg */
> > +                     status->link = !((lp_abil >> 12) & 3) &&
> > status->link;
> > +                     status->an_complete = true;
> > +                     status->duplex = ((lp_abil >> 5) & 1) ?
> > DUPLEX_FULL : DUPLEX_UNKNOWN;
> > +                     if ((lp_abil >> 8) & 1) /* symmetric pause */
> > +                             status->pause = MLO_PAUSE_RX |
> > MLO_PAUSE_TX;
> > +                     if (lp_abil & (1 << 7)) /* asymmetric pause
> > */
> > +                             status->pause |= MLO_PAUSE_RX;
> > +             }
> 
> Please check if there are any standard #defines you can use for
> this. Russell King has done some work for clause 37. Maybe there is
> some code in phy_driver.c you can use? phylink_decode_sgmii_word()

There are the ADVERTISE_* macroes in mii.h.
I've changed the code to use them.

> 
> > +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> > +                                 struct sparx5_port *port,
> > +                                 struct sparx5_port_config *conf)
> > +{
> > +     case PHY_INTERFACE_MODE_SGMII:
> > +             if (conf->speed != SPEED_1000 &&
> > +                 conf->speed != SPEED_100 &&
> > +                 conf->speed != SPEED_10 &&
> > +                 conf->speed != SPEED_2500)
> > +                     return sparx5_port_error(port, conf,
> > SPX5_PERR_SPEED);
> 
> Is it really SGMII over clocked at 2500? Or 2500BaseX?

Yes the SGMII mode in the serdes driver is overclocked.
Nothing in the switch driver needs changing when changing between
speeds 1G/2G5.
> 
> > +static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
> > +                            u32 portno, u32 speed)
> > +{
> > +     u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
> > +     u32 mac_width  = 8;
> > +     u32 fifo_width = 16;
> > +     u32 addition   = 0;
> > +     u32 mac_per    = 6400, tmp1, tmp2, tmp3;
> > +     u32 taxi_dist[SPX5_PORTS_ALL] = {
> 
> const. As it is at the moment, it gets copied onto the stack, so it
> can be modified. Const i guess prevents that copy?
> 
> > +             6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
> > +             4, 4, 4, 4,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             4, 6, 8, 4, 6, 8, 6, 8,
> > +             2, 2, 2, 2, 2, 2, 2, 4, 2
> > +     };
> > +static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)

Changed to const.
> 
> What is urg?

urg=urgency (taken directly from the name in the asic). 
Another name for speed, or actually how many clockcycles can go by
before the port instance must be served. 
> 
> > +static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
> > +{
> > +     if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37
> > aneg */
> > +             return ((1 << 14) | /* ack */
> > +             ((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
> > +             ((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
> > +             (1 << 5)); /* FDX only */
> 
> ADVERTISE_LPACK, ADVERTISE_PAUSE_ASYM, ADVERTISE_PAUSE_CAP,
> ADVERTISE_1000XFULL?

Yes, applied.
> 
> > +int sparx5_port_config(struct sparx5 *sparx5,
> > +                    struct sparx5_port *port,
> > +                    struct sparx5_port_config *conf)
> > +{
> > +     bool high_speed_dev = sparx5_is_high_speed_device(conf);
> > +     int err, urgency, stop_wm;
> > +
> > +     err = sparx5_port_verify_speed(sparx5, port, conf);
> > +     if (err)
> > +             return err;
> > +
> > +     /* high speed device is already configured */
> > +     if (!high_speed_dev)
> > +             sparx5_port_config_low_set(sparx5, port, conf);
> > +
> > +     /* Configure flow control */
> > +     err = sparx5_port_fc_setup(sparx5, port, conf);
> > +     if (err)
> > +             return err;
> > +
> > +     /* Set the DSM stop watermark */
> > +     stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf-
> > >speed);
> > +     spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
> > +              DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
> > +              sparx5,
> > +              DSM_DEV_TX_STOP_WM_CFG(port->portno));
> > +
> > +     /* Enable port forwarding */
> > +     urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
> > +     spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
> > +              QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
> > +              QFWD_SWITCH_PORT_MODE_PORT_ENA |
> > +              QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
> > +              sparx5,
> > +              QFWD_SWITCH_PORT_MODE(port->portno));
> 
> What does it mean by port forwarding? By default, packets should only
> go to the CPU, until the port is added to a bridge. I've not thought
> much about L3, since DSA so far only has L2 switches, but i guess you
> don't need to enable L3 forwarding until a route out the port has
> been
> added?

This mean that the port is enabled in the queue system - not that it
can participate in switching.  When the port joins the bridge, then
forwarding masks will enable swithcing.
I've changed the comment to "Enable port in queue system"
> 
> > +/* Initialize port config to default */
> > +int sparx5_port_init(struct sparx5 *sparx5,
> > +                  struct sparx5_port *port,
> > +                  struct sparx5_port_config *conf)
> > +{
> > +     /* Discard pause frame 01-80-C2-00-00-01 */
> > +     spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));
> 
> The comment is about pause frames, but the macro contain BPDU?

The "0xC" is a 2 bit field that chooses what to do with the second BPDU
with the address 01-80-C2-00-00-01 (0x3 chooses the first 01-80-C2-00-
00-00, and 0x300 chooses the third 01-80-C2-00-00-02, etc..).  Both
bits high means discard.
I've change the 0xC to a macro called "PAUSE_DISCARD"
> 
>     Andrew

Thanks
Bjarni Jonasson
Microchip


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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
@ 2020-12-22 14:55       ` Bjarni Jonasson
  0 siblings, 0 replies; 75+ messages in thread
From: Bjarni Jonasson @ 2020-12-22 14:55 UTC (permalink / raw)
  To: Andrew Lunn, Steen Hegelund
  Cc: Alexandre Belloni, linux-kernel, Arnd Bergmann, Madalin Bucur,
	netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

On Mon, 2020-12-21 at 00:35 +0100, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
> 
> > +     /* Aneg complete provides more information  */
> > +     if (DEV2G5_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(value)) {
> > +             if (port->conf.portmode == PHY_INTERFACE_MODE_SGMII)
> > {
> > +                     /* SGMII cisco aneg */
> > +                     u32 spdvalue = ((lp_abil >> 10) & 3);
> 
>                         u32 spdvalue = lp_abil & LPA_SGMII_SPD_MASK;
> 
> > +
> > +                     status->link = !!((lp_abil >> 15) == 1) &&
> > status->link;
> 
> Maybe
> 
>                         status->link = !!((lp_abil & LPA_SGMII_LINK)
> && status->link;
> 
> > +                     status->an_complete = true;
> > +                     status->duplex = (lp_abil >> 12) & 0x1
> > ?  DUPLEX_FULL : DUPLEX_HALF;
> 
>                         status->duplex = (lp_abil &
> LPA_SGMII_FULL_DUPLEX) ?  DUPLEX_FULL : DUPLEX_HALF;

Yes, changed to use the SGMII macroes.

> 
> 
> > +                     if (spdvalue == LPA_SGMII_10)
> > +                             status->speed = SPEED_10;
> > +                     else if (spdvalue == LPA_SGMII_100)
> > +                             status->speed = SPEED_100;
> > +                     else
> > +                             status->speed = SPEED_1000;
> 
> I wonder if there is a helper for this?
> 
> 
> > +             } else {
> > +                     /* Clause 37 Aneg */
> > +                     status->link = !((lp_abil >> 12) & 3) &&
> > status->link;
> > +                     status->an_complete = true;
> > +                     status->duplex = ((lp_abil >> 5) & 1) ?
> > DUPLEX_FULL : DUPLEX_UNKNOWN;
> > +                     if ((lp_abil >> 8) & 1) /* symmetric pause */
> > +                             status->pause = MLO_PAUSE_RX |
> > MLO_PAUSE_TX;
> > +                     if (lp_abil & (1 << 7)) /* asymmetric pause
> > */
> > +                             status->pause |= MLO_PAUSE_RX;
> > +             }
> 
> Please check if there are any standard #defines you can use for
> this. Russell King has done some work for clause 37. Maybe there is
> some code in phy_driver.c you can use? phylink_decode_sgmii_word()

There are the ADVERTISE_* macroes in mii.h.
I've changed the code to use them.

> 
> > +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> > +                                 struct sparx5_port *port,
> > +                                 struct sparx5_port_config *conf)
> > +{
> > +     case PHY_INTERFACE_MODE_SGMII:
> > +             if (conf->speed != SPEED_1000 &&
> > +                 conf->speed != SPEED_100 &&
> > +                 conf->speed != SPEED_10 &&
> > +                 conf->speed != SPEED_2500)
> > +                     return sparx5_port_error(port, conf,
> > SPX5_PERR_SPEED);
> 
> Is it really SGMII over clocked at 2500? Or 2500BaseX?

Yes the SGMII mode in the serdes driver is overclocked.
Nothing in the switch driver needs changing when changing between
speeds 1G/2G5.
> 
> > +static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
> > +                            u32 portno, u32 speed)
> > +{
> > +     u32 sys_clk    = sparx5_clk_period(sparx5->coreclock);
> > +     u32 mac_width  = 8;
> > +     u32 fifo_width = 16;
> > +     u32 addition   = 0;
> > +     u32 mac_per    = 6400, tmp1, tmp2, tmp3;
> > +     u32 taxi_dist[SPX5_PORTS_ALL] = {
> 
> const. As it is at the moment, it gets copied onto the stack, so it
> can be modified. Const i guess prevents that copy?
> 
> > +             6, 8, 10, 6, 8, 10, 6, 8, 10, 6, 8, 10,
> > +             4, 4, 4, 4,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             11, 12, 13, 14, 15, 16, 17, 18,
> > +             4, 6, 8, 4, 6, 8, 6, 8,
> > +             2, 2, 2, 2, 2, 2, 2, 4, 2
> > +     };
> > +static int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed)

Changed to const.
> 
> What is urg?

urg=urgency (taken directly from the name in the asic). 
Another name for speed, or actually how many clockcycles can go by
before the port instance must be served. 
> 
> > +static u16 sparx5_get_aneg_word(struct sparx5_port_config *conf)
> > +{
> > +     if (conf->portmode == PHY_INTERFACE_MODE_1000BASEX) /* cl-37
> > aneg */
> > +             return ((1 << 14) | /* ack */
> > +             ((conf->pause ? 1 : 0) << 8) | /* asymmetric pause */
> > +             ((conf->pause ? 1 : 0) << 7) | /* symmetric pause */
> > +             (1 << 5)); /* FDX only */
> 
> ADVERTISE_LPACK, ADVERTISE_PAUSE_ASYM, ADVERTISE_PAUSE_CAP,
> ADVERTISE_1000XFULL?

Yes, applied.
> 
> > +int sparx5_port_config(struct sparx5 *sparx5,
> > +                    struct sparx5_port *port,
> > +                    struct sparx5_port_config *conf)
> > +{
> > +     bool high_speed_dev = sparx5_is_high_speed_device(conf);
> > +     int err, urgency, stop_wm;
> > +
> > +     err = sparx5_port_verify_speed(sparx5, port, conf);
> > +     if (err)
> > +             return err;
> > +
> > +     /* high speed device is already configured */
> > +     if (!high_speed_dev)
> > +             sparx5_port_config_low_set(sparx5, port, conf);
> > +
> > +     /* Configure flow control */
> > +     err = sparx5_port_fc_setup(sparx5, port, conf);
> > +     if (err)
> > +             return err;
> > +
> > +     /* Set the DSM stop watermark */
> > +     stop_wm = sparx5_port_fifo_sz(sparx5, port->portno, conf-
> > >speed);
> > +     spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM_SET(stop_wm),
> > +              DSM_DEV_TX_STOP_WM_CFG_DEV_TX_STOP_WM,
> > +              sparx5,
> > +              DSM_DEV_TX_STOP_WM_CFG(port->portno));
> > +
> > +     /* Enable port forwarding */
> > +     urgency = sparx5_port_fwd_urg(sparx5, conf->speed);
> > +     spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1) |
> > +              QFWD_SWITCH_PORT_MODE_FWD_URGENCY_SET(urgency),
> > +              QFWD_SWITCH_PORT_MODE_PORT_ENA |
> > +              QFWD_SWITCH_PORT_MODE_FWD_URGENCY,
> > +              sparx5,
> > +              QFWD_SWITCH_PORT_MODE(port->portno));
> 
> What does it mean by port forwarding? By default, packets should only
> go to the CPU, until the port is added to a bridge. I've not thought
> much about L3, since DSA so far only has L2 switches, but i guess you
> don't need to enable L3 forwarding until a route out the port has
> been
> added?

This mean that the port is enabled in the queue system - not that it
can participate in switching.  When the port joins the bridge, then
forwarding masks will enable swithcing.
I've changed the comment to "Enable port in queue system"
> 
> > +/* Initialize port config to default */
> > +int sparx5_port_init(struct sparx5 *sparx5,
> > +                  struct sparx5_port *port,
> > +                  struct sparx5_port_config *conf)
> > +{
> > +     /* Discard pause frame 01-80-C2-00-00-01 */
> > +     spx5_wr(0xC, sparx5, ANA_CL_CAPTURE_BPDU_CFG(port->portno));
> 
> The comment is about pause frames, but the macro contain BPDU?

The "0xC" is a 2 bit field that chooses what to do with the second BPDU
with the address 01-80-C2-00-00-01 (0x3 chooses the first 01-80-C2-00-
00-00, and 0x300 chooses the third 01-80-C2-00-00-02, etc..).  Both
bits high means discard.
I've change the 0xC to a macro called "PAUSE_DISCARD"
> 
>     Andrew

Thanks
Bjarni Jonasson
Microchip


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-22 13:50       ` Steen Hegelund
@ 2020-12-22 15:01         ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 15:01 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

> > > +static void sparx5_board_init(struct sparx5 *sparx5)
> > > +{
> > > +     int idx;
> > > +
> > > +     if (!sparx5->sd_sgpio_remapping)
> > > +             return;
> > > +
> > > +     /* Enable SGPIO Signal Detect remapping */
> > > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > > +              sparx5,
> > > +              GCB_HW_SGPIO_SD_CFG);
> > > +
> > > +     /* Refer to LOS SGPIO */
> > > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
> > > +             if (sparx5->ports[idx]) {
> > > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
> > > {
> > > +                             spx5_wr(sparx5->ports[idx]-
> > > >conf.sd_sgpio,
> > > +                                     sparx5,
> > > +                                    
> > > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> > > +                     }
> > > +             }
> > > +     }
> > > +}
> > 
> > I've not looked at how you do SFP integration yet. Is this the LOS
> > from the SFP socket? Is there a Linux GPIO controller exported by
> > this
> > driver, so the SFP driver can use the GPIOs?
> 
> Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
> SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
> location of the LOS, and use this by default without any driver
> configuration.
> But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
> are not located in the expected position, so we have this board
> remapping function to handle that aspect.

Is it possible to turn this off in the hardware? It might be less
confusing if LOS it determined by phylink, not phylink and the switch
itself. Especially when we get into race conditions between PHYLINK
polling the GPIO and the hardware taking the short cut?


> > > +static int mchp_sparx5_probe(struct platform_device *pdev)
> > > +{
> > > +     struct device_node *np = pdev->dev.of_node;
> > > +     struct sparx5 *sparx5;
> > > +     struct device_node *ports, *portnp;
> > > +     const u8 *mac_addr;
> > > +     int err = 0;
> > > +
> > > +     if (!np && !pdev->dev.platform_data)
> > > +             return -ENODEV;
> > > +
> > > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
> > > GFP_KERNEL);
> > > +     if (!sparx5)
> > > +             return -ENOMEM;
> > > +
> > > +     platform_set_drvdata(pdev, sparx5);
> > > +     sparx5->pdev = pdev;
> > > +     sparx5->dev = &pdev->dev;
> > > +
> > > +     /* Default values, some from DT */
> > > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> > > +
> > > +     mac_addr = of_get_mac_address(np);
> > > +     if (IS_ERR_OR_NULL(mac_addr)) {
> > > +             dev_info(sparx5->dev, "MAC addr was not set, use
> > > random MAC\n");
> > > +             eth_random_addr(sparx5->base_mac);
> > > +             sparx5->base_mac[5] = 0;
> > > +     } else {
> > > +             ether_addr_copy(sparx5->base_mac, mac_addr);
> > > +     }
> > 
> > The binding document does not say anything about a MAC address at the
> > top level. What is this used for?
> 
> This the base MAC address used for generating the the switch NI's MAC
> addresses.

Yes, that is obvious from the code. But all DT properties must be in
the binding Documentation. The DT verifier is going to complain when
it finds a mac-address property which is not described in the yaml
file.

> > > +             config.media_type = ETH_MEDIA_DAC;
> > > +             config.serdes_reset = true;
> > > +             config.portmode = config.phy_mode;
> > > +             err = sparx5_probe_port(sparx5, portnp, serdes,
> > > portno, &config);
> > > +             if (err) {
> > > +                     dev_err(sparx5->dev, "port probe error\n");
> > > +                     goto cleanup_ports;
> > > +             }
> > > +     }
> > > +     sparx5_board_init(sparx5);
> > > +
> > > +cleanup_ports:
> > > +     return err;
> > 
> > Seems missed named, no cleanup.
> 
> Ah - this comes later (as the driver was split in functional groups for
> reviewing). I hope this is OK, as it is only temporary - I could add a
> comment to that effect.

Yes, this is fine. Here, and in other places, a comment like:

/* More code to be added in later patches */

would of been nice, just as a heads up. That is the problem with
linear patch review.

> > > +static int __init sparx5_switch_reset(void)
> > > +{
> > > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> > > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
> > > +     struct regmap *cpu_ctrl, *gcb_ctrl;
> > > +     u32 val;
> > > +
> > > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> > > +     if (IS_ERR(cpu_ctrl)) {
> > > +             pr_err("No '%s' syscon map\n", syscon_cpu);
> > > +             return PTR_ERR(cpu_ctrl);
> > > +     }
> > > +
> > > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> > > +     if (IS_ERR(gcb_ctrl)) {
> > > +             pr_err("No '%s' syscon map\n", syscon_gcb);
> > > +             return PTR_ERR(gcb_ctrl);
> > > +     }
> > > +
> > > +     /* Make sure the core is PROTECTED from reset */
> > > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> > > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> > > +
> > > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> > > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> > > +
> > > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
> > > val,
> > > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
> > > == 0,
> > > +                               1, 100);
> > > +}
> > > +postcore_initcall(sparx5_switch_reset);
> > 
> > That is pretty unusual. Why cannot this be done at probe time?
> 
> The problem is that the switch core reset also affects (reset) the
> SGPIO controller.
> 
> We tried to put this in the reset driver, but it was rejected. If the
> reset is done at probe time, the SGPIO driver may already have
> initialized state.
> 
> The switch core reset will then reset all SGPIO registers. 

Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
sounds like it should be embedded inside this driver if it is sharing
hardware.

Another option would be to look at the reset subsystem, and have this
driver export a reset controller, which the SGPIO driver can bind to.
Given that the GPIO driver has been merged, if this will work, it is
probably a better solution.

       Andrew

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-22 15:01         ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 15:01 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

> > > +static void sparx5_board_init(struct sparx5 *sparx5)
> > > +{
> > > +     int idx;
> > > +
> > > +     if (!sparx5->sd_sgpio_remapping)
> > > +             return;
> > > +
> > > +     /* Enable SGPIO Signal Detect remapping */
> > > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
> > > +              sparx5,
> > > +              GCB_HW_SGPIO_SD_CFG);
> > > +
> > > +     /* Refer to LOS SGPIO */
> > > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
> > > +             if (sparx5->ports[idx]) {
> > > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
> > > {
> > > +                             spx5_wr(sparx5->ports[idx]-
> > > >conf.sd_sgpio,
> > > +                                     sparx5,
> > > +                                    
> > > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
> > > +                     }
> > > +             }
> > > +     }
> > > +}
> > 
> > I've not looked at how you do SFP integration yet. Is this the LOS
> > from the SFP socket? Is there a Linux GPIO controller exported by
> > this
> > driver, so the SFP driver can use the GPIOs?
> 
> Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
> SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
> location of the LOS, and use this by default without any driver
> configuration.
> But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
> are not located in the expected position, so we have this board
> remapping function to handle that aspect.

Is it possible to turn this off in the hardware? It might be less
confusing if LOS it determined by phylink, not phylink and the switch
itself. Especially when we get into race conditions between PHYLINK
polling the GPIO and the hardware taking the short cut?


> > > +static int mchp_sparx5_probe(struct platform_device *pdev)
> > > +{
> > > +     struct device_node *np = pdev->dev.of_node;
> > > +     struct sparx5 *sparx5;
> > > +     struct device_node *ports, *portnp;
> > > +     const u8 *mac_addr;
> > > +     int err = 0;
> > > +
> > > +     if (!np && !pdev->dev.platform_data)
> > > +             return -ENODEV;
> > > +
> > > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
> > > GFP_KERNEL);
> > > +     if (!sparx5)
> > > +             return -ENOMEM;
> > > +
> > > +     platform_set_drvdata(pdev, sparx5);
> > > +     sparx5->pdev = pdev;
> > > +     sparx5->dev = &pdev->dev;
> > > +
> > > +     /* Default values, some from DT */
> > > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> > > +
> > > +     mac_addr = of_get_mac_address(np);
> > > +     if (IS_ERR_OR_NULL(mac_addr)) {
> > > +             dev_info(sparx5->dev, "MAC addr was not set, use
> > > random MAC\n");
> > > +             eth_random_addr(sparx5->base_mac);
> > > +             sparx5->base_mac[5] = 0;
> > > +     } else {
> > > +             ether_addr_copy(sparx5->base_mac, mac_addr);
> > > +     }
> > 
> > The binding document does not say anything about a MAC address at the
> > top level. What is this used for?
> 
> This the base MAC address used for generating the the switch NI's MAC
> addresses.

Yes, that is obvious from the code. But all DT properties must be in
the binding Documentation. The DT verifier is going to complain when
it finds a mac-address property which is not described in the yaml
file.

> > > +             config.media_type = ETH_MEDIA_DAC;
> > > +             config.serdes_reset = true;
> > > +             config.portmode = config.phy_mode;
> > > +             err = sparx5_probe_port(sparx5, portnp, serdes,
> > > portno, &config);
> > > +             if (err) {
> > > +                     dev_err(sparx5->dev, "port probe error\n");
> > > +                     goto cleanup_ports;
> > > +             }
> > > +     }
> > > +     sparx5_board_init(sparx5);
> > > +
> > > +cleanup_ports:
> > > +     return err;
> > 
> > Seems missed named, no cleanup.
> 
> Ah - this comes later (as the driver was split in functional groups for
> reviewing). I hope this is OK, as it is only temporary - I could add a
> comment to that effect.

Yes, this is fine. Here, and in other places, a comment like:

/* More code to be added in later patches */

would of been nice, just as a heads up. That is the problem with
linear patch review.

> > > +static int __init sparx5_switch_reset(void)
> > > +{
> > > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
> > > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
> > > +     struct regmap *cpu_ctrl, *gcb_ctrl;
> > > +     u32 val;
> > > +
> > > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
> > > +     if (IS_ERR(cpu_ctrl)) {
> > > +             pr_err("No '%s' syscon map\n", syscon_cpu);
> > > +             return PTR_ERR(cpu_ctrl);
> > > +     }
> > > +
> > > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
> > > +     if (IS_ERR(gcb_ctrl)) {
> > > +             pr_err("No '%s' syscon map\n", syscon_gcb);
> > > +             return PTR_ERR(gcb_ctrl);
> > > +     }
> > > +
> > > +     /* Make sure the core is PROTECTED from reset */
> > > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
> > > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
> > > +
> > > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
> > > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
> > > +
> > > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
> > > val,
> > > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
> > > == 0,
> > > +                               1, 100);
> > > +}
> > > +postcore_initcall(sparx5_switch_reset);
> > 
> > That is pretty unusual. Why cannot this be done at probe time?
> 
> The problem is that the switch core reset also affects (reset) the
> SGPIO controller.
> 
> We tried to put this in the reset driver, but it was rejected. If the
> reset is done at probe time, the SGPIO driver may already have
> initialized state.
> 
> The switch core reset will then reset all SGPIO registers. 

Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
sounds like it should be embedded inside this driver if it is sharing
hardware.

Another option would be to look at the reset subsystem, and have this
driver export a reset controller, which the SGPIO driver can bind to.
Given that the GPIO driver has been merged, if this will work, it is
probably a better solution.

       Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
  2020-12-22 14:55       ` Bjarni Jonasson
@ 2020-12-22 15:08         ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 15:08 UTC (permalink / raw)
  To: Bjarni Jonasson
  Cc: Steen Hegelund, David S. Miller, Jakub Kicinski, Russell King,
	Lars Povlsen, Microchip Linux Driver Support, Alexandre Belloni,
	Madalin Bucur, Nicolas Ferre, Mark Einon, Masahiro Yamada,
	Arnd Bergmann, netdev, linux-kernel, linux-arm-kernel

> > > +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> > > +                                 struct sparx5_port *port,
> > > +                                 struct sparx5_port_config *conf)
> > > +{
> > > +     case PHY_INTERFACE_MODE_SGMII:
> > > +             if (conf->speed != SPEED_1000 &&
> > > +                 conf->speed != SPEED_100 &&
> > > +                 conf->speed != SPEED_10 &&
> > > +                 conf->speed != SPEED_2500)
> > > +                     return sparx5_port_error(port, conf,
> > > SPX5_PERR_SPEED);
> > 
> > Is it really SGMII over clocked at 2500? Or 2500BaseX?
> 
> Yes the SGMII mode in the serdes driver is overclocked.
> Nothing in the switch driver needs changing when changing between
> speeds 1G/2G5.

So it continues to use the SGMII inband signalling?

There is a lot of confusion in this area, but SGMII inband signalling
overclocked does not make much sense. So it is more likely to be using
2500BaseX.

	Andrew

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

* Re: [RFC PATCH v2 4/8] net: sparx5: add port module support
@ 2020-12-22 15:08         ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-22 15:08 UTC (permalink / raw)
  To: Bjarni Jonasson
  Cc: Alexandre Belloni, linux-kernel, Arnd Bergmann, Madalin Bucur,
	netdev, Steen Hegelund, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, Masahiro Yamada, David S. Miller, Lars Povlsen

> > > +static int sparx5_port_verify_speed(struct sparx5 *sparx5,
> > > +                                 struct sparx5_port *port,
> > > +                                 struct sparx5_port_config *conf)
> > > +{
> > > +     case PHY_INTERFACE_MODE_SGMII:
> > > +             if (conf->speed != SPEED_1000 &&
> > > +                 conf->speed != SPEED_100 &&
> > > +                 conf->speed != SPEED_10 &&
> > > +                 conf->speed != SPEED_2500)
> > > +                     return sparx5_port_error(port, conf,
> > > SPX5_PERR_SPEED);
> > 
> > Is it really SGMII over clocked at 2500? Or 2500BaseX?
> 
> Yes the SGMII mode in the serdes driver is overclocked.
> Nothing in the switch driver needs changing when changing between
> speeds 1G/2G5.

So it continues to use the SGMII inband signalling?

There is a lot of confusion in this area, but SGMII inband signalling
overclocked does not make much sense. So it is more likely to be using
2500BaseX.

	Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-22 15:01         ` Andrew Lunn
@ 2020-12-22 16:56           ` Alexandre Belloni
  -1 siblings, 0 replies; 75+ messages in thread
From: Alexandre Belloni @ 2020-12-22 16:56 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Steen Hegelund, David S. Miller, Jakub Kicinski, Russell King,
	Lars Povlsen, Bjarni Jonasson, Microchip Linux Driver Support,
	Madalin Bucur, Nicolas Ferre, Mark Einon, Masahiro Yamada,
	Arnd Bergmann, netdev, linux-kernel, linux-arm-kernel

On 22/12/2020 16:01:22+0100, Andrew Lunn wrote:
> > The problem is that the switch core reset also affects (reset) the
> > SGPIO controller.
> > 
> > We tried to put this in the reset driver, but it was rejected. If the
> > reset is done at probe time, the SGPIO driver may already have
> > initialized state.
> > 
> > The switch core reset will then reset all SGPIO registers. 
> 
> Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
> sounds like it should be embedded inside this driver if it is sharing
> hardware.
> 
> Another option would be to look at the reset subsystem, and have this
> driver export a reset controller, which the SGPIO driver can bind to.
> Given that the GPIO driver has been merged, if this will work, it is
> probably a better solution.
> 

That was my suggestion. Then you can ensure from the reset controller
driver that this is done exactly once, either from the sgpio driver or
from the switchdev driver. IIRC, the sgpio from the other SoCs are not
affected by the reset.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-22 16:56           ` Alexandre Belloni
  0 siblings, 0 replies; 75+ messages in thread
From: Alexandre Belloni @ 2020-12-22 16:56 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, linux-kernel, Arnd Bergmann, Madalin Bucur,
	netdev, Steen Hegelund, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, Masahiro Yamada, David S. Miller, Lars Povlsen

On 22/12/2020 16:01:22+0100, Andrew Lunn wrote:
> > The problem is that the switch core reset also affects (reset) the
> > SGPIO controller.
> > 
> > We tried to put this in the reset driver, but it was rejected. If the
> > reset is done at probe time, the SGPIO driver may already have
> > initialized state.
> > 
> > The switch core reset will then reset all SGPIO registers. 
> 
> Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
> sounds like it should be embedded inside this driver if it is sharing
> hardware.
> 
> Another option would be to look at the reset subsystem, and have this
> driver export a reset controller, which the SGPIO driver can bind to.
> Given that the GPIO driver has been merged, if this will work, it is
> probably a better solution.
> 

That was my suggestion. Then you can ensure from the reset controller
driver that this is done exactly once, either from the sgpio driver or
from the switchdev driver. IIRC, the sgpio from the other SoCs are not
affected by the reset.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-22 15:01         ` Andrew Lunn
@ 2020-12-23  8:52           ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23  8:52 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

On 22.12.2020 16:01, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> > > +static void sparx5_board_init(struct sparx5 *sparx5)
>> > > +{
>> > > +     int idx;
>> > > +
>> > > +     if (!sparx5->sd_sgpio_remapping)
>> > > +             return;
>> > > +
>> > > +     /* Enable SGPIO Signal Detect remapping */
>> > > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
>> > > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
>> > > +              sparx5,
>> > > +              GCB_HW_SGPIO_SD_CFG);
>> > > +
>> > > +     /* Refer to LOS SGPIO */
>> > > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
>> > > +             if (sparx5->ports[idx]) {
>> > > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
>> > > {
>> > > +                             spx5_wr(sparx5->ports[idx]-
>> > > >conf.sd_sgpio,
>> > > +                                     sparx5,
>> > > +
>> > > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
>> > > +                     }
>> > > +             }
>> > > +     }
>> > > +}
>> >
>> > I've not looked at how you do SFP integration yet. Is this the LOS
>> > from the SFP socket? Is there a Linux GPIO controller exported by
>> > this
>> > driver, so the SFP driver can use the GPIOs?
>>
>> Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
>> SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
>> location of the LOS, and use this by default without any driver
>> configuration.
>> But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
>> are not located in the expected position, so we have this board
>> remapping function to handle that aspect.
>
>Is it possible to turn this off in the hardware? It might be less
>confusing if LOS it determined by phylink, not phylink and the switch
>itself. Especially when we get into race conditions between PHYLINK
>polling the GPIO and the hardware taking the short cut?
>
OK - I get you point, but I think the message I got when investigating
this, was that it was not possible to turn it off.  I will check that
again.
On the other hand this is also used by our bare-metal API (MESA) so in
that context it simpifies the setup, since the port modules are aware of
the SFP state.
>
>> > > +static int mchp_sparx5_probe(struct platform_device *pdev)
>> > > +{
>> > > +     struct device_node *np = pdev->dev.of_node;
>> > > +     struct sparx5 *sparx5;
>> > > +     struct device_node *ports, *portnp;
>> > > +     const u8 *mac_addr;
>> > > +     int err = 0;
>> > > +
>> > > +     if (!np && !pdev->dev.platform_data)
>> > > +             return -ENODEV;
>> > > +
>> > > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
>> > > GFP_KERNEL);
>> > > +     if (!sparx5)
>> > > +             return -ENOMEM;
>> > > +
>> > > +     platform_set_drvdata(pdev, sparx5);
>> > > +     sparx5->pdev = pdev;
>> > > +     sparx5->dev = &pdev->dev;
>> > > +
>> > > +     /* Default values, some from DT */
>> > > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
>> > > +
>> > > +     mac_addr = of_get_mac_address(np);
>> > > +     if (IS_ERR_OR_NULL(mac_addr)) {
>> > > +             dev_info(sparx5->dev, "MAC addr was not set, use
>> > > random MAC\n");
>> > > +             eth_random_addr(sparx5->base_mac);
>> > > +             sparx5->base_mac[5] = 0;
>> > > +     } else {
>> > > +             ether_addr_copy(sparx5->base_mac, mac_addr);
>> > > +     }
>> >
>> > The binding document does not say anything about a MAC address at the
>> > top level. What is this used for?
>>
>> This the base MAC address used for generating the the switch NI's MAC
>> addresses.
>
>Yes, that is obvious from the code. But all DT properties must be in
>the binding Documentation. The DT verifier is going to complain when
>it finds a mac-address property which is not described in the yaml
>file.

I will add a description for the MAC address to the bindings.

>
>> > > +             config.media_type = ETH_MEDIA_DAC;
>> > > +             config.serdes_reset = true;
>> > > +             config.portmode = config.phy_mode;
>> > > +             err = sparx5_probe_port(sparx5, portnp, serdes,
>> > > portno, &config);
>> > > +             if (err) {
>> > > +                     dev_err(sparx5->dev, "port probe error\n");
>> > > +                     goto cleanup_ports;
>> > > +             }
>> > > +     }
>> > > +     sparx5_board_init(sparx5);
>> > > +
>> > > +cleanup_ports:
>> > > +     return err;
>> >
>> > Seems missed named, no cleanup.
>>
>> Ah - this comes later (as the driver was split in functional groups for
>> reviewing). I hope this is OK, as it is only temporary - I could add a
>> comment to that effect.
>
>Yes, this is fine. Here, and in other places, a comment like:
>
>/* More code to be added in later patches */
>
>would of been nice, just as a heads up. That is the problem with
>linear patch review.

Will do
>
>> > > +static int __init sparx5_switch_reset(void)
>> > > +{
>> > > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
>> > > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
>> > > +     struct regmap *cpu_ctrl, *gcb_ctrl;
>> > > +     u32 val;
>> > > +
>> > > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
>> > > +     if (IS_ERR(cpu_ctrl)) {
>> > > +             pr_err("No '%s' syscon map\n", syscon_cpu);
>> > > +             return PTR_ERR(cpu_ctrl);
>> > > +     }
>> > > +
>> > > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
>> > > +     if (IS_ERR(gcb_ctrl)) {
>> > > +             pr_err("No '%s' syscon map\n", syscon_gcb);
>> > > +             return PTR_ERR(gcb_ctrl);
>> > > +     }
>> > > +
>> > > +     /* Make sure the core is PROTECTED from reset */
>> > > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
>> > > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
>> > > +
>> > > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
>> > > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
>> > > +
>> > > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
>> > > val,
>> > > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
>> > > == 0,
>> > > +                               1, 100);
>> > > +}
>> > > +postcore_initcall(sparx5_switch_reset);
>> >
>> > That is pretty unusual. Why cannot this be done at probe time?
>>
>> The problem is that the switch core reset also affects (reset) the
>> SGPIO controller.
>>
>> We tried to put this in the reset driver, but it was rejected. If the
>> reset is done at probe time, the SGPIO driver may already have
>> initialized state.
>>
>> The switch core reset will then reset all SGPIO registers.
>
>Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
>sounds like it should be embedded inside this driver if it is sharing
>hardware.

The same SGPIO block is present (with suitable scaling of the number of
SGPIOS) in all our switches, so this driver will be reused on all the
platforms when we get them upstreamed (or at least that is the plan).

>
>Another option would be to look at the reset subsystem, and have this
>driver export a reset controller, which the SGPIO driver can bind to.
>Given that the GPIO driver has been merged, if this will work, it is
>probably a better solution.

Alex has already commented on this, but this is probably the goal as I
understand.
>
>       Andrew


BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-23  8:52           ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23  8:52 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

On 22.12.2020 16:01, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> > > +static void sparx5_board_init(struct sparx5 *sparx5)
>> > > +{
>> > > +     int idx;
>> > > +
>> > > +     if (!sparx5->sd_sgpio_remapping)
>> > > +             return;
>> > > +
>> > > +     /* Enable SGPIO Signal Detect remapping */
>> > > +     spx5_rmw(GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
>> > > +              GCB_HW_SGPIO_SD_CFG_SD_MAP_SEL,
>> > > +              sparx5,
>> > > +              GCB_HW_SGPIO_SD_CFG);
>> > > +
>> > > +     /* Refer to LOS SGPIO */
>> > > +     for (idx = 0; idx < SPX5_PORTS; idx++) {
>> > > +             if (sparx5->ports[idx]) {
>> > > +                     if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
>> > > {
>> > > +                             spx5_wr(sparx5->ports[idx]-
>> > > >conf.sd_sgpio,
>> > > +                                     sparx5,
>> > > +
>> > > GCB_HW_SGPIO_TO_SD_MAP_CFG(idx));
>> > > +                     }
>> > > +             }
>> > > +     }
>> > > +}
>> >
>> > I've not looked at how you do SFP integration yet. Is this the LOS
>> > from the SFP socket? Is there a Linux GPIO controller exported by
>> > this
>> > driver, so the SFP driver can use the GPIOs?
>>
>> Yes the SFP driver (used by the Sparx5 SerDes driver) will use the
>> SGPIO LOS, Module Detect etc, and the Port Modules are aware of the
>> location of the LOS, and use this by default without any driver
>> configuration.
>> But on the PCB134 the SGPIOs are shifted one bit by a mistake, and they
>> are not located in the expected position, so we have this board
>> remapping function to handle that aspect.
>
>Is it possible to turn this off in the hardware? It might be less
>confusing if LOS it determined by phylink, not phylink and the switch
>itself. Especially when we get into race conditions between PHYLINK
>polling the GPIO and the hardware taking the short cut?
>
OK - I get you point, but I think the message I got when investigating
this, was that it was not possible to turn it off.  I will check that
again.
On the other hand this is also used by our bare-metal API (MESA) so in
that context it simpifies the setup, since the port modules are aware of
the SFP state.
>
>> > > +static int mchp_sparx5_probe(struct platform_device *pdev)
>> > > +{
>> > > +     struct device_node *np = pdev->dev.of_node;
>> > > +     struct sparx5 *sparx5;
>> > > +     struct device_node *ports, *portnp;
>> > > +     const u8 *mac_addr;
>> > > +     int err = 0;
>> > > +
>> > > +     if (!np && !pdev->dev.platform_data)
>> > > +             return -ENODEV;
>> > > +
>> > > +     sparx5 = devm_kzalloc(&pdev->dev, sizeof(*sparx5),
>> > > GFP_KERNEL);
>> > > +     if (!sparx5)
>> > > +             return -ENOMEM;
>> > > +
>> > > +     platform_set_drvdata(pdev, sparx5);
>> > > +     sparx5->pdev = pdev;
>> > > +     sparx5->dev = &pdev->dev;
>> > > +
>> > > +     /* Default values, some from DT */
>> > > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
>> > > +
>> > > +     mac_addr = of_get_mac_address(np);
>> > > +     if (IS_ERR_OR_NULL(mac_addr)) {
>> > > +             dev_info(sparx5->dev, "MAC addr was not set, use
>> > > random MAC\n");
>> > > +             eth_random_addr(sparx5->base_mac);
>> > > +             sparx5->base_mac[5] = 0;
>> > > +     } else {
>> > > +             ether_addr_copy(sparx5->base_mac, mac_addr);
>> > > +     }
>> >
>> > The binding document does not say anything about a MAC address at the
>> > top level. What is this used for?
>>
>> This the base MAC address used for generating the the switch NI's MAC
>> addresses.
>
>Yes, that is obvious from the code. But all DT properties must be in
>the binding Documentation. The DT verifier is going to complain when
>it finds a mac-address property which is not described in the yaml
>file.

I will add a description for the MAC address to the bindings.

>
>> > > +             config.media_type = ETH_MEDIA_DAC;
>> > > +             config.serdes_reset = true;
>> > > +             config.portmode = config.phy_mode;
>> > > +             err = sparx5_probe_port(sparx5, portnp, serdes,
>> > > portno, &config);
>> > > +             if (err) {
>> > > +                     dev_err(sparx5->dev, "port probe error\n");
>> > > +                     goto cleanup_ports;
>> > > +             }
>> > > +     }
>> > > +     sparx5_board_init(sparx5);
>> > > +
>> > > +cleanup_ports:
>> > > +     return err;
>> >
>> > Seems missed named, no cleanup.
>>
>> Ah - this comes later (as the driver was split in functional groups for
>> reviewing). I hope this is OK, as it is only temporary - I could add a
>> comment to that effect.
>
>Yes, this is fine. Here, and in other places, a comment like:
>
>/* More code to be added in later patches */
>
>would of been nice, just as a heads up. That is the problem with
>linear patch review.

Will do
>
>> > > +static int __init sparx5_switch_reset(void)
>> > > +{
>> > > +     const char *syscon_cpu = "microchip,sparx5-cpu-syscon",
>> > > +             *syscon_gcb = "microchip,sparx5-gcb-syscon";
>> > > +     struct regmap *cpu_ctrl, *gcb_ctrl;
>> > > +     u32 val;
>> > > +
>> > > +     cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon_cpu);
>> > > +     if (IS_ERR(cpu_ctrl)) {
>> > > +             pr_err("No '%s' syscon map\n", syscon_cpu);
>> > > +             return PTR_ERR(cpu_ctrl);
>> > > +     }
>> > > +
>> > > +     gcb_ctrl = syscon_regmap_lookup_by_compatible(syscon_gcb);
>> > > +     if (IS_ERR(gcb_ctrl)) {
>> > > +             pr_err("No '%s' syscon map\n", syscon_gcb);
>> > > +             return PTR_ERR(gcb_ctrl);
>> > > +     }
>> > > +
>> > > +     /* Make sure the core is PROTECTED from reset */
>> > > +     regmap_update_bits(cpu_ctrl, RESET_PROT_STAT,
>> > > +                        SYS_RST_PROT_VCORE, SYS_RST_PROT_VCORE);
>> > > +
>> > > +     regmap_write(gcb_ctrl, spx5_offset(GCB_SOFT_RST),
>> > > +                  GCB_SOFT_RST_SOFT_SWC_RST_SET(1));
>> > > +
>> > > +     return readx_poll_timeout(sparx5_read_gcb_soft_rst, gcb_ctrl,
>> > > val,
>> > > +                               GCB_SOFT_RST_SOFT_SWC_RST_GET(val)
>> > > == 0,
>> > > +                               1, 100);
>> > > +}
>> > > +postcore_initcall(sparx5_switch_reset);
>> >
>> > That is pretty unusual. Why cannot this be done at probe time?
>>
>> The problem is that the switch core reset also affects (reset) the
>> SGPIO controller.
>>
>> We tried to put this in the reset driver, but it was rejected. If the
>> reset is done at probe time, the SGPIO driver may already have
>> initialized state.
>>
>> The switch core reset will then reset all SGPIO registers.
>
>Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
>sounds like it should be embedded inside this driver if it is sharing
>hardware.

The same SGPIO block is present (with suitable scaling of the number of
SGPIOS) in all our switches, so this driver will be reused on all the
platforms when we get them upstreamed (or at least that is the plan).

>
>Another option would be to look at the reset subsystem, and have this
>driver export a reset controller, which the SGPIO driver can bind to.
>Given that the GPIO driver has been merged, if this will work, it is
>probably a better solution.

Alex has already commented on this, but this is probably the goal as I
understand.
>
>       Andrew


BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
  2020-12-22 16:56           ` Alexandre Belloni
@ 2020-12-23  9:03             ` Lars Povlsen
  -1 siblings, 0 replies; 75+ messages in thread
From: Lars Povlsen @ 2020-12-23  9:03 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Andrew Lunn, Steen Hegelund, David S. Miller, Jakub Kicinski,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Madalin Bucur, Nicolas Ferre,
	Mark Einon, Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel


Alexandre Belloni writes:

> On 22/12/2020 16:01:22+0100, Andrew Lunn wrote:
>> > The problem is that the switch core reset also affects (reset) the
>> > SGPIO controller.
>> >
>> > We tried to put this in the reset driver, but it was rejected. If the
>> > reset is done at probe time, the SGPIO driver may already have
>> > initialized state.
>> >
>> > The switch core reset will then reset all SGPIO registers.
>>
>> Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
>> sounds like it should be embedded inside this driver if it is sharing
>> hardware.
>>
>> Another option would be to look at the reset subsystem, and have this
>> driver export a reset controller, which the SGPIO driver can bind to.
>> Given that the GPIO driver has been merged, if this will work, it is
>> probably a better solution.
>>
>
> That was my suggestion. Then you can ensure from the reset controller
> driver that this is done exactly once, either from the sgpio driver or
> from the switchdev driver. IIRC, the sgpio from the other SoCs are not
> affected by the reset.

I will take a look to see if we can change the implementation to use a
reset controller.

---Lars

--
Lars Povlsen,
Microchip

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

* Re: [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver
@ 2020-12-23  9:03             ` Lars Povlsen
  0 siblings, 0 replies; 75+ messages in thread
From: Lars Povlsen @ 2020-12-23  9:03 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Andrew Lunn, linux-kernel, Arnd Bergmann, Bjarni Jonasson,
	Madalin Bucur, netdev, Steen Hegelund, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, Masahiro Yamada, David S. Miller, Lars Povlsen


Alexandre Belloni writes:

> On 22/12/2020 16:01:22+0100, Andrew Lunn wrote:
>> > The problem is that the switch core reset also affects (reset) the
>> > SGPIO controller.
>> >
>> > We tried to put this in the reset driver, but it was rejected. If the
>> > reset is done at probe time, the SGPIO driver may already have
>> > initialized state.
>> >
>> > The switch core reset will then reset all SGPIO registers.
>>
>> Ah, O.K. Dumb question. Why is the SGPIO driver a separate driver? It
>> sounds like it should be embedded inside this driver if it is sharing
>> hardware.
>>
>> Another option would be to look at the reset subsystem, and have this
>> driver export a reset controller, which the SGPIO driver can bind to.
>> Given that the GPIO driver has been merged, if this will work, it is
>> probably a better solution.
>>
>
> That was my suggestion. Then you can ensure from the reset controller
> driver that this is done exactly once, either from the sgpio driver or
> from the switchdev driver. IIRC, the sgpio from the other SoCs are not
> affected by the reset.

I will take a look to see if we can change the implementation to use a
reset controller.

---Lars

--
Lars Povlsen,
Microchip

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-22 14:41         ` Andrew Lunn
@ 2020-12-23 13:29           ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 13:29 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Hi Andrew,

On 22.12.2020 15:41, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>On Tue, Dec 22, 2020 at 10:46:12AM +0100, Steen Hegelund wrote:
>> Hi Andrew,
>>
>> On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
>> > EXTERNAL EMAIL: Do not click links or open attachments unless you
>> > know the content is safe
>> >
>> > > +     /* Create a phylink for PHY management.  Also handles SFPs */
>> > > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
>> > > +     spx5_port->phylink_co
>> > > nfig.type = PHYLINK_NETDEV;
>> > > +     spx5_port->phylink_config.pcs_poll = true;
>> > > +
>> > > +     /* phylink needs a valid interface mode to parse dt node */
>> > > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
>> > > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
>> >
>> > Maybe just enforce a valid value in DT?
>>
>> Maybe I need to clarify that you must choose between an Ethernet cuPHY
>> or an SFP, so it is optional.
>
>But you also need to watch out for somebody putting a copper modules
>in an SFP port. phylink will then set the mode to SGMII for a 1G
>copper module, etc.
>
The cuPHY SFPs are handled by phylink out-of-the-box if the
kernel has added support for the particular cuPHY driver, and that is
done just by specifying the SFP phandle.
So here we just need to know if the user has attached a cuPHY directly
or an SFP.

The phylink_of_phy_connect function provides a way to add a cuPHY
direcly to the PHYLINK instance, but I have not found a way that you can
specify a specific cuPHY embedded in an SFP, so here PHYLINK determines
what is the appropriate PHY (driver) to use.

Could this be done in a simpler way?

>> > > +/* Configuration */
>> > > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
>> > > +{
>> > > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
>> > > +}
>> >
>> > That is a rather odd definition of copper.
>>
>> Should I rather use a bool property to select between the two options
>> (cuPHY or SFP)?
>
>I guess what you are trying to indicate is between a hard wired Copper
>PHY and an SFP cage? You have some sort of MII switch which allows the
>MAC to be connected to either the QSGMII PHY, or an SFP cage? But
>since the SFP cage could be populated with a copper PHY, and PHYLINK
>will then instantiate a phylib copper PHY driver for it, looking at
>phy_mode is not reliable. You need a property which selects the port,
>not the technology.

Yes the intention was to be able to distinguish between the hardwired 
cuPHY case and the SFP case.

I am OK with adding a property to distinguish between the two cases, but
if the SFP handle is present, PHYLINK has been able to handle an
embedded cuPHY (if the driver is available) and use that in the tests
that I have done so far. So my thinking was that if a phy handle is
present, then the user wants a directly attached cuPHY, not an SFP.

>
>> > > +static int sparx5_port_open(struct net_device *ndev)
>> > > +{
>> > > +     struct sparx5_port *port = netdev_priv(ndev);
>> > > +     int err = 0;
>> > > +
>> > > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
>> > > 0);
>> > > +     if (err) {
>> > > +             netdev_err(ndev, "Could not attach to PHY\n");
>> > > +             return err;
>> > > +     }
>> > > +
>> > > +     phylink_start(port->phylink);
>> > > +
>> > > +     if (!ndev->phydev) {
>> >
>> > Humm. When is ndev->phydev set? I don't think phylink ever sets it.
>>
>> Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
>> the phydev.
>
>Ah, O.K. But watch out for a copper SFP module!

Hmm, my expectation is that we have this covered by now.

>
>> > > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
>> > > byte_swap)
>> > > +{
>> > > +     int i, byte_cnt = 0;
>> > > +     bool eof_flag = false, pruned_flag = false, abort_flag =
>> > > false;
>> > > +     u32 ifh[IFH_LEN];
>> > > +     struct sk_buff *skb;
>> > > +     struct frame_info fi;
>> > > +     struct sparx5_port *port;
>> > > +     struct net_device *netdev;
>> > > +     u32 *rxbuf;
>> > > +
>> > > +     /* Get IFH */
>> > > +     for (i = 0; i < IFH_LEN; i++)
>> > > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +
>> > > +     /* Decode IFH (whats needed) */
>> > > +     sparx5_ifh_parse(ifh, &fi);
>> > > +
>> > > +     /* Map to port netdev */
>> > > +     port = fi.src_port < SPX5_PORTS ?
>> > > +             sparx5->ports[fi.src_port] : NULL;
>> > > +     if (!port || !port->ndev) {
>> > > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
>> > > fi.src_port);
>> > > +             sparx5_xtr_flush(sparx5, grp);
>> > > +             return;
>> > > +     }
>> > > +
>> > > +     /* Have netdev, get skb */
>> > > +     netdev = port->ndev;
>> > > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
>> > > +     if (!skb) {
>> > > +             sparx5_xtr_flush(sparx5, grp);
>> > > +             dev_err(sparx5->dev, "No skb allocated\n");
>> > > +             return;
>> > > +     }
>> > > +     rxbuf = (u32 *)skb->data;
>> > > +
>> > > +     /* Now, pull frame data */
>> > > +     while (!eof_flag) {
>> > > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +             u32 cmp = val;
>> > > +
>> > > +             if (byte_swap)
>> > > +                     cmp = ntohl((__force __be32)val);
>> > > +
>> > > +             switch (cmp) {
>> > > +             case XTR_NOT_READY:
>> > > +                     break;
>> > > +             case XTR_ABORT:
>> > > +                     /* No accompanying data */
>> > > +                     abort_flag = true;
>> > > +                     eof_flag = true;
>> > > +                     break;
>> > > +             case XTR_EOF_0:
>> > > +             case XTR_EOF_1:
>> > > +             case XTR_EOF_2:
>> > > +             case XTR_EOF_3:
>> > > +                     /* This assumes STATUS_WORD_POS == 1, Status
>> > > +                      * just after last data
>> > > +                      */
>> > > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
>> > > +                     eof_flag = true;
>> > > +                     break;
>> > > +             case XTR_PRUNED:
>> > > +                     /* But get the last 4 bytes as well */
>> > > +                     eof_flag = true;
>> > > +                     pruned_flag = true;
>> > > +                     fallthrough;
>> > > +             case XTR_ESCAPE:
>> > > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +                     byte_cnt += 4;
>> > > +                     rxbuf++;
>> > > +                     break;
>> > > +             default:
>> > > +                     *rxbuf = val;
>> > > +                     byte_cnt += 4;
>> > > +                     rxbuf++;
>> > > +             }
>> > > +     }
>> > > +
>> > > +     if (abort_flag || pruned_flag || !eof_flag) {
>> > > +             netdev_err(netdev, "Discarded frame: abort:%d
>> > > pruned:%d eof:%d\n",
>> > > +                        abort_flag, pruned_flag, eof_flag);
>> > > +             kfree_skb(skb);
>> > > +             return;
>> > > +     }
>> > > +
>> > > +     if (!netif_oper_up(netdev)) {
>> > > +             netdev_err(netdev, "Discarded frame: Interface not
>> > > up\n");
>> > > +             kfree_skb(skb);
>> > > +             return;
>> > > +     }
>> >
>> > Why is it sending frames when it is not up?
>>
>> This is intended for received frames. A situation where the lower
>> layers have been enabled correctly but not the port.
>
>But why should that happen? It suggests you have the order wrong. The
>lower level should only be enabled once the port is opened.

Yes, on second thought I think this was added to capture an error
situation with a particular cuPHY that we were testing.
It should be removed now.
>
>> > No DMA? What sort of performance do you get? Enough for the odd BPDU,
>> > IGMP frame etc, but i guess you don't want any real bulk data to be
>> > sent this way?
>>
>> Yes the register based injection/extration is not going to be fast, but
>> the FDMA and its driver is being sent later as separate series to keep
>> the size of this review down.
>
>FDMA?

Ah, I should qualify this a bit more: A "Frame DMA" to transfer rx/tx
frames via CPU ports instead of the register based injection/extraction
that is in the driver at the moment.
>
>I need a bit more background here, just to make use this should be a
>pure switchdev driver and not a DSA driver.
>
It is not a DSA driver (if I have understood the principle correctly).
>>
>> >
>> > > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
>> > > +{
>> > > +     struct sparx5 *sparx5 = _sparx5;
>> > > +
>> > > +     /* Check data in queue */
>> > > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
>> > > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
>> > > +
>> > > +     return IRQ_HANDLED;
>> > > +}
>> >
>> > Is there any sort of limit how many times this will loop? If somebody
>> > is blasting 10Gbps at the CPU, will it ever get out of this loop?
>>
>> Hmmm, not at the moment but this is because the FDMA driver is intended
>> to be used in these scenarios.
>
>So throwing out an idea, which might be terrible. How about limiting
>it to 64 loops, the same as the NAPI poll? That might allow the
>machine to get some work done before the next interrupt? Does the
>hardware do interrupt coalescing? But is this is going to be quickly
>thrown away and replaced with FDMA, don't spend too much time on it.

I agree with you.  I will put a cap on the number of loops.

>
>         Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-23 13:29           ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 13:29 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

Hi Andrew,

On 22.12.2020 15:41, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>On Tue, Dec 22, 2020 at 10:46:12AM +0100, Steen Hegelund wrote:
>> Hi Andrew,
>>
>> On Sat, 2020-12-19 at 20:51 +0100, Andrew Lunn wrote:
>> > EXTERNAL EMAIL: Do not click links or open attachments unless you
>> > know the content is safe
>> >
>> > > +     /* Create a phylink for PHY management.  Also handles SFPs */
>> > > +     spx5_port->phylink_config.dev = &spx5_port->ndev->dev;
>> > > +     spx5_port->phylink_co
>> > > nfig.type = PHYLINK_NETDEV;
>> > > +     spx5_port->phylink_config.pcs_poll = true;
>> > > +
>> > > +     /* phylink needs a valid interface mode to parse dt node */
>> > > +     if (phy_mode == PHY_INTERFACE_MODE_NA)
>> > > +             phy_mode = PHY_INTERFACE_MODE_10GBASER;
>> >
>> > Maybe just enforce a valid value in DT?
>>
>> Maybe I need to clarify that you must choose between an Ethernet cuPHY
>> or an SFP, so it is optional.
>
>But you also need to watch out for somebody putting a copper modules
>in an SFP port. phylink will then set the mode to SGMII for a 1G
>copper module, etc.
>
The cuPHY SFPs are handled by phylink out-of-the-box if the
kernel has added support for the particular cuPHY driver, and that is
done just by specifying the SFP phandle.
So here we just need to know if the user has attached a cuPHY directly
or an SFP.

The phylink_of_phy_connect function provides a way to add a cuPHY
direcly to the PHYLINK instance, but I have not found a way that you can
specify a specific cuPHY embedded in an SFP, so here PHYLINK determines
what is the appropriate PHY (driver) to use.

Could this be done in a simpler way?

>> > > +/* Configuration */
>> > > +static inline bool sparx5_use_cu_phy(struct sparx5_port *port)
>> > > +{
>> > > +     return port->conf.phy_mode != PHY_INTERFACE_MODE_NA;
>> > > +}
>> >
>> > That is a rather odd definition of copper.
>>
>> Should I rather use a bool property to select between the two options
>> (cuPHY or SFP)?
>
>I guess what you are trying to indicate is between a hard wired Copper
>PHY and an SFP cage? You have some sort of MII switch which allows the
>MAC to be connected to either the QSGMII PHY, or an SFP cage? But
>since the SFP cage could be populated with a copper PHY, and PHYLINK
>will then instantiate a phylib copper PHY driver for it, looking at
>phy_mode is not reliable. You need a property which selects the port,
>not the technology.

Yes the intention was to be able to distinguish between the hardwired 
cuPHY case and the SFP case.

I am OK with adding a property to distinguish between the two cases, but
if the SFP handle is present, PHYLINK has been able to handle an
embedded cuPHY (if the driver is available) and use that in the tests
that I have done so far. So my thinking was that if a phy handle is
present, then the user wants a directly attached cuPHY, not an SFP.

>
>> > > +static int sparx5_port_open(struct net_device *ndev)
>> > > +{
>> > > +     struct sparx5_port *port = netdev_priv(ndev);
>> > > +     int err = 0;
>> > > +
>> > > +     err = phylink_of_phy_connect(port->phylink, port->of_node,
>> > > 0);
>> > > +     if (err) {
>> > > +             netdev_err(ndev, "Could not attach to PHY\n");
>> > > +             return err;
>> > > +     }
>> > > +
>> > > +     phylink_start(port->phylink);
>> > > +
>> > > +     if (!ndev->phydev) {
>> >
>> > Humm. When is ndev->phydev set? I don't think phylink ever sets it.
>>
>> Indirectly: phylink_of_phy_connect uses phy_attach_direct and that sets
>> the phydev.
>
>Ah, O.K. But watch out for a copper SFP module!

Hmm, my expectation is that we have this covered by now.

>
>> > > +static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool
>> > > byte_swap)
>> > > +{
>> > > +     int i, byte_cnt = 0;
>> > > +     bool eof_flag = false, pruned_flag = false, abort_flag =
>> > > false;
>> > > +     u32 ifh[IFH_LEN];
>> > > +     struct sk_buff *skb;
>> > > +     struct frame_info fi;
>> > > +     struct sparx5_port *port;
>> > > +     struct net_device *netdev;
>> > > +     u32 *rxbuf;
>> > > +
>> > > +     /* Get IFH */
>> > > +     for (i = 0; i < IFH_LEN; i++)
>> > > +             ifh[i] = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +
>> > > +     /* Decode IFH (whats needed) */
>> > > +     sparx5_ifh_parse(ifh, &fi);
>> > > +
>> > > +     /* Map to port netdev */
>> > > +     port = fi.src_port < SPX5_PORTS ?
>> > > +             sparx5->ports[fi.src_port] : NULL;
>> > > +     if (!port || !port->ndev) {
>> > > +             dev_err(sparx5->dev, "Data on inactive port %d\n",
>> > > fi.src_port);
>> > > +             sparx5_xtr_flush(sparx5, grp);
>> > > +             return;
>> > > +     }
>> > > +
>> > > +     /* Have netdev, get skb */
>> > > +     netdev = port->ndev;
>> > > +     skb = netdev_alloc_skb(netdev, netdev->mtu + ETH_HLEN);
>> > > +     if (!skb) {
>> > > +             sparx5_xtr_flush(sparx5, grp);
>> > > +             dev_err(sparx5->dev, "No skb allocated\n");
>> > > +             return;
>> > > +     }
>> > > +     rxbuf = (u32 *)skb->data;
>> > > +
>> > > +     /* Now, pull frame data */
>> > > +     while (!eof_flag) {
>> > > +             u32 val = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +             u32 cmp = val;
>> > > +
>> > > +             if (byte_swap)
>> > > +                     cmp = ntohl((__force __be32)val);
>> > > +
>> > > +             switch (cmp) {
>> > > +             case XTR_NOT_READY:
>> > > +                     break;
>> > > +             case XTR_ABORT:
>> > > +                     /* No accompanying data */
>> > > +                     abort_flag = true;
>> > > +                     eof_flag = true;
>> > > +                     break;
>> > > +             case XTR_EOF_0:
>> > > +             case XTR_EOF_1:
>> > > +             case XTR_EOF_2:
>> > > +             case XTR_EOF_3:
>> > > +                     /* This assumes STATUS_WORD_POS == 1, Status
>> > > +                      * just after last data
>> > > +                      */
>> > > +                     byte_cnt -= (4 - XTR_VALID_BYTES(val));
>> > > +                     eof_flag = true;
>> > > +                     break;
>> > > +             case XTR_PRUNED:
>> > > +                     /* But get the last 4 bytes as well */
>> > > +                     eof_flag = true;
>> > > +                     pruned_flag = true;
>> > > +                     fallthrough;
>> > > +             case XTR_ESCAPE:
>> > > +                     *rxbuf = spx5_rd(sparx5, QS_XTR_RD(grp));
>> > > +                     byte_cnt += 4;
>> > > +                     rxbuf++;
>> > > +                     break;
>> > > +             default:
>> > > +                     *rxbuf = val;
>> > > +                     byte_cnt += 4;
>> > > +                     rxbuf++;
>> > > +             }
>> > > +     }
>> > > +
>> > > +     if (abort_flag || pruned_flag || !eof_flag) {
>> > > +             netdev_err(netdev, "Discarded frame: abort:%d
>> > > pruned:%d eof:%d\n",
>> > > +                        abort_flag, pruned_flag, eof_flag);
>> > > +             kfree_skb(skb);
>> > > +             return;
>> > > +     }
>> > > +
>> > > +     if (!netif_oper_up(netdev)) {
>> > > +             netdev_err(netdev, "Discarded frame: Interface not
>> > > up\n");
>> > > +             kfree_skb(skb);
>> > > +             return;
>> > > +     }
>> >
>> > Why is it sending frames when it is not up?
>>
>> This is intended for received frames. A situation where the lower
>> layers have been enabled correctly but not the port.
>
>But why should that happen? It suggests you have the order wrong. The
>lower level should only be enabled once the port is opened.

Yes, on second thought I think this was added to capture an error
situation with a particular cuPHY that we were testing.
It should be removed now.
>
>> > No DMA? What sort of performance do you get? Enough for the odd BPDU,
>> > IGMP frame etc, but i guess you don't want any real bulk data to be
>> > sent this way?
>>
>> Yes the register based injection/extration is not going to be fast, but
>> the FDMA and its driver is being sent later as separate series to keep
>> the size of this review down.
>
>FDMA?

Ah, I should qualify this a bit more: A "Frame DMA" to transfer rx/tx
frames via CPU ports instead of the register based injection/extraction
that is in the driver at the moment.
>
>I need a bit more background here, just to make use this should be a
>pure switchdev driver and not a DSA driver.
>
It is not a DSA driver (if I have understood the principle correctly).
>>
>> >
>> > > +irqreturn_t sparx5_xtr_handler(int irq, void *_sparx5)
>> > > +{
>> > > +     struct sparx5 *sparx5 = _sparx5;
>> > > +
>> > > +     /* Check data in queue */
>> > > +     while (spx5_rd(sparx5, QS_XTR_DATA_PRESENT) & BIT(XTR_QUEUE))
>> > > +             sparx5_xtr_grp(sparx5, XTR_QUEUE, false);
>> > > +
>> > > +     return IRQ_HANDLED;
>> > > +}
>> >
>> > Is there any sort of limit how many times this will loop? If somebody
>> > is blasting 10Gbps at the CPU, will it ever get out of this loop?
>>
>> Hmmm, not at the moment but this is because the FDMA driver is intended
>> to be used in these scenarios.
>
>So throwing out an idea, which might be terrible. How about limiting
>it to 64 loops, the same as the NAPI poll? That might allow the
>machine to get some work done before the next interrupt? Does the
>hardware do interrupt coalescing? But is this is going to be quickly
>thrown away and replaced with FDMA, don't spend too much time on it.

I agree with you.  I will put a cap on the number of loops.

>
>         Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
  2020-12-21  0:25     ` Andrew Lunn
@ 2020-12-23 13:54       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 13:54 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Russell King, Lars Povlsen,
	Bjarni Jonasson, Microchip Linux Driver Support,
	Alexandre Belloni, Madalin Bucur, Nicolas Ferre, Mark Einon,
	Masahiro Yamada, Arnd Bergmann, netdev, linux-kernel,
	linux-arm-kernel

Hi Andrew,

On 21.12.2020 01:25, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
>> +
>> +static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
>> +{
>> +     return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
>> +}
>> +
>> +static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
>> +{
>> +     u32 val;
>> +
>> +     return readx_poll_timeout(sparx5_mact_get_status,
>> +             sparx5, val,
>> +             LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
>> +             TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
>> +}
>
>No inline functions in C files please.

OK.

>
>> +void sparx5_mact_init(struct sparx5 *sparx5)
>> +{
>> +     mutex_init(&sparx5->lock);
>> +
>> +     mutex_lock(&sparx5->lock);
>> +
>> +     /*  Flush MAC table */
>> +     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
>> +             LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
>> +             sparx5, LRN_COMMON_ACCESS_CTRL);
>> +
>> +     if (sparx5_mact_wait_for_completion(sparx5) != 0)
>> +             dev_warn(sparx5->dev, "MAC flush error\n");
>> +
>> +     mutex_unlock(&sparx5->lock);
>
>It always seems odd to me, when you initialise a mutex, and then
>immediately take it. Who are you locking against? I'm not saying it is
>wrong though, especially if you have code in spx5_wr() and spx5_rd()
>which check the lock is actually taken. I've found a number of locking
>bugs in mv88e6xxx by having such checks.

The driver has a workqueue and a notifier callback that may want to
access the table, and will have to wait in line to be served, but since
they have not yet been activated at this point, you are probably correct
in saying that locking is not needed at init time.
I will investigate this..

>
>> +
>> +     sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */
>
>BR_DEFAULT_AGEING_TIME is 300 seconds. Is this the same thing?

Same thing but different value.  I think this should change to same
default, but I will test it out.
>
>> +static int sparx5_port_bridge_join(struct sparx5_port *port,
>> +                                struct net_device *bridge)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +
>> +     if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
>> +             /* First bridged port */
>> +             sparx5->hw_bridge_dev = bridge;
>> +     else
>> +             if (sparx5->hw_bridge_dev != bridge)
>> +                     /* This is adding the port to a second bridge, this is
>> +                      * unsupported
>> +                      */
>> +                     return -ENODEV;
>
>Just checking my understanding. You have a 64 port switch, which only
>supports a single bridge?

Yes, at least for the initial version. The switch has some
virtualization features, but were not using that just yet.

>
>-EOPNOTSUPP seems like a better return code.
>
>> +
>> +     set_bit(port->portno, sparx5->bridge_mask);
>> +
>> +     /* Port enters in bridge mode therefor don't need to copy to CPU
>> +      * frames for multicast in case the bridge is not requesting them
>> +      */
>> +     __dev_mc_unsync(port->ndev, sparx5_mc_unsync);
>
>Did you copy that from the mellanox driver? I think in DSA we take the
>opposite approach. Multicast/broadcast goes to the CPU until the CPU
>says it does not want it.
>
>
It is "inspired" by the Ocelot driver. MC is explicitly opted in.

>> +static void sparx5_port_bridge_leave(struct sparx5_port *port,
>> +                                  struct net_device *bridge)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +
>> +     clear_bit(port->portno, sparx5->bridge_mask);
>> +     if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
>> +             sparx5->hw_bridge_dev = NULL;
>> +
>> +     /* Clear bridge vlan settings before updating the port settings */
>> +     port->vlan_aware = 0;
>> +     port->pvid = NULL_VID;
>> +     port->vid = NULL_VID;
>> +
>> +     /* Port enters in host more therefore restore mc list */
>
>s/more/mode

OK.

>
>> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
>> @@ -0,0 +1,223 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/* Microchip Sparx5 Switch driver
>> + *
>> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
>> + */
>> +
>> +#include "sparx5_main.h"
>> +
>> +static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
>
>Is the t in vlant typ0?

No, it is probably short for "vlan table". Again the inspiration comes
from the Ocelot implementation.

>
>> +int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
>> +                     bool untagged)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +     int ret;
>> +
>> +     /* Make the port a member of the VLAN */
>> +     set_bit(port->portno, sparx5->vlan_mask[vid]);
>> +     ret = sparx5_vlant_set_mask(sparx5, vid);
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* Default ingress vlan classification */
>> +     if (pvid)
>> +             port->pvid = vid;
>> +
>> +     /* Untagged egress vlan clasification */
>
>classification

OK.

>
>> +     if (untagged && port->vid != vid) {
>> +             if (port->vid) {
>> +                     netdev_err(port->ndev,
>> +                                "Port already has a native VLAN: %d\n",
>> +                                port->vid);
>> +                     return -EBUSY;
>> +             }
>> +             port->vid = vid;
>> +     }
>> +
>> +     sparx5_vlan_port_apply(sparx5, port);
>> +
>> +     return 0;
>> +}
>
>
>> +void sparx5_update_fwd(struct sparx5 *sparx5)
>> +{
>> +     u32 mask[3];
>> +     DECLARE_BITMAP(workmask, SPX5_PORTS);
>> +     int port;
>> +
>> +     /* Divide up fwd mask in 32 bit words */
>> +     bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
>> +
>> +     /* Update flood masks */
>> +     for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
>> +             spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
>> +             spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
>> +             spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
>> +     }
>> +
>> +     /* Update SRC masks */
>> +     for (port = 0; port < SPX5_PORTS; port++) {
>> +             if (test_bit(port, sparx5->bridge_fwd_mask)) {
>> +                     /* Allow to send to all bridged but self */
>> +                     bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
>> +                     clear_bit(port, workmask);
>> +                     bitmap_to_arr32(mask, workmask, SPX5_PORTS);
>> +                     spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
>> +                     spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
>> +                     spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
>> +             } else {
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
>> +             }
>
>Humm, interesting. This seems to control what other ports a port can
>send to. That is one of the basic features you need for supporting
>multiple bridges. So i assume your problems is you cannot partition
>the MAC table?
>
No, the MAC table is VLAN aware.

>
>    Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

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

* Re: [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support
@ 2020-12-23 13:54       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 13:54 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Madalin Bucur, netdev, Masahiro Yamada, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, David S. Miller, Lars Povlsen

Hi Andrew,

On 21.12.2020 01:25, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
>> +
>> +static inline int sparx5_mact_get_status(struct sparx5 *sparx5)
>> +{
>> +     return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
>> +}
>> +
>> +static inline int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
>> +{
>> +     u32 val;
>> +
>> +     return readx_poll_timeout(sparx5_mact_get_status,
>> +             sparx5, val,
>> +             LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
>> +             TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
>> +}
>
>No inline functions in C files please.

OK.

>
>> +void sparx5_mact_init(struct sparx5 *sparx5)
>> +{
>> +     mutex_init(&sparx5->lock);
>> +
>> +     mutex_lock(&sparx5->lock);
>> +
>> +     /*  Flush MAC table */
>> +     spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
>> +             LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
>> +             sparx5, LRN_COMMON_ACCESS_CTRL);
>> +
>> +     if (sparx5_mact_wait_for_completion(sparx5) != 0)
>> +             dev_warn(sparx5->dev, "MAC flush error\n");
>> +
>> +     mutex_unlock(&sparx5->lock);
>
>It always seems odd to me, when you initialise a mutex, and then
>immediately take it. Who are you locking against? I'm not saying it is
>wrong though, especially if you have code in spx5_wr() and spx5_rd()
>which check the lock is actually taken. I've found a number of locking
>bugs in mv88e6xxx by having such checks.

The driver has a workqueue and a notifier callback that may want to
access the table, and will have to wait in line to be served, but since
they have not yet been activated at this point, you are probably correct
in saying that locking is not needed at init time.
I will investigate this..

>
>> +
>> +     sparx5_set_ageing(sparx5, 10 * MSEC_PER_SEC); /* 10 sec */
>
>BR_DEFAULT_AGEING_TIME is 300 seconds. Is this the same thing?

Same thing but different value.  I think this should change to same
default, but I will test it out.
>
>> +static int sparx5_port_bridge_join(struct sparx5_port *port,
>> +                                struct net_device *bridge)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +
>> +     if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
>> +             /* First bridged port */
>> +             sparx5->hw_bridge_dev = bridge;
>> +     else
>> +             if (sparx5->hw_bridge_dev != bridge)
>> +                     /* This is adding the port to a second bridge, this is
>> +                      * unsupported
>> +                      */
>> +                     return -ENODEV;
>
>Just checking my understanding. You have a 64 port switch, which only
>supports a single bridge?

Yes, at least for the initial version. The switch has some
virtualization features, but were not using that just yet.

>
>-EOPNOTSUPP seems like a better return code.
>
>> +
>> +     set_bit(port->portno, sparx5->bridge_mask);
>> +
>> +     /* Port enters in bridge mode therefor don't need to copy to CPU
>> +      * frames for multicast in case the bridge is not requesting them
>> +      */
>> +     __dev_mc_unsync(port->ndev, sparx5_mc_unsync);
>
>Did you copy that from the mellanox driver? I think in DSA we take the
>opposite approach. Multicast/broadcast goes to the CPU until the CPU
>says it does not want it.
>
>
It is "inspired" by the Ocelot driver. MC is explicitly opted in.

>> +static void sparx5_port_bridge_leave(struct sparx5_port *port,
>> +                                  struct net_device *bridge)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +
>> +     clear_bit(port->portno, sparx5->bridge_mask);
>> +     if (bitmap_empty(sparx5->bridge_mask, SPX5_PORTS))
>> +             sparx5->hw_bridge_dev = NULL;
>> +
>> +     /* Clear bridge vlan settings before updating the port settings */
>> +     port->vlan_aware = 0;
>> +     port->pvid = NULL_VID;
>> +     port->vid = NULL_VID;
>> +
>> +     /* Port enters in host more therefore restore mc list */
>
>s/more/mode

OK.

>
>> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c
>> @@ -0,0 +1,223 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/* Microchip Sparx5 Switch driver
>> + *
>> + * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
>> + */
>> +
>> +#include "sparx5_main.h"
>> +
>> +static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
>
>Is the t in vlant typ0?

No, it is probably short for "vlan table". Again the inspiration comes
from the Ocelot implementation.

>
>> +int sparx5_vlan_vid_add(struct sparx5_port *port, u16 vid, bool pvid,
>> +                     bool untagged)
>> +{
>> +     struct sparx5 *sparx5 = port->sparx5;
>> +     int ret;
>> +
>> +     /* Make the port a member of the VLAN */
>> +     set_bit(port->portno, sparx5->vlan_mask[vid]);
>> +     ret = sparx5_vlant_set_mask(sparx5, vid);
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* Default ingress vlan classification */
>> +     if (pvid)
>> +             port->pvid = vid;
>> +
>> +     /* Untagged egress vlan clasification */
>
>classification

OK.

>
>> +     if (untagged && port->vid != vid) {
>> +             if (port->vid) {
>> +                     netdev_err(port->ndev,
>> +                                "Port already has a native VLAN: %d\n",
>> +                                port->vid);
>> +                     return -EBUSY;
>> +             }
>> +             port->vid = vid;
>> +     }
>> +
>> +     sparx5_vlan_port_apply(sparx5, port);
>> +
>> +     return 0;
>> +}
>
>
>> +void sparx5_update_fwd(struct sparx5 *sparx5)
>> +{
>> +     u32 mask[3];
>> +     DECLARE_BITMAP(workmask, SPX5_PORTS);
>> +     int port;
>> +
>> +     /* Divide up fwd mask in 32 bit words */
>> +     bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
>> +
>> +     /* Update flood masks */
>> +     for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
>> +             spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
>> +             spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
>> +             spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
>> +     }
>> +
>> +     /* Update SRC masks */
>> +     for (port = 0; port < SPX5_PORTS; port++) {
>> +             if (test_bit(port, sparx5->bridge_fwd_mask)) {
>> +                     /* Allow to send to all bridged but self */
>> +                     bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
>> +                     clear_bit(port, workmask);
>> +                     bitmap_to_arr32(mask, workmask, SPX5_PORTS);
>> +                     spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
>> +                     spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
>> +                     spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
>> +             } else {
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
>> +                     spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
>> +             }
>
>Humm, interesting. This seems to control what other ports a port can
>send to. That is one of the basic features you need for supporting
>multiple bridges. So i assume your problems is you cannot partition
>the MAC table?
>
No, the MAC table is VLAN aware.

>
>    Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
  2020-12-19 20:24     ` Andrew Lunn
@ 2020-12-23 14:31       ` Steen Hegelund
  -1 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 14:31 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On 19.12.2020 21:24, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> +             port13: port@13 {
>> +                     reg = <13>;
>> +                     /* Example: CU SFP, 1G speed */
>> +                     max-speed = <10000>;
>
>One too many 0's for 1G.

Ah, but this is allocation for the port, not the speed.  This just
used by the calendar module to allocate slots on the taxis as requested.
So I would say it is OK to overallocate in this case (but you could
argue it does not make much sense).

>
>> +             /* 25G SFPs */
>> +             port56: port@56 {
>> +                     reg = <56>;
>> +                     max-speed = <10000>;
>
>Why limit a 25G SFP to 10G?

In the PCB134 case it is to keep the total allocation below 200Gbits
((12+8)*10G).  There is a port mux mode that provides 8*25G on the 25G
SerDes'es, but that would be a different DT.

The Datasheet shows which port mux combinations are possible, and not
all combinations of SerDes, Speed and interface are allowed.

The PCB134 was designed to showcase this "many 10G ports" mode, so that
is why we have the current DT.

>
>    Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
@ 2020-12-23 14:31       ` Steen Hegelund
  0 siblings, 0 replies; 75+ messages in thread
From: Steen Hegelund @ 2020-12-23 14:31 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Device Tree List, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, Rob Herring,
	linux-arm-kernel, Mark Einon, Jakub Kicinski, David S. Miller,
	Lars Povlsen

On 19.12.2020 21:24, Andrew Lunn wrote:
>EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
>> +             port13: port@13 {
>> +                     reg = <13>;
>> +                     /* Example: CU SFP, 1G speed */
>> +                     max-speed = <10000>;
>
>One too many 0's for 1G.

Ah, but this is allocation for the port, not the speed.  This just
used by the calendar module to allocate slots on the taxis as requested.
So I would say it is OK to overallocate in this case (but you could
argue it does not make much sense).

>
>> +             /* 25G SFPs */
>> +             port56: port@56 {
>> +                     reg = <56>;
>> +                     max-speed = <10000>;
>
>Why limit a 25G SFP to 10G?

In the PCB134 case it is to keep the total allocation below 200Gbits
((12+8)*10G).  There is a port mux mode that provides 8*25G on the 25G
SerDes'es, but that would be a different DT.

The Datasheet shows which port mux combinations are possible, and not
all combinations of SerDes, Speed and interface are allowed.

The PCB134 was designed to showcase this "many 10G ports" mode, so that
is why we have the current DT.

>
>    Andrew

BR
Steen

---------------------------------------
Steen Hegelund
steen.hegelund@microchip.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
  2020-12-23 14:31       ` Steen Hegelund
@ 2020-12-23 15:49         ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-23 15:49 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: David S. Miller, Jakub Kicinski, Rob Herring, Device Tree List,
	Russell King, Lars Povlsen, Bjarni Jonasson,
	Microchip Linux Driver Support, Alexandre Belloni, Madalin Bucur,
	Nicolas Ferre, Mark Einon, Masahiro Yamada, Arnd Bergmann,
	netdev, linux-kernel, linux-arm-kernel

On Wed, Dec 23, 2020 at 03:31:24PM +0100, Steen Hegelund wrote:
> On 19.12.2020 21:24, Andrew Lunn wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> > 
> > > +             port13: port@13 {
> > > +                     reg = <13>;
> > > +                     /* Example: CU SFP, 1G speed */
> > > +                     max-speed = <10000>;
> > 
> > One too many 0's for 1G.
> 
> Ah, but this is allocation for the port, not the speed.

phylib will look for this property and change what the PHY advertises
based on this. There are some devices with Fast Ethernet but with a 1G
PHY, because they are cheaper. By setting max-speed=<100>; phylib will
stop the PHY advertising 1000Base-T/Full and 1000Base-T/Half. I can
imaging the same is used when the MAC can do 2.5G, but the PHY is 5G
capable, etc.

> This just used by the calendar module to allocate slots on the taxis
> as requested.  So I would say it is OK to overallocate in this case
> (but you could argue it does not make much sense).

Rather than misusing the max-speed property, it would be better to add
a property with your specific meaning.

  Andrew

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

* Re: [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node
@ 2020-12-23 15:49         ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-23 15:49 UTC (permalink / raw)
  To: Steen Hegelund
  Cc: Device Tree List, Alexandre Belloni, linux-kernel, Arnd Bergmann,
	Bjarni Jonasson, Madalin Bucur, netdev, Masahiro Yamada,
	Russell King, Microchip Linux Driver Support, Rob Herring,
	linux-arm-kernel, Mark Einon, Jakub Kicinski, David S. Miller,
	Lars Povlsen

On Wed, Dec 23, 2020 at 03:31:24PM +0100, Steen Hegelund wrote:
> On 19.12.2020 21:24, Andrew Lunn wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> > 
> > > +             port13: port@13 {
> > > +                     reg = <13>;
> > > +                     /* Example: CU SFP, 1G speed */
> > > +                     max-speed = <10000>;
> > 
> > One too many 0's for 1G.
> 
> Ah, but this is allocation for the port, not the speed.

phylib will look for this property and change what the PHY advertises
based on this. There are some devices with Fast Ethernet but with a 1G
PHY, because they are cheaper. By setting max-speed=<100>; phylib will
stop the PHY advertising 1000Base-T/Full and 1000Base-T/Half. I can
imaging the same is used when the MAC can do 2.5G, but the PHY is 5G
capable, etc.

> This just used by the calendar module to allocate slots on the taxis
> as requested.  So I would say it is OK to overallocate in this case
> (but you could argue it does not make much sense).

Rather than misusing the max-speed property, it would be better to add
a property with your specific meaning.

  Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-22 14:41         ` Andrew Lunn
@ 2020-12-23 20:58           ` Alexandre Belloni
  -1 siblings, 0 replies; 75+ messages in thread
From: Alexandre Belloni @ 2020-12-23 20:58 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Steen Hegelund, David S. Miller, Jakub Kicinski, Russell King,
	Lars Povlsen, Bjarni Jonasson, Microchip Linux Driver Support,
	Madalin Bucur, Nicolas Ferre, Mark Einon, Masahiro Yamada,
	Arnd Bergmann, netdev, linux-kernel, linux-arm-kernel

On 22/12/2020 15:41:41+0100, Andrew Lunn wrote:
> > Yes the register based injection/extration is not going to be fast, but
> > the FDMA and its driver is being sent later as separate series to keep
> > the size of this review down.
> 
> FDMA?
> 
> I need a bit more background here, just to make use this should be a
> pure switchdev driver and not a DSA driver.
> 

I don't think this should be a DSA driver. As for Ocelot, the CPU
port is not a MAC and in that use case, this would be like a top of the
rack switch with traffic going to the CPU port being mostly used for
managmement (dhcp, stp, etc...) as opposed to being used to forward
traffic to another interface, like WAN or wifi.

However, I would think there will be cases where the internal CPU is not
use and instead use ths switch in a DSA setting, very much like what is
done for Felix with regards to Ocelot.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-23 20:58           ` Alexandre Belloni
  0 siblings, 0 replies; 75+ messages in thread
From: Alexandre Belloni @ 2020-12-23 20:58 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Bjarni Jonasson, linux-kernel, Arnd Bergmann, Madalin Bucur,
	netdev, Steen Hegelund, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, Masahiro Yamada, David S. Miller, Lars Povlsen

On 22/12/2020 15:41:41+0100, Andrew Lunn wrote:
> > Yes the register based injection/extration is not going to be fast, but
> > the FDMA and its driver is being sent later as separate series to keep
> > the size of this review down.
> 
> FDMA?
> 
> I need a bit more background here, just to make use this should be a
> pure switchdev driver and not a DSA driver.
> 

I don't think this should be a DSA driver. As for Ocelot, the CPU
port is not a MAC and in that use case, this would be like a top of the
rack switch with traffic going to the CPU port being mostly used for
managmement (dhcp, stp, etc...) as opposed to being used to forward
traffic to another interface, like WAN or wifi.

However, I would think there will be cases where the internal CPU is not
use and instead use ths switch in a DSA setting, very much like what is
done for Felix with regards to Ocelot.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
  2020-12-23 20:58           ` Alexandre Belloni
@ 2020-12-23 21:05             ` Andrew Lunn
  -1 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-23 21:05 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Steen Hegelund, David S. Miller, Jakub Kicinski, Russell King,
	Lars Povlsen, Bjarni Jonasson, Microchip Linux Driver Support,
	Madalin Bucur, Nicolas Ferre, Mark Einon, Masahiro Yamada,
	Arnd Bergmann, netdev, linux-kernel, linux-arm-kernel

On Wed, Dec 23, 2020 at 09:58:52PM +0100, Alexandre Belloni wrote:
> On 22/12/2020 15:41:41+0100, Andrew Lunn wrote:
> > > Yes the register based injection/extration is not going to be fast, but
> > > the FDMA and its driver is being sent later as separate series to keep
> > > the size of this review down.
> > 
> > FDMA?
> > 
> > I need a bit more background here, just to make use this should be a
> > pure switchdev driver and not a DSA driver.
> > 
> 
> I don't think this should be a DSA driver. As for Ocelot, the CPU
> port is not a MAC and in that use case, this would be like a top of the
> rack switch with traffic going to the CPU port being mostly used for
> managmement (dhcp, stp, etc...) as opposed to being used to forward
> traffic to another interface, like WAN or wifi.
> 
> However, I would think there will be cases where the internal CPU is not
> use and instead use ths switch in a DSA setting, very much like what is
> done for Felix with regards to Ocelot.

From what i have heard so far, it does seem like a pure switchdev
driver is correct. So long as FDMA is not a standalone Ethernet
driver, but just a DMA engine incorporated into this driver, the
architecture looks correct.

I was asking because from the information that was available, it was
impossible to say what the correct architecture should be.

     Andrew

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

* Re: [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support
@ 2020-12-23 21:05             ` Andrew Lunn
  0 siblings, 0 replies; 75+ messages in thread
From: Andrew Lunn @ 2020-12-23 21:05 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Bjarni Jonasson, linux-kernel, Arnd Bergmann, Madalin Bucur,
	netdev, Steen Hegelund, Russell King,
	Microchip Linux Driver Support, linux-arm-kernel, Mark Einon,
	Jakub Kicinski, Masahiro Yamada, David S. Miller, Lars Povlsen

On Wed, Dec 23, 2020 at 09:58:52PM +0100, Alexandre Belloni wrote:
> On 22/12/2020 15:41:41+0100, Andrew Lunn wrote:
> > > Yes the register based injection/extration is not going to be fast, but
> > > the FDMA and its driver is being sent later as separate series to keep
> > > the size of this review down.
> > 
> > FDMA?
> > 
> > I need a bit more background here, just to make use this should be a
> > pure switchdev driver and not a DSA driver.
> > 
> 
> I don't think this should be a DSA driver. As for Ocelot, the CPU
> port is not a MAC and in that use case, this would be like a top of the
> rack switch with traffic going to the CPU port being mostly used for
> managmement (dhcp, stp, etc...) as opposed to being used to forward
> traffic to another interface, like WAN or wifi.
> 
> However, I would think there will be cases where the internal CPU is not
> use and instead use ths switch in a DSA setting, very much like what is
> done for Felix with regards to Ocelot.

From what i have heard so far, it does seem like a pure switchdev
driver is correct. So long as FDMA is not a standalone Ethernet
driver, but just a DMA engine incorporated into this driver, the
architecture looks correct.

I was asking because from the information that was available, it was
impossible to say what the correct architecture should be.

     Andrew

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, other threads:[~2020-12-23 21:07 UTC | newest]

Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-17  7:51 [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver Steen Hegelund
2020-12-17  7:51 ` Steen Hegelund
2020-12-17  7:51 ` [RFC PATCH v2 1/8] dt-bindings: net: sparx5: Add sparx5-switch bindings Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-19 17:54   ` Andrew Lunn
2020-12-19 17:54     ` Andrew Lunn
2020-12-21  0:55   ` Florian Fainelli
2020-12-21  0:55     ` Florian Fainelli
2020-12-21 10:00     ` Steen Hegelund
2020-12-21 10:00       ` Steen Hegelund
2020-12-21 21:40   ` Rob Herring
2020-12-21 21:40     ` Rob Herring
2020-12-22  7:30     ` Steen Hegelund
2020-12-22  7:30       ` Steen Hegelund
2020-12-17  7:51 ` [RFC PATCH v2 2/8] net: sparx5: add the basic sparx5 driver Steen Hegelund
2020-12-19 19:11   ` Andrew Lunn
2020-12-19 19:11     ` Andrew Lunn
2020-12-22 13:50     ` Steen Hegelund
2020-12-22 13:50       ` Steen Hegelund
2020-12-22 15:01       ` Andrew Lunn
2020-12-22 15:01         ` Andrew Lunn
2020-12-22 16:56         ` Alexandre Belloni
2020-12-22 16:56           ` Alexandre Belloni
2020-12-23  9:03           ` Lars Povlsen
2020-12-23  9:03             ` Lars Povlsen
2020-12-23  8:52         ` Steen Hegelund
2020-12-23  8:52           ` Steen Hegelund
2020-12-17  7:51 ` [RFC PATCH v2 3/8] net: sparx5: add hostmode with phylink support Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-19 19:51   ` Andrew Lunn
2020-12-19 19:51     ` Andrew Lunn
2020-12-22  9:46     ` Steen Hegelund
2020-12-22  9:46       ` Steen Hegelund
2020-12-22 14:41       ` Andrew Lunn
2020-12-22 14:41         ` Andrew Lunn
2020-12-23 13:29         ` Steen Hegelund
2020-12-23 13:29           ` Steen Hegelund
2020-12-23 20:58         ` Alexandre Belloni
2020-12-23 20:58           ` Alexandre Belloni
2020-12-23 21:05           ` Andrew Lunn
2020-12-23 21:05             ` Andrew Lunn
2020-12-17  7:51 ` [RFC PATCH v2 4/8] net: sparx5: add port module support Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-20 23:35   ` Andrew Lunn
2020-12-20 23:35     ` Andrew Lunn
2020-12-22 14:55     ` Bjarni Jonasson
2020-12-22 14:55       ` Bjarni Jonasson
2020-12-22 15:08       ` Andrew Lunn
2020-12-22 15:08         ` Andrew Lunn
2020-12-17  7:51 ` [RFC PATCH v2 5/8] net: sparx5: add switching, vlan and mactable support Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-21  0:25   ` Andrew Lunn
2020-12-21  0:25     ` Andrew Lunn
2020-12-23 13:54     ` Steen Hegelund
2020-12-23 13:54       ` Steen Hegelund
2020-12-17  7:51 ` [RFC PATCH v2 6/8] net: sparx5: add calendar bandwidth allocation support Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-17  7:51 ` [RFC PATCH v2 7/8] net: sparx5: add ethtool configuration and statistics support Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-19 23:31   ` Andrew Lunn
2020-12-19 23:31     ` Andrew Lunn
2020-12-17  7:51 ` [RFC PATCH v2 8/8] arm64: dts: sparx5: Add the Sparx5 switch node Steen Hegelund
2020-12-17  7:51   ` Steen Hegelund
2020-12-19 20:24   ` Andrew Lunn
2020-12-19 20:24     ` Andrew Lunn
2020-12-23 14:31     ` Steen Hegelund
2020-12-23 14:31       ` Steen Hegelund
2020-12-23 15:49       ` Andrew Lunn
2020-12-23 15:49         ` Andrew Lunn
2020-12-21  0:58 ` [RFC PATCH v2 0/8] Adding the Sparx5 Switch Driver Florian Fainelli
2020-12-21  0:58   ` Florian Fainelli
2020-12-21 14:31   ` Steen Hegelund
2020-12-21 14:31     ` Steen Hegelund
2020-12-22 11:29   ` Lars Povlsen
2020-12-22 11:29     ` Lars Povlsen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.