linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next 0/9] net: ethernet backplane support
@ 2020-03-26 13:51 Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 1/9] doc: net: add backplane documentation Florinel Iordache
                   ` (8 more replies)
  0 siblings, 9 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add support for Ethernet Backplane KR generic driver using link training
(ieee802.3ap/ba standards), equalization algorithms (bee, fixed) and
enable qoriq family of devices

Florinel Iordache (9):
  doc: net: add backplane documentation
  dt-bindings: net: add backplane dt bindings
  net: phy: add support for kr phy connection type
  net: fman: add kr support for dpaa1 mac
  net: dpaa2: add kr support for dpaa2 mac
  net: phy: add backplane kr driver support
  net: phy: enable qoriq backplane support
  net: phy: add bee algorithm for kr training
  arm64: dts: add serdes and mdio description

 .../bindings/net/ethernet-controller.yaml          |    3 +-
 .../devicetree/bindings/net/ethernet-phy.yaml      |   53 +
 Documentation/devicetree/bindings/net/serdes.yaml  |   90 ++
 Documentation/networking/backplane.rst             |  165 ++
 arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi     |   33 +-
 arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi     |   97 +-
 arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi     |  160 +-
 arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi     |  128 +-
 .../boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi    |    5 +-
 .../boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi    |    5 +-
 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c   |   10 +-
 drivers/net/ethernet/freescale/fman/mac.c          |   10 +-
 drivers/net/phy/Kconfig                            |    2 +
 drivers/net/phy/Makefile                           |    1 +
 drivers/net/phy/backplane/Kconfig                  |   40 +
 drivers/net/phy/backplane/Makefile                 |   12 +
 drivers/net/phy/backplane/backplane.c              | 1538 +++++++++++++++++++
 drivers/net/phy/backplane/backplane.h              |  262 ++++
 drivers/net/phy/backplane/eq_bee.c                 | 1078 +++++++++++++
 drivers/net/phy/backplane/eq_fixed.c               |   83 +
 drivers/net/phy/backplane/equalization.h           |  282 ++++
 drivers/net/phy/backplane/link_training.c          | 1604 ++++++++++++++++++++
 drivers/net/phy/backplane/link_training.h          |   34 +
 drivers/net/phy/backplane/qoriq_backplane.c        |  442 ++++++
 drivers/net/phy/backplane/qoriq_backplane.h        |   33 +
 drivers/net/phy/backplane/qoriq_serdes_10g.c       |  470 ++++++
 drivers/net/phy/backplane/qoriq_serdes_28g.c       |  533 +++++++
 drivers/net/phy/phylink.c                          |   15 +-
 include/linux/phy.h                                |    6 +-
 29 files changed, 7176 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/serdes.yaml
 create mode 100644 Documentation/networking/backplane.rst
 create mode 100644 drivers/net/phy/backplane/Kconfig
 create mode 100644 drivers/net/phy/backplane/Makefile
 create mode 100644 drivers/net/phy/backplane/backplane.c
 create mode 100644 drivers/net/phy/backplane/backplane.h
 create mode 100644 drivers/net/phy/backplane/eq_bee.c
 create mode 100644 drivers/net/phy/backplane/eq_fixed.c
 create mode 100644 drivers/net/phy/backplane/equalization.h
 create mode 100644 drivers/net/phy/backplane/link_training.c
 create mode 100644 drivers/net/phy/backplane/link_training.h
 create mode 100644 drivers/net/phy/backplane/qoriq_backplane.c
 create mode 100644 drivers/net/phy/backplane/qoriq_backplane.h
 create mode 100644 drivers/net/phy/backplane/qoriq_serdes_10g.c
 create mode 100644 drivers/net/phy/backplane/qoriq_serdes_28g.c

-- 
1.9.1


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

* [PATCH net-next 1/9] doc: net: add backplane documentation
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings Florinel Iordache
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add ethernet backplane documentation

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 Documentation/networking/backplane.rst | 165 +++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100644 Documentation/networking/backplane.rst

diff --git a/Documentation/networking/backplane.rst b/Documentation/networking/backplane.rst
new file mode 100644
index 0000000..951c17e
--- /dev/null
+++ b/Documentation/networking/backplane.rst
@@ -0,0 +1,165 @@
+.. SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+
+=========================
+Ethernet Backplane Driver
+=========================
+
+Author:
+Florinel Iordache <florinel.iordache@nxp.com>
+
+Contents
+========
+
+	- Ethernet Backplane Overview
+	- Equalization
+	- Auto-negotiation
+	- Link training
+	- Enable backplane support in Linux kernel
+	- Ethernet Backplane support architecture
+	- Supported equalization algorithms
+	- Supported backplane protocols
+	- Supported platforms
+
+Ethernet Backplane Overview
+===========================
+
+Ethernet operation over electrical backplanes, also referred to as Ethernet
+Backplane, combines the IEEE 802.3 Media Access Control (MAC) and MAC
+Control sublayers with a family of Physical Layers defined to support
+operation over a modular chassis backplane.
+The main standard specification for Ethernet Backplane is: IEEE802.3ap-2007
+Amendment 4: Ethernet Operation over Electrical Backplanes
+which includes the new Clause 69 through Clause 74.
+Additional specifications define support for various speeds and 4-lanes:
+IEEE802.3ba-2010.
+Signal equalization is required based on the link quality. The standard
+specifies that a start-up algorithm should be in place in order to get the
+link up.
+
+Equalization
+============
+
+Equalization represents the procedure required to minimize the effects of signal
+distortion, noise, interference occurred in high-speed communication channels.
+The equalizer purpose is to improve signal integrity in terms of bit error rate
+(BER) in order to allow accurate recovery of the transmitted symbols.
+
+A simplified view of channel equalization:
+
+            LD       <======== channel =========>      LP
+       Local Device                                Link Partner
+
+         |-----|                                         ___
+         |     |     <======== channel =========>       /   |
+         |     |      witout signal Equalization       /     \
+         |     |                                      /      |
+     ____|     |____                              ___/        \___
+
+         |\   _                                        |-----|
+         | \_/ |     <======== channel =========>      |     |
+         |     |       with signal Equalization        |     |
+         |     |                                       |     |
+     ____|     |____                               ____|     |____
+
+      LD Tx waveform                                LP Rx waveform
+
+Auto-negotiation
+================
+
+Auto-negotiation allows the devices at both ends of a link segment to advertise
+abilities, acknowledge receipt, and discover the common modes of operation that
+both devices share. It also rejects the use of operational modes not shared by
+both devices. Auto-negotiation does not test link segment characteristics.
+
+Link training
+=============
+
+Link training occurs after auto-negotiation has determined the link to be a
+Base-KR, but before auto-negotiation is done. It continuously exchanges messages
+(training frames) between the local and the remote device as part of the
+start-up phase. Link training tunes the equalization parameters of the remote and
+local transmitter to improve the link quality in terms of bit error rate.
+Both LP (link partner/remote device) and LD (local device) perform link training
+in parallel. Link training is finished when both sides decide that the channel is
+equalized and then the link is considered up.
+
+Enable backplane support in Linux kernel
+========================================
+
+To enable the Ethernet Backplane, the following Kconfig options are available:
+
+# enable generic Ethernet Backplane support:
+CONFIG_ETH_BACKPLANE=y
+# enable Fixed (No Equalization) algorithm:
+CONFIG_ETH_BACKPLANE_FIXED=y
+# enable 3-Taps Bit Edge Equalization (BEE) algorithm:
+CONFIG_ETH_BACKPLANE_BEE=y
+# enable QorIQ Ethernet Backplane driver:
+CONFIG_ETH_BACKPLANE_QORIQ=y
+
+Ethernet Backplane support architecture
+=======================================
+
+Ethernet Backplane support in Linux kernel complies with the following standard
+design concepts:
+* Modularity:
+    # internal components are separated in well defined functional modules
+* Reusability:
+    # lower layer components provide basic functionalities which are reused by
+    the upper layer modules
+* Extensibility:
+    # architecture can be easily extended with support for new:
+    	- backplane protocols
+    	- equalization algorithms
+    	- supported devices
+It is designed as a loosely coupled architecture in order to allow the
+possibility to easily create desired backplane system configurations according
+to user needs by specifying different components and initialization parameters
+without recompiling the kernel.
+
+       ------------------            ------------------------------------
+       |  EQ Algorithms |            |    Specific device drivers       |
+       |  ------------  |            |       Backplane support          |
+       |  |  Fixed   |  |            | ------------------   ----------- |
+       |  ------------  |            | |     QorIQ      |   |         | |
+       |  |   BEE    |  |            | |    devices     |   |         | |
+       |  ------------  |            | | -------------- |   |  other  | |
+       |  |  others  |  |            | | | Serdes 10G | |   | devices | |
+  ----------------------------       | | -------------- |   | support | |
+  |      Link Training       |       | | | Serdes 28G | |   |         | |
+  |   and Auto-negotiation   |       | | -------------- |   |         | |
+  |    (IEEE 802.3-ap/ba)    |       | |----------------|   |---------| |
+  ---------------------------------------------------------------------------
+  |                   Ethernet Backplane Generic Driver                     |
+  ---------------------------------------------------------------------------
+  |                         PHY Abstraction Layer                           |
+  ---------------------------------------------------------------------------
+
+Supported equalization algorithms
+=================================
+
+Ethernet Backplane supports the following equalization algorithms:
+
+- Fixed setup (No Equalization algorithm)
+- 3-Taps Bit Edge Equalization (BEE) algorithm
+
+Supported backplane protocols
+=============================
+
+Ethernet Backplane supports the following protocols:
+
+- Single-lane:
+10GBase-KR
+
+- Multi-lane:
+40GBase-KR4
+
+Supported platforms
+===================
+
+Ethernet Backplane is enabled on the following platforms:
+
+LS1046A
+LS1088A
+LS2088A
+LX2160A
\ No newline at end of file
-- 
1.9.1


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

* [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 1/9] doc: net: add backplane documentation Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-27  1:04   ` Andrew Lunn
  2020-03-30 15:39   ` Rob Herring
  2020-03-26 13:51 ` [PATCH net-next 3/9] net: phy: add kr phy connection type Florinel Iordache
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add ethernet backplane device tree bindings

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 .../bindings/net/ethernet-controller.yaml          |  3 +-
 .../devicetree/bindings/net/ethernet-phy.yaml      | 53 +++++++++++++
 Documentation/devicetree/bindings/net/serdes.yaml  | 90 ++++++++++++++++++++++
 3 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/net/serdes.yaml

diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index ac471b6..541cee5 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -93,8 +93,9 @@ properties:
       - rxaui
       - xaui
 
-      # 10GBASE-KR, XFI, SFI
+      # 10GBASE-KR, 40GBASE-KR4, XFI, SFI
       - 10gbase-kr
+      - 40gbase-kr4
       - usxgmii
 
   phy-mode:
diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml
index 8927941..2fb377e 100644
--- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml
@@ -158,6 +158,42 @@ properties:
     description:
       Specifies a reference to a node representing a SFP cage.
 
+  eq-algorithm:
+    oneOf:
+      - const: fixed
+        description:
+          Backplane KR using fixed coefficients meaning no
+          equalization algorithm
+      - const: bee
+        description:
+          Backplane KR using 3-Taps Bit Edge Equalization (BEE)
+          algorithm
+        description:
+          Specifies the desired equalization algorithm to be used
+          by the KR link training
+
+  eq-init:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 3
+    maxItems: 3
+        description:
+          Triplet of KR coefficients. Specifies the initialization
+	  values for standard KR equalization coefficients used by
+	  the link training: pre-cursor, post-cursor, main-cursor
+
+  eq-params:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+        description:
+          Variable size array of KR parameters. Specifies the HW
+	  specific parameters used by the link training
+
+  lane-handle:
+    $ref: /schemas/types.yaml#definitions/phandle
+    description:
+      Specifies a reference (or array of references) to a node
+      representing the desired SERDES lane (or lanes) used in
+      backplane mode
+
 required:
   - reg
 
@@ -180,3 +216,20 @@ examples:
             reset-deassert-us = <2000>;
         };
     };
+  - |
+    /* Backplane configurations for specific setup */
+    &mdio9 {
+        bpphy6: ethernet-phy@0 {
+            compatible = "ethernet-phy-ieee802.3-c45";
+            reg = <0x0>;
+            lane-handle = <&lane_d>; /* use lane D */
+            eq-algorithm = "bee";
+            /* 10G Short cables setup: up to 30 cm cable */
+            eq-init = <0x2 0x5 0x29>;
+            eq-params = <0>;
+        };
+    };
+    &mac9 {
+        phy-connection-type = "10gbase-kr";
+        phy-handle = <&bpphy6>;
+    };
diff --git a/Documentation/devicetree/bindings/net/serdes.yaml b/Documentation/devicetree/bindings/net/serdes.yaml
new file mode 100644
index 0000000..965152f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/serdes.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/serdes.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Serdes Module Binding
+
+maintainers:
+  - Florinel Iordache <florinel.iordache@nxp.com>
+
+properties:
+  $nodename:
+    pattern: "^serdes(@[a-f0-9]+)?$"
+
+  compatible:
+    oneOf:
+      - const: serdes-10g
+        description: SerDes module type of 10G
+      - const: serdes-28g
+        description: SerDes module type of 28G
+
+  reg:
+    description:
+      Registers memory map offset and size for this serdes module
+
+  reg-names:
+    description:
+      Names of the register map given in "reg" node.
+      Should be one of the following according to serdes type:
+      "serdes", "serdes-10g", "serdes-28g"
+
+  little-endian:
+    description:
+      Specifies the endianness of serdes module
+      For complete definition see:
+      Documentation/devicetree/bindings/common-properties.txt
+
+  #address-cells:
+    description: Must be <1>
+
+  #size-cells:
+    description: Must be <1>
+
+  ranges:
+    description:
+      Address range of serdes module.
+
+properties:
+  $nodename:
+    pattern: "^lane(@[a-f0-9]+)?$"
+
+  compatible:
+    oneOf:
+      - const: lane-10g
+        description: Lane part of a 10G SerDes module
+      - const: lane-28g
+        description: Lane part of a 28G SerDes module
+
+  reg:
+    description:
+      Registers memory map offset and size for this lane
+
+  reg-names:
+    description:
+      Names of the register map given in "reg" node.
+      Should be one of the following: "lane", "serdes-lane"
+
+examples:
+  - |
+    serdes1: serdes@1ea0000 {
+        compatible = "serdes-10g";
+        reg = <0x0 0x1ea0000 0 0x00002000>;
+        reg-names = "serdes", "serdes-10g";
+        little-endian;
+
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x00 0x1ea0000 0x00002000>;
+        lane_a: lane@800 {
+            compatible = "lane-10g";
+            reg = <0x800 0x40>;
+            reg-names = "lane", "serdes-lane";
+        };
+        lane_b: lane@840 {
+            compatible = "lane-10g";
+            reg = <0x840 0x40>;
+            reg-names = "lane", "serdes-lane";
+        };
+    };
-- 
1.9.1


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

* [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 1/9] doc: net: add backplane documentation Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-27  0:15   ` Andrew Lunn
  2020-03-27  0:32   ` Florian Fainelli
  2020-03-26 13:51 ` [PATCH net-next 4/9] net: fman: add kr support for dpaa1 mac Florinel Iordache
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add support for backplane kr phy connection types currently available
(10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
the cases for KR modes which are clause 45 compatible to correctly assign
phy_interface and phylink#supported)

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/phy/phylink.c | 15 ++++++++++++---
 include/linux/phy.h       |  6 +++++-
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index fed0c59..db1bb87 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -4,6 +4,7 @@
  * technologies such as SFP cages where the PHY is hot-pluggable.
  *
  * Copyright (C) 2015 Russell King
+ * Copyright 2020 NXP
  */
 #include <linux/ethtool.h>
 #include <linux/export.h>
@@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
 			break;
 
 		case PHY_INTERFACE_MODE_USXGMII:
-		case PHY_INTERFACE_MODE_10GKR:
 		case PHY_INTERFACE_MODE_10GBASER:
 			phylink_set(pl->supported, 10baseT_Half);
 			phylink_set(pl->supported, 10baseT_Full);
@@ -317,7 +317,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
 			phylink_set(pl->supported, 2500baseX_Full);
 			phylink_set(pl->supported, 5000baseT_Full);
 			phylink_set(pl->supported, 10000baseT_Full);
-			phylink_set(pl->supported, 10000baseKR_Full);
 			phylink_set(pl->supported, 10000baseKX4_Full);
 			phylink_set(pl->supported, 10000baseCR_Full);
 			phylink_set(pl->supported, 10000baseSR_Full);
@@ -326,6 +325,14 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
 			phylink_set(pl->supported, 10000baseER_Full);
 			break;
 
+		case PHY_INTERFACE_MODE_10GKR:
+			phylink_set(pl->supported, 10000baseKR_Full);
+			break;
+
+		case PHY_INTERFACE_MODE_40GKR4:
+			phylink_set(pl->supported, 40000baseKR4_Full);
+			break;
+
 		case PHY_INTERFACE_MODE_XLGMII:
 			phylink_set(pl->supported, 25000baseCR_Full);
 			phylink_set(pl->supported, 25000baseKR_Full);
@@ -823,7 +830,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
 	if (phy->is_c45 &&
 	    interface != PHY_INTERFACE_MODE_RXAUI &&
 	    interface != PHY_INTERFACE_MODE_XAUI &&
-	    interface != PHY_INTERFACE_MODE_USXGMII)
+	    interface != PHY_INTERFACE_MODE_USXGMII &&
+	    interface != PHY_INTERFACE_MODE_10GKR &&
+	    interface != PHY_INTERFACE_MODE_40GKR4)
 		config.interface = PHY_INTERFACE_MODE_NA;
 	else
 		config.interface = interface;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 2432ca4..d7cca4b 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -6,6 +6,7 @@
  * Author: Andy Fleming
  *
  * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
  */
 
 #ifndef __PHY_H
@@ -107,8 +108,9 @@
 	/* 10GBASE-R, XFI, SFI - single lane 10G Serdes */
 	PHY_INTERFACE_MODE_10GBASER,
 	PHY_INTERFACE_MODE_USXGMII,
-	/* 10GBASE-KR - with Clause 73 AN */
+	/* Backplane KR */
 	PHY_INTERFACE_MODE_10GKR,
+	PHY_INTERFACE_MODE_40GKR4,
 	PHY_INTERFACE_MODE_MAX,
 } phy_interface_t;
 
@@ -190,6 +192,8 @@ static inline const char *phy_modes(phy_interface_t interface)
 		return "usxgmii";
 	case PHY_INTERFACE_MODE_10GKR:
 		return "10gbase-kr";
+	case PHY_INTERFACE_MODE_40GKR4:
+		return "40gbase-kr4";
 	default:
 		return "unknown";
 	}
-- 
1.9.1


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

* [PATCH net-next 4/9] net: fman: add kr support for dpaa1 mac
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (2 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 3/9] net: phy: add kr phy connection type Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 5/9] net: dpaa2: add kr support for dpaa2 mac Florinel Iordache
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add kr support in mac driver for dpaa1

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/ethernet/freescale/fman/mac.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
index 43427c5..90fe594 100644
--- a/drivers/net/ethernet/freescale/fman/mac.c
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -1,4 +1,5 @@
 /* Copyright 2008-2015 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -220,6 +221,7 @@ static int memac_initialization(struct mac_device *mac_dev)
 
 	set_fman_mac_params(mac_dev, &params);
 
+	/* use XGMII mode for all 10G interfaces to setup memac */
 	if (priv->max_speed == SPEED_10000)
 		params.phy_if = PHY_INTERFACE_MODE_XGMII;
 
@@ -540,7 +542,8 @@ static void setup_memac(struct mac_device *mac_dev)
 	[PHY_INTERFACE_MODE_RGMII_TXID]	= SPEED_1000,
 	[PHY_INTERFACE_MODE_RTBI]		= SPEED_1000,
 	[PHY_INTERFACE_MODE_QSGMII]		= SPEED_1000,
-	[PHY_INTERFACE_MODE_XGMII]		= SPEED_10000
+	[PHY_INTERFACE_MODE_XGMII]		= SPEED_10000,
+	[PHY_INTERFACE_MODE_10GKR]		= SPEED_10000
 };
 
 static struct platform_device *dpaa_eth_add_device(int fman_id,
@@ -795,9 +798,12 @@ static int mac_probe(struct platform_device *_of_dev)
 	if (priv->max_speed == 1000)
 		mac_dev->if_support |= SUPPORTED_1000baseT_Full;
 
-	/* The 10G interface only supports one mode */
+	/* Supported 10G interfaces */
 	if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
 		mac_dev->if_support = SUPPORTED_10000baseT_Full;
+	/* Supported KR interfaces */
+	if (mac_dev->phy_if == PHY_INTERFACE_MODE_10GKR)
+		mac_dev->if_support = SUPPORTED_10000baseKR_Full;
 
 	/* Get the rest of the PHY information */
 	mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0);
-- 
1.9.1


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

* [PATCH net-next 5/9] net: dpaa2: add kr support for dpaa2 mac
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (3 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 4/9] net: fman: add kr support for dpaa1 mac Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add kr support in mac driver for dpaa2

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
index 3ee236c..f3c384c 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
-/* Copyright 2019 NXP */
+/* Copyright 2019, 2020 NXP */
 
 #include "dpaa2-eth.h"
 #include "dpaa2-mac.h"
@@ -71,6 +71,8 @@ static bool dpaa2_mac_phy_mode_mismatch(struct dpaa2_mac *mac,
 	case PHY_INTERFACE_MODE_RGMII_ID:
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 	case PHY_INTERFACE_MODE_RGMII_TXID:
+	case PHY_INTERFACE_MODE_10GKR:
+	case PHY_INTERFACE_MODE_40GKR4:
 		return (interface != mac->if_mode);
 	default:
 		return true;
@@ -103,6 +105,12 @@ static void dpaa2_mac_validate(struct phylink_config *config,
 		phylink_set(mask, 100baseT_Full);
 		phylink_set(mask, 1000baseT_Full);
 		break;
+	case PHY_INTERFACE_MODE_10GKR:
+		phylink_set(mask, 10000baseKR_Full);
+		break;
+	case PHY_INTERFACE_MODE_40GKR4:
+		phylink_set(mask, 40000baseKR4_Full);
+		break;
 	default:
 		goto empty_set;
 	}
-- 
1.9.1


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

* [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (4 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 5/9] net: dpaa2: add kr support for dpaa2 mac Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 18:53   ` David Miller
                     ` (5 more replies)
  2020-03-26 13:51 ` [PATCH net-next 7/9] net: phy: enable qoriq backplane support Florinel Iordache
                   ` (2 subsequent siblings)
  8 siblings, 6 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add support for backplane kr generic driver including link training
(ieee802.3ap/ba) and fixed equalization algorithm

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/phy/Kconfig                   |    2 +
 drivers/net/phy/Makefile                  |    1 +
 drivers/net/phy/backplane/Kconfig         |   20 +
 drivers/net/phy/backplane/Makefile        |    9 +
 drivers/net/phy/backplane/backplane.c     | 1538 +++++++++++++++++++++++++++
 drivers/net/phy/backplane/backplane.h     |  262 +++++
 drivers/net/phy/backplane/eq_fixed.c      |   83 ++
 drivers/net/phy/backplane/equalization.h  |  282 +++++
 drivers/net/phy/backplane/link_training.c | 1604 +++++++++++++++++++++++++++++
 drivers/net/phy/backplane/link_training.h |   34 +
 10 files changed, 3835 insertions(+)
 create mode 100644 drivers/net/phy/backplane/Kconfig
 create mode 100644 drivers/net/phy/backplane/Makefile
 create mode 100644 drivers/net/phy/backplane/backplane.c
 create mode 100644 drivers/net/phy/backplane/backplane.h
 create mode 100644 drivers/net/phy/backplane/eq_fixed.c
 create mode 100644 drivers/net/phy/backplane/equalization.h
 create mode 100644 drivers/net/phy/backplane/link_training.c
 create mode 100644 drivers/net/phy/backplane/link_training.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index cc7f1df..abab4e5 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -523,6 +523,8 @@ config XILINX_GMII2RGMII
 	  the Reduced Gigabit Media Independent Interface(RGMII) between
 	  Ethernet physical media devices and the Gigabit Ethernet controller.
 
+source "drivers/net/phy/backplane/Kconfig"
+
 endif # PHYLIB
 
 config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 70774ab..0b867fb 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -101,3 +101,4 @@ obj-$(CONFIG_STE10XP)		+= ste10Xp.o
 obj-$(CONFIG_TERANETICS_PHY)	+= teranetics.o
 obj-$(CONFIG_VITESSE_PHY)	+= vitesse.o
 obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+obj-$(CONFIG_ETH_BACKPLANE)	+= backplane/
diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig
new file mode 100644
index 0000000..9ec54b5
--- /dev/null
+++ b/drivers/net/phy/backplane/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+config ETH_BACKPLANE
+	tristate "Ethernet Backplane support"
+	depends on OF_MDIO
+	help
+	  This module provides driver support for Ethernet Operation over
+	  Electrical Backplanes. It includes Backplane generic
+	  driver including support for Link Training (IEEE802.3ap/ba).
+	  Based on the link quality, a signal equalization is required.
+	  The standard specifies that a start-up algorithm should be in place
+	  in order to get the link up.
+
+config ETH_BACKPLANE_FIXED
+	tristate "Fixed: No Equalization algorithm"
+	depends on ETH_BACKPLANE
+	help
+	  This module provides a driver to setup fixed user configurable
+	  coefficient values for backplanes equalization. This means
+	  No Equalization algorithm is used to adapt the initial coefficients
+	  initially set by the user.
\ No newline at end of file
diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile
new file mode 100644
index 0000000..ded6f2d
--- /dev/null
+++ b/drivers/net/phy/backplane/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+#
+# Makefile for Ethernet Backplane driver
+#
+
+obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o
+obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o
+
+eth_backplane-objs	:= backplane.o link_training.o
diff --git a/drivers/net/phy/backplane/backplane.c b/drivers/net/phy/backplane/backplane.c
new file mode 100644
index 0000000..1b580bc
--- /dev/null
+++ b/drivers/net/phy/backplane/backplane.c
@@ -0,0 +1,1538 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Backplane driver
+ *
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ * Copyright 2018-2020 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/mdio.h>
+#include <linux/ethtool.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+
+#include "backplane.h"
+#include "link_training.h"
+
+/* KR timeouts in milliseconds */
+#define KR_TIMEOUT_1				100
+#define KR_TIMEOUT_2				1000
+#define KR_DENY_RT_INTERVAL			3000
+#define KR_LT_TIMEOUT				500
+
+/* KR timings in interations */
+#define KR_AN_WAIT_ITERATIONS			5
+#define KR_TRAIN_STEP_ITERATIONS		2
+#define CDR_LOCK_RETRY_COUNT			3
+
+/* AN status register (Clause 45) (MMD 7): MDIO_STAT1 */
+#define AN_LINK_UP_MASK				0x04
+
+/* Logging buffer size */
+#define LOG_BUFFER_SIZE				200
+
+/* Backplane custom logging */
+#define BPDEV_LOG(name) \
+	char log_buffer[LOG_BUFFER_SIZE]; \
+	va_list args; va_start(args, msg); \
+	vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \
+	if (!bpphy->attached_dev) \
+		dev_##name(&bpphy->mdio.dev, log_buffer); \
+	else \
+		dev_##name(&bpphy->mdio.dev, "%s: %s", \
+			netdev_name(bpphy->attached_dev), log_buffer); \
+	va_end(args)
+
+/* Backplane features */
+__ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init;
+EXPORT_SYMBOL(backplane_features);
+
+const int backplane_common_features_array[] = {
+	ETHTOOL_LINK_MODE_Backplane_BIT,
+	ETHTOOL_LINK_MODE_Autoneg_BIT,
+	ETHTOOL_LINK_MODE_MII_BIT,
+};
+
+const int backplane_protocol_features_array[] = {
+	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+	ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+};
+
+/* map string key to pointer data */
+struct spmap_node {
+	struct list_head entry;
+	const char *key;
+	void *pdata;
+};
+
+/* registered equalization algorithms info */
+static LIST_HEAD(eqalg_list);
+
+/* lanes attached to an equalization algorithm */
+static LIST_HEAD(lnalg_list);
+
+/* Backplane mutex between all KR PHY threads */
+static struct mutex backplane_lock;
+
+static int get_backplane_speed(phy_interface_t bp_mode)
+{
+	switch (bp_mode) {
+	case PHY_INTERFACE_MODE_10GKR:
+		return SPEED_10000;
+	case PHY_INTERFACE_MODE_40GKR4:
+		return SPEED_40000;
+	default:
+		pr_err("%s: Unsupported backplane phy interface\n",
+		       BACKPLANE_DRIVER_NAME);
+		return SPEED_UNKNOWN;
+	}
+	return SPEED_UNKNOWN;
+}
+
+static enum ethtool_link_mode_bit_indices
+	get_backplane_supported_mode(phy_interface_t bp_mode)
+{
+	switch (bp_mode) {
+	case PHY_INTERFACE_MODE_10GKR:
+		return ETHTOOL_LINK_MODE_10000baseKR_Full_BIT;
+	case PHY_INTERFACE_MODE_40GKR4:
+		return ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT;
+	default:
+		pr_err("%s: Unsupported backplane phy interface\n",
+		       BACKPLANE_DRIVER_NAME);
+		return ETHTOOL_LINK_MODE_Backplane_BIT;
+	}
+	return ETHTOOL_LINK_MODE_Backplane_BIT;
+}
+
+static int spmap_add(struct list_head *list, const char *key, void *pdata)
+{
+	struct spmap_node *node;
+
+	/* create a new entry with desired key */
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	node->key = key;
+	node->pdata = pdata;
+
+	list_add(&node->entry, list);
+
+	return 0;
+}
+
+static const struct equalization_algorithm *eq_find(const char *key)
+{
+	struct spmap_node *eqalg, *eqalg_tmp;
+
+	if (!key)
+		return NULL;
+
+	/* search desired single key */
+	list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) {
+		if (strcmp(eqalg->key, key) == 0)
+			return (struct equalization_algorithm *)eqalg->pdata;
+	}
+	return NULL;
+}
+
+static void backplane_features_init(void)
+{
+	linkmode_set_bit_array(backplane_common_features_array,
+			       ARRAY_SIZE(backplane_common_features_array),
+			       backplane_features);
+
+	linkmode_set_bit_array(backplane_protocol_features_array,
+			       ARRAY_SIZE(backplane_protocol_features_array),
+			       backplane_features);
+}
+
+static u32 le_ioread32(void __iomem *reg)
+{
+	return ioread32(reg);
+}
+
+static void le_iowrite32(u32 value, void __iomem *reg)
+{
+	iowrite32(value, reg);
+}
+
+static u32 be_ioread32(void __iomem *reg)
+{
+	return ioread32be(reg);
+}
+
+static void be_iowrite32(u32 value, void __iomem *reg)
+{
+	iowrite32be(value, reg);
+}
+
+static void training_status_init(struct training_status *trst)
+{
+	trst->done_training = false;
+	trst->remote_tx_complete = false;
+	trst->remote_tx_running = false;
+	trst->sent_init = false;
+	trst->lp_rx_ready = 0;
+	trst->local_tx_running = false;
+}
+
+static void init_krln(struct kr_lane_info *krln, bool revert_default)
+{
+	if (revert_default)
+		backplane_default_kr_lane(krln);
+
+	training_status_init(&krln->trst);
+	krln->state = DETECTING_LP;
+	krln->an_acquired = false;
+
+	krln->ld_update = 0;
+	krln->prev_ld_update = 0;
+	krln->ld_last_nonhold_update = 0;
+	krln->lp_status = 0;
+	krln->lp_last_change_status = 0;
+	krln->last_lp_update_status[C_M1] = 0;
+	krln->last_lp_update_status[C_Z0] = 0;
+	krln->last_lp_update_status[C_P1] = 0;
+	krln->ld_status = 0;
+	krln->move_back_prev = false;
+	krln->move_back_cnt = 0;
+	krln->move_back_lp_status = 0;
+
+	lt_init_ld(krln);
+}
+
+static void setup_supported_linkmode(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	/* Clear all supported backplane protocols features
+	 * and setup only the currently configured protocol
+	 */
+	for (i = 0; i < ARRAY_SIZE(backplane_protocol_features_array); i++)
+		linkmode_clear_bit(backplane_protocol_features_array[i],
+				   bpphy->supported);
+
+	linkmode_set_bit(get_backplane_supported_mode(bp_phy->bp_mode),
+			 bpphy->supported);
+}
+
+/* Read AN Link Status */
+static int is_an_link_up(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int ret, val = 0;
+
+	mutex_lock(&bp_phy->bpphy_lock);
+
+	/* Read twice because Link_Status is LL (Latched Low) bit */
+	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);
+	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);
+
+	mutex_unlock(&bp_phy->bpphy_lock);
+
+	ret = (val & AN_LINK_UP_MASK) ? 1 : 0;
+
+	return ret;
+}
+
+static void start_kr_state_machine(struct kr_lane_info *krln, u32 timeout)
+{
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check if link training is used */
+	if (!krln->eq_alg->use_local_tx_training &&
+	    !krln->eq_alg->use_remote_tx_training)
+		return;
+
+	queue_delayed_work(system_power_efficient_wq, &krln->kr_wk,
+			   msecs_to_jiffies(timeout));
+}
+
+static void stop_kr_state_machine(struct kr_lane_info *krln)
+{
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check if link training is used */
+	if (!krln->eq_alg->use_local_tx_training &&
+	    !krln->eq_alg->use_remote_tx_training)
+		return;
+
+	cancel_delayed_work_sync(&krln->kr_wk);
+}
+
+static void setup_default_settings(struct kr_lane_info *krln)
+{
+	struct lane_kr_params krparam;
+
+	krln->bp_phy->bp_dev.lane_ops->read_lane_kr(krln->reg_base, &krparam);
+
+	if (krln->bp_phy->bp_dev.coef_def_dt) {
+		krln->def_ratio_preq = krln->bp_phy->bp_dev.cm_def;
+		krln->def_ratio_pstq = krln->bp_phy->bp_dev.cp_def;
+		krln->def_adpt_eq = krln->bp_phy->bp_dev.cz_def;
+	} else {
+		krln->def_ratio_preq = krparam.ratio_preq;
+		krln->def_ratio_pstq = krparam.ratio_pstq;
+		krln->def_adpt_eq = krparam.adpt_eq;
+	}
+
+	if (krln->bp_phy->bp_dev.ampr_def_dt)
+		krln->def_amp_red = krln->bp_phy->bp_dev.amp_red_def;
+	else
+		krln->def_amp_red = krparam.amp_red;
+}
+
+static void kr_reset_master_lane(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;
+
+	if (backplane_is_multi_lane(bp_phy)) {
+		/* Reset only the Master Lane */
+		if (krln->idx == MASTER_LANE)
+			lane_ops->reset_lane(krln->reg_base, LANE_RX_TX);
+	} else {
+		lane_ops->reset_lane(krln->reg_base, LANE_RX_TX);
+	}
+}
+
+static void print_single_lane_trained(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	bpdev_info(bpphy,
+		   "%s link trained, Tx equalization: preq = 0x%x, pstq = 0x%x, adpt_eq = 0x%x\n",
+		   phy_modes(bp_phy->bp_mode),
+		   krln->tuned_ratio_preq, krln->tuned_ratio_pstq,
+		   krln->tuned_adpt_eq);
+}
+
+static void print_multi_lane_trained(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	bpdev_info(bpphy,
+		   "%s link trained, Tx equalization:\n",
+		   phy_modes(bp_phy->bp_mode));
+
+	for (i = 0; i < bp_phy->num_lanes; i++)
+		bpdev_info(bpphy,
+			   "\t|- Lane %d: preq = 0x%x, pstq = 0x%x, adpt_eq = 0x%x\n",
+			   i + 1, bp_phy->krln[i].tuned_ratio_preq,
+			   bp_phy->krln[i].tuned_ratio_pstq,
+			   bp_phy->krln[i].tuned_adpt_eq);
+}
+
+static void kr_link_trained(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	mutex_lock(&bp_phy->trained_lock);
+	/* Setup lane state as TRAINED inside the phy trained lock
+	 * to avoid duplicated message printed on multi-lane PHYs
+	 */
+	krln->state = TRAINED;
+
+	mutex_lock(&backplane_lock);
+
+	if (backplane_is_single_lane(bp_phy))
+		print_single_lane_trained(krln);
+	else
+		if (backplane_are_all_lanes_trained(bp_phy))
+			print_multi_lane_trained(krln);
+
+	mutex_unlock(&backplane_lock);
+	mutex_unlock(&bp_phy->trained_lock);
+}
+
+static void kr_train_step(struct kr_lane_info *krln)
+{
+	struct training_status *trst = &krln->trst;
+	u32 lt_timeout = KR_LT_TIMEOUT;
+	u64 dead_line;
+	int i = 0;
+
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check if link training is used */
+	if (!krln->eq_alg->use_local_tx_training &&
+	    !krln->eq_alg->use_remote_tx_training)
+		return;
+
+	lt_start(krln);
+
+	while (i < KR_TRAIN_STEP_ITERATIONS) {
+		dead_line = jiffies + msecs_to_jiffies(lt_timeout);
+		while (time_before(jiffies, (unsigned long)dead_line)) {
+			/* check if the LT is already failed */
+			if (lt_is_training_failure(krln)) {
+				/* LT failed already, reset lane to avoid
+				 * it run into hanging, then start LT again.
+				 */
+				kr_reset_master_lane(krln);
+				lt_start(krln);
+			} else if (lt_is_frame_lock(krln)) {
+				break;
+			}
+			/* wait frame lock (without training_failure) */
+			usleep_range(100, 500);
+		}
+
+		if (!lt_is_frame_lock(krln)) {
+			i++;
+			continue;
+		}
+
+		/* the LT should be finished in 500ms, failed or OK. */
+		dead_line = jiffies + msecs_to_jiffies(lt_timeout);
+		while (time_before(jiffies, (unsigned long)dead_line)) {
+			/* check if the LT is already failed */
+			if (lt_is_training_failure(krln)) {
+				kr_reset_master_lane(krln);
+				break;
+			}
+
+			if (krln->eq_alg->use_local_tx_training)
+				lt_train_local_tx(krln);
+
+			if (krln->eq_alg->use_remote_tx_training)
+				lt_train_remote_tx(krln);
+
+			if (krln->lt_error)
+				break;
+
+			if (trst->lp_rx_ready && trst->remote_tx_complete)
+				break;
+
+			usleep_range(100, 500);
+		}
+
+		i++;
+		/* check if LT Error occurred */
+		if (krln->lt_error) {
+			init_krln(krln, false);
+			continue;
+		} else {
+			break;
+		}
+	}
+
+	lt_stop(krln);
+
+	/* check if Link is successfully TRAINED */
+	if (lt_is_rx_trained(krln))
+		kr_link_trained(krln);
+	else
+		kr_reset_master_lane(krln);
+}
+
+static void an_request_restart(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;
+	int i;
+
+	if (time_before(jiffies, (unsigned long)krln->rt_time))
+		return;
+	if (!backplane_is_mode_kr(bp_phy->bp_mode))
+		return;
+
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		init_krln(&bp_phy->krln[i], true);
+		/* Reset the lane to recover from link down */
+		lane_ops->reset_lane(bp_phy->krln[i].reg_base, LANE_RX_TX);
+		lt_reset(&bp_phy->krln[i]);
+	}
+	/* Start AN only for Master Lane */
+	lt_start_an(&bp_phy->krln[MASTER_LANE]);
+
+	krln->rt_time = jiffies + msecs_to_jiffies(KR_DENY_RT_INTERVAL);
+}
+
+static bool detect_lp(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	u32 an_bp_eth_status = krln->bp_phy->bp_dev.mdio.an_bp_eth_status;
+	bool start_train = false;
+	int an_state;
+
+	/* Check AN state on Master Lane */
+	an_state = backplane_read_mmd(&bp_phy->krln[MASTER_LANE], MDIO_MMD_AN,
+				      an_bp_eth_status);
+
+	/* The link training occurs after auto-negotiation
+	 * has determined the link to be a Base-KR link.
+	 * This is indicated by asserting the corresponding
+	 * technology bit within the BP_ETH_STATUS register.
+	 * Note that this occurs before auto-negotiation can declare
+	 * auto-negotiation complete,
+	 * as this requires the PCS to report a valid link.
+	 */
+	if (an_state &
+	    bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit(bp_phy->bp_mode)) {
+		/* AN acquired:
+		 * Train all lanes in order starting with Master Lane
+		 */
+		krln->an_acquired = true;
+		krln->an_wait_count = 0;
+		start_train = true;
+	} else {
+		/* AN lost or not yet acquired */
+		if (krln->an_acquired) {
+			/* AN acquired first time but now was lost */
+			if (!backplane_is_link_up(bpphy)) {
+				/* Link is down: restart training */
+				krln->an_wait_count = 0;
+				an_request_restart(krln);
+			} else {
+				/* Link is up:
+				 * wait few iterations for AN to be acquired
+				 */
+				if (krln->an_wait_count >=
+							KR_AN_WAIT_ITERATIONS) {
+					krln->an_wait_count = 0;
+					an_request_restart(krln);
+				} else {
+					krln->an_wait_count++;
+				}
+			}
+		}
+		/* else: AN was not yet acquired first time
+		 * DO nothing, just wait AN to be acquired first time
+		 */
+	}
+
+	return start_train;
+}
+
+static void detect_hotplug(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	if (krln->idx == MASTER_LANE) {
+		/* check if all lanes are trained
+		 * only if current lane is  Master Lane
+		 */
+		if (backplane_are_all_lanes_trained(bp_phy)) {
+			bpdev_info(bpphy, "Detect hotplug, restart training\n");
+			for (i = 0; i < bp_phy->num_lanes; i++) {
+				/* initializations on Detect hotplug / restart:
+				 * they must not be part of init_krln
+				 */
+				bp_phy->krln[i].first_recv_init = false;
+			}
+			an_request_restart(krln);
+		}
+	}
+}
+
+static void bp_kr_state_machine(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct kr_lane_info *krln = container_of(dwork, struct kr_lane_info,
+						 kr_wk);
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	bool start_train = false;
+	u32 kr_timeout = KR_TIMEOUT_1;
+
+	if (!backplane_is_mode_kr(bp_phy->bp_mode))
+		return;
+
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check if link training is used */
+	if (!krln->eq_alg->use_local_tx_training &&
+	    !krln->eq_alg->use_remote_tx_training)
+		return;
+
+	if (!bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit) {
+		bpdev_err(bpphy,
+			  "Unknown AN_BP_ETHERNET_STATUS KR detection bit\n");
+		return;
+	}
+
+	mutex_lock(&krln->lane_lock);
+	switch (krln->state) {
+	case DETECTING_LP:
+		start_train = detect_lp(krln);
+		break;
+	case TRAINED:
+		kr_timeout = KR_TIMEOUT_2;
+		if (!backplane_is_link_up(bpphy)) {
+			kr_timeout = KR_TIMEOUT_1;
+			detect_hotplug(krln);
+		}
+		break;
+	}
+
+	if (start_train)
+		kr_train_step(krln);
+
+	mutex_unlock(&krln->lane_lock);
+	start_kr_state_machine(krln, kr_timeout);
+}
+
+static void init_kr_state_machine(struct kr_lane_info *krln)
+{
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check if link training is used */
+	if (!krln->eq_alg->use_local_tx_training &&
+	    !krln->eq_alg->use_remote_tx_training)
+		return;
+
+	INIT_DELAYED_WORK(&krln->kr_wk, bp_kr_state_machine);
+}
+
+/* backplane_write_mmd - Wrapper function for phy_write_mmd
+ * for writing a register on an MMD on a given PHY.
+ *
+ * Same rules as for phy_write_mmd();
+ */
+int backplane_write_mmd(struct kr_lane_info *krln, int devad, u32 regnum,
+			u16 val)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int mdio_addr = bpphy->mdio.addr;
+	int err;
+
+	mutex_lock(&bp_phy->bpphy_lock);
+
+	if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) {
+		/* Multilane AN: prepare mdio address
+		 * for writing bpphy AN registers on respective lane
+		 * AN MDIO address offset for multilane is equal
+		 * to number of lanes
+		 */
+		bpphy->mdio.addr = bp_phy->num_lanes + krln->idx;
+	}
+
+	err = phy_write_mmd(bpphy, devad, regnum, val);
+	if (err)
+		bpdev_err(bpphy,
+			  "Writing PHY (%p) MMD = 0x%02x register = 0x%02x failed with error code: 0x%08x\n",
+			  bpphy, devad, regnum, err);
+
+	if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) {
+		/* Multilane AN: restore mdio address */
+		bpphy->mdio.addr = mdio_addr;
+	}
+
+	mutex_unlock(&bp_phy->bpphy_lock);
+
+	return err;
+}
+
+/* backplane_read_mmd - Wrapper function for phy_read_mmd
+ * for reading a register from an MMD on a given PHY.
+ *
+ * Same rules as for phy_read_mmd();
+ */
+int backplane_read_mmd(struct kr_lane_info *krln, int devad, u32 regnum)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int mdio_addr = bpphy->mdio.addr;
+	int ret;
+
+	mutex_lock(&bp_phy->bpphy_lock);
+
+	if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) {
+		/* Multilane AN: prepare mdio address
+		 * for reading bpphy AN registers on respective lane
+		 * AN MDIO address offset for multilane is equal to
+		 * number of lanes
+		 */
+		bpphy->mdio.addr = bp_phy->num_lanes + krln->idx;
+	}
+
+	ret = phy_read_mmd(bpphy, devad, regnum);
+
+	if (devad == MDIO_MMD_AN && backplane_is_multi_lane(bp_phy)) {
+		/* Multilane AN: restore mdio address */
+		bpphy->mdio.addr = mdio_addr;
+	}
+
+	mutex_unlock(&bp_phy->bpphy_lock);
+
+	return ret;
+}
+
+/* backplane_get_current_taps
+ * convert coefficient taps from internal backplane driver to link training
+ */
+void backplane_get_current_taps(struct kr_lane_info *krln, u32 *coef)
+{
+	coef[C_M1] = krln->ratio_preq;
+	coef[C_Z0] = krln->adpt_eq;
+	coef[C_P1] = krln->ratio_pstq;
+}
+
+/* backplane_set_current_taps
+ * convert coefficient taps from link training to internal backplane driver
+ */
+void backplane_set_current_taps(struct kr_lane_info *krln, u32 *coef)
+{
+	krln->ratio_preq = coef[C_M1];
+	krln->adpt_eq = coef[C_Z0];
+	krln->ratio_pstq = coef[C_P1];
+}
+
+/* backplane_set_all_taps_to_max
+ * setup all coefficients to MAX values from IEEE802.3ap perspective
+ */
+void backplane_set_all_taps_to_max(struct kr_lane_info *krln)
+{
+	krln->ratio_pstq = krln->bp_phy->bp_dev.cp_max;
+	krln->adpt_eq = krln->bp_phy->bp_dev.cz_max;
+	krln->ratio_preq = krln->bp_phy->bp_dev.cm_max;
+}
+
+void backplane_tune_kr_lane(struct kr_lane_info *krln, bool reset_lane)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct lane_kr_params krparams;
+	bool reset = false;
+
+	if (backplane_is_multi_lane(bp_phy)) {
+		/* Reset only the Master Lane */
+		reset = (krln->idx == MASTER_LANE);
+	} else {
+		reset = true;
+	}
+
+	/* Do not reset the lane if this is how it was asked */
+	if (!reset_lane)
+		reset = false;
+
+	krparams.ratio_preq = krln->ratio_preq;
+	krparams.ratio_pstq = krln->ratio_pstq;
+	krparams.adpt_eq = krln->adpt_eq;
+	krparams.amp_red = krln->def_amp_red;
+	krln->bp_phy->bp_dev.lane_ops->tune_lane_kr(krln->reg_base, &krparams,
+						    reset);
+
+	krln->tuned_ratio_preq = krln->ratio_preq;
+	krln->tuned_ratio_pstq = krln->ratio_pstq;
+	krln->tuned_adpt_eq = krln->adpt_eq;
+}
+
+void backplane_default_kr_lane(struct kr_lane_info *krln)
+{
+	krln->ratio_preq = krln->def_ratio_preq;
+	krln->ratio_pstq = krln->def_ratio_pstq;
+	krln->adpt_eq = krln->def_adpt_eq;
+
+	backplane_tune_kr_lane(krln, true);
+}
+
+void bpdev_err(struct phy_device *bpphy, char *msg, ...)
+{
+	BPDEV_LOG(err);
+}
+EXPORT_SYMBOL(bpdev_err);
+
+void bpdev_warn(struct phy_device *bpphy, char *msg, ...)
+{
+	BPDEV_LOG(warn);
+}
+EXPORT_SYMBOL(bpdev_warn);
+
+void bpdev_info(struct phy_device *bpphy, char *msg, ...)
+{
+	BPDEV_LOG(info);
+}
+EXPORT_SYMBOL(bpdev_info);
+
+void bpdev_dbg(struct phy_device *bpphy, char *msg, ...)
+{
+	BPDEV_LOG(dbg);
+}
+EXPORT_SYMBOL(bpdev_dbg);
+
+/* backplane_eq_register
+ *
+ * Registers an equalization algorithm with the specified key
+ *
+ * key: desired key on which eq algorithm must be registered
+ * eq_info: eq algorithm information to be registered
+ *
+ * Returns: Zero for success or error code in case of failure
+ */
+int backplane_eq_register(const char *key,
+			  const struct equalization_algorithm *eq_info)
+{
+	struct spmap_node *eqalg, *eqalg_tmp;
+
+	/* check if desired key already exists */
+	list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) {
+		if (strcmp(eqalg->key, key) == 0) {
+			pr_err("%s: Equalization algorithm registration failed: key '%s' already exists\n",
+			       BACKPLANE_DRIVER_NAME, key);
+			return -EEXIST;
+		}
+	}
+
+	spmap_add(&eqalg_list, key, (void *)eq_info);
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_eq_register);
+
+/* backplane_eq_unregister
+ *
+ * Unregisters all equalization algorithm for the specified key
+ *
+ * key: desired key for which all registered eq algorithms must be removed
+ *
+ * Returns: None
+ */
+void backplane_eq_unregister(const char *key)
+{
+	struct spmap_node *node, *node_tmp;
+	struct kr_lane_info *krln;
+
+	if (!key)
+		return;
+
+	/* search all keys in lanes list */
+	list_for_each_entry_safe(node, node_tmp, &lnalg_list, entry) {
+		if (strcmp(node->key, key) == 0) {
+			krln = (struct kr_lane_info *)node->pdata;
+			if (krln->eq_alg->ops.destroy)
+				krln->eq_alg->ops.destroy(krln->eq_priv);
+			krln->eq_alg = NULL;
+			krln->eq_priv = NULL;
+			list_del_init(&node->entry);
+			kfree(node);
+		}
+	}
+
+	/* search single key in eq algorithms list */
+	list_for_each_entry_safe(node, node_tmp, &eqalg_list, entry) {
+		if (strcmp(node->key, key) == 0) {
+			list_del_init(&node->entry);
+			kfree(node);
+			break;
+		}
+	}
+}
+EXPORT_SYMBOL(backplane_eq_unregister);
+
+void backplane_setup_mdio_c45(struct backplane_dev_info *bp_dev)
+{
+	/* KR PMD registers */
+	lt_setup_c45(bp_dev);
+
+	bp_dev->mdio.pmd_ctrl_1 = MDIO_CTRL1;
+
+	/* KX/KR AN registers: IEEE802.3 Clause 45 (MMD 7) */
+	bp_dev->mdio.an_control = MDIO_CTRL1;
+	bp_dev->mdio.an_status = MDIO_STAT1;
+	bp_dev->mdio.an_ad_ability_0 = MDIO_PMA_EXTABLE_10GBKR;
+	bp_dev->mdio.an_ad_ability_1 = MDIO_PMA_EXTABLE_10GBKR + 1;
+	bp_dev->mdio.an_lp_base_page_ability_1 = MDIO_PMA_EXTABLE_10GBKR + 4;
+}
+EXPORT_SYMBOL(backplane_setup_mdio_c45);
+
+void backplane_setup_kr_lt_mmd(struct backplane_dev_info *bp_dev, int devad,
+			       u32 base)
+{
+	lt_setup_memmap(bp_dev, devad, base);
+}
+EXPORT_SYMBOL(backplane_setup_kr_lt_mmd);
+
+bool backplane_is_mode_kr(phy_interface_t bp_mode)
+{
+	return (bp_mode >= PHY_INTERFACE_MODE_10GKR &&
+		bp_mode <= PHY_INTERFACE_MODE_40GKR4);
+}
+EXPORT_SYMBOL(backplane_is_mode_kr);
+
+bool backplane_is_valid_mode(phy_interface_t bp_mode)
+{
+	return (bp_mode >= PHY_INTERFACE_MODE_10GKR &&
+		bp_mode <= PHY_INTERFACE_MODE_40GKR4);
+}
+EXPORT_SYMBOL(backplane_is_valid_mode);
+
+u8 backplane_num_lanes(phy_interface_t bp_mode)
+{
+	const char *bp_name;
+	char num_lanes;
+	int len;
+
+	if (!backplane_is_valid_mode(bp_mode))
+		return 0;
+
+	bp_name = phy_modes(bp_mode);
+	if (!bp_name)
+		return 0;
+	if (strcmp(bp_name, "unknown") == 0)
+		return 0;
+
+	len = strlen(bp_name);
+	if (len == 0)
+		return 0;
+	num_lanes = bp_name[len - 1];
+	if (num_lanes >= '0' && num_lanes <= '9')
+		return num_lanes - '0';
+
+	return 1;
+}
+EXPORT_SYMBOL(backplane_num_lanes);
+
+bool backplane_is_single_lane(struct backplane_phy_info *bp_phy)
+{
+	return (bp_phy->num_lanes == 1);
+}
+EXPORT_SYMBOL(backplane_is_single_lane);
+
+bool backplane_is_multi_lane(struct backplane_phy_info *bp_phy)
+{
+	return (bp_phy->num_lanes > 1);
+}
+EXPORT_SYMBOL(backplane_is_multi_lane);
+
+/* backplane_is_cdr_lock
+ *
+ * Checks clock and data recovery bit: CDR Lock
+ *
+ * krln: desired lane to be verified
+ * retry: boolean value that specifies if to retry the check
+ *
+ * Returns: true if CDR_Lock bit is asserted or false otherwise
+ */
+bool backplane_is_cdr_lock(struct kr_lane_info *krln, bool retry)
+{
+	int i;
+
+	if (krln->bp_phy->bp_dev.lane_ops->is_cdr_lock(krln->reg_base))
+		return true;
+
+	if (!retry)
+		return false;
+
+	/* Try RX_RESET: Allow for few retries */
+	for (i = 0; i < CDR_LOCK_RETRY_COUNT; i++) {
+		krln->bp_phy->bp_dev.lane_ops->reset_lane(krln->reg_base,
+							  LANE_RX);
+		usleep_range(10, 50);
+
+		if (krln->bp_phy->bp_dev.lane_ops->is_cdr_lock(krln->reg_base))
+			return true;
+	}
+	return false;
+}
+EXPORT_SYMBOL(backplane_is_cdr_lock);
+
+/* backplane_is_link_up
+ * Generic Link-up Status: use AN link-up
+ */
+int backplane_is_link_up(struct phy_device *bpphy)
+{
+	return is_an_link_up(bpphy);
+}
+EXPORT_SYMBOL(backplane_is_link_up);
+
+int backplane_get_lanes_trained_count(struct backplane_phy_info *bp_phy)
+{
+	int i, lanes_trained = 0;
+
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		if (bp_phy->krln[i].state == TRAINED)
+			lanes_trained++;
+	}
+	return lanes_trained;
+}
+EXPORT_SYMBOL(backplane_get_lanes_trained_count);
+
+int backplane_are_all_lanes_trained(struct backplane_phy_info *bp_phy)
+{
+	int i;
+
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		if (bp_phy->krln[i].state != TRAINED)
+			return 0;
+	}
+	return 1;
+}
+EXPORT_SYMBOL(backplane_are_all_lanes_trained);
+
+int backplane_create(struct phy_device *bpphy)
+{
+	struct device_node *bpphy_node;
+	struct backplane_phy_info *bp_phy;
+
+	bpphy_node = bpphy->mdio.dev.of_node;
+	if (!bpphy_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+
+	/* allocate memory for backplane info structure */
+	bp_phy = devm_kzalloc(&bpphy->mdio.dev, sizeof(*bp_phy), GFP_KERNEL);
+	if (!bp_phy)
+		return -ENOMEM;
+
+	bpphy->priv = bp_phy;
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_create);
+
+/* backplane_parse_dt
+ * parses the device tree and saves backplane relevant data
+ * in backplane phy info structure
+ */
+int backplane_parse_dt(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct device_node *bpphy_node;
+	const char *eqa;
+	u32 eqinit[4];
+	int proplen;
+	int ret;
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	bpphy_node = bpphy->mdio.dev.of_node;
+	if (!bpphy_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+
+	if (!backplane_is_valid_mode(bpphy->interface))
+		return -EINVAL;
+
+	bp_phy->bp_mode = bpphy->interface;
+	bp_phy->num_lanes = backplane_num_lanes(bpphy->interface);
+
+	ret = of_property_read_string(bpphy_node, "eq-algorithm", &eqa);
+	/* if eq-algorithm node is not found then use the default algorithm */
+	if (ret == 0)
+		bp_phy->bp_dev.eqa_name = eqa;
+	else
+		bp_phy->bp_dev.eqa_name = DEFAULT_EQ_ALGORITHM;
+
+	/* if eq-init node exists then use the DTS specified values
+	 * if eq-init node doesn't exist then use values already found in HW
+	 */
+	proplen = of_property_count_u32_elems(bpphy_node, "eq-init");
+	if (proplen > 0) {
+		/* There are 3 standard equalization coefficient taps */
+		if (proplen > C_NO)
+			proplen = C_NO;
+		ret = of_property_read_u32_array(bpphy_node, "eq-init",
+						 (u32 *)eqinit, proplen);
+		if (ret == 0) {
+			bp_phy->bp_dev.coef_def_dt = true;
+			bp_phy->bp_dev.cm_def = eqinit[0];
+			bp_phy->bp_dev.cp_def = eqinit[1];
+			bp_phy->bp_dev.cz_def = eqinit[2];
+		}
+	}
+
+	/* setup ioread/iowrite according to endianness */
+	if (bp_phy->bp_dev.is_little_endian) {
+		bp_phy->bp_dev.io.read32 = le_ioread32;
+		bp_phy->bp_dev.io.write32 = le_iowrite32;
+	} else {
+		bp_phy->bp_dev.io.read32 = be_ioread32;
+		bp_phy->bp_dev.io.write32 = be_iowrite32;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_parse_dt);
+
+/* backplane_setup_mdio
+ */
+int backplane_setup_mdio(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	/* By default setup MDIO Clause 45 */
+	backplane_setup_mdio_c45(&bp_phy->bp_dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_setup_mdio);
+
+/* backplane_setup_lanes
+ * Allocates lanes memory map and setup lanes relevant data
+ * Requires:
+ *	- backplane_dev_info#lane_ops
+ *		for lane access operations
+ *	- backplane_dev_info#equalizer
+ *		for specific Equalizer access
+ *	- backplane_dev_info#lane_io_ops#memmap_size
+ *		for lane memory map allocation
+ *	- backplane_dev_info#cx_def
+ *		for default coefficient setup
+ */
+int backplane_setup_lanes(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct kr_lane_info *krln;
+	struct eq_setup_info eq_setup;
+	int i;
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+	if (!bp_phy->bp_dev.lane_ops) {
+		bpdev_err(bpphy, "Backplane lane ops is not set\n");
+		return -EINVAL;
+	}
+	if (!bp_phy->bp_dev.equalizer) {
+		bpdev_err(bpphy, "Backplane equalizer info is not set\n");
+		return -EINVAL;
+	}
+	if (bp_phy->bp_dev.lane_ops->memmap_size == 0) {
+		bpdev_err(bpphy, "Lane memory map size is zero\n");
+		return -EINVAL;
+	}
+
+	if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+		if (bp_phy->bp_dev.cm_def == 0 && bp_phy->bp_dev.cz_def == 0 &&
+		    bp_phy->bp_dev.cp_def == 0)
+			bpdev_warn(bpphy,
+				   "All default values for KR parameters are zero\n");
+	}
+
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		krln = &bp_phy->krln[i];
+
+		/* setup lane memory map size */
+		krln->memmap_size = bp_phy->bp_dev.lane_ops->memmap_size;
+
+		krln->reg_base = devm_ioremap(&bpphy->mdio.dev,
+					      krln->lane_addr,
+					      krln->memmap_size);
+		if (!krln->reg_base) {
+			bpdev_err(bpphy, "Lane memory map allocation failed\n");
+			return -ENOMEM;
+		}
+
+		krln->idx = i;
+		krln->bpphy = bpphy;
+		krln->bp_phy = bp_phy;
+		krln->rt_time = jiffies + msecs_to_jiffies(KR_DENY_RT_INTERVAL);
+
+		if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+			setup_default_settings(krln);
+
+			/* Find EQ Algorithm info */
+			krln->eq_alg = eq_find(bp_phy->bp_dev.eqa_name);
+			if (!krln->eq_alg) {
+				/* key for desired algorithm was not found */
+				bpdev_err(bpphy,
+					  "Equalization algorithm '%s' is not registered\n",
+					  bp_phy->bp_dev.eqa_name);
+				return -EINVAL;
+			}
+			if (!krln->eq_alg->ops.create) {
+				bpdev_err(bpphy,
+					  "Equalization algorithm creation failed: create operation is not available\n");
+				return -EINVAL;
+			}
+
+			/* Setup EQ Algorithm */
+			eq_setup.krlane = krln;
+			eq_setup.bpphy = krln->bpphy;
+			eq_setup.reg_base = krln->reg_base;
+			eq_setup.equalizer = *krln->bp_phy->bp_dev.equalizer;
+
+			/* Create EQ Algorithm */
+			krln->eq_priv = krln->eq_alg->ops.create(eq_setup);
+
+			/* register lane attached to an algorithm */
+			spmap_add(&lnalg_list, bp_phy->bp_dev.eqa_name, krln);
+
+			if (krln->eq_alg->use_remote_tx_training) {
+				if (!krln->eq_alg->ops.is_rx_ok)
+					bpdev_warn(bpphy,
+						   "Required operation for remote Tx training is missing: is_rx_ok\n");
+				if (!krln->eq_alg->ops.is_eq_done)
+					bpdev_warn(bpphy,
+						   "Required operation for remote Tx training is missing: is_eq_done\n");
+				if (!krln->eq_alg->ops.collect_statistics)
+					bpdev_warn(bpphy,
+						   "Required operation for remote Tx training is missing: collect_statistics\n");
+				if (!krln->eq_alg->ops.generate_request)
+					bpdev_warn(bpphy,
+						   "Required operation for remote Tx training is missing: generate_request\n");
+			}
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_setup_lanes);
+
+/* backplane_initialize
+ * Initializes all PHY and lane mutexes and
+ * starts lane timers for running the algorithm
+ */
+int backplane_initialize(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&bp_phy->bpphy_lock);
+	mutex_init(&bp_phy->trained_lock);
+
+	for (i = 0; i < bp_phy->num_lanes; i++)
+		mutex_init(&bp_phy->krln[i].lane_lock);
+
+	bpphy->speed = get_backplane_speed(bp_phy->bp_mode);
+	if (bpphy->speed < 0) {
+		bpdev_err(bpphy, "Unsupported backplane mode\n");
+		return -EINVAL;
+	}
+
+	if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+		for (i = 0; i < bp_phy->num_lanes; i++)
+			init_kr_state_machine(&bp_phy->krln[i]);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_initialize);
+
+/* backplane_probe
+ *
+ * Probe function for backplane driver to provide generic device behavior
+ *
+ * bpphy: backplane phy device
+ *	this is an internal phy block controlled by the software
+ *	which contains other component blocks like: PMA/PMD, PCS, AN
+ *
+ * Return: Zero for success or error code in case of failure
+ */
+int backplane_probe(struct phy_device *bpphy)
+{
+	return backplane_create(bpphy);
+}
+EXPORT_SYMBOL(backplane_probe);
+
+void backplane_remove(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return;
+	}
+
+	kfree(bp_phy);
+	bpphy->priv = NULL;
+}
+EXPORT_SYMBOL(backplane_remove);
+
+/* backplane_config_init
+ *
+ * Config_Init function for backplane driver to provide generic device behavior
+ *
+ * bpphy: backplane phy device
+ *
+ * Return: Zero for success or error code in case of failure
+ */
+int backplane_config_init(struct phy_device *bpphy)
+{
+	int ret;
+
+	ret = backplane_parse_dt(bpphy);
+	if (ret)
+		return ret;
+
+	ret = backplane_setup_mdio(bpphy);
+	if (ret)
+		return ret;
+
+	ret = backplane_setup_lanes(bpphy);
+	if (ret)
+		return ret;
+
+	ret = backplane_initialize(bpphy);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_config_init);
+
+int backplane_aneg_done(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	if (!bpphy->mdio.dev.of_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	bp_phy->aneg_done = true;
+	bpphy->state = PHY_RUNNING;
+
+	return 1;
+}
+EXPORT_SYMBOL(backplane_aneg_done);
+
+int backplane_config_aneg(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct kr_lane_info *krln;
+	struct equalization_ops ops;
+	int i;
+
+	if (!bpphy->mdio.dev.of_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+	if (backplane_get_lanes_trained_count(bp_phy) > 0) {
+		bpdev_err(bpphy, "Incorrectly trained lanes detected\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		krln = &bp_phy->krln[i];
+		if (krln->eq_alg) {
+			ops = krln->eq_alg->ops;
+			if (ops.dump_algorithm_context)
+				ops.dump_algorithm_context(krln->eq_priv);
+		}
+	}
+
+	if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+		/* Warning:
+		 * Order of the operations below is important
+		 * otherwise the training may be failing
+		 * with error: 'link_training_failed'
+		 */
+
+		/* setup all lanes to default */
+		for (i = 0; i < bp_phy->num_lanes; i++)
+			setup_default_settings(&bp_phy->krln[i]);
+
+		/* Initialize all lanes and reset LT */
+		for (i = 0; i < bp_phy->num_lanes; i++) {
+			init_krln(&bp_phy->krln[i], true);
+			lt_reset(&bp_phy->krln[i]);
+		}
+	}
+
+	/* Warning:
+	 * speed and protocol setup operation
+	 * must be done just before AN and state machine start
+	 * otherwise if it is done earlier,
+	 * the error: 'REQ Timeout' will occur
+	 */
+	/* setup supported speed and protocol */
+	bpphy->speed = get_backplane_speed(bp_phy->bp_mode);
+	if (bpphy->speed < 0) {
+		bpdev_err(bpphy, "Unsupported backplane mode\n");
+		return -EINVAL;
+	}
+
+	setup_supported_linkmode(bpphy);
+	linkmode_copy(bpphy->advertising, bpphy->supported);
+	bpphy->duplex = DUPLEX_FULL;
+
+	if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+		/* Start AN only for Master Lane */
+		lt_start_an(&bp_phy->krln[MASTER_LANE]);
+		/* start state machine on all lanes */
+		for (i = 0; i < bp_phy->num_lanes; i++)
+			start_kr_state_machine(&bp_phy->krln[i], KR_TIMEOUT_1);
+	}
+
+	bp_phy->aneg_config = true;
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_config_aneg);
+
+int backplane_suspend(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	if (!bpphy->mdio.dev.of_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	if (bp_phy->aneg_config && !bp_phy->phy_suspended) {
+		if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+			for (i = 0; i < bp_phy->num_lanes; i++)
+				stop_kr_state_machine(&bp_phy->krln[i]);
+		}
+		bp_phy->phy_suspended = true;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_suspend);
+
+int backplane_resume(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	int i;
+
+	if (!bpphy->mdio.dev.of_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	if (bp_phy->aneg_config && bp_phy->phy_suspended) {
+		if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+			for (i = 0; i < bp_phy->num_lanes; i++) {
+				init_krln(&bp_phy->krln[i], true);
+				start_kr_state_machine(&bp_phy->krln[i],
+						       KR_TIMEOUT_1);
+			}
+		}
+		bp_phy->phy_suspended = false;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_resume);
+
+int backplane_read_status(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+
+	if (!bpphy->mdio.dev.of_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	/* Linkup method proposal for training stability:
+	 * Don't raise linkup until all lanes are trained
+	 * in order to prevent interface sending packets that may
+	 * interfere with the training packets
+	 */
+	if (backplane_is_link_up(bpphy))
+		if (backplane_is_mode_kr(bp_phy->bp_mode))
+			bpphy->link = backplane_are_all_lanes_trained(bp_phy);
+		else
+			bpphy->link = 1;
+	else
+		bpphy->link = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(backplane_read_status);
+
+int backplane_match_phy_device(struct phy_device *bpphy)
+{
+	struct device_node *bpphy_node;
+
+	if (!bpphy->mdio.dev.of_node)
+		return 0;
+
+	if (!bpphy->is_c45)
+		return 0;
+
+	bpphy_node = bpphy->mdio.dev.of_node;
+	if (!bpphy_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return 0;
+	}
+
+	return 1;
+}
+EXPORT_SYMBOL(backplane_match_phy_device);
+
+static int __init backplane_module_init(void)
+{
+	pr_info("%s: Backplane driver version %s\n",
+		BACKPLANE_DRIVER_NAME, BACKPLANE_DRIVER_VERSION);
+	mutex_init(&backplane_lock);
+	backplane_features_init();
+	return 0;
+}
+
+static void __exit backplane_module_exit(void)
+{
+	pr_info("%s: Backplane driver version %s unloaded\n",
+		BACKPLANE_DRIVER_NAME, BACKPLANE_DRIVER_VERSION);
+}
+
+module_init(backplane_module_init);
+module_exit(backplane_module_exit);
+
+MODULE_DESCRIPTION("Backplane driver");
+MODULE_AUTHOR("Florinel Iordache <florinel.iordache@nxp.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/phy/backplane/backplane.h b/drivers/net/phy/backplane/backplane.h
new file mode 100644
index 0000000..911e418
--- /dev/null
+++ b/drivers/net/phy/backplane/backplane.h
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* Backplane driver
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#ifndef __BACKPLANE_H
+#define __BACKPLANE_H
+
+#include <linux/phy.h>
+#include <linux/mutex.h>
+
+#include "equalization.h"
+
+/* Backplane Driver name */
+#define BACKPLANE_DRIVER_NAME			"backplane"
+
+/* Backplane Driver version */
+#define BACKPLANE_DRIVER_VERSION		"1.0.0"
+
+/* Maximum number of lanes per phy */
+#define MAX_KR_LANES_PER_PHY			4
+
+/* Lanes definitions */
+#define MASTER_LANE				0
+#define SINGLE_LANE				0
+
+extern __ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init;
+
+#define BACKPLANE_FEATURES ((unsigned long *)&backplane_features)
+
+enum train_state {
+	DETECTING_LP,
+	TRAINED,
+};
+
+enum lane_req {
+	LANE_INVALID,
+	LANE_RX,
+	LANE_TX,
+	LANE_RX_TX
+};
+
+struct lane_kr_params {
+	u32 ratio_preq;
+	u32 ratio_pstq;
+	u32 adpt_eq;
+	u32 amp_red;
+};
+
+/* Generic Lane operations */
+struct lane_io_ops {
+	const void *priv;	/* device specific private info */
+	u32 memmap_size;	/* lane memory map size */
+	void (*reset_lane)(void __iomem *reg, enum lane_req req);
+	void (*tune_lane_kr)(void __iomem *reg, struct lane_kr_params *params,
+			     bool reset);
+	void (*read_lane_kr)(void __iomem *reg, struct lane_kr_params *params);
+	bool (*is_cdr_lock)(void __iomem *reg);
+};
+
+/* Endianness specific memory I/O operations
+ */
+struct mem_io_ops {
+	u32 (*read32)(void __iomem *addr);
+	void (*write32)(u32 value, void __iomem *addr);
+};
+
+struct training_status {
+	bool done_training;
+	bool remote_tx_complete;
+	bool remote_tx_running;
+	bool sent_init;
+	bool lp_rx_ready;
+	bool local_tx_running;
+};
+
+struct kr_mdio_info {
+	/* MDIO_XFI_PMD Registers */
+	int lt_devad;
+	u32 pmd_ctrl_1;
+	/* MDIO_XFI_PMD LT Registers */
+	u32 lt_kr_pmd_control;
+	u32 lt_kr_pmd_status;
+	u32 lt_kr_lp_cu;
+	u32 lt_kr_lp_status;
+	u32 lt_kr_ld_cu;
+	u32 lt_kr_ld_status;
+	u32 lt_prbs_berr_lower;
+	u32 lt_prbs_berr_upper;
+	/* MDIO_XFI_AN Registers: MMD 7 */
+	u32 an_control;
+	u32 an_status;
+	u32 an_ad_ability_0;
+	u32 an_ad_ability_1;
+	u32 an_lp_base_page_ability_1;
+	u32 an_bp_eth_status;
+	/* MDIO AN register ops */
+	u32 (*get_an_bp_eth_status_bit)(phy_interface_t bp_mode);
+	u32 (*get_an_ad_ability_1_init)(phy_interface_t bp_mode);
+};
+
+/* Backplane device info */
+struct backplane_dev_info {
+	u32 cm_min;
+	u32 cm_max;
+	u32 cz_min;
+	u32 cz_max;
+	u32 cp_min;
+	u32 cp_max;
+	u32 sum_ratio_numer;
+	u32 sum_ratio_denom;
+	u32 cm_def;
+	u32 cz_def;
+	u32 cp_def;
+	u32 amp_red_def;
+	bool coef_def_dt; /* defaults for eq coef initialized from DT */
+	bool ampr_def_dt; /* defaults for amp red initialized from DT */
+	bool is_little_endian; /* serdes endianness */
+	u32 base_addr;		/* serdes base address */
+	u32 memmap_size;	/* serdes memory map size */
+	const char *eqa_name; /* EQ algorithm name */
+	struct mem_io_ops io;
+	const struct lane_io_ops *lane_ops;
+	const struct equalizer_info *equalizer;
+	struct kr_mdio_info mdio;
+};
+
+struct backplane_phy_info;
+
+/* KR Lane info */
+struct kr_lane_info {
+	/* generic KR data */
+	void __iomem *reg_base;	/* lane memory map: registers base address */
+	u32 memmap_size;	/* lane memory map size */
+	u32 lane_addr;		/* lane address */
+	u8 idx;			/* lane relative index inside multi-lane PHY */
+	struct phy_device *bpphy; /* backplane phy device */
+	struct backplane_phy_info *bp_phy;
+	const struct equalization_algorithm *eq_alg;
+	struct eq_data_priv *eq_priv;
+	struct training_status trst;
+	struct delayed_work kr_wk;
+	/* mutex for multiple lanes training case */
+	struct mutex lane_lock;
+	enum train_state state;
+	/* KR LD/LP updates and status */
+	u32 ld_update;
+	u32 prev_ld_update;
+	u32 ld_last_nonhold_update; /* last change (non-hold) update */
+	u32 ld_status;
+	u32 lp_status;
+	u32 lp_last_change_status; /* last change (non-zero) status */
+	u32 last_lp_update_status[C_NO];
+	/* training status data */
+	bool lt_error;
+	bool move_back_prev;
+	u32 move_back_cnt;
+	u32 move_back_lp_status;
+	u32 req_ld_update_init_count;
+	u32 repeat_request_count;
+	u64 init_handshake_time;
+	bool first_recv_init;
+	bool an_acquired;
+	u32 an_wait_count;
+	u64 rt_time;
+	/* KR parameters (current, default, tunned) */
+	u32 ratio_preq;
+	u32 ratio_pstq;
+	u32 adpt_eq;
+	u32 def_ratio_preq;
+	u32 def_ratio_pstq;
+	u32 def_adpt_eq;
+	u32 def_amp_red;
+	u32 tuned_ratio_preq;
+	u32 tuned_ratio_pstq;
+	u32 tuned_adpt_eq;
+};
+
+struct backplane_phy_info {
+	phy_interface_t bp_mode;
+	u8 num_lanes;
+	bool aneg_config;
+	bool aneg_done;
+	bool phy_suspended;
+	struct backplane_dev_info bp_dev;
+	struct kr_lane_info krln[MAX_KR_LANES_PER_PHY];
+	/* bpphy mutexes */
+	struct mutex bpphy_lock;
+	/* mutex between multiple lanes training */
+	struct mutex trained_lock;
+};
+
+bool backplane_is_mode_kr(phy_interface_t bp_mode);
+
+bool backplane_is_valid_mode(phy_interface_t bp_mode);
+
+u8 backplane_num_lanes(phy_interface_t bp_mode);
+
+bool backplane_is_single_lane(struct backplane_phy_info *bp_phy);
+
+bool backplane_is_multi_lane(struct backplane_phy_info *bp_phy);
+
+int backplane_is_link_up(struct phy_device *bpphy);
+
+void backplane_setup_mdio_c45(struct backplane_dev_info *bp_dev);
+
+void backplane_setup_kr_lt_mmd(struct backplane_dev_info *bp_dev, int devad,
+			       u32 base);
+
+int backplane_read_mmd(struct kr_lane_info *krln, int devad, u32 regnum);
+
+int backplane_write_mmd(struct kr_lane_info *krln, int devad, u32 regnum,
+			u16 val);
+
+void backplane_default_kr_lane(struct kr_lane_info *krln);
+
+void backplane_get_current_taps(struct kr_lane_info *krln, u32 *coef);
+
+void backplane_set_current_taps(struct kr_lane_info *krln, u32 *coef);
+
+void backplane_set_all_taps_to_max(struct kr_lane_info *krln);
+
+void backplane_tune_kr_lane(struct kr_lane_info *krln, bool reset_lane);
+
+int backplane_are_all_lanes_trained(struct backplane_phy_info *bp_phy);
+
+int backplane_get_lanes_trained_count(struct backplane_phy_info *bp_phy);
+
+/* generic main operations to be used on probe callback */
+
+int backplane_create(struct phy_device *bpphy);
+
+int backplane_parse_dt(struct phy_device *bpphy);
+
+int backplane_setup_mdio(struct phy_device *bpphy);
+
+int backplane_setup_lanes(struct phy_device *bpphy);
+
+int backplane_initialize(struct phy_device *bpphy);
+
+/* predefined phy_driver callback functions */
+
+int backplane_probe(struct phy_device *bpphy);
+
+void backplane_remove(struct phy_device *bpphy);
+
+int backplane_config_init(struct phy_device *bpphy);
+
+int backplane_aneg_done(struct phy_device *bpphy);
+
+int backplane_config_aneg(struct phy_device *bpphy);
+
+int backplane_suspend(struct phy_device *bpphy);
+
+int backplane_resume(struct phy_device *bpphy);
+
+int backplane_read_status(struct phy_device *bpphy);
+
+int backplane_match_phy_device(struct phy_device *bpphy);
+
+#endif /* __BACKPLANE_H */
diff --git a/drivers/net/phy/backplane/eq_fixed.c b/drivers/net/phy/backplane/eq_fixed.c
new file mode 100644
index 0000000..5244cec
--- /dev/null
+++ b/drivers/net/phy/backplane/eq_fixed.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Fixed: No Equalization algorithm
+ *
+ * Copyright 2019-2020 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "equalization.h"
+
+#define ALGORITHM_NAME		"backplane_fixed"
+#define ALGORITHM_DESCR		"Fixed Equalization"
+#define ALGORITHM_VERSION	"1.0.0"
+
+/* Fixed Algorithm API */
+
+/* Create Fixed Equalization Algorithm */
+static struct eq_data_priv *create(struct eq_setup_info setup)
+{
+	return NULL;
+}
+
+static const struct equalization_algorithm eq_alg = {
+	.name = ALGORITHM_NAME,
+	.descr = ALGORITHM_DESCR,
+	.version = ALGORITHM_VERSION,
+	.use_local_tx_training = false,
+	.use_remote_tx_training = false,
+	.ops = {
+		.create = create,
+		.destroy = NULL,
+		.is_rx_ok = NULL,
+		.is_eq_done = NULL,
+		.collect_statistics = NULL,
+		.generate_request = NULL,
+		.process_bad_state = NULL,
+		.dump_algorithm_context = NULL,
+	}
+};
+
+static const char * const alg_keys[] = {
+	DEFAULT_EQ_ALGORITHM,
+	"bypass",
+};
+
+static int __init fixed_init(void)
+{
+	int i, err;
+
+	pr_info("%s: %s algorithm version %s\n",
+		ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION);
+
+	/* register Fixed algorithm: */
+	for (i = 0; i < ARRAY_SIZE(alg_keys); i++) {
+		err = backplane_eq_register(alg_keys[i], &eq_alg);
+		if (err) {
+			pr_err("%s: '%s' equalization algorithm registration failed\n",
+			       ALGORITHM_NAME, alg_keys[i]);
+		}
+	}
+
+	return 0;
+}
+
+static void __exit fixed_exit(void)
+{
+	int i;
+
+	/* unregister Fixed algorithm: */
+	for (i = 0; i < ARRAY_SIZE(alg_keys); i++)
+		backplane_eq_unregister(alg_keys[i]);
+
+	pr_info("%s: %s algorithm version %s unloaded\n",
+		ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION);
+}
+
+module_init(fixed_init);
+module_exit(fixed_exit);
+
+MODULE_DESCRIPTION("Fixed Equalization Algorithm");
+MODULE_AUTHOR("Florinel Iordache <florinel.iordache@nxp.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/phy/backplane/equalization.h b/drivers/net/phy/backplane/equalization.h
new file mode 100644
index 0000000..167c9f1
--- /dev/null
+++ b/drivers/net/phy/backplane/equalization.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* Equalization interface
+ * for Equalization and Link Training (IEEE802.3ap/ba)
+ *
+ * Copyright 2019-2020 NXP
+ */
+
+#ifndef __EQUALIZATION_H
+#define __EQUALIZATION_H
+
+#include <linux/phy.h>
+
+/* Default equalization algorithm */
+#define DEFAULT_EQ_ALGORITHM			"fixed"
+
+struct kr_lane_info;
+struct eq_setup_info;
+
+/* EQ Algorithms Interface used by Link Training
+ * to call equalization algorithms callbacks
+ */
+
+/* Equalization private data
+ * specifically defined by each algorithm to be used internally
+ */
+struct eq_data_priv;
+
+/* Equalization Algorithm operations */
+struct equalization_ops {
+	/* Mandatory operations: */
+	struct eq_data_priv *(*create)(struct eq_setup_info setup);
+	void (*destroy)(struct eq_data_priv *priv);
+	/* Required operations for remote Tx link training: */
+	bool (*is_rx_ok)(struct eq_data_priv *priv);
+	bool (*is_eq_done)(struct eq_data_priv *priv);
+	bool (*collect_statistics)(struct eq_data_priv *priv);
+	void (*generate_request)(struct eq_data_priv *priv);
+	/* Optional operations: */
+	void (*process_bad_state)(struct eq_data_priv *priv);
+	void (*dump_algorithm_context)(struct eq_data_priv *priv);
+};
+
+/* Equalization Algorithm description data */
+struct equalization_algorithm {
+	const char *name;
+	const char *descr;
+	const char *version;
+	bool use_local_tx_training;
+	bool use_remote_tx_training;
+	struct equalization_ops ops;
+};
+
+/* Equalizer Interface for EQ Algorithms:
+ * Used by equalization algorithms to collect equalizer statistics
+ * required to take correct decisions for tuning equalization parameters
+ */
+
+/* Equalizer counters type
+ *
+ * Equalizer Binning Counters for Data Dependent Edge Statistics:
+ *
+ *   Bin(s) = (# late edges - # early edges)
+ *            Prior/Next Edge at T -/+ #UI (Unit Interval)
+ *   Bin_1: 1UI wide pulses: Prior Edge at T - 1UI
+ *      final edges on short pulses:
+ *      - contains the scoring of final edges on pulses that are 1UI long
+ *      - represents the difference between the number of short pulse late edges
+ *        and the number of short pulse early edges
+ *   Bin_2: 2UI wide pulses: Prior Edge at T - 2UI
+ *   Bin_3: 3UI (or >=3UI) wide pulses: Prior Edge at T - 3UI (or T - >=3UI)
+ *   Bin_4: 4UI (or >=4UI) wide pulses: Prior Edge at T - 4UI (or T - >=4UI)
+ *   Bin_Med: >=5UI and <=7UI wide pulses:
+ *      Prior Edge in between T - >=5UI and T - <=7UI
+ *      final edges on medium pulses:
+ *      - contains the scoring of final edges on pulses between 5UI and 7UI long
+ *   Bin_Long: >=8UI wide pulses: Prior Edge at T - >=8UI
+ *      final edges on long pulses:
+ *      - contains the scoring of final edges on pulses longer than 7UI long
+ *      - represents the difference between the number of long pulse late edges
+ *        and the number of long pulse early edges
+ *   Bin_M1: 1UI wide pulses: Next Edge at T + 1UI
+ *      initial edges on short pulses following non-single bits:
+ *      - contains the scoring of initial edges on pulses that are 1UI long
+ *        following non-single bits
+ *      - the next edge is 1UI away and prior edge is more than 1UI away
+ *   Bin_M2: 2UI wide pulses: Next Edge at T + 2UI
+ *   Bin_M3: 3UI (or >=3UI) wide pulses: Next Edge at T + 3UI (or T + >=3UI)
+ *   Bin_M4: 4UI (or >=4UI) wide pulses: Next Edge at T + 4UI (or T + >=4UI)
+ *   Bin_MMed: >=5UI and <=7UI wide pulses:
+ *      Next Edge in between T + >=5UI and T + <=7UI
+ *      initial edges on medium pulses following non-single bits:
+ *      - contains the scoring of initial edges on pulses between 5UI and 7UI
+ *      following non-single bits
+ *   Bin_MLong: >=8UI wide pulses: Next Edge at T + >=8UI
+ *      initial edges on long pulses following non-single bits:
+ *      - contains the scoring of initial edges on pulses longer than 7UI long
+ *      - represents the difference between the number of long pulse late edges
+ *        and the number of long pulse early edges
+ *
+ *   Bin_Offset = [(# late rising edges + # early falling edges) -
+ *                 (# early rising edges + # late falling edges)]
+ *      - contains the transition information for the difference between
+ *        all bits that are narrower than expected and
+ *        all bits that are wider than expected
+ *
+ *   Bin_Avg: Low Pass Filter of Running Disparity
+ *      - Bin_Avg provides a time weighted, filtered average of disparity which
+ *        indicates the BLW potential of recently received data
+ *        New Bin_Avg = Bin_Avg - Bin_Avg/8 + block_disparity
+ *                      where block_disparity = (#of ones - #of zeros)
+ *
+ *   Bin_BLW: Bin Baseline Wander
+ *      - BinBLW accumulates the correlation between Bin_Avg and Bin_Offset
+ *      - Low frequency deficiency (LFD) causes BLW effect
+ *        New Bin_BLW = Bin_BLW + Bin_Avg, for Bin_Offset > 0
+ *                    = Bin_BLW - Bin_Avg, for Bin_Offset < 0
+ *                    = Bin_BLW,           for Bin_Offset = 0
+ *
+ * Equalizer gains:
+ *   GAIN_LF: Low-frequency gain of the equalizer amplifier
+ *   GAIN_MF: Middle-frequency gain of the equalizer amplifier
+ *   GAIN_HF: High-frequency gain of the equalizer amplifier
+ *
+ * Equalizer status:
+ *   EQOFFSET: equalization offset status
+ *      Binary coded status of RX Adaptive Equalization offset controls of lane
+ */
+enum eqc_type {
+	EQC_BIN_1,
+	EQC_BIN_2,
+	EQC_BIN_3,
+	EQC_BIN_4,
+	EQC_BIN_MED,
+	EQC_BIN_LONG,
+	EQC_BIN_M1,
+	EQC_BIN_M2,
+	EQC_BIN_M3,
+	EQC_BIN_M4,
+	EQC_BIN_MMED,
+	EQC_BIN_MLONG,
+	EQC_BIN_OFFSET,
+	EQC_BIN_AVG,
+	EQC_BIN_BLW,
+	EQC_GAIN_LF,
+	EQC_GAIN_MF,
+	EQC_GAIN_HF,
+	EQC_EQOFFSET,
+};
+
+/* Equalizer counters range */
+struct eqc_range {
+	s16 min;
+	s16 max;
+	s16 mid_low;
+	s16 mid_high;
+};
+
+/* Equalizer counters collection operations */
+struct equalizer_ops {
+	int (*collect_counters)(void *reg, enum eqc_type type, s16 *counters,
+				u8 size);
+	int (*collect_multiple_counters)(void *reg, enum eqc_type type[],
+					 u8 type_no, s16 *counters, u8 size);
+	struct eqc_range *(*get_counter_range)(enum eqc_type type);
+};
+
+/* Equalizer info and operations */
+struct equalizer_info {
+	const char *name;
+	const char *version;
+	struct equalizer_ops ops;
+};
+
+/* Equalization setup information */
+struct eq_setup_info {
+	struct phy_device *bpphy;
+	/* kr lane info used as parameter for link training API */
+	struct kr_lane_info *krlane;
+	void *reg_base;
+	struct equalizer_info equalizer;
+};
+
+/* Link Training Interface used by EQ Algorithms
+ * to interact with IEEE802.3ap/ba standards
+ */
+
+/* update request type
+ * Identifies the LP update request type according to IEEE802.3ap-2007
+ * which must be sent to LP to request coefficients update
+ *
+ * HOLD: Request LP to Hold all coefficients update
+ * INC: Request LP to Increment the specified coefficient
+ * DEC: Request LP to Decrement the specified coefficient
+ * INIT: Request LP to Initialize all coefficients
+ * PRESET: Request LP to set all coefficients to Preset
+ * INVALID: Invalid request type: should not be used as LP request
+ */
+enum req_type {
+	REQ_HOLD,
+	REQ_INC,
+	REQ_DEC,
+	REQ_INIT,
+	REQ_PRESET,
+	REQ_INVALID
+};
+
+/* coefficient field
+ * Identifies the coefficient field on which must take a desired action
+ * according to IEEE802.3ap-2007
+ *
+ * coefficients:
+ *  M1: C(-1): Pre-cursor
+ *  Z0: C(0):  Main cursor
+ *  P1: C(+1): Post-cursor
+ *  NO: Number of coefficients (this is not a valid coefficient field)
+ */
+enum coef_field {
+	C_M1,
+	C_Z0,
+	C_P1,
+	C_NO
+};
+
+/* coefficient status
+ * Specifies the coefficient status according to IEEE802.3ap-2007:
+ * 72.6.10.2.5 Coefficient update process
+ *
+ * NOTUPDATED: Coefficient is not updated
+ * UPDATED: Coefficient is updated
+ * MIN: Coefficient has reached the minimum threshold
+ * MAX: Coefficient has reached the maximum threshold
+ * INVALID: Invalid coefficient status
+ */
+enum coef_status {
+	COEF_NOTUPDATED,
+	COEF_UPDATED,
+	COEF_MIN,
+	COEF_MAX,
+	COEF_INVALID
+};
+
+void lt_lp_update(struct kr_lane_info *krln, u32 update);
+
+u32 lt_encode_request(u32 base_update, enum req_type req,
+		      enum coef_field field);
+
+u32 lt_encode_startup_request(enum req_type req);
+
+enum req_type lt_decode_coef_update(u32 update, enum coef_field field);
+
+bool lt_is_update_of_type(u32 update, enum req_type type);
+
+bool lt_is_lp_at_startup(struct kr_lane_info *krln, enum req_type type);
+
+enum coef_status lt_get_lp_coef_status(struct kr_lane_info *krln,
+				       enum coef_field field);
+
+void lt_move_lp_back(struct kr_lane_info *krln);
+
+void lt_set_error(struct kr_lane_info *krln, bool err);
+
+/* Backplane Driver Interface for EQ Algorithms:
+ * Used by equalization algorithms to interact
+ * with backplane driver during equalization
+ */
+
+/* equalization algorithm registration */
+int backplane_eq_register(const char *key,
+			  const struct equalization_algorithm *eq_info);
+void backplane_eq_unregister(const char *key);
+
+bool backplane_is_cdr_lock(struct kr_lane_info *krln, bool retry);
+
+void bpdev_err(struct phy_device *bpphy, char *msg, ...);
+
+void bpdev_warn(struct phy_device *bpphy, char *msg, ...);
+
+void bpdev_info(struct phy_device *bpphy, char *msg, ...);
+
+void bpdev_dbg(struct phy_device *bpphy, char *msg, ...);
+
+#endif /* __EQUALIZATION_H */
diff --git a/drivers/net/phy/backplane/link_training.c b/drivers/net/phy/backplane/link_training.c
new file mode 100644
index 0000000..2afecd4
--- /dev/null
+++ b/drivers/net/phy/backplane/link_training.c
@@ -0,0 +1,1604 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Link Training (IEEE802.3ap/ba)
+ * Ethernet Operation over Electrical Backplanes
+ *
+ * Copyright 2019-2020 NXP
+ */
+
+#include <linux/mdio.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+
+#include "link_training.h"
+
+/* KR LP/LD Coefficients */
+#define PRESET_MASK			0x2000
+#define INIT_MASK			0x1000
+#define COP1_MASK			0x30
+#define COP1_SHIFT			4
+#define COZ0_MASK			0xc
+#define COZ0_SHIFT			2
+#define COM1_MASK			0x3
+#define COM1_SHIFT			0
+#define ALL_COEF_MASK		(COP1_MASK | COZ0_MASK | COM1_MASK)
+#define LD_ALL_MASK		(PRESET_MASK | INIT_MASK | ALL_COEF_MASK)
+
+/* KR LP Status Report */
+#define LP_STATUS_ALL_COEF_UPDATED	0x15
+
+/* KR LP/LD Status Report:
+ * RX_READY_MASK - Receiver Ready
+ * 0b - The LP/LD receiver is requesting that training continue
+ * 1b - The LP/LD receiver has determined that training is complete
+ * and is prepared to receive data.
+ */
+#define RX_READY_MASK			0x8000
+
+/* Increment/Decrement Requests */
+#define HOLD					0
+#define INCREMENT				1
+#define DECREMENT				2
+#define RESERVED				3
+
+/* Increment/Decrement Steps */
+#define STEP_INCREMENT_P1			-1
+#define STEP_INCREMENT_Z0			1
+#define STEP_INCREMENT_M1			-1
+
+/* KR PMD Control defines */
+#define TRAIN_EN				0x3
+#define TRAIN_DISABLE				0x1
+#define PMD_RESET				0x1
+
+/* KR PMD Status defines */
+#define PMD_STATUS_TRAIN_FAIL			0x8
+#define PMD_STATUS_SUP_STAT			0x4
+#define PMD_STATUS_FRAME_LOCK			0x2
+#define PMD_STATUS_RX_STAT			0x1
+
+/* KR PMD control register (Register 1.150) */
+#define REGISTER_KR_PMD_CTRL			150
+
+/* Link training KR PMD registers offsets */
+#define OFFSET_KR_PMD_CTRL			0x0
+#define OFFSET_KR_PMD_STATUS			0x1
+#define OFFSET_KR_LP_CU				0x2
+#define OFFSET_KR_LP_STATUS			0x3
+#define OFFSET_KR_LD_CU				0x4
+#define OFFSET_KR_LD_STATUS			0x5
+#define OFFSET_KR_PRBS_BERR_LOWER		0x7F6B
+#define OFFSET_KR_PRBS_BERR_UPPER		0x7F6C
+
+/* Timeouts */
+#define TIMEOUT_MOVE_BACK_PREV			6
+#define TIMEOUT_REPEAT_REQUEST			10
+
+/* Backplane Ethernet status (Register 7.48) */
+#define AN_BP_ETH_STATUS_OFFSET			0x30
+
+/* AN registers initialization */
+#define AN_CTRL_INIT				0x1200
+
+/* Training for Remote Tx */
+
+static u32 get_mask_for_req(enum req_type req)
+{
+	u32 cmd = HOLD;
+
+	switch (req) {
+	case REQ_HOLD:
+		cmd = HOLD;
+		break;
+	case REQ_INC:
+		cmd = INCREMENT;
+		break;
+	case REQ_DEC:
+		cmd = DECREMENT;
+		break;
+	case REQ_INIT:
+		cmd = INIT_MASK;
+		break;
+	case REQ_PRESET:
+		cmd = PRESET_MASK;
+		break;
+	case REQ_INVALID:
+		cmd = RESERVED;
+		break;
+	default:
+		cmd = HOLD;
+		break;
+	}
+	return cmd;
+}
+
+static enum req_type get_req_for_mask(u32 cmd)
+{
+	enum req_type req = REQ_HOLD;
+
+	switch (cmd) {
+	case HOLD:
+		req = REQ_HOLD;
+		break;
+	case INCREMENT:
+		req = REQ_INC;
+		break;
+	case DECREMENT:
+		req = REQ_DEC;
+		break;
+	case INIT_MASK:
+		req = REQ_INIT;
+		break;
+	case PRESET_MASK:
+		req = REQ_PRESET;
+		break;
+	case RESERVED:
+		req = REQ_INVALID;
+		break;
+	default:
+		req = REQ_HOLD;
+		break;
+	}
+	return req;
+}
+
+/* ld_coef_status
+ * 72.6.10.2.5 Coefficient update process
+ * Once the updated, maximum, or minimum state is reported it continues
+ * to be reported until a hold request is received,
+ * after which the status reverts to not_updated.
+ */
+static void ld_coef_status(struct kr_lane_info *krln)
+{
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_ld_status,
+			    krln->ld_status);
+}
+
+/* ld_coef_update
+ * LD sends to LP the specified request for coefficients update
+ */
+static void ld_coef_update(struct kr_lane_info *krln)
+{
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_ld_cu,
+			    krln->ld_update);
+}
+
+/* get_lp_lcs
+ * get LP lcs (last change status)
+ * returns the last LP change (non-zero) status:
+ * meaning the last LP status resulted from a change request
+ * 72.6.10.2.5 Coefficient update process
+ * Once the updated, maximum, or minimum state is reported it continues
+ * to be reported until a hold request is received,
+ * after which the status reverts to not_updated.
+ */
+static u32 get_lp_lcs(struct kr_lane_info *krln)
+{
+	return krln->lp_last_change_status;
+}
+
+static bool is_all_status(u32 status, enum coef_status cs)
+{
+	return ((status & ALL_COEF_MASK) ==
+		(cs << COP1_SHIFT | cs << COZ0_SHIFT | cs << COM1_SHIFT));
+}
+
+/* Training for Local Tx */
+
+static void initialize(struct kr_lane_info *krln)
+{
+	backplane_default_kr_lane(krln);
+
+	krln->ld_status &= ~ALL_COEF_MASK;
+	krln->ld_status |= COEF_UPDATED << COP1_SHIFT |
+			   COEF_UPDATED << COZ0_SHIFT |
+			   COEF_UPDATED << COM1_SHIFT;
+
+	ld_coef_status(krln);
+}
+
+/* preset
+ * Preset as defined by: IEEE 802.3, sub-clause 72.6.10.2.3.1
+ * Setup all coefficients to MAX values from IEEE802.3 perspective
+ */
+static void preset(struct kr_lane_info *krln)
+{
+	backplane_set_all_taps_to_max(krln);
+
+	backplane_tune_kr_lane(krln, true);
+
+	krln->ld_status &= ~ALL_COEF_MASK;
+	krln->ld_status |= COEF_MAX << COP1_SHIFT |
+			   COEF_MAX << COZ0_SHIFT |
+			   COEF_MAX << COM1_SHIFT;
+
+	ld_coef_status(krln);
+}
+
+static bool is_rx_ready(u32 status)
+{
+	return ((status & RX_READY_MASK) != 0);
+}
+
+/* is_ld_valid
+ * LD coefficient values have hardware restrictions
+ * Check if all ld coefficients are in range
+ */
+static int is_ld_valid(struct kr_lane_info *krln, u32 *ld_coef)
+{
+	u32 ratio_pstq = ld_coef[C_P1];
+	u32 adpt_eq = ld_coef[C_Z0];
+	u32 ratio_preq = ld_coef[C_M1];
+	struct backplane_dev_info *bp_dev = &krln->bp_phy->bp_dev;
+
+	/* HW restrictions:
+	 * Section 5.3.1 10GBaseKR Transmit Adaptive Equalization Control
+	 * additional restrictions set down by 802.3 specification Clause 72,
+	 * specifically 72.7.1.11 Transmitter output waveform requirements
+	 *
+	 * Maintaining the following relationships limit transmit equalization
+	 * to reasonable levels compliant with the KR specification
+	 */
+
+	/* 1. [condition (1) was moved below for optimization purpose] */
+
+	/* Basic HW restrictions: */
+
+	/* 2. tx_ratio_preq <= MIN_C(-1) */
+	if (ratio_preq > bp_dev->cm_min)
+		return -ERANGE;
+	/* 3. tx_ratio_post1q <= MIN_C(+1) */
+	if (ratio_pstq > bp_dev->cp_min)
+		return -ERANGE;
+	/* 4. MIN_C(0) <= tx_adpt_eq <= MAX_C(0) */
+	if (adpt_eq < bp_dev->cz_min)
+		return -ERANGE;
+	if (adpt_eq > bp_dev->cz_max)
+		return -ERANGE;
+	/* 5. tx_ratio_post1q >= tx_ratio_preq */
+	if (ratio_pstq < ratio_preq)
+		return -ERANGE;
+
+	/* Additional HW restrictions:
+	 * 1. MIN_C(0) <= tx_ratio_preq + tx_adpt_eq +
+	 *                                tx_ratio_post1q <= MAX_C(0)
+	 */
+	if ((ratio_preq + ratio_pstq + adpt_eq) < bp_dev->cz_min)
+		return -ERANGE;
+	if ((ratio_preq + ratio_pstq + adpt_eq) > bp_dev->cz_max)
+		return -ERANGE;
+	/* 6.
+	 * ( tx_adpt_eq + tx_ratio_preq + tx_ratio_post1q ) /
+	 * ( tx_adpt_eq - tx_ratio_preq - tx_ratio_post1q ) <
+	 *    sum_ratio_numerator / sum_ratio_denominator
+	 */
+	if (((adpt_eq + ratio_preq + ratio_pstq) * bp_dev->sum_ratio_denom) >=
+	    ((adpt_eq - ratio_preq - ratio_pstq) * bp_dev->sum_ratio_numer))
+		return -ERANGE;
+
+	return 0;
+}
+
+static bool update_ld_status(struct kr_lane_info *krln, enum coef_field field,
+			     enum coef_status cs)
+{
+	u32 mask, val;
+	u32 ld_cs = cs;
+
+	if (cs == COEF_INVALID)
+		return false;
+
+	switch (field) {
+	case C_P1:
+		mask = COP1_MASK;
+		val = ld_cs << COP1_SHIFT;
+		break;
+	case C_Z0:
+		mask = COZ0_MASK;
+		val = ld_cs << COZ0_SHIFT;
+		break;
+	case C_M1:
+		mask = COM1_MASK;
+		val = ld_cs << COM1_SHIFT;
+		break;
+	default:
+		return false;
+	}
+
+	krln->ld_status &= ~mask;
+	krln->ld_status |= val;
+
+	return true;
+}
+
+static enum coef_status inc_dec(struct kr_lane_info *krln,
+				enum coef_field field, int request)
+{
+	u32 ld_coef[C_NO], step[C_NO], ld_limit[C_NO];
+	int err;
+
+	backplane_get_current_taps(krln, ld_coef);
+
+	step[C_P1] = STEP_INCREMENT_P1;
+	step[C_Z0] = STEP_INCREMENT_Z0;
+	step[C_M1] = STEP_INCREMENT_M1;
+
+	/* 72.6.10.2.5 Coefficient update process
+	 * Upon execution of a received increment or decrement request,
+	 * the status is reported as updated, maximum, or minimum.
+	 */
+	switch (request) {
+	case INCREMENT:
+		ld_limit[C_P1] = krln->bp_phy->bp_dev.cp_max;
+		ld_limit[C_Z0] = krln->bp_phy->bp_dev.cz_max;
+		ld_limit[C_M1] = krln->bp_phy->bp_dev.cm_max;
+		if (ld_coef[field] != ld_limit[field])
+			ld_coef[field] += step[field];
+		else
+			return COEF_MAX;
+		break;
+	case DECREMENT:
+		ld_limit[C_P1] = krln->bp_phy->bp_dev.cp_min;
+		ld_limit[C_Z0] = krln->bp_phy->bp_dev.cz_min;
+		ld_limit[C_M1] = krln->bp_phy->bp_dev.cm_min;
+		if (ld_coef[field] != ld_limit[field])
+			ld_coef[field] -= step[field];
+		else
+			return COEF_MIN;
+		break;
+	default:
+		break;
+	}
+
+	err = is_ld_valid(krln, ld_coef);
+	if (!err) {
+		/* accept new ld coefficients */
+		backplane_set_current_taps(krln, ld_coef);
+		backplane_tune_kr_lane(krln, false);
+	} else {
+		if (request == DECREMENT)
+			return COEF_MIN;
+		if (request == INCREMENT)
+			return COEF_MAX;
+	}
+
+	/* UPDATED */
+	return COEF_UPDATED;
+}
+
+static void check_request(struct kr_lane_info *krln, int request)
+{
+	int cop1_req, coz0_req, com1_req;
+	int old_status;
+	enum coef_status cu = COEF_INVALID;
+
+	cop1_req = (request & COP1_MASK) >> COP1_SHIFT;
+	coz0_req = (request & COZ0_MASK) >> COZ0_SHIFT;
+	com1_req = (request & COM1_MASK) >> COM1_SHIFT;
+
+	/* IEEE802.3-2008, 72.6.10.2.5
+	 * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED
+	 *
+	 * 72.6.10.2.5 Coefficient update process
+	 * An increment or decrement request will only be acted upon when
+	 * the state of the tap is not_updated.
+	 */
+	old_status = krln->ld_status;
+
+	if (cop1_req && !(krln->ld_status & COP1_MASK)) {
+		cu = inc_dec(krln, C_P1, cop1_req);
+		update_ld_status(krln, C_P1, cu);
+	}
+
+	if (coz0_req && !(krln->ld_status & COZ0_MASK)) {
+		cu = inc_dec(krln, C_Z0, coz0_req);
+		update_ld_status(krln, C_Z0, cu);
+	}
+
+	if (com1_req && !(krln->ld_status & COM1_MASK)) {
+		cu = inc_dec(krln, C_M1, com1_req);
+		update_ld_status(krln, C_M1, cu);
+	}
+
+	if (old_status != krln->ld_status)
+		ld_coef_status(krln);
+}
+
+static void training_complete(struct kr_lane_info *krln)
+{
+	struct training_status *trst = &krln->trst;
+
+	/* update training status */
+	trst->remote_tx_complete = true;
+	trst->remote_tx_running = false;
+
+	/* report LD status */
+	krln->ld_status |= RX_READY_MASK;
+	ld_coef_status(krln);
+
+	/* update PMD status and tell LP we are ready */
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status,
+			    PMD_STATUS_RX_STAT);
+}
+
+/* Link Training general API */
+
+/* Setup standard KR LT memory map registers
+ * 45.2.1.76 10GBASE-KR PMD control register (Register 1.150)
+ */
+void lt_setup_c45(struct backplane_dev_info *bp_dev)
+{
+	bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET;
+
+	lt_setup_memmap(bp_dev, MDIO_MMD_PMAPMD, REGISTER_KR_PMD_CTRL);
+}
+
+/* Setup KR LT memory map registers
+ * IEEE Std 802.3ap-2007: Table 45.3 PMA/PMD registers
+ */
+void lt_setup_memmap(struct backplane_dev_info *bp_dev, int devad, u32 base)
+{
+	bp_dev->mdio.lt_devad = devad;
+	bp_dev->mdio.lt_kr_pmd_control = base + OFFSET_KR_PMD_CTRL;
+	bp_dev->mdio.lt_kr_pmd_status = base + OFFSET_KR_PMD_STATUS;
+	bp_dev->mdio.lt_kr_lp_cu = base + OFFSET_KR_LP_CU;
+	bp_dev->mdio.lt_kr_lp_status = base + OFFSET_KR_LP_STATUS;
+	bp_dev->mdio.lt_kr_ld_cu = base + OFFSET_KR_LD_CU;
+	bp_dev->mdio.lt_kr_ld_status = base + OFFSET_KR_LD_STATUS;
+	bp_dev->mdio.lt_prbs_berr_lower = base + OFFSET_KR_PRBS_BERR_LOWER;
+	bp_dev->mdio.lt_prbs_berr_upper = base + OFFSET_KR_PRBS_BERR_UPPER;
+}
+
+/* lt_is_lp_rx_ready
+ * Reports if LP Receiver is ready
+ * false: The LP receiver is requesting that training continue
+ * true: The LP receiver has determined that training is complete
+ * and is prepared to receive data.
+ */
+bool lt_is_lp_rx_ready(struct kr_lane_info *krln)
+{
+	struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio;
+
+	/* Read LP Status */
+	krln->lp_status = backplane_read_mmd(krln,
+					     mdio->lt_devad,
+					     mdio->lt_kr_lp_status);
+	return is_rx_ready(krln->lp_status);
+}
+
+/* lt_is_ld_rx_ready
+ * Reports if LD Receiver is ready
+ * false: The LD receiver is requesting that training continue
+ * true: The LD receiver has determined that training is complete
+ * and is prepared to receive data.
+ */
+bool lt_is_ld_rx_ready(struct kr_lane_info *krln)
+{
+	return is_rx_ready(krln->ld_status);
+}
+
+void lt_start(struct kr_lane_info *krln)
+{
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control,
+			    TRAIN_EN);
+}
+
+void lt_stop(struct kr_lane_info *krln)
+{
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control,
+			    TRAIN_DISABLE);
+}
+
+void lt_reset(struct kr_lane_info *krln)
+{
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.pmd_ctrl_1, PMD_RESET);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_pmd_control,
+			    TRAIN_DISABLE);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_ld_cu, 0);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_ld_status, 0);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status, 0);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_lp_cu, 0);
+	backplane_write_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+			    krln->bp_phy->bp_dev.mdio.lt_kr_lp_status, 0);
+}
+
+/* lt_is_rx_trained
+ * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping
+ * PMD status variable: rx_trained
+ */
+bool lt_is_rx_trained(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	int val;
+	int timeout = 100;
+
+	val = backplane_read_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+				 krln->bp_phy->bp_dev.mdio.lt_kr_pmd_status);
+
+	if ((val & PMD_STATUS_RX_STAT) && !(val & PMD_STATUS_TRAIN_FAIL)) {
+		while (timeout--) {
+			if (backplane_is_link_up(bpphy))
+				return true;
+
+			usleep_range(100, 500);
+		}
+	}
+	return false;
+}
+
+/* lt_is_training_failure
+ * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping
+ * PMD status variable: PMD_fault
+ */
+bool lt_is_training_failure(struct kr_lane_info *krln)
+{
+	struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio;
+	int lt_state;
+
+	lt_state = backplane_read_mmd(krln, mdio->lt_devad,
+				      mdio->lt_kr_pmd_status);
+
+	/* according to spec: 8023ap-2007.pdf
+	 * training_failure
+	 * Boolean variable that is set to TRUE when the training state machine
+	 * has timed out due to expiration of the max_wait_timer while in the
+	 * SEND_TRAINING, TRAIN_LOCAL, or
+	 * TRAIN_REMOTE states and is set to FALSE otherwise.
+	 */
+	if (lt_state & PMD_STATUS_TRAIN_FAIL)
+		return true;
+
+	return false;
+}
+
+/* lt_is_frame_lock
+ * IEEE Std 802.3ap-2007: Table 72.3 MDIO/PMD status variable mapping
+ * PMD status variable: frame_lock
+ */
+bool lt_is_frame_lock(struct kr_lane_info *krln)
+{
+	struct kr_mdio_info *mdio = &krln->bp_phy->bp_dev.mdio;
+	int lt_state;
+
+	lt_state = backplane_read_mmd(krln, mdio->lt_devad,
+				      mdio->lt_kr_pmd_status);
+
+	if ((lt_state & PMD_STATUS_SUP_STAT) &&
+	    (lt_state & PMD_STATUS_FRAME_LOCK))
+		return true;
+
+	return false;
+}
+
+void lt_start_an(struct kr_lane_info *krln)
+{
+	struct phy_device *bpphy = krln->bpphy;
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct kr_mdio_info *mdio = &bp_phy->bp_dev.mdio;
+	u32 an_ad_ability_1 = mdio->an_ad_ability_1;
+	u32 init_an_ad_ab1;
+	int i;
+	int err;
+
+	if (!backplane_is_mode_kr(bp_phy->bp_mode))
+		return;
+
+	if (!mdio->get_an_ad_ability_1_init) {
+		bpdev_err(bpphy, "Unknown AN_AD_ABILITY_1 init value\n");
+		return;
+	}
+
+	init_an_ad_ab1 = mdio->get_an_ad_ability_1_init(bp_phy->bp_mode);
+
+	if (krln->idx == MASTER_LANE) {
+		for (i = 0; i < bp_phy->num_lanes; i++) {
+			err = backplane_write_mmd(&bp_phy->krln[i], MDIO_MMD_AN,
+						  an_ad_ability_1,
+						  init_an_ad_ab1);
+			if (err)
+				bpdev_err(bpphy,
+					  "Setting AN register 0x%02x on lane %d failed with error code: 0x%08x\n",
+					  an_ad_ability_1,
+					  bp_phy->krln[i].idx, err);
+		}
+		udelay(1);
+		err = backplane_write_mmd(krln, MDIO_MMD_AN, mdio->an_control,
+					  AN_CTRL_INIT);
+		if (err)
+			bpdev_err(bpphy,
+				  "Setting AN register 0x%02x on Master Lane failed with error code: 0x%08x\n",
+				  MDIO_CTRL1, err);
+	}
+}
+
+/* Training for Remote Tx
+ * This is the main routine for Remote Tx training
+ */
+void lt_train_remote_tx(struct kr_lane_info *krln)
+{
+	struct training_status *trst = &krln->trst;
+	u32 prev_req_init, prev_req_preset;
+	u32 prev_req_cp1, prev_req_cz0, prev_req_cm1;
+	u32 status_cp1, status_cz0, status_cm1;
+	u64 lp_resp_time;
+
+	/* Check stop condition for Remote Tx training */
+	if (trst->remote_tx_complete)
+		return;
+
+	/* Check if equalization algorithm is installed */
+	if (!krln->eq_alg)
+		return;
+
+	/* Check that all required callback operations are installed */
+	if (!krln->eq_alg->ops.collect_statistics ||
+	    !krln->eq_alg->ops.is_rx_ok ||
+	    !krln->eq_alg->ops.generate_request ||
+	    !krln->eq_alg->ops.is_eq_done)
+		return;
+
+	/* Start new Remote Tx training step */
+	trst->remote_tx_running = true;
+
+	/* Store current state as previous state */
+	krln->prev_ld_update = krln->ld_update;
+	if ((krln->prev_ld_update & ALL_COEF_MASK) != HOLD)
+		krln->ld_last_nonhold_update = krln->prev_ld_update;
+
+	prev_req_init = krln->prev_ld_update & INIT_MASK;
+	prev_req_preset = krln->prev_ld_update & PRESET_MASK;
+	prev_req_cp1 = (krln->prev_ld_update & COP1_MASK) >> COP1_SHIFT;
+	prev_req_cz0 = (krln->prev_ld_update & COZ0_MASK) >> COZ0_SHIFT;
+	prev_req_cm1 = (krln->prev_ld_update & COM1_MASK) >> COM1_SHIFT;
+
+	/* Training Done condition */
+	if (krln->eq_alg->ops.is_eq_done(krln->eq_priv))
+		trst->done_training = true;
+
+	/* Check if Training is Done */
+	if (trst->done_training) {
+		training_complete(krln);
+		return;
+	}
+
+	/* Read LP Status */
+	krln->lp_status =
+		backplane_read_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+				   krln->bp_phy->bp_dev.mdio.lt_kr_lp_status);
+
+	if ((krln->lp_status & ALL_COEF_MASK) != 0)
+		krln->lp_last_change_status = krln->lp_status;
+
+	status_cp1 = (krln->lp_status & COP1_MASK) >> COP1_SHIFT;
+	status_cz0 = (krln->lp_status & COZ0_MASK) >> COZ0_SHIFT;
+	status_cm1 = (krln->lp_status & COM1_MASK) >> COM1_SHIFT;
+
+	if (status_cp1 == COEF_UPDATED || status_cp1 == COEF_MIN ||
+	    status_cp1 == COEF_MAX)
+		krln->last_lp_update_status[C_P1] = status_cp1;
+	if (status_cz0 == COEF_UPDATED || status_cz0 == COEF_MIN ||
+	    status_cz0 == COEF_MAX)
+		krln->last_lp_update_status[C_Z0] = status_cz0;
+	if (status_cm1 == COEF_UPDATED || status_cm1 == COEF_MIN ||
+	    status_cm1 == COEF_MAX)
+		krln->last_lp_update_status[C_M1] = status_cm1;
+
+	/* IEEE802.3-2008, 72.6.10.2.3.2
+	 * we send initialize to the other side to ensure default settings
+	 * for the LP. Naturally, we should do this only once.
+	 */
+	if (!trst->sent_init) {
+		/* All status MUST be NOTUPDATED for INIT to be executed
+		 * otherwise send HOLD first
+		 */
+		if (status_cp1 == COEF_NOTUPDATED &&
+		    status_cz0 == COEF_NOTUPDATED &&
+		    status_cm1 == COEF_NOTUPDATED) {
+			trst->sent_init = true;
+			krln->ld_update = INIT_MASK;
+			krln->req_ld_update_init_count = 1;
+			krln->init_handshake_time = jiffies_to_msecs(jiffies);
+		} else {
+			/* send HOLD before sending subsequent Init requests
+			 * this is not the very first Init sent
+			 */
+			krln->ld_update = HOLD;
+		}
+		ld_coef_update(krln);
+		return;
+	}
+	/* continue to sent init request until LP responds to init */
+	if (prev_req_init) {
+		if (krln->lp_status == 0) {
+			/* nothing to do here for now...
+			 * perhaps the partner board LP has not yet started
+			 * so continue to send INIT requests
+			 * this will happen in the next condition anyway...
+			 */
+		}
+		/* 72.6.10.2.3.2 Initialize
+		 * The initialize control shall only be initially sent when all
+		 * coefficient status fields indicate not_updated,
+		 * and will then continue to be sent
+		 * until no coefficient status field indicates not_updated.
+		 */
+		if (status_cp1 == COEF_NOTUPDATED ||
+		    status_cz0 == COEF_NOTUPDATED ||
+		    status_cm1 == COEF_NOTUPDATED) {
+			krln->ld_update = INIT_MASK;
+			ld_coef_update(krln);
+			krln->req_ld_update_init_count++;
+		} else {
+			/* IEEE802.3-2008, 72.6.10.2.3.2
+			 * We may clear INITIALIZE when no coefficients
+			 * show NOT UPDATED.
+			 */
+			/* v1: krln->ld_update &= ~INIT_MASK; */
+			/* better send request: HOLD ALL
+			 * should be equivalent since only INIT is set now
+			 */
+			krln->ld_update = HOLD;
+
+			lp_resp_time = jiffies_to_msecs(jiffies) -
+					       krln->init_handshake_time;
+			if (!krln->first_recv_init) {
+				/* Init handshake not done yet,
+				 * but will be soon
+				 */
+				krln->req_ld_update_init_count = 1;
+				lp_resp_time = 0;
+			}
+			ld_coef_update(krln);
+		}
+		return;
+	}
+
+	/* 72.6.10.2.3.1 Preset
+	 * The preset control shall only be initially sent when all coefficient
+	 * status fields indicate not_updated,
+	 * and will then continue to be sent until the status for all
+	 * coefficients indicates updated or maximum
+	 */
+	/* IEEE802.3-2008, 72.6.10.2.3.1
+	 * We may clear PRESET when all coefficients show UPDATED or MAX.
+	 */
+	/* check if previous request was preset */
+	if (prev_req_preset) {
+		if ((status_cp1 == COEF_UPDATED || status_cp1 == COEF_MAX) &&
+		    (status_cz0 == COEF_UPDATED || status_cz0 == COEF_MAX) &&
+		    (status_cm1 == COEF_UPDATED || status_cm1 == COEF_MAX)) {
+			krln->ld_update &= ~PRESET_MASK;
+		} else {
+			/* All status MUST be NOTUPDATED for INIT to be executed
+			 * otherwise send HOLD first
+			 */
+			if (status_cp1 == COEF_NOTUPDATED &&
+			    status_cz0 == COEF_NOTUPDATED &&
+			    status_cm1 == COEF_NOTUPDATED) {
+				krln->ld_update = PRESET_MASK;
+			} else {
+				/* send HOLD before sending subsequent
+				 * Preset requests
+				 */
+				krln->ld_update = HOLD;
+			}
+			ld_coef_update(krln);
+			return;
+		}
+	}
+
+	/* IEEE802.3-2008, 72.6.10.2.3.3
+	 * We only request coefficient updates when no PRESET/INITIALIZE is
+	 * pending. We also only request coefficient updates when the
+	 * corresponding status is NOT UPDATED and nothing is pending.
+	 */
+	if (krln->ld_update & (PRESET_MASK | INIT_MASK))
+		return;
+
+	/* continue to move back to previous request until LP responds to it
+	 * Move back to previous C(-1), C(0), C(+1) and HOLD
+	 */
+	if (krln->move_back_prev) {
+		/* can exit from here only with: DONE Training */
+		if (krln->move_back_cnt == TIMEOUT_MOVE_BACK_PREV) {
+			trst->done_training = true;
+			training_complete(krln);
+			return;
+		}
+		krln->move_back_cnt++;
+
+		if (status_cp1 == COEF_UPDATED)
+			krln->move_back_lp_status |=
+						(COEF_UPDATED << COP1_SHIFT);
+		if (status_cz0 == COEF_UPDATED)
+			krln->move_back_lp_status |=
+						(COEF_UPDATED << COZ0_SHIFT);
+		if (status_cm1 == COEF_UPDATED)
+			krln->move_back_lp_status |=
+						(COEF_UPDATED << COM1_SHIFT);
+
+		if ((krln->move_back_lp_status & ALL_COEF_MASK) ==
+						LP_STATUS_ALL_COEF_UPDATED) {
+			trst->done_training = true;
+			training_complete(krln);
+			return;
+		}
+
+		/* Move back to previous C(-1), C(0), C(+1) */
+		krln->ld_update = krln->prev_ld_update;
+		ld_coef_update(krln);
+		return;
+	}
+
+	/* 72.6.10.2.5 Coefficient update process
+	 * Once the updated, maximum, or minimum state is reported it continues
+	 * to be reported until a hold request is received,
+	 * after which the status reverts to not_updated.
+	 */
+
+	/* IEEE802.3-2008, 72.6.10.2.3.3
+	 * We set coefficient requests to HOLD when we get the information
+	 * about any updates On clearing our prior response, we also update
+	 * our internal status.
+	 */
+
+	/* send a Hold if want to send another INC same as previous
+	 * and received status: NOTUPDATED
+	 * 1. Continue to send previous REQ until receive status UPDATED
+	 * 2. Continue to send HOLD until receive status NOTUPDATED
+	 */
+
+	/* 3. LP can remain stuck ~42 ms in reset Rx lane: so we should wait
+	 * around ~50 ms and only after that issue Timeout error message
+	 */
+
+	switch (prev_req_cp1) {
+	case HOLD:
+		/* previous request was: HOLD */
+		if (status_cp1 == COEF_NOTUPDATED) {
+			/* All good here:
+			 * continue to check the other coefficient requests
+			 * and if all are good then proceed to
+			 * generate coefficient tuning requests
+			 */
+		} else {
+			/* Continue to send the same request: (2.)
+			 * Continue to send HOLD until receive status NOTUPDATED
+			 */
+			if (krln->repeat_request_count >=
+					TIMEOUT_REPEAT_REQUEST) {
+				bpdev_err(krln->bpphy,
+					  "REQ Timeout: Repeating C(+1) HOLD request without LP response timeout !\n");
+				/* Hold Request Timeout:
+				 * continue to send HOLD until LP responds
+				 * with NOTUPDATED
+				 */
+				krln->repeat_request_count = 0;
+			} else {
+				/* Allow LP some time to respond
+				 * and repeat request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond,
+				 * as the last chance, on the last time
+				 * before issuing timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+			}
+			ld_coef_update(krln);
+			return;
+		}
+		break;
+	case INCREMENT:
+	case DECREMENT:
+		/* previous request was: INC/DEC */
+		if (status_cp1 == COEF_NOTUPDATED) {
+			/* Continue to send the same request: (1.)
+			 * Continue to send previous REQ
+			 * until receive status UPDATED
+			 */
+			if (krln->repeat_request_count >=
+					TIMEOUT_REPEAT_REQUEST) {
+				if (prev_req_cp1 == INCREMENT)
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(+1) INC request without LP response timeout !\n");
+				else
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(+1) DEC request without LP response timeout !\n");
+				/* Request Timeout:
+				 * just continue: proceed again to
+				 * generate coefficient tuning requests
+				 */
+			} else {
+				/* Allow LP some time to respond
+				 * and repeat request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond,
+				 * as the last chance,
+				 * on the last time before
+				 * issuing timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+				ld_coef_update(krln);
+				return;
+			}
+		} else {
+			/* All good here:
+			 * LP responded to this Request
+			 * Sent HOLD for this coefficient
+			 * before asking another request
+			 * continue to check the other coefficient requests
+			 */
+			krln->ld_update &= ~COP1_MASK;
+		}
+		break;
+	default:
+		/* previous request was: RESERVED: do nothing */
+		break;
+	}
+
+	switch (prev_req_cz0) {
+	case HOLD:
+		/* previous request was: HOLD */
+		if (status_cz0 == COEF_NOTUPDATED) {
+			/* All good here:
+			 * continue to check the other coefficient requests
+			 * and if all are good then proceed to
+			 * generate coefficient tuning requests
+			 */
+		} else {
+			/* Continue to send the same request: (2.)
+			 * Continue to send HOLD until receive status NOTUPDATED
+			 */
+			if (krln->repeat_request_count >=
+					TIMEOUT_REPEAT_REQUEST) {
+				bpdev_err(krln->bpphy,
+					  "REQ Timeout: Repeating C(0) HOLD request without LP response timeout !\n");
+				/* Hold Request Timeout:
+				 * continue to send HOLD until LP responds
+				 * with NOTUPDATED
+				 */
+				krln->repeat_request_count = 0;
+			} else {
+				/* Allow LP some time to respond
+				 * and repeat request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond,
+				 * as the last chance,
+				 * on the last time before issuing
+				 * timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+			}
+			ld_coef_update(krln);
+			return;
+		}
+		break;
+	case INCREMENT:
+	case DECREMENT:
+		/* previous request was: INC/DEC */
+		if (status_cz0 == COEF_NOTUPDATED) {
+			/* Continue to send the same request: (1.)
+			 * Continue to send previous REQ until receive
+			 * status UPDATED
+			 */
+			if (krln->repeat_request_count >=
+					TIMEOUT_REPEAT_REQUEST) {
+				if (prev_req_cz0 == INCREMENT)
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(0) INC request without LP response timeout !\n");
+				else
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(0) DEC request without LP response timeout !\n");
+				/* Request Timeout:
+				 * just continue: proceed again to
+				 * generate coefficient tuning requests
+				 */
+			} else {
+				/* Allow LP some time to respond
+				 * and repeat request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond, as the last
+				 * chance, on the last time before issuing
+				 * timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+				ld_coef_update(krln);
+				return;
+			}
+		} else {
+			/* All good here:
+			 * LP responded to this Request
+			 * Sent HOLD for this coefficient
+			 * before asking another request
+			 * continue to check the other coefficient requests
+			 */
+			krln->ld_update &= ~COZ0_MASK;
+		}
+		break;
+	default:
+		/* previous request was: RESERVED: do nothing */
+		break;
+	}
+
+	switch (prev_req_cm1) {
+	case HOLD:
+		/* previous request was: HOLD */
+		if (status_cm1 == COEF_NOTUPDATED) {
+			/* All good here:
+			 * continue to check the other coefficient requests
+			 * and if all are good then proceed to
+			 * generate coefficient tuning requests
+			 */
+		} else {
+			/* Continue to send the same request: (2.)
+			 * Continue to send HOLD until receive status
+			 * NOTUPDATED
+			 */
+			if (krln->repeat_request_count >=
+					TIMEOUT_REPEAT_REQUEST) {
+				bpdev_err(krln->bpphy,
+					  "REQ Timeout: Repeating C(-1) HOLD request without LP response timeout !\n");
+				/* Hold Request Timeout:
+				 * continue to send HOLD until
+				 * LP responds with NOTUPDATED
+				 */
+				krln->repeat_request_count = 0;
+			} else {
+				/* Allow LP some time to respond
+				 * and repeat request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond,
+				 * as the last chance,
+				 * on the last time
+				 * before issuing timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+			}
+			ld_coef_update(krln);
+			return;
+		}
+		break;
+	case INCREMENT:
+	case DECREMENT:
+		/* previous request was: INC/DEC */
+		if (status_cm1 == COEF_NOTUPDATED) {
+			/* Continue to send the same request: (1.)
+			 * Continue to send previous REQ until receive status
+			 * UPDATED
+			 */
+			if (krln->repeat_request_count >=
+						TIMEOUT_REPEAT_REQUEST) {
+				if (prev_req_cm1 == INCREMENT)
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(-1) INC request without LP response timeout !\n");
+				else
+					bpdev_err(krln->bpphy,
+						  "REQ Timeout: Repeating C(-1) DEC request without LP response timeout !\n");
+				/* Request Timeout:
+				 * just continue: proceed again to
+				 * generate coefficient tuning requests
+				 */
+			} else {
+				/* Allow LP some time to respond and repeat
+				 * request
+				 */
+				msleep(20);
+				/* Allow LP more time to respond, as the last
+				 * chance, on the last time before issuing
+				 * timeout error: (3.)
+				 */
+				if (krln->repeat_request_count ==
+						TIMEOUT_REPEAT_REQUEST - 1)
+					msleep(30);
+				krln->repeat_request_count++;
+				ld_coef_update(krln);
+				return;
+			}
+		} else {
+			/* All good here:
+			 * LP responded to this Request
+			 * Sent HOLD for this coefficient
+			 * before asking another request
+			 * continue to check the other coefficient requests
+			 */
+			krln->ld_update &= ~COM1_MASK;
+		}
+		break;
+	default:
+		/* previous request was: RESERVED: do nothing */
+		break;
+	}
+
+	/* Reset repeat request counter:
+	 * must be after all prev_req verifications above
+	 */
+	krln->repeat_request_count = 0;
+
+	if (krln->prev_ld_update != krln->ld_update) {
+		ld_coef_update(krln);
+		/* Redo these status checks and updates until we have no more
+		 * changes, to speed up the overall process.
+		 */
+		return;
+	}
+
+	/* Do nothing if we have pending request. */
+	if (prev_req_cp1 || prev_req_cz0 || prev_req_cm1)
+		return;
+	else if (krln->lp_status & ALL_COEF_MASK)
+		/* No pending request but LP status was not reverted to
+		 * not updated.
+		 */
+		return;
+
+	/* Initialize status for the current step */
+	krln->lt_error = false;
+
+	/* if CDR_LOCK = 0: Statistics are invalid */
+	if (!backplane_is_cdr_lock(krln, true)) {
+		if (krln->eq_alg->ops.process_bad_state)
+			krln->eq_alg->ops.process_bad_state(krln->eq_priv);
+		return;
+	}
+
+	/* collect bit edge statistics */
+	if (!krln->eq_alg->ops.collect_statistics(krln->eq_priv))
+		return;
+
+	/* if CDR_LOCK = 0: Statistics are invalid */
+	if (!backplane_is_cdr_lock(krln, true)) {
+		if (krln->eq_alg->ops.process_bad_state)
+			krln->eq_alg->ops.process_bad_state(krln->eq_priv);
+		return;
+	}
+
+	/* Check Rx */
+	if (!krln->eq_alg->ops.is_rx_ok(krln->eq_priv)) {
+		if (krln->eq_alg->ops.process_bad_state)
+			krln->eq_alg->ops.process_bad_state(krln->eq_priv);
+		return;
+	}
+	krln->eq_alg->ops.generate_request(krln->eq_priv);
+
+	/* All C are in Hold and both Bins are stopped:
+	 * So the Training is done
+	 */
+	if (krln->eq_alg->ops.is_eq_done(krln->eq_priv)) {
+		trst->done_training = true;
+		training_complete(krln);
+	}
+}
+
+/* Training for Local Tx
+ * Initialize LD (Local Device)
+ */
+void lt_init_ld(struct kr_lane_info *krln)
+{
+	/* report initial ld status to lp */
+	krln->ld_status = 0;
+	ld_coef_status(krln);
+}
+
+/* Training for Local Tx
+ * This is the main routine for Local Tx training
+ */
+void lt_train_local_tx(struct kr_lane_info *krln)
+{
+	struct training_status *trst = &krln->trst;
+	int request, old_ld_status;
+
+	/* Check stop condition for Local Tx training */
+	trst->lp_rx_ready = lt_is_lp_rx_ready(krln);
+	if (trst->lp_rx_ready) {
+		/* LP receiver is ready
+		 * As soon as the LP shows ready,
+		 * no need to do any more updates.
+		 */
+		krln->ld_status &= ~ALL_COEF_MASK;
+		ld_coef_status(krln);
+
+		trst->local_tx_running = false;
+		return;
+	}
+
+	/* Start new Local Tx training step */
+	trst->local_tx_running = true;
+
+	/* get request from LP */
+	request = backplane_read_mmd(krln, krln->bp_phy->bp_dev.mdio.lt_devad,
+				     krln->bp_phy->bp_dev.mdio.lt_kr_lp_cu) &
+					LD_ALL_MASK;
+
+	old_ld_status = krln->ld_status;
+
+	/* IEEE802.3-2008, 72.6.10.2.5
+	 * Ensure we always go to NOT UDPATED for status reporting in
+	 * response to HOLD requests.
+	 * IEEE802.3-2008, 72.6.10.2.3.1/2
+	 * ... but only if PRESET/INITIALIZE are not active to ensure
+	 * we keep status until they are released.
+	 *
+	 * 72.6.10.2.5 Coefficient update process
+	 * Once the updated, maximum, or minimum state is reported it continues
+	 * to be reported until a hold request is received,
+	 * after which the status reverts to not_updated.
+	 */
+	if (!(request & (PRESET_MASK | INIT_MASK))) {
+		/* Reset status on HOLD request */
+		if (!(request & COP1_MASK))
+			krln->ld_status &= ~COP1_MASK;
+
+		if (!(request & COZ0_MASK))
+			krln->ld_status &= ~COZ0_MASK;
+
+		if (!(request & COM1_MASK))
+			krln->ld_status &= ~COM1_MASK;
+
+		ld_coef_status(krln);
+	}
+
+	/* IEEE802.3-2008, 72.6.10.2.3.1/2
+	 * only act on PRESET/INITIALIZE if all status is NOT UPDATED.
+	 */
+	if (request & (PRESET_MASK | INIT_MASK)) {
+		if (!(krln->ld_status & ALL_COEF_MASK)) {
+			if (request & PRESET_MASK)
+				preset(krln);
+
+			if (request & INIT_MASK) {
+				if (!krln->first_recv_init) {
+					krln->first_recv_init = true;
+					/* Init requests must be counted
+					 * from initial handshake
+					 */
+					krln->req_ld_update_init_count = 1;
+					krln->init_handshake_time =
+						jiffies_to_msecs(jiffies);
+				}
+				initialize(krln);
+			}
+		} else {
+			/* Inform the partner about current ld status
+			 * which should be: ALL UPDATED for INIT  and
+			 * ALL MAX for PRESET
+			 */
+			ld_coef_status(krln);
+		}
+	}
+
+	/* check if LP Coefficient are not in HOLD */
+	if (request & ALL_COEF_MASK)
+		check_request(krln, request & ALL_COEF_MASK);
+
+	/* Make sure the partner is always informed about the current ld status
+	 * this will ensure avoidance of several training issues and errors:
+	 *   'link_training_failed'
+	 *   'Repeating request without LP response'
+	 */
+	ld_coef_status(krln);
+}
+
+/* Training for Remote Tx API */
+
+/* lt_lp_update
+ *
+ * Sends to LP the specified request for coefficients update
+ *
+ * krln: desired lane for which to send lp update
+ * update: desired update request to be sent to LP
+ *
+ * Returns: None
+ */
+void lt_lp_update(struct kr_lane_info *krln, u32 update)
+{
+	krln->ld_update = update;
+	ld_coef_update(krln);
+}
+EXPORT_SYMBOL(lt_lp_update);
+
+/* lt_encode_request
+ *
+ * Encodes a request in the update word
+ * and adds it to other bit requests already existent in the update word
+ *
+ * base_update: base update word used to add a new desired request
+ * req: desired request type to be encoded
+ * field: the field for which the request must be encoded
+ *
+ * Returns: the encoded update word
+ */
+u32 lt_encode_request(u32 base_update, enum req_type req,
+		      enum coef_field field)
+{
+	u32 new_cmd = base_update;
+	u32 cmd;
+
+	if (req >= REQ_INIT)
+		return RESERVED;
+
+	cmd = get_mask_for_req(req);
+
+	switch (field) {
+	case C_P1:
+		new_cmd |= (cmd << COP1_SHIFT);
+		break;
+	case C_Z0:
+		new_cmd |= (cmd << COZ0_SHIFT);
+		break;
+	case C_M1:
+		new_cmd |= (cmd << COM1_SHIFT);
+		break;
+	default:
+		return RESERVED;
+	}
+	return new_cmd;
+}
+EXPORT_SYMBOL(lt_encode_request);
+
+/* lt_encode_startup_request
+ *
+ * Encodes a startup request in the update word
+ *
+ * req: desired startup request type to be encoded
+ *
+ * Returns: the encoded update word
+ */
+u32 lt_encode_startup_request(enum req_type req)
+{
+	if (req == REQ_HOLD || req == REQ_INIT || req == REQ_PRESET)
+		return get_mask_for_req(req);
+
+	return RESERVED;
+}
+EXPORT_SYMBOL(lt_encode_startup_request);
+
+/* lt_decode_coef_update
+ *
+ * Decodes a request update for the specified field
+ *
+ * update: update word to be decoded
+ * field: desired field for which to decode the update
+ *
+ * Returns: the decoded request type
+ */
+enum req_type lt_decode_coef_update(u32 update, enum coef_field field)
+{
+	u32 cmd = HOLD;
+
+	switch (field) {
+	case C_P1:
+		cmd = (update & COP1_MASK) >> COP1_SHIFT;
+		break;
+	case C_Z0:
+		cmd = (update & COZ0_MASK) >> COZ0_SHIFT;
+		break;
+	case C_M1:
+		cmd = (update & COM1_MASK) >> COM1_SHIFT;
+		break;
+	default:
+		return REQ_INVALID;
+	}
+
+	return get_req_for_mask(cmd);
+}
+EXPORT_SYMBOL(lt_decode_coef_update);
+
+/* lt_is_update_of_type
+ *
+ * Checks if a request update is according to the specified type
+ * by checking the specific request bit in update word
+ *
+ * update: desired update word to be verified
+ * type: desired type to check against
+ *
+ * Returns: true if update is according to asked type or false otherwise
+ */
+bool lt_is_update_of_type(u32 update, enum req_type type)
+{
+	u32 mask = HOLD;
+
+	switch (type) {
+	case REQ_HOLD:
+		return (update == HOLD);
+	case REQ_INC:
+		mask |= (INCREMENT << COP1_SHIFT);
+		mask |= (INCREMENT << COZ0_SHIFT);
+		mask |= (INCREMENT << COM1_SHIFT);
+		return ((update & mask) != 0);
+	case REQ_DEC:
+		mask |= (DECREMENT << COP1_SHIFT);
+		mask |= (DECREMENT << COZ0_SHIFT);
+		mask |= (DECREMENT << COM1_SHIFT);
+		return ((update & mask) != 0);
+	case REQ_INIT:
+		return ((update & INIT_MASK) != 0);
+	case REQ_PRESET:
+		return ((update & PRESET_MASK) != 0);
+	default:
+		return false;
+	}
+	return false;
+}
+EXPORT_SYMBOL(lt_is_update_of_type);
+
+/* lt_is_lp_at_startup
+ *
+ * Checks if LP status is still at startup status: INIT or PRESET
+ *
+ * krln: desired lane to be verified
+ * req: request type to check startup status
+ *	it makes sense only for INIT or PRESET requests
+ *
+ * Returns: true if LP status is still at startup status or false otherwise
+ */
+bool lt_is_lp_at_startup(struct kr_lane_info *krln, enum req_type type)
+{
+	u32 lp_st = krln->lp_status;
+	u32 lp_lcs = get_lp_lcs(krln);
+	bool lp_startup;
+
+	/* LP status still at Init/Preset:
+	 * IF now LP status is Init/Preset
+	 * OR (now LP status is NOTUPDATED
+	 * AND the last nonzero LP status was Init/Preset)
+	 */
+	switch (type) {
+	case REQ_INIT:
+		if (is_all_status(lp_st, COEF_UPDATED))
+			lp_startup = true;
+		else
+			lp_startup = is_all_status(lp_st, COEF_NOTUPDATED) &&
+					is_all_status(lp_lcs, COEF_UPDATED);
+		break;
+	case REQ_PRESET:
+		/* LP status still at Preset
+		 * if now LP status is Preset
+		 * OR now LP status is NOTUPDATED
+		 *    AND the last nonzero LP status was Preset
+		 */
+		if (is_all_status(lp_st, COEF_MAX) ||
+		    is_all_status(lp_st, COEF_UPDATED))
+			lp_startup = true;
+		else
+			lp_startup = is_all_status(lp_st, COEF_NOTUPDATED) &&
+					(is_all_status(lp_lcs, COEF_MAX) ||
+					 is_all_status(lp_lcs, COEF_UPDATED));
+		break;
+	default:
+		return false;
+	}
+
+	return lp_startup;
+}
+EXPORT_SYMBOL(lt_is_lp_at_startup);
+
+/* lt_get_lp_coef_status
+ *
+ * Determines the last LP coefficient status
+ * according to IEEE802.3ap-2007:
+ * 72.6.10.2.5 Coefficient update process
+ *
+ * krln: desired lane to be verified
+ * field: coefficient field to be verified
+ *
+ * Returns: the last LP coefficient status
+ */
+enum coef_status lt_get_lp_coef_status(struct kr_lane_info *krln,
+				       enum coef_field field)
+{
+	return krln->last_lp_update_status[field];
+}
+EXPORT_SYMBOL(lt_get_lp_coef_status);
+
+/* lt_set_error
+ *
+ * Sets or resets the LT (Link Training) Error flag
+ * This is used to signal to the generic kr training step procedure
+ * that an LT error state has occurred
+ * and link training cannot be successfully finished
+ *
+ * krln: desired lane to set lt error
+ * err: boolean value that specifies if set or reset the error flag
+ *
+ * Returns: None
+ */
+void lt_set_error(struct kr_lane_info *krln, bool err)
+{
+	krln->lt_error = err;
+}
+EXPORT_SYMBOL(lt_set_error);
+
+/* lt_move_lp_back
+ * Request LP to move back to previous coefficients setup and HOLD
+ * The procedure for sending this request is based on reverting the
+ * latest change request (non-hold update) for all coefficients
+ * This procedure should be used to exit from bad states like not CDR_Lock
+ *
+ * krln: desired lane for which to send lp update
+ *
+ * Returns: None
+ */
+void lt_move_lp_back(struct kr_lane_info *krln)
+{
+	u32 prev_req_cp1 = (krln->ld_last_nonhold_update & COP1_MASK) >>
+				COP1_SHIFT;
+	u32 prev_req_cz0 = (krln->ld_last_nonhold_update & COZ0_MASK) >>
+				COZ0_SHIFT;
+	u32 prev_req_cm1 = (krln->ld_last_nonhold_update & COM1_MASK) >>
+				COM1_SHIFT;
+	u32 temp;
+
+	/* Move back to previous C(-1), C(0), C(+1) and HOLD */
+	temp = HOLD;
+	switch (prev_req_cp1) {
+	case INCREMENT:
+		temp |= DECREMENT << COP1_SHIFT;
+		break;
+	case DECREMENT:
+		temp |= INCREMENT << COP1_SHIFT;
+		break;
+	}
+	switch (prev_req_cz0) {
+	case INCREMENT:
+		temp |= DECREMENT << COZ0_SHIFT;
+		break;
+	case DECREMENT:
+		temp |= INCREMENT << COZ0_SHIFT;
+		break;
+	}
+	switch (prev_req_cm1) {
+	case INCREMENT:
+		temp |= DECREMENT << COM1_SHIFT;
+		break;
+	case DECREMENT:
+		temp |= INCREMENT << COM1_SHIFT;
+		break;
+	}
+
+	krln->ld_update = temp;
+	ld_coef_update(krln);
+
+	/* start the procedure for sending request to move LP back
+	 * to previous setup until LP responds to it
+	 */
+	krln->move_back_prev = true;
+	krln->move_back_cnt = 0;
+	krln->move_back_lp_status = 0;
+	if (prev_req_cp1 == HOLD)
+		krln->move_back_lp_status |= (COEF_UPDATED << COP1_SHIFT);
+	if (prev_req_cz0 == HOLD)
+		krln->move_back_lp_status |= (COEF_UPDATED << COZ0_SHIFT);
+	if (prev_req_cm1 == HOLD)
+		krln->move_back_lp_status |= (COEF_UPDATED << COM1_SHIFT);
+}
+EXPORT_SYMBOL(lt_move_lp_back);
diff --git a/drivers/net/phy/backplane/link_training.h b/drivers/net/phy/backplane/link_training.h
new file mode 100644
index 0000000..54374aa
--- /dev/null
+++ b/drivers/net/phy/backplane/link_training.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* Link Training (IEEE802.3ap/ba)
+ *
+ * Copyright 2019-2020 NXP
+ */
+
+#ifndef __LINK_TRAINING_H
+#define __LINK_TRAINING_H
+
+#include "backplane.h"
+
+/* Link Training interface with backplane driver */
+
+void lt_setup_c45(struct backplane_dev_info *bp_dev);
+void lt_setup_memmap(struct backplane_dev_info *bp_dev, int devad, u32 base);
+
+void lt_start(struct kr_lane_info *krln);
+void lt_stop(struct kr_lane_info *krln);
+void lt_reset(struct kr_lane_info *krln);
+
+bool lt_is_rx_trained(struct kr_lane_info *krln);
+bool lt_is_training_failure(struct kr_lane_info *krln);
+bool lt_is_frame_lock(struct kr_lane_info *krln);
+
+bool lt_is_lp_rx_ready(struct kr_lane_info *krln);
+bool lt_is_ld_rx_ready(struct kr_lane_info *krln);
+
+void lt_init_ld(struct kr_lane_info *krln);
+void lt_start_an(struct kr_lane_info *krln);
+
+void lt_train_remote_tx(struct kr_lane_info *krln);
+void lt_train_local_tx(struct kr_lane_info *krln);
+
+#endif /* __LINK_TRAINING_H */
-- 
1.9.1


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

* [PATCH net-next 7/9] net: phy: enable qoriq backplane support
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (5 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 20:03   ` Joe Perches
  2020-03-26 13:51 ` [PATCH net-next 8/9] net: phy: add bee algorithm for kr training Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 9/9] arm64: dts: add serdes and mdio description Florinel Iordache
  8 siblings, 1 reply; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Enable backplane support for qoriq family of devices

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/phy/backplane/Kconfig            |  11 +-
 drivers/net/phy/backplane/Makefile           |   2 +
 drivers/net/phy/backplane/qoriq_backplane.c  | 442 ++++++++++++++++++++++
 drivers/net/phy/backplane/qoriq_backplane.h  |  33 ++
 drivers/net/phy/backplane/qoriq_serdes_10g.c | 470 +++++++++++++++++++++++
 drivers/net/phy/backplane/qoriq_serdes_28g.c | 533 +++++++++++++++++++++++++++
 6 files changed, 1490 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/phy/backplane/qoriq_backplane.c
 create mode 100644 drivers/net/phy/backplane/qoriq_backplane.h
 create mode 100644 drivers/net/phy/backplane/qoriq_serdes_10g.c
 create mode 100644 drivers/net/phy/backplane/qoriq_serdes_28g.c

diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig
index 9ec54b5..3e20a78 100644
--- a/drivers/net/phy/backplane/Kconfig
+++ b/drivers/net/phy/backplane/Kconfig
@@ -17,4 +17,13 @@ config ETH_BACKPLANE_FIXED
 	  This module provides a driver to setup fixed user configurable
 	  coefficient values for backplanes equalization. This means
 	  No Equalization algorithm is used to adapt the initial coefficients
-	  initially set by the user.
\ No newline at end of file
+	  initially set by the user.
+
+config ETH_BACKPLANE_QORIQ
+	tristate "QorIQ Ethernet Backplane driver"
+	depends on ETH_BACKPLANE
+	help
+	  This module provides a driver for Ethernet Operation over
+	  Electrical Backplanes enabled for QorIQ family of devices.
+	  This driver is using the services provided by the generic
+	  backplane and link training modules.
\ No newline at end of file
diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile
index ded6f2d..d8f95ac 100644
--- a/drivers/net/phy/backplane/Makefile
+++ b/drivers/net/phy/backplane/Makefile
@@ -5,5 +5,7 @@
 
 obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o
 obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o
+obj-$(CONFIG_ETH_BACKPLANE_QORIQ) += eth_backplane_qoriq.o
 
 eth_backplane-objs	:= backplane.o link_training.o
+eth_backplane_qoriq-objs	:= qoriq_backplane.o qoriq_serdes_10g.o qoriq_serdes_28g.o
diff --git a/drivers/net/phy/backplane/qoriq_backplane.c b/drivers/net/phy/backplane/qoriq_backplane.c
new file mode 100644
index 0000000..9a2cd4e
--- /dev/null
+++ b/drivers/net/phy/backplane/qoriq_backplane.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* QorIQ Backplane driver
+ *
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ * Copyright 2018-2020 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/mdio.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "qoriq_backplane.h"
+
+/* QorIQ Backplane Driver name */
+#define QORIQ_BACKPLANE_DRIVER_NAME		"backplane_qoriq"
+
+/* QorIQ Backplane Driver version */
+#define QORIQ_BACKPLANE_DRIVER_VERSION		"1.0.0"
+
+/* PCS Device Identifier */
+#define PCS_PHY_DEVICE_ID			0x0083e400
+#define PCS_PHY_DEVICE_ID_MASK			0xffffffff
+
+/* AN registers initialization */
+#define KR_AN_AD1_INIT_10G			0x85
+#define KR_AN_AD1_INIT_40G			0x105
+
+/* AN masks: Backplane Ethernet status (Register 7.48) */
+#define AN_MASK_10GBASE_KR			0x08
+#define AN_MASK_40GBASE_KR4			0x20
+
+/* Max/Min coefficients range values */
+#define PRE_COEF_MAX				0x0
+#define PRE_COEF_MIN				0x8
+#define POST_COEF_MAX				0x0
+#define POST_COEF_MIN				0x10
+#define ZERO_COEF_MIN				0x1A
+#define ZERO_COEF_MAX				0x30
+
+/* Coefficients sum ratio: (their sum divided by their difference) */
+#define COEF_SUM_RATIO_NUMERATOR		17
+#define COEF_SUM_RATIO_DENOMINATOR		4
+
+/* Number of equalization custom parameters */
+#define EQ_PARAMS_NO				1
+
+/* Serdes types supported by QorIQ devices */
+enum serdes_type {
+	SERDES_10G,
+	SERDES_28G,
+	SERDES_INVAL
+};
+
+/* Backplane Ethernet Status Register (an_bp_eth_status)
+ * chapter: 45.2.7.12 Backplane Ethernet status (Register 7.48)
+ *	- bit AN_MASK_10GBASE_KR for 10GBase-KR
+ *	- bit AN_MASK_40GBASE_KR4 for 40GBase-KR4
+ */
+static u32 get_an_bp_eth_status_bit(phy_interface_t mode)
+{
+	u32 an_mask = 0;
+
+	switch (mode) {
+	case PHY_INTERFACE_MODE_10GKR:
+		an_mask = AN_MASK_10GBASE_KR;
+		break;
+	case PHY_INTERFACE_MODE_40GKR4:
+		an_mask = AN_MASK_40GBASE_KR4;
+		break;
+	/* add AN support for other backplane modes here */
+	default:
+		an_mask = 0;
+		break;
+	}
+	return an_mask;
+}
+
+static u32 get_an_ad_ability_1_init(phy_interface_t mode)
+{
+	u32 init_value = 0;
+
+	switch (mode) {
+	case PHY_INTERFACE_MODE_10GKR:
+		init_value = KR_AN_AD1_INIT_10G;
+		break;
+	case PHY_INTERFACE_MODE_40GKR4:
+		init_value = KR_AN_AD1_INIT_40G;
+		break;
+	/* add AN support for other backplane modes here */
+	default:
+		init_value = 0;
+		break;
+	}
+	return init_value;
+}
+
+/* qoriq_backplane_probe
+ *
+ * Probe function for QorIQ backplane driver to provide QorIQ device specific
+ * behavior
+ *
+ * bpphy: backplane phy device
+ *	this is an internal phy block controlled by the software
+ *	which contains other component blocks like: PMA/PMD, PCS, AN
+ *
+ * Return: Zero for success or error code in case of failure
+ */
+static int qoriq_backplane_probe(struct phy_device *bpphy)
+{
+	static bool one_time_action = true;
+
+	if (one_time_action) {
+		one_time_action = false;
+		pr_info("%s: QorIQ Backplane driver version %s\n",
+			QORIQ_BACKPLANE_DRIVER_NAME,
+			QORIQ_BACKPLANE_DRIVER_VERSION);
+	}
+
+	/* call generic driver probe */
+	return backplane_probe(bpphy);
+}
+
+/* qoriq_backplane_config_init
+ *
+ * Config_Init function for QorIQ devices to provide QorIQ specific behavior
+ *
+ * bpphy: backplane phy device
+ *
+ * Return: Zero for success or error code in case of failure
+ */
+static int qoriq_backplane_config_init(struct phy_device *bpphy)
+{
+	struct backplane_phy_info *bp_phy = bpphy->priv;
+	struct device_node *bpphy_node, *serdes_node, *lane_node;
+	struct resource res;
+	const char *serdes_comp;
+	const struct lane_io_ops *lane_ops = NULL;
+	struct qoriq_lane_ops *qoriq_lane = NULL;
+	const struct equalizer_info *equalizer = NULL;
+	int comp_no, i, ret;
+	int serdes_type = SERDES_INVAL;
+	u32 eqparams[EQ_PARAMS_NO];
+	int proplen;
+
+	bpphy_node = bpphy->mdio.dev.of_node;
+	if (!bpphy_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return -EINVAL;
+	}
+
+	if (!bp_phy) {
+		bpdev_err(bpphy, "Backplane phy info is not allocated\n");
+		return -EINVAL;
+	}
+
+	if (!backplane_is_valid_mode(bpphy->interface))
+		return -EINVAL;
+
+	bp_phy->bp_mode = bpphy->interface;
+	bp_phy->num_lanes = backplane_num_lanes(bpphy->interface);
+
+	proplen = of_property_count_u32_elems(bpphy_node, "lane-handle");
+	if (proplen < bp_phy->num_lanes) {
+		bpdev_err(bpphy, "Unspecified lane handles\n");
+		return -EINVAL;
+	}
+	serdes_node = NULL;
+	for (i = 0; i < bp_phy->num_lanes; i++) {
+		lane_node = of_parse_phandle(bpphy_node, "lane-handle", i);
+		if (!lane_node) {
+			bpdev_err(bpphy, "parse lane-handle failed\n");
+			return -EINVAL;
+		}
+		if (i == 0)
+			serdes_node = lane_node->parent;
+		ret = of_address_to_resource(lane_node, 0, &res);
+		if (ret) {
+			bpdev_err(bpphy,
+				  "could not obtain lane memory map for index=%d, ret = %d\n",
+				  i, ret);
+			return ret;
+		}
+		/* setup lane address */
+		bp_phy->krln[i].lane_addr = res.start;
+
+		of_node_put(lane_node);
+	}
+	if (!serdes_node) {
+		bpdev_err(bpphy, "serdes node not found\n");
+		return -EINVAL;
+	}
+	bp_phy->bp_dev.is_little_endian = of_property_read_bool(serdes_node,
+								"little-endian");
+
+	ret = of_address_to_resource(serdes_node, 0, &res);
+	if (ret) {
+		bpdev_err(bpphy,
+			  "could not obtain serdes memory map, ret = %d\n",
+			  ret);
+		return ret;
+	}
+	bp_phy->bp_dev.base_addr = res.start;
+	bp_phy->bp_dev.memmap_size = res.end - res.start + 1;
+
+	comp_no = of_property_count_strings(serdes_node, "compatible");
+	for (i = 0; i < comp_no; i++) {
+		ret = of_property_read_string_index(serdes_node, "compatible",
+						    i, &serdes_comp);
+		if (ret == 0) {
+			if (!strcasecmp(serdes_comp, "serdes-10g")) {
+				serdes_type = SERDES_10G;
+				break;
+			} else if (!strcasecmp(serdes_comp, "serdes-28g")) {
+				serdes_type = SERDES_28G;
+				break;
+			}
+		}
+	}
+
+	if (serdes_type == SERDES_INVAL) {
+		bpdev_err(bpphy, "Unknown serdes-type\n");
+		return 0;
+	}
+
+	/* call generic driver parse DT */
+	ret = backplane_parse_dt(bpphy);
+	if (ret)
+		return ret;
+
+	/* call generic driver setup mdio */
+	ret = backplane_setup_mdio(bpphy);
+	if (ret)
+		return ret;
+
+	/* override default mdio setup and initialize lane ops */
+	switch (serdes_type) {
+	case SERDES_10G:
+		qoriq_setup_mdio_10g(&bp_phy->bp_dev);
+		lane_ops = qoriq_get_lane_ops_10g();
+		qoriq_setup_mem_io_10g(bp_phy->bp_dev.io);
+		equalizer = qoriq_get_equalizer_info_10g();
+		break;
+	case SERDES_28G:
+		qoriq_setup_mdio_28g(&bp_phy->bp_dev);
+		lane_ops = qoriq_get_lane_ops_28g();
+		qoriq_setup_mem_io_28g(bp_phy->bp_dev.io);
+		equalizer = qoriq_get_equalizer_info_28g();
+		break;
+	default:
+		bpdev_err(bpphy, "Serdes type not supported\n");
+		return -EINVAL;
+	}
+	if (!lane_ops) {
+		bpdev_err(bpphy, "Lane ops not available\n");
+		return -EINVAL;
+	}
+	if (!equalizer) {
+		bpdev_err(bpphy, "Equalizer not available\n");
+		return -EINVAL;
+	}
+
+	bp_phy->bp_dev.lane_ops = lane_ops;
+	bp_phy->bp_dev.equalizer = equalizer;
+	qoriq_lane = (struct qoriq_lane_ops *)lane_ops->priv;
+
+	/* install AN bp_eth_status decoding callback */
+	bp_phy->bp_dev.mdio.get_an_bp_eth_status_bit = get_an_bp_eth_status_bit;
+	bp_phy->bp_dev.mdio.get_an_ad_ability_1_init = get_an_ad_ability_1_init;
+
+	if (backplane_is_mode_kr(bp_phy->bp_mode)) {
+		/* setup coefficient limits */
+		bp_phy->bp_dev.cm_min = PRE_COEF_MIN;
+		bp_phy->bp_dev.cm_max = PRE_COEF_MAX;
+		bp_phy->bp_dev.cz_min = ZERO_COEF_MIN;
+		bp_phy->bp_dev.cz_max = ZERO_COEF_MAX;
+		bp_phy->bp_dev.cp_min = POST_COEF_MIN;
+		bp_phy->bp_dev.cp_max = POST_COEF_MAX;
+		bp_phy->bp_dev.sum_ratio_numer = COEF_SUM_RATIO_NUMERATOR;
+		bp_phy->bp_dev.sum_ratio_denom = COEF_SUM_RATIO_DENOMINATOR;
+	}
+
+	/* if eq-params node exists then use the DTS specified values
+	 * if eq-params node doesn't exist then use values already found in HW
+	 * eq-params is a custom node and variable in size
+	 */
+	proplen = of_property_count_u32_elems(bpphy_node, "eq-params");
+	if (proplen > 0) {
+		/* we use only 1 custom coefficient tap: amp_red */
+		if (proplen > EQ_PARAMS_NO)
+			proplen = EQ_PARAMS_NO;
+		ret = of_property_read_u32_array(bpphy_node, "eq-params",
+						 (u32 *)eqparams, proplen);
+		if (ret == 0) {
+			bp_phy->bp_dev.ampr_def_dt = true;
+			bp_phy->bp_dev.amp_red_def = eqparams[0];
+		}
+	}
+
+	/* call generic driver setup lanes */
+	ret = backplane_setup_lanes(bpphy);
+	if (ret)
+		return ret;
+
+	/* call generic driver initialize
+	 * start the lane timers used to run the algorithm
+	 */
+	ret = backplane_initialize(bpphy);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int qoriq_backplane_match_phy_device(struct phy_device *bpphy)
+{
+	struct device_node *bpphy_node, *serdes_node, *lane_node;
+	const char *serdes_comp;
+	int comp_no, i, ret;
+	int serdes_type = SERDES_INVAL;
+
+	if (!bpphy->mdio.dev.of_node)
+		return 0;
+
+	if (!bpphy->is_c45)
+		return 0;
+
+	bpphy_node = bpphy->mdio.dev.of_node;
+	if (!bpphy_node) {
+		bpdev_err(bpphy, "No associated device tree node\n");
+		return 0;
+	}
+
+	/* Get Master lane node */
+	lane_node = of_parse_phandle(bpphy_node, "lane-handle", 0);
+	if (!lane_node)
+		return 0;
+	serdes_node = lane_node->parent;
+	of_node_put(lane_node);
+	if (!serdes_node)
+		return 0;
+
+	comp_no = of_property_count_strings(serdes_node, "compatible");
+	for (i = 0; i < comp_no; i++) {
+		ret = of_property_read_string_index(serdes_node, "compatible",
+						    i, &serdes_comp);
+		if (ret == 0) {
+			if (!strcasecmp(serdes_comp, "serdes-10g")) {
+				serdes_type = SERDES_10G;
+				break;
+			} else if (!strcasecmp(serdes_comp, "serdes-28g")) {
+				serdes_type = SERDES_28G;
+				break;
+			}
+		}
+	}
+
+	if (serdes_type == SERDES_INVAL) {
+		bpdev_err(bpphy, "Unknown serdes-type\n");
+		return 0;
+	}
+
+	switch (serdes_type) {
+	case SERDES_10G:
+		/* On LS devices we must find the c45 device with correct PHY ID
+		 * Implementation similar with the one existent in phy_device:
+		 * @function: phy_bus_match
+		 */
+		for (i = 1; i < ARRAY_SIZE(bpphy->c45_ids.device_ids); i++) {
+			if (!(bpphy->c45_ids.devices_in_package & (1 << i)))
+				continue;
+
+			if ((PCS_PHY_DEVICE_ID & PCS_PHY_DEVICE_ID_MASK) ==
+			    (bpphy->c45_ids.device_ids[i] &
+			     PCS_PHY_DEVICE_ID_MASK))
+				return 1;
+		}
+		break;
+	case SERDES_28G:
+		/*	 WORKAROUND:
+		 * Required for LX2 devices
+		 * where PHY ID cannot be verified in PCS
+		 * because PCS Device Identifier Upper and Lower registers are
+		 * hidden and always return 0 when they are read:
+		 * 2  02	Device_ID0  RO		Bits 15:0	0
+		 * val = phy_read_mmd(bpphy, MDIO_MMD_PCS, 0x2);
+		 * 3  03	Device_ID1  RO		Bits 31:16	0
+		 * val = phy_read_mmd(bpphy, MDIO_MMD_PCS, 0x3);
+		 *
+		 * To be removed: After the issue will be fixed on LX2 devices
+		 */
+
+		/* On LX devices we cannot verify PHY ID
+		 * phy id because registers are hidden
+		 * so we are happy only with preliminary verifications
+		 * already made: mdio.dev.of_node, is_c45
+		 * and lane-handle with valid serdes parent
+		 * because we already filtered other undesired devices:
+		 * non clause 45
+		 */
+		return 1;
+	default:
+		bpdev_err(bpphy, "Unknown serdes-type\n");
+		return 0;
+	}
+	return 0;
+}
+
+static struct phy_driver qoriq_backplane_driver[] = {
+	{
+	.phy_id		= PCS_PHY_DEVICE_ID,
+	.name		= QORIQ_BACKPLANE_DRIVER_NAME,
+	.phy_id_mask	= PCS_PHY_DEVICE_ID_MASK,
+	.features       = BACKPLANE_FEATURES,
+	.probe          = qoriq_backplane_probe,
+	.remove         = backplane_remove,
+	.config_init    = qoriq_backplane_config_init,
+	.aneg_done      = backplane_aneg_done,
+	.config_aneg	= backplane_config_aneg,
+	.read_status	= backplane_read_status,
+	.suspend	= backplane_suspend,
+	.resume		= backplane_resume,
+	.match_phy_device = qoriq_backplane_match_phy_device,
+	},
+};
+
+module_phy_driver(qoriq_backplane_driver);
+
+static struct mdio_device_id __maybe_unused qoriq_backplane_tbl[] = {
+	{ PCS_PHY_DEVICE_ID, PCS_PHY_DEVICE_ID_MASK },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, qoriq_backplane_tbl);
+
+MODULE_DESCRIPTION("QorIQ Backplane driver");
+MODULE_AUTHOR("Florinel Iordache <florinel.iordache@nxp.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/phy/backplane/qoriq_backplane.h b/drivers/net/phy/backplane/qoriq_backplane.h
new file mode 100644
index 0000000..bbfdfb5
--- /dev/null
+++ b/drivers/net/phy/backplane/qoriq_backplane.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* QorIQ Backplane driver
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#ifndef __QORIQ_BACKPLANE_H
+#define __QORIQ_BACKPLANE_H
+
+#include "backplane.h"
+
+/* Bins thresholds */
+#define QORIQ_BIN_M1_THRESHOLD			3
+#define QORIQ_BIN_LONG_THRESHOLD		2
+
+struct qoriq_lane_ops {
+	u32 (*read_tecr0)(void *reg);
+	u32 (*read_tecr1)(void *reg);
+};
+
+const struct lane_io_ops *qoriq_get_lane_ops_10g(void);
+const struct lane_io_ops *qoriq_get_lane_ops_28g(void);
+
+const struct equalizer_info *qoriq_get_equalizer_info_10g(void);
+const struct equalizer_info *qoriq_get_equalizer_info_28g(void);
+
+void qoriq_setup_mem_io_10g(struct mem_io_ops io);
+void qoriq_setup_mem_io_28g(struct mem_io_ops io);
+
+void qoriq_setup_mdio_10g(struct backplane_dev_info *bp_dev);
+void qoriq_setup_mdio_28g(struct backplane_dev_info *bp_dev);
+
+#endif /* __QORIQ_BACKPLANE_H */
diff --git a/drivers/net/phy/backplane/qoriq_serdes_10g.c b/drivers/net/phy/backplane/qoriq_serdes_10g.c
new file mode 100644
index 0000000..8ee7308
--- /dev/null
+++ b/drivers/net/phy/backplane/qoriq_serdes_10g.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* QorIQ Backplane driver for SerDes 10G
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include "qoriq_backplane.h"
+
+#define EQUALIZER_NAME				"qoriq_serdes_10g"
+#define EQUALIZER_VERSION			"1.0.0"
+
+#define BIN_1_SEL				0x00000000
+#define BIN_2_SEL				0x00010000
+#define BIN_3_SEL				0x00020000
+#define BIN_OFFSET_SEL				0x00030000
+#define BIN_BLW_SEL				0x00040000
+#define BIN_AVG_SEL				0x00050000
+#define BIN_M1_SEL				0x00060000
+#define BIN_LONG_SEL				0x00070000
+#define CDR_SEL_MASK				0x00070000
+
+#define RATIO_PREQ_SHIFT			22
+#define RATIO_PST1Q_SHIFT			16
+#define ADPT_EQ_SHIFT				8
+#define AMP_RED_SHIFT				0
+
+#define RATIO_PREQ_MASK				0x03c00000
+#define RATIO_PST1Q_MASK			0x001f0000
+#define ADPT_EQ_MASK				0x00003f00
+#define AMP_RED_MASK				0x0000003f
+
+#define TECR0_INIT				0x24200000
+
+#define GCR0_RESET_MASK				0x00600000
+#define GCR0_TRST_MASK				0x00200000
+#define GCR0_RRST_MASK				0x00400000
+
+#define GCR1_SNP_START_MASK			0x00000040
+#define GCR1_CTL_SNP_START_MASK			0x00002000
+
+#define RECR1_CTL_SNP_DONE_MASK			0x00000002
+#define RECR1_SNP_DONE_MASK			0x00000004
+#define TCSR1_SNP_DATA_MASK			0x00007fc0
+#define TCSR1_SNP_DATA_SHIFT			6
+#define TCSR1_EQ_SNPBIN_SIGN_MASK		0x100
+
+#define TCSR3_CDR_LCK_MASK			0x08000000
+
+#define RECR1_GAINK2_MASK			0x0f000000
+#define RECR1_GAINK2_SHIFT			24
+
+#define RECR1_GAINK3_MASK			0x000f0000
+#define RECR1_GAINK3_SHIFT			16
+
+#define RECR1_EQ_OFFSET_MASK			0x00001f80
+#define RECR1_EQ_OFFSET_SHIFT			7
+
+#define AN_AD_ABILITY_0				0x10
+#define AN_AD_ABILITY_1				0x11
+#define AN_BP_ETH_STATUS_OFFSET			0x30
+#define KR_PMD_BASE_OFFSET			0x96
+
+/* Bin snapshots thresholds range */
+#define EQ_BIN_MIN				-256
+#define EQ_BIN_MAX				255
+/* Bin snapshots average thresholds range */
+#define EQ_BIN_SNP_AV_THR_LOW			-150
+#define EQ_BIN_SNP_AV_THR_HIGH			150
+
+#define EQ_GAINK_MIN				0xF
+#define EQ_GAINK_MAX				0x0
+#define EQ_GAINK_MIDRANGE_LOW			0xE
+#define EQ_GAINK_MIDRANGE_HIGH			0x1
+
+#define EQ_OFFSET_MIN				0
+#define EQ_OFFSET_MAX				0x3F
+#define EQ_OFFSET_MIDRANGE_LOW			0x10
+#define EQ_OFFSET_MIDRANGE_HIGH			0x2F
+
+#define MEMORY_MAP_SIZE				0x40
+
+struct qoriq_lane_regs {
+	u32 gcr0;	/* 0x00: General Control Register 0 */
+	u32 gcr1;	/* 0x04: General Control Register 1 */
+	u32 gcr2;	/* 0x08: General Control Register 2 */
+	u32 res_0c;	/* 0x0C: Reserved */
+	u32 recr0;	/* 0x10: Receive Equalization Control Register 0 */
+	u32 recr1;	/* 0x14: Receive Equalization Control Register 1 */
+	u32 tecr0;	/* 0x18: Transmit Equalization Control Register 0 */
+	u32 res_1c;	/* 0x1C: Reserved */
+	u32 tlcr0;	/* 0x20: TTL Control Register 0 */
+	u32 tlcr1;	/* 0x24: TTL Control Register 1 */
+	u32 tlcr2;	/* 0x28: TTL Control Register 2 */
+	u32 tlcr3;	/* 0x2C: TTL Control Register 3 */
+	u32 tcsr0;	/* 0x30: Test Control/Status Register 0 */
+	u32 tcsr1;	/* 0x34: Test Control/Status Register 1 */
+	u32 tcsr2;	/* 0x38: Test Control/Status Register 2 */
+	u32 tcsr3;	/* 0x3C: Test Control/Status Register 3 */
+};
+
+static struct mem_io_ops io_ops;
+
+static void reset_lane(void __iomem *reg, enum lane_req ln_req)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	/* reset Tx lane: send reset request */
+	if (ln_req | LANE_TX) {
+		io_ops.write32(io_ops.read32(&reg_base->gcr0) & ~GCR0_TRST_MASK,
+			       &reg_base->gcr0);
+	}
+	/* reset Rx lane: send reset request */
+	if (ln_req | LANE_RX) {
+		io_ops.write32(io_ops.read32(&reg_base->gcr0) & ~GCR0_RRST_MASK,
+			       &reg_base->gcr0);
+	}
+	/* unreset the lane */
+	if (ln_req != LANE_INVALID) {
+		udelay(1);
+		io_ops.write32(io_ops.read32(&reg_base->gcr0) | GCR0_RESET_MASK,
+			       &reg_base->gcr0);
+		udelay(1);
+	}
+}
+
+static u32 read_tecr0(void __iomem *reg)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	return io_ops.read32(&reg_base->tecr0);
+}
+
+static u32 read_tecr1(void __iomem *reg)
+{
+	return 0;
+}
+
+static void read_tecr_params(void __iomem *reg, struct lane_kr_params *params)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	u32 val;
+
+	val = io_ops.read32(&reg_base->tecr0);
+
+	params->ratio_preq = (val & RATIO_PREQ_MASK) >> RATIO_PREQ_SHIFT;
+	params->ratio_pstq = (val & RATIO_PST1Q_MASK) >> RATIO_PST1Q_SHIFT;
+	params->adpt_eq = (val & ADPT_EQ_MASK) >> ADPT_EQ_SHIFT;
+	params->amp_red = (val & AMP_RED_MASK) >> AMP_RED_SHIFT;
+}
+
+static void setup_tecr(void __iomem *reg, struct lane_kr_params *params,
+		       bool reset)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	u32 val;
+
+	val = TECR0_INIT |
+		params->adpt_eq << ADPT_EQ_SHIFT |
+		params->ratio_preq << RATIO_PREQ_SHIFT |
+		params->ratio_pstq << RATIO_PST1Q_SHIFT |
+		params->amp_red << AMP_RED_SHIFT;
+
+	if (reset) {
+		/* reset the lane */
+		io_ops.write32(io_ops.read32(&reg_base->gcr0) &
+			       ~GCR0_RESET_MASK, &reg_base->gcr0);
+		udelay(1);
+	}
+
+	io_ops.write32(val, &reg_base->tecr0);
+	udelay(1);
+
+	if (reset) {
+		/* unreset the lane */
+		io_ops.write32(io_ops.read32(&reg_base->gcr0) | GCR0_RESET_MASK,
+			       &reg_base->gcr0);
+		udelay(1);
+	}
+}
+
+/* collect_gains
+ *
+ * reg: serdes registers memory map
+ * gaink2: High-frequency gain of the equalizer amplifier
+ *         the high-frequency gain of the equalizer amplifier is increased by
+ *         decrementing the value of eq_gaink2 by one
+ * gaink3: Middle-frequency gain of the equalizer amplifier
+ *         the mid-frequency gain of the equalizer amplifier is increased by
+ *         decrementing the value of eq_gaink3 by one
+ * osestat: equalization offset status
+ *          the equalizer offset is reduced by decrementing the value of osestat
+ * size: size of snapshots data collection
+ */
+static int collect_gains(void __iomem *reg, s16 *gaink2, s16 *gaink3,
+			 s16 *osestat, u8 size)
+{
+	u32 rx_eq_snp;
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	int timeout;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		/* wait RECR1_CTL_SNP_DONE_MASK has cleared */
+		timeout = 100;
+		while (io_ops.read32(&reg_base->recr1) &
+		       RECR1_CTL_SNP_DONE_MASK) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* start snapshot */
+		io_ops.write32((io_ops.read32(&reg_base->gcr1) |
+				GCR1_CTL_SNP_START_MASK), &reg_base->gcr1);
+
+		/* wait for SNP done */
+		timeout = 100;
+		while (!(io_ops.read32(&reg_base->recr1) &
+		       RECR1_CTL_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* read and save the snapshot */
+		rx_eq_snp = io_ops.read32(&reg_base->recr1);
+
+		if (gaink2)
+			gaink2[i] = (u8)((rx_eq_snp & RECR1_GAINK2_MASK) >>
+					 RECR1_GAINK2_SHIFT);
+		if (gaink3)
+			gaink3[i] = (u8)((rx_eq_snp & RECR1_GAINK3_MASK) >>
+					 RECR1_GAINK3_SHIFT);
+		if (osestat)
+			osestat[i] = (u8)((rx_eq_snp & RECR1_EQ_OFFSET_MASK) >>
+					  RECR1_EQ_OFFSET_SHIFT);
+
+		/* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */
+		io_ops.write32((io_ops.read32(&reg_base->gcr1) &
+			       ~GCR1_CTL_SNP_START_MASK), &reg_base->gcr1);
+	}
+	return i;
+}
+
+static int collect_eq_status(void __iomem *reg, enum eqc_type type[],
+			     u8 type_no, s16 *counters, u8 size)
+{
+	s16 *gaink2 = NULL, *gaink3 = NULL, *osestat = NULL;
+	u8 i;
+
+	for (i = 0; i < type_no; i++) {
+		switch (type[i]) {
+		case EQC_GAIN_HF:
+			gaink2 = counters;
+			break;
+		case EQC_GAIN_MF:
+			gaink3 = counters + size;
+			break;
+		case EQC_EQOFFSET:
+			osestat = counters + 2 * size;
+			break;
+		default:
+			/* invalid type */
+			break;
+		}
+	}
+
+	return collect_gains(reg, gaink2, gaink3, osestat, size);
+}
+
+static int collect_bin_snapshots(void __iomem *reg, enum eqc_type type,
+				 s16 *bin_counters, u8 bin_size)
+{
+	int bin_snapshot;
+	u32 bin_sel;
+	int i, timeout;
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	/* calculate TCSR1[CDR_SEL] */
+	switch (type) {
+	case EQC_BIN_1:
+		bin_sel = BIN_1_SEL;
+		break;
+	case EQC_BIN_2:
+		bin_sel = BIN_2_SEL;
+		break;
+	case EQC_BIN_3:
+		bin_sel = BIN_3_SEL;
+		break;
+	case EQC_BIN_LONG:
+		bin_sel = BIN_LONG_SEL;
+		break;
+	case EQC_BIN_M1:
+		bin_sel = BIN_M1_SEL;
+		break;
+	case EQC_BIN_OFFSET:
+		bin_sel = BIN_OFFSET_SEL;
+		break;
+	case EQC_BIN_AVG:
+		bin_sel = BIN_AVG_SEL;
+		break;
+	case EQC_BIN_BLW:
+		bin_sel = BIN_BLW_SEL;
+		break;
+	default:
+		/* invalid bin type */
+		return 0;
+	}
+
+	for (i = 0; i < bin_size; i++) {
+		/* wait RECR1_SNP_DONE_MASK has cleared */
+		timeout = 100;
+		while ((io_ops.read32(&reg_base->recr1) &
+			RECR1_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* set TCSR1[CDR_SEL] */
+		io_ops.write32((io_ops.read32(&reg_base->tcsr1) &
+				~CDR_SEL_MASK) | bin_sel, &reg_base->tcsr1);
+
+		/* start snapshot */
+		io_ops.write32(io_ops.read32(&reg_base->gcr1) |
+			       GCR1_SNP_START_MASK, &reg_base->gcr1);
+
+		/* wait for SNP done */
+		timeout = 100;
+		while (!(io_ops.read32(&reg_base->recr1) &
+			 RECR1_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* read and save the snapshot:
+		 * 2's complement 9 bit long value (-256 to 255)
+		 */
+		bin_snapshot = (io_ops.read32(&reg_base->tcsr1) &
+				TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT;
+		if (bin_snapshot & TCSR1_EQ_SNPBIN_SIGN_MASK) {
+			/* 2's complement 9 bit long negative number */
+			bin_snapshot &= ~TCSR1_EQ_SNPBIN_SIGN_MASK;
+			bin_snapshot -= 256;
+		}
+
+		/* save collected Bin snapshot */
+		bin_counters[i] = (s16)bin_snapshot;
+
+		/* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */
+		io_ops.write32(io_ops.read32(&reg_base->gcr1) &
+			       ~GCR1_SNP_START_MASK, &reg_base->gcr1);
+	}
+	return i;
+}
+
+static struct eqc_range bin_range = {
+	.min = EQ_BIN_MIN,
+	.max = EQ_BIN_MAX,
+	.mid_low = EQ_BIN_SNP_AV_THR_LOW,
+	.mid_high = EQ_BIN_SNP_AV_THR_HIGH,
+};
+
+static struct eqc_range gaink_range = {
+	.min = EQ_GAINK_MIN,
+	.max = EQ_GAINK_MAX,
+	.mid_low = EQ_GAINK_MIDRANGE_LOW,
+	.mid_high = EQ_GAINK_MIDRANGE_HIGH,
+};
+
+static struct eqc_range osestat_range = {
+	.min = EQ_OFFSET_MIN,
+	.max = EQ_OFFSET_MAX,
+	.mid_low = EQ_OFFSET_MIDRANGE_LOW,
+	.mid_high = EQ_OFFSET_MIDRANGE_HIGH,
+};
+
+static struct eqc_range *get_counter_range(enum eqc_type type)
+{
+	switch (type) {
+	case EQC_BIN_1:
+	case EQC_BIN_2:
+	case EQC_BIN_3:
+	case EQC_BIN_LONG:
+	case EQC_BIN_M1:
+	case EQC_BIN_OFFSET:
+	case EQC_BIN_AVG:
+	case EQC_BIN_BLW:
+		return &bin_range;
+	case EQC_GAIN_HF:
+	case EQC_GAIN_MF:
+		return &gaink_range;
+	case EQC_EQOFFSET:
+		return &osestat_range;
+	default:
+		/* invalid counter type */
+		return NULL;
+	}
+	return NULL;
+}
+
+static bool is_cdr_lock_bit(void __iomem *reg)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	if (io_ops.read32(&reg_base->tcsr3) & TCSR3_CDR_LCK_MASK)
+		return true;
+
+	return false;
+}
+
+static const struct qoriq_lane_ops qoriq_lane = {
+	.read_tecr0 = read_tecr0,
+	.read_tecr1 = read_tecr1,
+};
+
+static const struct lane_io_ops lane_ops = {
+	.priv = &qoriq_lane,
+	.memmap_size = MEMORY_MAP_SIZE,
+	.reset_lane = reset_lane,
+	.tune_lane_kr = setup_tecr,
+	.read_lane_kr = read_tecr_params,
+	.is_cdr_lock = is_cdr_lock_bit,
+};
+
+const struct lane_io_ops *qoriq_get_lane_ops_10g(void)
+{
+	return &lane_ops;
+}
+
+static const struct equalizer_info equalizer = {
+	.name = EQUALIZER_NAME,
+	.version = EQUALIZER_VERSION,
+	.ops = {
+		.collect_counters = collect_bin_snapshots,
+		.collect_multiple_counters = collect_eq_status,
+		.get_counter_range = get_counter_range,
+	},
+};
+
+const struct equalizer_info *qoriq_get_equalizer_info_10g(void)
+{
+	return &equalizer;
+}
+
+void qoriq_setup_mem_io_10g(struct mem_io_ops io)
+{
+	io_ops = io;
+}
+
+void qoriq_setup_mdio_10g(struct backplane_dev_info *bp_dev)
+{
+	/* IEEE802.3 Clause 45 register spaces */
+
+	/* KR PMD registers */
+	backplane_setup_kr_lt_mmd(bp_dev, MDIO_MMD_PMAPMD, KR_PMD_BASE_OFFSET);
+
+	/* KX/KR AN registers: IEEE802.3 Clause 45 MMD 7 */
+	bp_dev->mdio.an_ad_ability_0 = AN_AD_ABILITY_0;
+	bp_dev->mdio.an_ad_ability_1 = AN_AD_ABILITY_1;
+	bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET;
+}
diff --git a/drivers/net/phy/backplane/qoriq_serdes_28g.c b/drivers/net/phy/backplane/qoriq_serdes_28g.c
new file mode 100644
index 0000000..5d847a4
--- /dev/null
+++ b/drivers/net/phy/backplane/qoriq_serdes_28g.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* QorIQ Backplane driver for SerDes 28G
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+
+#include "qoriq_backplane.h"
+
+#define EQUALIZER_NAME				"qoriq_serdes_28g"
+#define EQUALIZER_VERSION			"1.0.0"
+
+#define BIN_1_SEL				0x00000000
+#define BIN_2_SEL				0x00001000
+#define BIN_3_SEL				0x00002000
+#define BIN_4_SEL				0x00003000
+#define BIN_OFFSET_SEL				0x00004000
+#define BIN_BLW_SEL				0x00008000
+#define BIN_AVG_SEL				0x00009000
+#define BIN_M1_SEL				0x0000c000
+#define BIN_LONG_SEL				0x0000d000
+#define CDR_SEL_MASK				0x0000f000
+
+#define RATIO_PREQ_SHIFT			16
+#define RATIO_PST1Q_SHIFT			8
+#define AMP_RED_SHIFT				0
+#define ADPT_EQ_SHIFT				24
+
+#define RATIO_PREQ_MASK				0x000f0000
+#define RATIO_PST1Q_MASK			0x00001f00
+#define ADPT_EQ_MASK				0x3f000000
+#define AMP_RED_MASK				0x0000003f
+
+#define TECR0_INIT				0x20808000
+
+#define RESET_REQ_MASK				0x80000000
+
+#define TCSR0_SD_STAT_OBS_EN_MASK		0x80000000
+#define RECR3_SNP_START_MASK			0x80000000
+#define RECR3_SNP_DONE_MASK			0x40000000
+
+#define RECR4_SNP_DATA_MASK			0x000001ff
+#define RECR4_SNP_DATA_SHIFT			0
+#define RECR4_EQ_SNPBIN_SIGN_MASK		0x100
+
+#define RECR3_GAINK2_MASK			0x1f000000
+#define RECR3_GAINK2_SHIFT			24
+
+#define RECR3_GAINK3_MASK			0x001f0000
+#define RECR3_GAINK3_SHIFT			16
+
+#define RECR4_EQ_OFFSET_MASK			0x003f0000
+#define RECR4_EQ_OFFSET_SHIFT			16
+
+#define RRSTCTL_CDR_LOCK_MASK			0x00001000
+
+#define AN_AD_ABILITY_0				0x02
+#define AN_AD_ABILITY_1				0x03
+#define AN_BP_ETH_STATUS_OFFSET			0x0F
+#define KR_PMD_BASE_OFFSET			0x100
+
+/* Bin snapshots thresholds range */
+#define EQ_BIN_MIN				-256
+#define EQ_BIN_MAX				255
+/* Bin snapshots average thresholds range */
+#define EQ_BIN_SNP_AV_THR_LOW			-150
+#define EQ_BIN_SNP_AV_THR_HIGH			150
+
+#define EQ_GAINK_MIN				0x1F
+#define EQ_GAINK_MAX				0x0
+#define EQ_GAINK_MIDRANGE_LOW			0x1E
+#define EQ_GAINK_MIDRANGE_HIGH			0x1
+
+#define EQ_OFFSET_MIN				0
+#define EQ_OFFSET_MAX				0x3F
+#define EQ_OFFSET_MIDRANGE_LOW			0x10
+#define EQ_OFFSET_MIDRANGE_HIGH			0x2F
+
+#define MEMORY_MAP_SIZE				0x100
+
+struct qoriq_lane_regs {
+	u32 gcr0;	/* 0x00: General Control Register 0 */
+	u32 res_04[7];	/* 0x04: Reserved */
+	u32 trstctl;	/* 0x20: TX Reset Control Register */
+	u32 tgcr0;	/* 0x24: TX General Control Register 0 */
+	u32 tgcr1;	/* 0x28: TX General Control Register 1 */
+	u32 tgcr2;	/* 0x2C: TX General Control Register 2 */
+	u32 tecr0;	/* 0x30: Transmit Equalization Control Register 0 */
+	u32 tecr1;	/* 0x34: Transmit Equalization Control Register 1 */
+	u32 res_38[2];	/* 0x38: Reserved */
+	u32 rrstctl;	/* 0x40: RX Reset Control Register */
+	u32 rgcr0;	/* 0x44: RX General Control Register 0 */
+	u32 rxgcr1;	/* 0x48: RX General Control Register 1 */
+	u32 res_4c;	/* 0x4C: Reserved */
+	u32 recr0;	/* 0x50: RX Equalization Register 0 */
+	u32 recr1;	/* 0x54: RX Equalization Register 1 */
+	u32 recr2;	/* 0x58: RX Equalization Register 2 */
+	u32 recr3;	/* 0x5C: RX Equalization Register 3 */
+	u32 recr4;	/* 0x60: RX Equalization Register 4 */
+	u32 res_64;	/* 0x64: Reserved */
+	u32 rccr0;	/* 0x68: RX Calibration Register 0 */
+	u32 rccr1;	/* 0x6C: RX Calibration Register 1 */
+	u32 rcpcr0;	/* 0x70: RX Clock Path Register 0 */
+	u32 rsccr0;	/* 0x74: RX Sampler Calibration Control Register 0 */
+	u32 rsccr1;	/* 0x78: RX Sampler Calibration Control Register 1 */
+	u32 res_7c;	/* 0x7C: Reserved */
+	u32 ttlcr0;	/* 0x80: Transition Tracking Loop Register 0 */
+	u32 ttlcr1;	/* 0x84: Transition Tracking Loop Register 1 */
+	u32 ttlcr2;	/* 0x88: Transition Tracking Loop Register 2 */
+	u32 ttlcr3;	/* 0x8C: Transition Tracking Loop Register 3 */
+	u32 res_90[4];	/* 0x90: Reserved */
+	u32 tcsr0;	/* 0xA0: Test Control/Status Register 0 */
+	u32 tcsr1;	/* 0xA4: Test Control/Status Register 1 */
+	u32 tcsr2;	/* 0xA8: Test Control/Status Register 2 */
+	u32 tcsr3;	/* 0xAC: Test Control/Status Register 3 */
+	u32 tcsr4;	/* 0xB0: Test Control/Status Register 4 */
+	u32 res_b4[3];	/* 0xB4: Reserved */
+	u32 rxcb0;	/* 0xC0: RX Control Block Register 0 */
+	u32 rxcb1;	/* 0xC4: RX Control Block Register 1 */
+	u32 res_c8[2];	/* 0xC8: Reserved */
+	u32 rxss0;	/* 0xD0: RX Speed Switch Register 0 */
+	u32 rxss1;	/* 0xD4: RX Speed Switch Register 1 */
+	u32 rxss2;	/* 0xD8: RX Speed Switch Register 2 */
+	u32 res_dc;	/* 0xDC: Reserved */
+	u32 txcb0;	/* 0xE0: TX Control Block Register 0 */
+	u32 txcb1;	/* 0xE4: TX Control Block Register 1 */
+	u32 res_e8[2];	/* 0xE8: Reserved */
+	u32 txss0;	/* 0xF0: TX Speed Switch Register 0 */
+	u32 txss1;	/* 0xF4: TX Speed Switch Register 1 */
+	u32 txss2;	/* 0xF8: TX Speed Switch Register 2 */
+	u32 res_fc;	/* 0xFC: Reserved */
+};
+
+static struct mem_io_ops io_ops;
+
+static void reset_lane(void __iomem *reg, enum lane_req ln_req)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	u32 val;
+	u64 timeout;
+
+	/* reset Tx lane: send reset request */
+	if (ln_req | LANE_TX) {
+		io_ops.write32(io_ops.read32(&reg_base->trstctl) |
+			       RESET_REQ_MASK, &reg_base->trstctl);
+		udelay(1);
+		timeout = 10;
+		while (timeout--) {
+			val = io_ops.read32(&reg_base->trstctl);
+			if (!(val & RESET_REQ_MASK))
+				break;
+			usleep_range(5, 20);
+		}
+	}
+
+	/* reset Rx lane: send reset request */
+	if (ln_req | LANE_RX) {
+		io_ops.write32(io_ops.read32(&reg_base->rrstctl) |
+			       RESET_REQ_MASK, &reg_base->rrstctl);
+		udelay(1);
+		timeout = 10;
+		while (timeout--) {
+			val = io_ops.read32(&reg_base->rrstctl);
+			if (!(val & RESET_REQ_MASK))
+				break;
+			usleep_range(5, 20);
+		}
+	}
+
+	/* wait for a while after reset */
+	if (ln_req != LANE_INVALID) {
+		timeout = jiffies + 10;
+		while (time_before(jiffies, (unsigned long)timeout)) {
+			schedule();
+			usleep_range(5, 20);
+		}
+	}
+}
+
+static u32 read_tecr0(void __iomem *reg)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	return io_ops.read32(&reg_base->tecr0);
+}
+
+static u32 read_tecr1(void __iomem *reg)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	return io_ops.read32(&reg_base->tecr1);
+}
+
+static void read_tecr_params(void __iomem *reg, struct lane_kr_params *params)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	u32 val;
+
+	val = io_ops.read32(&reg_base->tecr0);
+	params->ratio_preq = (val & RATIO_PREQ_MASK) >> RATIO_PREQ_SHIFT;
+	params->ratio_pstq = (val & RATIO_PST1Q_MASK) >> RATIO_PST1Q_SHIFT;
+	params->amp_red = (val & AMP_RED_MASK) >> AMP_RED_SHIFT;
+
+	val = io_ops.read32(&reg_base->tecr1);
+	params->adpt_eq = (val & ADPT_EQ_MASK) >> ADPT_EQ_SHIFT;
+}
+
+static void setup_tecr(void __iomem *reg, struct lane_kr_params *params,
+		       bool reset)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	u32 val;
+
+	/* reset lanes */
+	if (reset)
+		reset_lane(reg, LANE_RX_TX);
+
+	val = TECR0_INIT |
+		params->ratio_preq << RATIO_PREQ_SHIFT |
+		params->ratio_pstq << RATIO_PST1Q_SHIFT |
+		params->amp_red << AMP_RED_SHIFT;
+	io_ops.write32(val, &reg_base->tecr0);
+
+	val = params->adpt_eq << ADPT_EQ_SHIFT;
+	io_ops.write32(val, &reg_base->tecr1);
+
+	udelay(1);
+}
+
+/* collect_gains
+ *
+ * reg: serdes registers memory map
+ * gaink2: High-frequency gain of the equalizer amplifier
+ *         the high-frequency gain of the equalizer amplifier is increased by
+ *         decrementing the value of eq_gaink2 by one
+ * gaink3: Middle-frequency gain of the equalizer amplifier
+ *         the mid-frequency gain of the equalizer amplifier is increased by
+ *         decrementing the value of eq_gaink3 by one
+ * osestat: equalization offset status
+ *          the equalizer offset is reduced by decrementing the value of osestat
+ * size: size of snapshots data collection
+ */
+static int collect_gains(void __iomem *reg, s16 *gaink2, s16 *gaink3,
+			 s16 *osestat, u8 size)
+{
+	u32 recr3, recr4;
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+	int timeout;
+	int i;
+
+	/* Enable observation of SerDes status on all status registers */
+	io_ops.write32(io_ops.read32(&reg_base->tcsr0) |
+		       TCSR0_SD_STAT_OBS_EN_MASK, &reg_base->tcsr0);
+
+	for (i = 0; i < size; i++) {
+		/* wait RECR3_SNP_DONE_MASK has cleared */
+		timeout = 100;
+		while (io_ops.read32(&reg_base->recr3) & RECR3_SNP_DONE_MASK) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* start snapshot */
+		io_ops.write32((io_ops.read32(&reg_base->recr3) |
+				RECR3_SNP_START_MASK), &reg_base->recr3);
+
+		/* wait for SNP done */
+		timeout = 100;
+		while (!(io_ops.read32(&reg_base->recr3) &
+			 RECR3_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* read and save the snapshot */
+		recr3 = io_ops.read32(&reg_base->recr3);
+		recr4 = io_ops.read32(&reg_base->recr4);
+
+		if (gaink2)
+			gaink2[i] = (u8)((recr3 & RECR3_GAINK2_MASK) >>
+					 RECR3_GAINK2_SHIFT);
+		if (gaink3)
+			gaink3[i] = (u8)((recr3 & RECR3_GAINK3_MASK) >>
+					 RECR3_GAINK3_SHIFT);
+		if (osestat)
+			osestat[i] = (u8)((recr4 & RECR4_EQ_OFFSET_MASK) >>
+					  RECR4_EQ_OFFSET_SHIFT);
+
+		/* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */
+		io_ops.write32((io_ops.read32(&reg_base->recr3) &
+				~RECR3_SNP_START_MASK), &reg_base->recr3);
+	}
+	return i;
+}
+
+static int collect_eq_status(void __iomem *reg, enum eqc_type type[],
+			     u8 type_no, s16 *counters, u8 size)
+{
+	s16 *gaink2 = NULL, *gaink3 = NULL, *osestat = NULL;
+	u8 i;
+
+	for (i = 0; i < type_no; i++) {
+		switch (type[i]) {
+		case EQC_GAIN_HF:
+			gaink2 = counters;
+			break;
+		case EQC_GAIN_MF:
+			gaink3 = counters + size;
+			break;
+		case EQC_EQOFFSET:
+			osestat = counters + 2 * size;
+			break;
+		default:
+			/* invalid type */
+			break;
+		}
+	}
+
+	return collect_gains(reg, gaink2, gaink3, osestat, size);
+}
+
+static int collect_bin_snapshots(void __iomem *reg, enum eqc_type type,
+				 s16 *bin_counters, u8 bin_size)
+{
+	int bin_snapshot;
+	u32 bin_sel;
+	int i, timeout;
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	/* calculate RECR4[EQ_BIN_DATA_SEL] */
+	switch (type) {
+	case EQC_BIN_1:
+		bin_sel = BIN_1_SEL;
+		break;
+	case EQC_BIN_2:
+		bin_sel = BIN_2_SEL;
+		break;
+	case EQC_BIN_3:
+		bin_sel = BIN_3_SEL;
+		break;
+	case EQC_BIN_4:
+		bin_sel = BIN_4_SEL;
+		break;
+	case EQC_BIN_LONG:
+		bin_sel = BIN_LONG_SEL;
+		break;
+	case EQC_BIN_M1:
+		bin_sel = BIN_M1_SEL;
+		break;
+	case EQC_BIN_OFFSET:
+		bin_sel = BIN_OFFSET_SEL;
+		break;
+	case EQC_BIN_AVG:
+		bin_sel = BIN_AVG_SEL;
+		break;
+	case EQC_BIN_BLW:
+		bin_sel = BIN_BLW_SEL;
+		break;
+	default:
+		/* invalid bin type */
+		return 0;
+	}
+
+	/* Enable observation of SerDes status on all status registers */
+	io_ops.write32(io_ops.read32(&reg_base->tcsr0) |
+		       TCSR0_SD_STAT_OBS_EN_MASK, &reg_base->tcsr0);
+
+	for (i = 0; i < bin_size; i++) {
+		/* wait RECR3_SNP_DONE_MASK has cleared */
+		timeout = 100;
+		while ((io_ops.read32(&reg_base->recr3) &
+			RECR3_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* set RECR4[EQ_BIN_DATA_SEL] */
+		io_ops.write32((io_ops.read32(&reg_base->recr4) &
+				~CDR_SEL_MASK) | bin_sel, &reg_base->recr4);
+
+		/* start snapshot */
+		io_ops.write32(io_ops.read32(&reg_base->recr3) |
+			       RECR3_SNP_START_MASK, &reg_base->recr3);
+
+		/* wait for SNP done */
+		timeout = 100;
+		while (!(io_ops.read32(&reg_base->recr3) &
+			 RECR3_SNP_DONE_MASK)) {
+			udelay(1);
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		/* read and save the snapshot:
+		 * 2's complement 9 bit long value (-256 to 255)
+		 */
+		bin_snapshot = (io_ops.read32(&reg_base->recr4) &
+				RECR4_SNP_DATA_MASK) >> RECR4_SNP_DATA_SHIFT;
+		if (bin_snapshot & RECR4_EQ_SNPBIN_SIGN_MASK) {
+			/* 2's complement 9 bit long negative number */
+			bin_snapshot &= ~RECR4_EQ_SNPBIN_SIGN_MASK;
+			bin_snapshot -= 256;
+		}
+
+		/* save collected Bin snapshot */
+		bin_counters[i] = (s16)bin_snapshot;
+
+		/* terminate the snapshot by setting GCR1[REQ_CTL_SNP] */
+		io_ops.write32(io_ops.read32(&reg_base->recr3) &
+			       ~RECR3_SNP_START_MASK, &reg_base->recr3);
+	}
+	return i;
+}
+
+static struct eqc_range bin_range = {
+	.min = EQ_BIN_MIN,
+	.max = EQ_BIN_MAX,
+	.mid_low = EQ_BIN_SNP_AV_THR_LOW,
+	.mid_high = EQ_BIN_SNP_AV_THR_HIGH,
+};
+
+static struct eqc_range gaink_range = {
+	.min = EQ_GAINK_MIN,
+	.max = EQ_GAINK_MAX,
+	.mid_low = EQ_GAINK_MIDRANGE_LOW,
+	.mid_high = EQ_GAINK_MIDRANGE_HIGH,
+};
+
+static struct eqc_range osestat_range = {
+	.min = EQ_OFFSET_MIN,
+	.max = EQ_OFFSET_MAX,
+	.mid_low = EQ_OFFSET_MIDRANGE_LOW,
+	.mid_high = EQ_OFFSET_MIDRANGE_HIGH,
+};
+
+static struct eqc_range *get_counter_range(enum eqc_type type)
+{
+	switch (type) {
+	case EQC_BIN_1:
+	case EQC_BIN_2:
+	case EQC_BIN_3:
+	case EQC_BIN_4:
+	case EQC_BIN_LONG:
+	case EQC_BIN_M1:
+	case EQC_BIN_OFFSET:
+	case EQC_BIN_AVG:
+	case EQC_BIN_BLW:
+		return &bin_range;
+	case EQC_GAIN_HF:
+	case EQC_GAIN_MF:
+		return &gaink_range;
+	case EQC_EQOFFSET:
+		return &osestat_range;
+	default:
+		/* invalid counter type */
+		return NULL;
+	}
+	return NULL;
+}
+
+static bool is_cdr_lock_bit(void __iomem *reg)
+{
+	struct qoriq_lane_regs __iomem *reg_base = reg;
+
+	if (io_ops.read32(&reg_base->rrstctl) & RRSTCTL_CDR_LOCK_MASK)
+		return true;
+
+	return false;
+}
+
+static const struct qoriq_lane_ops qoriq_lane = {
+	.read_tecr0 = read_tecr0,
+	.read_tecr1 = read_tecr1,
+};
+
+static const struct lane_io_ops lane_ops = {
+	.priv = &qoriq_lane,
+	.memmap_size = MEMORY_MAP_SIZE,
+	.reset_lane = reset_lane,
+	.tune_lane_kr = setup_tecr,
+	.read_lane_kr = read_tecr_params,
+	.is_cdr_lock = is_cdr_lock_bit,
+};
+
+const struct lane_io_ops *qoriq_get_lane_ops_28g(void)
+{
+	return &lane_ops;
+}
+
+static const struct equalizer_info equalizer = {
+	.name = EQUALIZER_NAME,
+	.version = EQUALIZER_VERSION,
+	.ops = {
+		.collect_counters = collect_bin_snapshots,
+		.collect_multiple_counters = collect_eq_status,
+		.get_counter_range = get_counter_range,
+	},
+};
+
+const struct equalizer_info *qoriq_get_equalizer_info_28g(void)
+{
+	return &equalizer;
+}
+
+void qoriq_setup_mem_io_28g(struct mem_io_ops io)
+{
+	io_ops = io;
+}
+
+void qoriq_setup_mdio_28g(struct backplane_dev_info *bp_dev)
+{
+	/* Auto-Negotiation and Link Training Core Registers:
+	 * IEEE802.3 Clauses 72, 73, 92
+	 */
+
+	/* Link Training Control and Status Registers: page 1: 0x100 */
+	backplane_setup_kr_lt_mmd(bp_dev, MDIO_MMD_AN, KR_PMD_BASE_OFFSET);
+
+	/* Auto-Negotiation Control and Status Registers: page 0 */
+	bp_dev->mdio.an_ad_ability_0 = AN_AD_ABILITY_0;
+	bp_dev->mdio.an_ad_ability_1 = AN_AD_ABILITY_1;
+	bp_dev->mdio.an_bp_eth_status = AN_BP_ETH_STATUS_OFFSET;
+}
-- 
1.9.1


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

* [PATCH net-next 8/9] net: phy: add bee algorithm for kr training
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (6 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 7/9] net: phy: enable qoriq backplane support Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-26 13:51 ` [PATCH net-next 9/9] arm64: dts: add serdes and mdio description Florinel Iordache
  8 siblings, 0 replies; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add support for bee equalization algorithm used by kr training:
3-Taps Bit Edge Equalization (BEE) algorithm

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 drivers/net/phy/backplane/Kconfig  |   11 +
 drivers/net/phy/backplane/Makefile |    1 +
 drivers/net/phy/backplane/eq_bee.c | 1078 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1090 insertions(+)
 create mode 100644 drivers/net/phy/backplane/eq_bee.c

diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig
index 3e20a78..ee5cf1c 100644
--- a/drivers/net/phy/backplane/Kconfig
+++ b/drivers/net/phy/backplane/Kconfig
@@ -19,6 +19,17 @@ config ETH_BACKPLANE_FIXED
 	  No Equalization algorithm is used to adapt the initial coefficients
 	  initially set by the user.
 
+config ETH_BACKPLANE_BEE
+	tristate "3-Taps Bit Edge Equalization (BEE) algorithm"
+	depends on ETH_BACKPLANE
+	help
+	  This module provides a driver for BEE algorithm: 3-Taps
+	  Bit Edge Equalization. This algorithm is using a method
+	  based on 3-taps coefficients for mitigating intersymbol
+	  interference (ISI) in high-speed backplane applications.
+	  The initial values for algorithm coefficient values are
+	  user configurable and used as a starting point of the algorithm.
+
 config ETH_BACKPLANE_QORIQ
 	tristate "QorIQ Ethernet Backplane driver"
 	depends on ETH_BACKPLANE
diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile
index d8f95ac..242e938 100644
--- a/drivers/net/phy/backplane/Makefile
+++ b/drivers/net/phy/backplane/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o
 obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o
+obj-$(CONFIG_ETH_BACKPLANE_BEE) += eq_bee.o
 obj-$(CONFIG_ETH_BACKPLANE_QORIQ) += eth_backplane_qoriq.o
 
 eth_backplane-objs	:= backplane.o link_training.o
diff --git a/drivers/net/phy/backplane/eq_bee.c b/drivers/net/phy/backplane/eq_bee.c
new file mode 100644
index 0000000..f62b777
--- /dev/null
+++ b/drivers/net/phy/backplane/eq_bee.c
@@ -0,0 +1,1078 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* 3-Taps Bit Edge Equalization (BEE) algorithm
+ *
+ * Copyright 2019-2020 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "equalization.h"
+
+#define ALGORITHM_NAME		"backplane_bee_3tap"
+#define ALGORITHM_DESCR		"3-Taps Bit Edge Equalization"
+#define ALGORITHM_VERSION	"1.5.5"
+
+/* BEE algorithm timeouts */
+#define TIMEOUT_LONG				3
+#define TIMEOUT_M1				3
+
+/* Size of equalization snapshots data collection */
+#define EQ_SNAPSHOTS_SIZE			10
+
+/* Rx link quality conditions:
+ * The following macros are used to determine the Rx link quality
+ * which is used to decide if/when to proceed with BinLong/BinM1 modules.
+ * The code that considers Rx in good quality is always in place:
+ * Rx link is in 'good quality' if:
+ * Bin1, Bin2 and Bin3 are toggling
+ *
+ * These macros are used to enable less quality link conditions.
+ * If Rx link quality is considered good enough then proceed to BinLong/BinM1
+ */
+
+/* Rx is 'less quality' if:
+ * Bin1 is toggling
+ * AND
+ *   Bin2 is Early, GainMF stuck at max_eq_gain and Bin3 is Late
+ *   OR
+ *   Bin2 is Late, GainMF stuck at min_eq_gain and Bin3 is Early
+ */
+#define ENABLE_LESS_QUALITY_CONDITION
+
+/* Rx is 'even less quality' if:
+ * Bin1 is Early AND GainHF stuck at max_eq_gain and Bin2 is Late AND
+ *     GainMF stuck at min_eq_gain
+ * OR
+ * Bin1 is Late AND GainHF stuck at min_eq_gain AND
+ *     Bin2 is Early, GainMF stuck at max_eq_gain
+ */
+#define ENABLE_EVEN_LESS_QUALITY_CONDITION
+
+/* Rx is 'seemingly quality' if:
+ * Bin1 is always Late for all snapshots AND
+ *     GainHF is stuck at min_eq_gain
+ * AND
+ * Bin2 and Bin3 are both Toggling
+ */
+#define ENABLE_SEEMINGLY_QUALITY_CONDITION
+
+enum bin_state {
+	BIN_INVALID,
+	BIN_EARLY,
+	BIN_TOGGLE,
+	BIN_LATE
+};
+
+struct eq_data_priv {
+	/* Equalization Algorithm setup data */
+	struct eq_setup_info eq_setup;
+
+	/* Bin state */
+	enum bin_state bin_m1_state;
+	enum bin_state bin_long_state;
+	enum bin_state prev_bin_m1_state;
+	enum bin_state prev_bin_long_state;
+
+	/* Bin training status */
+	bool bin_m1_stop;
+	bool bin_long_stop;
+	int m1_min_max_cnt;
+	int long_min_max_cnt;
+
+	/* Algorithm controlled value */
+	u32 ld_update;
+
+	/* Bit edge statistics: Bin snapshots */
+	s16 bin1_snapshot[EQ_SNAPSHOTS_SIZE];
+	s16 bin2_snapshot[EQ_SNAPSHOTS_SIZE];
+	s16 bin3_snapshot[EQ_SNAPSHOTS_SIZE];
+	s16 bin_long_snapshot[EQ_SNAPSHOTS_SIZE];
+	s16 bin_m1_snapshot[EQ_SNAPSHOTS_SIZE];
+	s16 bin_offset_snapshot[EQ_SNAPSHOTS_SIZE];
+
+	/* Gain snapshots */
+	u8 gain_hf_snapshot[EQ_SNAPSHOTS_SIZE];
+	u8 gain_mf_snapshot[EQ_SNAPSHOTS_SIZE];
+
+	/* Offset status snapshot */
+	u8 osestat_snapshot[EQ_SNAPSHOTS_SIZE];
+};
+
+static enum bin_state get_bin_snapshots_state(struct eq_data_priv *priv,
+					      enum eqc_type type)
+{
+	s16 bin_snp_av_thr_low, bin_snp_av_thr_high;
+	s16 snapshot_average, snapshot_sum = 0;
+	s16 *bin_snapshot;
+	struct eqc_range *bin_range;
+	int i;
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return BIN_INVALID;
+	}
+
+	switch (type) {
+	case EQC_BIN_1:
+		bin_snapshot = priv->bin1_snapshot;
+		break;
+	case EQC_BIN_2:
+		bin_snapshot = priv->bin2_snapshot;
+		break;
+	case EQC_BIN_3:
+		bin_snapshot = priv->bin3_snapshot;
+		break;
+	case EQC_BIN_LONG:
+		bin_snapshot = priv->bin_long_snapshot;
+		break;
+	case EQC_BIN_OFFSET:
+		bin_snapshot = priv->bin_offset_snapshot;
+		break;
+	case EQC_BIN_M1:
+		bin_snapshot = priv->bin_m1_snapshot;
+		break;
+	default:
+		/* invalid bin type */
+		return BIN_INVALID;
+	}
+	if (!bin_snapshot)
+		return BIN_INVALID;
+
+	bin_range = priv->eq_setup.equalizer.ops.get_counter_range(type);
+	if (!bin_range)
+		return BIN_INVALID;
+
+	bin_snp_av_thr_low = bin_range->mid_low;
+	bin_snp_av_thr_high = bin_range->mid_high;
+
+	for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++)
+		snapshot_sum += bin_snapshot[i];
+
+	snapshot_average = (s16)(snapshot_sum / EQ_SNAPSHOTS_SIZE);
+
+	if (snapshot_average >= -256 && snapshot_average < bin_snp_av_thr_low)
+		return BIN_EARLY;
+	else if (snapshot_average >= bin_snp_av_thr_low &&
+		 snapshot_average < bin_snp_av_thr_high)
+		return BIN_TOGGLE;
+	else if (snapshot_average >= bin_snp_av_thr_high &&
+		 snapshot_average <= 255)
+		return BIN_LATE;
+
+	return BIN_INVALID;
+}
+
+static u32 process_bin_m1_toggle(struct eq_data_priv *priv)
+{
+	enum req_type prev_req_cm1;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	prev_req_cm1 = lt_decode_coef_update(priv->ld_update, C_M1);
+
+	/* Toggle path */
+	if (priv->prev_bin_m1_state == priv->bin_m1_state) {
+		/* Hold C- */
+		update = lt_encode_startup_request(REQ_HOLD);
+	} else {
+		update = lt_encode_startup_request(REQ_HOLD);
+		/* If previous step moved C- repeat C- move */
+		if (prev_req_cm1 == REQ_INC ||
+		    prev_req_cm1 == REQ_DEC)
+			update = lt_encode_request(update, prev_req_cm1, C_M1);
+	}
+
+	return update;
+}
+
+static u32 process_bin_m1_prev_toggle(struct eq_data_priv *priv)
+{
+	enum req_type prev_req_cm1;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	prev_req_cm1 = lt_decode_coef_update(priv->ld_update, C_M1);
+
+	update = lt_encode_startup_request(REQ_HOLD);
+	/* If previous step moved C- go back on C- */
+	if (prev_req_cm1 == REQ_INC)
+		update = lt_encode_request(update, REQ_DEC, C_M1);
+	if (prev_req_cm1 == REQ_DEC)
+		update = lt_encode_request(update, REQ_INC, C_M1);
+
+	return update;
+}
+
+static u32 process_bin_m1_early(struct eq_data_priv *priv)
+{
+	enum coef_status lpst_cm1;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	/* Get LP coefficient status to determine
+	 * if coefficient is in range or reached the limit thresholds
+	 * IF the coefficient is at MIN/MAX and still want to INC/DEC
+	 * THEN do we are done with this module
+	 */
+	lpst_cm1 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_M1);
+
+	/* Early path */
+	if (lpst_cm1 == COEF_MAX) {
+		/* Hold C- */
+		update = lt_encode_startup_request(REQ_HOLD);
+	} else {
+		/* request Increment C- */
+		update = lt_encode_request(update, REQ_INC, C_M1);
+	}
+
+	return update;
+}
+
+static u32 process_bin_m1_late(struct eq_data_priv *priv)
+{
+	enum coef_status lpst_cm1;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	/* Get LP coefficient status to determine
+	 * if coefficient is in range or reached the limit thresholds
+	 * IF the coefficient is at MIN/MAX and still want to INC/DEC
+	 * THEN do we are done with this module
+	 */
+	lpst_cm1 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_M1);
+
+	/* Late path */
+	if (lpst_cm1 == COEF_MIN) {
+		/* Hold C- */
+		update = lt_encode_startup_request(REQ_HOLD);
+	} else {
+		/* request Decrement C- */
+		update = lt_encode_request(update, REQ_DEC, C_M1);
+	}
+
+	return update;
+}
+
+static u32 process_bin_m1_antipodal(struct eq_data_priv *priv)
+{
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	if (priv->bin_m1_state == BIN_LATE) {
+		/* request Decrement C- */
+		update = lt_encode_request(update, REQ_DEC, C_M1);
+	} else {
+		/* Hold C- */
+		update = lt_encode_startup_request(REQ_HOLD);
+	}
+
+	return update;
+}
+
+/* process_bin_m1
+ *
+ * Bin_M1:
+ *   contains the scoring of initial edges on pulses that are 1UI long
+ *      following non-single bits
+ *   used to adjust LP coefficient: C_M1
+ */
+static void process_bin_m1(struct eq_data_priv *priv)
+{
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return;
+	}
+
+	if (priv->bin_m1_state == BIN_INVALID) {
+		bpdev_err(priv->eq_setup.bpphy, "Invalid Bin_M1 state\n");
+		return;
+	}
+
+	if (priv->bin_m1_state == BIN_TOGGLE) {
+		update = process_bin_m1_toggle(priv);
+	} else {
+		if (priv->prev_bin_m1_state == BIN_TOGGLE) {
+			update = process_bin_m1_prev_toggle(priv);
+		} else {
+			if (priv->prev_bin_m1_state == priv->bin_m1_state) {
+				if (priv->bin_m1_state == BIN_LATE)
+					update = process_bin_m1_late(priv);
+				else
+					update = process_bin_m1_early(priv);
+			} else {
+				update = process_bin_m1_antipodal(priv);
+			}
+		}
+	}
+
+	/* Store current algorithm decision
+	 * as previous algorithm ld_update for next step
+	 */
+	priv->ld_update = update;
+}
+
+static u32 process_bin_long_toggle(struct eq_data_priv *priv)
+{
+	enum req_type prev_req_cp1, prev_req_cz0;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1);
+	prev_req_cz0 = lt_decode_coef_update(priv->ld_update, C_Z0);
+
+	/* Toggle path */
+	if (priv->prev_bin_long_state == priv->bin_long_state) {
+		/* Hold C+ and C0 */
+		update = lt_encode_startup_request(REQ_HOLD);
+	} else {
+		update = lt_encode_startup_request(REQ_HOLD);
+		/* If previous step moved C+/C0 repeat C+/C0 move */
+		if (prev_req_cp1 == REQ_INC ||
+		    prev_req_cp1 == REQ_DEC ||
+		    prev_req_cz0 == REQ_INC ||
+		    prev_req_cz0 == REQ_DEC) {
+			update = lt_encode_request(update, prev_req_cp1, C_P1);
+			update = lt_encode_request(update, prev_req_cz0, C_Z0);
+		}
+	}
+
+	return update;
+}
+
+static u32 process_bin_long_prev_toggle(struct eq_data_priv *priv)
+{
+	enum req_type prev_req_cp1, prev_req_cz0;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1);
+	prev_req_cz0 = lt_decode_coef_update(priv->ld_update, C_Z0);
+
+	/* If previous step moved C+/C0 then go back on C+/C0 */
+	if (prev_req_cp1 == REQ_INC)
+		update = lt_encode_request(update, REQ_DEC, C_P1);
+	if (prev_req_cp1 == REQ_DEC)
+		update = lt_encode_request(update, REQ_INC, C_P1);
+	if (prev_req_cz0 == REQ_INC)
+		update = lt_encode_request(update, REQ_DEC, C_Z0);
+	if (prev_req_cz0 == REQ_DEC)
+		update = lt_encode_request(update, REQ_INC, C_Z0);
+
+	return update;
+}
+
+static u32 process_bin_long_early(struct eq_data_priv *priv)
+{
+	enum coef_status lpst_cp1, lpst_cz0;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	/* Get LP coefficient status to determine
+	 * if coefficient is in range or reached the limit thresholds
+	 * IF the coefficient is at MIN/MAX and still want to INC/DEC
+	 * THEN do we are done with this module
+	 */
+	lpst_cp1 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_P1);
+	lpst_cz0 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_Z0);
+
+	/* Early path (make edge later) */
+	if (lpst_cp1 == COEF_MAX) {
+		if (lpst_cz0 == COEF_MAX) {
+			/* Hold C+, C0 */
+			update = lt_encode_startup_request(REQ_HOLD);
+		} else {
+			/* request Increment C0 and
+			 * Decrement C+
+			 */
+			update = lt_encode_request(update, REQ_INC, C_Z0);
+			update = lt_encode_request(update, REQ_DEC, C_P1);
+		}
+	} else {
+		/* request Increment C+ */
+		update = lt_encode_request(update, REQ_INC, C_P1);
+	}
+
+	return update;
+}
+
+static u32 process_bin_long_late(struct eq_data_priv *priv)
+{
+	enum coef_status lpst_cp1, lpst_cz0;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	/* Get LP coefficient status to determine
+	 * if coefficient is in range or reached the limit thresholds
+	 * IF the coefficient is at MIN/MAX and still want to INC/DEC
+	 * THEN do we are done with this module
+	 */
+	lpst_cp1 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_P1);
+	lpst_cz0 = lt_get_lp_coef_status(priv->eq_setup.krlane, C_Z0);
+
+	/* Late path (make edge earlier) */
+	if (lpst_cp1 == COEF_MIN) {
+		if (lpst_cz0 == COEF_MIN) {
+			/* Hold C0 */
+			update = lt_encode_startup_request(REQ_HOLD);
+		} else {
+			/* request Decrement C0 */
+			update = lt_encode_request(update, REQ_DEC, C_Z0);
+		}
+	} else {
+		/* request Decrement C+ */
+		update = lt_encode_request(update, REQ_DEC, C_P1);
+	}
+
+	return update;
+}
+
+static u32 process_bin_long_antipodal(struct eq_data_priv *priv)
+{
+	enum req_type prev_req_cp1;
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return update;
+	}
+
+	prev_req_cp1 = lt_decode_coef_update(priv->ld_update, C_P1);
+
+	/* Request move on C+ and C0 */
+	/* If previous step moved C+ then go back on C+ */
+	if (prev_req_cp1 == REQ_INC)
+		update = lt_encode_request(update, REQ_DEC, C_P1);
+	if (prev_req_cp1 == REQ_DEC)
+		update = lt_encode_request(update, REQ_INC, C_P1);
+
+	if (priv->bin_long_state == BIN_LATE) {
+		/* request Decrement C0 */
+		update = lt_encode_request(update, REQ_DEC, C_Z0);
+	} else {
+		/* request Increment C0 */
+		update = lt_encode_request(update, REQ_INC, C_Z0);
+	}
+
+	return update;
+}
+
+/* process_bin_long
+ *
+ * Bin_Long:
+ *   contains the scoring of final edges on pulses longer than 7UI long
+ *   used to adjust LP coefficients: C_P1 and C_Z0
+ */
+static void process_bin_long(struct eq_data_priv *priv)
+{
+	u32 update = lt_encode_startup_request(REQ_HOLD);
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return;
+	}
+
+	if (priv->bin_long_state == BIN_INVALID) {
+		bpdev_err(priv->eq_setup.bpphy, "Invalid Bin_Long state\n");
+		return;
+	}
+
+	if (priv->bin_long_state == BIN_TOGGLE) {
+		update = process_bin_long_toggle(priv);
+	} else {
+		if (priv->prev_bin_long_state == BIN_TOGGLE) {
+			update = process_bin_long_prev_toggle(priv);
+		} else {
+			if (priv->prev_bin_long_state == priv->bin_long_state) {
+				if (priv->bin_long_state == BIN_LATE)
+					update = process_bin_long_late(priv);
+				else
+					update = process_bin_long_early(priv);
+			} else {
+				update = process_bin_long_antipodal(priv);
+			}
+		}
+	}
+
+	/* Store current algorithm decision
+	 * as previous algorithm ld_update for next step
+	 */
+	priv->ld_update = update;
+}
+
+/* Callbacks:
+ * required by generic equalization_algorithm
+ */
+static void process_bad_state(struct eq_data_priv *priv)
+{
+	bool lp_at_init, lp_at_preset;
+	u32 req;
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return;
+	}
+
+	lp_at_init = lt_is_lp_at_startup(priv->eq_setup.krlane, REQ_INIT);
+	lp_at_preset = lt_is_lp_at_startup(priv->eq_setup.krlane, REQ_PRESET);
+
+	if (lp_at_init) {
+		/* Try Request Preset */
+		req = lt_encode_startup_request(REQ_PRESET);
+		lt_lp_update(priv->eq_setup.krlane, req);
+	} else if (lp_at_preset) {
+		/* LT ERROR
+		 * set lt_error flag to prevent reaching
+		 * training state = TRAINED
+		 * and resume training in case of LT error
+		 */
+		lt_set_error(priv->eq_setup.krlane, true);
+		bpdev_err(priv->eq_setup.bpphy,
+			  "LT Error: CDR_LOCK is zero on Preset\n");
+	} else {
+		/* Move LP back to previous C-, C0, C+ and HOLD */
+		lt_move_lp_back(priv->eq_setup.krlane);
+	}
+}
+
+static bool collect_bin_counters(struct eq_data_priv *priv,
+				 enum eqc_type type)
+{
+	struct equalizer_ops *eqops;
+	s16 *bin_snapshot = NULL;
+	int snp_size;
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return false;
+	}
+
+	/* collect Bin snapshots */
+	switch (type) {
+	case EQC_BIN_1:
+		bin_snapshot = priv->bin1_snapshot;
+		break;
+	case EQC_BIN_2:
+		bin_snapshot = priv->bin2_snapshot;
+		break;
+	case EQC_BIN_3:
+		bin_snapshot = priv->bin3_snapshot;
+		break;
+	case EQC_BIN_LONG:
+		bin_snapshot = priv->bin_long_snapshot;
+		break;
+	case EQC_BIN_OFFSET:
+		bin_snapshot = priv->bin_offset_snapshot;
+		break;
+	case EQC_BIN_M1:
+		bin_snapshot = priv->bin_m1_snapshot;
+		break;
+	default:
+		/* invalid bin type */
+		return false;
+	}
+	if (!bin_snapshot)
+		return false;
+
+	eqops = &priv->eq_setup.equalizer.ops;
+	if (!eqops) {
+		bpdev_err(priv->eq_setup.bpphy,
+			  "No operations for equalizer %s %s\n",
+			  priv->eq_setup.equalizer.name,
+			  priv->eq_setup.equalizer.version);
+		return false;
+	}
+	snp_size = eqops->collect_counters(priv->eq_setup.reg_base, type,
+					   bin_snapshot, EQ_SNAPSHOTS_SIZE);
+	/* Check if snapshots collection failed */
+	if (snp_size < EQ_SNAPSHOTS_SIZE) {
+		bpdev_err(priv->eq_setup.bpphy,
+			  "Counters collection failed for equalizer %s %s\n",
+			  priv->eq_setup.equalizer.name,
+			  priv->eq_setup.equalizer.version);
+		return false;
+	}
+
+	/* if CDR_LOCK = 0: Statistics are invalid */
+	if (!backplane_is_cdr_lock(priv->eq_setup.krlane, true)) {
+		process_bad_state(priv);
+		return false;
+	}
+
+	return true;
+}
+
+static bool collect_bit_edge_statistics(struct eq_data_priv *priv)
+{
+	struct equalizer_ops *eqops;
+	enum eqc_type status_types[] = {
+		EQC_GAIN_HF, EQC_GAIN_MF, EQC_EQOFFSET
+	};
+	s16 status_counters[ARRAY_SIZE(status_types)][EQ_SNAPSHOTS_SIZE];
+	int i, snp_size;
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return false;
+	}
+
+	/* collect Bin snapshots */
+	if (!collect_bin_counters(priv, EQC_BIN_1))
+		return false;
+	if (!collect_bin_counters(priv, EQC_BIN_2))
+		return false;
+	if (!collect_bin_counters(priv, EQC_BIN_3))
+		return false;
+	if (!collect_bin_counters(priv, EQC_BIN_LONG))
+		return false;
+	if (!collect_bin_counters(priv, EQC_BIN_OFFSET))
+		return false;
+	if (!collect_bin_counters(priv, EQC_BIN_M1))
+		return false;
+
+	/* collect Gains */
+	eqops = &priv->eq_setup.equalizer.ops;
+	if (!eqops) {
+		bpdev_err(priv->eq_setup.bpphy,
+			  "No operations for equalizer %s %s\n",
+			  priv->eq_setup.equalizer.name,
+			  priv->eq_setup.equalizer.version);
+		return false;
+	}
+	snp_size = eqops->collect_multiple_counters(priv->eq_setup.reg_base,
+						    status_types,
+						    ARRAY_SIZE(status_types),
+						    (s16 *)status_counters,
+						    EQ_SNAPSHOTS_SIZE);
+	/* Check if snapshots collection failed */
+	if (snp_size < EQ_SNAPSHOTS_SIZE) {
+		bpdev_err(priv->eq_setup.bpphy,
+			  "Counters collection failed for equalizer %s %s\n",
+			  priv->eq_setup.equalizer.name,
+			  priv->eq_setup.equalizer.version);
+		return false;
+	}
+
+	for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+		priv->gain_hf_snapshot[i] = (u8)status_counters[0][i];
+		priv->gain_mf_snapshot[i] = (u8)status_counters[1][i];
+		priv->osestat_snapshot[i] = (u8)status_counters[2][i];
+	}
+
+	return true;
+}
+
+static void generate_3taps_request(struct eq_data_priv *priv)
+{
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return;
+	}
+
+	/* Store current state as previous state */
+	priv->prev_bin_m1_state = priv->bin_m1_state;
+	priv->prev_bin_long_state = priv->bin_long_state;
+
+	priv->bin_m1_state = get_bin_snapshots_state(priv, EQC_BIN_M1);
+	if (priv->bin_m1_state == BIN_INVALID) {
+		/* invalid state: should never happen */
+		return;
+	}
+
+	priv->bin_long_state = get_bin_snapshots_state(priv, EQC_BIN_LONG);
+	if (priv->bin_long_state == BIN_INVALID) {
+		/* invalid state: should never happen */
+		return;
+	}
+
+	/* Move to BinLong/BinM1 modules:
+	 * Bin Modules order: BinLong before BinM1
+	 * We try to finish BinLong before we do BinM1
+	 */
+
+	/* Process BinLong module
+	 * decide and ask for movement of C+/C0
+	 */
+	if (!priv->bin_long_stop) {
+		process_bin_long(priv);
+		lt_lp_update(priv->eq_setup.krlane, priv->ld_update);
+		if (lt_is_update_of_type(priv->ld_update, REQ_HOLD)) {
+			/* Sent All Hold request */
+			priv->long_min_max_cnt++;
+			if (priv->long_min_max_cnt >= TIMEOUT_LONG)
+				priv->bin_long_stop = true;
+		} else {
+			/* Sent C Inc/Dec request */
+			priv->long_min_max_cnt = 0;
+		}
+		return;
+	}
+
+	/* Process BinM1 module
+	 * decide and ask for movement of C-
+	 */
+	if (!priv->bin_m1_stop) {
+		process_bin_m1(priv);
+		lt_lp_update(priv->eq_setup.krlane, priv->ld_update);
+		if (lt_is_update_of_type(priv->ld_update, REQ_HOLD)) {
+			/* Sent All Hold request */
+			priv->m1_min_max_cnt++;
+			if (priv->m1_min_max_cnt >= TIMEOUT_M1)
+				priv->bin_m1_stop = true;
+		} else {
+			/* Sent C Inc/Dec request */
+			priv->m1_min_max_cnt = 0;
+		}
+		return;
+	}
+}
+
+static bool is_rx_ok(struct eq_data_priv *priv)
+{
+	enum bin_state bin1_snapshot_state;
+	enum bin_state bin2_snapshot_state;
+	enum bin_state bin3_snapshot_state;
+	struct eqc_range *gain_range, *osestat_range;
+	struct equalizer_ops *eqops;
+	bool rx_quality_1, rx_quality_2;
+	bool is_ok, rx_good_quality;
+	u8 min_eq_gain, max_eq_gain;
+	u8 osestat_mid_low, osestat_mid_high;
+	u8 min_snp, max_snp;
+	s16 snapshot;
+	int i;
+
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return false;
+	}
+
+	/* Checking Bins/Gains after LP has updated its TX */
+	eqops = &priv->eq_setup.equalizer.ops;
+	if (!eqops)
+		return false;
+	gain_range = eqops->get_counter_range(EQC_GAIN_HF);
+	if (!gain_range)
+		return false;
+	osestat_range = eqops->get_counter_range(EQC_EQOFFSET);
+	if (!osestat_range)
+		return false;
+
+	min_eq_gain = (u8)gain_range->min;
+	max_eq_gain = (u8)gain_range->max;
+	osestat_mid_low = (u8)osestat_range->mid_low;
+	osestat_mid_high = (u8)osestat_range->mid_high;
+
+	/* CDR_LOCK must be 1 */
+	if (!backplane_is_cdr_lock(priv->eq_setup.krlane, true))
+		return false;
+
+	/* Offset Bin must NOT be 10 of the same value */
+	rx_good_quality = false;
+	snapshot = priv->bin_offset_snapshot[0];
+	for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+		if (snapshot != priv->bin_offset_snapshot[i]) {
+			rx_good_quality = true;
+			break;
+		}
+	}
+	if (!rx_good_quality)
+		return false;
+
+	/* Offset status must dither (+/-2) around MidRange value
+	 * What we want to see is that the Offset has settled to a value
+	 * somewhere between: mid-range low and mid-range high and that
+	 * the series of snapshot values are +/-2 of the settled value.
+	 */
+	rx_good_quality = true;
+	min_snp = priv->osestat_snapshot[0];
+	max_snp = priv->osestat_snapshot[0];
+	for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+		if (priv->osestat_snapshot[i] < osestat_mid_low ||
+		    priv->osestat_snapshot[i] > osestat_mid_high) {
+			rx_good_quality = false;
+			break;
+		}
+		if (priv->osestat_snapshot[i] < min_snp)
+			min_snp = priv->osestat_snapshot[i];
+		if (priv->osestat_snapshot[i] > max_snp)
+			max_snp = priv->osestat_snapshot[i];
+	}
+	if (max_snp - min_snp > 4)
+		rx_good_quality = false;
+	if (!rx_good_quality)
+		return false;
+
+	/* The Rx is in good quality if:
+	 * Bin1, Bin2, and Bin3 are toggling
+	 * Proceed to BinLong/BinM1 modules
+	 */
+	bin1_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_1);
+	bin2_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_2);
+	bin3_snapshot_state = get_bin_snapshots_state(priv, EQC_BIN_3);
+
+	rx_good_quality = (bin1_snapshot_state == BIN_TOGGLE &&
+			   bin2_snapshot_state == BIN_TOGGLE &&
+			   bin3_snapshot_state == BIN_TOGGLE);
+
+	/* If Rx is in good quality then proceed to BinLong/BinM1 */
+	if (rx_good_quality)
+		return true;
+
+#ifdef ENABLE_LESS_QUALITY_CONDITION
+	rx_quality_1 = false;
+	rx_quality_2 = false;
+	if (bin1_snapshot_state == BIN_TOGGLE) {
+		if (bin2_snapshot_state == BIN_EARLY &&
+		    bin3_snapshot_state == BIN_LATE) {
+			/* check if GainMF is stuck at max_eq_gain */
+			rx_quality_1 = true;
+			for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+				if (priv->gain_mf_snapshot[i] != max_eq_gain) {
+					rx_quality_1 = false;
+					break;
+				}
+			}
+		}
+		if (bin2_snapshot_state == BIN_LATE &&
+		    bin3_snapshot_state == BIN_EARLY) {
+			/* check if GainMF is stuck at min_eq_gain */
+			rx_quality_2 = true;
+			for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+				if (priv->gain_mf_snapshot[i] != min_eq_gain) {
+					rx_quality_2 = false;
+					break;
+				}
+			}
+		}
+	}
+
+	/* If Rx is less quality then proceed to BinLong/BinM1 */
+	if (rx_quality_1 || rx_quality_2)
+		return true;
+#endif
+
+#ifdef ENABLE_EVEN_LESS_QUALITY_CONDITION
+	rx_quality_1 = false;
+	rx_quality_2 = false;
+	if (bin1_snapshot_state == BIN_EARLY &&
+	    bin2_snapshot_state == BIN_LATE) {
+		/* check if GainHF is stuck at max_eq_gain */
+		is_ok = true;
+		for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+			if (priv->gain_hf_snapshot[i] != max_eq_gain) {
+				is_ok = false;
+				break;
+			}
+		}
+		if (is_ok) {
+			/* check if GainMF is stuck at min_eq_gain */
+			is_ok = true;
+			for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+				if (priv->gain_mf_snapshot[i] != min_eq_gain) {
+					is_ok = false;
+					break;
+				}
+			}
+			if (is_ok)
+				rx_quality_1 = true;
+		}
+	}
+	if (bin1_snapshot_state == BIN_LATE &&
+	    bin2_snapshot_state == BIN_EARLY) {
+		/* check if GainHF is stuck at min_eq_gain */
+		is_ok = true;
+		for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+			if (priv->gain_hf_snapshot[i] != min_eq_gain) {
+				is_ok = false;
+				break;
+			}
+		}
+		if (is_ok) {
+			/* check if GainMF is stuck at max_eq_gain */
+			is_ok = true;
+			for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+				if (priv->gain_mf_snapshot[i] != max_eq_gain) {
+					is_ok = false;
+					break;
+				}
+			}
+			if (is_ok)
+				rx_quality_2 = true;
+		}
+	}
+
+	/* If Rx is in good quality then proceed to BinLong/BinM1 */
+	if (rx_quality_1 || rx_quality_2)
+		return true;
+#endif
+
+#ifdef ENABLE_SEEMINGLY_QUALITY_CONDITION
+	rx_quality_1 = false;
+	if (bin1_snapshot_state == BIN_LATE &&
+	    bin2_snapshot_state == BIN_TOGGLE &&
+	    bin3_snapshot_state == BIN_TOGGLE) {
+		/* check if GainHF is stuck at min_eq_gain */
+		rx_quality_1 = true;
+		for (i = 0; i < EQ_SNAPSHOTS_SIZE; i++) {
+			if (priv->gain_hf_snapshot[i] != min_eq_gain) {
+				rx_quality_1 = false;
+				break;
+			}
+		}
+	}
+
+	if (rx_quality_1)
+		return true;
+#endif
+
+	return false;
+}
+
+static bool is_eq_done(struct eq_data_priv *priv)
+{
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return false;
+	}
+
+	return (priv->bin_m1_stop && priv->bin_long_stop);
+}
+
+/* BEE 3 TAP Algorithm API */
+
+/* Create BEE 3-TAP Equalization Algorithm */
+static struct eq_data_priv *create(struct eq_setup_info setup)
+{
+	struct eq_data_priv *bee_data;
+
+	bee_data = devm_kzalloc(&setup.bpphy->mdio.dev,
+				sizeof(*bee_data), GFP_KERNEL);
+	if (!bee_data)
+		return NULL;
+
+	/* initialize algorithm setup data */
+	bee_data->eq_setup = setup;
+
+	/* initialize specific BEE algorithm data */
+	bee_data->bin_m1_state = BIN_INVALID;
+	bee_data->bin_long_state = BIN_INVALID;
+	bee_data->prev_bin_m1_state = BIN_INVALID;
+	bee_data->prev_bin_long_state = BIN_INVALID;
+	bee_data->m1_min_max_cnt = 0;
+	bee_data->long_min_max_cnt = 0;
+	bee_data->bin_m1_stop = false;
+	bee_data->bin_long_stop = false;
+	bee_data->ld_update = 0;
+
+	return bee_data;
+}
+
+static void destroy(struct eq_data_priv *priv)
+{
+	if (!priv) {
+		pr_err("%s: NULL private data\n", ALGORITHM_NAME);
+		return;
+	}
+
+	kfree(priv);
+}
+
+static const struct equalization_algorithm eq_alg = {
+	.name = ALGORITHM_NAME,
+	.descr = ALGORITHM_DESCR,
+	.version = ALGORITHM_VERSION,
+	.use_local_tx_training = true,
+	.use_remote_tx_training = true,
+	.ops = {
+		.create = create,
+		.destroy = destroy,
+		.is_rx_ok = is_rx_ok,
+		.is_eq_done = is_eq_done,
+		.collect_statistics = collect_bit_edge_statistics,
+		.generate_request = generate_3taps_request,
+		.process_bad_state = process_bad_state,
+		.dump_algorithm_context = NULL,
+	}
+};
+
+static const char * const alg_keys[] = {
+	"bee",
+	"BEE",
+};
+
+static int __init bee_init(void)
+{
+	int i, err;
+
+	pr_info("%s: %s algorithm version %s\n",
+		ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION);
+
+	/* register BEE algorithm: */
+	for (i = 0; i < ARRAY_SIZE(alg_keys); i++) {
+		err = backplane_eq_register(alg_keys[i], &eq_alg);
+		if (err) {
+			pr_err("%s: '%s' equalization algorithm registration failed\n",
+			       ALGORITHM_NAME, alg_keys[i]);
+		}
+	}
+
+	return 0;
+}
+
+static void __exit bee_exit(void)
+{
+	int i;
+
+	/* unregister BEE algorithm: */
+	for (i = 0; i < ARRAY_SIZE(alg_keys); i++)
+		backplane_eq_unregister(alg_keys[i]);
+
+	pr_info("%s: %s algorithm version %s unloaded\n",
+		ALGORITHM_NAME, ALGORITHM_DESCR, ALGORITHM_VERSION);
+}
+
+module_init(bee_init);
+module_exit(bee_exit);
+
+MODULE_DESCRIPTION("Bit Edge Equalization Algorithm");
+MODULE_AUTHOR("Florinel Iordache <florinel.iordache@nxp.com>");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.9.1


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

* [PATCH net-next 9/9] arm64: dts: add serdes and mdio description
  2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
                   ` (7 preceding siblings ...)
  2020-03-26 13:51 ` [PATCH net-next 8/9] net: phy: add bee algorithm for kr training Florinel Iordache
@ 2020-03-26 13:51 ` Florinel Iordache
  2020-03-27 12:09   ` Russell King - ARM Linux admin
  8 siblings, 1 reply; 42+ messages in thread
From: Florinel Iordache @ 2020-03-26 13:51 UTC (permalink / raw)
  To: davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

Add dt nodes with serdes, lanes, mdio generic description for supported
platforms: ls1046, ls1088, ls2088, lx2160. This is a prerequisite to
enable backplane on device tree for these platforms.

Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
---
 arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi     |  33 ++++-
 arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi     |  97 ++++++++++++-
 arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi     | 160 ++++++++++++++++++++-
 arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi     | 128 ++++++++++++++++-
 .../boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi    |   5 +-
 .../boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi    |   5 +-
 6 files changed, 418 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index d4c1da3..c7d845f 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -3,7 +3,7 @@
  * Device Tree Include file for Freescale Layerscape-1046A family SoC.
  *
  * Copyright 2016 Freescale Semiconductor, Inc.
- * Copyright 2018 NXP
+ * Copyright 2018, 2020 NXP
  *
  * Mingkai Hu <mingkai.hu@nxp.com>
  */
@@ -735,6 +735,37 @@
 			status = "disabled";
 		};
 
+		serdes1: serdes@1ea0000 {
+			compatible = "serdes-10g";
+			reg = <0x0 0x1ea0000 0 0x00002000>;
+			reg-names = "serdes", "serdes-10g";
+			big-endian;
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x0 0x00 0x1ea0000 0x00002000>;
+			lane_a: lane@800 {
+				compatible = "lane-10g";
+				reg = <0x800 0x40>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_b: lane@840 {
+				compatible = "lane-10g";
+				reg = <0x840 0x40>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_c: lane@880 {
+				compatible = "lane-10g";
+				reg = <0x880 0x40>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_d: lane@8c0 {
+				compatible = "lane-10g";
+				reg = <0x8c0 0x40>;
+				reg-names = "lane", "serdes-lane";
+			};
+		};
+
 		pcie_ep@3600000 {
 			compatible = "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep";
 			reg = <0x00 0x03600000 0x0 0x00100000
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
index 5945662..474464e 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
@@ -2,7 +2,7 @@
 /*
  * Device Tree Include file for NXP Layerscape-1088A family SoC.
  *
- * Copyright 2017 NXP
+ * Copyright 2017, 2020 NXP
  *
  * Harninder Rai <harninder.rai@nxp.com>
  *
@@ -325,6 +325,69 @@
 			#interrupt-cells = <2>;
 		};
 
+		/* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */
+		emdio1: mdio@8B96000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8B96000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;	/* force the driver in LE mode */
+
+			/* Not necessary on the QDS, but needed on the RDB */
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		/* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */
+		emdio2: mdio@8B97000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8B97000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;	/* force the driver in LE mode */
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio1: mdio@0x8c07000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c07000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio2: mdio@0x8c0b000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0b000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio3: mdio@0x8c0f000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0f000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio4: mdio@0x8c13000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c13000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
 		ifc: ifc@2240000 {
 			compatible = "fsl,ifc", "simple-bus";
 			reg = <0x0 0x2240000 0x0 0x20000>;
@@ -777,6 +840,38 @@
 				};
 			};
 		};
+
+		serdes1: serdes@1ea0000 {
+				compatible = "serdes-10g";
+				reg = <0x0 0x1ea0000 0 0x00002000>;
+				reg-names = "serdes", "serdes-10g";
+				little-endian;
+
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x00 0x1ea0000 0x00002000>;
+				lane_a: lane@800 {
+					compatible = "lane-10g";
+					reg = <0x800 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_b: lane@840 {
+					compatible = "lane-10g";
+					reg = <0x840 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_c: lane@880 {
+					compatible = "lane-10g";
+					reg = <0x880 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_d: lane@8c0 {
+					compatible = "lane-10g";
+					reg = <0x8c0 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+		};
+
 	};
 
 	firmware {
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
index f96d06d..e8f3026 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
@@ -3,7 +3,7 @@
  * Device Tree Include file for Freescale Layerscape-2080A family SoC.
  *
  * Copyright 2016 Freescale Semiconductor, Inc.
- * Copyright 2017 NXP
+ * Copyright 2017, 2020 NXP
  *
  * Abhimanyu Saini <abhimanyu.saini@nxp.com>
  *
@@ -560,6 +560,113 @@
 			#interrupt-cells = <2>;
 		};
 
+		/* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */
+		emdio1: mdio@8B96000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8B96000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;	/* force the driver in LE mode */
+
+			/* Not necessary on the QDS, but needed on the RDB */
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		/* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */
+		emdio2: mdio@8B97000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8B97000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;	/* force the driver in LE mode */
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio1: mdio@0x8c07000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c07000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio2: mdio@0x8c0b000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0b000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio3: mdio@0x8c0f000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0f000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio4: mdio@0x8c13000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c13000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio5: mdio@0x8c17000 {
+			status = "disabled";
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c17000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio6: mdio@0x8c1b000 {
+			status = "disabled";
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c1b000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio7: mdio@0x8c1f000 {
+			status = "disabled";
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c1f000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio8: mdio@0x8c23000 {
+			status = "disabled";
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c23000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
 		i2c0: i2c@2000000 {
 			status = "disabled";
 			compatible = "fsl,vf610-i2c";
@@ -754,6 +861,57 @@
 			snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>;
 		};
 
+		serdes1: serdes@1ea0000 {
+				compatible = "serdes-10g";
+				reg = <0x0 0x1ea0000 0 0x00002000>;
+				reg-names = "serdes", "serdes-10g";
+				little-endian;
+
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x00 0x1ea0000 0x00002000>;
+				lane_a: lane@800 {
+					compatible = "lane-10g";
+					reg = <0x800 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_b: lane@840 {
+					compatible = "lane-10g";
+					reg = <0x840 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_c: lane@880 {
+					compatible = "lane-10g";
+					reg = <0x880 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_d: lane@8c0 {
+					compatible = "lane-10g";
+					reg = <0x8c0 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_e: lane@900 {
+					compatible = "lane-10g";
+					reg = <0x900 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_f: lane@940 {
+					compatible = "lane-10g";
+					reg = <0x940 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_g: lane@980 {
+					compatible = "lane-10g";
+					reg = <0x980 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+				lane_h: lane@9c0 {
+					compatible = "lane-10g";
+					reg = <0x9c0 0x40>;
+					reg-names = "lane", "serdes-lane";
+				};
+		};
+
 		ccn@4000000 {
 			compatible = "arm,ccn-504";
 			reg = <0x0 0x04000000 0x0 0x01000000>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
index e5ee559..2815908 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
@@ -2,7 +2,7 @@
 //
 // Device Tree Include file for Layerscape-LX2160A family SoC.
 //
-// Copyright 2018 NXP
+// Copyright 2018, 2020 NXP
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -947,9 +947,9 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 			little-endian;
-			status = "disabled";
 		};
 
+		/* WRIOP0: 0x8b8_0000, E-MDIO2: 0x1_7000 */
 		emdio2: mdio@8b97000 {
 			compatible = "fsl,fman-memac-mdio";
 			reg = <0x0 0x8b97000 0x0 0x1000>;
@@ -957,7 +957,129 @@
 			little-endian;
 			#address-cells = <1>;
 			#size-cells = <0>;
-			status = "disabled";
+		};
+
+		pcs_mdio1: mdio@0x8c07000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c07000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio2: mdio@0x8c0b000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0b000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio3: mdio@0x8c0f000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c0f000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio4: mdio@0x8c13000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c13000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio5: mdio@0x8c17000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c17000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio6: mdio@0x8c1b000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c1b000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio7: mdio@0x8c1f000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c1f000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		pcs_mdio8: mdio@0x8c23000 {
+			compatible = "fsl,fman-memac-mdio";
+			reg = <0x0 0x8c23000 0x0 0x1000>;
+			device_type = "mdio";
+			little-endian;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		serdes1: serdes@1ea0000 {
+			compatible = "serdes-28g";
+			reg = <0x0 0x1ea0000 0 0x00002000>;
+			reg-names = "serdes", "serdes-28g";
+			little-endian;
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x0 0x00 0x1ea0000 0x00002000>;
+			lane_a: lane@800 {
+				compatible = "lane-28g";
+				reg = <0x800 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_b: lane@900 {
+				compatible = "lane-28g";
+				reg = <0x900 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_c: lane@a00 {
+				compatible = "lane-28g";
+				reg = <0xa00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_d: lane@b00 {
+				compatible = "lane-28g";
+				reg = <0xb00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_e: lane@c00 {
+				compatible = "lane-28g";
+				reg = <0xc00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_f: lane@d00 {
+				compatible = "lane-28g";
+				reg = <0xd00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_g: lane@e00 {
+				compatible = "lane-28g";
+				reg = <0xe00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
+			lane_h: lane@f00 {
+				compatible = "lane-28g";
+				reg = <0xf00 0x100>;
+				reg-names = "lane", "serdes-lane";
+			};
 		};
 
 		fsl_mc: fsl-mc@80c000000 {
diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
index dbd2fc3..d6191f1 100644
--- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
+++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
@@ -3,6 +3,7 @@
  * QorIQ FMan v3 10g port #0 device tree
  *
  * Copyright 2012-2015 Freescale Semiconductor Inc.
+ * Copyright 2020 NXP
  *
  */
 
@@ -21,7 +22,7 @@ fman@1a00000 {
 		fsl,fman-10g-port;
 	};
 
-	ethernet@f0000 {
+	mac9: ethernet@f0000 {
 		cell-index = <0x8>;
 		compatible = "fsl,fman-memac";
 		reg = <0xf0000 0x1000>;
@@ -29,7 +30,7 @@ fman@1a00000 {
 		pcsphy-handle = <&pcsphy6>;
 	};
 
-	mdio@f1000 {
+	mdio9: mdio@f1000 {
 		#address-cells = <1>;
 		#size-cells = <0>;
 		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
index 6fc5d25..1f6f28f 100644
--- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
+++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
@@ -3,6 +3,7 @@
  * QorIQ FMan v3 10g port #1 device tree
  *
  * Copyright 2012-2015 Freescale Semiconductor Inc.
+ * Copyright 2020 NXP
  *
  */
 
@@ -21,7 +22,7 @@ fman@1a00000 {
 		fsl,fman-10g-port;
 	};
 
-	ethernet@f2000 {
+	mac10: ethernet@f2000 {
 		cell-index = <0x9>;
 		compatible = "fsl,fman-memac";
 		reg = <0xf2000 0x1000>;
@@ -29,7 +30,7 @@ fman@1a00000 {
 		pcsphy-handle = <&pcsphy7>;
 	};
 
-	mdio@f3000 {
+	mdio10: mdio@f3000 {
 		#address-cells = <1>;
 		#size-cells = <0>;
 		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
-- 
1.9.1


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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
@ 2020-03-26 18:53   ` David Miller
  2020-03-26 18:55     ` Joe Perches
  2020-03-27  1:07   ` Andrew Lunn
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: David Miller @ 2020-03-26 18:53 UTC (permalink / raw)
  To: florinel.iordache
  Cc: netdev, andrew, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

From: Florinel Iordache <florinel.iordache@nxp.com>
Date: Thu, 26 Mar 2020 15:51:19 +0200

> +static void kr_reset_master_lane(struct kr_lane_info *krln)
> +{
> +	struct phy_device *bpphy = krln->bpphy;
> +	struct backplane_phy_info *bp_phy = bpphy->priv;
> +	const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;

Please use reverse christmas tree ordering for local variables.

Please audit your entire submission for this issue.

Thank you.

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 18:53   ` David Miller
@ 2020-03-26 18:55     ` Joe Perches
  2020-03-26 19:07       ` David Miller
  0 siblings, 1 reply; 42+ messages in thread
From: Joe Perches @ 2020-03-26 18:55 UTC (permalink / raw)
  To: David Miller, florinel.iordache
  Cc: netdev, andrew, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, 2020-03-26 at 11:53 -0700, David Miller wrote:
> From: Florinel Iordache <florinel.iordache@nxp.com>
> Date: Thu, 26 Mar 2020 15:51:19 +0200
> 
> > +static void kr_reset_master_lane(struct kr_lane_info *krln)
> > +{
> > +     struct phy_device *bpphy = krln->bpphy;
> > +     struct backplane_phy_info *bp_phy = bpphy->priv;
> > +     const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;
> 
> Please use reverse christmas tree ordering for local variables.

How (any why) do you suggest the first 2 entries here
should be ordered?



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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 18:55     ` Joe Perches
@ 2020-03-26 19:07       ` David Miller
  2020-03-26 19:42         ` Joe Perches
  0 siblings, 1 reply; 42+ messages in thread
From: David Miller @ 2020-03-26 19:07 UTC (permalink / raw)
  To: joe
  Cc: florinel.iordache, netdev, andrew, f.fainelli, hkallweit1, linux,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

From: Joe Perches <joe@perches.com>
Date: Thu, 26 Mar 2020 11:55:17 -0700

> On Thu, 2020-03-26 at 11:53 -0700, David Miller wrote:
>> From: Florinel Iordache <florinel.iordache@nxp.com>
>> Date: Thu, 26 Mar 2020 15:51:19 +0200
>> 
>> > +static void kr_reset_master_lane(struct kr_lane_info *krln)
>> > +{
>> > +     struct phy_device *bpphy = krln->bpphy;
>> > +     struct backplane_phy_info *bp_phy = bpphy->priv;
>> > +     const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;
>> 
>> Please use reverse christmas tree ordering for local variables.
> 
> How (any why) do you suggest the first 2 entries here
> should be ordered?

You have to sometimes put assignments into the code body rather than
the declarations in situations like this.

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 19:07       ` David Miller
@ 2020-03-26 19:42         ` Joe Perches
  0 siblings, 0 replies; 42+ messages in thread
From: Joe Perches @ 2020-03-26 19:42 UTC (permalink / raw)
  To: David Miller
  Cc: florinel.iordache, netdev, andrew, f.fainelli, hkallweit1, linux,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, 2020-03-26 at 12:07 -0700, David Miller wrote:
> From: Joe Perches <joe@perches.com>
> Date: Thu, 26 Mar 2020 11:55:17 -0700
> 
> > On Thu, 2020-03-26 at 11:53 -0700, David Miller wrote:
> >> From: Florinel Iordache <florinel.iordache@nxp.com>
> >> Date: Thu, 26 Mar 2020 15:51:19 +0200
> >> 
> >> > +static void kr_reset_master_lane(struct kr_lane_info *krln)
> >> > +{
> >> > +     struct phy_device *bpphy = krln->bpphy;
> >> > +     struct backplane_phy_info *bp_phy = bpphy->priv;
> >> > +     const struct lane_io_ops *lane_ops = krln->bp_phy->bp_dev.lane_ops;
> >> 
> >> Please use reverse christmas tree ordering for local variables.
> > 
> > How (any why) do you suggest the first 2 entries here
> > should be ordered?
> 
> You have to sometimes put assignments into the code body rather than
> the declarations in situations like this.

No "why" reply given.

An option is not using reverse christmas tree to both
avoid ordering
constraints and reduce overall line count.

I think this is your own personal taste rather than an
actual valuable addition for subsystem maintenance.

And if this a serious requirement for the subsystem
you oversee, you should add it to something like a
maintainer-entry-profile with a "P:" line in MAINTAINERS.

https://www.kernel.org/doc/html/latest/maintainer/maintainer-entry-profile.html




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

* Re: [PATCH net-next 7/9] net: phy: enable qoriq backplane support
  2020-03-26 13:51 ` [PATCH net-next 7/9] net: phy: enable qoriq backplane support Florinel Iordache
@ 2020-03-26 20:03   ` Joe Perches
  2020-03-26 20:13     ` Andrew Lunn
  0 siblings, 1 reply; 42+ messages in thread
From: Joe Perches @ 2020-03-26 20:03 UTC (permalink / raw)
  To: florinel.iordache, davem, netdev, andrew, f.fainelli, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, 2020-03-26 at 15:51 +0200, Florinel Iordache wrote:
> Enable backplane support for qoriq family of devices

trivial notes:

> diff --git a/drivers/net/phy/backplane/qoriq_backplane.c b/drivers/net/phy/backplane/qoriq_backplane.c
[]
> +static int qoriq_backplane_probe(struct phy_device *bpphy)
> +{
> +	static bool one_time_action = true;
> +
> +	if (one_time_action) {
> +		one_time_action = false;
> +		pr_info("%s: QorIQ Backplane driver version %s\n",
> +			QORIQ_BACKPLANE_DRIVER_NAME,
> +			QORIQ_BACKPLANE_DRIVER_VERSION);
> +	}

There is an existing mechanism for this:

	pr_info_once("%s: ... %s\n", etc...);

[]

> +static int qoriq_backplane_config_init(struct phy_device *bpphy)
> +{
[]
> +	for (i = 0; i < bp_phy->num_lanes; i++) {
[]
> +		ret = of_address_to_resource(lane_node, 0, &res);
> +		if (ret) {
> +			bpdev_err(bpphy,
> +				  "could not obtain lane memory map for index=%d, ret = %d\n",
> +				  i, ret);
> +			return ret;

This could use the new vsprintf %pe extension:

			bpdev_err(bpphy,
				  "could not obtain lane memory map for index=%d, %pe\n",
				  i, ERR_PTR(ret));

> +	ret = of_address_to_resource(serdes_node, 0, &res);
> +	if (ret) {
> +		bpdev_err(bpphy,
> +			  "could not obtain serdes memory map, ret = %d\n",
> +			  ret);
> +		return ret;

%pe etc.

[]

> +	for (i = 0; i < comp_no; i++) {
> +		ret = of_property_read_string_index(serdes_node, "compatible",
> +						    i, &serdes_comp);
> +		if (ret == 0) {
> +			if (!strcasecmp(serdes_comp, "serdes-10g")) {
> +				serdes_type = SERDES_10G;
> +				break;
> +			} else if (!strcasecmp(serdes_comp, "serdes-28g")) {
> +				serdes_type = SERDES_28G;
> +				break;
> +			}
> +		}
> +	}
[]
> +static int qoriq_backplane_match_phy_device(struct phy_device *bpphy)
> +{
[]
> +	for (i = 0; i < comp_no; i++) {
> +		ret = of_property_read_string_index(serdes_node, "compatible",
> +						    i, &serdes_comp);
> +		if (ret == 0) {
> +			if (!strcasecmp(serdes_comp, "serdes-10g")) {
> +				serdes_type = SERDES_10G;
> +				break;
> +			} else if (!strcasecmp(serdes_comp, "serdes-28g")) {
> +				serdes_type = SERDES_28G;
> +				break;
> +			}
> +		}
> +	}
[]

Maybe add and use a helper function?



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

* Re: [PATCH net-next 7/9] net: phy: enable qoriq backplane support
  2020-03-26 20:03   ` Joe Perches
@ 2020-03-26 20:13     ` Andrew Lunn
  2020-03-26 20:27       ` Joe Perches
  0 siblings, 1 reply; 42+ messages in thread
From: Andrew Lunn @ 2020-03-26 20:13 UTC (permalink / raw)
  To: Joe Perches
  Cc: florinel.iordache, davem, netdev, f.fainelli, hkallweit1, linux,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

> > +static int qoriq_backplane_config_init(struct phy_device *bpphy)
> > +{
> []
> > +	for (i = 0; i < bp_phy->num_lanes; i++) {
> []
> > +		ret = of_address_to_resource(lane_node, 0, &res);
> > +		if (ret) {
> > +			bpdev_err(bpphy,
> > +				  "could not obtain lane memory map for index=%d, ret = %d\n",
> > +				  i, ret);
> > +			return ret;
> 
> This could use the new vsprintf %pe extension:

Hi Joe

Probably a FAQ. But is there plans to extend vsprintf to take an int
errno value, rather than having to do this ugly ERR_PTR(ret) every
time? Format string %de ?

      Andrew

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

* Re: [PATCH net-next 7/9] net: phy: enable qoriq backplane support
  2020-03-26 20:13     ` Andrew Lunn
@ 2020-03-26 20:27       ` Joe Perches
  0 siblings, 0 replies; 42+ messages in thread
From: Joe Perches @ 2020-03-26 20:27 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: florinel.iordache, davem, netdev, f.fainelli, hkallweit1, linux,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, 2020-03-26 at 21:13 +0100, Andrew Lunn wrote:
> > > +static int qoriq_backplane_config_init(struct phy_device *bpphy)
> > > +{
> > []
> > > +	for (i = 0; i < bp_phy->num_lanes; i++) {
> > []
> > > +		ret = of_address_to_resource(lane_node, 0, &res);
> > > +		if (ret) {
> > > +			bpdev_err(bpphy,
> > > +				  "could not obtain lane memory map for index=%d, ret = %d\n",
> > > +				  i, ret);
> > > +			return ret;
> > 
> > This could use the new vsprintf %pe extension:
> 
> Hi Joe

Hello Andrew.

> Probably a FAQ. But is there plans to extend vsprintf to take an int
> errno value, rather than having to do this ugly ERR_PTR(ret) every
> time? Format string %de ?

Uwe Kleine-König proposed one a couple times.

https://lkml.org/lkml/2019/8/27/1456

Though I believe one %<type><extra> extension mechanism
in vsprintf is enough.  At least the %p<foo> use is
unlikely to ever have desired but dropped output after
an output of %p.  It seems less true for %[diux].





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

* Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-26 13:51 ` [PATCH net-next 3/9] net: phy: add kr phy connection type Florinel Iordache
@ 2020-03-27  0:15   ` Andrew Lunn
  2020-03-27 12:01     ` Russell King - ARM Linux admin
  2020-03-29  8:22     ` [EXT] " Florinel Iordache
  2020-03-27  0:32   ` Florian Fainelli
  1 sibling, 2 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27  0:15 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> Add support for backplane kr phy connection types currently available
> (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> the cases for KR modes which are clause 45 compatible to correctly assign
> phy_interface and phylink#supported)
> 
> Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> ---
>  drivers/net/phy/phylink.c | 15 ++++++++++++---
>  include/linux/phy.h       |  6 +++++-
>  2 files changed, 17 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> index fed0c59..db1bb87 100644
> --- a/drivers/net/phy/phylink.c
> +++ b/drivers/net/phy/phylink.c
> @@ -4,6 +4,7 @@
>   * technologies such as SFP cages where the PHY is hot-pluggable.
>   *
>   * Copyright (C) 2015 Russell King
> + * Copyright 2020 NXP
>   */
>  #include <linux/ethtool.h>
>  #include <linux/export.h>
> @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
>  			break;
>  
>  		case PHY_INTERFACE_MODE_USXGMII:
> -		case PHY_INTERFACE_MODE_10GKR:

We might have a backwards compatibility issue here. If i remember
correctly, there are some boards out in the wild using
PHY_INTERFACE_MODE_10GKR not PHY_INTERFACE_MODE_10GBASER.

See e0f909bc3a242296da9ccff78277f26d4883a79d

Russell, what do you say about this?

	 Andrew

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

* Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-26 13:51 ` [PATCH net-next 3/9] net: phy: add kr phy connection type Florinel Iordache
  2020-03-27  0:15   ` Andrew Lunn
@ 2020-03-27  0:32   ` Florian Fainelli
  1 sibling, 0 replies; 42+ messages in thread
From: Florian Fainelli @ 2020-03-27  0:32 UTC (permalink / raw)
  To: florinel.iordache, davem, netdev, andrew, hkallweit1, linux
  Cc: devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel



On 3/26/2020 6:51 AM, Florinel Iordache wrote:
> Add support for backplane kr phy connection types currently available
> (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> the cases for KR modes which are clause 45 compatible to correctly assign
> phy_interface and phylink#supported)
> 
> Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> ---
>  drivers/net/phy/phylink.c | 15 ++++++++++++---
>  include/linux/phy.h       |  6 +++++-
>  2 files changed, 17 insertions(+), 4 deletions(-)

Please remember to update Documentation/networking/phy.rst, and
Documentation/ABI/testing/sysfs-class-net-phydev with these new PHY
interface values.
-- 
Florian

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

* Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-26 13:51 ` [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings Florinel Iordache
@ 2020-03-27  1:04   ` Andrew Lunn
  2020-03-27 12:06     ` Russell King - ARM Linux admin
  2020-03-27 15:00     ` [EXT] " Florinel Iordache
  2020-03-30 15:39   ` Rob Herring
  1 sibling, 2 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27  1:04 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, Mar 26, 2020 at 03:51:15PM +0200, Florinel Iordache wrote:
> Add ethernet backplane device tree bindings

> +  - |
> +    /* Backplane configurations for specific setup */
> +    &mdio9 {
> +        bpphy6: ethernet-phy@0 {
> +            compatible = "ethernet-phy-ieee802.3-c45";
> +            reg = <0x0>;
> +            lane-handle = <&lane_d>; /* use lane D */
> +            eq-algorithm = "bee";
> +            /* 10G Short cables setup: up to 30 cm cable */
> +            eq-init = <0x2 0x5 0x29>;
> +            eq-params = <0>;
> +        };
> +    };

So you are modelling this as just another PHY? Does the driver get
loaded based on the PHY ID in registers 2 and 3? Does the standard
define these IDs or are they vendor specific?

Thanks
	Andrew

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
  2020-03-26 18:53   ` David Miller
@ 2020-03-27  1:07   ` Andrew Lunn
  2020-03-27 13:02     ` [EXT] " Florinel Iordache
  2020-03-27 17:43     ` Florian Fainelli
  2020-03-27 14:22   ` Andrew Lunn
                     ` (3 subsequent siblings)
  5 siblings, 2 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27  1:07 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

> +static u32 le_ioread32(void __iomem *reg)
> +{
> +	return ioread32(reg);
> +}
> +
> +static void le_iowrite32(u32 value, void __iomem *reg)
> +{
> +	iowrite32(value, reg);
> +}
> +
> +static u32 be_ioread32(void __iomem *reg)
> +{
> +	return ioread32be(reg);
> +}
> +
> +static void be_iowrite32(u32 value, void __iomem *reg)
> +{
> +	iowrite32be(value, reg);
> +}

This is very surprising to me. I've not got my head around the
structure of this code yet, but i'm surprised to see memory mapped
access functions in generic code.

       Andrew

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

* Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-27  0:15   ` Andrew Lunn
@ 2020-03-27 12:01     ` Russell King - ARM Linux admin
  2020-03-27 12:12       ` Madalin Bucur (OSS)
  2020-03-29  8:22     ` [EXT] " Florinel Iordache
  1 sibling, 1 reply; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 12:01 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Fri, Mar 27, 2020 at 01:15:15AM +0100, Andrew Lunn wrote:
> On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> > Add support for backplane kr phy connection types currently available
> > (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> > the cases for KR modes which are clause 45 compatible to correctly assign
> > phy_interface and phylink#supported)
> > 
> > Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> > ---
> >  drivers/net/phy/phylink.c | 15 ++++++++++++---
> >  include/linux/phy.h       |  6 +++++-
> >  2 files changed, 17 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > index fed0c59..db1bb87 100644
> > --- a/drivers/net/phy/phylink.c
> > +++ b/drivers/net/phy/phylink.c
> > @@ -4,6 +4,7 @@
> >   * technologies such as SFP cages where the PHY is hot-pluggable.
> >   *
> >   * Copyright (C) 2015 Russell King
> > + * Copyright 2020 NXP
> >   */
> >  #include <linux/ethtool.h>
> >  #include <linux/export.h>
> > @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
> >  			break;
> >  
> >  		case PHY_INTERFACE_MODE_USXGMII:
> > -		case PHY_INTERFACE_MODE_10GKR:
> 
> We might have a backwards compatibility issue here. If i remember
> correctly, there are some boards out in the wild using
> PHY_INTERFACE_MODE_10GKR not PHY_INTERFACE_MODE_10GBASER.
> 
> See e0f909bc3a242296da9ccff78277f26d4883a79d
> 
> Russell, what do you say about this?

Yes, and that's a point that I made when I introduced 10GBASER to
correct that mistake.  It is way too soon to change this; it will
definitely cause regressions:

$ grep 10gbase-kr arch/*/boot/dts -r
arch/arm64/boot/dts/marvell/cn9131-db.dts:      phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-7040-db.dts:    phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-clearfog-gt-8k.dts:     phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/cn9132-db.dts:      phy-mode = "10gbase-kr";
arch/arm64/boot/dts/marvell/cn9130-db.dts:      phy-mode = "10gbase-kr";

So any change to the existing PHY_INTERFACE_MODE_10GKR will likely
break all these platforms.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27  1:04   ` Andrew Lunn
@ 2020-03-27 12:06     ` Russell King - ARM Linux admin
  2020-03-27 15:00     ` [EXT] " Florinel Iordache
  1 sibling, 0 replies; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 12:06 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Fri, Mar 27, 2020 at 02:04:11AM +0100, Andrew Lunn wrote:
> On Thu, Mar 26, 2020 at 03:51:15PM +0200, Florinel Iordache wrote:
> > Add ethernet backplane device tree bindings
> 
> > +  - |
> > +    /* Backplane configurations for specific setup */
> > +    &mdio9 {
> > +        bpphy6: ethernet-phy@0 {
> > +            compatible = "ethernet-phy-ieee802.3-c45";
> > +            reg = <0x0>;
> > +            lane-handle = <&lane_d>; /* use lane D */
> > +            eq-algorithm = "bee";
> > +            /* 10G Short cables setup: up to 30 cm cable */
> > +            eq-init = <0x2 0x5 0x29>;
> > +            eq-params = <0>;
> > +        };
> > +    };
> 
> So you are modelling this as just another PHY? Does the driver get
> loaded based on the PHY ID in registers 2 and 3? Does the standard
> define these IDs or are they vendor specific?

We likely need some mutual coordination here between the patches I've
already posted for PCS support and these patches.

As I've said, we can't deal with multiple ethernet PHYs connected to
one MAC, the patches I've posted cope with that, but likely will need
changes for this.  Conversely, these patches will need changes for my
work.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* Re: [PATCH net-next 9/9] arm64: dts: add serdes and mdio description
  2020-03-26 13:51 ` [PATCH net-next 9/9] arm64: dts: add serdes and mdio description Florinel Iordache
@ 2020-03-27 12:09   ` Russell King - ARM Linux admin
  0 siblings, 0 replies; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 12:09 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, andrew, f.fainelli, hkallweit1, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, Mar 26, 2020 at 03:51:22PM +0200, Florinel Iordache wrote:
> Add dt nodes with serdes, lanes, mdio generic description for supported
> platforms: ls1046, ls1088, ls2088, lx2160. This is a prerequisite to
> enable backplane on device tree for these platforms.
> 
> Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> ---
>  arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi     |  33 ++++-
>  arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi     |  97 ++++++++++++-
>  arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi     | 160 ++++++++++++++++++++-
>  arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi     | 128 ++++++++++++++++-
>  .../boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi    |   5 +-
>  .../boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi    |   5 +-
>  6 files changed, 418 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
> index d4c1da3..c7d845f 100644
> --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
> +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
> @@ -3,7 +3,7 @@
>   * Device Tree Include file for Freescale Layerscape-1046A family SoC.
>   *
>   * Copyright 2016 Freescale Semiconductor, Inc.
> - * Copyright 2018 NXP
> + * Copyright 2018, 2020 NXP
>   *
>   * Mingkai Hu <mingkai.hu@nxp.com>
>   */
> @@ -735,6 +735,37 @@
>  			status = "disabled";
>  		};
>  
> +		serdes1: serdes@1ea0000 {
> +			compatible = "serdes-10g";
> +			reg = <0x0 0x1ea0000 0 0x00002000>;
> +			reg-names = "serdes", "serdes-10g";
> +			big-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0x0 0x00 0x1ea0000 0x00002000>;
> +			lane_a: lane@800 {
> +				compatible = "lane-10g";
> +				reg = <0x800 0x40>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_b: lane@840 {
> +				compatible = "lane-10g";
> +				reg = <0x840 0x40>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_c: lane@880 {
> +				compatible = "lane-10g";
> +				reg = <0x880 0x40>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_d: lane@8c0 {
> +				compatible = "lane-10g";
> +				reg = <0x8c0 0x40>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +		};
> +
>  		pcie_ep@3600000 {
>  			compatible = "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep";
>  			reg = <0x00 0x03600000 0x0 0x00100000
> diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
> index 5945662..474464e 100644
> --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
> +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
> @@ -2,7 +2,7 @@
>  /*
>   * Device Tree Include file for NXP Layerscape-1088A family SoC.
>   *
> - * Copyright 2017 NXP
> + * Copyright 2017, 2020 NXP
>   *
>   * Harninder Rai <harninder.rai@nxp.com>
>   *
> @@ -325,6 +325,69 @@
>  			#interrupt-cells = <2>;
>  		};
>  
> +		/* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */
> +		emdio1: mdio@8B96000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8B96000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;	/* force the driver in LE mode */
> +
> +			/* Not necessary on the QDS, but needed on the RDB */
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		/* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */
> +		emdio2: mdio@8B97000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8B97000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;	/* force the driver in LE mode */
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio1: mdio@0x8c07000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c07000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio2: mdio@0x8c0b000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0b000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio3: mdio@0x8c0f000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0f000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio4: mdio@0x8c13000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c13000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
>  		ifc: ifc@2240000 {
>  			compatible = "fsl,ifc", "simple-bus";
>  			reg = <0x0 0x2240000 0x0 0x20000>;
> @@ -777,6 +840,38 @@
>  				};
>  			};
>  		};
> +
> +		serdes1: serdes@1ea0000 {
> +				compatible = "serdes-10g";
> +				reg = <0x0 0x1ea0000 0 0x00002000>;
> +				reg-names = "serdes", "serdes-10g";
> +				little-endian;
> +
> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				ranges = <0x0 0x00 0x1ea0000 0x00002000>;
> +				lane_a: lane@800 {
> +					compatible = "lane-10g";
> +					reg = <0x800 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_b: lane@840 {
> +					compatible = "lane-10g";
> +					reg = <0x840 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_c: lane@880 {
> +					compatible = "lane-10g";
> +					reg = <0x880 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_d: lane@8c0 {
> +					compatible = "lane-10g";
> +					reg = <0x8c0 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +		};
> +
>  	};
>  
>  	firmware {
> diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
> index f96d06d..e8f3026 100644
> --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
> +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi
> @@ -3,7 +3,7 @@
>   * Device Tree Include file for Freescale Layerscape-2080A family SoC.
>   *
>   * Copyright 2016 Freescale Semiconductor, Inc.
> - * Copyright 2017 NXP
> + * Copyright 2017, 2020 NXP
>   *
>   * Abhimanyu Saini <abhimanyu.saini@nxp.com>
>   *
> @@ -560,6 +560,113 @@
>  			#interrupt-cells = <2>;
>  		};
>  
> +		/* WRIOP0: 0x8B8_0000, E-MDIO1: 0x1_6000 */
> +		emdio1: mdio@8B96000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8B96000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;	/* force the driver in LE mode */
> +
> +			/* Not necessary on the QDS, but needed on the RDB */
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		/* WRIOP0: 0x8B8_0000, E-MDIO2: 0x1_7000 */
> +		emdio2: mdio@8B97000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8B97000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;	/* force the driver in LE mode */
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio1: mdio@0x8c07000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c07000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio2: mdio@0x8c0b000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0b000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio3: mdio@0x8c0f000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0f000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio4: mdio@0x8c13000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c13000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio5: mdio@0x8c17000 {
> +			status = "disabled";
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c17000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio6: mdio@0x8c1b000 {
> +			status = "disabled";
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c1b000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio7: mdio@0x8c1f000 {
> +			status = "disabled";
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c1f000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio8: mdio@0x8c23000 {
> +			status = "disabled";
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c23000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
>  		i2c0: i2c@2000000 {
>  			status = "disabled";
>  			compatible = "fsl,vf610-i2c";
> @@ -754,6 +861,57 @@
>  			snps,incr-burst-type-adjustment = <1>, <4>, <8>, <16>;
>  		};
>  
> +		serdes1: serdes@1ea0000 {
> +				compatible = "serdes-10g";
> +				reg = <0x0 0x1ea0000 0 0x00002000>;
> +				reg-names = "serdes", "serdes-10g";
> +				little-endian;
> +
> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				ranges = <0x0 0x00 0x1ea0000 0x00002000>;
> +				lane_a: lane@800 {
> +					compatible = "lane-10g";
> +					reg = <0x800 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_b: lane@840 {
> +					compatible = "lane-10g";
> +					reg = <0x840 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_c: lane@880 {
> +					compatible = "lane-10g";
> +					reg = <0x880 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_d: lane@8c0 {
> +					compatible = "lane-10g";
> +					reg = <0x8c0 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_e: lane@900 {
> +					compatible = "lane-10g";
> +					reg = <0x900 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_f: lane@940 {
> +					compatible = "lane-10g";
> +					reg = <0x940 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_g: lane@980 {
> +					compatible = "lane-10g";
> +					reg = <0x980 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +				lane_h: lane@9c0 {
> +					compatible = "lane-10g";
> +					reg = <0x9c0 0x40>;
> +					reg-names = "lane", "serdes-lane";
> +				};
> +		};
> +
>  		ccn@4000000 {
>  			compatible = "arm,ccn-504";
>  			reg = <0x0 0x04000000 0x0 0x01000000>;
> diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
> index e5ee559..2815908 100644
> --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
> +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
> @@ -2,7 +2,7 @@
>  //
>  // Device Tree Include file for Layerscape-LX2160A family SoC.
>  //
> -// Copyright 2018 NXP
> +// Copyright 2018, 2020 NXP
>  
>  #include <dt-bindings/gpio/gpio.h>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
> @@ -947,9 +947,9 @@
>  			#address-cells = <1>;
>  			#size-cells = <0>;
>  			little-endian;
> -			status = "disabled";
>  		};
>  
> +		/* WRIOP0: 0x8b8_0000, E-MDIO2: 0x1_7000 */
>  		emdio2: mdio@8b97000 {
>  			compatible = "fsl,fman-memac-mdio";
>  			reg = <0x0 0x8b97000 0x0 0x1000>;
> @@ -957,7 +957,129 @@
>  			little-endian;
>  			#address-cells = <1>;
>  			#size-cells = <0>;
> -			status = "disabled";
> +		};
> +
> +		pcs_mdio1: mdio@0x8c07000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c07000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio2: mdio@0x8c0b000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0b000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio3: mdio@0x8c0f000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c0f000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio4: mdio@0x8c13000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c13000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio5: mdio@0x8c17000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c17000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio6: mdio@0x8c1b000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c1b000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio7: mdio@0x8c1f000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c1f000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		pcs_mdio8: mdio@0x8c23000 {
> +			compatible = "fsl,fman-memac-mdio";
> +			reg = <0x0 0x8c23000 0x0 0x1000>;
> +			device_type = "mdio";
> +			little-endian;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};

This is a subset of the entries I've posted in my PCS series...

> +
> +		serdes1: serdes@1ea0000 {
> +			compatible = "serdes-28g";
> +			reg = <0x0 0x1ea0000 0 0x00002000>;
> +			reg-names = "serdes", "serdes-28g";
> +			little-endian;
> +
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0x0 0x00 0x1ea0000 0x00002000>;
> +			lane_a: lane@800 {
> +				compatible = "lane-28g";
> +				reg = <0x800 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_b: lane@900 {
> +				compatible = "lane-28g";
> +				reg = <0x900 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_c: lane@a00 {
> +				compatible = "lane-28g";
> +				reg = <0xa00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_d: lane@b00 {
> +				compatible = "lane-28g";
> +				reg = <0xb00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_e: lane@c00 {
> +				compatible = "lane-28g";
> +				reg = <0xc00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_f: lane@d00 {
> +				compatible = "lane-28g";
> +				reg = <0xd00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_g: lane@e00 {
> +				compatible = "lane-28g";
> +				reg = <0xe00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
> +			lane_h: lane@f00 {
> +				compatible = "lane-28g";
> +				reg = <0xf00 0x100>;
> +				reg-names = "lane", "serdes-lane";
> +			};
>  		};
>  
>  		fsl_mc: fsl-mc@80c000000 {
> diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
> index dbd2fc3..d6191f1 100644
> --- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
> +++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-0.dtsi
> @@ -3,6 +3,7 @@
>   * QorIQ FMan v3 10g port #0 device tree
>   *
>   * Copyright 2012-2015 Freescale Semiconductor Inc.
> + * Copyright 2020 NXP
>   *
>   */
>  
> @@ -21,7 +22,7 @@ fman@1a00000 {
>  		fsl,fman-10g-port;
>  	};
>  
> -	ethernet@f0000 {
> +	mac9: ethernet@f0000 {
>  		cell-index = <0x8>;
>  		compatible = "fsl,fman-memac";
>  		reg = <0xf0000 0x1000>;
> @@ -29,7 +30,7 @@ fman@1a00000 {
>  		pcsphy-handle = <&pcsphy6>;
>  	};
>  
> -	mdio@f1000 {
> +	mdio9: mdio@f1000 {
>  		#address-cells = <1>;
>  		#size-cells = <0>;
>  		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
> diff --git a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
> index 6fc5d25..1f6f28f 100644
> --- a/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
> +++ b/arch/arm64/boot/dts/freescale/qoriq-fman3-0-10g-1.dtsi
> @@ -3,6 +3,7 @@
>   * QorIQ FMan v3 10g port #1 device tree
>   *
>   * Copyright 2012-2015 Freescale Semiconductor Inc.
> + * Copyright 2020 NXP
>   *
>   */
>  
> @@ -21,7 +22,7 @@ fman@1a00000 {
>  		fsl,fman-10g-port;
>  	};
>  
> -	ethernet@f2000 {
> +	mac10: ethernet@f2000 {
>  		cell-index = <0x9>;
>  		compatible = "fsl,fman-memac";
>  		reg = <0xf2000 0x1000>;
> @@ -29,7 +30,7 @@ fman@1a00000 {
>  		pcsphy-handle = <&pcsphy7>;
>  	};
>  
> -	mdio@f3000 {
> +	mdio10: mdio@f3000 {
>  		#address-cells = <1>;
>  		#size-cells = <0>;
>  		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
> -- 
> 1.9.1
> 
> 

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* RE: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-27 12:01     ` Russell King - ARM Linux admin
@ 2020-03-27 12:12       ` Madalin Bucur (OSS)
  2020-03-27 12:40         ` Russell King - ARM Linux admin
  0 siblings, 1 reply; 42+ messages in thread
From: Madalin Bucur (OSS) @ 2020-03-27 12:12 UTC (permalink / raw)
  To: Russell King - ARM Linux admin, Andrew Lunn
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, Leo Li, Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

> -----Original Message-----
> From: netdev-owner@vger.kernel.org <netdev-owner@vger.kernel.org> On
> Behalf Of Russell King - ARM Linux admin
> Sent: Friday, March 27, 2020 2:02 PM
> To: Andrew Lunn <andrew@lunn.ch>
> Cc: Florinel Iordache <florinel.iordache@nxp.com>; davem@davemloft.net;
> netdev@vger.kernel.org; f.fainelli@gmail.com; hkallweit1@gmail.com;
> devicetree@vger.kernel.org; linux-doc@vger.kernel.org; robh+dt@kernel.org;
> mark.rutland@arm.com; kuba@kernel.org; corbet@lwn.net;
> shawnguo@kernel.org; Leo Li <leoyang.li@nxp.com>; Madalin Bucur (OSS)
> <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>; linux-
> kernel@vger.kernel.org
> Subject: Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
> 
> On Fri, Mar 27, 2020 at 01:15:15AM +0100, Andrew Lunn wrote:
> > On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> > > Add support for backplane kr phy connection types currently available
> > > (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> > > the cases for KR modes which are clause 45 compatible to correctly
> assign
> > > phy_interface and phylink#supported)
> > >
> > > Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> > > ---
> > >  drivers/net/phy/phylink.c | 15 ++++++++++++---
> > >  include/linux/phy.h       |  6 +++++-
> > >  2 files changed, 17 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > > index fed0c59..db1bb87 100644
> > > --- a/drivers/net/phy/phylink.c
> > > +++ b/drivers/net/phy/phylink.c
> > > @@ -4,6 +4,7 @@
> > >   * technologies such as SFP cages where the PHY is hot-pluggable.
> > >   *
> > >   * Copyright (C) 2015 Russell King
> > > + * Copyright 2020 NXP
> > >   */
> > >  #include <linux/ethtool.h>
> > >  #include <linux/export.h>
> > > @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl,
> struct fwnode_handle *fwnode)
> > >  			break;
> > >
> > >  		case PHY_INTERFACE_MODE_USXGMII:
> > > -		case PHY_INTERFACE_MODE_10GKR:
> >
> > We might have a backwards compatibility issue here. If i remember
> > correctly, there are some boards out in the wild using
> > PHY_INTERFACE_MODE_10GKR not PHY_INTERFACE_MODE_10GBASER.
> >
> > See e0f909bc3a242296da9ccff78277f26d4883a79d
> >
> > Russell, what do you say about this?
> 
> Yes, and that's a point that I made when I introduced 10GBASER to
> correct that mistake.  It is way too soon to change this; it will
> definitely cause regressions:
> 
> $ grep 10gbase-kr arch/*/boot/dts -r
> arch/arm64/boot/dts/marvell/cn9131-db.dts:      phy-mode = "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode =
> "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode =
> "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-7040-db.dts:    phy-mode = "10gbase-
> kr";
> arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode =
> "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode =
> "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-clearfog-gt-8k.dts:     phy-mode =
> "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
> arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
> arch/arm64/boot/dts/marvell/cn9132-db.dts:      phy-mode = "10gbase-kr";
> arch/arm64/boot/dts/marvell/cn9130-db.dts:      phy-mode = "10gbase-kr";
> 
> So any change to the existing PHY_INTERFACE_MODE_10GKR will likely
> break all these platforms.
> 
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps
> up

Hi Russell,

I hoped a fix for those would be in by now, it's not useful to leave them like
that. We have a similar situation, where all boards using XFI interfaces contain
phy-connection-type="xgmii" for a long time now but that did not stop anyone from
adding a warning in the Aquantia driver:

+       WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
+            "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
+

Maybe we need a warning added here too, until the proper phy-mode is used for
these boards, to allow for a transition period.

Madalin

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

* Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-27 12:12       ` Madalin Bucur (OSS)
@ 2020-03-27 12:40         ` Russell King - ARM Linux admin
  0 siblings, 0 replies; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 12:40 UTC (permalink / raw)
  To: Madalin Bucur (OSS)
  Cc: Andrew Lunn, Florinel Iordache, davem, netdev, f.fainelli,
	hkallweit1, devicetree, linux-doc, robh+dt, mark.rutland, kuba,
	corbet, shawnguo, Leo Li, Ioana Ciornei, linux-kernel

On Fri, Mar 27, 2020 at 12:12:37PM +0000, Madalin Bucur (OSS) wrote:
> > -----Original Message-----
> > From: netdev-owner@vger.kernel.org <netdev-owner@vger.kernel.org> On
> > Behalf Of Russell King - ARM Linux admin
> > Sent: Friday, March 27, 2020 2:02 PM
> > To: Andrew Lunn <andrew@lunn.ch>
> > Cc: Florinel Iordache <florinel.iordache@nxp.com>; davem@davemloft.net;
> > netdev@vger.kernel.org; f.fainelli@gmail.com; hkallweit1@gmail.com;
> > devicetree@vger.kernel.org; linux-doc@vger.kernel.org; robh+dt@kernel.org;
> > mark.rutland@arm.com; kuba@kernel.org; corbet@lwn.net;
> > shawnguo@kernel.org; Leo Li <leoyang.li@nxp.com>; Madalin Bucur (OSS)
> > <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>; linux-
> > kernel@vger.kernel.org
> > Subject: Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
> > 
> > On Fri, Mar 27, 2020 at 01:15:15AM +0100, Andrew Lunn wrote:
> > > On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> > > > Add support for backplane kr phy connection types currently available
> > > > (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> > > > the cases for KR modes which are clause 45 compatible to correctly
> > assign
> > > > phy_interface and phylink#supported)
> > > >
> > > > Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> > > > ---
> > > >  drivers/net/phy/phylink.c | 15 ++++++++++++---
> > > >  include/linux/phy.h       |  6 +++++-
> > > >  2 files changed, 17 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > > > index fed0c59..db1bb87 100644
> > > > --- a/drivers/net/phy/phylink.c
> > > > +++ b/drivers/net/phy/phylink.c
> > > > @@ -4,6 +4,7 @@
> > > >   * technologies such as SFP cages where the PHY is hot-pluggable.
> > > >   *
> > > >   * Copyright (C) 2015 Russell King
> > > > + * Copyright 2020 NXP
> > > >   */
> > > >  #include <linux/ethtool.h>
> > > >  #include <linux/export.h>
> > > > @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl,
> > struct fwnode_handle *fwnode)
> > > >  			break;
> > > >
> > > >  		case PHY_INTERFACE_MODE_USXGMII:
> > > > -		case PHY_INTERFACE_MODE_10GKR:
> > >
> > > We might have a backwards compatibility issue here. If i remember
> > > correctly, there are some boards out in the wild using
> > > PHY_INTERFACE_MODE_10GKR not PHY_INTERFACE_MODE_10GBASER.
> > >
> > > See e0f909bc3a242296da9ccff78277f26d4883a79d
> > >
> > > Russell, what do you say about this?
> > 
> > Yes, and that's a point that I made when I introduced 10GBASER to
> > correct that mistake.  It is way too soon to change this; it will
> > definitely cause regressions:
> > 
> > $ grep 10gbase-kr arch/*/boot/dts -r
> > arch/arm64/boot/dts/marvell/cn9131-db.dts:      phy-mode = "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode =
> > "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts:      phy-mode =
> > "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-7040-db.dts:    phy-mode = "10gbase-
> > kr";
> > arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode =
> > "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-mcbin-singleshot.dts:   phy-mode =
> > "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-clearfog-gt-8k.dts:     phy-mode =
> > "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
> > arch/arm64/boot/dts/marvell/armada-8040-db.dts: phy-mode = "10gbase-kr";
> > arch/arm64/boot/dts/marvell/cn9132-db.dts:      phy-mode = "10gbase-kr";
> > arch/arm64/boot/dts/marvell/cn9130-db.dts:      phy-mode = "10gbase-kr";
> > 
> > So any change to the existing PHY_INTERFACE_MODE_10GKR will likely
> > break all these platforms.
> 
> Hi Russell,
> 
> I hoped a fix for those would be in by now, it's not useful to leave them like
> that.

I haven't had the time to address the ones I know about, sorry.
However, there are some platforms in that list which I've no
knowledge of, which I therefore can't change.

> We have a similar situation, where all boards using XFI interfaces contain
> phy-connection-type="xgmii" for a long time now but that did not stop anyone from
> adding a warning in the Aquantia driver:
> 
> +       WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
> +            "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
> +
> 
> Maybe we need a warning added here too, until the proper phy-mode is used for
> these boards, to allow for a transition period.

Adding a warning can only be done once the current users have been
updated, otherwise it's technically introducing a regression.  Plus
some users may actually be correct.  I never did get to the bottom
of that, because that required discussion and no one seems willing
to discuss it.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* RE: [EXT] Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-27  1:07   ` Andrew Lunn
@ 2020-03-27 13:02     ` Florinel Iordache
  2020-03-27 13:23       ` Andrew Lunn
  2020-03-27 17:43     ` Florian Fainelli
  1 sibling, 1 reply; 42+ messages in thread
From: Florinel Iordache @ 2020-03-27 13:02 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel, Florinel Iordache

> > +static u32 le_ioread32(void __iomem *reg) {
> > +     return ioread32(reg);
> > +}
> > +
> > +static void le_iowrite32(u32 value, void __iomem *reg) {
> > +     iowrite32(value, reg);
> > +}
> > +
> > +static u32 be_ioread32(void __iomem *reg) {
> > +     return ioread32be(reg);
> > +}
> > +
> > +static void be_iowrite32(u32 value, void __iomem *reg) {
> > +     iowrite32be(value, reg);
> > +}
> 
> This is very surprising to me. I've not got my head around the structure of this
> code yet, but i'm surprised to see memory mapped access functions in generic
> code.
> 
>        Andrew

Hi Andrew,

This is part of the framework used to automatically setup desired I/O 
callbacks for memory access according to device specific endianness 
which is specified in the specific device tree (DTS).
This approach (explained below) was used to avoid the potential 
redundant code related to memory access LE/BE which should be 
similar for all devices. 

This portion of code is just preparing these four static IO routines 
for specific endianness access LE/BE which are then installed as 
callbacks by the generic driver on generic DT parsing routine: 
backplane_parse_dt according to endianness flag:
+    /* setup ioread/iowrite according to endianness */
+    if (bp_phy->bp_dev.is_little_endian) {
+        bp_phy->bp_dev.io.read32 = le_ioread32;
+        bp_phy->bp_dev.io.write32 = le_iowrite32;
+    } else {
+        bp_phy->bp_dev.io.read32 = be_ioread32;
+        bp_phy->bp_dev.io.write32 = be_iowrite32;
+    }

These io callbacks are setup in the following structure:
+/* Endianness specific memory I/O operations */ 
struct mem_io_ops {

which is part of generic structure:
+/* Backplane device info */
+struct backplane_dev_info {
+. . . 
+    struct mem_io_ops io;

which in the end will be used directly by the device specific code for 
specific memory access according to specific endianness specified 
in the DTS. 

The endianness flag must be correctly set by the device specific code 
before calling the generic function backplane_parse_dt, according to 
device specific endianness specified in the specific device tree DTS:
+bp_phy->bp_dev.is_little_endian = of_property_read_bool(serdes_node,
                            "little-endian");

This action to setup desired IO callbacks could also be performed in the 
specific device code but by using this framework the process is more 
automatic, it's reusing the logic and therefore decreasing the overall 
LOC required.
If any specific device is doing this action by itself (which is a similar 
action regardless the specific device) then we will end up with a lot of 
redundant code.

Florin.

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

* Re: [EXT] Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-27 13:02     ` [EXT] " Florinel Iordache
@ 2020-03-27 13:23       ` Andrew Lunn
  0 siblings, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 13:23 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

On Fri, Mar 27, 2020 at 01:02:17PM +0000, Florinel Iordache wrote:
> > > +static u32 le_ioread32(void __iomem *reg) {
> > > +     return ioread32(reg);
> > > +}
> > > +
> > > +static void le_iowrite32(u32 value, void __iomem *reg) {
> > > +     iowrite32(value, reg);
> > > +}
> > > +
> > > +static u32 be_ioread32(void __iomem *reg) {
> > > +     return ioread32be(reg);
> > > +}
> > > +
> > > +static void be_iowrite32(u32 value, void __iomem *reg) {
> > > +     iowrite32be(value, reg);
> > > +}
> > 
> > This is very surprising to me. I've not got my head around the structure of this
> > code yet, but i'm surprised to see memory mapped access functions in generic
> > code.
> > 
> >        Andrew
> 
> Hi Andrew,
> 
> This is part of the framework used to automatically setup desired I/O 
> callbacks for memory access according to device specific endianness 
> which is specified in the specific device tree (DTS).
> This approach (explained below) was used to avoid the potential 
> redundant code related to memory access LE/BE which should be 
> similar for all devices. 

All devices which are using mmio. I assume the standard does not say
anything about memory mapped IO. It talks just about MDIO registers.

I would expect the generic code to just have generic accessors, which
could work for MMIO, yet more MDIO registers, i2c, spi, etc.

So add another support file which adds an MMIO implementation of these
generic access functions. Any driver which uses MMIO can pull it in.
The same should be true for the DT binding. Don't assume MMIO in the
generic binding.

> This portion of code is just preparing these four static IO routines 
> for specific endianness access LE/BE

Linux has a lot of MMIO accessors. Are you sure there is not one which
will do the right thing, making use of cpu_le32() or cpu_be32()
etc. Or are there different variants of the hardware, with some using
BE registers and some using LE registers? Note, this is all about the
endianness of the register, not the endianness of the cpu. cpu_le32()
will be a NOP when the CPU is running LE.

     Andrew

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
  2020-03-26 18:53   ` David Miller
  2020-03-27  1:07   ` Andrew Lunn
@ 2020-03-27 14:22   ` Andrew Lunn
  2020-03-27 18:25     ` Joe Perches
  2020-03-27 14:28   ` Andrew Lunn
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 14:22 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

> +/* Backplane custom logging */
> +#define BPDEV_LOG(name) \
> +	char log_buffer[LOG_BUFFER_SIZE]; \
> +	va_list args; va_start(args, msg); \
> +	vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \
> +	if (!bpphy->attached_dev) \
> +		dev_##name(&bpphy->mdio.dev, log_buffer); \
> +	else \
> +		dev_##name(&bpphy->mdio.dev, "%s: %s", \
> +			netdev_name(bpphy->attached_dev), log_buffer); \
> +	va_end(args)

> +void bpdev_err(struct phy_device *bpphy, char *msg, ...)
> +{
> +	BPDEV_LOG(err);
> +}
> +EXPORT_SYMBOL(bpdev_err);
> +
> +void bpdev_warn(struct phy_device *bpphy, char *msg, ...)
> +{
> +	BPDEV_LOG(warn);
> +}
> +EXPORT_SYMBOL(bpdev_warn);
> +
> +void bpdev_info(struct phy_device *bpphy, char *msg, ...)
> +{
> +	BPDEV_LOG(info);
> +}
> +EXPORT_SYMBOL(bpdev_info);
> +
> +void bpdev_dbg(struct phy_device *bpphy, char *msg, ...)
> +{
> +	BPDEV_LOG(dbg);
> +}
> +EXPORT_SYMBOL(bpdev_dbg);

You are currently modelling this as a phydev. So please just use
phydev_err(), phydev_info(), phydev_dbg() etc.

Also, if you look at other PHY code, struct phy_device * is nearly
always called phydev. Please try to be consistent with the existing
code base.

     Andrew

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
                     ` (2 preceding siblings ...)
  2020-03-27 14:22   ` Andrew Lunn
@ 2020-03-27 14:28   ` Andrew Lunn
  2020-03-27 14:33   ` Andrew Lunn
  2020-03-27 14:38   ` Andrew Lunn
  5 siblings, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 14:28 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

> +/* Read AN Link Status */
> +static int is_an_link_up(struct phy_device *bpphy)
> +{
> +	struct backplane_phy_info *bp_phy = bpphy->priv;
> +	int ret, val = 0;
> +
> +	mutex_lock(&bp_phy->bpphy_lock);
> +
> +	/* Read twice because Link_Status is LL (Latched Low) bit */
> +	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);
> +	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);
> +
> +	mutex_unlock(&bp_phy->bpphy_lock);

How does this mutex interact with phydev->lock? It appears both are
trying to do the same thing, serialise access to the PHY hardware.

	 Andrew

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
                     ` (3 preceding siblings ...)
  2020-03-27 14:28   ` Andrew Lunn
@ 2020-03-27 14:33   ` Andrew Lunn
  2020-03-27 14:38   ` Andrew Lunn
  5 siblings, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 14:33 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, Mar 26, 2020 at 03:51:19PM +0200, Florinel Iordache wrote:
> Add support for backplane kr generic driver including link training
> (ieee802.3ap/ba) and fixed equalization algorithm
> 
> Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> ---
>  drivers/net/phy/Kconfig                   |    2 +
>  drivers/net/phy/Makefile                  |    1 +
>  drivers/net/phy/backplane/Kconfig         |   20 +
>  drivers/net/phy/backplane/Makefile        |    9 +
>  drivers/net/phy/backplane/backplane.c     | 1538 +++++++++++++++++++++++++++
>  drivers/net/phy/backplane/backplane.h     |  262 +++++
>  drivers/net/phy/backplane/eq_fixed.c      |   83 ++
>  drivers/net/phy/backplane/equalization.h  |  282 +++++
>  drivers/net/phy/backplane/link_training.c | 1604 +++++++++++++++++++++++++++++
>  drivers/net/phy/backplane/link_training.h |   34 +
>  10 files changed, 3835 insertions(+)
>  create mode 100644 drivers/net/phy/backplane/Kconfig
>  create mode 100644 drivers/net/phy/backplane/Makefile
>  create mode 100644 drivers/net/phy/backplane/backplane.c
>  create mode 100644 drivers/net/phy/backplane/backplane.h
>  create mode 100644 drivers/net/phy/backplane/eq_fixed.c
>  create mode 100644 drivers/net/phy/backplane/equalization.h
>  create mode 100644 drivers/net/phy/backplane/link_training.c
>  create mode 100644 drivers/net/phy/backplane/link_training.h
> 
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index cc7f1df..abab4e5 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -523,6 +523,8 @@ config XILINX_GMII2RGMII
>  	  the Reduced Gigabit Media Independent Interface(RGMII) between
>  	  Ethernet physical media devices and the Gigabit Ethernet controller.
>  
> +source "drivers/net/phy/backplane/Kconfig"
> +
>  endif # PHYLIB
>  
>  config MICREL_KS8995MA
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> index 70774ab..0b867fb 100644
> --- a/drivers/net/phy/Makefile
> +++ b/drivers/net/phy/Makefile
> @@ -101,3 +101,4 @@ obj-$(CONFIG_STE10XP)		+= ste10Xp.o
>  obj-$(CONFIG_TERANETICS_PHY)	+= teranetics.o
>  obj-$(CONFIG_VITESSE_PHY)	+= vitesse.o
>  obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
> +obj-$(CONFIG_ETH_BACKPLANE)	+= backplane/
> diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig
> new file mode 100644
> index 0000000..9ec54b5
> --- /dev/null
> +++ b/drivers/net/phy/backplane/Kconfig
> @@ -0,0 +1,20 @@
> +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +config ETH_BACKPLANE
> +	tristate "Ethernet Backplane support"
> +	depends on OF_MDIO
> +	help
> +	  This module provides driver support for Ethernet Operation over
> +	  Electrical Backplanes. It includes Backplane generic
> +	  driver including support for Link Training (IEEE802.3ap/ba).
> +	  Based on the link quality, a signal equalization is required.
> +	  The standard specifies that a start-up algorithm should be in place
> +	  in order to get the link up.
> +
> +config ETH_BACKPLANE_FIXED
> +	tristate "Fixed: No Equalization algorithm"
> +	depends on ETH_BACKPLANE
> +	help
> +	  This module provides a driver to setup fixed user configurable
> +	  coefficient values for backplanes equalization. This means
> +	  No Equalization algorithm is used to adapt the initial coefficients
> +	  initially set by the user.
> \ No newline at end of file
> diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile
> new file mode 100644
> index 0000000..ded6f2d
> --- /dev/null
> +++ b/drivers/net/phy/backplane/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +#
> +# Makefile for Ethernet Backplane driver
> +#
> +
> +obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o
> +obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o
> +
> +eth_backplane-objs	:= backplane.o link_training.o
> diff --git a/drivers/net/phy/backplane/backplane.c b/drivers/net/phy/backplane/backplane.c
> new file mode 100644
> index 0000000..1b580bc
> --- /dev/null
> +++ b/drivers/net/phy/backplane/backplane.c
> @@ -0,0 +1,1538 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/* Backplane driver
> + *
> + * Copyright 2015 Freescale Semiconductor, Inc.
> + * Copyright 2018-2020 NXP
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mii.h>
> +#include <linux/mdio.h>
> +#include <linux/ethtool.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_net.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/timer.h>
> +#include <linux/delay.h>
> +#include <linux/workqueue.h>
> +#include <linux/netdevice.h>
> +#include <linux/list.h>
> +
> +#include "backplane.h"
> +#include "link_training.h"
> +
> +/* KR timeouts in milliseconds */
> +#define KR_TIMEOUT_1				100
> +#define KR_TIMEOUT_2				1000
> +#define KR_DENY_RT_INTERVAL			3000
> +#define KR_LT_TIMEOUT				500
> +
> +/* KR timings in interations */
> +#define KR_AN_WAIT_ITERATIONS			5
> +#define KR_TRAIN_STEP_ITERATIONS		2
> +#define CDR_LOCK_RETRY_COUNT			3
> +
> +/* AN status register (Clause 45) (MMD 7): MDIO_STAT1 */
> +#define AN_LINK_UP_MASK				0x04
> +
> +/* Logging buffer size */
> +#define LOG_BUFFER_SIZE				200
> +
> +/* Backplane custom logging */
> +#define BPDEV_LOG(name) \
> +	char log_buffer[LOG_BUFFER_SIZE]; \
> +	va_list args; va_start(args, msg); \
> +	vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \
> +	if (!bpphy->attached_dev) \
> +		dev_##name(&bpphy->mdio.dev, log_buffer); \
> +	else \
> +		dev_##name(&bpphy->mdio.dev, "%s: %s", \
> +			netdev_name(bpphy->attached_dev), log_buffer); \
> +	va_end(args)
> +
> +/* Backplane features */
> +__ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init;
> +EXPORT_SYMBOL(backplane_features);
> +
> +const int backplane_common_features_array[] = {
> +	ETHTOOL_LINK_MODE_Backplane_BIT,
> +	ETHTOOL_LINK_MODE_Autoneg_BIT,
> +	ETHTOOL_LINK_MODE_MII_BIT,
> +};
> +
> +const int backplane_protocol_features_array[] = {
> +	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
> +	ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
> +};
> +
> +/* map string key to pointer data */
> +struct spmap_node {
> +	struct list_head entry;
> +	const char *key;
> +	void *pdata;
> +};
> +
> +/* registered equalization algorithms info */
> +static LIST_HEAD(eqalg_list);
> +
> +/* lanes attached to an equalization algorithm */
> +static LIST_HEAD(lnalg_list);
> +
> +/* Backplane mutex between all KR PHY threads */
> +static struct mutex backplane_lock;
> +
> +static int get_backplane_speed(phy_interface_t bp_mode)
> +{
> +	switch (bp_mode) {
> +	case PHY_INTERFACE_MODE_10GKR:
> +		return SPEED_10000;
> +	case PHY_INTERFACE_MODE_40GKR4:
> +		return SPEED_40000;
> +	default:
> +		pr_err("%s: Unsupported backplane phy interface\n",
> +		       BACKPLANE_DRIVER_NAME);
> +		return SPEED_UNKNOWN;
> +	}
> +	return SPEED_UNKNOWN;
> +}
> +
> +static enum ethtool_link_mode_bit_indices
> +	get_backplane_supported_mode(phy_interface_t bp_mode)
> +{
> +	switch (bp_mode) {
> +	case PHY_INTERFACE_MODE_10GKR:
> +		return ETHTOOL_LINK_MODE_10000baseKR_Full_BIT;
> +	case PHY_INTERFACE_MODE_40GKR4:
> +		return ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT;
> +	default:
> +		pr_err("%s: Unsupported backplane phy interface\n",
> +		       BACKPLANE_DRIVER_NAME);
> +		return ETHTOOL_LINK_MODE_Backplane_BIT;
> +	}
> +	return ETHTOOL_LINK_MODE_Backplane_BIT;
> +}
> +
> +static int spmap_add(struct list_head *list, const char *key, void *pdata)
> +{
> +	struct spmap_node *node;
> +
> +	/* create a new entry with desired key */
> +	node = kzalloc(sizeof(*node), GFP_KERNEL);
> +	if (!node)
> +		return -ENOMEM;
> +
> +	node->key = key;
> +	node->pdata = pdata;
> +
> +	list_add(&node->entry, list);
> +
> +	return 0;
> +}
> +
> +static const struct equalization_algorithm *eq_find(const char *key)
> +{
> +	struct spmap_node *eqalg, *eqalg_tmp;
> +
> +	if (!key)
> +		return NULL;
> +
> +	/* search desired single key */
> +	list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) {
> +		if (strcmp(eqalg->key, key) == 0)
> +			return (struct equalization_algorithm *)eqalg->pdata;
> +	}
> +	return NULL;
> +}
> +
> +static void backplane_features_init(void)
> +{
> +	linkmode_set_bit_array(backplane_common_features_array,
> +			       ARRAY_SIZE(backplane_common_features_array),
> +			       backplane_features);
> +
> +	linkmode_set_bit_array(backplane_protocol_features_array,
> +			       ARRAY_SIZE(backplane_protocol_features_array),
> +			       backplane_features);
> +}
> +
> +static u32 le_ioread32(void __iomem *reg)
> +{
> +	return ioread32(reg);
> +}
> +
> +static void le_iowrite32(u32 value, void __iomem *reg)
> +{
> +	iowrite32(value, reg);
> +}
> +
> +static u32 be_ioread32(void __iomem *reg)
> +{
> +	return ioread32be(reg);
> +}
> +
> +static void be_iowrite32(u32 value, void __iomem *reg)
> +{
> +	iowrite32be(value, reg);
> +}
> +
> +static void training_status_init(struct training_status *trst)
> +{
> +	trst->done_training = false;
> +	trst->remote_tx_complete = false;
> +	trst->remote_tx_running = false;
> +	trst->sent_init = false;
> +	trst->lp_rx_ready = 0;
> +	trst->local_tx_running = false;
> +}
> +
> +static void init_krln(struct kr_lane_info *krln, bool revert_default)
> +{
> +	if (revert_default)
> +		backplane_default_kr_lane(krln);
> +
> +	training_status_init(&krln->trst);
> +	krln->state = DETECTING_LP;
> +	krln->an_acquired = false;
> +
> +	krln->ld_update = 0;
> +	krln->prev_ld_update = 0;
> +	krln->ld_last_nonhold_update = 0;
> +	krln->lp_status = 0;
> +	krln->lp_last_change_status = 0;
> +	krln->last_lp_update_status[C_M1] = 0;
> +	krln->last_lp_update_status[C_Z0] = 0;
> +	krln->last_lp_update_status[C_P1] = 0;
> +	krln->ld_status = 0;
> +	krln->move_back_prev = false;
> +	krln->move_back_cnt = 0;
> +	krln->move_back_lp_status = 0;
> +
> +	lt_init_ld(krln);
> +}
> +
> +static void setup_supported_linkmode(struct phy_device *bpphy)
> +{
> +	struct backplane_phy_info *bp_phy = bpphy->priv;
> +	int i;
> +
> +	/* Clear all supported backplane protocols features
> +	 * and setup only the currently configured protocol
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(backplane_protocol_features_array); i++)
> +		linkmode_clear_bit(backplane_protocol_features_array[i],
> +				   bpphy->supported);
> +
> +	linkmode_set_bit(get_backplane_supported_mode(bp_phy->bp_mode),
> +			 bpphy->supported);
> +}
> +
> +/* Read AN Link Status */
> +static int is_an_link_up(struct phy_device *bpphy)
> +{
> +	struct backplane_phy_info *bp_phy = bpphy->priv;
> +	int ret, val = 0;
> +
> +	mutex_lock(&bp_phy->bpphy_lock);
> +
> +	/* Read twice because Link_Status is LL (Latched Low) bit */
> +	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);
> +	val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status);

Why not just 

val = phy_read_mmd(bpphy, MDIO_MMD_AN, MDIO_CTRL1);

Or is your hardware not actually conformant to the standard?

There has also been a lot of discussion of reading the status twice is
correct or not. Don't you care the link has briefly gone down and up
again?

	Andrew

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
                     ` (4 preceding siblings ...)
  2020-03-27 14:33   ` Andrew Lunn
@ 2020-03-27 14:38   ` Andrew Lunn
  5 siblings, 0 replies; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 14:38 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Thu, Mar 26, 2020 at 03:51:19PM +0200, Florinel Iordache wrote:
> +static void setup_supported_linkmode(struct phy_device *bpphy)
> +{
> +	struct backplane_phy_info *bp_phy = bpphy->priv;

I'm not sure it is a good idea to completely take over phydev->priv
like this, in what is just helper code. What if the PHY driver needs
memory of its own? There are a few examples of this already in other
PHY drivers. Could a KR PHY contain a temperature sensor? Could it
contain statistics counters which need accumulating?

	Andrew

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

* RE: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27  1:04   ` Andrew Lunn
  2020-03-27 12:06     ` Russell King - ARM Linux admin
@ 2020-03-27 15:00     ` Florinel Iordache
  2020-03-27 15:28       ` Russell King - ARM Linux admin
  1 sibling, 1 reply; 42+ messages in thread
From: Florinel Iordache @ 2020-03-27 15:00 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel, Florinel Iordache

> On Thu, Mar 26, 2020 at 03:51:15PM +0200, Florinel Iordache wrote:
> > Add ethernet backplane device tree bindings
> 
> > +  - |
> > +    /* Backplane configurations for specific setup */
> > +    &mdio9 {
> > +        bpphy6: ethernet-phy@0 {
> > +            compatible = "ethernet-phy-ieee802.3-c45";
> > +            reg = <0x0>;
> > +            lane-handle = <&lane_d>; /* use lane D */
> > +            eq-algorithm = "bee";
> > +            /* 10G Short cables setup: up to 30 cm cable */
> > +            eq-init = <0x2 0x5 0x29>;
> > +            eq-params = <0>;
> > +        };
> > +    };
> 
> So you are modelling this as just another PHY? Does the driver get loaded based
> on the PHY ID in registers 2 and 3? Does the standard define these IDs or are
> they vendor specific?
> 
> Thanks
>         Andrew

Hi Andrew,
Thank you all for the feedback.
I am currently working to address the entire feedback received 
so far for this new Backplane driver.

Yes, we are modelling backplane driver as a phy driver.
The driver is loaded based on PHY ID in registers 2 and 3 which 
are specified by the standard but it is a vendor specific value: 
32-Bit identifier composed of the 3rd through 24th bits of the 
Organizationally Unique Identifier (OUI) assigned to the device 
manufacturer by the IEEE, plus a six-bit model number, plus a 
four-bit revision number.
This is done in the device specific code and not in backplane 
generic driver.
You can check support for QorIQ devices where qoriq_backplane_driver 
is registered as a phy_driver:
 
@file: qoriq_backplane.c
+static struct phy_driver qoriq_backplane_driver[] = {
+	{
+	.phy_id		= PCS_PHY_DEVICE_ID,
+	.name		= QORIQ_BACKPLANE_DRIVER_NAME,
+	.phy_id_mask	= PCS_PHY_DEVICE_ID_MASK,
+	.features       = BACKPLANE_FEATURES,
+	.probe          = qoriq_backplane_probe,
+	.remove         = backplane_remove,
+	.config_init    = qoriq_backplane_config_init,
+	.aneg_done      = backplane_aneg_done,

Here we register the particular phy device ID/mask and driver name 
specific for qoriq devices. 
Also we can use generic routines provided by generic backplane driver 
if they are suitable for particular qoriq device or otherwise we can use 
more specialized specific routines like: qoriq_backplane_config_init

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

* Re: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27 15:00     ` [EXT] " Florinel Iordache
@ 2020-03-27 15:28       ` Russell King - ARM Linux admin
  2020-03-27 15:44         ` Andrew Lunn
  0 siblings, 1 reply; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 15:28 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: Andrew Lunn, davem, netdev, f.fainelli, hkallweit1, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

On Fri, Mar 27, 2020 at 03:00:22PM +0000, Florinel Iordache wrote:
> > On Thu, Mar 26, 2020 at 03:51:15PM +0200, Florinel Iordache wrote:
> > > Add ethernet backplane device tree bindings
> > 
> > > +  - |
> > > +    /* Backplane configurations for specific setup */
> > > +    &mdio9 {
> > > +        bpphy6: ethernet-phy@0 {
> > > +            compatible = "ethernet-phy-ieee802.3-c45";
> > > +            reg = <0x0>;
> > > +            lane-handle = <&lane_d>; /* use lane D */
> > > +            eq-algorithm = "bee";
> > > +            /* 10G Short cables setup: up to 30 cm cable */
> > > +            eq-init = <0x2 0x5 0x29>;
> > > +            eq-params = <0>;
> > > +        };
> > > +    };
> > 
> > So you are modelling this as just another PHY? Does the driver get loaded based
> > on the PHY ID in registers 2 and 3? Does the standard define these IDs or are
> > they vendor specific?
> > 
> > Thanks
> >         Andrew
> 
> Hi Andrew,
> Thank you all for the feedback.
> I am currently working to address the entire feedback received 
> so far for this new Backplane driver.
> 
> Yes, we are modelling backplane driver as a phy driver.

I think we need to think very carefully about that, and consider
whether that really is a good idea.  phylib is currently built
primarily around copper PHYs, although there are some which also
support fiber as well in weird "non-standard" forms.

What worries me is the situation which I've been working on, where
we want access to the PCS PHYs, and we can't have the PCS PHYs
represented as a phylib PHY because we may have a copper PHY behind
the PCS PHY, and we want to be talking to the copper PHY in the
first instance (the PCS PHY effectivel ybecomes a slave to the
copper PHY.)

My worry is that we're ending up with conflicting implementations
for the same hardware which may only end up causing problems down
the line.

Please can you look at my DPAA2 PCS series which has been previously
posted to netdev - it's rather difficult to work out who in NXP should
be copied, because that information is not visible to those of us in
the community - we only find that out after someone inside NXP posts
patches, and even then the MAINTAINERS file doesn't seem to get
updated.

It's also worth mentioning that on other SoCs, such as Marvell SoCs,
the serdes and "PCS" are entirely separate hardware blocks, and the
implementation in the kernel, which works very well, is to use the
drivers/phy for the serdes/comphy as they call it, and the ethernet
driver binds to the comphy to control the serdes settings, whereas
the ethernet driver looks after the PCS.  I haven't been able to
look at your code enough yet to work out if that would be possible.

I also wonder whether we want a separate class of MDIO device for
PCS PHYs, just as we have things like DSA switches implemented
entirely separately from phylib - they're basically different sub-
classes of a mdio device.

I think we have around 20 or so weeks to hash this out, since it's
clear that the 10gbase-kr (10GKR) phy interface mode can't be used
until we've eliminated it from existing dts files.

> The driver is loaded based on PHY ID in registers 2 and 3 which 
> are specified by the standard but it is a vendor specific value: 
> 32-Bit identifier composed of the 3rd through 24th bits of the 
> Organizationally Unique Identifier (OUI) assigned to the device 
> manufacturer by the IEEE, plus a six-bit model number, plus a 
> four-bit revision number.
> This is done in the device specific code and not in backplane 
> generic driver.
> You can check support for QorIQ devices where qoriq_backplane_driver 
> is registered as a phy_driver:
>  
> @file: qoriq_backplane.c
> +static struct phy_driver qoriq_backplane_driver[] = {
> +	{
> +	.phy_id		= PCS_PHY_DEVICE_ID,
> +	.name		= QORIQ_BACKPLANE_DRIVER_NAME,
> +	.phy_id_mask	= PCS_PHY_DEVICE_ID_MASK,
> +	.features       = BACKPLANE_FEATURES,
> +	.probe          = qoriq_backplane_probe,
> +	.remove         = backplane_remove,
> +	.config_init    = qoriq_backplane_config_init,
> +	.aneg_done      = backplane_aneg_done,
> 
> Here we register the particular phy device ID/mask and driver name 
> specific for qoriq devices. 
> Also we can use generic routines provided by generic backplane driver 
> if they are suitable for particular qoriq device or otherwise we can use 
> more specialized specific routines like: qoriq_backplane_config_init
> 

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* Re: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27 15:28       ` Russell King - ARM Linux admin
@ 2020-03-27 15:44         ` Andrew Lunn
  2020-03-27 17:35           ` Russell King - ARM Linux admin
  0 siblings, 1 reply; 42+ messages in thread
From: Andrew Lunn @ 2020-03-27 15:44 UTC (permalink / raw)
  To: Russell King - ARM Linux admin
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, Leo Li, Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

> What worries me is the situation which I've been working on, where
> we want access to the PCS PHYs, and we can't have the PCS PHYs
> represented as a phylib PHY because we may have a copper PHY behind
> the PCS PHY, and we want to be talking to the copper PHY in the
> first instance (the PCS PHY effectivel ybecomes a slave to the
> copper PHY.)

I guess we need to clarify what KR actually means. If we have a
backplane with a MAC on each end, i think modelling it as a PHY could
work.

If however, we have a MAC connected to a backplane, and on the end of
the backplane is a traditional PHY, or an SFP cage, we have problems.
As your point out, we cannot have two PHYs in a chain for one MAC.

But i agree with Russell. We need a general solution of how we deal
with PCSs.

   Andrew

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

* Re: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27 15:44         ` Andrew Lunn
@ 2020-03-27 17:35           ` Russell King - ARM Linux admin
  2020-03-30  5:43             ` Madalin Bucur (OSS)
  0 siblings, 1 reply; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-27 17:35 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, Leo Li, Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

On Fri, Mar 27, 2020 at 04:44:48PM +0100, Andrew Lunn wrote:
> > What worries me is the situation which I've been working on, where
> > we want access to the PCS PHYs, and we can't have the PCS PHYs
> > represented as a phylib PHY because we may have a copper PHY behind
> > the PCS PHY, and we want to be talking to the copper PHY in the
> > first instance (the PCS PHY effectivel ybecomes a slave to the
> > copper PHY.)
> 
> I guess we need to clarify what KR actually means. If we have a
> backplane with a MAC on each end, i think modelling it as a PHY could
> work.
> 
> If however, we have a MAC connected to a backplane, and on the end of
> the backplane is a traditional PHY, or an SFP cage, we have problems.
> As your point out, we cannot have two PHYs in a chain for one MAC.
> 
> But i agree with Russell. We need a general solution of how we deal
> with PCSs.

What really worries me is that we may be driving the same hardware
with two different approaches/drivers for two different applications
which isn't going to work out very well in the long run.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-27  1:07   ` Andrew Lunn
  2020-03-27 13:02     ` [EXT] " Florinel Iordache
@ 2020-03-27 17:43     ` Florian Fainelli
  1 sibling, 0 replies; 42+ messages in thread
From: Florian Fainelli @ 2020-03-27 17:43 UTC (permalink / raw)
  To: Andrew Lunn, Florinel Iordache
  Cc: davem, netdev, hkallweit1, linux, devicetree, linux-doc, robh+dt,
	mark.rutland, kuba, corbet, shawnguo, leoyang.li, madalin.bucur,
	ioana.ciornei, linux-kernel



On 3/26/2020 6:07 PM, Andrew Lunn wrote:
>> +static u32 le_ioread32(void __iomem *reg)
>> +{
>> +	return ioread32(reg);
>> +}
>> +
>> +static void le_iowrite32(u32 value, void __iomem *reg)
>> +{
>> +	iowrite32(value, reg);
>> +}
>> +
>> +static u32 be_ioread32(void __iomem *reg)
>> +{
>> +	return ioread32be(reg);
>> +}
>> +
>> +static void be_iowrite32(u32 value, void __iomem *reg)
>> +{
>> +	iowrite32be(value, reg);
>> +}
> 
> This is very surprising to me. I've not got my head around the
> structure of this code yet, but i'm surprised to see memory mapped
> access functions in generic code.

This abstraction makes no sense whatsoever, you already have
io{read,write}32{be,} to deal with the correct endian, and you can use
the standard Device Tree properties 'big-endian', 'little-endian',
'native-endian' to decide which of those of to use. If you need to
introduce a wrapper or indirect function calls to select the correct I/O
accessor, that is fine of course.
-- 
Florian

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

* Re: [PATCH net-next 6/9] net: phy: add backplane kr driver support
  2020-03-27 14:22   ` Andrew Lunn
@ 2020-03-27 18:25     ` Joe Perches
  0 siblings, 0 replies; 42+ messages in thread
From: Joe Perches @ 2020-03-27 18:25 UTC (permalink / raw)
  To: Andrew Lunn, Florinel Iordache
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel

On Fri, 2020-03-27 at 15:22 +0100, Andrew Lunn wrote:
> > +/* Backplane custom logging */
> > +#define BPDEV_LOG(name) \
> > +	char log_buffer[LOG_BUFFER_SIZE]; \
> > +	va_list args; va_start(args, msg); \
> > +	vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \
> > +	if (!bpphy->attached_dev) \
> > +		dev_##name(&bpphy->mdio.dev, log_buffer); \
> > +	else \
> > +		dev_##name(&bpphy->mdio.dev, "%s: %s", \
> > +			netdev_name(bpphy->attached_dev), log_buffer); \
> > +	va_end(args)

This could also use %pV instead of an intermediate buffer.

It's also bad form to use macros with required
external variables.


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

* RE: [EXT] Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-27  0:15   ` Andrew Lunn
  2020-03-27 12:01     ` Russell King - ARM Linux admin
@ 2020-03-29  8:22     ` Florinel Iordache
  2020-03-29  9:01       ` Russell King - ARM Linux admin
  1 sibling, 1 reply; 42+ messages in thread
From: Florinel Iordache @ 2020-03-29  8:22 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: davem, netdev, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel, Florinel Iordache

> On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> > Add support for backplane kr phy connection types currently available
> > (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> > the cases for KR modes which are clause 45 compatible to correctly
> > assign phy_interface and phylink#supported)
> >
> > Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> > ---
> >  drivers/net/phy/phylink.c | 15 ++++++++++++---
> >  include/linux/phy.h       |  6 +++++-
> >  2 files changed, 17 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > index fed0c59..db1bb87 100644
> > --- a/drivers/net/phy/phylink.c
> > +++ b/drivers/net/phy/phylink.c
> > @@ -4,6 +4,7 @@
> >   * technologies such as SFP cages where the PHY is hot-pluggable.
> >   *
> >   * Copyright (C) 2015 Russell King
> > + * Copyright 2020 NXP
> >   */
> >  #include <linux/ethtool.h>
> >  #include <linux/export.h>
> > @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct
> fwnode_handle *fwnode)
> >                       break;
> >
> >               case PHY_INTERFACE_MODE_USXGMII:
> > -             case PHY_INTERFACE_MODE_10GKR:
> 
> We might have a backwards compatibility issue here. If i remember correctly,
> there are some boards out in the wild using PHY_INTERFACE_MODE_10GKR not
> PHY_INTERFACE_MODE_10GBASER.
> 
> See e0f909bc3a242296da9ccff78277f26d4883a79d
> 
> Russell, what do you say about this?
> 
>          Andrew

Ethernet interface nomenclature is using the following terminology:
e.g. 10GBase-KR: data rate (10G),  modulation type (Base = Baseband),
medium type (K = BacKplane), physical layer encoding scheme
(R = scRambling/descRambling using 64b/66b encoding that allows for
Ethernet framing at a rate of 10.3125 Gbit/s)
So 10GBase-R name provide information only about the data rate and
the encoding scheme without specifying the interface medium.
10GBase-R is a family of many different standards specified for
several different physical medium for copper and optical fiber like
for example:
	10GBase-SR: Short range (over fiber)
	10GBase-LR: Long reach (over fiber)
	10GBase-LRM: Long reach multi-mode (over fiber)
	10GBase-ER: Extended reach (over fiber)
	10GBase-CR: 10G over copper
	10GBase-KR: Backplane

10GBase-KR represents Ethernet operation over electrical backplanes on
single lane and uses the same physical layer encoding as 10GBase-LR/ER/SR
defined in IEEE802.3 Clause 49. 
So prior to my change, phy_interface_t provided both enumerators which is correct:
	PHY_INTERFACE_MODE_10GBASER
	PHY_INTERFACE_MODE_10GKR
Perhaps PHY_INTERFACE_MODE_10GKR was not used before because Backplane
support was not available and all 10GBase-R family of interfaces should
be using PHY_INTERFACE_MODE_10GBASER.
In case PHY_INTERFACE_MODE_10GKR was used before, this is probably
incorrect and should be changed for those boards to use the correct phy
connection type: PHY_INTERFACE_MODE_10GBASER.
Moreover now I also introduced a new phy connection type to cover also:
40GBase-KR4 which is 40G Backplane over 4-lanes:
	PHY_INTERFACE_MODE_40GKR4

Prior to adding backplane support, 10GKR case was handled the same as
10GBASER in phylink_parse_mode. 
But now because we are adding support for backplane modes, all backplane
phy connection types (like: 10GKR, 40GKR4) must be treated separately to
correctly setup the supported phylink.

Florin.

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

* Re: [EXT] Re: [PATCH net-next 3/9] net: phy: add kr phy connection type
  2020-03-29  8:22     ` [EXT] " Florinel Iordache
@ 2020-03-29  9:01       ` Russell King - ARM Linux admin
  0 siblings, 0 replies; 42+ messages in thread
From: Russell King - ARM Linux admin @ 2020-03-29  9:01 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: Andrew Lunn, davem, netdev, f.fainelli, hkallweit1, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo, Leo Li,
	Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

On Sun, Mar 29, 2020 at 08:22:10AM +0000, Florinel Iordache wrote:
> > On Thu, Mar 26, 2020 at 03:51:16PM +0200, Florinel Iordache wrote:
> > > Add support for backplane kr phy connection types currently available
> > > (10gbase-kr, 40gbase-kr4) and the required phylink updates (cover all
> > > the cases for KR modes which are clause 45 compatible to correctly
> > > assign phy_interface and phylink#supported)
> > >
> > > Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> > > ---
> > >  drivers/net/phy/phylink.c | 15 ++++++++++++---
> > >  include/linux/phy.h       |  6 +++++-
> > >  2 files changed, 17 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > > index fed0c59..db1bb87 100644
> > > --- a/drivers/net/phy/phylink.c
> > > +++ b/drivers/net/phy/phylink.c
> > > @@ -4,6 +4,7 @@
> > >   * technologies such as SFP cages where the PHY is hot-pluggable.
> > >   *
> > >   * Copyright (C) 2015 Russell King
> > > + * Copyright 2020 NXP
> > >   */
> > >  #include <linux/ethtool.h>
> > >  #include <linux/export.h>
> > > @@ -303,7 +304,6 @@ static int phylink_parse_mode(struct phylink *pl, struct
> > fwnode_handle *fwnode)
> > >                       break;
> > >
> > >               case PHY_INTERFACE_MODE_USXGMII:
> > > -             case PHY_INTERFACE_MODE_10GKR:
> > 
> > We might have a backwards compatibility issue here. If i remember correctly,
> > there are some boards out in the wild using PHY_INTERFACE_MODE_10GKR not
> > PHY_INTERFACE_MODE_10GBASER.
> > 
> > See e0f909bc3a242296da9ccff78277f26d4883a79d
> > 
> > Russell, what do you say about this?
> > 
> >          Andrew
> 
> Ethernet interface nomenclature is using the following terminology:
> e.g. 10GBase-KR: data rate (10G),  modulation type (Base = Baseband),
> medium type (K = BacKplane), physical layer encoding scheme
> (R = scRambling/descRambling using 64b/66b encoding that allows for
> Ethernet framing at a rate of 10.3125 Gbit/s)
> So 10GBase-R name provide information only about the data rate and
> the encoding scheme without specifying the interface medium.
> 10GBase-R is a family of many different standards specified for
> several different physical medium for copper and optical fiber like
> for example:
> 	10GBase-SR: Short range (over fiber)
> 	10GBase-LR: Long reach (over fiber)
> 	10GBase-LRM: Long reach multi-mode (over fiber)
> 	10GBase-ER: Extended reach (over fiber)
> 	10GBase-CR: 10G over copper
> 	10GBase-KR: Backplane
> 
> 10GBase-KR represents Ethernet operation over electrical backplanes on
> single lane and uses the same physical layer encoding as 10GBase-LR/ER/SR
> defined in IEEE802.3 Clause 49. 

I'm not sure why NXP folk think that they have to keep explaining this
to us.  You do not.

> So prior to my change, phy_interface_t provided both enumerators which is correct:
> 	PHY_INTERFACE_MODE_10GBASER
> 	PHY_INTERFACE_MODE_10GKR
> Perhaps PHY_INTERFACE_MODE_10GKR was not used before because Backplane
> support was not available and all 10GBase-R family of interfaces should
> be using PHY_INTERFACE_MODE_10GBASER.

What you are missing is that PHY_INTERFACE_MODE_10GKR was introduced
first and used _incorrectly_.  We are currently mid-transition to
correct that mistake.

While we are in transition, PHY_INTERFACE_MODE_10GKR can _not_ be used
correctly, and nothing NXP or anyone else says will change that fact
until the transition has been completed.

In kernel land, we do not intentionally regress platforms, even if we
have made a mistake.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 10.2Mbps down 587kbps up

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

* RE: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-27 17:35           ` Russell King - ARM Linux admin
@ 2020-03-30  5:43             ` Madalin Bucur (OSS)
  0 siblings, 0 replies; 42+ messages in thread
From: Madalin Bucur (OSS) @ 2020-03-30  5:43 UTC (permalink / raw)
  To: Russell King - ARM Linux admin, Andrew Lunn
  Cc: Florinel Iordache, davem, netdev, f.fainelli, hkallweit1,
	devicetree, linux-doc, robh+dt, mark.rutland, kuba, corbet,
	shawnguo, Leo Li, Madalin Bucur (OSS),
	Ioana Ciornei, linux-kernel

> -----Original Message-----
> From: netdev-owner@vger.kernel.org <netdev-owner@vger.kernel.org> On
> Behalf Of Russell King - ARM Linux admin
> Sent: Friday, March 27, 2020 7:35 PM
> To: Andrew Lunn <andrew@lunn.ch>
> Cc: Florinel Iordache <florinel.iordache@nxp.com>; davem@davemloft.net;
> netdev@vger.kernel.org; f.fainelli@gmail.com; hkallweit1@gmail.com;
> devicetree@vger.kernel.org; linux-doc@vger.kernel.org; robh+dt@kernel.org;
> mark.rutland@arm.com; kuba@kernel.org; corbet@lwn.net;
> shawnguo@kernel.org; Leo Li <leoyang.li@nxp.com>; Madalin Bucur (OSS)
> <madalin.bucur@oss.nxp.com>; Ioana Ciornei <ioana.ciornei@nxp.com>; linux-
> kernel@vger.kernel.org
> Subject: Re: [EXT] Re: [PATCH net-next 2/9] dt-bindings: net: add
> backplane dt bindings
> 
> On Fri, Mar 27, 2020 at 04:44:48PM +0100, Andrew Lunn wrote:
> > > What worries me is the situation which I've been working on, where
> > > we want access to the PCS PHYs, and we can't have the PCS PHYs
> > > represented as a phylib PHY because we may have a copper PHY behind
> > > the PCS PHY, and we want to be talking to the copper PHY in the
> > > first instance (the PCS PHY effectivel ybecomes a slave to the
> > > copper PHY.)
> >
> > I guess we need to clarify what KR actually means. If we have a
> > backplane with a MAC on each end, i think modelling it as a PHY could
> > work.
> >
> > If however, we have a MAC connected to a backplane, and on the end of
> > the backplane is a traditional PHY, or an SFP cage, we have problems.
> > As your point out, we cannot have two PHYs in a chain for one MAC.
> >
> > But i agree with Russell. We need a general solution of how we deal
> > with PCSs.
> 
> What really worries me is that we may be driving the same hardware
> with two different approaches/drivers for two different applications
> which isn't going to work out very well in the long run.

The same HW can be used in multiple ways here so having different drivers
for these modes is not really an issue, you won't be able to use it both
in backplane and non-backplane mode at the same time.

Besides the (oversimplifying) model used in SW, there is no constraint
to have just one independently manageable entity belonging to the PHY
layer. Nowadays there are complex configurable PCS/PMA units, retimers,
single chip PHYs that can function also in backplane mode, and so on.
All these require a rethinking of the one PHY per interface, tied to a
MDIO bus model we use today. The DPAA 1 already make use of the MDIO bus
infrastructure to manage the PCS devices in the SoC, without an issue
related to the PHYlib one PHY assumption.

One risk I see here is that we may abandon PHYlib before we give it a
chance to adapt to the new complexity of the HW and roll something new
just to do away with the required work in understanding its inner workings.
This could even be fine but it creates a no return point for drivers that
will use a new infrastructure we put in place (i.e. no backporting).

Madalin

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

* Re: [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings
  2020-03-26 13:51 ` [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings Florinel Iordache
  2020-03-27  1:04   ` Andrew Lunn
@ 2020-03-30 15:39   ` Rob Herring
  1 sibling, 0 replies; 42+ messages in thread
From: Rob Herring @ 2020-03-30 15:39 UTC (permalink / raw)
  To: Florinel Iordache
  Cc: davem, netdev, andrew, f.fainelli, hkallweit1, linux, devicetree,
	linux-doc, robh+dt, mark.rutland, kuba, corbet, shawnguo,
	leoyang.li, madalin.bucur, ioana.ciornei, linux-kernel,
	Florinel Iordache

On Thu, 26 Mar 2020 15:51:15 +0200, Florinel Iordache wrote:
> Add ethernet backplane device tree bindings
> 
> Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com>
> ---
>  .../bindings/net/ethernet-controller.yaml          |  3 +-
>  .../devicetree/bindings/net/ethernet-phy.yaml      | 53 +++++++++++++
>  Documentation/devicetree/bindings/net/serdes.yaml  | 90 ++++++++++++++++++++++
>  3 files changed, 145 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/net/serdes.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

Documentation/devicetree/bindings/net/serdes.yaml:  mapping values are not allowed in this context
  in "<unicode string>", line 30, column 62
Documentation/devicetree/bindings/Makefile:12: recipe for target 'Documentation/devicetree/bindings/net/serdes.example.dts' failed
make[1]: *** [Documentation/devicetree/bindings/net/serdes.example.dts] Error 1
make[1]: *** Waiting for unfinished jobs....
Documentation/devicetree/bindings/net/ethernet-phy.yaml:  mapping values are not allowed in this context
  in "<unicode string>", line 179, column 20
Documentation/devicetree/bindings/Makefile:12: recipe for target 'Documentation/devicetree/bindings/net/ethernet-phy.example.dts' failed
make[1]: *** [Documentation/devicetree/bindings/net/ethernet-phy.example.dts] Error 1
warning: no schema found in file: Documentation/devicetree/bindings/net/serdes.yaml
warning: no schema found in file: Documentation/devicetree/bindings/net/ethernet-phy.yaml
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/net/serdes.yaml: ignoring, error parsing file
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/net/ethernet-phy.yaml: ignoring, error parsing file
Makefile:1262: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1261991

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.

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

end of thread, other threads:[~2020-03-30 15:39 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-26 13:51 [PATCH net-next 0/9] net: ethernet backplane support Florinel Iordache
2020-03-26 13:51 ` [PATCH net-next 1/9] doc: net: add backplane documentation Florinel Iordache
2020-03-26 13:51 ` [PATCH net-next 2/9] dt-bindings: net: add backplane dt bindings Florinel Iordache
2020-03-27  1:04   ` Andrew Lunn
2020-03-27 12:06     ` Russell King - ARM Linux admin
2020-03-27 15:00     ` [EXT] " Florinel Iordache
2020-03-27 15:28       ` Russell King - ARM Linux admin
2020-03-27 15:44         ` Andrew Lunn
2020-03-27 17:35           ` Russell King - ARM Linux admin
2020-03-30  5:43             ` Madalin Bucur (OSS)
2020-03-30 15:39   ` Rob Herring
2020-03-26 13:51 ` [PATCH net-next 3/9] net: phy: add kr phy connection type Florinel Iordache
2020-03-27  0:15   ` Andrew Lunn
2020-03-27 12:01     ` Russell King - ARM Linux admin
2020-03-27 12:12       ` Madalin Bucur (OSS)
2020-03-27 12:40         ` Russell King - ARM Linux admin
2020-03-29  8:22     ` [EXT] " Florinel Iordache
2020-03-29  9:01       ` Russell King - ARM Linux admin
2020-03-27  0:32   ` Florian Fainelli
2020-03-26 13:51 ` [PATCH net-next 4/9] net: fman: add kr support for dpaa1 mac Florinel Iordache
2020-03-26 13:51 ` [PATCH net-next 5/9] net: dpaa2: add kr support for dpaa2 mac Florinel Iordache
2020-03-26 13:51 ` [PATCH net-next 6/9] net: phy: add backplane kr driver support Florinel Iordache
2020-03-26 18:53   ` David Miller
2020-03-26 18:55     ` Joe Perches
2020-03-26 19:07       ` David Miller
2020-03-26 19:42         ` Joe Perches
2020-03-27  1:07   ` Andrew Lunn
2020-03-27 13:02     ` [EXT] " Florinel Iordache
2020-03-27 13:23       ` Andrew Lunn
2020-03-27 17:43     ` Florian Fainelli
2020-03-27 14:22   ` Andrew Lunn
2020-03-27 18:25     ` Joe Perches
2020-03-27 14:28   ` Andrew Lunn
2020-03-27 14:33   ` Andrew Lunn
2020-03-27 14:38   ` Andrew Lunn
2020-03-26 13:51 ` [PATCH net-next 7/9] net: phy: enable qoriq backplane support Florinel Iordache
2020-03-26 20:03   ` Joe Perches
2020-03-26 20:13     ` Andrew Lunn
2020-03-26 20:27       ` Joe Perches
2020-03-26 13:51 ` [PATCH net-next 8/9] net: phy: add bee algorithm for kr training Florinel Iordache
2020-03-26 13:51 ` [PATCH net-next 9/9] arm64: dts: add serdes and mdio description Florinel Iordache
2020-03-27 12:09   ` Russell King - ARM Linux admin

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