devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink
@ 2022-06-28 22:13 Sean Anderson
  2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:13 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Benjamin Herrenschmidt,
	Ioana Ciornei, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Li Yang, Michael Ellerman, Paul Mackerras,
	Rob Herring, Shawn Guo, Vinod Koul, devicetree, linux-doc,
	linux-phy, linuxppc-dev

This series converts the DPAA driver to phylink. Additionally,
it also adds a serdes driver to allow for dynamic reconfiguration
between 1g and 10g interfaces (such as in an SFP+ slot). These changes
are submitted together for this RFC, but they will eventually be
submitted separately to the appropriate subsystem maintainers.

I have tried to maintain backwards compatibility with existing device
trees whereever possible. However, one area where I was unable to
achieve this was with QSGMII. Please refer to patch 3 for details.

All mac drivers have now been converted. I would greatly appreciate if
anyone has QorIQ boards they can test/debug this series on. I only have an
LS1046ARDB. Everything but QSGMII should work without breakage; QSGMII
needs patch 33.

The serdes driver is mostly functional (but not quite, see patches 5 and
31).  This series only adds support for the LS1046ARDB SerDes, but it
should be fairly straightforward to add support for other SoCs and
boards (see Documentation/driver-api/phy/qoriq.rst). Patches 34 and 25
should show the typical steps.

This patches in this series can be logically categorized as follows:
- 1, 4, 29, 34-35: SerDes support
- 2, 5-25: Cleanups. These can be applied as-is.
- 3, 26-28, 30-33: Phylink conversion

Patches 5-9 were first submitted as [1].

[1] https://lore.kernel.org/netdev/20220531195851.1592220-1-sean.anderson@seco.com/

Changes in v2:
- Add #clock-cells. This will allow using assigned-clocks* to configure
  the PLLs.
- Add CGR update function
- Add helper for sanity checking cgr ops
- Add nodes for QSGMII PCSs
- Add rgmii property to all DPAA MACs
- Adjust queue depth on rate change
- Allow a value of 1 for phy-cells. This allows for compatibility with
  the similar (but according to Ioana Ciornei different enough) lynx-28g
  binding.
- Better document how we select which PCS to use in the default case
- Clear SGMIIaCR1_PCS_EN during probe
- Configure the SerDes in enable/disable
- Convert 10GEC and dTSEC as well
- Convert FMan MAC bindings to yaml
- Disable SerDes by default to prevent breaking boards inadvertently.
- Document phy cells in the description
- Document the structure of the compatible strings
- Fix capitalization of mEMAC in commit messages
- Fix example binding having too many cells in regs
- Fix not clearing group->pll after disabling it
- Fix prototype for dtsec_initialization
- Fix warning if sizeof(void *) != sizeof(resource_size_t)
- Handle 1000Base-KX in lynx_proto_mode_prep
- Move PCS_LYNX dependency to fman Kconfig
- Move compatible first
- Power off lanes during probe
- Properly implement all ethtool ops and ioctls. These were mostly
  stubbed out just enough to compile last time.
- Refer to the device in the documentation, rather than the binding
- Remove minItems
- Remove some unused variables
- Remove unused variable slow_10g_if
- Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
- Rename driver to Lynx 10G (etc.)
- Rename to fsl,lynx-10g.yaml
- Restrict valid link modes based on the phy interface. This is easier
  to set up, and mostly captures what I intended to do the first time.
  We now have a custom validate which restricts half-duplex for some SoCs
  for RGMII, but generally just uses the default phylink validate.
- Specify type of mac_dev for exception_cb
- Support 1 and 2 phy-cells
- Use list for clock-names
- Use one phy cell for SerDes1, since no lanes can be grouped

Sean Anderson (35):
  dt-bindings: phy: Add QorIQ SerDes binding
  dt-bindings: net: Convert FMan MAC bindings to yaml
  dt-bindings: net: fman: Add additional interface properties
  [RFC] phy: fsl: Add Lynx 10G SerDes driver
  net: fman: Convert to SPDX identifiers
  net: fman: Don't pass comm_mode to enable/disable
  net: fman: Store en/disable in mac_device instead of mac_priv_s
  net: fman: dtsec: Always gracefully stop/start
  net: fman: Get PCS node in per-mac init
  net: fman: Store initialization function in match data
  net: fman: Move struct dev to mac_device
  net: fman: Configure fixed link in memac_initialization
  net: fman: Export/rename some common functions
  net: fman: memac: Use params instead of priv for max_speed
  net: fman: Move initialization to mac-specific files
  net: fman: Mark mac methods static
  net: fman: Inline several functions into initialization
  net: fman: Remove internal_phy_node from params
  net: fman: Map the base address once
  net: fman: Pass params directly to mac init
  net: fman: Use mac_dev for some params
  net: fman: Specify type of mac_dev for exception_cb
  net: fman: Clean up error handling
  net: fman: Change return type of disable to void
  net: dpaa: Use mac_dev variable in dpaa_netdev_init
  soc: fsl: qbman: Add helper for sanity checking cgr ops
  soc: fsl: qbman: Add CGR update function
  net: dpaa: Adjust queue depth on rate change
  net: fman: memac: Add serdes support
  net: fman: memac: Use lynx pcs driver
  [RFT] net: dpaa: Convert to phylink
  qoriq: Specify which MACs support RGMII
  qoriq: Add nodes for QSGMII PCSs
  arm64: dts: ls1046a: Add serdes bindings
  arm64: dts: ls1046ardb: Add serdes bindings

 .../bindings/net/fsl,fman-dtsec.yaml          |  188 +++
 .../devicetree/bindings/net/fsl-fman.txt      |  133 +-
 .../devicetree/bindings/phy/fsl,lynx-10g.yaml |   93 ++
 Documentation/driver-api/phy/index.rst        |    1 +
 Documentation/driver-api/phy/qoriq.rst        |   93 ++
 MAINTAINERS                                   |    6 +
 .../boot/dts/freescale/fsl-ls1043-post.dtsi   |   28 +
 .../boot/dts/freescale/fsl-ls1046-post.dtsi   |   33 +
 .../boot/dts/freescale/fsl-ls1046a-rdb.dts    |   34 +
 .../arm64/boot/dts/freescale/fsl-ls1046a.dtsi |   16 +
 arch/powerpc/boot/dts/fsl/b4860si-post.dtsi   |    4 +
 arch/powerpc/boot/dts/fsl/b4si-post.dtsi      |    4 +
 .../fsl/qoriq-fman3-0-10g-0-best-effort.dtsi  |    3 +-
 .../boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi     |    9 +-
 .../fsl/qoriq-fman3-0-10g-1-best-effort.dtsi  |    9 +-
 .../boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi     |    9 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi      |    3 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi      |    3 +-
 .../boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi     |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi     |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi      |    3 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi      |    9 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi      |    3 +-
 .../boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi      |    9 +-
 arch/powerpc/boot/dts/fsl/t1023si-post.dtsi   |    4 +
 arch/powerpc/boot/dts/fsl/t1040si-post.dtsi   |    7 +
 arch/powerpc/boot/dts/fsl/t2081si-post.dtsi   |    8 +
 arch/powerpc/boot/dts/fsl/t4240si-post.dtsi   |   16 +
 drivers/net/ethernet/freescale/dpaa/Kconfig   |    4 +-
 .../net/ethernet/freescale/dpaa/dpaa_eth.c    |  132 +-
 .../ethernet/freescale/dpaa/dpaa_eth_sysfs.c  |    2 +-
 .../ethernet/freescale/dpaa/dpaa_ethtool.c    |   90 +-
 drivers/net/ethernet/freescale/fman/Kconfig   |    3 +-
 drivers/net/ethernet/freescale/fman/fman.c    |   31 +-
 drivers/net/ethernet/freescale/fman/fman.h    |   31 +-
 .../net/ethernet/freescale/fman/fman_dtsec.c  |  670 ++++----
 .../net/ethernet/freescale/fman/fman_dtsec.h  |   58 +-
 .../net/ethernet/freescale/fman/fman_keygen.c |   29 +-
 .../net/ethernet/freescale/fman/fman_keygen.h |   29 +-
 .../net/ethernet/freescale/fman/fman_mac.h    |   34 +-
 .../net/ethernet/freescale/fman/fman_memac.c  |  877 +++++-----
 .../net/ethernet/freescale/fman/fman_memac.h  |   57 +-
 .../net/ethernet/freescale/fman/fman_muram.c  |   31 +-
 .../net/ethernet/freescale/fman/fman_muram.h  |   32 +-
 .../net/ethernet/freescale/fman/fman_port.c   |   29 +-
 .../net/ethernet/freescale/fman/fman_port.h   |   29 +-
 drivers/net/ethernet/freescale/fman/fman_sp.c |   29 +-
 drivers/net/ethernet/freescale/fman/fman_sp.h |   28 +-
 .../net/ethernet/freescale/fman/fman_tgec.c   |  274 ++-
 .../net/ethernet/freescale/fman/fman_tgec.h   |   54 +-
 drivers/net/ethernet/freescale/fman/mac.c     |  653 +-------
 drivers/net/ethernet/freescale/fman/mac.h     |   66 +-
 drivers/phy/freescale/Kconfig                 |   20 +
 drivers/phy/freescale/Makefile                |    1 +
 drivers/phy/freescale/phy-fsl-lynx-10g.c      | 1483 +++++++++++++++++
 drivers/soc/fsl/qbman/qman.c                  |   76 +-
 include/soc/fsl/qman.h                        |    9 +
 63 files changed, 3330 insertions(+), 2331 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
 create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
 create mode 100644 Documentation/driver-api/phy/qoriq.rst
 create mode 100644 drivers/phy/freescale/phy-fsl-lynx-10g.c

-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
@ 2022-06-28 22:13 ` Sean Anderson
  2022-06-29  2:09   ` Rob Herring
  2022-06-30 17:27   ` Rob Herring
  2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:13 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-phy

This adds a binding for the SerDes module found on QorIQ processors. The
phy reference has two cells, one for the first lane and one for the
last. This should allow for good support of multi-lane protocols when
(if) they are added. There is no protocol option, because the driver is
designed to be able to completely reconfigure lanes at runtime.
Generally, the phy consumer can select the appropriate protocol using
set_mode. For the most part there is only one protocol controller
(consumer) per lane/protocol combination. The exception to this is the
B4860 processor, which has some lanes which can be connected to
multiple MACs. For that processor, I anticipate the easiest way to
resolve this will be to add an additional cell with a "protocol
controller instance" property.

Each serdes has a unique set of supported protocols (and lanes). The
support matrix is stored in the driver and is selected based on the
compatible string. It is anticipated that a new compatible string will
need to be added for each serdes on each SoC that drivers support is
added for. There is no "generic" compatible string for this reason.

There are two PLLs, each of which can be used as the master clock for
each lane. Each PLL has its own reference. For the moment they are
required, because it simplifies the driver implementation. Absent
reference clocks can be modeled by a fixed-clock with a rate of 0.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- Add #clock-cells. This will allow using assigned-clocks* to configure
  the PLLs.
- Allow a value of 1 for phy-cells. This allows for compatibility with
  the similar (but according to Ioana Ciornei different enough) lynx-28g
  binding.
- Document phy cells in the description
- Document the structure of the compatible strings
- Fix example binding having too many cells in regs
- Move compatible first
- Refer to the device in the documentation, rather than the binding
- Remove minItems
- Rename to fsl,lynx-10g.yaml
- Use list for clock-names

 .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml

diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
new file mode 100644
index 000000000000..b5a6f631df9f
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP Lynx 10G SerDes
+
+maintainers:
+  - Sean Anderson <sean.anderson@seco.com>
+
+description: |
+  These Lynx "SerDes" devices are found in NXP's QorIQ line of processors. The
+  SerDes provides up to eight lanes. Each lane may be configured individually,
+  or may be combined with adjacent lanes for a multi-lane protocol. The SerDes
+  supports a variety of protocols, including up to 10G Ethernet, PCIe, SATA, and
+  others. The specific protocols supported for each lane depend on the
+  particular SoC.
+
+properties:
+  compatible:
+    description: |
+      Each compatible is of the form "fsl,<soc-name>-serdes-<instance>".
+      Although many registers are compatible between different SoCs, the
+      supported protocols and lane assignments tend to be unique to each SerDes.
+      Additionally, the method of activating protocols may also be unique.
+      Because of this, each SerDes instance will need its own compatible string.
+      In cases where two SoCs share the same SerDes implementation (such as the
+      LS1046A and LS1026A), both SoCs should share the same compatible strings.
+    enum:
+      - fsl,ls1046a-serdes-1
+      - fsl,ls1046a-serdes-2
+
+  "#clock-cells":
+    const: 1
+    description: |
+      The cell contains the index of the PLL, starting from 0. Note that when
+      assigning a rate to a PLL, the PLLs' rates are divided by 1000 to avoid
+      overflow. A rate of 5000000 corresponds to 5GHz.
+
+  "#phy-cells":
+    minimum: 1
+    maximum: 2
+    description: |
+      The cells contain the following arguments:
+      - The first lane in the group. Lanes are numbered based on the register
+        offsets, not the I/O ports. This corresponds to the letter-based ("Lane
+        A") naming scheme, and not the number-based ("Lane 0") naming scheme. On
+        most SoCs, "Lane A" is "Lane 0", but not always.
+      - Last lane. For single-lane protocols, this should be the same as the
+        first lane.
+      If no lanes in a SerDes can be grouped, then #phy-cells may be 1, and the
+      first cell will specify the only lane in the group.
+
+  clocks:
+    maxItems: 2
+    description: |
+      Clock for each PLL reference clock input.
+
+  clock-names:
+    items:
+      - enum: &clocks
+          - ref0
+          - ref1
+      - enum: *clocks
+
+  reg:
+    maxItems: 1
+
+required:
+  - "#clock-cells"
+  - "#phy-cells"
+  - compatible
+  - clocks
+  - clock-names
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    serdes1: phy@1ea0000 {
+      #clock-cells = <1>;
+      #phy-cells = <2>;
+      compatible = "fsl,ls1046a-serdes-1";
+      reg = <0x1ea0000 0x2000>;
+      clocks = <&clk_100mhz>, <&clk_156mhz>;
+      clock-names = "ref0", "ref1";
+      assigned-clocks = <&serdes1 0>;
+      assigned-clock-rates = <5000000>;
+    };
+
+...
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
  2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
@ 2022-06-28 22:13 ` Sean Anderson
  2022-06-29  2:09   ` Rob Herring
                     ` (2 more replies)
  2022-06-28 22:13 ` [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties Sean Anderson
                   ` (5 subsequent siblings)
  7 siblings, 3 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:13 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Krzysztof Kozlowski, Rob Herring,
	devicetree

This converts the MAC portion of the FMan MAC bindings to yaml.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- New

 .../bindings/net/fsl,fman-dtsec.yaml          | 144 ++++++++++++++++++
 .../devicetree/bindings/net/fsl-fman.txt      | 128 +---------------
 2 files changed, 145 insertions(+), 127 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml

diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
new file mode 100644
index 000000000000..809df1589f20
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/fsl,fman-dtsec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP FMan MAC
+
+maintainers:
+  - Madalin Bucur <madalin.bucur@nxp.com>
+
+description: |
+  Each FMan has several MACs, each implementing an Ethernet interface. Earlier
+  versions of FMan used the Datapath Three Speed Ethernet Controller (dTSEC) for
+  10/100/1000 MBit/s speeds, and the 10-Gigabit Ethernet Media Access Controller
+  (10GEC) for 10 Gbit/s speeds. Later versions of FMan use the Multirate
+  Ethernet Media Access Controller (mEMAC) to handle all speeds.
+
+properties:
+  compatible:
+    enum:
+      - fsl,fman-dtsec
+      - fsl,fman-xgec
+      - fsl,fman-memac
+
+  cell-index:
+    maximum: 64
+    description: |
+      FManV2:
+      register[bit]           MAC             cell-index
+      ============================================================
+      FM_EPI[16]              XGEC            8
+      FM_EPI[16+n]            dTSECn          n-1
+      FM_NPI[11+n]            dTSECn          n-1
+              n = 1,..,5
+
+      FManV3:
+      register[bit]           MAC             cell-index
+      ============================================================
+      FM_EPI[16+n]            mEMACn          n-1
+      FM_EPI[25]              mEMAC10         9
+
+      FM_NPI[11+n]            mEMACn          n-1
+      FM_NPI[10]              mEMAC10         9
+      FM_NPI[11]              mEMAC9          8
+              n = 1,..8
+
+      FM_EPI and FM_NPI are located in the FMan memory map.
+
+      2. SoC registers:
+
+      - P2041, P3041, P4080 P5020, P5040:
+      register[bit]           FMan            MAC             cell
+                              Unit                            index
+      ============================================================
+      DCFG_DEVDISR2[7]        1               XGEC            8
+      DCFG_DEVDISR2[7+n]      1               dTSECn          n-1
+      DCFG_DEVDISR2[15]       2               XGEC            8
+      DCFG_DEVDISR2[15+n]     2               dTSECn          n-1
+              n = 1,..5
+
+      - T1040, T2080, T4240, B4860:
+      register[bit]                   FMan    MAC             cell
+                                      Unit                    index
+      ============================================================
+      DCFG_CCSR_DEVDISR2[n-1]         1       mEMACn          n-1
+      DCFG_CCSR_DEVDISR2[11+n]        2       mEMACn          n-1
+              n = 1,..6,9,10
+
+      EVDISR, DCFG_DEVDISR2 and DCFG_CCSR_DEVDISR2 are located in
+      the specific SoC "Device Configuration/Pin Control" Memory
+      Map.
+
+  reg:
+    maxItems: 1
+
+  fsl,fman-ports:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    maxItems: 2
+    description: |
+      An array of two references: the first is the FMan RX port and the second
+      is the TX port used by this MAC.
+
+  ptp-timer:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: A reference to the IEEE1588 timer
+
+  pcsphy-handle:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: A reference to the PCS (typically found on the SerDes)
+
+  tbi-handle:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: A reference to the (TBI-based) PCS
+
+required:
+  - compatible
+  - cell-index
+  - reg
+  - fsl,fman-ports
+  - ptp-timer
+
+allOf:
+  - $ref: "ethernet-controller.yaml#"
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,fman-dtsec
+    then:
+      required:
+        - tbi-handle
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,fman-memac
+    then:
+      required:
+        - pcsphy-handle
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    ethernet@e0000 {
+            compatible = "fsl,fman-dtsec";
+            cell-index = <0>;
+            reg = <0xe0000 0x1000>;
+            fsl,fman-ports = <&fman1_rx8 &fman1_tx28>;
+            ptp-timer = <&ptp_timer>;
+            tbi-handle = <&tbi0>;
+    };
+  - |
+    ethernet@e8000 {
+            cell-index = <4>;
+            compatible = "fsl,fman-memac";
+            reg = <0xe8000 0x1000>;
+            fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>;
+            ptp-timer = <&ptp_timer0>;
+            pcsphy-handle = <&pcsphy4>;
+            phy-handle = <&sgmii_phy1>;
+            phy-connection-type = "sgmii";
+    };
diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt
index 801efc7d6818..b9055335db3b 100644
--- a/Documentation/devicetree/bindings/net/fsl-fman.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fman.txt
@@ -232,133 +232,7 @@ port@81000 {
 =============================================================================
 FMan dTSEC/XGEC/mEMAC Node
 
-DESCRIPTION
-
-mEMAC/dTSEC/XGEC are the Ethernet network interfaces
-
-PROPERTIES
-
-- compatible
-		Usage: required
-		Value type: <stringlist>
-		Definition: A standard property.
-		Must include one of the following:
-		- "fsl,fman-dtsec" for dTSEC MAC
-		- "fsl,fman-xgec" for XGEC MAC
-		- "fsl,fman-memac" for mEMAC MAC
-
-- cell-index
-		Usage: required
-		Value type: <u32>
-		Definition: Specifies the MAC id.
-
-		The cell-index value may be used by the FMan or the SoC, to
-		identify the MAC unit in the FMan (or SoC) memory map.
-		In the tables below there's a description of the cell-index
-		use, there are two tables, one describes the use of cell-index
-		by the FMan, the second describes the use by the SoC:
-
-		1. FMan Registers
-
-		FManV2:
-		register[bit]		MAC		cell-index
-		============================================================
-		FM_EPI[16]		XGEC		8
-		FM_EPI[16+n]		dTSECn		n-1
-		FM_NPI[11+n]		dTSECn		n-1
-			n = 1,..,5
-
-		FManV3:
-		register[bit]		MAC		cell-index
-		============================================================
-		FM_EPI[16+n]		mEMACn		n-1
-		FM_EPI[25]		mEMAC10		9
-
-		FM_NPI[11+n]		mEMACn		n-1
-		FM_NPI[10]		mEMAC10		9
-		FM_NPI[11]		mEMAC9		8
-			n = 1,..8
-
-		FM_EPI and FM_NPI are located in the FMan memory map.
-
-		2. SoC registers:
-
-		- P2041, P3041, P4080 P5020, P5040:
-		register[bit]		FMan		MAC		cell
-					Unit				index
-		============================================================
-		DCFG_DEVDISR2[7]	1		XGEC		8
-		DCFG_DEVDISR2[7+n]	1		dTSECn		n-1
-		DCFG_DEVDISR2[15]	2		XGEC		8
-		DCFG_DEVDISR2[15+n]	2		dTSECn		n-1
-			n = 1,..5
-
-		- T1040, T2080, T4240, B4860:
-		register[bit]			FMan	MAC		cell
-						Unit			index
-		============================================================
-		DCFG_CCSR_DEVDISR2[n-1]		1	mEMACn		n-1
-		DCFG_CCSR_DEVDISR2[11+n]	2	mEMACn		n-1
-			n = 1,..6,9,10
-
-		EVDISR, DCFG_DEVDISR2 and DCFG_CCSR_DEVDISR2 are located in
-		the specific SoC "Device Configuration/Pin Control" Memory
-		Map.
-
-- reg
-		Usage: required
-		Value type: <prop-encoded-array>
-		Definition: A standard property.
-
-- fsl,fman-ports
-		Usage: required
-		Value type: <prop-encoded-array>
-		Definition: An array of two phandles - the first references is
-		the FMan RX port and the second is the TX port used by this
-		MAC.
-
-- ptp-timer
-		Usage required
-		Value type: <phandle>
-		Definition: A phandle for 1EEE1588 timer.
-
-- pcsphy-handle
-		Usage required for "fsl,fman-memac" MACs
-		Value type: <phandle>
-		Definition: A phandle for pcsphy.
-
-- tbi-handle
-		Usage required for "fsl,fman-dtsec" MACs
-		Value type: <phandle>
-		Definition: A phandle for tbiphy.
-
-EXAMPLE
-
-fman1_tx28: port@a8000 {
-	cell-index = <0x28>;
-	compatible = "fsl,fman-v2-port-tx";
-	reg = <0xa8000 0x1000>;
-};
-
-fman1_rx8: port@88000 {
-	cell-index = <0x8>;
-	compatible = "fsl,fman-v2-port-rx";
-	reg = <0x88000 0x1000>;
-};
-
-ptp-timer: ptp_timer@fe000 {
-	compatible = "fsl,fman-ptp-timer";
-	reg = <0xfe000 0x1000>;
-};
-
-ethernet@e0000 {
-	compatible = "fsl,fman-dtsec";
-	cell-index = <0>;
-	reg = <0xe0000 0x1000>;
-	fsl,fman-ports = <&fman1_rx8 &fman1_tx28>;
-	ptp-timer = <&ptp-timer>;
-	tbi-handle = <&tbi0>;
-};
+Refer to Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
 
 ============================================================================
 FMan IEEE 1588 Node
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
  2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
  2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
@ 2022-06-28 22:13 ` Sean Anderson
  2022-06-30 16:01   ` Rob Herring
  2022-06-28 22:13 ` [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:13 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Krzysztof Kozlowski, Rob Herring,
	devicetree

At the moment, mEMACs are configured almost completely based on the
phy-connection-type. That is, if the phy interface is RGMII, it assumed
that RGMII is supported. For some interfaces, it is assumed that the
RCW/bootloader has set up the SerDes properly. This is generally OK, but
restricts runtime reconfiguration. The actual link state is never
reported.

To address these shortcomings, the driver will need additional
information. First, it needs to know how to access the PCS/PMAs (in
order to configure them and get the link status). The SGMII PCS/PMA is
the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as
well. The XFI (and 1GBase-KR) PCS/PMA is a c45 "phy" which sits on the
same MDIO bus as SGMII PCS/PMA. By default they will have conflicting
addresses, but they are also not enabled at the same time by default.
Therefore, we can let the XFI PCS/PMA be the default when
phy-connection-type is xgmii. This will allow for
backwards-compatibility.

QSGMII, however, cannot work with the current binding. This is because
the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the
moment this is worked around by having every MAC write to the PCS/PMA
addresses (without checking if they are present). This only works if
each MAC has the same configuration, and only if we don't need to know
the status. Because the QSGMII PCS/PMA will typically be located on a
different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback
for the QSGMII PCS/PMA.

mEMACs (across all SoCs) support the following protocols:

- MII
- RGMII
- SGMII, 1000Base-X, and 1000Base-KX
- 2500Base-X (aka 2.5G SGMII)
- QSGMII
- 10GBase-R (aka XFI) and 10GBase-KR
- XAUI and HiGig

Each line documents a set of orthogonal protocols (e.g. XAUI is
supported if and only if HiGig is supported). Additionally,

- XAUI implies support for 10GBase-R
- 10GBase-R is supported if and only if RGMII is not supported
- 2500Base-X implies support for 1000Base-X
- MII implies support for RGMII

To switch between different protocols, we must reconfigure the SerDes.
This is done by using the standard phys property. We can also use it to
validate whether different protocols are supported (e.g. using
phy_validate). This will work for serial protocols, but not RGMII or
MII. Additionally, we still need to be compatible when there is no
SerDes.

While we can detect 10G support by examining the port speed (as set by
fsl,fman-10g-port), we cannot determine support for any of the other
protocols based on the existing binding. In fact, the binding works
against us in some respects, because pcsphy-handle is required even if
there is no possible PCS/PMA for that MAC. To allow for backwards-
compatibility, we use a boolean-style property for RGMII (instead of
presence/absence-style). When the property for RGMII is missing, we will
assume that it is supported. The exception is MII, since no existing
device trees use it (as far as I could tell).

Unfortunately, QSGMII support will be broken for old device trees. There
is nothing we can do about this because of the PCS/PMA situation (as
described above).

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- Better document how we select which PCS to use in the default case

 .../bindings/net/fsl,fman-dtsec.yaml          | 52 +++++++++++++++++--
 .../devicetree/bindings/net/fsl-fman.txt      |  5 +-
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
index 809df1589f20..ecb772258164 100644
--- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
+++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
@@ -85,9 +85,41 @@ properties:
     $ref: /schemas/types.yaml#/definitions/phandle
     description: A reference to the IEEE1588 timer
 
+  phys:
+    description: A reference to the SerDes lane(s)
+    maxItems: 1
+
+  phy-names:
+    items:
+      - const: serdes
+
   pcsphy-handle:
-    $ref: /schemas/types.yaml#/definitions/phandle
-    description: A reference to the PCS (typically found on the SerDes)
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    minItems: 1
+    maxItems: 3
+    description: |
+      A reference to the various PCSs (typically found on the SerDes). If
+      pcs-names is absent, and phy-connection-type is "xgmii", then the first
+      reference will be assumed to be for "xfi". Otherwise, if pcs-names is
+      absent, then the first reference will be assumed to be for "sgmii".
+
+  pcs-names:
+    $ref: /schemas/types.yaml#/definitions/string-array
+    minItems: 1
+    maxItems: 3
+    contains:
+      enum:
+        - sgmii
+        - qsgmii
+        - xfi
+    description: The type of each PCS in pcsphy-handle.
+
+  rgmii:
+    enum: [0, 1]
+    description: 1 indicates RGMII is supported, and 0 indicates it is not.
+
+  mii:
+    description: If present, indicates that MII is supported.
 
   tbi-handle:
     $ref: /schemas/types.yaml#/definitions/phandle
@@ -100,6 +132,10 @@ required:
   - fsl,fman-ports
   - ptp-timer
 
+dependencies:
+  pcs-names: [pcsphy-handle]
+  mii: [rgmii]
+
 allOf:
   - $ref: "ethernet-controller.yaml#"
   - if:
@@ -117,7 +153,11 @@ allOf:
             const: fsl,fman-memac
     then:
       required:
-        - pcsphy-handle
+        - rgmii
+    else:
+      properties:
+        rgmii: false
+        mii: false
 
 unevaluatedProperties: false
 
@@ -138,7 +178,11 @@ examples:
             reg = <0xe8000 0x1000>;
             fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>;
             ptp-timer = <&ptp_timer0>;
-            pcsphy-handle = <&pcsphy4>;
+            pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs1>;
+            pcs-names = "sgmii", "qsgmii";
+            rgmii = <0>;
             phy-handle = <&sgmii_phy1>;
             phy-connection-type = "sgmii";
+            phys = <&serdes1 1>;
+            phy-names = "serdes";
     };
diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt
index b9055335db3b..bda4b41af074 100644
--- a/Documentation/devicetree/bindings/net/fsl-fman.txt
+++ b/Documentation/devicetree/bindings/net/fsl-fman.txt
@@ -320,8 +320,9 @@ For internal PHY device on internal mdio bus, a PHY node should be created.
 See the definition of the PHY node in booting-without-of.txt for an
 example of how to define a PHY (Internal PHY has no interrupt line).
 - For "fsl,fman-mdio" compatible internal mdio bus, the PHY is TBI PHY.
-- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY,
-  PCS PHY addr must be '0'.
+- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY.
+  The PCS PHY address should correspond to the value of the appropriate
+  MDEV_PORT.
 
 EXAMPLE
 
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
                   ` (2 preceding siblings ...)
  2022-06-28 22:13 ` [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties Sean Anderson
@ 2022-06-28 22:13 ` Sean Anderson
  2022-06-30 15:56   ` Ioana Ciornei
  2022-07-05  6:12   ` Vinod Koul
  2022-06-28 22:14 ` [PATCH net-next v2 32/35] qoriq: Specify which MACs support RGMII Sean Anderson
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:13 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Ioana Ciornei, Jonathan Corbet,
	Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring,
	Vinod Koul, devicetree, linux-doc, linux-phy

This adds support for the Lynx 10G "SerDes" devices found on various NXP
QorIQ SoCs. There may be up to four SerDes devices on each SoC, each
supporting up to eight lanes. Protocol support for each SerDes is highly
heterogeneous, with each SoC typically having a totally different
selection of supported protocols for each lane. Additionally, the SerDes
devices on each SoC also have differing support. One SerDes will
typically support Ethernet on most lanes, while the other will typically
support PCIe on most lanes.

There is wide hardware support for this SerDes. I have not done extensive
digging, but it seems to be used on almost every QorIQ device, including
the AMP and Layerscape series. Because each SoC typically has specific
instructions and exceptions for its SerDes, I have limited the initial
scope of this module to just the LS1046A. Additionally, I have only added
support for Ethernet protocols. There is not a great need for dynamic
reconfiguration for other protocols (SATA and PCIe handle rate changes in
hardware), so support for them may never be added.

Nevertheless, I have tried to provide an obvious path for adding support
for other SoCs as well as other protocols. SATA just needs support for
configuring LNmSSCR0. PCIe may need to configure the equalization
registers. It also uses multiple lanes. I have tried to write the driver
with multi-lane support in mind, so there should not need to be any large
changes. Although there are 6 protocols supported, I have only tested SGMII
and XFI. The rest have been implemented as described in the datasheet.

The PLLs are modeled as clocks proper. This lets us take advantage of the
existing clock infrastructure. I have not given the same treatment to the
lane "clocks" (dividers) because they need to be programmed in-concert with
the rest of the lane settings. One tricky thing is that the VCO (pll) rate
exceeds 2^32 (maxing out at around 5GHz). This will be a problem on 32-bit
platforms, since clock rates are stored as unsigned longs. To work around
this, the pll clock rate is generally treated in units of kHz.

The PLLs are configured rather interestingly. Instead of the usual direct
programming of the appropriate divisors, the input and output clock rates
are selected directly. Generally, the only restriction is that the input
and output must be integer multiples of each other. This suggests some kind
of internal look-up table. The datasheets generally list out the supported
combinations explicitly, and not all input/output combinations are
documented. I'm not sure if this is due to lack of support, or due to an
oversight. If this becomes an issue, then some combinations can be
blacklisted (or whitelisted). This may also be necessary for other SoCs
which have more stringent clock requirements.

The general API call list for this PHY is documented under the driver-api
docs. I think this is rather standard, except that most driverts configure
the mode (protocol) at xlate-time. Unlike some other phys where e.g. PCIe
x4 will use 4 separate phys all configured for PCIe, this driver uses one
phy configured to use 4 lanes. This is because while the individual lanes
may be configured individually, the protocol selection acts on all lanes at
once. Additionally, the order which lanes should be configured in is
specified by the datasheet.  To coordinate this, lanes are reserved in
phy_init, and released in phy_exit.

When getting a phy, if a phy already exists for those lanes, it is reused.
This is to make things like QSGMII work. Four MACs will all want to ensure
that the lane is configured properly, and we need to ensure they can all
call phy_init, etc. There is refcounting for phy_init and phy_power_on, so
the phy will only be powered on once. However, there is no refcounting for
phy_set_mode. A "rogue" MAC could set the mode to something non-QSGMII and
break the other MACs. Perhaps there is an opportunity for future
enhancement here.

This driver was written with reference to the LS1046A reference manual.
However, it was informed by reference manuals for all processors with
mEMACs, especially the T4240 (which appears to have a "maxed-out"
configuration).

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
As noted later on in this series (in the phylink conversion patch), this
driver does not quite function properly. When the bootloader is
instructed to not configure the SerDes, only one lane comes up.

Changes in v2:
- Clear SGMIIaCR1_PCS_EN during probe
- Fix not clearing group->pll after disabling it
- Handle 1000Base-KX in lynx_proto_mode_prep
- Power off lanes during probe
- Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
- Rename driver to Lynx 10G (etc.)
- Support 1 and 2 phy-cells

 Documentation/driver-api/phy/index.rst   |    1 +
 Documentation/driver-api/phy/qoriq.rst   |   93 ++
 MAINTAINERS                              |    6 +
 drivers/phy/freescale/Kconfig            |   19 +
 drivers/phy/freescale/Makefile           |    1 +
 drivers/phy/freescale/phy-fsl-lynx-10g.c | 1483 ++++++++++++++++++++++
 6 files changed, 1603 insertions(+)
 create mode 100644 Documentation/driver-api/phy/qoriq.rst
 create mode 100644 drivers/phy/freescale/phy-fsl-lynx-10g.c

diff --git a/Documentation/driver-api/phy/index.rst b/Documentation/driver-api/phy/index.rst
index 69ba1216de72..cc7ded8b969c 100644
--- a/Documentation/driver-api/phy/index.rst
+++ b/Documentation/driver-api/phy/index.rst
@@ -7,6 +7,7 @@ Generic PHY Framework
 .. toctree::
 
    phy
+   qoriq
    samsung-usb2
 
 .. only::  subproject and html
diff --git a/Documentation/driver-api/phy/qoriq.rst b/Documentation/driver-api/phy/qoriq.rst
new file mode 100644
index 000000000000..cbc2ac9ca4aa
--- /dev/null
+++ b/Documentation/driver-api/phy/qoriq.rst
@@ -0,0 +1,93 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================
+QorIQ SerDes (Lynx 10G)
+=======================
+
+Using this phy
+--------------
+
+The general order of calls should be::
+
+    [devm_][of_]phy_get()
+    phy_init()
+    phy_power_on()
+    phy_set_mode[_ext]()
+    ...
+    phy_power_off()
+    phy_exit()
+    [[of_]phy_put()]
+
+:c:func:`phy_get` just gets (or creates) a new :c:type:`phy` with the lanes
+described in the phandle. :c:func:`phy_init` is what actually reserves the
+lanes for use. Unlike some other drivers, when the phy is created, there is no
+default protocol. :c:func:`phy_set_mode <phy_set_mode_ext>` must be called in
+order to set the protocol.
+
+Supporting SoCs
+---------------
+
+Each new SoC needs a :c:type:`struct lynx_conf <lynx_conf>` for each SerDes.
+The most important member is `modes`, which is an array of :c:type:`struct
+lynx_mode <lynx_mode>`. Each "mode" represents a configuration which can be
+programmed into a protocol control register. Modes can support multiple lanes
+(such for PCIe x2 or x4), as well as multiple protocols (such as SGMII and
+1000Base-KX). There are several helper macros to make configuring each mode
+easier. It is important that the list of modes is complete, even if not all
+protocols are supported. This lets the driver know which lanes are available,
+and which have been configured by the RCW.
+
+If a protocol is missing, add it to :c:type:`enum lynx_protocol
+<lynx_protocol>`, and to ``UNSUPPORTED_PROTOS``. If the PCCR shifts/masks for
+your protocol are missing, you will need to add them to
+:c:func:`lynx_proto_mode_mask` and :c:func:`lynx_proto_mode_shift`.
+
+For example, the configuration for SerDes1 of the LS1046A is::
+
+    static const struct lynx_mode ls1046a_modes1[] = {
+        CONF_SINGLE(1, PCIE, 0x0, 1, 0b001),
+        CONF_1000BASEKX(0, 0x8, 0, 0b001),
+        CONF_SGMII25KX(1, 0x8, 1, 0b001),
+        CONF_SGMII25KX(2, 0x8, 2, 0b001),
+        CONF_SGMII25KX(3, 0x8, 3, 0b001),
+        CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001),
+        CONF_XFI(2, 0xB, 0, 0b010),
+        CONF_XFI(3, 0xB, 1, 0b001),
+    };
+
+    static const struct lynx_conf ls1046a_conf1 = {
+        .modes = ls1046a_modes1,
+        .mode_count = ARRAY_SIZE(ls1046a_modes1),
+        .lanes = 4,
+        .endian = REGMAP_ENDIAN_BIG,
+    };
+
+There is an additional set of configuration for SerDes2, which supports a
+different set of modes. Both configurations should be added to the match
+table::
+
+    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
+    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
+
+Supporting Protocols
+--------------------
+
+Each protocol is a combination of values which must be programmed into the lane
+registers. To add a new protocol, first add it to :c:type:`enum lynx_protocol
+<lynx_protocol>`. If it is in ``UNSUPPORTED_PROTOS``, remove it. Add a new
+entry to `lynx_proto_params`, and populate the appropriate fields. You may need
+to add some new members to support new fields. Modify `lynx_lookup_proto` to
+map the :c:type:`enum phy_mode <phy_mode>` to :c:type:`enum lynx_protocol
+<lynx_protocol>`. Ensure that :c:func:`lynx_proto_mode_mask` and
+:c:func:`lynx_proto_mode_shift` have been updated with support for your
+protocol.
+
+You may need to modify :c:func:`lynx_set_mode` in order to support your
+procotol. This can happen when you have added members to :c:type:`struct
+lynx_proto_params <lynx_proto_params>`. It can also happen if you have specific
+clocking requirements, or protocol-specific registers to program.
+
+Internal API Reference
+----------------------
+
+.. kernel-doc:: drivers/phy/freescale/phy-fsl-lynx-10g.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ca95b1833b97..ef65e2acdb48 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7977,6 +7977,12 @@ F:	drivers/ptp/ptp_qoriq.c
 F:	drivers/ptp/ptp_qoriq_debugfs.c
 F:	include/linux/fsl/ptp_qoriq.h
 
+FREESCALE QORIQ SERDES DRIVER
+M:	Sean Anderson <sean.anderson@seco.com>
+S:	Maintained
+F:	Documentation/driver-api/phy/qoriq.rst
+F:	drivers/phy/freescale/phy-qoriq.c
+
 FREESCALE QUAD SPI DRIVER
 M:	Han Xu <han.xu@nxp.com>
 L:	linux-spi@vger.kernel.org
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index f9c54cd02036..857b4d123515 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -38,3 +38,22 @@ config PHY_FSL_LYNX_28G
 	  found on NXP's Layerscape platforms such as LX2160A.
 	  Used to change the protocol running on SerDes lanes at runtime.
 	  Only useful for a restricted set of Ethernet protocols.
+
+config PHY_FSL_LYNX_10G
+	tristate "Freescale Layerscale Lynx 10G SerDes support"
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  This adds support for the Lynx "SerDes" devices found on various QorIQ
+	  SoCs. There may be up to four SerDes devices on each SoC, and each
+	  device supports up to eight lanes. The SerDes is configured by default
+	  by the RCW, but this module is necessary in order to support dynamic
+	  reconfiguration (such as to support 1G and 10G ethernet on the same
+	  interface). The hardware supports a variety of protocols, including
+	  Ethernet, SATA, PCIe, and more exotic links such as Interlaken and
+	  Aurora. This driver only supports Ethernet, but it will try not to
+	  touch lanes configured for other protocols.
+
+	  If you have a QorIQ processor and want to dynamically reconfigure your
+	  SerDes, say Y. If this driver is compiled as a module, it will be
+	  named phy-qoriq.
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
index 3518d5dbe8a7..aa4374ed217c 100644
--- a/drivers/phy/freescale/Makefile
+++ b/drivers/phy/freescale/Makefile
@@ -2,4 +2,5 @@
 obj-$(CONFIG_PHY_FSL_IMX8MQ_USB)	+= phy-fsl-imx8mq-usb.o
 obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY)	+= phy-fsl-imx8-mipi-dphy.o
 obj-$(CONFIG_PHY_FSL_IMX8M_PCIE)	+= phy-fsl-imx8m-pcie.o
+obj-$(CONFIG_PHY_FSL_LYNX_10G)		+= phy-fsl-lynx-10g.o
 obj-$(CONFIG_PHY_FSL_LYNX_28G)		+= phy-fsl-lynx-28g.o
diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c
new file mode 100644
index 000000000000..480bd493fbc2
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
@@ -0,0 +1,1483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/math64.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#define PLL_STRIDE	0x20
+#define PLLa(a, off)	((a) * PLL_STRIDE + (off))
+#define PLLaRSTCTL(a)	PLLa(a, 0x00)
+#define PLLaCR0(a)	PLLa(a, 0x04)
+
+#define PLLaRSTCTL_RSTREQ	BIT(31)
+#define PLLaRSTCTL_RST_DONE	BIT(30)
+#define PLLaRSTCTL_RST_ERR	BIT(29)
+#define PLLaRSTCTL_PLLRST_B	BIT(7)
+#define PLLaRSTCTL_SDRST_B	BIT(6)
+#define PLLaRSTCTL_SDEN		BIT(5)
+
+#define PLLaCR0_POFF		BIT(31)
+#define PLLaCR0_RFCLK_SEL	GENMASK(30, 28)
+#define PLLaCR0_PLL_LCK		BIT(23)
+#define PLLaCR0_FRATE_SEL	GENMASK(19, 16)
+#define PLLaCR0_DLYDIV_SEL	GENMASK(1, 0)
+
+#define PCCR_BASE	0x200
+#define PCCR_STRIDE	0x4
+#define PCCRn(n)	(PCCR_BASE + n * PCCR_STRIDE)
+
+#define PCCR0_PEXa_MASK		GENMASK(2, 0)
+#define PCCR0_PEXa_SHIFT(a)	(28 - (a) * 4)
+
+#define PCCR2_SATAa_MASK	GENMASK(2, 0)
+#define PCCR2_SATAa_SHIFT(a)	(28 - (a) * 4)
+
+#define PCCR8_SGMIIa_KX		BIT(3)
+#define PCCR8_SGMIIa_MASK	GENMASK(3, 0)
+#define PCCR8_SGMIIa_SHIFT(a)	(28 - (a) * 4)
+
+#define PCCR9_QSGMIIa_MASK	GENMASK(2, 0)
+#define PCCR9_QSGMIIa_SHIFT(a)	(28 - (a) * 4)
+
+#define PCCRB_XFIa_MASK		GENMASK(2, 0)
+#define PCCRB_XFIa_SHIFT(a)	(28 - (a) * 4)
+
+#define LANE_BASE	0x800
+#define LANE_STRIDE	0x40
+#define LNm(m, off)	(LANE_BASE + (m) * LANE_STRIDE + (off))
+#define LNmGCR0(m)	LNm(m, 0x00)
+#define LNmGCR1(m)	LNm(m, 0x04)
+#define LNmSSCR0(m)	LNm(m, 0x0C)
+#define LNmRECR0(m)	LNm(m, 0x10)
+#define LNmRECR1(m)	LNm(m, 0x14)
+#define LNmTECR0(m)	LNm(m, 0x18)
+#define LNmSSCR1(m)	LNm(m, 0x1C)
+#define LNmTTLCR0(m)	LNm(m, 0x20)
+
+#define LNmGCR0_RPLL_LES	BIT(31)
+#define LNmGCR0_RRAT_SEL	GENMASK(29, 28)
+#define LNmGCR0_TPLL_LES	BIT(27)
+#define LNmGCR0_TRAT_SEL	GENMASK(25, 24)
+#define LNmGCR0_RRST_B		BIT(22)
+#define LNmGCR0_TRST_B		BIT(21)
+#define LNmGCR0_RX_PD		BIT(20)
+#define LNmGCR0_TX_PD		BIT(19)
+#define LNmGCR0_IF20BIT_EN	BIT(18)
+#define LNmGCR0_FIRST_LANE	BIT(16)
+#define LNmGCR0_TTRM_VM_SEL	GENMASK(13, 12)
+#define LNmGCR0_PROTS		GENMASK(11, 7)
+
+#define LNmGCR0_RAT_SEL_SAME		0b00
+#define LNmGCR0_RAT_SEL_HALF		0b01
+#define LNmGCR0_RAT_SEL_QUARTER		0b10
+#define LNmGCR0_RAT_SEL_DOUBLE		0b11
+
+#define LNmGCR0_PROTS_PCIE		0b00000
+#define LNmGCR0_PROTS_SGMII		0b00001
+#define LNmGCR0_PROTS_SATA		0b00010
+#define LNmGCR0_PROTS_XFI		0b01010
+
+#define LNmGCR1_RDAT_INV	BIT(31)
+#define LNmGCR1_TDAT_INV	BIT(30)
+#define LNmGCR1_OPAD_CTL	BIT(26)
+#define LNmGCR1_REIDL_TH	GENMASK(22, 20)
+#define LNmGCR1_REIDL_EX_SEL	GENMASK(19, 18)
+#define LNmGCR1_REIDL_ET_SEL	GENMASK(17, 16)
+#define LNmGCR1_REIDL_EX_MSB	BIT(15)
+#define LNmGCR1_REIDL_ET_MSB	BIT(14)
+#define LNmGCR1_REQ_CTL_SNP	BIT(13)
+#define LNmGCR1_REQ_CDR_SNP	BIT(12)
+#define LNmGCR1_TRSTDIR		BIT(7)
+#define LNmGCR1_REQ_BIN_SNP	BIT(6)
+#define LNmGCR1_ISLEW_RCTL	GENMASK(5, 4)
+#define LNmGCR1_OSLEW_RCTL	GENMASK(1, 0)
+
+#define LNmRECR0_GK2OVD		GENMASK(27, 24)
+#define LNmRECR0_GK3OVD		GENMASK(19, 16)
+#define LNmRECR0_GK2OVD_EN	BIT(15)
+#define LNmRECR0_GK3OVD_EN	BIT(16)
+#define LNmRECR0_BASE_WAND	GENMASK(11, 10)
+#define LNmRECR0_OSETOVD	GENMASK(5, 0)
+
+#define LNmRECR0_BASE_WAND_OFF		0b00
+#define LNmRECR0_BASE_WAND_DEFAULT	0b01
+#define LNmRECR0_BASE_WAND_ALTERNATE	0b10
+#define LNmRECR0_BASE_WAND_OSETOVD	0b11
+
+#define LNmTECR0_TEQ_TYPE	GENMASK(29, 28)
+#define LNmTECR0_SGN_PREQ	BIT(26)
+#define LNmTECR0_RATIO_PREQ	GENMASK(25, 22)
+#define LNmTECR0_SGN_POST1Q	BIT(21)
+#define LNmTECR0_RATIO_PST1Q	GENMASK(20, 16)
+#define LNmTECR0_ADPT_EQ	GENMASK(13, 8)
+#define LNmTECR0_AMP_RED	GENMASK(5, 0)
+
+#define LNmTECR0_TEQ_TYPE_NONE		0b00
+#define LNmTECR0_TEQ_TYPE_PRE		0b01
+#define LNmTECR0_TEQ_TYPE_BOTH		0b10
+
+#define LNmTTLCR0_FLT_SEL	GENMASK(29, 24)
+
+#define PCS_STRIDE	0x10
+#define CR_STRIDE	0x4
+#define PCSa(a, base, cr)	(base + (a) * PCS_STRIDE + (cr) * CR_STRIDE)
+
+#define PCSaCR1_MDEV_PORT	GENMASK(31, 27)
+
+#define SGMII_BASE	0x1800
+#define SGMIIaCR1(a)	PCSa(a, SGMII_BASE, 1)
+
+#define SGMIIaCR1_SGPCS_EN	BIT(11)
+
+#define QSGMII_OFFSET	0x1880
+#define QSGMIIaCR1(a)	PCSa(a, QSGMII_BASE, 1)
+
+#define XFI_OFFSET	0x1980
+#define XFIaCR1(a)	PCSa(a, XFI_BASE, 1)
+
+/* The maximum number of lanes in a single serdes */
+#define MAX_LANES	8
+
+enum lynx_protocol {
+	LYNX_PROTO_NONE = 0,
+	LYNX_PROTO_SGMII,
+	LYNX_PROTO_SGMII25,
+	LYNX_PROTO_1000BASEKX,
+	LYNX_PROTO_QSGMII,
+	LYNX_PROTO_XFI,
+	LYNX_PROTO_10GKR,
+	LYNX_PROTO_PCIE, /* Not implemented */
+	LYNX_PROTO_SATA, /* Not implemented */
+	LYNX_PROTO_LAST,
+};
+
+static const char lynx_proto_str[][16] = {
+	[LYNX_PROTO_NONE] = "unknown",
+	[LYNX_PROTO_SGMII] = "SGMII",
+	[LYNX_PROTO_SGMII25] = "2.5G SGMII",
+	[LYNX_PROTO_1000BASEKX] = "1000Base-KX",
+	[LYNX_PROTO_QSGMII] = "QSGMII",
+	[LYNX_PROTO_XFI] = "XFI",
+	[LYNX_PROTO_10GKR] = "10GBase-KR",
+	[LYNX_PROTO_PCIE] = "PCIe",
+	[LYNX_PROTO_SATA] = "SATA",
+};
+
+#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
+#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
+
+/**
+ * struct lynx_proto_params - Parameters for configuring a protocol
+ * @frate_khz: The PLL rate, in kHz
+ * @rat_sel: The divider to get the line rate
+ * @if20bit: Whether the proto is 20 bits or 10 bits
+ * @prots: Lane protocol select
+ * @reidl_th: Receiver electrical idle detection threshold
+ * @reidl_ex: Exit electrical idle filter
+ * @reidl_et: Enter idle filter
+ * @slew: Slew control
+ * @baseline_wander: Enable baseline wander correction
+ * @gain: Adaptive equalization gain override
+ * @offset_override: Adaptive equalization offset override
+ * @teq: Transmit equalization type (none, precursor, or precursor and
+ *       postcursor). The next few values are only used for appropriate
+ *       equalization types.
+ * @preq_ratio: Ratio of full swing transition bit to pre-cursor
+ * @postq_ratio: Ratio of full swing transition bit to first post-cursor.
+ * @adpt_eq: Transmitter Adjustments for 8G/10G
+ * @amp_red: Overall TX Amplitude Reduction
+ * @flt_sel: TTL configuration selector
+ */
+struct lynx_proto_params {
+	u32 frate_khz;
+	u8 rat_sel;
+	u8 prots;
+	u8 reidl_th;
+	u8 reidl_ex;
+	u8 reidl_et;
+	u8 slew;
+	u8 gain;
+	u8 baseline_wander;
+	u8 offset_override;
+	u8 teq;
+	u8 preq_ratio;
+	u8 postq_ratio;
+	u8 adpt_eq;
+	u8 amp_red;
+	u8 flt_sel;
+	bool if20bit;
+};
+
+static const struct lynx_proto_params lynx_proto_params[] = {
+	[LYNX_PROTO_SGMII] = {
+		.frate_khz = 5000000,
+		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
+		.if20bit = false,
+		.prots = LNmGCR0_PROTS_SGMII,
+		.reidl_th = 0b001,
+		.reidl_ex = 0b011,
+		.reidl_et = 0b100,
+		.slew = 0b01,
+		.gain = 0b1111,
+		.offset_override = 0b0011111,
+		.teq = LNmTECR0_TEQ_TYPE_NONE,
+		.adpt_eq = 0b110000,
+		.amp_red = 0b000110,
+		.flt_sel = 0b111001,
+	},
+	[LYNX_PROTO_1000BASEKX] = {
+		.frate_khz = 5000000,
+		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
+		.if20bit = false,
+		.prots = LNmGCR0_PROTS_SGMII,
+		.slew = 0b01,
+		.gain = 0b1111,
+		.offset_override = 0b0011111,
+		.teq = LNmTECR0_TEQ_TYPE_NONE,
+		.adpt_eq = 0b110000,
+		.flt_sel = 0b111001,
+	},
+	[LYNX_PROTO_SGMII25] = {
+		.frate_khz = 3125000,
+		.rat_sel = LNmGCR0_RAT_SEL_SAME,
+		.if20bit = false,
+		.prots = LNmGCR0_PROTS_SGMII,
+		.slew = 0b10,
+		.offset_override = 0b0011111,
+		.teq = LNmTECR0_TEQ_TYPE_PRE,
+		.postq_ratio = 0b00110,
+		.adpt_eq = 0b110000,
+	},
+	[LYNX_PROTO_QSGMII] = {
+		.frate_khz = 5000000,
+		.rat_sel = LNmGCR0_RAT_SEL_SAME,
+		.if20bit = true,
+		.prots = LNmGCR0_PROTS_SGMII,
+		.slew = 0b01,
+		.offset_override = 0b0011111,
+		.teq = LNmTECR0_TEQ_TYPE_PRE,
+		.postq_ratio = 0b00110,
+		.adpt_eq = 0b110000,
+		.amp_red = 0b000010,
+	},
+	[LYNX_PROTO_XFI] = {
+		.frate_khz = 5156250,
+		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
+		.if20bit = true,
+		.prots = LNmGCR0_PROTS_XFI,
+		.slew = 0b01,
+		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
+		.offset_override = 0b1011111,
+		.teq = LNmTECR0_TEQ_TYPE_PRE,
+		.postq_ratio = 0b00011,
+		.adpt_eq = 0b110000,
+		.amp_red = 0b000111,
+	},
+	[LYNX_PROTO_10GKR] = {
+		.frate_khz = 5156250,
+		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
+		.prots = LNmGCR0_PROTS_XFI,
+		.slew = 0b01,
+		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
+		.offset_override = 0b1011111,
+		.teq = LNmTECR0_TEQ_TYPE_BOTH,
+		.preq_ratio = 0b0011,
+		.postq_ratio = 0b01100,
+		.adpt_eq = 0b110000,
+	},
+};
+
+/**
+ * struct lynx_mode - A single configuration of a protocol controller
+ * @protos: A bitmask of the &enum lynx_protocol this mode supports
+ * @lanes: A bitmask of the lanes which will be used when this config is
+ *         selected
+ * @pccr: The number of the PCCR which contains this mode
+ * @idx: The index of the protocol controller. For example, SGMIIB would have
+ *       index 1.
+ * @cfg: The value to program into the controller to select this mode
+ *
+ * The serdes has multiple protocol controllers which can be each be selected
+ * independently. Depending on their configuration, they may use multiple lanes
+ * at once (e.g. AUI or PCIe x4). Additionally, multiple protocols may be
+ * supported by a single mode (XFI and 10GKR differ only in their protocol
+ * parameters).
+ */
+struct lynx_mode {
+	u16 protos;
+	u8 lanes;
+	u8 pccr;
+	u8 idx;
+	u8 cfg;
+};
+
+static_assert(LYNX_PROTO_LAST - 1 <=
+	      sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
+static_assert(MAX_LANES <=
+	      sizeof_field(struct lynx_mode, lanes) * BITS_PER_BYTE);
+
+#define CONF(_lanes, _protos, _pccr, _idx, _cfg) { \
+	.lanes = _lanes, \
+	.protos = _protos, \
+	.pccr = _pccr, \
+	.idx = _idx, \
+	.cfg = _cfg, \
+}
+
+#define CONF_SINGLE(lane, proto, pccr, idx, cfg) \
+	CONF(BIT(lane), PROTO_MASK(proto), pccr, idx, cfg)
+
+#define CONF_1000BASEKX(lane, pccr, idx, cfg) \
+	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX), \
+	     pccr, idx, cfg)
+
+#define CONF_SGMII25(lane, pccr, idx, cfg) \
+	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(SGMII25), \
+	     pccr, idx, cfg)
+
+#define CONF_SGMII25KX(lane, pccr, idx, cfg) \
+	CONF(BIT(lane), \
+	     PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX) | PROTO_MASK(SGMII25), \
+	     pccr, idx, cfg)
+
+#define CONF_XFI(lane, pccr, idx, cfg) \
+	CONF(BIT(lane), PROTO_MASK(XFI) | PROTO_MASK(10GKR), pccr, idx, cfg)
+
+/**
+ * struct lynx_conf - Configuration for a particular serdes
+ * @modes: Valid protocol controller configurations
+ * @mode_count: Number of modes in @modes
+ * @lanes: Number of lanes
+ * @endian: Endianness of the registers
+ */
+struct lynx_conf {
+	const struct lynx_mode *modes;
+	size_t mode_count;
+	unsigned int lanes;
+	enum regmap_endian endian;
+};
+
+struct lynx_priv;
+
+/**
+ * struct lynx_clk - Driver data for the PLLs
+ * @hw: The clock hardware
+ * @serdes: The parent serdes
+ * @idx: Which PLL this clock is for
+ */
+struct lynx_clk {
+	struct clk_hw hw;
+	struct lynx_priv *serdes;
+	unsigned int idx;
+};
+
+static struct lynx_clk *lynx_clk_hw_to_priv(struct clk_hw *hw)
+{
+	return container_of(hw, struct lynx_clk, hw);
+}
+
+/**
+ * struct lynx_priv - Driver data for the serdes
+ * @lock: A lock protecting "common" registers in @regmap, as well as the
+ *        members of this struct. Lane-specific registers are protected by the
+ *        phy's lock. PLL registers are protected by the clock's lock.
+ * @pll: The PLL clocks
+ * @ref: The reference clocks for the PLLs
+ * @dev: The serdes device
+ * @regmap: The backing regmap
+ * @conf: The configuration for this serdes
+ * @used_lanes: Bitmap of the lanes currently used by phys
+ * @groups: List of the created groups
+ */
+struct lynx_priv {
+	struct mutex lock;
+	struct lynx_clk pll[2];
+	struct clk *ref[2];
+	struct device *dev;
+	struct regmap *regmap;
+	const struct lynx_conf *conf;
+	unsigned int used_lanes;
+	struct list_head groups;
+};
+
+/**
+ * struct lynx_group - Driver data for a group of lanes
+ * @groups: List of other groups; protected by @serdes->lock.
+ * @phy: The associated phy
+ * @serdes: The parent serdes
+ * @pll: The currently-used pll
+ * @first_lane: The first lane in the group
+ * @last_lane: The last lane in the group
+ * @proto: The currently-configured protocol
+ * @users: Number of current users; protected by @serdes->lock.
+ */
+struct lynx_group {
+	struct list_head groups;
+	struct phy *phy;
+	struct lynx_priv *serdes;
+	struct clk *pll;
+	unsigned int first_lane;
+	unsigned int last_lane;
+	enum lynx_protocol proto;
+	unsigned int users;
+};
+
+static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
+{
+	unsigned int ret = 0;
+
+	WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
+	return ret;
+}
+
+static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
+{
+	WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
+}
+
+/* XXX: The output rate is in kHz to avoid overflow on 32-bit arches */
+
+static void lynx_pll_disable(struct clk_hw *hw)
+{
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	struct lynx_priv *serdes = clk->serdes;
+	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
+
+	rstctl &= ~PLLaRSTCTL_SDRST_B;
+	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
+	ndelay(50);
+	rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
+	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
+	ndelay(100);
+}
+
+static int lynx_pll_enable(struct clk_hw *hw)
+{
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	struct lynx_priv *serdes = clk->serdes;
+	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
+
+	rstctl |= PLLaRSTCTL_RSTREQ;
+	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
+
+	rstctl &= ~PLLaRSTCTL_RSTREQ;
+	rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
+	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
+
+	/* TODO: wait for the PLL to lock */
+
+	return 0;
+}
+
+static int lynx_pll_is_enabled(struct clk_hw *hw)
+{
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	struct lynx_priv *serdes = clk->serdes;
+	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
+
+	return rstctl & PLLaRSTCTL_RST_DONE && !(rstctl & PLLaRSTCTL_RST_ERR);
+}
+
+static const u32 rfclk_sel_map[8] = {
+	[0b000] = 100000000,
+	[0b001] = 125000000,
+	[0b010] = 156250000,
+	[0b011] = 150000000,
+};
+
+/**
+ * lynx_rfclk_to_sel() - Convert a reference clock rate to a selector
+ * @rate: The reference clock rate
+ *
+ * To allow for some variation in the reference clock rate, up to 100ppm of
+ * error is allowed.
+ *
+ * Return: An appropriate selector for @rate, or -%EINVAL.
+ */
+static int lynx_rfclk_to_sel(u32 rate)
+{
+	int ret;
+
+	for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
+		u32 rfclk_rate = rfclk_sel_map[ret];
+		/* Allow an error of 100ppm */
+		u32 error = rfclk_rate / 10000;
+
+		if (rate > rfclk_rate - error && rate < rfclk_rate + error)
+			return ret;
+	}
+
+	return -EINVAL;
+}
+
+static const u32 frate_sel_map[16] = {
+	[0b0000] = 5000000,
+	[0b0101] = 3750000,
+	[0b0110] = 5156250,
+	[0b0111] = 4000000,
+	[0b1001] = 3125000,
+	[0b1010] = 3000000,
+};
+
+/**
+ * lynx_frate_to_sel() - Convert a VCO clock rate to a selector
+ * @rate_khz: The VCO frequency, in kHz
+ *
+ * Return: An appropriate selector for @rate_khz, or -%EINVAL.
+ */
+static int lynx_frate_to_sel(u32 rate_khz)
+{
+	int ret;
+
+	for (ret = 0; ret < ARRAY_SIZE(frate_sel_map); ret++)
+		if (frate_sel_map[ret] == rate_khz)
+			return ret;
+
+	return -EINVAL;
+}
+
+static u32 lynx_pll_ratio(u32 frate_sel, u32 rfclk_sel)
+{
+	u64 frate;
+	u32 rfclk, error, ratio;
+
+	frate = frate_sel_map[frate_sel] * (u64)HZ_PER_KHZ;
+	rfclk = rfclk_sel_map[rfclk_sel];
+
+	if (!frate || !rfclk)
+		return 0;
+
+	ratio = div_u64_rem(frate, rfclk, &error);
+	if (!error)
+		return ratio;
+	return 0;
+}
+
+static unsigned long lynx_pll_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	struct lynx_priv *serdes = clk->serdes;
+	u32 cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
+	u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
+	u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
+	unsigned long ret;
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu)\n", __func__,
+		clk->idx, parent_rate);
+
+	ret = mult_frac(parent_rate, lynx_pll_ratio(frate_sel, rfclk_sel),
+			 HZ_PER_KHZ);
+	return ret;
+}
+
+static long lynx_pll_round_rate(struct clk_hw *hw, unsigned long rate_khz,
+			      unsigned long *parent_rate)
+{
+	int frate_sel, rfclk_sel;
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	u32 ratio;
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
+		clk->idx, rate_khz, *parent_rate);
+
+	frate_sel = lynx_frate_to_sel(rate_khz);
+	if (frate_sel < 0)
+		return frate_sel;
+
+	rfclk_sel = lynx_rfclk_to_sel(*parent_rate);
+	if (rfclk_sel >= 0) {
+		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+		if (ratio)
+			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
+	}
+
+	for (rfclk_sel = 0;
+	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
+	     rfclk_sel++) {
+		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+		if (ratio) {
+			*parent_rate = rfclk_sel_map[rfclk_sel];
+			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int lynx_pll_set_rate(struct clk_hw *hw, unsigned long rate_khz,
+			   unsigned long parent_rate)
+{
+	int frate_sel, rfclk_sel, ret;
+	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
+	struct lynx_priv *serdes = clk->serdes;
+	u32 ratio, cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
+
+	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
+		clk->idx, rate_khz, parent_rate);
+
+	frate_sel = lynx_frate_to_sel(rate_khz);
+	if (frate_sel < 0)
+		return frate_sel;
+
+	/* First try the existing rate */
+	rfclk_sel = lynx_rfclk_to_sel(parent_rate);
+	if (rfclk_sel >= 0) {
+		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+		if (ratio)
+			goto got_rfclk;
+	}
+
+	for (rfclk_sel = 0;
+	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
+	     rfclk_sel++) {
+		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+		if (ratio) {
+			ret = clk_set_rate(serdes->ref[clk->idx],
+					   rfclk_sel_map[rfclk_sel]);
+			if (!ret)
+				goto got_rfclk;
+		}
+	}
+
+	return ret;
+
+got_rfclk:
+	cr0 &= ~(PLLaCR0_RFCLK_SEL | PLLaCR0_FRATE_SEL);
+	cr0 |= FIELD_PREP(PLLaCR0_RFCLK_SEL, rfclk_sel);
+	cr0 |= FIELD_PREP(PLLaCR0_FRATE_SEL, frate_sel);
+	lynx_write(serdes, cr0, PLLaCR0(clk->idx));
+	return 0;
+}
+
+static const struct clk_ops lynx_pll_clk_ops = {
+	.enable = lynx_pll_enable,
+	.disable = lynx_pll_disable,
+	.is_enabled = lynx_pll_is_enabled,
+	.recalc_rate = lynx_pll_recalc_rate,
+	.round_rate = lynx_pll_round_rate,
+	.set_rate = lynx_pll_set_rate,
+};
+
+static struct clk_hw *lynx_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+	struct lynx_priv *serdes = data;
+
+	if (clkspec->args_count != 1)
+		return ERR_PTR(-EINVAL);
+
+	if (clkspec->args[0] > 1)
+		return ERR_PTR(-EINVAL);
+
+	return &serdes->pll[clkspec->args[0]].hw;
+}
+
+/**
+ * lynx_lane_bitmap() - Get a bitmap for a group of lanes
+ * @group: The group of lanes
+ *
+ * Return: A mask containing all bits between @group->first and @group->last
+ */
+static unsigned int lynx_lane_bitmap(struct lynx_group *group)
+{
+	if (group->first_lane > group->last_lane)
+		return GENMASK(group->first_lane, group->last_lane);
+	else
+		return GENMASK(group->last_lane, group->first_lane);
+}
+
+static int lynx_init(struct phy *phy)
+{
+	int ret = 0;
+	struct lynx_group *group = phy_get_drvdata(phy);
+	struct lynx_priv *serdes = group->serdes;
+	unsigned int lane_mask = lynx_lane_bitmap(group);
+
+	mutex_lock(&serdes->lock);
+	if (serdes->used_lanes & lane_mask)
+		ret = -EBUSY;
+	else
+		serdes->used_lanes |= lane_mask;
+	mutex_unlock(&serdes->lock);
+	return ret;
+}
+
+static int lynx_exit(struct phy *phy)
+{
+	struct lynx_group *group = phy_get_drvdata(phy);
+	struct lynx_priv *serdes = group->serdes;
+
+	clk_disable_unprepare(group->pll);
+	clk_rate_exclusive_put(group->pll);
+	group->pll = NULL;
+
+	mutex_lock(&serdes->lock);
+	serdes->used_lanes &= ~lynx_lane_bitmap(group);
+	mutex_unlock(&serdes->lock);
+	return 0;
+}
+
+/*
+ * This is tricky. If first_lane=1 and last_lane=0, the condition will see 2,
+ * 1, 0. But the loop body will see 1, 0. We do this to avoid underflow. We
+ * can't pull the same trick when incrementing, because then we might have to
+ * start at -1 if (e.g.) first_lane = 0.
+ */
+#define for_range(val, start, end) \
+	for (val = start < end ? start : start + 1; \
+	     start < end ? val <= end : val-- > end; \
+	     start < end ? val++ : 0)
+#define for_each_lane(lane, group) \
+	for_range(lane, group->first_lane, group->last_lane)
+#define for_each_lane_reverse(lane, group) \
+	for_range(lane, group->last_lane, group->first_lane)
+
+static int lynx_power_on(struct phy *phy)
+{
+	int i;
+	struct lynx_group *group = phy_get_drvdata(phy);
+	u32 gcr0;
+
+	for_each_lane(i, group) {
+		gcr0 = lynx_read(group->serdes, LNmGCR0(i));
+		gcr0 &= ~(LNmGCR0_RX_PD | LNmGCR0_TX_PD);
+		lynx_write(group->serdes, gcr0, LNmGCR0(i));
+
+		usleep_range(15, 30);
+		gcr0 |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
+		lynx_write(group->serdes, gcr0, LNmGCR0(i));
+	}
+
+	return 0;
+}
+
+static void lynx_power_off_lane(struct lynx_priv *serdes, unsigned int lane)
+{
+	u32 gcr0 = lynx_read(serdes, LNmGCR0(lane));
+
+	gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
+	gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
+	lynx_write(serdes, gcr0, LNmGCR0(lane));
+}
+
+static int lynx_power_off(struct phy *phy)
+{
+	unsigned int i;
+	struct lynx_group *group = phy_get_drvdata(phy);
+
+	for_each_lane_reverse(i, group)
+		lynx_power_off_lane(group->serdes, i);
+
+	return 0;
+}
+
+/**
+ * lynx_lookup_proto() - Convert a phy-subsystem mode to a protocol
+ * @mode: The mode to convert
+ * @submode: The submode of @mode
+ *
+ * Return: A corresponding serdes-specific mode
+ */
+static enum lynx_protocol lynx_lookup_proto(enum phy_mode mode, int submode)
+{
+	switch (mode) {
+	case PHY_MODE_ETHERNET:
+		switch (submode) {
+		case PHY_INTERFACE_MODE_SGMII:
+		case PHY_INTERFACE_MODE_1000BASEX:
+			return LYNX_PROTO_SGMII;
+		case PHY_INTERFACE_MODE_2500BASEX:
+			return LYNX_PROTO_SGMII25;
+		case PHY_INTERFACE_MODE_QSGMII:
+			return LYNX_PROTO_QSGMII;
+		case PHY_INTERFACE_MODE_XGMII:
+		case PHY_INTERFACE_MODE_10GBASER:
+			return LYNX_PROTO_XFI;
+		case PHY_INTERFACE_MODE_10GKR:
+			return LYNX_PROTO_10GKR;
+		default:
+			return LYNX_PROTO_NONE;
+		}
+	/* Not implemented (yet) */
+	case PHY_MODE_PCIE:
+	case PHY_MODE_SATA:
+	default:
+		return LYNX_PROTO_NONE;
+	}
+}
+
+/**
+ * lynx_lookup_mode() - Get the mode for a group/protocol combination
+ * @group: The group of lanes to use
+ * @proto: The protocol to use
+ *
+ * Return: An appropriate mode to use, or %NULL if none match.
+ */
+static const struct lynx_mode *lynx_lookup_mode(struct lynx_group *group,
+					    enum lynx_protocol proto)
+{
+	int i;
+	const struct lynx_conf *conf = group->serdes->conf;
+
+	for (i = 0; i < conf->mode_count; i++) {
+		const struct lynx_mode *mode = &conf->modes[i];
+
+		if (BIT(proto) & mode->protos &&
+		    lynx_lane_bitmap(group) == mode->lanes)
+			return mode;
+	}
+
+	return NULL;
+}
+
+static int lynx_validate(struct phy *phy, enum phy_mode phy_mode, int submode,
+		       union phy_configure_opts *opts)
+{
+	enum lynx_protocol proto;
+	struct lynx_group *group = phy_get_drvdata(phy);
+	const struct lynx_mode *mode;
+
+	proto = lynx_lookup_proto(phy_mode, submode);
+	if (proto == LYNX_PROTO_NONE)
+		return -EINVAL;
+
+	/* Nothing to do */
+	if (proto == group->proto)
+		return 0;
+
+	mode = lynx_lookup_mode(group, proto);
+	if (!mode)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * lynx_proto_mode_mask() - Get the mask for a PCCR config
+ * @mode: The mode to use
+ *
+ * Return: The mask, shifted down to the lsb.
+ */
+static u32 lynx_proto_mode_mask(const struct lynx_mode *mode)
+{
+	switch (mode->pccr) {
+	case 0x0:
+		if (mode->protos & PROTO_MASK(PCIE))
+			return PCCR0_PEXa_MASK;
+		break;
+	case 0x2:
+		if (mode->protos & PROTO_MASK(SATA))
+			return PCCR2_SATAa_MASK;
+		break;
+	case 0x8:
+		if (mode->protos & PROTO_MASK(SGMII))
+			return PCCR8_SGMIIa_MASK;
+		break;
+	case 0x9:
+		if (mode->protos & PROTO_MASK(QSGMII))
+			return PCCR9_QSGMIIa_MASK;
+		break;
+	case 0xB:
+		if (mode->protos & PROTO_MASK(XFI))
+			return PCCRB_XFIa_MASK;
+		break;
+	}
+	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
+	       lynx_proto_str[mode->protos], 'A' + mode->idx);
+	return 0;
+}
+
+/**
+ * lynx_proto_mode_shift() - Get the shift for a PCCR config
+ * @mode: The mode to use
+ *
+ * Return: The amount of bits to shift the mask.
+ */
+static u32 lynx_proto_mode_shift(const struct lynx_mode *mode)
+{
+	switch (mode->pccr) {
+	case 0x0:
+		if (mode->protos & PROTO_MASK(PCIE))
+			return PCCR0_PEXa_SHIFT(mode->idx);
+		break;
+	case 0x2:
+		if (mode->protos & PROTO_MASK(SATA))
+			return PCCR2_SATAa_SHIFT(mode->idx);
+		break;
+	case 0x8:
+		if (mode->protos & PROTO_MASK(SGMII))
+			return PCCR8_SGMIIa_SHIFT(mode->idx);
+		break;
+	case 0x9:
+		if (mode->protos & PROTO_MASK(QSGMII))
+			return PCCR9_QSGMIIa_SHIFT(mode->idx);
+		break;
+	case 0xB:
+		if (mode->protos & PROTO_MASK(XFI))
+			return PCCRB_XFIa_SHIFT(mode->idx);
+		break;
+	}
+	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
+	       lynx_proto_str[mode->protos], 'A' + mode->idx);
+	return 0;
+}
+
+/**
+ * lynx_proto_mode_get() - Get the current config for a PCCR mode
+ * @mode: The mode to use
+ * @pccr: The current value of the PCCR
+ *
+ * Return: The current value of the PCCR config for this mode
+ */
+static u32 lynx_proto_mode_get(const struct lynx_mode *mode, u32 pccr)
+{
+	return (pccr >> lynx_proto_mode_shift(mode)) &
+	       lynx_proto_mode_mask(mode);
+}
+
+/**
+ * lynx_proto_mode_prep() - Configure a PCCR for a protocol
+ * @mode: The mode to use
+ * @pccr: The current value of the PCCR
+ * @proto: The protocol to configure
+ *
+ * This configures a PCCR for a mode and protocol. To disable a mode, pass
+ * %LYNX_PROTO_NONE as @proto. If @proto is 1000Base-KX, then the KX bit
+ * will be set.
+ *
+ * Return: The new value for the PCCR
+ */
+static u32 lynx_proto_mode_prep(const struct lynx_mode *mode, u32 pccr,
+				enum lynx_protocol proto)
+{
+	u32 shift = lynx_proto_mode_shift(mode);
+
+	pccr &= ~(lynx_proto_mode_mask(mode) << shift);
+	if (proto != LYNX_PROTO_NONE)
+		pccr |= mode->cfg << shift;
+
+	if (proto == LYNX_PROTO_1000BASEKX) {
+		if (mode->pccr == 8)
+			pccr |= PCCR8_SGMIIa_KX << shift;
+		else
+			pr_err("PCCR%X doesn't have a KX bit\n", mode->pccr);
+	}
+
+	return pccr;
+}
+
+#define abs_diff(a, b) ({ \
+	typeof(a) _a = (a); \
+	typeof(b) _b = (b); \
+	_a > _b ? _a - _b : _b - _a; \
+})
+
+static int lynx_set_mode(struct phy *phy, enum phy_mode phy_mode, int submode)
+{
+	enum lynx_protocol proto;
+	const struct lynx_proto_params *params;
+	const struct lynx_mode *old_mode = NULL, *new_mode;
+	int i, pll, ret;
+	struct lynx_group *group = phy_get_drvdata(phy);
+	struct lynx_priv *serdes = group->serdes;
+	u32 tmp;
+	u32 gcr0 = 0, gcr1 = 0, recr0 = 0, tecr0 = 0;
+	u32 gcr0_mask = 0, gcr1_mask = 0, recr0_mask = 0, tecr0_mask = 0;
+
+	proto = lynx_lookup_proto(phy_mode, submode);
+	if (proto == LYNX_PROTO_NONE) {
+		dev_dbg(&phy->dev, "unknown mode/submode %d/%d\n",
+			phy_mode, submode);
+		return -EINVAL;
+	}
+
+	/* Nothing to do */
+	if (proto == group->proto)
+		return 0;
+
+	new_mode = lynx_lookup_mode(group, proto);
+	if (!new_mode) {
+		dev_dbg(&phy->dev, "could not find mode for %s on lanes %u to %u\n",
+			lynx_proto_str[proto], group->first_lane,
+			group->last_lane);
+		return -EINVAL;
+	}
+
+	if (group->proto != LYNX_PROTO_NONE) {
+		old_mode = lynx_lookup_mode(group, group->proto);
+		if (!old_mode) {
+			dev_err(&phy->dev, "could not find mode for %s\n",
+				lynx_proto_str[group->proto]);
+			return -EBUSY;
+		}
+	}
+
+	clk_disable_unprepare(group->pll);
+	clk_rate_exclusive_put(group->pll);
+	group->pll = NULL;
+
+	/* First, try to use a PLL which already has the correct rate */
+	params = &lynx_proto_params[proto];
+	for (pll = 0; pll < ARRAY_SIZE(serdes->pll); pll++) {
+		struct clk *clk = serdes->pll[pll].hw.clk;
+		unsigned long rate = clk_get_rate(clk);
+		unsigned long error = abs_diff(rate, params->frate_khz);
+
+		dev_dbg(&phy->dev, "pll%d has rate %lu\n", pll, rate);
+		/* Accept up to 100ppm deviation */
+		if ((!error || params->frate_khz / error > 10000) &&
+		    !clk_set_rate_exclusive(clk, rate))
+			goto got_pll;
+		/* Someone else got a different rate first */
+	}
+
+	/* If neither PLL has the right rate, try setting it */
+	for (pll = 0; pll < 2; pll++) {
+		ret = clk_set_rate_exclusive(serdes->pll[pll].hw.clk,
+					     params->frate_khz);
+		if (!ret)
+			goto got_pll;
+	}
+
+	dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
+		params->frate_khz);
+	return ret;
+
+got_pll:
+	group->pll = serdes->pll[pll].hw.clk;
+	clk_prepare_enable(group->pll);
+
+	gcr0_mask |= LNmGCR0_RRAT_SEL | LNmGCR0_TRAT_SEL;
+	gcr0_mask |= LNmGCR0_RPLL_LES | LNmGCR0_TPLL_LES;
+	gcr0_mask |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
+	gcr0_mask |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
+	gcr0_mask |= LNmGCR0_IF20BIT_EN | LNmGCR0_PROTS;
+	gcr0 |= FIELD_PREP(LNmGCR0_RPLL_LES, !pll);
+	gcr0 |= FIELD_PREP(LNmGCR0_TPLL_LES, !pll);
+	gcr0 |= FIELD_PREP(LNmGCR0_RRAT_SEL, params->rat_sel);
+	gcr0 |= FIELD_PREP(LNmGCR0_TRAT_SEL, params->rat_sel);
+	gcr0 |= FIELD_PREP(LNmGCR0_IF20BIT_EN, params->if20bit);
+	gcr0 |= FIELD_PREP(LNmGCR0_PROTS, params->prots);
+
+	gcr1_mask |= LNmGCR1_RDAT_INV | LNmGCR1_TDAT_INV;
+	gcr1_mask |= LNmGCR1_OPAD_CTL | LNmGCR1_REIDL_TH;
+	gcr1_mask |= LNmGCR1_REIDL_EX_SEL | LNmGCR1_REIDL_ET_SEL;
+	gcr1_mask |= LNmGCR1_REIDL_EX_MSB | LNmGCR1_REIDL_ET_MSB;
+	gcr1_mask |= LNmGCR1_REQ_CTL_SNP | LNmGCR1_REQ_CDR_SNP;
+	gcr1_mask |= LNmGCR1_TRSTDIR | LNmGCR1_REQ_BIN_SNP;
+	gcr1_mask |= LNmGCR1_ISLEW_RCTL | LNmGCR1_OSLEW_RCTL;
+	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_TH, params->reidl_th);
+	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_SEL, params->reidl_ex & 3);
+	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_SEL, params->reidl_et & 3);
+	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_MSB, params->reidl_ex >> 2);
+	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_MSB, params->reidl_et >> 2);
+	gcr1 |= FIELD_PREP(LNmGCR1_TRSTDIR,
+			   group->first_lane > group->last_lane);
+	gcr1 |= FIELD_PREP(LNmGCR1_ISLEW_RCTL, params->slew);
+	gcr1 |= FIELD_PREP(LNmGCR1_OSLEW_RCTL, params->slew);
+
+	recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
+	recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
+	recr0_mask |= LNmRECR0_BASE_WAND | LNmRECR0_OSETOVD;
+	if (params->gain) {
+		recr0 |= FIELD_PREP(LNmRECR0_GK2OVD, params->gain);
+		recr0 |= FIELD_PREP(LNmRECR0_GK3OVD, params->gain);
+		recr0 |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
+	}
+	recr0 |= FIELD_PREP(LNmRECR0_BASE_WAND, params->baseline_wander);
+	recr0 |= FIELD_PREP(LNmRECR0_OSETOVD, params->offset_override);
+
+	tecr0_mask |= LNmTECR0_TEQ_TYPE;
+	tecr0_mask |= LNmTECR0_SGN_PREQ | LNmTECR0_RATIO_PREQ;
+	tecr0_mask |= LNmTECR0_SGN_POST1Q | LNmTECR0_RATIO_PST1Q;
+	tecr0_mask |= LNmTECR0_ADPT_EQ | LNmTECR0_AMP_RED;
+	tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, params->teq);
+	if (params->preq_ratio) {
+		tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
+		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
+	}
+	if (params->postq_ratio) {
+		tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
+		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
+	}
+	tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
+	tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
+
+	mutex_lock(&serdes->lock);
+
+	/* Disable the old controller */
+	if (old_mode) {
+		tmp = lynx_read(serdes, PCCRn(old_mode->pccr));
+		tmp = lynx_proto_mode_prep(old_mode, tmp, LYNX_PROTO_NONE);
+		lynx_write(serdes, tmp, PCCRn(old_mode->pccr));
+
+		if (old_mode->protos & PROTO_MASK(SGMII)) {
+			tmp = lynx_read(serdes, SGMIIaCR1(old_mode->idx));
+			tmp &= SGMIIaCR1_SGPCS_EN;
+			lynx_write(serdes, tmp, SGMIIaCR1(old_mode->idx));
+		}
+	}
+
+	for_each_lane_reverse(i, group) {
+		tmp = lynx_read(serdes, LNmGCR0(i));
+		tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
+		lynx_write(serdes, tmp, LNmGCR0(i));
+		ndelay(50);
+
+		tmp &= ~gcr0_mask;
+		tmp |= gcr0;
+		tmp |= FIELD_PREP(LNmGCR0_FIRST_LANE, i == group->first_lane);
+		lynx_write(serdes, tmp, LNmGCR0(i));
+
+		tmp = lynx_read(serdes, LNmGCR1(i));
+		tmp &= ~gcr1_mask;
+		tmp |= gcr1;
+		lynx_write(serdes, tmp, LNmGCR1(i));
+
+		tmp = lynx_read(serdes, LNmRECR0(i));
+		tmp &= ~recr0_mask;
+		tmp |= recr0;
+		lynx_write(serdes, tmp, LNmRECR0(i));
+
+		tmp = lynx_read(serdes, LNmTECR0(i));
+		tmp &= ~tecr0_mask;
+		tmp |= tecr0;
+		lynx_write(serdes, tmp, LNmTECR0(i));
+
+		tmp = lynx_read(serdes, LNmTTLCR0(i));
+		tmp &= ~LNmTTLCR0_FLT_SEL;
+		tmp |= FIELD_PREP(LNmTTLCR0_FLT_SEL, params->flt_sel);
+		lynx_write(serdes, tmp, LNmTTLCR0(i));
+
+		ndelay(120);
+		tmp = lynx_read(serdes, LNmGCR0(i));
+		tmp |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
+		lynx_write(serdes, tmp, LNmGCR0(i));
+	}
+
+	if (proto == LYNX_PROTO_1000BASEKX) {
+		/* FIXME: this races with clock updates */
+		tmp = lynx_read(serdes, PLLaCR0(pll));
+		tmp &= ~PLLaCR0_DLYDIV_SEL;
+		tmp |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, 1);
+		lynx_write(serdes, tmp, PLLaCR0(pll));
+	}
+
+	/* Enable the new controller */
+	tmp = lynx_read(serdes, PCCRn(new_mode->pccr));
+	tmp = lynx_proto_mode_prep(new_mode, tmp, proto);
+	lynx_write(serdes, tmp, PCCRn(new_mode->pccr));
+
+	if (new_mode->protos & PROTO_MASK(SGMII)) {
+		tmp = lynx_read(serdes, SGMIIaCR1(new_mode->idx));
+		tmp |= SGMIIaCR1_SGPCS_EN;
+		lynx_write(serdes, tmp, SGMIIaCR1(new_mode->idx));
+	}
+
+	mutex_unlock(&serdes->lock);
+
+	group->proto = proto;
+	dev_dbg(&phy->dev, "set mode to %s on lanes %u to %u\n",
+		lynx_proto_str[proto], group->first_lane, group->last_lane);
+	return 0;
+}
+
+static void lynx_release(struct phy *phy)
+{
+	struct lynx_group *group = phy_get_drvdata(phy);
+	struct lynx_priv *serdes = group->serdes;
+
+	mutex_lock(&serdes->lock);
+	if (--group->users) {
+		mutex_unlock(&serdes->lock);
+		return;
+	}
+	list_del(&group->groups);
+	mutex_unlock(&serdes->lock);
+
+	phy_destroy(phy);
+	kfree(group);
+}
+
+static const struct phy_ops lynx_phy_ops = {
+	.init = lynx_init,
+	.exit = lynx_exit,
+	.power_on = lynx_power_on,
+	.power_off = lynx_power_off,
+	.set_mode = lynx_set_mode,
+	.validate = lynx_validate,
+	.release = lynx_release,
+	.owner = THIS_MODULE,
+};
+
+static struct phy *lynx_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	struct phy *phy;
+	struct list_head *head;
+	struct lynx_group *group;
+	struct lynx_priv *serdes = dev_get_drvdata(dev);
+	unsigned int last_lane;
+
+	if (args->args_count == 1)
+		last_lane = args->args[0];
+	else if (args->args_count == 2)
+		last_lane = args->args[1];
+	else
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&serdes->lock);
+
+	/* Look for an existing group */
+	list_for_each(head, &serdes->groups) {
+		group = container_of(head, struct lynx_group, groups);
+		if (group->first_lane == args->args[0] &&
+		    group->last_lane == last_lane) {
+			group->users++;
+			return group->phy;
+		}
+	}
+
+	/* None found, create our own */
+	group = kzalloc(sizeof(*group), GFP_KERNEL);
+	if (!group) {
+		mutex_unlock(&serdes->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	group->serdes = serdes;
+	group->first_lane = args->args[0];
+	group->last_lane = last_lane;
+	group->users = 1;
+	phy = phy_create(dev, NULL, &lynx_phy_ops);
+	if (IS_ERR(phy)) {
+		kfree(group);
+	} else {
+		group->phy = phy;
+		phy_set_drvdata(phy, group);
+		list_add(&group->groups, &serdes->groups);
+	}
+
+	mutex_unlock(&serdes->lock);
+	return phy;
+}
+
+static int lynx_probe(struct platform_device *pdev)
+{
+	bool grabbed_clocks = false;
+	int i, ret;
+	struct device *dev = &pdev->dev;
+	struct lynx_priv *serdes;
+	struct regmap_config regmap_config = {};
+	const struct lynx_conf *conf;
+	struct resource *res;
+	void __iomem *base;
+
+	serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
+	if (!serdes)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, serdes);
+	mutex_init(&serdes->lock);
+	INIT_LIST_HEAD(&serdes->groups);
+	serdes->dev = dev;
+
+	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		dev_err_probe(dev, ret, "could not get/map registers\n");
+		return ret;
+	}
+
+	conf = device_get_match_data(dev);
+	serdes->conf = conf;
+	regmap_config.reg_bits = 32;
+	regmap_config.reg_stride = 4;
+	regmap_config.val_bits = 32;
+	regmap_config.val_format_endian = conf->endian;
+	regmap_config.max_register = res->end - res->start;
+	regmap_config.disable_locking = true;
+	serdes->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
+	if (IS_ERR(serdes->regmap)) {
+		ret = PTR_ERR(serdes->regmap);
+		dev_err_probe(dev, ret, "could not create regmap\n");
+		return ret;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(serdes->ref); i++) {
+		static const char fmt[] = "ref%d";
+		char name[sizeof(fmt)];
+
+		snprintf(name, sizeof(name), fmt, i);
+		serdes->ref[i] = devm_clk_get(dev, name);
+		if (IS_ERR(serdes->ref[i])) {
+			ret = PTR_ERR(serdes->ref[i]);
+			dev_err_probe(dev, ret, "could not get %s\n", name);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(serdes->pll); i++) {
+		static const char fmt[] = "%s.pll%d";
+		char *name;
+		const struct clk_hw *ref_hw[] = {
+			__clk_get_hw(serdes->ref[i]),
+		};
+		size_t len;
+		struct clk_init_data init = {};
+
+		len = snprintf(NULL, 0, fmt, pdev->name, i);
+		name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+
+		snprintf(name, len + 1, fmt, pdev->name, i);
+		init.name = name;
+		init.ops = &lynx_pll_clk_ops;
+		init.parent_hws = ref_hw;
+		init.num_parents = 1;
+		init.flags = CLK_SET_RATE_GATE | CLK_GET_RATE_NOCACHE;
+		init.flags |= CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
+
+		serdes->pll[i].hw.init = &init;
+		serdes->pll[i].serdes = serdes;
+		serdes->pll[i].idx = i;
+		ret = devm_clk_hw_register(dev, &serdes->pll[i].hw);
+		if (ret) {
+			dev_err_probe(dev, ret, "could not register %s\n",
+				      name);
+			return ret;
+		}
+	}
+
+	ret = devm_of_clk_add_hw_provider(dev, lynx_clk_get, serdes);
+	if (ret) {
+		dev_err_probe(dev, ret, "could not register clock provider\n");
+		return ret;
+	}
+
+	/* Deselect anything configured by the RCW/bootloader */
+	for (i = 0; i < conf->mode_count; i++) {
+		const struct lynx_mode *mode = &conf->modes[i];
+		u32 pccr = lynx_read(serdes, PCCRn(mode->pccr));
+
+		if (lynx_proto_mode_get(mode, pccr) == mode->cfg) {
+			if (mode->protos & UNSUPPORTED_PROTOS) {
+				/* Don't mess with modes we don't support */
+				serdes->used_lanes |= mode->lanes;
+				if (grabbed_clocks)
+					continue;
+
+				grabbed_clocks = true;
+				clk_prepare_enable(serdes->pll[0].hw.clk);
+				clk_prepare_enable(serdes->pll[1].hw.clk);
+				clk_rate_exclusive_get(serdes->pll[0].hw.clk);
+				clk_rate_exclusive_get(serdes->pll[1].hw.clk);
+			} else {
+				/* Otherwise, clear out the existing config */
+				pccr = lynx_proto_mode_prep(mode, pccr,
+							    LYNX_PROTO_NONE);
+				lynx_write(serdes, pccr, PCCRn(mode->pccr));
+			}
+
+			/* Disable the SGMII PCS until we're ready for it */
+			if (mode->protos & LYNX_PROTO_SGMII) {
+				u32 cr1;
+
+				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
+				cr1 &= ~SGMIIaCR1_SGPCS_EN;
+				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
+			}
+		}
+	}
+
+	/* Power off all lanes; used ones will be powered on later */
+	for (i = 0; i < conf->lanes; i++)
+		lynx_power_off_lane(serdes, i);
+
+	ret = PTR_ERR_OR_ZERO(devm_of_phy_provider_register(dev, lynx_xlate));
+	if (ret)
+		dev_err_probe(dev, ret, "could not register phy provider\n");
+	else
+		dev_info(dev, "probed with %d lanes\n", conf->lanes);
+	return ret;
+}
+
+/*
+ * XXX: For SerDes1, lane A uses pins SD1_RX3_P/N! That is, the lane numbers
+ * and pin numbers are _reversed_. In addition, the PCCR documentation is
+ * _inconsistent_ in its usage of these terms!
+ *
+ * PCCR "Lane 0" refers to...
+ * ==== =====================
+ *    0 Lane A
+ *    2 Lane A
+ *    8 Lane A
+ *    9 Lane A
+ *    B Lane D!
+ */
+static const struct lynx_mode ls1046a_modes1[] = {
+	CONF_SINGLE(1, PCIE, 0x0, 1, 0b001), /* PCIe.1 x1 */
+	CONF_1000BASEKX(0, 0x8, 0, 0b001), /* SGMII.6 */
+	CONF_SGMII25KX(1, 0x8, 1, 0b001), /* SGMII.5 */
+	CONF_SGMII25KX(2, 0x8, 2, 0b001), /* SGMII.10 */
+	CONF_SGMII25KX(3, 0x8, 3, 0b001), /* SGMII.9 */
+	CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001), /* QSGMII.6,5,10,1 */
+	CONF_XFI(2, 0xB, 0, 0b010), /* XFI.10 */
+	CONF_XFI(3, 0xB, 1, 0b001), /* XFI.9 */
+};
+
+static const struct lynx_conf ls1046a_conf1 = {
+	.modes = ls1046a_modes1,
+	.mode_count = ARRAY_SIZE(ls1046a_modes1),
+	.lanes = 4,
+	.endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct lynx_mode ls1046a_modes2[] = {
+	CONF_SINGLE(0, PCIE, 0x0, 0, 0b001), /* PCIe.1 x1 */
+	CONF(GENMASK(3, 0), PROTO_MASK(PCIE), 0x0, 0, 0b011), /* PCIe.1 x4 */
+	CONF_SINGLE(2, PCIE, 0x0, 2, 0b001), /* PCIe.2 x1 */
+	CONF(GENMASK(3, 2), PROTO_MASK(PCIE), 0x0, 2, 0b010), /* PCIe.3 x2 */
+	CONF_SINGLE(3, PCIE, 0x0, 2, 0b011), /* PCIe.3 x1 */
+	CONF_SINGLE(3, SATA, 0x2, 0, 0b001), /* SATA */
+	CONF_1000BASEKX(1, 0x8, 1, 0b001), /* SGMII.2 */
+};
+
+static const struct lynx_conf ls1046a_conf2 = {
+	.modes = ls1046a_modes2,
+	.mode_count = ARRAY_SIZE(ls1046a_modes2),
+	.lanes = 4,
+	.endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct of_device_id lynx_of_match[] = {
+	{ .compatible = "fsl,ls1046a-serdes-1", .data = &ls1046a_conf1 },
+	{ .compatible = "fsl,ls1046a-serdes-2", .data = &ls1046a_conf2 },
+};
+MODULE_DEVICE_TABLE(of, lynx_of_match);
+
+static struct platform_driver lynx_driver = {
+	.probe = lynx_probe,
+	.driver = {
+		.name = "qoriq_serdes",
+		.of_match_table = lynx_of_match,
+	},
+};
+module_platform_driver(lynx_driver);
+
+MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
+MODULE_DESCRIPTION("Lynx 10G SerDes driver");
+MODULE_LICENSE("GPL");
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 32/35] qoriq: Specify which MACs support RGMII
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
                   ` (3 preceding siblings ...)
  2022-06-28 22:13 ` [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
@ 2022-06-28 22:14 ` Sean Anderson
  2022-06-28 22:14 ` [PATCH net-next v2 33/35] qoriq: Add nodes for QSGMII PCSs Sean Anderson
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:14 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Benjamin Herrenschmidt,
	Krzysztof Kozlowski, Li Yang, Michael Ellerman, Paul Mackerras,
	Rob Herring, Shawn Guo, devicetree, linuxppc-dev

For more precise link mode support, we can add a property specifying
which MACs support RGMII. This silences the warning

	missing 'rgmii' property; assuming supported

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- Add rgmii property to all DPAA MACs

 .../boot/dts/freescale/fsl-ls1043-post.dtsi      |  7 +++++++
 .../boot/dts/freescale/fsl-ls1046-post.dtsi      |  8 ++++++++
 arch/powerpc/boot/dts/fsl/b4860si-post.dtsi      |  4 ++++
 arch/powerpc/boot/dts/fsl/b4si-post.dtsi         |  4 ++++
 arch/powerpc/boot/dts/fsl/t1023si-post.dtsi      |  4 ++++
 arch/powerpc/boot/dts/fsl/t1040si-post.dtsi      |  7 +++++++
 arch/powerpc/boot/dts/fsl/t2081si-post.dtsi      |  8 ++++++++
 arch/powerpc/boot/dts/fsl/t4240si-post.dtsi      | 16 ++++++++++++++++
 8 files changed, 58 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
index d237162a8744..f12d860a2ed4 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
@@ -24,23 +24,30 @@ &fman0 {
 
 	/* these aliases provide the FMan ports mapping */
 	enet0: ethernet@e0000 {
+		rgmii = <0>;
 	};
 
 	enet1: ethernet@e2000 {
+		rgmii = <0>;
 	};
 
 	enet2: ethernet@e4000 {
+		rgmii = <1>;
 	};
 
 	enet3: ethernet@e6000 {
+		rgmii = <1>;
 	};
 
 	enet4: ethernet@e8000 {
+		rgmii = <0>;
 	};
 
 	enet5: ethernet@ea000 {
+		rgmii = <0>;
 	};
 
 	enet6: ethernet@f0000 {
+		rgmii = <0>;
 	};
 };
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
index d6caaea57d90..4bb314388a72 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
@@ -23,26 +23,34 @@ &soc {
 &fman0 {
 	/* these aliases provide the FMan ports mapping */
 	enet0: ethernet@e0000 {
+		rgmii = <0>;
 	};
 
 	enet1: ethernet@e2000 {
+		rgmii = <0>;
 	};
 
 	enet2: ethernet@e4000 {
+		rgmii = <1>;
 	};
 
 	enet3: ethernet@e6000 {
+		rgmii = <1>;
 	};
 
 	enet4: ethernet@e8000 {
+		rgmii = <0>;
 	};
 
 	enet5: ethernet@ea000 {
+		rgmii = <0>;
 	};
 
 	enet6: ethernet@f0000 {
+		rgmii = <0>;
 	};
 
 	enet7: ethernet@f2000 {
+		rgmii = <0>;
 	};
 };
diff --git a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
index 868719821106..68f68f8cfa4e 100644
--- a/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
@@ -264,15 +264,19 @@ rcpm: global-utilities@e2000 {
 /include/ "qoriq-fman3-0-10g-1.dtsi"
 	fman@400000 {
 		enet4: ethernet@e8000 {
+			rgmii = <0>;
 		};
 
 		enet5: ethernet@ea000 {
+			rgmii = <0>;
 		};
 
 		enet6: ethernet@f0000 {
+			rgmii = <0>;
 		};
 
 		enet7: ethernet@f2000 {
+			rgmii = <0>;
 		};
 	};
 
diff --git a/arch/powerpc/boot/dts/fsl/b4si-post.dtsi b/arch/powerpc/boot/dts/fsl/b4si-post.dtsi
index 4f044b41a776..5e50b96e6b52 100644
--- a/arch/powerpc/boot/dts/fsl/b4si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/b4si-post.dtsi
@@ -465,15 +465,19 @@ muram@0 {
 		};
 
 		enet0: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet1: ethernet@e2000 {
+			rgmii = <0>;
 		};
 
 		enet2: ethernet@e4000 {
+			rgmii = <0>;
 		};
 
 		enet3: ethernet@e6000 {
+			rgmii = <0>;
 		};
 
 		mdio@fc000 {
diff --git a/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi b/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi
index d552044c5afc..e2a7bf643393 100644
--- a/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi
@@ -508,15 +508,19 @@ sata@220000 {
 /include/ "qoriq-fman3-0-1g-3.dtsi"
 	fman@400000 {
 		enet0: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet1: ethernet@e2000 {
+			rgmii = <0>;
 		};
 
 		enet2: ethernet@e4000 {
+			rgmii = <1>;
 		};
 
 		enet3: ethernet@e6000 {
+			rgmii = <1>;
 		};
 	};
 };
diff --git a/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi b/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
index f58eb820eb5e..a585f5faaf9e 100644
--- a/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
@@ -606,18 +606,25 @@ sata@221000 {
 /include/ "qoriq-fman3-0-1g-4.dtsi"
 	fman@400000 {
 		enet0: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet1: ethernet@e2000 {
+			rgmii = <1>;
+			mii;
 		};
 
 		enet2: ethernet@e4000 {
+			rgmii = <0>;
 		};
 
 		enet3: ethernet@e6000 {
+			rgmii = <1>;
+			mii;
 		};
 
 		enet4: ethernet@e8000 {
+			rgmii = <1>;
 		};
 
 		mdio@fc000 {
diff --git a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
index ecbb447920bc..f8a52ce0b590 100644
--- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
@@ -619,27 +619,35 @@ usb1: usb@211000 {
 /include/ "qoriq-fman3-0-10g-1.dtsi"
 	fman@400000 {
 		enet0: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet1: ethernet@e2000 {
+			rgmii = <0>;
 		};
 
 		enet2: ethernet@e4000 {
+			rgmii = <1>;
 		};
 
 		enet3: ethernet@e6000 {
+			rgmii = <1>;
 		};
 
 		enet4: ethernet@e8000 {
+			rgmii = <0>;
 		};
 
 		enet5: ethernet@ea000 {
+			rgmii = <0>;
 		};
 
 		enet6: ethernet@f0000 {
+			rgmii = <0>;
 		};
 
 		enet7: ethernet@f2000 {
+			rgmii = <1>;
 		};
 
 		mdio@fc000 {
diff --git a/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi b/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
index fcac73486d48..c0aa26db78b0 100644
--- a/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
@@ -1018,27 +1018,35 @@ usb1: usb@211000 {
 /include/ "qoriq-fman3-0-10g-1.dtsi"
 	fman@400000 {
 		enet0: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet1: ethernet@e2000 {
+			rgmii = <0>;
 		};
 
 		enet2: ethernet@e4000 {
+			rgmii = <0>;
 		};
 
 		enet3: ethernet@e6000 {
+			rgmii = <0>;
 		};
 
 		enet4: ethernet@e8000 {
+			rgmii = <1>;
 		};
 
 		enet5: ethernet@ea000 {
+			rgmii = <0>;
 		};
 
 		enet6: ethernet@f0000 {
+			rgmii = <0>;
 		};
 
 		enet7: ethernet@f2000 {
+			rgmii = <0>;
 		};
 
 		mdio@fc000 {
@@ -1061,27 +1069,35 @@ mdio@fd000 {
 /include/ "qoriq-fman3-1-10g-1.dtsi"
 	fman@500000 {
 		enet8: ethernet@e0000 {
+			rgmii = <0>;
 		};
 
 		enet9: ethernet@e2000 {
+			rgmii = <0>;
 		};
 
 		enet10: ethernet@e4000 {
+			rgmii = <0>;
 		};
 
 		enet11: ethernet@e6000 {
+			rgmii = <0>;
 		};
 
 		enet12: ethernet@e8000 {
+			rgmii = <1>;
 		};
 
 		enet13: ethernet@ea000 {
+			rgmii = <1>;
 		};
 
 		enet14: ethernet@f0000 {
+			rgmii = <0>;
 		};
 
 		enet15: ethernet@f2000 {
+			rgmii = <0>;
 		};
 
 		mdio@fc000 {
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 33/35] qoriq: Add nodes for QSGMII PCSs
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
                   ` (4 preceding siblings ...)
  2022-06-28 22:14 ` [PATCH net-next v2 32/35] qoriq: Specify which MACs support RGMII Sean Anderson
@ 2022-06-28 22:14 ` Sean Anderson
  2022-06-28 22:14 ` [PATCH net-next v2 34/35] arm64: dts: ls1046a: Add serdes bindings Sean Anderson
  2022-06-28 22:14 ` [PATCH net-next v2 35/35] arm64: dts: ls1046ardb: " Sean Anderson
  7 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:14 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Benjamin Herrenschmidt,
	Krzysztof Kozlowski, Li Yang, Michael Ellerman, Paul Mackerras,
	Rob Herring, Shawn Guo, devicetree, linuxppc-dev

Now that we actually read registers from QSGMII PCSs, it's important
that we have the correct address (instead of hoping that we're the MAC
with all the QSGMII PCSs on its bus). Add nodes for the QSGMII PCSs.

On the PowerPC platforms, all the QSGMII PCSs have the same structure
(e.g. if QSGMIIA is present it's used for MACs 1 through 4). On ARM
platforms, the exact mapping of QSGMII to MACs depends on the SoC.

Since the first QSGMII PCSs share an address with the SGMII and XFI
PCSs, we only add new nodes for PCSs 2-4. This avoids address conflicts
on the bus.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- New

 .../boot/dts/freescale/fsl-ls1043-post.dtsi   | 21 ++++++++++++++++
 .../boot/dts/freescale/fsl-ls1046-post.dtsi   | 25 +++++++++++++++++++
 .../fsl/qoriq-fman3-0-10g-0-best-effort.dtsi  |  3 ++-
 .../boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi     |  9 ++++++-
 .../fsl/qoriq-fman3-0-10g-1-best-effort.dtsi  |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi     |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi      |  3 ++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi      |  3 ++-
 .../boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi     |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi     |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi      |  3 ++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi      |  9 ++++++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi      |  3 ++-
 .../boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi      |  9 ++++++-
 20 files changed, 160 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
index f12d860a2ed4..6fd77ce41466 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043-post.dtsi
@@ -24,10 +24,13 @@ &fman0 {
 
 	/* these aliases provide the FMan ports mapping */
 	enet0: ethernet@e0000 {
+		pcs-names = "qsgmii";
 		rgmii = <0>;
 	};
 
 	enet1: ethernet@e2000 {
+		pcsphy-handle = <&pcsphy1>, <&qsgmiib_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
 		rgmii = <0>;
 	};
 
@@ -40,14 +43,32 @@ enet3: ethernet@e6000 {
 	};
 
 	enet4: ethernet@e8000 {
+		pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs2>;
+		pcs-names = "sgmii", "qsgmii";
 		rgmii = <0>;
 	};
 
 	enet5: ethernet@ea000 {
+		pcsphy-handle = <&pcsphy5>, <&qsgmiib_pcs3>;
+		pcs-names = "sgmii", "qsgmii";
 		rgmii = <0>;
 	};
 
 	enet6: ethernet@f0000 {
 		rgmii = <0>;
 	};
+
+	mdio@e1000 {
+		qsgmiib_pcs1: ethernet-pcs@1 {
+			reg = <0x1>;
+		};
+
+		qsgmiib_pcs2: ethernet-pcs@2 {
+			reg = <0x2>;
+		};
+
+		qsgmiib_pcs3: ethernet-pcs@3 {
+			reg = <0x3>;
+		};
+	};
 };
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
index 4bb314388a72..6a80accd4845 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046-post.dtsi
@@ -23,6 +23,8 @@ &soc {
 &fman0 {
 	/* these aliases provide the FMan ports mapping */
 	enet0: ethernet@e0000 {
+		pcsphy-handle = <&qsgmiib_pcs3>;
+		pcs-names = "qsgmii";
 		rgmii = <0>;
 	};
 
@@ -39,10 +41,14 @@ enet3: ethernet@e6000 {
 	};
 
 	enet4: ethernet@e8000 {
+		pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
 		rgmii = <0>;
 	};
 
 	enet5: ethernet@ea000 {
+		pcsphy-handle = <&pcsphy5>, <&pcsphy5>;
+		pcs-names = "sgmii", "qsgmii";
 		rgmii = <0>;
 	};
 
@@ -51,6 +57,25 @@ enet6: ethernet@f0000 {
 	};
 
 	enet7: ethernet@f2000 {
+		pcsphy-handle = <&pcsphy7>, <&qsgmiib_pcs2>, <&pcsphy7>;
+		pcs-names = "sgmii", "qsgmii", "xfi";
 		rgmii = <0>;
 	};
+
+	mdio@eb000 {
+		qsgmiib_pcs1: ethernet-pcs@1 {
+			compatible = "fsl,lynx-10g-qsgmii-pcs";
+			reg = <0x1>;
+		};
+
+		qsgmiib_pcs2: ethernet-pcs@2 {
+			compatible = "fsl,lynx-10g-qsgmii-pcs";
+			reg = <0x2>;
+		};
+
+		qsgmiib_pcs3: ethernet-pcs@3 {
+			compatible = "fsl,lynx-10g-qsgmii-pcs";
+			reg = <0x3>;
+		};
+	};
 };
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi
index baa0c503e741..db169d630db3 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi
@@ -55,7 +55,8 @@ ethernet@e0000 {
 		reg = <0xe0000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy0>;
+		pcsphy-handle = <&pcsphy0>, <&pcsphy0>;
+		pcs-names = "sgmii", "qsgmii";
 	};
 
 	mdio@e1000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi
index 93095600e808..fc709261c672 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi
@@ -52,7 +52,14 @@ ethernet@f0000 {
 		compatible = "fsl,fman-memac";
 		reg = <0xf0000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x10 &fman0_tx_0x30>;
-		pcsphy-handle = <&pcsphy6>;
+		pcsphy-handle = <&pcsphy6>, <&qsgmiib_pcs2>, <&pcsphy6>;
+		pcs-names = "sgmii", "qsgmii", "xfi";
+	};
+
+	mdio@e9000 {
+		qsgmiib_pcs2: ethernet-pcs@2 {
+			reg = <2>;
+		};
 	};
 
 	mdio@f1000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi
index ff4bd38f0645..dca4702777ef 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi
@@ -55,7 +55,14 @@ ethernet@e2000 {
 		reg = <0xe2000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy1>;
+		pcsphy-handle = <&pcsphy1>, <&qsgmiia_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiia_pcs1: ethernet-pcs@1 {
+			reg = <1>;
+		};
 	};
 
 	mdio@e3000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi
index 1fa38ed6f59e..220a8fe67fc1 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi
@@ -52,7 +52,14 @@ ethernet@f2000 {
 		compatible = "fsl,fman-memac";
 		reg = <0xf2000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x11 &fman0_tx_0x31>;
-		pcsphy-handle = <&pcsphy7>;
+		pcsphy-handle = <&pcsphy7>, <&qsgmiib_pcs3>, <&pcsphy7>;
+		pcs-names = "sgmii", "qsgmii", "xfi";
+	};
+
+	mdio@e9000 {
+		qsgmiib_pcs3: ethernet-pcs@3 {
+			reg = <3>;
+		};
 	};
 
 	mdio@f3000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi
index a8cc9780c0c4..ce76725e6eb2 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi
@@ -51,7 +51,8 @@ ethernet@e0000 {
 		reg = <0xe0000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x08 &fman0_tx_0x28>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy0>;
+		pcsphy-handle = <&pcsphy0>, <&pcsphy0>;
+		pcs-names = "sgmii", "qsgmii";
 	};
 
 	mdio@e1000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi
index 8b8bd70c9382..4e54516aea2f 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi
@@ -51,7 +51,14 @@ ethernet@e2000 {
 		reg = <0xe2000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x09 &fman0_tx_0x29>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy1>;
+		pcsphy-handle = <&pcsphy1>, <&qsgmiia_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiia_pcs1: ethernet-pcs@1 {
+			reg = <1>;
+		};
 	};
 
 	mdio@e3000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi
index 619c880b54d8..0c7459f9efa9 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi
@@ -51,7 +51,14 @@ ethernet@e4000 {
 		reg = <0xe4000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x0a &fman0_tx_0x2a>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy2>;
+		pcsphy-handle = <&pcsphy2>, <&qsgmiia_pcs2>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiia_pcs2: ethernet-pcs@2 {
+			reg = <2>;
+		};
 	};
 
 	mdio@e5000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi
index d7ebb73a400d..2c138ebaa6fc 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi
@@ -51,7 +51,14 @@ ethernet@e6000 {
 		reg = <0xe6000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x0b &fman0_tx_0x2b>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy3>;
+		pcsphy-handle = <&pcsphy3>, <&qsgmiia_pcs3>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiia_pcs3: ethernet-pcs@3 {
+			reg = <3>;
+		};
 	};
 
 	mdio@e7000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi
index b151d696a069..e2174c0fc841 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi
@@ -51,7 +51,8 @@ ethernet@e8000 {
 		reg = <0xe8000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy4>;
+		pcsphy-handle = <&pcsphy4>, <&pcsphy4>;
+		pcs-names = "sgmii", "qsgmii";
 	};
 
 	mdio@e9000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi
index adc0ae0013a3..8c5e70da4450 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi
@@ -51,7 +51,14 @@ ethernet@ea000 {
 		reg = <0xea000 0x1000>;
 		fsl,fman-ports = <&fman0_rx_0x0d &fman0_tx_0x2d>;
 		ptp-timer = <&ptp_timer0>;
-		pcsphy-handle = <&pcsphy5>;
+		pcsphy-handle = <&pcsphy5>, <&qsgmiib_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e9000 {
+		qsgmiib_pcs1: ethernet-pcs@1 {
+			reg = <1>;
+		};
 	};
 
 	mdio@eb000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi
index 435047e0e250..24ab7fc89a87 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi
@@ -52,7 +52,14 @@ ethernet@f0000 {
 		compatible = "fsl,fman-memac";
 		reg = <0xf0000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x10 &fman1_tx_0x30>;
-		pcsphy-handle = <&pcsphy14>;
+		pcsphy-handle = <&pcsphy14>, <&qsgmiid_pcs2>, <&pcsphy14>;
+		pcs-names = "sgmii", "qsgmii", "xfi";
+	};
+
+	mdio@e9000 {
+		qsgmiid_pcs2: ethernet-pcs@2 {
+			reg = <2>;
+		};
 	};
 
 	mdio@f1000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi
index c098657cca0a..16a437edda9a 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi
@@ -52,7 +52,14 @@ ethernet@f2000 {
 		compatible = "fsl,fman-memac";
 		reg = <0xf2000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x11 &fman1_tx_0x31>;
-		pcsphy-handle = <&pcsphy15>;
+		pcsphy-handle = <&pcsphy15>, <&qsgmiid_pcs3>, <&pcsphy15>;
+		pcs-names = "sgmii", "qsgmii", "xfi";
+	};
+
+	mdio@e9000 {
+		qsgmiid_pcs3: ethernet-pcs@3 {
+			reg = <3>;
+		};
 	};
 
 	mdio@f3000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi
index 9d06824815f3..16fb299f615a 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi
@@ -51,7 +51,8 @@ ethernet@e0000 {
 		reg = <0xe0000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x08 &fman1_tx_0x28>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy8>;
+		pcsphy-handle = <&pcsphy8>, <&pcsphy8>;
+		pcs-names = "sgmii", "qsgmii";
 	};
 
 	mdio@e1000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi
index 70e947730c4b..32af9ed28094 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi
@@ -51,7 +51,14 @@ ethernet@e2000 {
 		reg = <0xe2000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x09 &fman1_tx_0x29>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy9>;
+		pcsphy-handle = <&pcsphy9>, <&qsgmiic_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiic_pcs1: ethernet-pcs@1 {
+			reg = <1>;
+		};
 	};
 
 	mdio@e3000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi
index ad96e6529595..bf830e5b084a 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi
@@ -51,7 +51,14 @@ ethernet@e4000 {
 		reg = <0xe4000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x0a &fman1_tx_0x2a>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy10>;
+		pcsphy-handle = <&pcsphy10>, <&qsgmiic_pcs2>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiic_pcs2: ethernet-pcs@2 {
+			reg = <2>;
+		};
 	};
 
 	mdio@e5000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi
index 034bc4b71f7a..0fe2c962f72e 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi
@@ -51,7 +51,14 @@ ethernet@e6000 {
 		reg = <0xe6000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x0b &fman1_tx_0x2b>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy11>;
+		pcsphy-handle = <&pcsphy11>, <&qsgmiic_pcs3>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e1000 {
+		qsgmiic_pcs3: ethernet-pcs@3 {
+			reg = <3>;
+		};
 	};
 
 	mdio@e7000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi
index 93ca23d82b39..9366935ebc02 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi
@@ -51,7 +51,8 @@ ethernet@e8000 {
 		reg = <0xe8000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x0c &fman1_tx_0x2c>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy12>;
+		pcsphy-handle = <&pcsphy12>, <&pcsphy12>;
+		pcs-names = "sgmii", "qsgmii";
 	};
 
 	mdio@e9000 {
diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi
index 23b3117a2fd2..b05e7a46e2e3 100644
--- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi
+++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi
@@ -51,7 +51,14 @@ ethernet@ea000 {
 		reg = <0xea000 0x1000>;
 		fsl,fman-ports = <&fman1_rx_0x0d &fman1_tx_0x2d>;
 		ptp-timer = <&ptp_timer1>;
-		pcsphy-handle = <&pcsphy13>;
+		pcsphy-handle = <&pcsphy13>, <&qsgmiid_pcs1>;
+		pcs-names = "sgmii", "qsgmii";
+	};
+
+	mdio@e9000 {
+		qsgmiid_pcs1: ethernet-pcs@1 {
+			reg = <1>;
+		};
 	};
 
 	mdio@eb000 {
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 34/35] arm64: dts: ls1046a: Add serdes bindings
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
                   ` (5 preceding siblings ...)
  2022-06-28 22:14 ` [PATCH net-next v2 33/35] qoriq: Add nodes for QSGMII PCSs Sean Anderson
@ 2022-06-28 22:14 ` Sean Anderson
  2022-06-28 22:14 ` [PATCH net-next v2 35/35] arm64: dts: ls1046ardb: " Sean Anderson
  7 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:14 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Krzysztof Kozlowski, Li Yang,
	Rob Herring, Shawn Guo, devicetree

This adds bindings for the SerDes devices. They are disabled by default
to prevent any breakage on existing boards.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

Changes in v2:
- Disable SerDes by default to prevent breaking boards inadvertently.
- Use one phy cell for SerDes1, since no lanes can be grouped

 arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index 0085e83adf65..8b15653607c9 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -413,6 +413,22 @@ bportals: bman-portals@508000000 {
 			ranges = <0x0 0x5 0x08000000 0x8000000>;
 		};
 
+		serdes1: phy@1ea0000 {
+			#clock-cells = <1>;
+			#phy-cells = <1>;
+			compatible = "fsl,ls1046a-serdes-1";
+			reg = <0x0 0x1ea0000 0x0 0x2000>;
+			status = "disabled";
+		};
+
+		serdes2: phy@1eb0000 {
+			#clock-cells = <1>;
+			#phy-cells = <2>;
+			compatible = "fsl,ls1046a-serdes-2";
+			reg = <0x0 0x1eb0000 0x0 0x2000>;
+			status = "disabled";
+		};
+
 		dcfg: dcfg@1ee0000 {
 			compatible = "fsl,ls1046a-dcfg", "syscon";
 			reg = <0x0 0x1ee0000 0x0 0x1000>;
-- 
2.35.1.1320.gc452695387.dirty


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

* [PATCH net-next v2 35/35] arm64: dts: ls1046ardb: Add serdes bindings
  2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
                   ` (6 preceding siblings ...)
  2022-06-28 22:14 ` [PATCH net-next v2 34/35] arm64: dts: ls1046a: Add serdes bindings Sean Anderson
@ 2022-06-28 22:14 ` Sean Anderson
  7 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-28 22:14 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev
  Cc: Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Sean Anderson, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Li Yang, Rob Herring, Shawn Guo, Vinod Koul,
	devicetree, linux-phy

This adds appropriate bindings for the macs which use the SerDes. The
156.25MHz fixed clock is a crystal. The 100MHz clocks (there are
actually 3) come from a Renesas 6V49205B at address 69 on i2c0. There is
no driver for this device (and as far as I know all you can do with the
100MHz clocks is gate them), so I have chosen to model it as a single
fixed clock.

Note: the SerDes1 lane numbering for the LS1046A is *reversed*.
This means that Lane A (what the driver thinks is lane 0) uses pins
SD1_TX3_P/N.

Because this will break ethernet if the serdes is not enabled, enable
the serdes driver by default on Layerscape.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
Please let me know if there is a better/more specific config I can use
here.

(no changes since v1)

 .../boot/dts/freescale/fsl-ls1046a-rdb.dts    | 34 +++++++++++++++++++
 drivers/phy/freescale/Kconfig                 |  1 +
 2 files changed, 35 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
index 7025aad8ae89..4f4dd0ed8c53 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
@@ -26,6 +26,32 @@ aliases {
 	chosen {
 		stdout-path = "serial0:115200n8";
 	};
+
+	clocks {
+		clk_100mhz: clock-100mhz {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <100000000>;
+		};
+
+		clk_156mhz: clock-156mhz {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <156250000>;
+		};
+	};
+};
+
+&serdes1 {
+	clocks = <&clk_100mhz>, <&clk_156mhz>;
+	clock-names = "ref0", "ref1";
+	status = "okay";
+};
+
+&serdes2 {
+	clocks = <&clk_100mhz>, <&clk_100mhz>;
+	clock-names = "ref0", "ref1";
+	status = "okay";
 };
 
 &duart0 {
@@ -140,21 +166,29 @@ ethernet@e6000 {
 	ethernet@e8000 {
 		phy-handle = <&sgmii_phy1>;
 		phy-connection-type = "sgmii";
+		phys = <&serdes1 1>;
+		phy-names = "serdes";
 	};
 
 	ethernet@ea000 {
 		phy-handle = <&sgmii_phy2>;
 		phy-connection-type = "sgmii";
+		phys = <&serdes1 0>;
+		phy-names = "serdes";
 	};
 
 	ethernet@f0000 { /* 10GEC1 */
 		phy-handle = <&aqr106_phy>;
 		phy-connection-type = "xgmii";
+		phys = <&serdes1 3>;
+		phy-names = "serdes";
 	};
 
 	ethernet@f2000 { /* 10GEC2 */
 		fixed-link = <0 1 1000 0 0>;
 		phy-connection-type = "xgmii";
+		phys = <&serdes1 2>;
+		phy-names = "serdes";
 	};
 
 	mdio@fc000 {
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 857b4d123515..c9f687384c13 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -43,6 +43,7 @@ config PHY_FSL_LYNX_10G
 	tristate "Freescale Layerscale Lynx 10G SerDes support"
 	select GENERIC_PHY
 	select REGMAP_MMIO
+	default y if ARCH_LAYERSCAPE
 	help
 	  This adds support for the Lynx "SerDes" devices found on various QorIQ
 	  SoCs. There may be up to four SerDes devices on each SoC, and each
-- 
2.35.1.1320.gc452695387.dirty


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

* Re: [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml
  2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
@ 2022-06-29  2:09   ` Rob Herring
  2022-06-29 14:50   ` Russell King (Oracle)
  2022-07-01  0:01   ` Rob Herring
  2 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2022-06-29  2:09 UTC (permalink / raw)
  To: Sean Anderson
  Cc: linux-arm-kernel, Rob Herring, David S . Miller, Paolo Abeni,
	Jakub Kicinski, Russell King, linux-kernel, Eric Dumazet, netdev,
	Krzysztof Kozlowski, devicetree, Madalin Bucur

On Tue, 28 Jun 2022 18:13:31 -0400, Sean Anderson wrote:
> This converts the MAC portion of the FMan MAC bindings to yaml.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - New
> 
>  .../bindings/net/fsl,fman-dtsec.yaml          | 144 ++++++++++++++++++
>  .../devicetree/bindings/net/fsl-fman.txt      | 128 +---------------
>  2 files changed, 145 insertions(+), 127 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: patternProperties:^thermistor@:properties:adi,excitation-current-nanoamp: '$ref' should not be valid under {'const': '$ref'}
	hint: Standard unit suffix properties don't need a type $ref
	from schema $id: http://devicetree.org/meta-schemas/core.yaml#
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: ignoring, error in schema: patternProperties: ^thermistor@: properties: adi,excitation-current-nanoamp
Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.example.dtb:0:0: /example-0/spi/ltc2983@0: failed to match any schema with compatible: ['adi,ltc2983']

doc reference errors (make refcheckdocs):

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

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

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

pip3 install dtschema --upgrade

Please check and re-submit.


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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
@ 2022-06-29  2:09   ` Rob Herring
  2022-06-30 15:53     ` Sean Anderson
  2022-06-30 17:27   ` Rob Herring
  1 sibling, 1 reply; 32+ messages in thread
From: Rob Herring @ 2022-06-29  2:09 UTC (permalink / raw)
  To: Sean Anderson
  Cc: devicetree, Eric Dumazet, Vinod Koul, linux-phy, Jakub Kicinski,
	David S . Miller, netdev, Russell King, linux-kernel,
	Paolo Abeni, Kishon Vijay Abraham I, Madalin Bucur,
	Krzysztof Kozlowski, Rob Herring, linux-arm-kernel

On Tue, 28 Jun 2022 18:13:30 -0400, Sean Anderson wrote:
> This adds a binding for the SerDes module found on QorIQ processors. The
> phy reference has two cells, one for the first lane and one for the
> last. This should allow for good support of multi-lane protocols when
> (if) they are added. There is no protocol option, because the driver is
> designed to be able to completely reconfigure lanes at runtime.
> Generally, the phy consumer can select the appropriate protocol using
> set_mode. For the most part there is only one protocol controller
> (consumer) per lane/protocol combination. The exception to this is the
> B4860 processor, which has some lanes which can be connected to
> multiple MACs. For that processor, I anticipate the easiest way to
> resolve this will be to add an additional cell with a "protocol
> controller instance" property.
> 
> Each serdes has a unique set of supported protocols (and lanes). The
> support matrix is stored in the driver and is selected based on the
> compatible string. It is anticipated that a new compatible string will
> need to be added for each serdes on each SoC that drivers support is
> added for. There is no "generic" compatible string for this reason.
> 
> There are two PLLs, each of which can be used as the master clock for
> each lane. Each PLL has its own reference. For the moment they are
> required, because it simplifies the driver implementation. Absent
> reference clocks can be modeled by a fixed-clock with a rate of 0.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - Add #clock-cells. This will allow using assigned-clocks* to configure
>   the PLLs.
> - Allow a value of 1 for phy-cells. This allows for compatibility with
>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>   binding.
> - Document phy cells in the description
> - Document the structure of the compatible strings
> - Fix example binding having too many cells in regs
> - Move compatible first
> - Refer to the device in the documentation, rather than the binding
> - Remove minItems
> - Rename to fsl,lynx-10g.yaml
> - Use list for clock-names
> 
>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>  1 file changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: patternProperties:^thermistor@:properties:adi,excitation-current-nanoamp: '$ref' should not be valid under {'const': '$ref'}
	hint: Standard unit suffix properties don't need a type $ref
	from schema $id: http://devicetree.org/meta-schemas/core.yaml#
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: ignoring, error in schema: patternProperties: ^thermistor@: properties: adi,excitation-current-nanoamp
Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.example.dtb:0:0: /example-0/spi/ltc2983@0: failed to match any schema with compatible: ['adi,ltc2983']

doc reference errors (make refcheckdocs):

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

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

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

pip3 install dtschema --upgrade

Please check and re-submit.


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

* Re: [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml
  2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
  2022-06-29  2:09   ` Rob Herring
@ 2022-06-29 14:50   ` Russell King (Oracle)
  2022-06-30 14:59     ` Sean Anderson
  2022-07-01  0:01   ` Rob Herring
  2 siblings, 1 reply; 32+ messages in thread
From: Russell King (Oracle) @ 2022-06-29 14:50 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Paolo Abeni, linux-arm-kernel, Eric Dumazet, linux-kernel,
	Krzysztof Kozlowski, Rob Herring, devicetree

On Tue, Jun 28, 2022 at 06:13:31PM -0400, Sean Anderson wrote:
> This converts the MAC portion of the FMan MAC bindings to yaml.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - New
> 
>  .../bindings/net/fsl,fman-dtsec.yaml          | 144 ++++++++++++++++++
>  .../devicetree/bindings/net/fsl-fman.txt      | 128 +---------------
>  2 files changed, 145 insertions(+), 127 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> 
> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> new file mode 100644
> index 000000000000..809df1589f20
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> @@ -0,0 +1,144 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/fsl,fman-dtsec.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP FMan MAC
> +
> +maintainers:
> +  - Madalin Bucur <madalin.bucur@nxp.com>
> +
> +description: |
> +  Each FMan has several MACs, each implementing an Ethernet interface. Earlier
> +  versions of FMan used the Datapath Three Speed Ethernet Controller (dTSEC) for
> +  10/100/1000 MBit/s speeds, and the 10-Gigabit Ethernet Media Access Controller
> +  (10GEC) for 10 Gbit/s speeds. Later versions of FMan use the Multirate
> +  Ethernet Media Access Controller (mEMAC) to handle all speeds.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - fsl,fman-dtsec
> +      - fsl,fman-xgec
> +      - fsl,fman-memac
> +
> +  cell-index:
> +    maximum: 64
> +    description: |
> +      FManV2:
> +      register[bit]           MAC             cell-index
> +      ============================================================
> +      FM_EPI[16]              XGEC            8
> +      FM_EPI[16+n]            dTSECn          n-1
> +      FM_NPI[11+n]            dTSECn          n-1
> +              n = 1,..,5
> +
> +      FManV3:
> +      register[bit]           MAC             cell-index
> +      ============================================================
> +      FM_EPI[16+n]            mEMACn          n-1
> +      FM_EPI[25]              mEMAC10         9
> +
> +      FM_NPI[11+n]            mEMACn          n-1
> +      FM_NPI[10]              mEMAC10         9
> +      FM_NPI[11]              mEMAC9          8
> +              n = 1,..8
> +
> +      FM_EPI and FM_NPI are located in the FMan memory map.
> +
> +      2. SoC registers:
> +
> +      - P2041, P3041, P4080 P5020, P5040:
> +      register[bit]           FMan            MAC             cell
> +                              Unit                            index
> +      ============================================================
> +      DCFG_DEVDISR2[7]        1               XGEC            8
> +      DCFG_DEVDISR2[7+n]      1               dTSECn          n-1
> +      DCFG_DEVDISR2[15]       2               XGEC            8
> +      DCFG_DEVDISR2[15+n]     2               dTSECn          n-1
> +              n = 1,..5
> +
> +      - T1040, T2080, T4240, B4860:
> +      register[bit]                   FMan    MAC             cell
> +                                      Unit                    index
> +      ============================================================
> +      DCFG_CCSR_DEVDISR2[n-1]         1       mEMACn          n-1
> +      DCFG_CCSR_DEVDISR2[11+n]        2       mEMACn          n-1
> +              n = 1,..6,9,10
> +
> +      EVDISR, DCFG_DEVDISR2 and DCFG_CCSR_DEVDISR2 are located in
> +      the specific SoC "Device Configuration/Pin Control" Memory
> +      Map.
> +
> +  reg:
> +    maxItems: 1
> +
> +  fsl,fman-ports:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    maxItems: 2
> +    description: |
> +      An array of two references: the first is the FMan RX port and the second
> +      is the TX port used by this MAC.
> +
> +  ptp-timer:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: A reference to the IEEE1588 timer
> +
> +  pcsphy-handle:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: A reference to the PCS (typically found on the SerDes)

This description includes ethernet-controller.yaml, which contains:

  pcs-handle:
    $ref: /schemas/types.yaml#/definitions/phandle
    description:
      Specifies a reference to a node representing a PCS PHY device on a MDIO
      bus to link with an external PHY (phy-handle) if exists.

Is there a reason why a custom property is needed rather than using the
pcs-handle property already provided by the ethernet-controller DT
description?

Thanks.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!

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

* Re: [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml
  2022-06-29 14:50   ` Russell King (Oracle)
@ 2022-06-30 14:59     ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 14:59 UTC (permalink / raw)
  To: Russell King (Oracle)
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Paolo Abeni, linux-arm-kernel, Eric Dumazet, linux-kernel,
	Krzysztof Kozlowski, Rob Herring, devicetree

Hi Russell,

On 6/29/22 10:50 AM, Russell King (Oracle) wrote:
> On Tue, Jun 28, 2022 at 06:13:31PM -0400, Sean Anderson wrote:
>> This converts the MAC portion of the FMan MAC bindings to yaml.
>> 
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>> 
>> Changes in v2:
>> - New
>> 
>>  .../bindings/net/fsl,fman-dtsec.yaml          | 144 ++++++++++++++++++
>>  .../devicetree/bindings/net/fsl-fman.txt      | 128 +---------------
>>  2 files changed, 145 insertions(+), 127 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> 
>> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> new file mode 100644
>> index 000000000000..809df1589f20
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> @@ -0,0 +1,144 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/net/fsl,fman-dtsec.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: NXP FMan MAC
>> +
>> +maintainers:
>> +  - Madalin Bucur <madalin.bucur@nxp.com>
>> +
>> +description: |
>> +  Each FMan has several MACs, each implementing an Ethernet interface. Earlier
>> +  versions of FMan used the Datapath Three Speed Ethernet Controller (dTSEC) for
>> +  10/100/1000 MBit/s speeds, and the 10-Gigabit Ethernet Media Access Controller
>> +  (10GEC) for 10 Gbit/s speeds. Later versions of FMan use the Multirate
>> +  Ethernet Media Access Controller (mEMAC) to handle all speeds.
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - fsl,fman-dtsec
>> +      - fsl,fman-xgec
>> +      - fsl,fman-memac
>> +
>> +  cell-index:
>> +    maximum: 64
>> +    description: |
>> +      FManV2:
>> +      register[bit]           MAC             cell-index
>> +      ============================================================
>> +      FM_EPI[16]              XGEC            8
>> +      FM_EPI[16+n]            dTSECn          n-1
>> +      FM_NPI[11+n]            dTSECn          n-1
>> +              n = 1,..,5
>> +
>> +      FManV3:
>> +      register[bit]           MAC             cell-index
>> +      ============================================================
>> +      FM_EPI[16+n]            mEMACn          n-1
>> +      FM_EPI[25]              mEMAC10         9
>> +
>> +      FM_NPI[11+n]            mEMACn          n-1
>> +      FM_NPI[10]              mEMAC10         9
>> +      FM_NPI[11]              mEMAC9          8
>> +              n = 1,..8
>> +
>> +      FM_EPI and FM_NPI are located in the FMan memory map.
>> +
>> +      2. SoC registers:
>> +
>> +      - P2041, P3041, P4080 P5020, P5040:
>> +      register[bit]           FMan            MAC             cell
>> +                              Unit                            index
>> +      ============================================================
>> +      DCFG_DEVDISR2[7]        1               XGEC            8
>> +      DCFG_DEVDISR2[7+n]      1               dTSECn          n-1
>> +      DCFG_DEVDISR2[15]       2               XGEC            8
>> +      DCFG_DEVDISR2[15+n]     2               dTSECn          n-1
>> +              n = 1,..5
>> +
>> +      - T1040, T2080, T4240, B4860:
>> +      register[bit]                   FMan    MAC             cell
>> +                                      Unit                    index
>> +      ============================================================
>> +      DCFG_CCSR_DEVDISR2[n-1]         1       mEMACn          n-1
>> +      DCFG_CCSR_DEVDISR2[11+n]        2       mEMACn          n-1
>> +              n = 1,..6,9,10
>> +
>> +      EVDISR, DCFG_DEVDISR2 and DCFG_CCSR_DEVDISR2 are located in
>> +      the specific SoC "Device Configuration/Pin Control" Memory
>> +      Map.
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  fsl,fman-ports:
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>> +    maxItems: 2
>> +    description: |
>> +      An array of two references: the first is the FMan RX port and the second
>> +      is the TX port used by this MAC.
>> +
>> +  ptp-timer:
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +    description: A reference to the IEEE1588 timer
>> +
>> +  pcsphy-handle:
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +    description: A reference to the PCS (typically found on the SerDes)
> 
> This description includes ethernet-controller.yaml, which contains:
> 
>   pcs-handle:
>     $ref: /schemas/types.yaml#/definitions/phandle
>     description:
>       Specifies a reference to a node representing a PCS PHY device on a MDIO
>       bus to link with an external PHY (phy-handle) if exists.
> 
> Is there a reason why a custom property is needed rather than using the
> pcs-handle property already provided by the ethernet-controller DT
> description?

This is the existing property name. It must remain the same for backwards compatibility. c.f. tbi-handle.

--Sean

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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-29  2:09   ` Rob Herring
@ 2022-06-30 15:53     ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 15:53 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, Eric Dumazet, Vinod Koul, linux-phy, Jakub Kicinski,
	David S . Miller, netdev, Russell King, linux-kernel,
	Paolo Abeni, Kishon Vijay Abraham I, Madalin Bucur,
	Krzysztof Kozlowski, Rob Herring, linux-arm-kernel



On 6/28/22 10:09 PM, Rob Herring wrote:
> On Tue, 28 Jun 2022 18:13:30 -0400, Sean Anderson wrote:
>> This adds a binding for the SerDes module found on QorIQ processors. The
>> phy reference has two cells, one for the first lane and one for the
>> last. This should allow for good support of multi-lane protocols when
>> (if) they are added. There is no protocol option, because the driver is
>> designed to be able to completely reconfigure lanes at runtime.
>> Generally, the phy consumer can select the appropriate protocol using
>> set_mode. For the most part there is only one protocol controller
>> (consumer) per lane/protocol combination. The exception to this is the
>> B4860 processor, which has some lanes which can be connected to
>> multiple MACs. For that processor, I anticipate the easiest way to
>> resolve this will be to add an additional cell with a "protocol
>> controller instance" property.
>> 
>> Each serdes has a unique set of supported protocols (and lanes). The
>> support matrix is stored in the driver and is selected based on the
>> compatible string. It is anticipated that a new compatible string will
>> need to be added for each serdes on each SoC that drivers support is
>> added for. There is no "generic" compatible string for this reason.
>> 
>> There are two PLLs, each of which can be used as the master clock for
>> each lane. Each PLL has its own reference. For the moment they are
>> required, because it simplifies the driver implementation. Absent
>> reference clocks can be modeled by a fixed-clock with a rate of 0.
>> 
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>> 
>> Changes in v2:
>> - Add #clock-cells. This will allow using assigned-clocks* to configure
>>   the PLLs.
>> - Allow a value of 1 for phy-cells. This allows for compatibility with
>>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>>   binding.
>> - Document phy cells in the description
>> - Document the structure of the compatible strings
>> - Fix example binding having too many cells in regs
>> - Move compatible first
>> - Refer to the device in the documentation, rather than the binding
>> - Remove minItems
>> - Rename to fsl,lynx-10g.yaml
>> - Use list for clock-names
>> 
>>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>>  1 file changed, 93 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>> 
> 
> My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
> on your patch (DT_CHECKER_FLAGS is new in v5.13):
> 
> yamllint warnings/errors:
> 
> dtschema/dtc warnings/errors:
> /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: patternProperties:^thermistor@:properties:adi,excitation-current-nanoamp: '$ref' should not be valid under {'const': '$ref'}
> 	hint: Standard unit suffix properties don't need a type $ref
> 	from schema $id: http://devicetree.org/meta-schemas/core.yaml#
> /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml: ignoring, error in schema: patternProperties: ^thermistor@: properties: adi,excitation-current-nanoamp
> Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.example.dtb:0:0: /example-0/spi/ltc2983@0: failed to match any schema with compatible: ['adi,ltc2983']

This looks unrelated to this patch AFAICT.

--Sean

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-06-28 22:13 ` [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
@ 2022-06-30 15:56   ` Ioana Ciornei
  2022-06-30 18:11     ` Sean Anderson
  2022-07-05  6:12   ` Vinod Koul
  1 sibling, 1 reply; 32+ messages in thread
From: Ioana Ciornei @ 2022-06-30 15:56 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-doc, linux-phy


Hi Sean,

I am in the process of adding the necessary configuration for this
driver to work on a LS1088A based board. At the moment, I can see that
the lane's PLL is changed depending on the SFP module plugged, I have a
CDR lock but no PCS link.

I'll let you know when I get to the bottom of this.

I didn't go through the driver in detail but added some comments below.

On Tue, Jun 28, 2022 at 06:13:33PM -0400, Sean Anderson wrote:
> This adds support for the Lynx 10G "SerDes" devices found on various NXP
> QorIQ SoCs. There may be up to four SerDes devices on each SoC, each
> supporting up to eight lanes. Protocol support for each SerDes is highly
> heterogeneous, with each SoC typically having a totally different
> selection of supported protocols for each lane. Additionally, the SerDes
> devices on each SoC also have differing support. One SerDes will
> typically support Ethernet on most lanes, while the other will typically
> support PCIe on most lanes.
> 

(...)

> +For example, the configuration for SerDes1 of the LS1046A is::
> +
> +    static const struct lynx_mode ls1046a_modes1[] = {
> +        CONF_SINGLE(1, PCIE, 0x0, 1, 0b001),
> +        CONF_1000BASEKX(0, 0x8, 0, 0b001),
> +        CONF_SGMII25KX(1, 0x8, 1, 0b001),
> +        CONF_SGMII25KX(2, 0x8, 2, 0b001),
> +        CONF_SGMII25KX(3, 0x8, 3, 0b001),
> +        CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001),
> +        CONF_XFI(2, 0xB, 0, 0b010),
> +        CONF_XFI(3, 0xB, 1, 0b001),
> +    };
> +
> +    static const struct lynx_conf ls1046a_conf1 = {
> +        .modes = ls1046a_modes1,
> +        .mode_count = ARRAY_SIZE(ls1046a_modes1),
> +        .lanes = 4,
> +        .endian = REGMAP_ENDIAN_BIG,
> +    };
> +
> +There is an additional set of configuration for SerDes2, which supports a
> +different set of modes. Both configurations should be added to the match
> +table::
> +
> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },

I am not 100% sure that different compatible strings are needed for each
SerDes block. I know that in the 'supported SerDes options' tables only
a certain list of combinations are present, different for each block.
Even with this, I find it odd to believe that, for example, SerDes block
2 from LS1046A was instantiated so that it does not support any Ethernet
protocols.

I'll ask around to see if indeed this happens.

> +
> +Supporting Protocols
> +--------------------
> +
> +Each protocol is a combination of values which must be programmed into the lane
> +registers. To add a new protocol, first add it to :c:type:`enum lynx_protocol
> +<lynx_protocol>`. If it is in ``UNSUPPORTED_PROTOS``, remove it. Add a new
> +entry to `lynx_proto_params`, and populate the appropriate fields. You may need
> +to add some new members to support new fields. Modify `lynx_lookup_proto` to
> +map the :c:type:`enum phy_mode <phy_mode>` to :c:type:`enum lynx_protocol
> +<lynx_protocol>`. Ensure that :c:func:`lynx_proto_mode_mask` and
> +:c:func:`lynx_proto_mode_shift` have been updated with support for your
> +protocol.
> +
> +You may need to modify :c:func:`lynx_set_mode` in order to support your
> +procotol. This can happen when you have added members to :c:type:`struct
> +lynx_proto_params <lynx_proto_params>`. It can also happen if you have specific
> +clocking requirements, or protocol-specific registers to program.
> +
> +Internal API Reference
> +----------------------
> +
> +.. kernel-doc:: drivers/phy/freescale/phy-fsl-lynx-10g.c
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ca95b1833b97..ef65e2acdb48 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7977,6 +7977,12 @@ F:	drivers/ptp/ptp_qoriq.c
>  F:	drivers/ptp/ptp_qoriq_debugfs.c
>  F:	include/linux/fsl/ptp_qoriq.h
>  
> +FREESCALE QORIQ SERDES DRIVER
> +M:	Sean Anderson <sean.anderson@seco.com>
> +S:	Maintained
> +F:	Documentation/driver-api/phy/qoriq.rst
> +F:	drivers/phy/freescale/phy-qoriq.c
> +

These file names have to be changed as well.

(...)

> +enum lynx_protocol {
> +	LYNX_PROTO_NONE = 0,
> +	LYNX_PROTO_SGMII,
> +	LYNX_PROTO_SGMII25,
> +	LYNX_PROTO_1000BASEKX,
> +	LYNX_PROTO_QSGMII,
> +	LYNX_PROTO_XFI,
> +	LYNX_PROTO_10GKR,
> +	LYNX_PROTO_PCIE, /* Not implemented */
> +	LYNX_PROTO_SATA, /* Not implemented */
> +	LYNX_PROTO_LAST,
> +};
> +
> +static const char lynx_proto_str[][16] = {
> +	[LYNX_PROTO_NONE] = "unknown",
> +	[LYNX_PROTO_SGMII] = "SGMII",
> +	[LYNX_PROTO_SGMII25] = "2.5G SGMII",
> +	[LYNX_PROTO_1000BASEKX] = "1000Base-KX",
> +	[LYNX_PROTO_QSGMII] = "QSGMII",
> +	[LYNX_PROTO_XFI] = "XFI",
> +	[LYNX_PROTO_10GKR] = "10GBase-KR",
> +	[LYNX_PROTO_PCIE] = "PCIe",
> +	[LYNX_PROTO_SATA] = "SATA",
> +};

> +
> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))

From what I know, -KX and -KR need software level link training.
Did you test these protocols?

I would be much more comfortable if we only add to the supported
protocols list what was tested.

(...)

> +	/* Deselect anything configured by the RCW/bootloader */
> +	for (i = 0; i < conf->mode_count; i++) {
> +		const struct lynx_mode *mode = &conf->modes[i];
> +		u32 pccr = lynx_read(serdes, PCCRn(mode->pccr));
> +
> +		if (lynx_proto_mode_get(mode, pccr) == mode->cfg) {
> +			if (mode->protos & UNSUPPORTED_PROTOS) {
> +				/* Don't mess with modes we don't support */
> +				serdes->used_lanes |= mode->lanes;
> +				if (grabbed_clocks)
> +					continue;
> +
> +				grabbed_clocks = true;
> +				clk_prepare_enable(serdes->pll[0].hw.clk);
> +				clk_prepare_enable(serdes->pll[1].hw.clk);
> +				clk_rate_exclusive_get(serdes->pll[0].hw.clk);
> +				clk_rate_exclusive_get(serdes->pll[1].hw.clk);

Am I understanding correctly that if you encounter a protocol which is
not supported (PCIe, SATA) both PLLs will not be capable of changing,
right?

Why aren't you just getting exclusivity on the PLL that is actually used
by a lane configured with a protocol which the driver does not support?


> +			} else {
> +				/* Otherwise, clear out the existing config */
> +				pccr = lynx_proto_mode_prep(mode, pccr,
> +							    LYNX_PROTO_NONE);
> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
> +			}

Hmmm, do you need this?

Wouldn't it be better to just leave the lane untouched (as it was setup
by the RCW) just in case the lane is not requested by a consumer driver
but actually used in practice. I am referring to the case in which some
ethernet nodes have the 'phys' property, some don't.

If you really need this, maybe you can move it in the phy_init callback.

> +
> +			/* Disable the SGMII PCS until we're ready for it */
> +			if (mode->protos & LYNX_PROTO_SGMII) {
> +				u32 cr1;
> +
> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
> +			}
> +		}
> +	}
> +
> +	/* Power off all lanes; used ones will be powered on later */
> +	for (i = 0; i < conf->lanes; i++)
> +		lynx_power_off_lane(serdes, i);

This means that you are powering-off any lane, PCIe/SATA lanes
which are not integrated with this driver at all, right?.
I don't think we want to break stuff that used to be working.

(...)

> +MODULE_DEVICE_TABLE(of, lynx_of_match);
> +
> +static struct platform_driver lynx_driver = {
> +	.probe = lynx_probe,
> +	.driver = {
> +		.name = "qoriq_serdes",

Please change the driver's name as well.

Ioana

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

* Re: [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties
  2022-06-28 22:13 ` [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties Sean Anderson
@ 2022-06-30 16:01   ` Rob Herring
  2022-06-30 16:11     ` Sean Anderson
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2022-06-30 16:01 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Krzysztof Kozlowski, devicetree

On Tue, Jun 28, 2022 at 06:13:32PM -0400, Sean Anderson wrote:
> At the moment, mEMACs are configured almost completely based on the
> phy-connection-type. That is, if the phy interface is RGMII, it assumed
> that RGMII is supported. For some interfaces, it is assumed that the
> RCW/bootloader has set up the SerDes properly. This is generally OK, but
> restricts runtime reconfiguration. The actual link state is never
> reported.
> 
> To address these shortcomings, the driver will need additional
> information. First, it needs to know how to access the PCS/PMAs (in
> order to configure them and get the link status). The SGMII PCS/PMA is
> the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as
> well. The XFI (and 1GBase-KR) PCS/PMA is a c45 "phy" which sits on the
> same MDIO bus as SGMII PCS/PMA. By default they will have conflicting
> addresses, but they are also not enabled at the same time by default.
> Therefore, we can let the XFI PCS/PMA be the default when
> phy-connection-type is xgmii. This will allow for
> backwards-compatibility.
> 
> QSGMII, however, cannot work with the current binding. This is because
> the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the
> moment this is worked around by having every MAC write to the PCS/PMA
> addresses (without checking if they are present). This only works if
> each MAC has the same configuration, and only if we don't need to know
> the status. Because the QSGMII PCS/PMA will typically be located on a
> different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback
> for the QSGMII PCS/PMA.
> 
> mEMACs (across all SoCs) support the following protocols:
> 
> - MII
> - RGMII
> - SGMII, 1000Base-X, and 1000Base-KX
> - 2500Base-X (aka 2.5G SGMII)
> - QSGMII
> - 10GBase-R (aka XFI) and 10GBase-KR
> - XAUI and HiGig
> 
> Each line documents a set of orthogonal protocols (e.g. XAUI is
> supported if and only if HiGig is supported). Additionally,
> 
> - XAUI implies support for 10GBase-R
> - 10GBase-R is supported if and only if RGMII is not supported
> - 2500Base-X implies support for 1000Base-X
> - MII implies support for RGMII
> 
> To switch between different protocols, we must reconfigure the SerDes.
> This is done by using the standard phys property. We can also use it to
> validate whether different protocols are supported (e.g. using
> phy_validate). This will work for serial protocols, but not RGMII or
> MII. Additionally, we still need to be compatible when there is no
> SerDes.
> 
> While we can detect 10G support by examining the port speed (as set by
> fsl,fman-10g-port), we cannot determine support for any of the other
> protocols based on the existing binding. In fact, the binding works
> against us in some respects, because pcsphy-handle is required even if
> there is no possible PCS/PMA for that MAC. To allow for backwards-
> compatibility, we use a boolean-style property for RGMII (instead of
> presence/absence-style). When the property for RGMII is missing, we will
> assume that it is supported. The exception is MII, since no existing
> device trees use it (as far as I could tell).
> 
> Unfortunately, QSGMII support will be broken for old device trees. There
> is nothing we can do about this because of the PCS/PMA situation (as
> described above).
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - Better document how we select which PCS to use in the default case
> 
>  .../bindings/net/fsl,fman-dtsec.yaml          | 52 +++++++++++++++++--
>  .../devicetree/bindings/net/fsl-fman.txt      |  5 +-
>  2 files changed, 51 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> index 809df1589f20..ecb772258164 100644
> --- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> @@ -85,9 +85,41 @@ properties:
>      $ref: /schemas/types.yaml#/definitions/phandle
>      description: A reference to the IEEE1588 timer
>  
> +  phys:
> +    description: A reference to the SerDes lane(s)
> +    maxItems: 1
> +
> +  phy-names:
> +    items:
> +      - const: serdes
> +
>    pcsphy-handle:
> -    $ref: /schemas/types.yaml#/definitions/phandle
> -    description: A reference to the PCS (typically found on the SerDes)
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    minItems: 1
> +    maxItems: 3

What determines how many entries?

> +    description: |
> +      A reference to the various PCSs (typically found on the SerDes). If
> +      pcs-names is absent, and phy-connection-type is "xgmii", then the first
> +      reference will be assumed to be for "xfi". Otherwise, if pcs-names is
> +      absent, then the first reference will be assumed to be for "sgmii".
> +
> +  pcs-names:
> +    $ref: /schemas/types.yaml#/definitions/string-array
> +    minItems: 1
> +    maxItems: 3
> +    contains:
> +      enum:
> +        - sgmii
> +        - qsgmii
> +        - xfi

This means '"foo", "xfi", "bar"' is valid. I think you want to 
s/contains/items/.

> +    description: The type of each PCS in pcsphy-handle.
> +

> +  rgmii:
> +    enum: [0, 1]
> +    description: 1 indicates RGMII is supported, and 0 indicates it is not.
> +
> +  mii:
> +    description: If present, indicates that MII is supported.

Types? Need vendor prefixes.

Are these board specific or SoC specific? Properties are appropriate for 
the former. The latter case should be implied by the compatible string.

>  
>    tbi-handle:
>      $ref: /schemas/types.yaml#/definitions/phandle
> @@ -100,6 +132,10 @@ required:
>    - fsl,fman-ports
>    - ptp-timer
>  
> +dependencies:
> +  pcs-names: [pcsphy-handle]
> +  mii: [rgmii]
> +
>  allOf:
>    - $ref: "ethernet-controller.yaml#"
>    - if:
> @@ -117,7 +153,11 @@ allOf:
>              const: fsl,fman-memac
>      then:
>        required:
> -        - pcsphy-handle
> +        - rgmii
> +    else:
> +      properties:
> +        rgmii: false
> +        mii: false
>  
>  unevaluatedProperties: false
>  
> @@ -138,7 +178,11 @@ examples:
>              reg = <0xe8000 0x1000>;
>              fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>;
>              ptp-timer = <&ptp_timer0>;
> -            pcsphy-handle = <&pcsphy4>;
> +            pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs1>;
> +            pcs-names = "sgmii", "qsgmii";
> +            rgmii = <0>;
>              phy-handle = <&sgmii_phy1>;
>              phy-connection-type = "sgmii";
> +            phys = <&serdes1 1>;
> +            phy-names = "serdes";
>      };
> diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt
> index b9055335db3b..bda4b41af074 100644
> --- a/Documentation/devicetree/bindings/net/fsl-fman.txt
> +++ b/Documentation/devicetree/bindings/net/fsl-fman.txt
> @@ -320,8 +320,9 @@ For internal PHY device on internal mdio bus, a PHY node should be created.
>  See the definition of the PHY node in booting-without-of.txt for an
>  example of how to define a PHY (Internal PHY has no interrupt line).
>  - For "fsl,fman-mdio" compatible internal mdio bus, the PHY is TBI PHY.
> -- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY,
> -  PCS PHY addr must be '0'.
> +- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY.
> +  The PCS PHY address should correspond to the value of the appropriate
> +  MDEV_PORT.
>  
>  EXAMPLE
>  
> -- 
> 2.35.1.1320.gc452695387.dirty
> 
> 

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

* Re: [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties
  2022-06-30 16:01   ` Rob Herring
@ 2022-06-30 16:11     ` Sean Anderson
  2022-07-12 19:36       ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 16:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Krzysztof Kozlowski, devicetree

Hi Rob,

On 6/30/22 12:01 PM, Rob Herring wrote:
> On Tue, Jun 28, 2022 at 06:13:32PM -0400, Sean Anderson wrote:
>> At the moment, mEMACs are configured almost completely based on the
>> phy-connection-type. That is, if the phy interface is RGMII, it assumed
>> that RGMII is supported. For some interfaces, it is assumed that the
>> RCW/bootloader has set up the SerDes properly. This is generally OK, but
>> restricts runtime reconfiguration. The actual link state is never
>> reported.
>> 
>> To address these shortcomings, the driver will need additional
>> information. First, it needs to know how to access the PCS/PMAs (in
>> order to configure them and get the link status). The SGMII PCS/PMA is
>> the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as
>> well. The XFI (and 1GBase-KR) PCS/PMA is a c45 "phy" which sits on the
>> same MDIO bus as SGMII PCS/PMA. By default they will have conflicting
>> addresses, but they are also not enabled at the same time by default.
>> Therefore, we can let the XFI PCS/PMA be the default when
>> phy-connection-type is xgmii. This will allow for
>> backwards-compatibility.
>> 
>> QSGMII, however, cannot work with the current binding. This is because
>> the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the
>> moment this is worked around by having every MAC write to the PCS/PMA
>> addresses (without checking if they are present). This only works if
>> each MAC has the same configuration, and only if we don't need to know
>> the status. Because the QSGMII PCS/PMA will typically be located on a
>> different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback
>> for the QSGMII PCS/PMA.
>> 
>> mEMACs (across all SoCs) support the following protocols:
>> 
>> - MII
>> - RGMII
>> - SGMII, 1000Base-X, and 1000Base-KX
>> - 2500Base-X (aka 2.5G SGMII)
>> - QSGMII
>> - 10GBase-R (aka XFI) and 10GBase-KR
>> - XAUI and HiGig
>> 
>> Each line documents a set of orthogonal protocols (e.g. XAUI is
>> supported if and only if HiGig is supported). Additionally,
>> 
>> - XAUI implies support for 10GBase-R
>> - 10GBase-R is supported if and only if RGMII is not supported
>> - 2500Base-X implies support for 1000Base-X
>> - MII implies support for RGMII
>> 
>> To switch between different protocols, we must reconfigure the SerDes.
>> This is done by using the standard phys property. We can also use it to
>> validate whether different protocols are supported (e.g. using
>> phy_validate). This will work for serial protocols, but not RGMII or
>> MII. Additionally, we still need to be compatible when there is no
>> SerDes.
>> 
>> While we can detect 10G support by examining the port speed (as set by
>> fsl,fman-10g-port), we cannot determine support for any of the other
>> protocols based on the existing binding. In fact, the binding works
>> against us in some respects, because pcsphy-handle is required even if
>> there is no possible PCS/PMA for that MAC. To allow for backwards-
>> compatibility, we use a boolean-style property for RGMII (instead of
>> presence/absence-style). When the property for RGMII is missing, we will
>> assume that it is supported. The exception is MII, since no existing
>> device trees use it (as far as I could tell).
>> 
>> Unfortunately, QSGMII support will be broken for old device trees. There
>> is nothing we can do about this because of the PCS/PMA situation (as
>> described above).
>> 
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>> 
>> Changes in v2:
>> - Better document how we select which PCS to use in the default case
>> 
>>  .../bindings/net/fsl,fman-dtsec.yaml          | 52 +++++++++++++++++--
>>  .../devicetree/bindings/net/fsl-fman.txt      |  5 +-
>>  2 files changed, 51 insertions(+), 6 deletions(-)
>> 
>> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> index 809df1589f20..ecb772258164 100644
>> --- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> @@ -85,9 +85,41 @@ properties:
>>      $ref: /schemas/types.yaml#/definitions/phandle
>>      description: A reference to the IEEE1588 timer
>>  
>> +  phys:
>> +    description: A reference to the SerDes lane(s)
>> +    maxItems: 1
>> +
>> +  phy-names:
>> +    items:
>> +      - const: serdes
>> +
>>    pcsphy-handle:
>> -    $ref: /schemas/types.yaml#/definitions/phandle
>> -    description: A reference to the PCS (typically found on the SerDes)
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>> +    minItems: 1
>> +    maxItems: 3
> 
> What determines how many entries?

It depends on what the particular MAC supports. From what I can tell, the following
combinations are valid:

- Neither SGMII, QSGMII, or XFI
- Just SGMII
- Just QSGMII
- SGMII and QSGMII
- SGMII and XFI
- All of SGMII, QSGMII, and XFI

All of these are used on different SoCs.

>> +    description: |
>> +      A reference to the various PCSs (typically found on the SerDes). If
>> +      pcs-names is absent, and phy-connection-type is "xgmii", then the first
>> +      reference will be assumed to be for "xfi". Otherwise, if pcs-names is
>> +      absent, then the first reference will be assumed to be for "sgmii".
>> +
>> +  pcs-names:
>> +    $ref: /schemas/types.yaml#/definitions/string-array
>> +    minItems: 1
>> +    maxItems: 3
>> +    contains:
>> +      enum:
>> +        - sgmii
>> +        - qsgmii
>> +        - xfi
> 
> This means '"foo", "xfi", "bar"' is valid. I think you want to 
> s/contains/items/.
> 
>> +    description: The type of each PCS in pcsphy-handle.
>> +
> 
>> +  rgmii:
>> +    enum: [0, 1]
>> +    description: 1 indicates RGMII is supported, and 0 indicates it is not.
>> +
>> +  mii:
>> +    description: If present, indicates that MII is supported.
> 
> Types? Need vendor prefixes.

OK.

> Are these board specific or SoC specific? Properties are appropriate for 
> the former. The latter case should be implied by the compatible string.

Unfortunately, there are not existing specific compatible strings for each
device in each SoC. I suppose those could be added; however, this basically
reflects how each device is hooked up. E.g. on one SoC a device would be
connected to the RGMII pins, but not on another SoC. The MAC itself still
has hardware support for RGMII, but such a configuration would not function.

--Sean

>>  
>>    tbi-handle:
>>      $ref: /schemas/types.yaml#/definitions/phandle
>> @@ -100,6 +132,10 @@ required:
>>    - fsl,fman-ports
>>    - ptp-timer
>>  
>> +dependencies:
>> +  pcs-names: [pcsphy-handle]
>> +  mii: [rgmii]
>> +
>>  allOf:
>>    - $ref: "ethernet-controller.yaml#"
>>    - if:
>> @@ -117,7 +153,11 @@ allOf:
>>              const: fsl,fman-memac
>>      then:
>>        required:
>> -        - pcsphy-handle
>> +        - rgmii
>> +    else:
>> +      properties:
>> +        rgmii: false
>> +        mii: false
>>  
>>  unevaluatedProperties: false
>>  
>> @@ -138,7 +178,11 @@ examples:
>>              reg = <0xe8000 0x1000>;
>>              fsl,fman-ports = <&fman0_rx_0x0c &fman0_tx_0x2c>;
>>              ptp-timer = <&ptp_timer0>;
>> -            pcsphy-handle = <&pcsphy4>;
>> +            pcsphy-handle = <&pcsphy4>, <&qsgmiib_pcs1>;
>> +            pcs-names = "sgmii", "qsgmii";
>> +            rgmii = <0>;
>>              phy-handle = <&sgmii_phy1>;
>>              phy-connection-type = "sgmii";
>> +            phys = <&serdes1 1>;
>> +            phy-names = "serdes";
>>      };
>> diff --git a/Documentation/devicetree/bindings/net/fsl-fman.txt b/Documentation/devicetree/bindings/net/fsl-fman.txt
>> index b9055335db3b..bda4b41af074 100644
>> --- a/Documentation/devicetree/bindings/net/fsl-fman.txt
>> +++ b/Documentation/devicetree/bindings/net/fsl-fman.txt
>> @@ -320,8 +320,9 @@ For internal PHY device on internal mdio bus, a PHY node should be created.
>>  See the definition of the PHY node in booting-without-of.txt for an
>>  example of how to define a PHY (Internal PHY has no interrupt line).
>>  - For "fsl,fman-mdio" compatible internal mdio bus, the PHY is TBI PHY.
>> -- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY,
>> -  PCS PHY addr must be '0'.
>> +- For "fsl,fman-memac-mdio" compatible internal mdio bus, the PHY is PCS PHY.
>> +  The PCS PHY address should correspond to the value of the appropriate
>> +  MDEV_PORT.
>>  
>>  EXAMPLE
>>  
>> -- 
>> 2.35.1.1320.gc452695387.dirty
>> 
>> 
> 

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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
  2022-06-29  2:09   ` Rob Herring
@ 2022-06-30 17:27   ` Rob Herring
  2022-06-30 18:01     ` Sean Anderson
  1 sibling, 1 reply; 32+ messages in thread
From: Rob Herring @ 2022-06-30 17:27 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Kishon Vijay Abraham I, Krzysztof Kozlowski,
	Vinod Koul, devicetree, linux-phy

On Tue, Jun 28, 2022 at 06:13:30PM -0400, Sean Anderson wrote:
> This adds a binding for the SerDes module found on QorIQ processors. The
> phy reference has two cells, one for the first lane and one for the
> last. This should allow for good support of multi-lane protocols when
> (if) they are added. There is no protocol option, because the driver is
> designed to be able to completely reconfigure lanes at runtime.
> Generally, the phy consumer can select the appropriate protocol using
> set_mode. For the most part there is only one protocol controller
> (consumer) per lane/protocol combination. The exception to this is the
> B4860 processor, which has some lanes which can be connected to
> multiple MACs. For that processor, I anticipate the easiest way to
> resolve this will be to add an additional cell with a "protocol
> controller instance" property.
> 
> Each serdes has a unique set of supported protocols (and lanes). The
> support matrix is stored in the driver and is selected based on the
> compatible string. It is anticipated that a new compatible string will
> need to be added for each serdes on each SoC that drivers support is
> added for. There is no "generic" compatible string for this reason.
> 
> There are two PLLs, each of which can be used as the master clock for
> each lane. Each PLL has its own reference. For the moment they are
> required, because it simplifies the driver implementation. Absent
> reference clocks can be modeled by a fixed-clock with a rate of 0.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - Add #clock-cells. This will allow using assigned-clocks* to configure
>   the PLLs.
> - Allow a value of 1 for phy-cells. This allows for compatibility with
>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>   binding.
> - Document phy cells in the description
> - Document the structure of the compatible strings
> - Fix example binding having too many cells in regs
> - Move compatible first
> - Refer to the device in the documentation, rather than the binding
> - Remove minItems
> - Rename to fsl,lynx-10g.yaml
> - Use list for clock-names
> 
>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>  1 file changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> 
> diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> new file mode 100644
> index 000000000000..b5a6f631df9f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> @@ -0,0 +1,93 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP Lynx 10G SerDes
> +
> +maintainers:
> +  - Sean Anderson <sean.anderson@seco.com>
> +
> +description: |
> +  These Lynx "SerDes" devices are found in NXP's QorIQ line of processors. The
> +  SerDes provides up to eight lanes. Each lane may be configured individually,
> +  or may be combined with adjacent lanes for a multi-lane protocol. The SerDes
> +  supports a variety of protocols, including up to 10G Ethernet, PCIe, SATA, and
> +  others. The specific protocols supported for each lane depend on the
> +  particular SoC.
> +
> +properties:
> +  compatible:
> +    description: |
> +      Each compatible is of the form "fsl,<soc-name>-serdes-<instance>".
> +      Although many registers are compatible between different SoCs, the
> +      supported protocols and lane assignments tend to be unique to each SerDes.
> +      Additionally, the method of activating protocols may also be unique.

We typically have properties for handling these variables. Numbering 
instances is something we avoid.

> +      Because of this, each SerDes instance will need its own compatible string.

> +      In cases where two SoCs share the same SerDes implementation (such as the
> +      LS1046A and LS1026A), both SoCs should share the same compatible strings.

No, not unless the 2 parts are just fuse or package pinout differences. 
Otherwise, there's always the chance they are not 'the same' and have 
different bugs.

You could have "fsl,ls1046a-serdes", "fsl,ls1026a-serdes" (whichever SoC 
is older last) if you think they are 'the same'.


> +    enum:
> +      - fsl,ls1046a-serdes-1
> +      - fsl,ls1046a-serdes-2
> +
> +  "#clock-cells":
> +    const: 1
> +    description: |
> +      The cell contains the index of the PLL, starting from 0. Note that when
> +      assigning a rate to a PLL, the PLLs' rates are divided by 1000 to avoid
> +      overflow. A rate of 5000000 corresponds to 5GHz.
> +
> +  "#phy-cells":
> +    minimum: 1
> +    maximum: 2
> +    description: |
> +      The cells contain the following arguments:
> +      - The first lane in the group. Lanes are numbered based on the register
> +        offsets, not the I/O ports. This corresponds to the letter-based ("Lane
> +        A") naming scheme, and not the number-based ("Lane 0") naming scheme. On
> +        most SoCs, "Lane A" is "Lane 0", but not always.
> +      - Last lane. For single-lane protocols, this should be the same as the
> +        first lane.
> +      If no lanes in a SerDes can be grouped, then #phy-cells may be 1, and the
> +      first cell will specify the only lane in the group.

Usually when we have per lane phys, the consumer side will have a 'phys' 
entry per lane.

Having a variable number of cells isn't great either. We usually only do 
that when we have to extend an existing binding.

> +
> +  clocks:
> +    maxItems: 2
> +    description: |
> +      Clock for each PLL reference clock input.
> +
> +  clock-names:
> +    items:
> +      - enum: &clocks
> +          - ref0
> +          - ref1
> +      - enum: *clocks

Whoa, there's a first. Don't use YAML anchors and references. We only 
support JSON subset of YAML.

> +
> +  reg:
> +    maxItems: 1
> +
> +required:
> +  - "#clock-cells"
> +  - "#phy-cells"
> +  - compatible
> +  - clocks
> +  - clock-names
> +  - reg
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    serdes1: phy@1ea0000 {
> +      #clock-cells = <1>;
> +      #phy-cells = <2>;
> +      compatible = "fsl,ls1046a-serdes-1";
> +      reg = <0x1ea0000 0x2000>;
> +      clocks = <&clk_100mhz>, <&clk_156mhz>;
> +      clock-names = "ref0", "ref1";
> +      assigned-clocks = <&serdes1 0>;
> +      assigned-clock-rates = <5000000>;
> +    };
> +
> +...
> -- 
> 2.35.1.1320.gc452695387.dirty
> 
> 

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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-30 17:27   ` Rob Herring
@ 2022-06-30 18:01     ` Sean Anderson
  2022-06-30 18:08       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 18:01 UTC (permalink / raw)
  To: Rob Herring
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Kishon Vijay Abraham I, Krzysztof Kozlowski,
	Vinod Koul, devicetree, linux-phy

Hi Rob,

On 6/30/22 1:27 PM, Rob Herring wrote:
> On Tue, Jun 28, 2022 at 06:13:30PM -0400, Sean Anderson wrote:
>> This adds a binding for the SerDes module found on QorIQ processors. The
>> phy reference has two cells, one for the first lane and one for the
>> last. This should allow for good support of multi-lane protocols when
>> (if) they are added. There is no protocol option, because the driver is
>> designed to be able to completely reconfigure lanes at runtime.
>> Generally, the phy consumer can select the appropriate protocol using
>> set_mode. For the most part there is only one protocol controller
>> (consumer) per lane/protocol combination. The exception to this is the
>> B4860 processor, which has some lanes which can be connected to
>> multiple MACs. For that processor, I anticipate the easiest way to
>> resolve this will be to add an additional cell with a "protocol
>> controller instance" property.
>> 
>> Each serdes has a unique set of supported protocols (and lanes). The
>> support matrix is stored in the driver and is selected based on the
>> compatible string. It is anticipated that a new compatible string will
>> need to be added for each serdes on each SoC that drivers support is
>> added for. There is no "generic" compatible string for this reason.
>> 
>> There are two PLLs, each of which can be used as the master clock for
>> each lane. Each PLL has its own reference. For the moment they are
>> required, because it simplifies the driver implementation. Absent
>> reference clocks can be modeled by a fixed-clock with a rate of 0.
>> 
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>> 
>> Changes in v2:
>> - Add #clock-cells. This will allow using assigned-clocks* to configure
>>   the PLLs.
>> - Allow a value of 1 for phy-cells. This allows for compatibility with
>>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>>   binding.
>> - Document phy cells in the description
>> - Document the structure of the compatible strings
>> - Fix example binding having too many cells in regs
>> - Move compatible first
>> - Refer to the device in the documentation, rather than the binding
>> - Remove minItems
>> - Rename to fsl,lynx-10g.yaml
>> - Use list for clock-names
>> 
>>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>>  1 file changed, 93 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>> 
>> diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>> new file mode 100644
>> index 000000000000..b5a6f631df9f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>> @@ -0,0 +1,93 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: NXP Lynx 10G SerDes
>> +
>> +maintainers:
>> +  - Sean Anderson <sean.anderson@seco.com>
>> +
>> +description: |
>> +  These Lynx "SerDes" devices are found in NXP's QorIQ line of processors. The
>> +  SerDes provides up to eight lanes. Each lane may be configured individually,
>> +  or may be combined with adjacent lanes for a multi-lane protocol. The SerDes
>> +  supports a variety of protocols, including up to 10G Ethernet, PCIe, SATA, and
>> +  others. The specific protocols supported for each lane depend on the
>> +  particular SoC.
>> +
>> +properties:
>> +  compatible:
>> +    description: |
>> +      Each compatible is of the form "fsl,<soc-name>-serdes-<instance>".
>> +      Although many registers are compatible between different SoCs, the
>> +      supported protocols and lane assignments tend to be unique to each SerDes.
>> +      Additionally, the method of activating protocols may also be unique.
> 
> We typically have properties for handling these variables. Numbering 
> instances is something we avoid.

On v1, Krzysztof said that this was a better route...

>> +      Because of this, each SerDes instance will need its own compatible string.
> 
>> +      In cases where two SoCs share the same SerDes implementation (such as the
>> +      LS1046A and LS1026A), both SoCs should share the same compatible strings.
> 
> No, not unless the 2 parts are just fuse or package pinout differences. 
> Otherwise, there's always the chance they are not 'the same' and have 
> different bugs.
>
> You could have "fsl,ls1046a-serdes", "fsl,ls1026a-serdes" (whichever SoC 
> is older last) if you think they are 'the same'.

Fine by me.

>> +    enum:
>> +      - fsl,ls1046a-serdes-1
>> +      - fsl,ls1046a-serdes-2
>> +
>> +  "#clock-cells":
>> +    const: 1
>> +    description: |
>> +      The cell contains the index of the PLL, starting from 0. Note that when
>> +      assigning a rate to a PLL, the PLLs' rates are divided by 1000 to avoid
>> +      overflow. A rate of 5000000 corresponds to 5GHz.
>> +
>> +  "#phy-cells":
>> +    minimum: 1
>> +    maximum: 2
>> +    description: |
>> +      The cells contain the following arguments:
>> +      - The first lane in the group. Lanes are numbered based on the register
>> +        offsets, not the I/O ports. This corresponds to the letter-based ("Lane
>> +        A") naming scheme, and not the number-based ("Lane 0") naming scheme. On
>> +        most SoCs, "Lane A" is "Lane 0", but not always.
>> +      - Last lane. For single-lane protocols, this should be the same as the
>> +        first lane.
>> +      If no lanes in a SerDes can be grouped, then #phy-cells may be 1, and the
>> +      first cell will specify the only lane in the group.
> 
> Usually when we have per lane phys, the consumer side will have a 'phys' 
> entry per lane.

The problem is that it is hard to coordinate multiple lanes coming up at once. There
are particular ordering requirements when multiple lanes are grouped together:

> Note that if the lanes being reconfigured are grouped for multi-lane or synchronous
> mode, then the master source clock lane for the group must have TRST_B set to 1 
> after all the other lanes in the group. The master source clock lane of the group
> is indicated by LNmGCR0[1STLANE]=1. All lanes p<m (if LNmGCR1[TRSTDIR]=0) or p> (if LNmGCR1[TRSTDIR]=1) of the master source clock lane until the next lane with
> LNmGCR0[1STLANE]=1, or the end of the SerDes, are grouped with the master source
> clock lane. All grouped lanes must have the same setting of TRSTDIR.

This is trivial to do if we enable things as a "group" but not so easy if each lane
is a separate phy. In particular, we have to know up front whether lanes are being
grouped together in order to program 1STLANE correctly. I think the enable order
corresponds to the order in the dtb, but AFAIK that is not guaranteed by the phy
subsystem.

> Having a variable number of cells isn't great either. We usually only do 
> that when we have to extend an existing binding.

This is mainly to align closer to the lynx-28g binding. It can always be restricted.

>> +
>> +  clocks:
>> +    maxItems: 2
>> +    description: |
>> +      Clock for each PLL reference clock input.
>> +
>> +  clock-names:
>> +    items:
>> +      - enum: &clocks
>> +          - ref0
>> +          - ref1
>> +      - enum: *clocks
> 
> Whoa, there's a first. Don't use YAML anchors and references. We only 
> support JSON subset of YAML.

How else should this be expressed? I would like to allow both

clock-names = "ref0", "ref1";

and

clock-names = "ref1", "ref0";

ideally without repeating myself.

--Sean

>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +required:
>> +  - "#clock-cells"
>> +  - "#phy-cells"
>> +  - compatible
>> +  - clocks
>> +  - clock-names
>> +  - reg
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    serdes1: phy@1ea0000 {
>> +      #clock-cells = <1>;
>> +      #phy-cells = <2>;
>> +      compatible = "fsl,ls1046a-serdes-1";
>> +      reg = <0x1ea0000 0x2000>;
>> +      clocks = <&clk_100mhz>, <&clk_156mhz>;
>> +      clock-names = "ref0", "ref1";
>> +      assigned-clocks = <&serdes1 0>;
>> +      assigned-clock-rates = <5000000>;
>> +    };
>> +
>> +...
>> -- 
>> 2.35.1.1320.gc452695387.dirty
>> 
>> 
> 

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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-30 18:01     ` Sean Anderson
@ 2022-06-30 18:08       ` Krzysztof Kozlowski
  2022-06-30 18:16         ` Sean Anderson
  0 siblings, 1 reply; 32+ messages in thread
From: Krzysztof Kozlowski @ 2022-06-30 18:08 UTC (permalink / raw)
  To: Sean Anderson, Rob Herring
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Kishon Vijay Abraham I, Krzysztof Kozlowski,
	Vinod Koul, devicetree, linux-phy

On 30/06/2022 20:01, Sean Anderson wrote:
> Hi Rob,
> 
> On 6/30/22 1:27 PM, Rob Herring wrote:
>> On Tue, Jun 28, 2022 at 06:13:30PM -0400, Sean Anderson wrote:
>>> This adds a binding for the SerDes module found on QorIQ processors. The
>>> phy reference has two cells, one for the first lane and one for the
>>> last. This should allow for good support of multi-lane protocols when
>>> (if) they are added. There is no protocol option, because the driver is
>>> designed to be able to completely reconfigure lanes at runtime.
>>> Generally, the phy consumer can select the appropriate protocol using
>>> set_mode. For the most part there is only one protocol controller
>>> (consumer) per lane/protocol combination. The exception to this is the
>>> B4860 processor, which has some lanes which can be connected to
>>> multiple MACs. For that processor, I anticipate the easiest way to
>>> resolve this will be to add an additional cell with a "protocol
>>> controller instance" property.
>>>
>>> Each serdes has a unique set of supported protocols (and lanes). The
>>> support matrix is stored in the driver and is selected based on the
>>> compatible string. It is anticipated that a new compatible string will
>>> need to be added for each serdes on each SoC that drivers support is
>>> added for. There is no "generic" compatible string for this reason.
>>>
>>> There are two PLLs, each of which can be used as the master clock for
>>> each lane. Each PLL has its own reference. For the moment they are
>>> required, because it simplifies the driver implementation. Absent
>>> reference clocks can be modeled by a fixed-clock with a rate of 0.
>>>
>>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>>> ---
>>>
>>> Changes in v2:
>>> - Add #clock-cells. This will allow using assigned-clocks* to configure
>>>   the PLLs.
>>> - Allow a value of 1 for phy-cells. This allows for compatibility with
>>>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>>>   binding.
>>> - Document phy cells in the description
>>> - Document the structure of the compatible strings
>>> - Fix example binding having too many cells in regs
>>> - Move compatible first
>>> - Refer to the device in the documentation, rather than the binding
>>> - Remove minItems
>>> - Rename to fsl,lynx-10g.yaml
>>> - Use list for clock-names
>>>
>>>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>>>  1 file changed, 93 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>> new file mode 100644
>>> index 000000000000..b5a6f631df9f
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>> @@ -0,0 +1,93 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: NXP Lynx 10G SerDes
>>> +
>>> +maintainers:
>>> +  - Sean Anderson <sean.anderson@seco.com>
>>> +
>>> +description: |
>>> +  These Lynx "SerDes" devices are found in NXP's QorIQ line of processors. The
>>> +  SerDes provides up to eight lanes. Each lane may be configured individually,
>>> +  or may be combined with adjacent lanes for a multi-lane protocol. The SerDes
>>> +  supports a variety of protocols, including up to 10G Ethernet, PCIe, SATA, and
>>> +  others. The specific protocols supported for each lane depend on the
>>> +  particular SoC.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    description: |
>>> +      Each compatible is of the form "fsl,<soc-name>-serdes-<instance>".
>>> +      Although many registers are compatible between different SoCs, the
>>> +      supported protocols and lane assignments tend to be unique to each SerDes.
>>> +      Additionally, the method of activating protocols may also be unique.
>>
>> We typically have properties for handling these variables. Numbering 
>> instances is something we avoid.
> 
> On v1, Krzysztof said that this was a better route...

I commented about "-1" and "-2" saying you have to make them properties.
You disagreed and with long messages were convincing me that "-1" and
"-2" is the only reasonable approach. I never said it is a better route.
I explicitly asked in several places for defining these as properties,
not as compatibles.

You are twisting the entire discussion now.

Best regards,
Krzysztof

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-06-30 15:56   ` Ioana Ciornei
@ 2022-06-30 18:11     ` Sean Anderson
  2022-07-01 10:03       ` Ioana Ciornei
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 18:11 UTC (permalink / raw)
  To: Ioana Ciornei
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-doc, linux-phy



On 6/30/22 11:56 AM, Ioana Ciornei wrote:
> 
> Hi Sean,
> 
> I am in the process of adding the necessary configuration for this
> driver to work on a LS1088A based board. At the moment, I can see that
> the lane's PLL is changed depending on the SFP module plugged, I have a
> CDR lock but no PCS link.

I have a LS1088A board which I can test on.

> I'll let you know when I get to the bottom of this.
> 
> I didn't go through the driver in detail but added some comments below.
> 
> On Tue, Jun 28, 2022 at 06:13:33PM -0400, Sean Anderson wrote:
>> This adds support for the Lynx 10G "SerDes" devices found on various NXP
>> QorIQ SoCs. There may be up to four SerDes devices on each SoC, each
>> supporting up to eight lanes. Protocol support for each SerDes is highly
>> heterogeneous, with each SoC typically having a totally different
>> selection of supported protocols for each lane. Additionally, the SerDes
>> devices on each SoC also have differing support. One SerDes will
>> typically support Ethernet on most lanes, while the other will typically
>> support PCIe on most lanes.
>> 
> 
> (...)
> 
>> +For example, the configuration for SerDes1 of the LS1046A is::
>> +
>> +    static const struct lynx_mode ls1046a_modes1[] = {
>> +        CONF_SINGLE(1, PCIE, 0x0, 1, 0b001),
>> +        CONF_1000BASEKX(0, 0x8, 0, 0b001),
>> +        CONF_SGMII25KX(1, 0x8, 1, 0b001),
>> +        CONF_SGMII25KX(2, 0x8, 2, 0b001),
>> +        CONF_SGMII25KX(3, 0x8, 3, 0b001),
>> +        CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001),
>> +        CONF_XFI(2, 0xB, 0, 0b010),
>> +        CONF_XFI(3, 0xB, 1, 0b001),
>> +    };
>> +
>> +    static const struct lynx_conf ls1046a_conf1 = {
>> +        .modes = ls1046a_modes1,
>> +        .mode_count = ARRAY_SIZE(ls1046a_modes1),
>> +        .lanes = 4,
>> +        .endian = REGMAP_ENDIAN_BIG,
>> +    };
>> +
>> +There is an additional set of configuration for SerDes2, which supports a
>> +different set of modes. Both configurations should be added to the match
>> +table::
>> +
>> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
>> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
> 
> I am not 100% sure that different compatible strings are needed for each
> SerDes block. I know that in the 'supported SerDes options' tables only
> a certain list of combinations are present, different for each block.
> Even with this, I find it odd to believe that, for example, SerDes block
> 2 from LS1046A was instantiated so that it does not support any Ethernet
> protocols.

As it happens, it does support SGMII on lane B, but it mainly supports
SATA/PCIe.

If you happen to have some additional info about the internal structure of
the SerDes, I'd be very interested. However, as far as I can tell from the
public documentation the protocols supported are different for each SerDes
on each SoC.

E.g. the LS1043A has a completely different set of supported protocols on its SerDes.

> I'll ask around to see if indeed this happens.
> 
>> +
>> +Supporting Protocols
>> +--------------------
>> +
>> +Each protocol is a combination of values which must be programmed into the lane
>> +registers. To add a new protocol, first add it to :c:type:`enum lynx_protocol
>> +<lynx_protocol>`. If it is in ``UNSUPPORTED_PROTOS``, remove it. Add a new
>> +entry to `lynx_proto_params`, and populate the appropriate fields. You may need
>> +to add some new members to support new fields. Modify `lynx_lookup_proto` to
>> +map the :c:type:`enum phy_mode <phy_mode>` to :c:type:`enum lynx_protocol
>> +<lynx_protocol>`. Ensure that :c:func:`lynx_proto_mode_mask` and
>> +:c:func:`lynx_proto_mode_shift` have been updated with support for your
>> +protocol.
>> +
>> +You may need to modify :c:func:`lynx_set_mode` in order to support your
>> +procotol. This can happen when you have added members to :c:type:`struct
>> +lynx_proto_params <lynx_proto_params>`. It can also happen if you have specific
>> +clocking requirements, or protocol-specific registers to program.
>> +
>> +Internal API Reference
>> +----------------------
>> +
>> +.. kernel-doc:: drivers/phy/freescale/phy-fsl-lynx-10g.c
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ca95b1833b97..ef65e2acdb48 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7977,6 +7977,12 @@ F:	drivers/ptp/ptp_qoriq.c
>>  F:	drivers/ptp/ptp_qoriq_debugfs.c
>>  F:	include/linux/fsl/ptp_qoriq.h
>>  
>> +FREESCALE QORIQ SERDES DRIVER
>> +M:	Sean Anderson <sean.anderson@seco.com>
>> +S:	Maintained
>> +F:	Documentation/driver-api/phy/qoriq.rst
>> +F:	drivers/phy/freescale/phy-qoriq.c
>> +
> 
> These file names have to be changed as well.
> 
> (...)

Will fix.

>> +enum lynx_protocol {
>> +	LYNX_PROTO_NONE = 0,
>> +	LYNX_PROTO_SGMII,
>> +	LYNX_PROTO_SGMII25,
>> +	LYNX_PROTO_1000BASEKX,
>> +	LYNX_PROTO_QSGMII,
>> +	LYNX_PROTO_XFI,
>> +	LYNX_PROTO_10GKR,
>> +	LYNX_PROTO_PCIE, /* Not implemented */
>> +	LYNX_PROTO_SATA, /* Not implemented */
>> +	LYNX_PROTO_LAST,
>> +};
>> +
>> +static const char lynx_proto_str[][16] = {
>> +	[LYNX_PROTO_NONE] = "unknown",
>> +	[LYNX_PROTO_SGMII] = "SGMII",
>> +	[LYNX_PROTO_SGMII25] = "2.5G SGMII",
>> +	[LYNX_PROTO_1000BASEKX] = "1000Base-KX",
>> +	[LYNX_PROTO_QSGMII] = "QSGMII",
>> +	[LYNX_PROTO_XFI] = "XFI",
>> +	[LYNX_PROTO_10GKR] = "10GBase-KR",
>> +	[LYNX_PROTO_PCIE] = "PCIe",
>> +	[LYNX_PROTO_SATA] = "SATA",
>> +};
> 
>> +
>> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
>> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
> 
> From what I know, -KX and -KR need software level link training.

There was no mention of that in the datasheet, but I suspect that's
a PCS issue.

> Did you test these protocols?

No, as noted in the commit message.

> I would be much more comfortable if we only add to the supported
> protocols list what was tested.

Fine by me.

>> +	/* Deselect anything configured by the RCW/bootloader */
>> +	for (i = 0; i < conf->mode_count; i++) {
>> +		const struct lynx_mode *mode = &conf->modes[i];
>> +		u32 pccr = lynx_read(serdes, PCCRn(mode->pccr));
>> +
>> +		if (lynx_proto_mode_get(mode, pccr) == mode->cfg) {
>> +			if (mode->protos & UNSUPPORTED_PROTOS) {
>> +				/* Don't mess with modes we don't support */
>> +				serdes->used_lanes |= mode->lanes;
>> +				if (grabbed_clocks)
>> +					continue;
>> +
>> +				grabbed_clocks = true;
>> +				clk_prepare_enable(serdes->pll[0].hw.clk);
>> +				clk_prepare_enable(serdes->pll[1].hw.clk);
>> +				clk_rate_exclusive_get(serdes->pll[0].hw.clk);
>> +				clk_rate_exclusive_get(serdes->pll[1].hw.clk);
> 
> Am I understanding correctly that if you encounter a protocol which is
> not supported (PCIe, SATA) both PLLs will not be capable of changing,
> right?

Correct.

> Why aren't you just getting exclusivity on the PLL that is actually used
> by a lane configured with a protocol which the driver does not support?

PCIe will automatically switch between PLLs in order to switch speeds. So
we can't change either, because the currently-used PLL could change at any
time. SATA doesn't have this restriction. Its rates have power-of-two
relationships with each other, so it can just change the divider. However,
I've chosen to get things exclusively in both cases for simplicity.

>> +			} else {
>> +				/* Otherwise, clear out the existing config */
>> +				pccr = lynx_proto_mode_prep(mode, pccr,
>> +							    LYNX_PROTO_NONE);
>> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
>> +			}
> 
> Hmmm, do you need this?
> 
> Wouldn't it be better to just leave the lane untouched (as it was setup
> by the RCW) just in case the lane is not requested by a consumer driver
> but actually used in practice. I am referring to the case in which some
> ethernet nodes have the 'phys' property, some don't.

The reason why I do this is to make sure that no other protocols are selected.
We only clear out the protocol configuration registers for a protocol that we've
configured (e.g when we go from SGMII to XFI we clear out the SGMII register).
But if the RCW e.g. configured QSGMII, we need to disable it because otherwise we
will accidentally leave it enabled.

> If you really need this, maybe you can move it in the phy_init callback.

That's fine by me.

>> +
>> +			/* Disable the SGMII PCS until we're ready for it */
>> +			if (mode->protos & LYNX_PROTO_SGMII) {
>> +				u32 cr1;
>> +
>> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
>> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
>> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
>> +			}
>> +		}
>> +	}
>> +
>> +	/* Power off all lanes; used ones will be powered on later */
>> +	for (i = 0; i < conf->lanes; i++)
>> +		lynx_power_off_lane(serdes, i);
> 
> This means that you are powering-off any lane, PCIe/SATA lanes
> which are not integrated with this driver at all, right?.
> I don't think we want to break stuff that used to be working.

You're right. This should really check used_lanes first.

> (...)
> 
>> +MODULE_DEVICE_TABLE(of, lynx_of_match);
>> +
>> +static struct platform_driver lynx_driver = {
>> +	.probe = lynx_probe,
>> +	.driver = {
>> +		.name = "qoriq_serdes",
> 
> Please change the driver's name as well.

Will do.

--Sean

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

* Re: [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding
  2022-06-30 18:08       ` Krzysztof Kozlowski
@ 2022-06-30 18:16         ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-06-30 18:16 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Rob Herring
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Kishon Vijay Abraham I, Krzysztof Kozlowski,
	Vinod Koul, devicetree, linux-phy



On 6/30/22 2:08 PM, Krzysztof Kozlowski wrote:
> On 30/06/2022 20:01, Sean Anderson wrote:
>> Hi Rob,
>> 
>> On 6/30/22 1:27 PM, Rob Herring wrote:
>>> On Tue, Jun 28, 2022 at 06:13:30PM -0400, Sean Anderson wrote:
>>>> This adds a binding for the SerDes module found on QorIQ processors. The
>>>> phy reference has two cells, one for the first lane and one for the
>>>> last. This should allow for good support of multi-lane protocols when
>>>> (if) they are added. There is no protocol option, because the driver is
>>>> designed to be able to completely reconfigure lanes at runtime.
>>>> Generally, the phy consumer can select the appropriate protocol using
>>>> set_mode. For the most part there is only one protocol controller
>>>> (consumer) per lane/protocol combination. The exception to this is the
>>>> B4860 processor, which has some lanes which can be connected to
>>>> multiple MACs. For that processor, I anticipate the easiest way to
>>>> resolve this will be to add an additional cell with a "protocol
>>>> controller instance" property.
>>>>
>>>> Each serdes has a unique set of supported protocols (and lanes). The
>>>> support matrix is stored in the driver and is selected based on the
>>>> compatible string. It is anticipated that a new compatible string will
>>>> need to be added for each serdes on each SoC that drivers support is
>>>> added for. There is no "generic" compatible string for this reason.
>>>>
>>>> There are two PLLs, each of which can be used as the master clock for
>>>> each lane. Each PLL has its own reference. For the moment they are
>>>> required, because it simplifies the driver implementation. Absent
>>>> reference clocks can be modeled by a fixed-clock with a rate of 0.
>>>>
>>>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>>>> ---
>>>>
>>>> Changes in v2:
>>>> - Add #clock-cells. This will allow using assigned-clocks* to configure
>>>>   the PLLs.
>>>> - Allow a value of 1 for phy-cells. This allows for compatibility with
>>>>   the similar (but according to Ioana Ciornei different enough) lynx-28g
>>>>   binding.
>>>> - Document phy cells in the description
>>>> - Document the structure of the compatible strings
>>>> - Fix example binding having too many cells in regs
>>>> - Move compatible first
>>>> - Refer to the device in the documentation, rather than the binding
>>>> - Remove minItems
>>>> - Rename to fsl,lynx-10g.yaml
>>>> - Use list for clock-names
>>>>
>>>>  .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 93 +++++++++++++++++++
>>>>  1 file changed, 93 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>>> new file mode 100644
>>>> index 000000000000..b5a6f631df9f
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>>>> @@ -0,0 +1,93 @@
>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>> +%YAML 1.2
>>>> +---
>>>> +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>> +
>>>> +title: NXP Lynx 10G SerDes
>>>> +
>>>> +maintainers:
>>>> +  - Sean Anderson <sean.anderson@seco.com>
>>>> +
>>>> +description: |
>>>> +  These Lynx "SerDes" devices are found in NXP's QorIQ line of processors. The
>>>> +  SerDes provides up to eight lanes. Each lane may be configured individually,
>>>> +  or may be combined with adjacent lanes for a multi-lane protocol. The SerDes
>>>> +  supports a variety of protocols, including up to 10G Ethernet, PCIe, SATA, and
>>>> +  others. The specific protocols supported for each lane depend on the
>>>> +  particular SoC.
>>>> +
>>>> +properties:
>>>> +  compatible:
>>>> +    description: |
>>>> +      Each compatible is of the form "fsl,<soc-name>-serdes-<instance>".
>>>> +      Although many registers are compatible between different SoCs, the
>>>> +      supported protocols and lane assignments tend to be unique to each SerDes.
>>>> +      Additionally, the method of activating protocols may also be unique.
>>>
>> 
>> On v1, Krzysztof said that this was a better route...
> 
> I commented about "-1" and "-2" saying you have to make them properties.
> You disagreed and with long messages were convincing me that "-1" and
> "-2" is the only reasonable approach. I never said it is a better route.
> I explicitly asked in several places for defining these as properties,
> not as compatibles.
> 
> You are twisting the entire discussion now.

Sorry, I didn't mean to come off that way.

>>> We typically have properties for handling these variables. Numbering 
>>> instances is something we avoid.

But I would like to point out that for the phy subsystem, it is typical
to select using the compatible (or just write a new driver).

--Sean

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

* Re: [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml
  2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
  2022-06-29  2:09   ` Rob Herring
  2022-06-29 14:50   ` Russell King (Oracle)
@ 2022-07-01  0:01   ` Rob Herring
  2 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2022-07-01  0:01 UTC (permalink / raw)
  To: Sean Anderson
  Cc: devicetree, Madalin Bucur, Paolo Abeni, Rob Herring,
	Jakub Kicinski, netdev, Russell King, David S . Miller,
	Eric Dumazet, Krzysztof Kozlowski, linux-arm-kernel,
	linux-kernel

On Tue, 28 Jun 2022 18:13:31 -0400, Sean Anderson wrote:
> This converts the MAC portion of the FMan MAC bindings to yaml.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
> Changes in v2:
> - New
> 
>  .../bindings/net/fsl,fman-dtsec.yaml          | 144 ++++++++++++++++++
>  .../devicetree/bindings/net/fsl-fman.txt      | 128 +---------------
>  2 files changed, 145 insertions(+), 127 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-06-30 18:11     ` Sean Anderson
@ 2022-07-01 10:03       ` Ioana Ciornei
  2022-07-01 15:51         ` Sean Anderson
       [not found]         ` <343faa45-4e4a-7a7f-b0c3-fcc9db89e976@seco.com>
  0 siblings, 2 replies; 32+ messages in thread
From: Ioana Ciornei @ 2022-07-01 10:03 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-doc, linux-phy

On Thu, Jun 30, 2022 at 02:11:17PM -0400, Sean Anderson wrote:
> 
> 
> On 6/30/22 11:56 AM, Ioana Ciornei wrote:
> > 
> > Hi Sean,
> > 
> > I am in the process of adding the necessary configuration for this
> > driver to work on a LS1088A based board. At the moment, I can see that
> > the lane's PLL is changed depending on the SFP module plugged, I have a
> > CDR lock but no PCS link.
> 
> I have a LS1088A board which I can test on.

If it's a LS1088ARDB one, you have to bypass / disable the retimer which
is between the SerDes lane and the SFP cage. I have some i2cset commands
which do this, let me know if you need them.

By the way, I think the LS1046ARDB also has a retimer. What are you
doing with that when you switch to an SFP module (SGMII/1000Base-X)?

> >> +There is an additional set of configuration for SerDes2, which supports a
> >> +different set of modes. Both configurations should be added to the match
> >> +table::
> >> +
> >> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
> >> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
> > 
> > I am not 100% sure that different compatible strings are needed for each
> > SerDes block. I know that in the 'supported SerDes options' tables only
> > a certain list of combinations are present, different for each block.
> > Even with this, I find it odd to believe that, for example, SerDes block
> > 2 from LS1046A was instantiated so that it does not support any Ethernet
> > protocols.
> 
> As it happens, it does support SGMII on lane B, but it mainly supports
> SATA/PCIe.
> 
> If you happen to have some additional info about the internal structure of
> the SerDes, I'd be very interested. However, as far as I can tell from the
> public documentation the protocols supported are different for each SerDes
> on each SoC.
> 
> E.g. the LS1043A has a completely different set of supported protocols on its SerDes.

Yes, between the SoCs there are differences and having SoC specific
compatible helps there.

What I am not sure of is if there are different instantiations of the
SerDes in the same SoC. Will let you know when I find out more myself.

> >> +
> >> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
> >> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
> > 
> > From what I know, -KX and -KR need software level link training.
> 
> There was no mention of that in the datasheet, but I suspect that's
> a PCS issue.


No, not just the PCS is involved in the backplane (-KR, -KX) link
training.
Depending on the what the link partner requests, the pre- and post-tap
coefficients (the TECR0 register) need to be changed. Those default
values presented in the RM may well work in some situations, but not all
of them. They are usually just used as a starting point for the link
training algorithm which will try to get the link to an optimal point.

Here is an application note which describes in more details what I just
said: https://www.nxp.com/docs/en/application-note/AN12572.pdf

> > Am I understanding correctly that if you encounter a protocol which is
> > not supported (PCIe, SATA) both PLLs will not be capable of changing,
> > right?
> 
> Correct.
> 
> > Why aren't you just getting exclusivity on the PLL that is actually used
> > by a lane configured with a protocol which the driver does not support?
> 
> PCIe will automatically switch between PLLs in order to switch speeds. So
> we can't change either, because the currently-used PLL could change at any
> time. SATA doesn't have this restriction. Its rates have power-of-two
> relationships with each other, so it can just change the divider. However,
> I've chosen to get things exclusively in both cases for simplicity.

Oh, ok. I didn't know that PCIe does this automatic switchover between
PLLs. Thanks!

> 
> >> +			} else {
> >> +				/* Otherwise, clear out the existing config */
> >> +				pccr = lynx_proto_mode_prep(mode, pccr,
> >> +							    LYNX_PROTO_NONE);
> >> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
> >> +			}
> > 
> > Hmmm, do you need this?
> > 
> > Wouldn't it be better to just leave the lane untouched (as it was setup
> > by the RCW) just in case the lane is not requested by a consumer driver
> > but actually used in practice. I am referring to the case in which some
> > ethernet nodes have the 'phys' property, some don't.
> 
> The reason why I do this is to make sure that no other protocols are selected.
> We only clear out the protocol configuration registers for a protocol that we've
> configured (e.g when we go from SGMII to XFI we clear out the SGMII register).
> But if the RCW e.g. configured QSGMII, we need to disable it because otherwise we
> will accidentally leave it enabled.
> 
> > If you really need this, maybe you can move it in the phy_init callback.
> 
> That's fine by me.
> 
> >> +
> >> +			/* Disable the SGMII PCS until we're ready for it */
> >> +			if (mode->protos & LYNX_PROTO_SGMII) {
> >> +				u32 cr1;
> >> +
> >> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
> >> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
> >> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
> >> +			}
> >> +		}
> >> +	}
> >> +
> >> +	/* Power off all lanes; used ones will be powered on later */
> >> +	for (i = 0; i < conf->lanes; i++)
> >> +		lynx_power_off_lane(serdes, i);
> > 
> > This means that you are powering-off any lane, PCIe/SATA lanes
> > which are not integrated with this driver at all, right?.
> > I don't think we want to break stuff that used to be working.
> 
> You're right. This should really check used_lanes first.
> 

I am not sure if the used_lanes indication will cover the case in which
just some, for example, SGMII lanes have a 'phys' property pointing to
them but not all of them.

Again, powering off the lane can be done in the phy_init.

Ioana

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-07-01 10:03       ` Ioana Ciornei
@ 2022-07-01 15:51         ` Sean Anderson
       [not found]         ` <343faa45-4e4a-7a7f-b0c3-fcc9db89e976@seco.com>
  1 sibling, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-07-01 15:51 UTC (permalink / raw)
  To: Ioana Ciornei
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-doc, linux-phy

Hi Ioana,

On 7/1/22 6:03 AM, Ioana Ciornei wrote:
> On Thu, Jun 30, 2022 at 02:11:17PM -0400, Sean Anderson wrote:
>> 
>> 
>> On 6/30/22 11:56 AM, Ioana Ciornei wrote:
>> > 
>> > Hi Sean,
>> > 
>> > I am in the process of adding the necessary configuration for this
>> > driver to work on a LS1088A based board. At the moment, I can see that
>> > the lane's PLL is changed depending on the SFP module plugged, I have a
>> > CDR lock but no PCS link.
>> 
>> I have a LS1088A board which I can test on.
> 
> If it's a LS1088ARDB one, you have to bypass / disable the retimer which
> is between the SerDes lane and the SFP cage. I have some i2cset commands
> which do this, let me know if you need them.

I'd appreciate that.

> By the way, I think the LS1046ARDB also has a retimer. What are you
> doing with that when you switch to an SFP module (SGMII/1000Base-X)?

I haven't tested that so far... In fact, I'd forgotten about that retimer.
Perhaps it can be modeled as an additional "phy". Although according to
the datasheet,

> Each channel of the DS110DF111 will, by default operate at 10.3125 Gbps
> and 1.25 Gbps

so it seems like it shouldn't need reconfiguration to switch between SGMII
and XFI.

>> >> +There is an additional set of configuration for SerDes2, which supports a
>> >> +different set of modes. Both configurations should be added to the match
>> >> +table::
>> >> +
>> >> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
>> >> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
>> > 
>> > I am not 100% sure that different compatible strings are needed for each
>> > SerDes block. I know that in the 'supported SerDes options' tables only
>> > a certain list of combinations are present, different for each block.
>> > Even with this, I find it odd to believe that, for example, SerDes block
>> > 2 from LS1046A was instantiated so that it does not support any Ethernet
>> > protocols.
>> 
>> As it happens, it does support SGMII on lane B, but it mainly supports
>> SATA/PCIe.
>> 
>> If you happen to have some additional info about the internal structure of
>> the SerDes, I'd be very interested. However, as far as I can tell from the
>> public documentation the protocols supported are different for each SerDes
>> on each SoC.
>> 
>> E.g. the LS1043A has a completely different set of supported protocols on its SerDes.
> 
> Yes, between the SoCs there are differences and having SoC specific
> compatible helps there.
> 
> What I am not sure of is if there are different instantiations of the
> SerDes in the same SoC. Will let you know when I find out more myself.
> 
>> >> +
>> >> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
>> >> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
>> > 
>> > From what I know, -KX and -KR need software level link training.
>> 
>> There was no mention of that in the datasheet, but I suspect that's
>> a PCS issue.
> 
> 
> No, not just the PCS is involved in the backplane (-KR, -KX) link
> training.
> Depending on the what the link partner requests, the pre- and post-tap
> coefficients (the TECR0 register) need to be changed. Those default
> values presented in the RM may well work in some situations, but not all
> of them. They are usually just used as a starting point for the link
> training algorithm which will try to get the link to an optimal point.
> 
> Here is an application note which describes in more details what I just
> said: https://www.nxp.com/docs/en/application-note/AN12572.pdf

Well the linked repo [1] certainly is interesting, as it contains around 1/3
of a general phy driver. To support KX/KR it definitely seems like some kind
of iterative process is necessary, probably using phy_configure. Such a process
is most naturally driven using the PCS... it might make sense to reference the
SerDes from the PCS node instead of the MAC. E.g.

	mdio@e9000 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
		reg = <0xe7000 0x1000>;

		pcsphy4: ethernet-phy@0 {
			reg = <0x0>;
			phys = <&serdes1 1>;
		};
	};

This of course would be easier with a more normal probing process.

That said, I do agree with you that KX/KR would probably not function as-is.

[1] https://source.codeaurora.org/external/qoriq/qoriq-components/linux-extras/

>> > Am I understanding correctly that if you encounter a protocol which is
>> > not supported (PCIe, SATA) both PLLs will not be capable of changing,
>> > right?
>> 
>> Correct.
>> 
>> > Why aren't you just getting exclusivity on the PLL that is actually used
>> > by a lane configured with a protocol which the driver does not support?
>> 
>> PCIe will automatically switch between PLLs in order to switch speeds. So
>> we can't change either, because the currently-used PLL could change at any
>> time. SATA doesn't have this restriction. Its rates have power-of-two
>> relationships with each other, so it can just change the divider. However,
>> I've chosen to get things exclusively in both cases for simplicity.
> 
> Oh, ok. I didn't know that PCIe does this automatic switchover between
> PLLs. Thanks!
> 
>> 
>> >> +			} else {
>> >> +				/* Otherwise, clear out the existing config */
>> >> +				pccr = lynx_proto_mode_prep(mode, pccr,
>> >> +							    LYNX_PROTO_NONE);
>> >> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
>> >> +			}
>> > 
>> > Hmmm, do you need this?
>> > 
>> > Wouldn't it be better to just leave the lane untouched (as it was setup
>> > by the RCW) just in case the lane is not requested by a consumer driver
>> > but actually used in practice. I am referring to the case in which some
>> > ethernet nodes have the 'phys' property, some don't.
>> 
>> The reason why I do this is to make sure that no other protocols are selected.
>> We only clear out the protocol configuration registers for a protocol that we've
>> configured (e.g when we go from SGMII to XFI we clear out the SGMII register).
>> But if the RCW e.g. configured QSGMII, we need to disable it because otherwise we
>> will accidentally leave it enabled.
>> 
>> > If you really need this, maybe you can move it in the phy_init callback.
>> 
>> That's fine by me.
>> 
>> >> +
>> >> +			/* Disable the SGMII PCS until we're ready for it */
>> >> +			if (mode->protos & LYNX_PROTO_SGMII) {
>> >> +				u32 cr1;
>> >> +
>> >> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
>> >> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
>> >> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
>> >> +			}
>> >> +		}
>> >> +	}
>> >> +
>> >> +	/* Power off all lanes; used ones will be powered on later */
>> >> +	for (i = 0; i < conf->lanes; i++)
>> >> +		lynx_power_off_lane(serdes, i);
>> > 
>> > This means that you are powering-off any lane, PCIe/SATA lanes
>> > which are not integrated with this driver at all, right?.
>> > I don't think we want to break stuff that used to be working.
>> 
>> You're right. This should really check used_lanes first.
>> 
> 
> I am not sure if the used_lanes indication will cover the case in which
> just some, for example, SGMII lanes have a 'phys' property pointing to
> them but not all of them.

This is why I've disabled the SerDes by default. Boards which enable it
will need to ensure that all the Ethernet interfaces have had their phys
property added.

> Again, powering off the lane can be done in the phy_init.

Not if no one ever uses the lane. Unlike the clock subsystem, unused phys
are not automatically powered off. We could of course wait until sometime
after probe, but doing it now is easiest.

--Sean

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
       [not found]         ` <343faa45-4e4a-7a7f-b0c3-fcc9db89e976@seco.com>
@ 2022-07-01 21:04           ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-07-01 21:04 UTC (permalink / raw)
  To: Ioana Ciornei
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Jonathan Corbet, Kishon Vijay Abraham I,
	Krzysztof Kozlowski, Rob Herring, Vinod Koul, devicetree,
	linux-doc, linux-phy

Just some follow-ups to my earlier email:

On 7/1/22 11:50 AM, Sean Anderson wrote:
> Hi Ioana,
> 
> On 7/1/22 6:03 AM, Ioana Ciornei wrote:
>> On Thu, Jun 30, 2022 at 02:11:17PM -0400, Sean Anderson wrote:
>>> 
>>> 
>>> On 6/30/22 11:56 AM, Ioana Ciornei wrote:
>>> > 
>>> > Hi Sean,
>>> > 
>>> > I am in the process of adding the necessary configuration for this
>>> > driver to work on a LS1088A based board. At the moment, I can see that
>>> > the lane's PLL is changed depending on the SFP module plugged, I have a
>>> > CDR lock but no PCS link.
>>> 
>>> I have a LS1088A board which I can test on.
>> 
>> If it's a LS1088ARDB one, you have to bypass / disable the retimer which
>> is between the SerDes lane and the SFP cage. I have some i2cset commands
>> which do this, let me know if you need them.
> 
> I'd appreciate that.
> 
>> By the way, I think the LS1046ARDB also has a retimer. What are you
>> doing with that when you switch to an SFP module (SGMII/1000Base-X)?
> 
> I haven't tested that so far... In fact, I'd forgotten about that retimer.
> Perhaps it can be modeled as an additional "phy". Although according to
> the datasheet,
> 
>> Each channel of the DS110DF111 will, by default operate at 10.3125 Gbps
>> and 1.25 Gbps
> 
> so it seems like it shouldn't need reconfiguration to switch between SGMII
> and XFI.

I tested this, and the SFP module works for both SGMII and XFI.

>>> >> +There is an additional set of configuration for SerDes2, which supports a
>>> >> +different set of modes. Both configurations should be added to the match
>>> >> +table::
>>> >> +
>>> >> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
>>> >> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
>>> > 
>>> > I am not 100% sure that different compatible strings are needed for each
>>> > SerDes block. I know that in the 'supported SerDes options' tables only
>>> > a certain list of combinations are present, different for each block.
>>> > Even with this, I find it odd to believe that, for example, SerDes block
>>> > 2 from LS1046A was instantiated so that it does not support any Ethernet
>>> > protocols.
>>> 
>>> As it happens, it does support SGMII on lane B, but it mainly supports
>>> SATA/PCIe.
>>> 
>>> If you happen to have some additional info about the internal structure of
>>> the SerDes, I'd be very interested. However, as far as I can tell from the
>>> public documentation the protocols supported are different for each SerDes
>>> on each SoC.
>>> 
>>> E.g. the LS1043A has a completely different set of supported protocols on its SerDes.
>> 
>> Yes, between the SoCs there are differences and having SoC specific
>> compatible helps there.
>> 
>> What I am not sure of is if there are different instantiations of the
>> SerDes in the same SoC. Will let you know when I find out more myself.

I don't think there are any major register layout differences between
SerDes on the same SoC; the differences are mainly in protocol support.
For example, consider the T4240. It has 4 SerDes -- two "networking" and
two "non-networking". The "networking" SerDes mostly support the same
protocols (except SerDes2 supports XFI on lanes A-D). Similarly, the
"non-networking" SerDes both support PCIe and SRIO, but SerDes3 supports
Interlaken, and SerDes2 supports Aurora and SATA. There are also several
pages of additional restrictions which I haven't fully read through.

Now, that's not to say that you couldn't use one set of configuration
for all four SerDes. You'd mainly lose the ability to determine which
protocols were valid. This is of course important for things like SFP
slots: XFI is available on some lanes but not others, and if the
networking layer doesn't figure that out it can silently fail to work.
It's also nice to get some kind of error message if you select the wrong
lane.

>>> >> +
>>> >> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
>>> >> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
>>> > 
>>> > From what I know, -KX and -KR need software level link training.
>>> 
>>> There was no mention of that in the datasheet, but I suspect that's
>>> a PCS issue.
>> 
>> 
>> No, not just the PCS is involved in the backplane (-KR, -KX) link
>> training.
>> Depending on the what the link partner requests, the pre- and post-tap
>> coefficients (the TECR0 register) need to be changed. Those default
>> values presented in the RM may well work in some situations, but not all
>> of them. They are usually just used as a starting point for the link
>> training algorithm which will try to get the link to an optimal point.
>> 
>> Here is an application note which describes in more details what I just
>> said: https://www.nxp.com/docs/en/application-note/AN12572.pdf
> 
> Well the linked repo [1] certainly is interesting, as it contains around 1/3
> of a general phy driver. To support KX/KR it definitely seems like some kind
> of iterative process is necessary, probably using phy_configure. Such a process
> is most naturally driven using the PCS... it might make sense to reference the
> SerDes from the PCS node. E.g.
> 
> 	mdio@e9000 {
> 		#address-cells = <1>;
> 		#size-cells = <0>;
> 		compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
> 		reg = <0xe7000 0x1000>;
> 
> 		pcsphy4: ethernet-phy@0 {
> 			reg = <0x0>;
> 			phys = <&serdes1 1>;
> 		};
> 	};
> 
> This of course would be easier with a more normal probing process.
> 
> That said, I do agree with you that KX/KR would probably not function as-is.

I had a longer look at that driver, and while KR would probably not
work, the KX portions seem like they would work as-is.

The other thing is that UNSUPPORTED_PROTOS is really supposed to hold
protocols which used to work and which we don't know how to manage.
AFAICT, KR/KX always required a downstream Linux (e.g. they were never
configured just using the RCW). So it should be fine to keep these as
is, perhaps with some comments or warnings. Ultimately, the PCS doesn't
support these modes, so they will not normally be selected.

> [1] https://source.codeaurora.org/external/qoriq/qoriq-components/linux-extras/
> 
>>> > Am I understanding correctly that if you encounter a protocol which is
>>> > not supported (PCIe, SATA) both PLLs will not be capable of changing,
>>> > right?
>>> 
>>> Correct.
>>> 
>>> > Why aren't you just getting exclusivity on the PLL that is actually used
>>> > by a lane configured with a protocol which the driver does not support?
>>> 
>>> PCIe will automatically switch between PLLs in order to switch speeds. So
>>> we can't change either, because the currently-used PLL could change at any
>>> time. SATA doesn't have this restriction. Its rates have power-of-two
>>> relationships with each other, so it can just change the divider. However,
>>> I've chosen to get things exclusively in both cases for simplicity.
>> 
>> Oh, ok. I didn't know that PCIe does this automatic switchover between
>> PLLs. Thanks!

A small correction: Apparently in some circumstances the PCIe controller
can reconfigure an existing PLL to switch. I'm not sure exactly how this
is configured.

>>> 
>>> >> +			} else {
>>> >> +				/* Otherwise, clear out the existing config */
>>> >> +				pccr = lynx_proto_mode_prep(mode, pccr,
>>> >> +							    LYNX_PROTO_NONE);
>>> >> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
>>> >> +			}
>>> > 
>>> > Hmmm, do you need this?
>>> > 
>>> > Wouldn't it be better to just leave the lane untouched (as it was setup
>>> > by the RCW) just in case the lane is not requested by a consumer driver
>>> > but actually used in practice. I am referring to the case in which some
>>> > ethernet nodes have the 'phys' property, some don't.
>>> 
>>> The reason why I do this is to make sure that no other protocols are selected.
>>> We only clear out the protocol configuration registers for a protocol that we've
>>> configured (e.g when we go from SGMII to XFI we clear out the SGMII register).
>>> But if the RCW e.g. configured QSGMII, we need to disable it because otherwise we
>>> will accidentally leave it enabled.
>>> 
>>> > If you really need this, maybe you can move it in the phy_init callback.
>>> 
>>> That's fine by me.
>>> 
>>> >> +
>>> >> +			/* Disable the SGMII PCS until we're ready for it */
>>> >> +			if (mode->protos & LYNX_PROTO_SGMII) {
>>> >> +				u32 cr1;
>>> >> +
>>> >> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
>>> >> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
>>> >> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
>>> >> +			}
>>> >> +		}
>>> >> +	}
>>> >> +
>>> >> +	/* Power off all lanes; used ones will be powered on later */
>>> >> +	for (i = 0; i < conf->lanes; i++)
>>> >> +		lynx_power_off_lane(serdes, i);
>>> > 
>>> > This means that you are powering-off any lane, PCIe/SATA lanes
>>> > which are not integrated with this driver at all, right?.
>>> > I don't think we want to break stuff that used to be working.
>>> 
>>> You're right. This should really check used_lanes first.
>>> 
>> 
>> I am not sure if the used_lanes indication will cover the case in which
>> just some, for example, SGMII lanes have a 'phys' property pointing to
>> them but not all of them.
> 
> This is why I've disabled the SerDes by default. Boards which enable it
> will need to ensure that all the Ethernet interfaces have had their phys
> property added.
> 
>> Again, powering off the lane can be done in the phy_init.
> 
> Not if no one ever uses the lane. Unlike the clock subsystem, unused phys
> are not automatically powered off. We could of course wait until sometime
> after probe, but doing it now is easiest.
> 
> --S
> 

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-06-28 22:13 ` [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
  2022-06-30 15:56   ` Ioana Ciornei
@ 2022-07-05  6:12   ` Vinod Koul
  2022-07-05 15:29     ` Sean Anderson
  1 sibling, 1 reply; 32+ messages in thread
From: Vinod Koul @ 2022-07-05  6:12 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Ioana Ciornei, Jonathan Corbet,
	Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring,
	devicetree, linux-doc, linux-phy

On 28-06-22, 18:13, Sean Anderson wrote:
> This adds support for the Lynx 10G "SerDes" devices found on various NXP
> QorIQ SoCs. There may be up to four SerDes devices on each SoC, each
> supporting up to eight lanes. Protocol support for each SerDes is highly
> heterogeneous, with each SoC typically having a totally different
> selection of supported protocols for each lane. Additionally, the SerDes
> devices on each SoC also have differing support. One SerDes will
> typically support Ethernet on most lanes, while the other will typically
> support PCIe on most lanes.
> 
> There is wide hardware support for this SerDes. I have not done extensive
> digging, but it seems to be used on almost every QorIQ device, including
> the AMP and Layerscape series. Because each SoC typically has specific
> instructions and exceptions for its SerDes, I have limited the initial
> scope of this module to just the LS1046A. Additionally, I have only added
> support for Ethernet protocols. There is not a great need for dynamic
> reconfiguration for other protocols (SATA and PCIe handle rate changes in
> hardware), so support for them may never be added.
> 
> Nevertheless, I have tried to provide an obvious path for adding support
> for other SoCs as well as other protocols. SATA just needs support for
> configuring LNmSSCR0. PCIe may need to configure the equalization
> registers. It also uses multiple lanes. I have tried to write the driver
> with multi-lane support in mind, so there should not need to be any large
> changes. Although there are 6 protocols supported, I have only tested SGMII
> and XFI. The rest have been implemented as described in the datasheet.
> 
> The PLLs are modeled as clocks proper. This lets us take advantage of the
> existing clock infrastructure. I have not given the same treatment to the
> lane "clocks" (dividers) because they need to be programmed in-concert with
> the rest of the lane settings. One tricky thing is that the VCO (pll) rate
> exceeds 2^32 (maxing out at around 5GHz). This will be a problem on 32-bit
> platforms, since clock rates are stored as unsigned longs. To work around
> this, the pll clock rate is generally treated in units of kHz.
> 
> The PLLs are configured rather interestingly. Instead of the usual direct
> programming of the appropriate divisors, the input and output clock rates
> are selected directly. Generally, the only restriction is that the input
> and output must be integer multiples of each other. This suggests some kind
> of internal look-up table. The datasheets generally list out the supported
> combinations explicitly, and not all input/output combinations are
> documented. I'm not sure if this is due to lack of support, or due to an
> oversight. If this becomes an issue, then some combinations can be
> blacklisted (or whitelisted). This may also be necessary for other SoCs
> which have more stringent clock requirements.
> 
> The general API call list for this PHY is documented under the driver-api
> docs. I think this is rather standard, except that most driverts configure
> the mode (protocol) at xlate-time. Unlike some other phys where e.g. PCIe
> x4 will use 4 separate phys all configured for PCIe, this driver uses one
> phy configured to use 4 lanes. This is because while the individual lanes
> may be configured individually, the protocol selection acts on all lanes at
> once. Additionally, the order which lanes should be configured in is
> specified by the datasheet.  To coordinate this, lanes are reserved in
> phy_init, and released in phy_exit.
> 
> When getting a phy, if a phy already exists for those lanes, it is reused.
> This is to make things like QSGMII work. Four MACs will all want to ensure
> that the lane is configured properly, and we need to ensure they can all
> call phy_init, etc. There is refcounting for phy_init and phy_power_on, so
> the phy will only be powered on once. However, there is no refcounting for
> phy_set_mode. A "rogue" MAC could set the mode to something non-QSGMII and
> break the other MACs. Perhaps there is an opportunity for future
> enhancement here.
> 
> This driver was written with reference to the LS1046A reference manual.
> However, it was informed by reference manuals for all processors with
> mEMACs, especially the T4240 (which appears to have a "maxed-out"
> configuration).
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> As noted later on in this series (in the phylink conversion patch), this
> driver does not quite function properly. When the bootloader is
> instructed to not configure the SerDes, only one lane comes up.
> 
> Changes in v2:
> - Clear SGMIIaCR1_PCS_EN during probe
> - Fix not clearing group->pll after disabling it
> - Handle 1000Base-KX in lynx_proto_mode_prep
> - Power off lanes during probe
> - Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
> - Rename driver to Lynx 10G (etc.)
> - Support 1 and 2 phy-cells
> 
>  Documentation/driver-api/phy/index.rst   |    1 +
>  Documentation/driver-api/phy/qoriq.rst   |   93 ++
>  MAINTAINERS                              |    6 +
>  drivers/phy/freescale/Kconfig            |   19 +
>  drivers/phy/freescale/Makefile           |    1 +
>  drivers/phy/freescale/phy-fsl-lynx-10g.c | 1483 ++++++++++++++++++++++
>  6 files changed, 1603 insertions(+)
>  create mode 100644 Documentation/driver-api/phy/qoriq.rst
>  create mode 100644 drivers/phy/freescale/phy-fsl-lynx-10g.c
> 
> diff --git a/Documentation/driver-api/phy/index.rst b/Documentation/driver-api/phy/index.rst
> index 69ba1216de72..cc7ded8b969c 100644
> --- a/Documentation/driver-api/phy/index.rst
> +++ b/Documentation/driver-api/phy/index.rst
> @@ -7,6 +7,7 @@ Generic PHY Framework
>  .. toctree::
>  
>     phy
> +   qoriq
>     samsung-usb2
>  
>  .. only::  subproject and html
> diff --git a/Documentation/driver-api/phy/qoriq.rst b/Documentation/driver-api/phy/qoriq.rst
> new file mode 100644
> index 000000000000..cbc2ac9ca4aa
> --- /dev/null
> +++ b/Documentation/driver-api/phy/qoriq.rst
> @@ -0,0 +1,93 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=======================
> +QorIQ SerDes (Lynx 10G)
> +=======================
> +
> +Using this phy
> +--------------
> +
> +The general order of calls should be::
> +
> +    [devm_][of_]phy_get()
> +    phy_init()
> +    phy_power_on()
> +    phy_set_mode[_ext]()
> +    ...
> +    phy_power_off()
> +    phy_exit()
> +    [[of_]phy_put()]

Why is this required here? This should conform to generic phy sequences
as documented in Documentation/driver-api/phy/phy.rst

> +
> +:c:func:`phy_get` just gets (or creates) a new :c:type:`phy` with the lanes
> +described in the phandle. :c:func:`phy_init` is what actually reserves the
> +lanes for use. Unlike some other drivers, when the phy is created, there is no
> +default protocol. :c:func:`phy_set_mode <phy_set_mode_ext>` must be called in
> +order to set the protocol.
> +
> +Supporting SoCs
> +---------------
> +
> +Each new SoC needs a :c:type:`struct lynx_conf <lynx_conf>` for each SerDes.
> +The most important member is `modes`, which is an array of :c:type:`struct
> +lynx_mode <lynx_mode>`. Each "mode" represents a configuration which can be
> +programmed into a protocol control register. Modes can support multiple lanes
> +(such for PCIe x2 or x4), as well as multiple protocols (such as SGMII and
> +1000Base-KX). There are several helper macros to make configuring each mode
> +easier. It is important that the list of modes is complete, even if not all
> +protocols are supported. This lets the driver know which lanes are available,
> +and which have been configured by the RCW.
> +
> +If a protocol is missing, add it to :c:type:`enum lynx_protocol
> +<lynx_protocol>`, and to ``UNSUPPORTED_PROTOS``. If the PCCR shifts/masks for
> +your protocol are missing, you will need to add them to
> +:c:func:`lynx_proto_mode_mask` and :c:func:`lynx_proto_mode_shift`.
> +
> +For example, the configuration for SerDes1 of the LS1046A is::
> +
> +    static const struct lynx_mode ls1046a_modes1[] = {
> +        CONF_SINGLE(1, PCIE, 0x0, 1, 0b001),
> +        CONF_1000BASEKX(0, 0x8, 0, 0b001),
> +        CONF_SGMII25KX(1, 0x8, 1, 0b001),
> +        CONF_SGMII25KX(2, 0x8, 2, 0b001),
> +        CONF_SGMII25KX(3, 0x8, 3, 0b001),
> +        CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001),
> +        CONF_XFI(2, 0xB, 0, 0b010),
> +        CONF_XFI(3, 0xB, 1, 0b001),
> +    };
> +
> +    static const struct lynx_conf ls1046a_conf1 = {
> +        .modes = ls1046a_modes1,
> +        .mode_count = ARRAY_SIZE(ls1046a_modes1),
> +        .lanes = 4,
> +        .endian = REGMAP_ENDIAN_BIG,
> +    };
> +
> +There is an additional set of configuration for SerDes2, which supports a
> +different set of modes. Both configurations should be added to the match
> +table::
> +
> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
> +
> +Supporting Protocols
> +--------------------
> +
> +Each protocol is a combination of values which must be programmed into the lane
> +registers. To add a new protocol, first add it to :c:type:`enum lynx_protocol
> +<lynx_protocol>`. If it is in ``UNSUPPORTED_PROTOS``, remove it. Add a new
> +entry to `lynx_proto_params`, and populate the appropriate fields. You may need
> +to add some new members to support new fields. Modify `lynx_lookup_proto` to
> +map the :c:type:`enum phy_mode <phy_mode>` to :c:type:`enum lynx_protocol
> +<lynx_protocol>`. Ensure that :c:func:`lynx_proto_mode_mask` and
> +:c:func:`lynx_proto_mode_shift` have been updated with support for your
> +protocol.
> +
> +You may need to modify :c:func:`lynx_set_mode` in order to support your
> +procotol. This can happen when you have added members to :c:type:`struct
> +lynx_proto_params <lynx_proto_params>`. It can also happen if you have specific
> +clocking requirements, or protocol-specific registers to program.
> +
> +Internal API Reference
> +----------------------
> +
> +.. kernel-doc:: drivers/phy/freescale/phy-fsl-lynx-10g.c
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ca95b1833b97..ef65e2acdb48 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7977,6 +7977,12 @@ F:	drivers/ptp/ptp_qoriq.c
>  F:	drivers/ptp/ptp_qoriq_debugfs.c
>  F:	include/linux/fsl/ptp_qoriq.h
>  
> +FREESCALE QORIQ SERDES DRIVER
> +M:	Sean Anderson <sean.anderson@seco.com>
> +S:	Maintained
> +F:	Documentation/driver-api/phy/qoriq.rst
> +F:	drivers/phy/freescale/phy-qoriq.c
> +
>  FREESCALE QUAD SPI DRIVER
>  M:	Han Xu <han.xu@nxp.com>
>  L:	linux-spi@vger.kernel.org
> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> index f9c54cd02036..857b4d123515 100644
> --- a/drivers/phy/freescale/Kconfig
> +++ b/drivers/phy/freescale/Kconfig
> @@ -38,3 +38,22 @@ config PHY_FSL_LYNX_28G
>  	  found on NXP's Layerscape platforms such as LX2160A.
>  	  Used to change the protocol running on SerDes lanes at runtime.
>  	  Only useful for a restricted set of Ethernet protocols.
> +
> +config PHY_FSL_LYNX_10G
> +	tristate "Freescale Layerscale Lynx 10G SerDes support"
> +	select GENERIC_PHY
> +	select REGMAP_MMIO
> +	help
> +	  This adds support for the Lynx "SerDes" devices found on various QorIQ
> +	  SoCs. There may be up to four SerDes devices on each SoC, and each
> +	  device supports up to eight lanes. The SerDes is configured by default
> +	  by the RCW, but this module is necessary in order to support dynamic
> +	  reconfiguration (such as to support 1G and 10G ethernet on the same
> +	  interface). The hardware supports a variety of protocols, including
> +	  Ethernet, SATA, PCIe, and more exotic links such as Interlaken and
> +	  Aurora. This driver only supports Ethernet, but it will try not to
> +	  touch lanes configured for other protocols.
> +
> +	  If you have a QorIQ processor and want to dynamically reconfigure your
> +	  SerDes, say Y. If this driver is compiled as a module, it will be
> +	  named phy-qoriq.
> diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
> index 3518d5dbe8a7..aa4374ed217c 100644
> --- a/drivers/phy/freescale/Makefile
> +++ b/drivers/phy/freescale/Makefile
> @@ -2,4 +2,5 @@
>  obj-$(CONFIG_PHY_FSL_IMX8MQ_USB)	+= phy-fsl-imx8mq-usb.o
>  obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY)	+= phy-fsl-imx8-mipi-dphy.o
>  obj-$(CONFIG_PHY_FSL_IMX8M_PCIE)	+= phy-fsl-imx8m-pcie.o
> +obj-$(CONFIG_PHY_FSL_LYNX_10G)		+= phy-fsl-lynx-10g.o
>  obj-$(CONFIG_PHY_FSL_LYNX_28G)		+= phy-fsl-lynx-28g.o
> diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c
> new file mode 100644
> index 000000000000..480bd493fbc2
> --- /dev/null
> +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
> @@ -0,0 +1,1483 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/math64.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include <linux/units.h>
> +
> +#define PLL_STRIDE	0x20
> +#define PLLa(a, off)	((a) * PLL_STRIDE + (off))
> +#define PLLaRSTCTL(a)	PLLa(a, 0x00)
> +#define PLLaCR0(a)	PLLa(a, 0x04)
> +
> +#define PLLaRSTCTL_RSTREQ	BIT(31)
> +#define PLLaRSTCTL_RST_DONE	BIT(30)
> +#define PLLaRSTCTL_RST_ERR	BIT(29)
> +#define PLLaRSTCTL_PLLRST_B	BIT(7)
> +#define PLLaRSTCTL_SDRST_B	BIT(6)
> +#define PLLaRSTCTL_SDEN		BIT(5)
> +
> +#define PLLaCR0_POFF		BIT(31)
> +#define PLLaCR0_RFCLK_SEL	GENMASK(30, 28)
> +#define PLLaCR0_PLL_LCK		BIT(23)
> +#define PLLaCR0_FRATE_SEL	GENMASK(19, 16)
> +#define PLLaCR0_DLYDIV_SEL	GENMASK(1, 0)
> +
> +#define PCCR_BASE	0x200
> +#define PCCR_STRIDE	0x4
> +#define PCCRn(n)	(PCCR_BASE + n * PCCR_STRIDE)
> +
> +#define PCCR0_PEXa_MASK		GENMASK(2, 0)
> +#define PCCR0_PEXa_SHIFT(a)	(28 - (a) * 4)

use FIELD_GET/PREP instead of defining shifts? That would use MASK to
extract/set the field

> +
> +#define PCCR2_SATAa_MASK	GENMASK(2, 0)
> +#define PCCR2_SATAa_SHIFT(a)	(28 - (a) * 4)
> +
> +#define PCCR8_SGMIIa_KX		BIT(3)
> +#define PCCR8_SGMIIa_MASK	GENMASK(3, 0)
> +#define PCCR8_SGMIIa_SHIFT(a)	(28 - (a) * 4)
> +
> +#define PCCR9_QSGMIIa_MASK	GENMASK(2, 0)
> +#define PCCR9_QSGMIIa_SHIFT(a)	(28 - (a) * 4)
> +
> +#define PCCRB_XFIa_MASK		GENMASK(2, 0)
> +#define PCCRB_XFIa_SHIFT(a)	(28 - (a) * 4)
> +
> +#define LANE_BASE	0x800
> +#define LANE_STRIDE	0x40
> +#define LNm(m, off)	(LANE_BASE + (m) * LANE_STRIDE + (off))
> +#define LNmGCR0(m)	LNm(m, 0x00)
> +#define LNmGCR1(m)	LNm(m, 0x04)
> +#define LNmSSCR0(m)	LNm(m, 0x0C)
> +#define LNmRECR0(m)	LNm(m, 0x10)
> +#define LNmRECR1(m)	LNm(m, 0x14)
> +#define LNmTECR0(m)	LNm(m, 0x18)
> +#define LNmSSCR1(m)	LNm(m, 0x1C)
> +#define LNmTTLCR0(m)	LNm(m, 0x20)
> +
> +#define LNmGCR0_RPLL_LES	BIT(31)
> +#define LNmGCR0_RRAT_SEL	GENMASK(29, 28)
> +#define LNmGCR0_TPLL_LES	BIT(27)
> +#define LNmGCR0_TRAT_SEL	GENMASK(25, 24)
> +#define LNmGCR0_RRST_B		BIT(22)
> +#define LNmGCR0_TRST_B		BIT(21)
> +#define LNmGCR0_RX_PD		BIT(20)
> +#define LNmGCR0_TX_PD		BIT(19)
> +#define LNmGCR0_IF20BIT_EN	BIT(18)
> +#define LNmGCR0_FIRST_LANE	BIT(16)
> +#define LNmGCR0_TTRM_VM_SEL	GENMASK(13, 12)
> +#define LNmGCR0_PROTS		GENMASK(11, 7)
> +
> +#define LNmGCR0_RAT_SEL_SAME		0b00
> +#define LNmGCR0_RAT_SEL_HALF		0b01
> +#define LNmGCR0_RAT_SEL_QUARTER		0b10
> +#define LNmGCR0_RAT_SEL_DOUBLE		0b11
> +
> +#define LNmGCR0_PROTS_PCIE		0b00000
> +#define LNmGCR0_PROTS_SGMII		0b00001
> +#define LNmGCR0_PROTS_SATA		0b00010
> +#define LNmGCR0_PROTS_XFI		0b01010
> +
> +#define LNmGCR1_RDAT_INV	BIT(31)
> +#define LNmGCR1_TDAT_INV	BIT(30)
> +#define LNmGCR1_OPAD_CTL	BIT(26)
> +#define LNmGCR1_REIDL_TH	GENMASK(22, 20)
> +#define LNmGCR1_REIDL_EX_SEL	GENMASK(19, 18)
> +#define LNmGCR1_REIDL_ET_SEL	GENMASK(17, 16)
> +#define LNmGCR1_REIDL_EX_MSB	BIT(15)
> +#define LNmGCR1_REIDL_ET_MSB	BIT(14)
> +#define LNmGCR1_REQ_CTL_SNP	BIT(13)
> +#define LNmGCR1_REQ_CDR_SNP	BIT(12)
> +#define LNmGCR1_TRSTDIR		BIT(7)
> +#define LNmGCR1_REQ_BIN_SNP	BIT(6)
> +#define LNmGCR1_ISLEW_RCTL	GENMASK(5, 4)
> +#define LNmGCR1_OSLEW_RCTL	GENMASK(1, 0)
> +
> +#define LNmRECR0_GK2OVD		GENMASK(27, 24)
> +#define LNmRECR0_GK3OVD		GENMASK(19, 16)
> +#define LNmRECR0_GK2OVD_EN	BIT(15)
> +#define LNmRECR0_GK3OVD_EN	BIT(16)
> +#define LNmRECR0_BASE_WAND	GENMASK(11, 10)
> +#define LNmRECR0_OSETOVD	GENMASK(5, 0)
> +
> +#define LNmRECR0_BASE_WAND_OFF		0b00
> +#define LNmRECR0_BASE_WAND_DEFAULT	0b01
> +#define LNmRECR0_BASE_WAND_ALTERNATE	0b10
> +#define LNmRECR0_BASE_WAND_OSETOVD	0b11
> +
> +#define LNmTECR0_TEQ_TYPE	GENMASK(29, 28)
> +#define LNmTECR0_SGN_PREQ	BIT(26)
> +#define LNmTECR0_RATIO_PREQ	GENMASK(25, 22)
> +#define LNmTECR0_SGN_POST1Q	BIT(21)
> +#define LNmTECR0_RATIO_PST1Q	GENMASK(20, 16)
> +#define LNmTECR0_ADPT_EQ	GENMASK(13, 8)
> +#define LNmTECR0_AMP_RED	GENMASK(5, 0)
> +
> +#define LNmTECR0_TEQ_TYPE_NONE		0b00
> +#define LNmTECR0_TEQ_TYPE_PRE		0b01
> +#define LNmTECR0_TEQ_TYPE_BOTH		0b10
> +
> +#define LNmTTLCR0_FLT_SEL	GENMASK(29, 24)
> +
> +#define PCS_STRIDE	0x10
> +#define CR_STRIDE	0x4
> +#define PCSa(a, base, cr)	(base + (a) * PCS_STRIDE + (cr) * CR_STRIDE)
> +
> +#define PCSaCR1_MDEV_PORT	GENMASK(31, 27)
> +
> +#define SGMII_BASE	0x1800
> +#define SGMIIaCR1(a)	PCSa(a, SGMII_BASE, 1)
> +
> +#define SGMIIaCR1_SGPCS_EN	BIT(11)
> +
> +#define QSGMII_OFFSET	0x1880
> +#define QSGMIIaCR1(a)	PCSa(a, QSGMII_BASE, 1)
> +
> +#define XFI_OFFSET	0x1980
> +#define XFIaCR1(a)	PCSa(a, XFI_BASE, 1)
> +
> +/* The maximum number of lanes in a single serdes */
> +#define MAX_LANES	8
> +
> +enum lynx_protocol {
> +	LYNX_PROTO_NONE = 0,
> +	LYNX_PROTO_SGMII,
> +	LYNX_PROTO_SGMII25,
> +	LYNX_PROTO_1000BASEKX,
> +	LYNX_PROTO_QSGMII,
> +	LYNX_PROTO_XFI,
> +	LYNX_PROTO_10GKR,
> +	LYNX_PROTO_PCIE, /* Not implemented */
> +	LYNX_PROTO_SATA, /* Not implemented */

lets skip that and add when it is implemented

> +	LYNX_PROTO_LAST,
> +};
> +
> +static const char lynx_proto_str[][16] = {
> +	[LYNX_PROTO_NONE] = "unknown",
> +	[LYNX_PROTO_SGMII] = "SGMII",
> +	[LYNX_PROTO_SGMII25] = "2.5G SGMII",
> +	[LYNX_PROTO_1000BASEKX] = "1000Base-KX",
> +	[LYNX_PROTO_QSGMII] = "QSGMII",
> +	[LYNX_PROTO_XFI] = "XFI",
> +	[LYNX_PROTO_10GKR] = "10GBase-KR",
> +	[LYNX_PROTO_PCIE] = "PCIe",
> +	[LYNX_PROTO_SATA] = "SATA",
> +};
> +
> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
> +
> +/**
> + * struct lynx_proto_params - Parameters for configuring a protocol
> + * @frate_khz: The PLL rate, in kHz
> + * @rat_sel: The divider to get the line rate
> + * @if20bit: Whether the proto is 20 bits or 10 bits
> + * @prots: Lane protocol select
> + * @reidl_th: Receiver electrical idle detection threshold
> + * @reidl_ex: Exit electrical idle filter
> + * @reidl_et: Enter idle filter
> + * @slew: Slew control
> + * @baseline_wander: Enable baseline wander correction
> + * @gain: Adaptive equalization gain override
> + * @offset_override: Adaptive equalization offset override
> + * @teq: Transmit equalization type (none, precursor, or precursor and
> + *       postcursor). The next few values are only used for appropriate
> + *       equalization types.
> + * @preq_ratio: Ratio of full swing transition bit to pre-cursor
> + * @postq_ratio: Ratio of full swing transition bit to first post-cursor.
> + * @adpt_eq: Transmitter Adjustments for 8G/10G
> + * @amp_red: Overall TX Amplitude Reduction
> + * @flt_sel: TTL configuration selector
> + */
> +struct lynx_proto_params {
> +	u32 frate_khz;
> +	u8 rat_sel;
> +	u8 prots;
> +	u8 reidl_th;
> +	u8 reidl_ex;
> +	u8 reidl_et;
> +	u8 slew;
> +	u8 gain;
> +	u8 baseline_wander;
> +	u8 offset_override;
> +	u8 teq;
> +	u8 preq_ratio;
> +	u8 postq_ratio;
> +	u8 adpt_eq;
> +	u8 amp_red;
> +	u8 flt_sel;
> +	bool if20bit;
> +};
> +
> +static const struct lynx_proto_params lynx_proto_params[] = {
> +	[LYNX_PROTO_SGMII] = {
> +		.frate_khz = 5000000,
> +		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
> +		.if20bit = false,
> +		.prots = LNmGCR0_PROTS_SGMII,
> +		.reidl_th = 0b001,
> +		.reidl_ex = 0b011,
> +		.reidl_et = 0b100,
> +		.slew = 0b01,
> +		.gain = 0b1111,
> +		.offset_override = 0b0011111,
> +		.teq = LNmTECR0_TEQ_TYPE_NONE,
> +		.adpt_eq = 0b110000,
> +		.amp_red = 0b000110,
> +		.flt_sel = 0b111001,
> +	},
> +	[LYNX_PROTO_1000BASEKX] = {
> +		.frate_khz = 5000000,
> +		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
> +		.if20bit = false,
> +		.prots = LNmGCR0_PROTS_SGMII,
> +		.slew = 0b01,
> +		.gain = 0b1111,
> +		.offset_override = 0b0011111,
> +		.teq = LNmTECR0_TEQ_TYPE_NONE,
> +		.adpt_eq = 0b110000,
> +		.flt_sel = 0b111001,
> +	},
> +	[LYNX_PROTO_SGMII25] = {
> +		.frate_khz = 3125000,
> +		.rat_sel = LNmGCR0_RAT_SEL_SAME,
> +		.if20bit = false,
> +		.prots = LNmGCR0_PROTS_SGMII,
> +		.slew = 0b10,
> +		.offset_override = 0b0011111,
> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
> +		.postq_ratio = 0b00110,
> +		.adpt_eq = 0b110000,
> +	},
> +	[LYNX_PROTO_QSGMII] = {
> +		.frate_khz = 5000000,
> +		.rat_sel = LNmGCR0_RAT_SEL_SAME,
> +		.if20bit = true,
> +		.prots = LNmGCR0_PROTS_SGMII,
> +		.slew = 0b01,
> +		.offset_override = 0b0011111,
> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
> +		.postq_ratio = 0b00110,
> +		.adpt_eq = 0b110000,
> +		.amp_red = 0b000010,
> +	},
> +	[LYNX_PROTO_XFI] = {
> +		.frate_khz = 5156250,
> +		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
> +		.if20bit = true,
> +		.prots = LNmGCR0_PROTS_XFI,
> +		.slew = 0b01,
> +		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
> +		.offset_override = 0b1011111,
> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
> +		.postq_ratio = 0b00011,
> +		.adpt_eq = 0b110000,
> +		.amp_red = 0b000111,
> +	},
> +	[LYNX_PROTO_10GKR] = {
> +		.frate_khz = 5156250,
> +		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
> +		.prots = LNmGCR0_PROTS_XFI,
> +		.slew = 0b01,
> +		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
> +		.offset_override = 0b1011111,
> +		.teq = LNmTECR0_TEQ_TYPE_BOTH,
> +		.preq_ratio = 0b0011,
> +		.postq_ratio = 0b01100,
> +		.adpt_eq = 0b110000,
> +	},
> +};
> +
> +/**
> + * struct lynx_mode - A single configuration of a protocol controller
> + * @protos: A bitmask of the &enum lynx_protocol this mode supports
> + * @lanes: A bitmask of the lanes which will be used when this config is
> + *         selected
> + * @pccr: The number of the PCCR which contains this mode
> + * @idx: The index of the protocol controller. For example, SGMIIB would have
> + *       index 1.
> + * @cfg: The value to program into the controller to select this mode
> + *
> + * The serdes has multiple protocol controllers which can be each be selected
> + * independently. Depending on their configuration, they may use multiple lanes
> + * at once (e.g. AUI or PCIe x4). Additionally, multiple protocols may be
> + * supported by a single mode (XFI and 10GKR differ only in their protocol
> + * parameters).
> + */
> +struct lynx_mode {
> +	u16 protos;
> +	u8 lanes;
> +	u8 pccr;
> +	u8 idx;
> +	u8 cfg;
> +};
> +
> +static_assert(LYNX_PROTO_LAST - 1 <=
> +	      sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
> +static_assert(MAX_LANES <=
> +	      sizeof_field(struct lynx_mode, lanes) * BITS_PER_BYTE);
> +
> +#define CONF(_lanes, _protos, _pccr, _idx, _cfg) { \
> +	.lanes = _lanes, \
> +	.protos = _protos, \
> +	.pccr = _pccr, \
> +	.idx = _idx, \
> +	.cfg = _cfg, \
> +}
> +
> +#define CONF_SINGLE(lane, proto, pccr, idx, cfg) \
> +	CONF(BIT(lane), PROTO_MASK(proto), pccr, idx, cfg)
> +
> +#define CONF_1000BASEKX(lane, pccr, idx, cfg) \
> +	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX), \
> +	     pccr, idx, cfg)
> +
> +#define CONF_SGMII25(lane, pccr, idx, cfg) \
> +	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(SGMII25), \
> +	     pccr, idx, cfg)
> +
> +#define CONF_SGMII25KX(lane, pccr, idx, cfg) \
> +	CONF(BIT(lane), \
> +	     PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX) | PROTO_MASK(SGMII25), \
> +	     pccr, idx, cfg)
> +
> +#define CONF_XFI(lane, pccr, idx, cfg) \
> +	CONF(BIT(lane), PROTO_MASK(XFI) | PROTO_MASK(10GKR), pccr, idx, cfg)
> +
> +/**
> + * struct lynx_conf - Configuration for a particular serdes
> + * @modes: Valid protocol controller configurations
> + * @mode_count: Number of modes in @modes
> + * @lanes: Number of lanes
> + * @endian: Endianness of the registers
> + */
> +struct lynx_conf {
> +	const struct lynx_mode *modes;
> +	size_t mode_count;
> +	unsigned int lanes;
> +	enum regmap_endian endian;
> +};
> +
> +struct lynx_priv;
> +
> +/**
> + * struct lynx_clk - Driver data for the PLLs
> + * @hw: The clock hardware
> + * @serdes: The parent serdes
> + * @idx: Which PLL this clock is for
> + */
> +struct lynx_clk {
> +	struct clk_hw hw;
> +	struct lynx_priv *serdes;
> +	unsigned int idx;
> +};
> +
> +static struct lynx_clk *lynx_clk_hw_to_priv(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct lynx_clk, hw);
> +}
> +
> +/**
> + * struct lynx_priv - Driver data for the serdes
> + * @lock: A lock protecting "common" registers in @regmap, as well as the
> + *        members of this struct. Lane-specific registers are protected by the
> + *        phy's lock. PLL registers are protected by the clock's lock.
> + * @pll: The PLL clocks
> + * @ref: The reference clocks for the PLLs
> + * @dev: The serdes device
> + * @regmap: The backing regmap
> + * @conf: The configuration for this serdes
> + * @used_lanes: Bitmap of the lanes currently used by phys
> + * @groups: List of the created groups
> + */
> +struct lynx_priv {
> +	struct mutex lock;
> +	struct lynx_clk pll[2];
> +	struct clk *ref[2];
> +	struct device *dev;
> +	struct regmap *regmap;
> +	const struct lynx_conf *conf;
> +	unsigned int used_lanes;
> +	struct list_head groups;
> +};
> +
> +/**
> + * struct lynx_group - Driver data for a group of lanes
> + * @groups: List of other groups; protected by @serdes->lock.
> + * @phy: The associated phy
> + * @serdes: The parent serdes
> + * @pll: The currently-used pll
> + * @first_lane: The first lane in the group
> + * @last_lane: The last lane in the group
> + * @proto: The currently-configured protocol
> + * @users: Number of current users; protected by @serdes->lock.
> + */
> +struct lynx_group {
> +	struct list_head groups;
> +	struct phy *phy;
> +	struct lynx_priv *serdes;
> +	struct clk *pll;
> +	unsigned int first_lane;
> +	unsigned int last_lane;
> +	enum lynx_protocol proto;
> +	unsigned int users;
> +};
> +
> +static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
> +{
> +	unsigned int ret = 0;
> +
> +	WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
> +	return ret;
> +}
> +
> +static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
> +{
> +	WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
> +}
> +
> +/* XXX: The output rate is in kHz to avoid overflow on 32-bit arches */
> +
> +static void lynx_pll_disable(struct clk_hw *hw)
> +{
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	struct lynx_priv *serdes = clk->serdes;
> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);

no need for __func__ in dev_dbg pls

> +
> +	rstctl &= ~PLLaRSTCTL_SDRST_B;
> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
> +	ndelay(50);
> +	rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
> +	ndelay(100);
> +}
> +
> +static int lynx_pll_enable(struct clk_hw *hw)
> +{
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	struct lynx_priv *serdes = clk->serdes;
> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
> +
> +	rstctl |= PLLaRSTCTL_RSTREQ;
> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
> +
> +	rstctl &= ~PLLaRSTCTL_RSTREQ;
> +	rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
> +
> +	/* TODO: wait for the PLL to lock */

when will this be added?

> +
> +	return 0;
> +}
> +
> +static int lynx_pll_is_enabled(struct clk_hw *hw)
> +{
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	struct lynx_priv *serdes = clk->serdes;
> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
> +
> +	return rstctl & PLLaRSTCTL_RST_DONE && !(rstctl & PLLaRSTCTL_RST_ERR);
> +}
> +
> +static const u32 rfclk_sel_map[8] = {
> +	[0b000] = 100000000,
> +	[0b001] = 125000000,
> +	[0b010] = 156250000,
> +	[0b011] = 150000000,
> +};
> +
> +/**
> + * lynx_rfclk_to_sel() - Convert a reference clock rate to a selector
> + * @rate: The reference clock rate
> + *
> + * To allow for some variation in the reference clock rate, up to 100ppm of
> + * error is allowed.
> + *
> + * Return: An appropriate selector for @rate, or -%EINVAL.
> + */
> +static int lynx_rfclk_to_sel(u32 rate)
> +{
> +	int ret;
> +
> +	for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
> +		u32 rfclk_rate = rfclk_sel_map[ret];
> +		/* Allow an error of 100ppm */
> +		u32 error = rfclk_rate / 10000;
> +
> +		if (rate > rfclk_rate - error && rate < rfclk_rate + error)
> +			return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const u32 frate_sel_map[16] = {
> +	[0b0000] = 5000000,
> +	[0b0101] = 3750000,
> +	[0b0110] = 5156250,
> +	[0b0111] = 4000000,
> +	[0b1001] = 3125000,
> +	[0b1010] = 3000000,
> +};
> +
> +/**
> + * lynx_frate_to_sel() - Convert a VCO clock rate to a selector
> + * @rate_khz: The VCO frequency, in kHz
> + *
> + * Return: An appropriate selector for @rate_khz, or -%EINVAL.
> + */
> +static int lynx_frate_to_sel(u32 rate_khz)
> +{
> +	int ret;
> +
> +	for (ret = 0; ret < ARRAY_SIZE(frate_sel_map); ret++)
> +		if (frate_sel_map[ret] == rate_khz)
> +			return ret;
> +
> +	return -EINVAL;
> +}
> +
> +static u32 lynx_pll_ratio(u32 frate_sel, u32 rfclk_sel)
> +{
> +	u64 frate;
> +	u32 rfclk, error, ratio;
> +
> +	frate = frate_sel_map[frate_sel] * (u64)HZ_PER_KHZ;
> +	rfclk = rfclk_sel_map[rfclk_sel];
> +
> +	if (!frate || !rfclk)
> +		return 0;
> +
> +	ratio = div_u64_rem(frate, rfclk, &error);
> +	if (!error)
> +		return ratio;
> +	return 0;
> +}
> +
> +static unsigned long lynx_pll_recalc_rate(struct clk_hw *hw,
> +					unsigned long parent_rate)
> +{
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	struct lynx_priv *serdes = clk->serdes;
> +	u32 cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
> +	u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
> +	u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
> +	unsigned long ret;
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu)\n", __func__,
> +		clk->idx, parent_rate);
> +
> +	ret = mult_frac(parent_rate, lynx_pll_ratio(frate_sel, rfclk_sel),
> +			 HZ_PER_KHZ);
> +	return ret;
> +}
> +
> +static long lynx_pll_round_rate(struct clk_hw *hw, unsigned long rate_khz,
> +			      unsigned long *parent_rate)
> +{
> +	int frate_sel, rfclk_sel;
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	u32 ratio;
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
> +		clk->idx, rate_khz, *parent_rate);
> +
> +	frate_sel = lynx_frate_to_sel(rate_khz);
> +	if (frate_sel < 0)
> +		return frate_sel;
> +
> +	rfclk_sel = lynx_rfclk_to_sel(*parent_rate);
> +	if (rfclk_sel >= 0) {
> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> +		if (ratio)
> +			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
> +	}
> +
> +	for (rfclk_sel = 0;
> +	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
> +	     rfclk_sel++) {
> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> +		if (ratio) {
> +			*parent_rate = rfclk_sel_map[rfclk_sel];
> +			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int lynx_pll_set_rate(struct clk_hw *hw, unsigned long rate_khz,
> +			   unsigned long parent_rate)

This really sounds like a clk driver, why is this in phy driver. Ideally
this should a clock driver. Please move it to one..

> +{
> +	int frate_sel, rfclk_sel, ret;
> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
> +	struct lynx_priv *serdes = clk->serdes;
> +	u32 ratio, cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
> +
> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
> +		clk->idx, rate_khz, parent_rate);
> +
> +	frate_sel = lynx_frate_to_sel(rate_khz);
> +	if (frate_sel < 0)
> +		return frate_sel;
> +
> +	/* First try the existing rate */
> +	rfclk_sel = lynx_rfclk_to_sel(parent_rate);
> +	if (rfclk_sel >= 0) {
> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> +		if (ratio)
> +			goto got_rfclk;
> +	}
> +
> +	for (rfclk_sel = 0;
> +	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
> +	     rfclk_sel++) {
> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> +		if (ratio) {
> +			ret = clk_set_rate(serdes->ref[clk->idx],
> +					   rfclk_sel_map[rfclk_sel]);
> +			if (!ret)
> +				goto got_rfclk;
> +		}
> +	}
> +
> +	return ret;
> +
> +got_rfclk:
> +	cr0 &= ~(PLLaCR0_RFCLK_SEL | PLLaCR0_FRATE_SEL);
> +	cr0 |= FIELD_PREP(PLLaCR0_RFCLK_SEL, rfclk_sel);
> +	cr0 |= FIELD_PREP(PLLaCR0_FRATE_SEL, frate_sel);
> +	lynx_write(serdes, cr0, PLLaCR0(clk->idx));
> +	return 0;
> +}
> +
> +static const struct clk_ops lynx_pll_clk_ops = {
> +	.enable = lynx_pll_enable,
> +	.disable = lynx_pll_disable,
> +	.is_enabled = lynx_pll_is_enabled,
> +	.recalc_rate = lynx_pll_recalc_rate,
> +	.round_rate = lynx_pll_round_rate,
> +	.set_rate = lynx_pll_set_rate,
> +};

right, this should be a clk driver

> +
> +static struct clk_hw *lynx_clk_get(struct of_phandle_args *clkspec, void *data)
> +{
> +	struct lynx_priv *serdes = data;
> +
> +	if (clkspec->args_count != 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (clkspec->args[0] > 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	return &serdes->pll[clkspec->args[0]].hw;
> +}
> +
> +/**
> + * lynx_lane_bitmap() - Get a bitmap for a group of lanes
> + * @group: The group of lanes
> + *
> + * Return: A mask containing all bits between @group->first and @group->last
> + */
> +static unsigned int lynx_lane_bitmap(struct lynx_group *group)
> +{
> +	if (group->first_lane > group->last_lane)
> +		return GENMASK(group->first_lane, group->last_lane);
> +	else
> +		return GENMASK(group->last_lane, group->first_lane);
> +}
> +
> +static int lynx_init(struct phy *phy)
> +{
> +	int ret = 0;
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	struct lynx_priv *serdes = group->serdes;
> +	unsigned int lane_mask = lynx_lane_bitmap(group);
> +
> +	mutex_lock(&serdes->lock);
> +	if (serdes->used_lanes & lane_mask)
> +		ret = -EBUSY;
> +	else
> +		serdes->used_lanes |= lane_mask;
> +	mutex_unlock(&serdes->lock);
> +	return ret;
> +}
> +
> +static int lynx_exit(struct phy *phy)
> +{
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	struct lynx_priv *serdes = group->serdes;
> +
> +	clk_disable_unprepare(group->pll);
> +	clk_rate_exclusive_put(group->pll);
> +	group->pll = NULL;
> +
> +	mutex_lock(&serdes->lock);
> +	serdes->used_lanes &= ~lynx_lane_bitmap(group);
> +	mutex_unlock(&serdes->lock);
> +	return 0;
> +}
> +
> +/*
> + * This is tricky. If first_lane=1 and last_lane=0, the condition will see 2,
> + * 1, 0. But the loop body will see 1, 0. We do this to avoid underflow. We
> + * can't pull the same trick when incrementing, because then we might have to
> + * start at -1 if (e.g.) first_lane = 0.
> + */
> +#define for_range(val, start, end) \
> +	for (val = start < end ? start : start + 1; \
> +	     start < end ? val <= end : val-- > end; \
> +	     start < end ? val++ : 0)
> +#define for_each_lane(lane, group) \
> +	for_range(lane, group->first_lane, group->last_lane)
> +#define for_each_lane_reverse(lane, group) \
> +	for_range(lane, group->last_lane, group->first_lane)
> +
> +static int lynx_power_on(struct phy *phy)
> +{
> +	int i;
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	u32 gcr0;
> +
> +	for_each_lane(i, group) {
> +		gcr0 = lynx_read(group->serdes, LNmGCR0(i));
> +		gcr0 &= ~(LNmGCR0_RX_PD | LNmGCR0_TX_PD);
> +		lynx_write(group->serdes, gcr0, LNmGCR0(i));
> +
> +		usleep_range(15, 30);
> +		gcr0 |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
> +		lynx_write(group->serdes, gcr0, LNmGCR0(i));
> +	}
> +
> +	return 0;
> +}
> +
> +static void lynx_power_off_lane(struct lynx_priv *serdes, unsigned int lane)
> +{
> +	u32 gcr0 = lynx_read(serdes, LNmGCR0(lane));
> +
> +	gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
> +	gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
> +	lynx_write(serdes, gcr0, LNmGCR0(lane));
> +}
> +
> +static int lynx_power_off(struct phy *phy)
> +{
> +	unsigned int i;
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +
> +	for_each_lane_reverse(i, group)
> +		lynx_power_off_lane(group->serdes, i);
> +
> +	return 0;
> +}
> +
> +/**
> + * lynx_lookup_proto() - Convert a phy-subsystem mode to a protocol
> + * @mode: The mode to convert
> + * @submode: The submode of @mode
> + *
> + * Return: A corresponding serdes-specific mode
> + */
> +static enum lynx_protocol lynx_lookup_proto(enum phy_mode mode, int submode)
> +{
> +	switch (mode) {
> +	case PHY_MODE_ETHERNET:
> +		switch (submode) {
> +		case PHY_INTERFACE_MODE_SGMII:
> +		case PHY_INTERFACE_MODE_1000BASEX:
> +			return LYNX_PROTO_SGMII;
> +		case PHY_INTERFACE_MODE_2500BASEX:
> +			return LYNX_PROTO_SGMII25;
> +		case PHY_INTERFACE_MODE_QSGMII:
> +			return LYNX_PROTO_QSGMII;
> +		case PHY_INTERFACE_MODE_XGMII:
> +		case PHY_INTERFACE_MODE_10GBASER:
> +			return LYNX_PROTO_XFI;
> +		case PHY_INTERFACE_MODE_10GKR:
> +			return LYNX_PROTO_10GKR;
> +		default:
> +			return LYNX_PROTO_NONE;
> +		}
> +	/* Not implemented (yet) */
> +	case PHY_MODE_PCIE:
> +	case PHY_MODE_SATA:
> +	default:
> +		return LYNX_PROTO_NONE;
> +	}
> +}
> +
> +/**
> + * lynx_lookup_mode() - Get the mode for a group/protocol combination
> + * @group: The group of lanes to use
> + * @proto: The protocol to use
> + *
> + * Return: An appropriate mode to use, or %NULL if none match.
> + */
> +static const struct lynx_mode *lynx_lookup_mode(struct lynx_group *group,
> +					    enum lynx_protocol proto)
> +{
> +	int i;
> +	const struct lynx_conf *conf = group->serdes->conf;
> +
> +	for (i = 0; i < conf->mode_count; i++) {
> +		const struct lynx_mode *mode = &conf->modes[i];
> +
> +		if (BIT(proto) & mode->protos &&
> +		    lynx_lane_bitmap(group) == mode->lanes)
> +			return mode;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int lynx_validate(struct phy *phy, enum phy_mode phy_mode, int submode,
> +		       union phy_configure_opts *opts)
> +{
> +	enum lynx_protocol proto;
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	const struct lynx_mode *mode;
> +
> +	proto = lynx_lookup_proto(phy_mode, submode);
> +	if (proto == LYNX_PROTO_NONE)
> +		return -EINVAL;
> +
> +	/* Nothing to do */
> +	if (proto == group->proto)
> +		return 0;
> +
> +	mode = lynx_lookup_mode(group, proto);
> +	if (!mode)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * lynx_proto_mode_mask() - Get the mask for a PCCR config
> + * @mode: The mode to use
> + *
> + * Return: The mask, shifted down to the lsb.
> + */
> +static u32 lynx_proto_mode_mask(const struct lynx_mode *mode)
> +{
> +	switch (mode->pccr) {
> +	case 0x0:
> +		if (mode->protos & PROTO_MASK(PCIE))
> +			return PCCR0_PEXa_MASK;
> +		break;
> +	case 0x2:
> +		if (mode->protos & PROTO_MASK(SATA))
> +			return PCCR2_SATAa_MASK;
> +		break;
> +	case 0x8:
> +		if (mode->protos & PROTO_MASK(SGMII))
> +			return PCCR8_SGMIIa_MASK;
> +		break;
> +	case 0x9:
> +		if (mode->protos & PROTO_MASK(QSGMII))
> +			return PCCR9_QSGMIIa_MASK;
> +		break;
> +	case 0xB:
> +		if (mode->protos & PROTO_MASK(XFI))
> +			return PCCRB_XFIa_MASK;
> +		break;
> +	}
> +	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
> +	       lynx_proto_str[mode->protos], 'A' + mode->idx);
> +	return 0;
> +}
> +
> +/**
> + * lynx_proto_mode_shift() - Get the shift for a PCCR config
> + * @mode: The mode to use
> + *
> + * Return: The amount of bits to shift the mask.
> + */
> +static u32 lynx_proto_mode_shift(const struct lynx_mode *mode)
> +{
> +	switch (mode->pccr) {
> +	case 0x0:
> +		if (mode->protos & PROTO_MASK(PCIE))
> +			return PCCR0_PEXa_SHIFT(mode->idx);
> +		break;
> +	case 0x2:
> +		if (mode->protos & PROTO_MASK(SATA))
> +			return PCCR2_SATAa_SHIFT(mode->idx);
> +		break;
> +	case 0x8:
> +		if (mode->protos & PROTO_MASK(SGMII))
> +			return PCCR8_SGMIIa_SHIFT(mode->idx);
> +		break;
> +	case 0x9:
> +		if (mode->protos & PROTO_MASK(QSGMII))
> +			return PCCR9_QSGMIIa_SHIFT(mode->idx);
> +		break;
> +	case 0xB:
> +		if (mode->protos & PROTO_MASK(XFI))
> +			return PCCRB_XFIa_SHIFT(mode->idx);
> +		break;
> +	}
> +	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
> +	       lynx_proto_str[mode->protos], 'A' + mode->idx);
> +	return 0;
> +}
> +
> +/**
> + * lynx_proto_mode_get() - Get the current config for a PCCR mode
> + * @mode: The mode to use
> + * @pccr: The current value of the PCCR
> + *
> + * Return: The current value of the PCCR config for this mode
> + */
> +static u32 lynx_proto_mode_get(const struct lynx_mode *mode, u32 pccr)
> +{
> +	return (pccr >> lynx_proto_mode_shift(mode)) &
> +	       lynx_proto_mode_mask(mode);
> +}
> +
> +/**
> + * lynx_proto_mode_prep() - Configure a PCCR for a protocol
> + * @mode: The mode to use
> + * @pccr: The current value of the PCCR
> + * @proto: The protocol to configure
> + *
> + * This configures a PCCR for a mode and protocol. To disable a mode, pass
> + * %LYNX_PROTO_NONE as @proto. If @proto is 1000Base-KX, then the KX bit
> + * will be set.
> + *
> + * Return: The new value for the PCCR
> + */
> +static u32 lynx_proto_mode_prep(const struct lynx_mode *mode, u32 pccr,
> +				enum lynx_protocol proto)
> +{
> +	u32 shift = lynx_proto_mode_shift(mode);
> +
> +	pccr &= ~(lynx_proto_mode_mask(mode) << shift);
> +	if (proto != LYNX_PROTO_NONE)
> +		pccr |= mode->cfg << shift;
> +
> +	if (proto == LYNX_PROTO_1000BASEKX) {
> +		if (mode->pccr == 8)
> +			pccr |= PCCR8_SGMIIa_KX << shift;
> +		else
> +			pr_err("PCCR%X doesn't have a KX bit\n", mode->pccr);
> +	}
> +
> +	return pccr;
> +}
> +
> +#define abs_diff(a, b) ({ \
> +	typeof(a) _a = (a); \
> +	typeof(b) _b = (b); \
> +	_a > _b ? _a - _b : _b - _a; \
> +})
> +
> +static int lynx_set_mode(struct phy *phy, enum phy_mode phy_mode, int submode)
> +{
> +	enum lynx_protocol proto;
> +	const struct lynx_proto_params *params;
> +	const struct lynx_mode *old_mode = NULL, *new_mode;
> +	int i, pll, ret;
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	struct lynx_priv *serdes = group->serdes;
> +	u32 tmp;
> +	u32 gcr0 = 0, gcr1 = 0, recr0 = 0, tecr0 = 0;
> +	u32 gcr0_mask = 0, gcr1_mask = 0, recr0_mask = 0, tecr0_mask = 0;
> +
> +	proto = lynx_lookup_proto(phy_mode, submode);
> +	if (proto == LYNX_PROTO_NONE) {
> +		dev_dbg(&phy->dev, "unknown mode/submode %d/%d\n",
> +			phy_mode, submode);
> +		return -EINVAL;
> +	}
> +
> +	/* Nothing to do */
> +	if (proto == group->proto)
> +		return 0;
> +
> +	new_mode = lynx_lookup_mode(group, proto);
> +	if (!new_mode) {
> +		dev_dbg(&phy->dev, "could not find mode for %s on lanes %u to %u\n",
> +			lynx_proto_str[proto], group->first_lane,
> +			group->last_lane);
> +		return -EINVAL;
> +	}
> +
> +	if (group->proto != LYNX_PROTO_NONE) {
> +		old_mode = lynx_lookup_mode(group, group->proto);
> +		if (!old_mode) {
> +			dev_err(&phy->dev, "could not find mode for %s\n",
> +				lynx_proto_str[group->proto]);
> +			return -EBUSY;
> +		}
> +	}
> +
> +	clk_disable_unprepare(group->pll);
> +	clk_rate_exclusive_put(group->pll);
> +	group->pll = NULL;
> +
> +	/* First, try to use a PLL which already has the correct rate */
> +	params = &lynx_proto_params[proto];
> +	for (pll = 0; pll < ARRAY_SIZE(serdes->pll); pll++) {
> +		struct clk *clk = serdes->pll[pll].hw.clk;
> +		unsigned long rate = clk_get_rate(clk);
> +		unsigned long error = abs_diff(rate, params->frate_khz);
> +
> +		dev_dbg(&phy->dev, "pll%d has rate %lu\n", pll, rate);
> +		/* Accept up to 100ppm deviation */
> +		if ((!error || params->frate_khz / error > 10000) &&
> +		    !clk_set_rate_exclusive(clk, rate))
> +			goto got_pll;
> +		/* Someone else got a different rate first */
> +	}
> +
> +	/* If neither PLL has the right rate, try setting it */
> +	for (pll = 0; pll < 2; pll++) {
> +		ret = clk_set_rate_exclusive(serdes->pll[pll].hw.clk,
> +					     params->frate_khz);
> +		if (!ret)
> +			goto got_pll;
> +	}
> +
> +	dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
> +		params->frate_khz);
> +	return ret;
> +
> +got_pll:
> +	group->pll = serdes->pll[pll].hw.clk;
> +	clk_prepare_enable(group->pll);
> +
> +	gcr0_mask |= LNmGCR0_RRAT_SEL | LNmGCR0_TRAT_SEL;
> +	gcr0_mask |= LNmGCR0_RPLL_LES | LNmGCR0_TPLL_LES;
> +	gcr0_mask |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
> +	gcr0_mask |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
> +	gcr0_mask |= LNmGCR0_IF20BIT_EN | LNmGCR0_PROTS;
> +	gcr0 |= FIELD_PREP(LNmGCR0_RPLL_LES, !pll);
> +	gcr0 |= FIELD_PREP(LNmGCR0_TPLL_LES, !pll);
> +	gcr0 |= FIELD_PREP(LNmGCR0_RRAT_SEL, params->rat_sel);
> +	gcr0 |= FIELD_PREP(LNmGCR0_TRAT_SEL, params->rat_sel);
> +	gcr0 |= FIELD_PREP(LNmGCR0_IF20BIT_EN, params->if20bit);
> +	gcr0 |= FIELD_PREP(LNmGCR0_PROTS, params->prots);
> +
> +	gcr1_mask |= LNmGCR1_RDAT_INV | LNmGCR1_TDAT_INV;
> +	gcr1_mask |= LNmGCR1_OPAD_CTL | LNmGCR1_REIDL_TH;
> +	gcr1_mask |= LNmGCR1_REIDL_EX_SEL | LNmGCR1_REIDL_ET_SEL;
> +	gcr1_mask |= LNmGCR1_REIDL_EX_MSB | LNmGCR1_REIDL_ET_MSB;
> +	gcr1_mask |= LNmGCR1_REQ_CTL_SNP | LNmGCR1_REQ_CDR_SNP;
> +	gcr1_mask |= LNmGCR1_TRSTDIR | LNmGCR1_REQ_BIN_SNP;
> +	gcr1_mask |= LNmGCR1_ISLEW_RCTL | LNmGCR1_OSLEW_RCTL;
> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_TH, params->reidl_th);
> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_SEL, params->reidl_ex & 3);
> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_SEL, params->reidl_et & 3);
> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_MSB, params->reidl_ex >> 2);
> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_MSB, params->reidl_et >> 2);
> +	gcr1 |= FIELD_PREP(LNmGCR1_TRSTDIR,
> +			   group->first_lane > group->last_lane);
> +	gcr1 |= FIELD_PREP(LNmGCR1_ISLEW_RCTL, params->slew);
> +	gcr1 |= FIELD_PREP(LNmGCR1_OSLEW_RCTL, params->slew);
> +
> +	recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
> +	recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
> +	recr0_mask |= LNmRECR0_BASE_WAND | LNmRECR0_OSETOVD;
> +	if (params->gain) {
> +		recr0 |= FIELD_PREP(LNmRECR0_GK2OVD, params->gain);
> +		recr0 |= FIELD_PREP(LNmRECR0_GK3OVD, params->gain);
> +		recr0 |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
> +	}
> +	recr0 |= FIELD_PREP(LNmRECR0_BASE_WAND, params->baseline_wander);
> +	recr0 |= FIELD_PREP(LNmRECR0_OSETOVD, params->offset_override);
> +
> +	tecr0_mask |= LNmTECR0_TEQ_TYPE;
> +	tecr0_mask |= LNmTECR0_SGN_PREQ | LNmTECR0_RATIO_PREQ;
> +	tecr0_mask |= LNmTECR0_SGN_POST1Q | LNmTECR0_RATIO_PST1Q;
> +	tecr0_mask |= LNmTECR0_ADPT_EQ | LNmTECR0_AMP_RED;
> +	tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, params->teq);
> +	if (params->preq_ratio) {
> +		tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
> +		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
> +	}
> +	if (params->postq_ratio) {
> +		tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
> +		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
> +	}
> +	tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
> +	tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
> +
> +	mutex_lock(&serdes->lock);
> +
> +	/* Disable the old controller */
> +	if (old_mode) {
> +		tmp = lynx_read(serdes, PCCRn(old_mode->pccr));
> +		tmp = lynx_proto_mode_prep(old_mode, tmp, LYNX_PROTO_NONE);
> +		lynx_write(serdes, tmp, PCCRn(old_mode->pccr));
> +
> +		if (old_mode->protos & PROTO_MASK(SGMII)) {
> +			tmp = lynx_read(serdes, SGMIIaCR1(old_mode->idx));
> +			tmp &= SGMIIaCR1_SGPCS_EN;
> +			lynx_write(serdes, tmp, SGMIIaCR1(old_mode->idx));
> +		}
> +	}
> +
> +	for_each_lane_reverse(i, group) {
> +		tmp = lynx_read(serdes, LNmGCR0(i));
> +		tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
> +		lynx_write(serdes, tmp, LNmGCR0(i));
> +		ndelay(50);
> +
> +		tmp &= ~gcr0_mask;
> +		tmp |= gcr0;
> +		tmp |= FIELD_PREP(LNmGCR0_FIRST_LANE, i == group->first_lane);
> +		lynx_write(serdes, tmp, LNmGCR0(i));
> +
> +		tmp = lynx_read(serdes, LNmGCR1(i));
> +		tmp &= ~gcr1_mask;
> +		tmp |= gcr1;
> +		lynx_write(serdes, tmp, LNmGCR1(i));
> +
> +		tmp = lynx_read(serdes, LNmRECR0(i));
> +		tmp &= ~recr0_mask;
> +		tmp |= recr0;
> +		lynx_write(serdes, tmp, LNmRECR0(i));
> +
> +		tmp = lynx_read(serdes, LNmTECR0(i));
> +		tmp &= ~tecr0_mask;
> +		tmp |= tecr0;
> +		lynx_write(serdes, tmp, LNmTECR0(i));
> +
> +		tmp = lynx_read(serdes, LNmTTLCR0(i));
> +		tmp &= ~LNmTTLCR0_FLT_SEL;
> +		tmp |= FIELD_PREP(LNmTTLCR0_FLT_SEL, params->flt_sel);
> +		lynx_write(serdes, tmp, LNmTTLCR0(i));
> +
> +		ndelay(120);
> +		tmp = lynx_read(serdes, LNmGCR0(i));
> +		tmp |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
> +		lynx_write(serdes, tmp, LNmGCR0(i));
> +	}
> +
> +	if (proto == LYNX_PROTO_1000BASEKX) {
> +		/* FIXME: this races with clock updates */
> +		tmp = lynx_read(serdes, PLLaCR0(pll));
> +		tmp &= ~PLLaCR0_DLYDIV_SEL;
> +		tmp |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, 1);
> +		lynx_write(serdes, tmp, PLLaCR0(pll));
> +	}
> +
> +	/* Enable the new controller */
> +	tmp = lynx_read(serdes, PCCRn(new_mode->pccr));
> +	tmp = lynx_proto_mode_prep(new_mode, tmp, proto);
> +	lynx_write(serdes, tmp, PCCRn(new_mode->pccr));
> +
> +	if (new_mode->protos & PROTO_MASK(SGMII)) {
> +		tmp = lynx_read(serdes, SGMIIaCR1(new_mode->idx));
> +		tmp |= SGMIIaCR1_SGPCS_EN;
> +		lynx_write(serdes, tmp, SGMIIaCR1(new_mode->idx));
> +	}
> +
> +	mutex_unlock(&serdes->lock);
> +
> +	group->proto = proto;
> +	dev_dbg(&phy->dev, "set mode to %s on lanes %u to %u\n",
> +		lynx_proto_str[proto], group->first_lane, group->last_lane);
> +	return 0;
> +}
> +
> +static void lynx_release(struct phy *phy)
> +{
> +	struct lynx_group *group = phy_get_drvdata(phy);
> +	struct lynx_priv *serdes = group->serdes;
> +
> +	mutex_lock(&serdes->lock);
> +	if (--group->users) {
> +		mutex_unlock(&serdes->lock);
> +		return;
> +	}
> +	list_del(&group->groups);
> +	mutex_unlock(&serdes->lock);
> +
> +	phy_destroy(phy);
> +	kfree(group);
> +}
> +
> +static const struct phy_ops lynx_phy_ops = {
> +	.init = lynx_init,
> +	.exit = lynx_exit,
> +	.power_on = lynx_power_on,
> +	.power_off = lynx_power_off,
> +	.set_mode = lynx_set_mode,
> +	.validate = lynx_validate,
> +	.release = lynx_release,
> +	.owner = THIS_MODULE,
> +};
> +
> +static struct phy *lynx_xlate(struct device *dev, struct of_phandle_args *args)
> +{
> +	struct phy *phy;
> +	struct list_head *head;
> +	struct lynx_group *group;
> +	struct lynx_priv *serdes = dev_get_drvdata(dev);
> +	unsigned int last_lane;
> +
> +	if (args->args_count == 1)
> +		last_lane = args->args[0];
> +	else if (args->args_count == 2)
> +		last_lane = args->args[1];
> +	else
> +		return ERR_PTR(-EINVAL);
> +
> +	mutex_lock(&serdes->lock);
> +
> +	/* Look for an existing group */
> +	list_for_each(head, &serdes->groups) {
> +		group = container_of(head, struct lynx_group, groups);
> +		if (group->first_lane == args->args[0] &&
> +		    group->last_lane == last_lane) {
> +			group->users++;
> +			return group->phy;
> +		}
> +	}
> +
> +	/* None found, create our own */
> +	group = kzalloc(sizeof(*group), GFP_KERNEL);
> +	if (!group) {
> +		mutex_unlock(&serdes->lock);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	group->serdes = serdes;
> +	group->first_lane = args->args[0];
> +	group->last_lane = last_lane;
> +	group->users = 1;
> +	phy = phy_create(dev, NULL, &lynx_phy_ops);
> +	if (IS_ERR(phy)) {
> +		kfree(group);
> +	} else {
> +		group->phy = phy;
> +		phy_set_drvdata(phy, group);
> +		list_add(&group->groups, &serdes->groups);
> +	}
> +
> +	mutex_unlock(&serdes->lock);
> +	return phy;
> +}
> +
> +static int lynx_probe(struct platform_device *pdev)
> +{
> +	bool grabbed_clocks = false;
> +	int i, ret;
> +	struct device *dev = &pdev->dev;
> +	struct lynx_priv *serdes;
> +	struct regmap_config regmap_config = {};
> +	const struct lynx_conf *conf;
> +	struct resource *res;
> +	void __iomem *base;
> +
> +	serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
> +	if (!serdes)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, serdes);
> +	mutex_init(&serdes->lock);
> +	INIT_LIST_HEAD(&serdes->groups);
> +	serdes->dev = dev;
> +
> +	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
> +	if (IS_ERR(base)) {
> +		ret = PTR_ERR(base);
> +		dev_err_probe(dev, ret, "could not get/map registers\n");
> +		return ret;
> +	}
> +
> +	conf = device_get_match_data(dev);
> +	serdes->conf = conf;
> +	regmap_config.reg_bits = 32;
> +	regmap_config.reg_stride = 4;
> +	regmap_config.val_bits = 32;
> +	regmap_config.val_format_endian = conf->endian;
> +	regmap_config.max_register = res->end - res->start;
> +	regmap_config.disable_locking = true;
> +	serdes->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
> +	if (IS_ERR(serdes->regmap)) {
> +		ret = PTR_ERR(serdes->regmap);
> +		dev_err_probe(dev, ret, "could not create regmap\n");
> +		return ret;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(serdes->ref); i++) {
> +		static const char fmt[] = "ref%d";
> +		char name[sizeof(fmt)];
> +
> +		snprintf(name, sizeof(name), fmt, i);
> +		serdes->ref[i] = devm_clk_get(dev, name);
> +		if (IS_ERR(serdes->ref[i])) {
> +			ret = PTR_ERR(serdes->ref[i]);
> +			dev_err_probe(dev, ret, "could not get %s\n", name);
> +			return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(serdes->pll); i++) {
> +		static const char fmt[] = "%s.pll%d";
> +		char *name;
> +		const struct clk_hw *ref_hw[] = {
> +			__clk_get_hw(serdes->ref[i]),
> +		};
> +		size_t len;
> +		struct clk_init_data init = {};
> +
> +		len = snprintf(NULL, 0, fmt, pdev->name, i);
> +		name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
> +		if (!name)
> +			return -ENOMEM;
> +
> +		snprintf(name, len + 1, fmt, pdev->name, i);
> +		init.name = name;
> +		init.ops = &lynx_pll_clk_ops;
> +		init.parent_hws = ref_hw;
> +		init.num_parents = 1;
> +		init.flags = CLK_SET_RATE_GATE | CLK_GET_RATE_NOCACHE;
> +		init.flags |= CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
> +
> +		serdes->pll[i].hw.init = &init;
> +		serdes->pll[i].serdes = serdes;
> +		serdes->pll[i].idx = i;
> +		ret = devm_clk_hw_register(dev, &serdes->pll[i].hw);
> +		if (ret) {
> +			dev_err_probe(dev, ret, "could not register %s\n",
> +				      name);
> +			return ret;
> +		}
> +	}
> +
> +	ret = devm_of_clk_add_hw_provider(dev, lynx_clk_get, serdes);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "could not register clock provider\n");
> +		return ret;
> +	}
> +
> +	/* Deselect anything configured by the RCW/bootloader */
> +	for (i = 0; i < conf->mode_count; i++) {
> +		const struct lynx_mode *mode = &conf->modes[i];
> +		u32 pccr = lynx_read(serdes, PCCRn(mode->pccr));
> +
> +		if (lynx_proto_mode_get(mode, pccr) == mode->cfg) {
> +			if (mode->protos & UNSUPPORTED_PROTOS) {
> +				/* Don't mess with modes we don't support */
> +				serdes->used_lanes |= mode->lanes;
> +				if (grabbed_clocks)
> +					continue;
> +
> +				grabbed_clocks = true;
> +				clk_prepare_enable(serdes->pll[0].hw.clk);
> +				clk_prepare_enable(serdes->pll[1].hw.clk);
> +				clk_rate_exclusive_get(serdes->pll[0].hw.clk);
> +				clk_rate_exclusive_get(serdes->pll[1].hw.clk);
> +			} else {
> +				/* Otherwise, clear out the existing config */
> +				pccr = lynx_proto_mode_prep(mode, pccr,
> +							    LYNX_PROTO_NONE);
> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
> +			}
> +
> +			/* Disable the SGMII PCS until we're ready for it */
> +			if (mode->protos & LYNX_PROTO_SGMII) {
> +				u32 cr1;
> +
> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
> +			}
> +		}
> +	}
> +
> +	/* Power off all lanes; used ones will be powered on later */
> +	for (i = 0; i < conf->lanes; i++)
> +		lynx_power_off_lane(serdes, i);
> +
> +	ret = PTR_ERR_OR_ZERO(devm_of_phy_provider_register(dev, lynx_xlate));
> +	if (ret)
> +		dev_err_probe(dev, ret, "could not register phy provider\n");
> +	else
> +		dev_info(dev, "probed with %d lanes\n", conf->lanes);
> +	return ret;
> +}
> +
> +/*
> + * XXX: For SerDes1, lane A uses pins SD1_RX3_P/N! That is, the lane numbers
> + * and pin numbers are _reversed_. In addition, the PCCR documentation is
> + * _inconsistent_ in its usage of these terms!
> + *
> + * PCCR "Lane 0" refers to...
> + * ==== =====================
> + *    0 Lane A
> + *    2 Lane A
> + *    8 Lane A
> + *    9 Lane A
> + *    B Lane D!
> + */
> +static const struct lynx_mode ls1046a_modes1[] = {
> +	CONF_SINGLE(1, PCIE, 0x0, 1, 0b001), /* PCIe.1 x1 */
> +	CONF_1000BASEKX(0, 0x8, 0, 0b001), /* SGMII.6 */
> +	CONF_SGMII25KX(1, 0x8, 1, 0b001), /* SGMII.5 */
> +	CONF_SGMII25KX(2, 0x8, 2, 0b001), /* SGMII.10 */
> +	CONF_SGMII25KX(3, 0x8, 3, 0b001), /* SGMII.9 */
> +	CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001), /* QSGMII.6,5,10,1 */
> +	CONF_XFI(2, 0xB, 0, 0b010), /* XFI.10 */
> +	CONF_XFI(3, 0xB, 1, 0b001), /* XFI.9 */
> +};
> +
> +static const struct lynx_conf ls1046a_conf1 = {
> +	.modes = ls1046a_modes1,
> +	.mode_count = ARRAY_SIZE(ls1046a_modes1),
> +	.lanes = 4,
> +	.endian = REGMAP_ENDIAN_BIG,
> +};
> +
> +static const struct lynx_mode ls1046a_modes2[] = {
> +	CONF_SINGLE(0, PCIE, 0x0, 0, 0b001), /* PCIe.1 x1 */
> +	CONF(GENMASK(3, 0), PROTO_MASK(PCIE), 0x0, 0, 0b011), /* PCIe.1 x4 */
> +	CONF_SINGLE(2, PCIE, 0x0, 2, 0b001), /* PCIe.2 x1 */
> +	CONF(GENMASK(3, 2), PROTO_MASK(PCIE), 0x0, 2, 0b010), /* PCIe.3 x2 */
> +	CONF_SINGLE(3, PCIE, 0x0, 2, 0b011), /* PCIe.3 x1 */
> +	CONF_SINGLE(3, SATA, 0x2, 0, 0b001), /* SATA */
> +	CONF_1000BASEKX(1, 0x8, 1, 0b001), /* SGMII.2 */
> +};
> +
> +static const struct lynx_conf ls1046a_conf2 = {
> +	.modes = ls1046a_modes2,
> +	.mode_count = ARRAY_SIZE(ls1046a_modes2),
> +	.lanes = 4,
> +	.endian = REGMAP_ENDIAN_BIG,
> +};
> +
> +static const struct of_device_id lynx_of_match[] = {
> +	{ .compatible = "fsl,ls1046a-serdes-1", .data = &ls1046a_conf1 },
> +	{ .compatible = "fsl,ls1046a-serdes-2", .data = &ls1046a_conf2 },
> +};
> +MODULE_DEVICE_TABLE(of, lynx_of_match);
> +
> +static struct platform_driver lynx_driver = {
> +	.probe = lynx_probe,
> +	.driver = {
> +		.name = "qoriq_serdes",
> +		.of_match_table = lynx_of_match,
> +	},
> +};
> +module_platform_driver(lynx_driver);
> +
> +MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
> +MODULE_DESCRIPTION("Lynx 10G SerDes driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.35.1.1320.gc452695387.dirty

-- 
~Vinod

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-07-05  6:12   ` Vinod Koul
@ 2022-07-05 15:29     ` Sean Anderson
  2022-07-06 16:57       ` Vinod Koul
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Anderson @ 2022-07-05 15:29 UTC (permalink / raw)
  To: Vinod Koul
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Ioana Ciornei, Jonathan Corbet,
	Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring,
	devicetree, linux-doc, linux-phy

Hi Vinod,

On 7/5/22 2:12 AM, Vinod Koul wrote:
> On 28-06-22, 18:13, Sean Anderson wrote:
>> This adds support for the Lynx 10G "SerDes" devices found on various NXP
>> QorIQ SoCs. There may be up to four SerDes devices on each SoC, each
>> supporting up to eight lanes. Protocol support for each SerDes is highly
>> heterogeneous, with each SoC typically having a totally different
>> selection of supported protocols for each lane. Additionally, the SerDes
>> devices on each SoC also have differing support. One SerDes will
>> typically support Ethernet on most lanes, while the other will typically
>> support PCIe on most lanes.
>> 
>> There is wide hardware support for this SerDes. I have not done extensive
>> digging, but it seems to be used on almost every QorIQ device, including
>> the AMP and Layerscape series. Because each SoC typically has specific
>> instructions and exceptions for its SerDes, I have limited the initial
>> scope of this module to just the LS1046A. Additionally, I have only added
>> support for Ethernet protocols. There is not a great need for dynamic
>> reconfiguration for other protocols (SATA and PCIe handle rate changes in
>> hardware), so support for them may never be added.
>> 
>> Nevertheless, I have tried to provide an obvious path for adding support
>> for other SoCs as well as other protocols. SATA just needs support for
>> configuring LNmSSCR0. PCIe may need to configure the equalization
>> registers. It also uses multiple lanes. I have tried to write the driver
>> with multi-lane support in mind, so there should not need to be any large
>> changes. Although there are 6 protocols supported, I have only tested SGMII
>> and XFI. The rest have been implemented as described in the datasheet.
>> 
>> The PLLs are modeled as clocks proper. This lets us take advantage of the
>> existing clock infrastructure. I have not given the same treatment to the
>> lane "clocks" (dividers) because they need to be programmed in-concert with
>> the rest of the lane settings. One tricky thing is that the VCO (pll) rate
>> exceeds 2^32 (maxing out at around 5GHz). This will be a problem on 32-bit
>> platforms, since clock rates are stored as unsigned longs. To work around
>> this, the pll clock rate is generally treated in units of kHz.
>> 
>> The PLLs are configured rather interestingly. Instead of the usual direct
>> programming of the appropriate divisors, the input and output clock rates
>> are selected directly. Generally, the only restriction is that the input
>> and output must be integer multiples of each other. This suggests some kind
>> of internal look-up table. The datasheets generally list out the supported
>> combinations explicitly, and not all input/output combinations are
>> documented. I'm not sure if this is due to lack of support, or due to an
>> oversight. If this becomes an issue, then some combinations can be
>> blacklisted (or whitelisted). This may also be necessary for other SoCs
>> which have more stringent clock requirements.
>> 
>> The general API call list for this PHY is documented under the driver-api
>> docs. I think this is rather standard, except that most driverts configure
>> the mode (protocol) at xlate-time. Unlike some other phys where e.g. PCIe
>> x4 will use 4 separate phys all configured for PCIe, this driver uses one
>> phy configured to use 4 lanes. This is because while the individual lanes
>> may be configured individually, the protocol selection acts on all lanes at
>> once. Additionally, the order which lanes should be configured in is
>> specified by the datasheet.  To coordinate this, lanes are reserved in
>> phy_init, and released in phy_exit.
>> 
>> When getting a phy, if a phy already exists for those lanes, it is reused.
>> This is to make things like QSGMII work. Four MACs will all want to ensure
>> that the lane is configured properly, and we need to ensure they can all
>> call phy_init, etc. There is refcounting for phy_init and phy_power_on, so
>> the phy will only be powered on once. However, there is no refcounting for
>> phy_set_mode. A "rogue" MAC could set the mode to something non-QSGMII and
>> break the other MACs. Perhaps there is an opportunity for future
>> enhancement here.
>> 
>> This driver was written with reference to the LS1046A reference manual.
>> However, it was informed by reference manuals for all processors with
>> mEMACs, especially the T4240 (which appears to have a "maxed-out"
>> configuration).
>> 
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>> As noted later on in this series (in the phylink conversion patch), this
>> driver does not quite function properly. When the bootloader is
>> instructed to not configure the SerDes, only one lane comes up.
>> 
>> Changes in v2:
>> - Clear SGMIIaCR1_PCS_EN during probe
>> - Fix not clearing group->pll after disabling it
>> - Handle 1000Base-KX in lynx_proto_mode_prep
>> - Power off lanes during probe
>> - Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
>> - Rename driver to Lynx 10G (etc.)
>> - Support 1 and 2 phy-cells
>> 
>>  Documentation/driver-api/phy/index.rst   |    1 +
>>  Documentation/driver-api/phy/qoriq.rst   |   93 ++
>>  MAINTAINERS                              |    6 +
>>  drivers/phy/freescale/Kconfig            |   19 +
>>  drivers/phy/freescale/Makefile           |    1 +
>>  drivers/phy/freescale/phy-fsl-lynx-10g.c | 1483 ++++++++++++++++++++++
>>  6 files changed, 1603 insertions(+)
>>  create mode 100644 Documentation/driver-api/phy/qoriq.rst
>>  create mode 100644 drivers/phy/freescale/phy-fsl-lynx-10g.c
>> 
>> diff --git a/Documentation/driver-api/phy/index.rst b/Documentation/driver-api/phy/index.rst
>> index 69ba1216de72..cc7ded8b969c 100644
>> --- a/Documentation/driver-api/phy/index.rst
>> +++ b/Documentation/driver-api/phy/index.rst
>> @@ -7,6 +7,7 @@ Generic PHY Framework
>>  .. toctree::
>>  
>>     phy
>> +   qoriq
>>     samsung-usb2
>>  
>>  .. only::  subproject and html
>> diff --git a/Documentation/driver-api/phy/qoriq.rst b/Documentation/driver-api/phy/qoriq.rst
>> new file mode 100644
>> index 000000000000..cbc2ac9ca4aa
>> --- /dev/null
>> +++ b/Documentation/driver-api/phy/qoriq.rst
>> @@ -0,0 +1,93 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +=======================
>> +QorIQ SerDes (Lynx 10G)
>> +=======================
>> +
>> +Using this phy
>> +--------------
>> +
>> +The general order of calls should be::
>> +
>> +    [devm_][of_]phy_get()
>> +    phy_init()
>> +    phy_power_on()
>> +    phy_set_mode[_ext]()
>> +    ...
>> +    phy_power_off()
>> +    phy_exit()
>> +    [[of_]phy_put()]
> 
> Why is this required here? This should conform to generic phy sequences
> as documented in Documentation/driver-api/phy/phy.rst

That file does not cover the order of calls. I had a look around, and not all phy
consumers use the same order for these calls, nor does every consumer make all calls.
For maximum clarity, I have documented the specific order which this driver expects.

>> +
>> +:c:func:`phy_get` just gets (or creates) a new :c:type:`phy` with the lanes
>> +described in the phandle. :c:func:`phy_init` is what actually reserves the
>> +lanes for use. Unlike some other drivers, when the phy is created, there is no
>> +default protocol. :c:func:`phy_set_mode <phy_set_mode_ext>` must be called in
>> +order to set the protocol.
>> +
>> +Supporting SoCs
>> +---------------
>> +
>> +Each new SoC needs a :c:type:`struct lynx_conf <lynx_conf>` for each SerDes.
>> +The most important member is `modes`, which is an array of :c:type:`struct
>> +lynx_mode <lynx_mode>`. Each "mode" represents a configuration which can be
>> +programmed into a protocol control register. Modes can support multiple lanes
>> +(such for PCIe x2 or x4), as well as multiple protocols (such as SGMII and
>> +1000Base-KX). There are several helper macros to make configuring each mode
>> +easier. It is important that the list of modes is complete, even if not all
>> +protocols are supported. This lets the driver know which lanes are available,
>> +and which have been configured by the RCW.
>> +
>> +If a protocol is missing, add it to :c:type:`enum lynx_protocol
>> +<lynx_protocol>`, and to ``UNSUPPORTED_PROTOS``. If the PCCR shifts/masks for
>> +your protocol are missing, you will need to add them to
>> +:c:func:`lynx_proto_mode_mask` and :c:func:`lynx_proto_mode_shift`.
>> +
>> +For example, the configuration for SerDes1 of the LS1046A is::
>> +
>> +    static const struct lynx_mode ls1046a_modes1[] = {
>> +        CONF_SINGLE(1, PCIE, 0x0, 1, 0b001),
>> +        CONF_1000BASEKX(0, 0x8, 0, 0b001),
>> +        CONF_SGMII25KX(1, 0x8, 1, 0b001),
>> +        CONF_SGMII25KX(2, 0x8, 2, 0b001),
>> +        CONF_SGMII25KX(3, 0x8, 3, 0b001),
>> +        CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001),
>> +        CONF_XFI(2, 0xB, 0, 0b010),
>> +        CONF_XFI(3, 0xB, 1, 0b001),
>> +    };
>> +
>> +    static const struct lynx_conf ls1046a_conf1 = {
>> +        .modes = ls1046a_modes1,
>> +        .mode_count = ARRAY_SIZE(ls1046a_modes1),
>> +        .lanes = 4,
>> +        .endian = REGMAP_ENDIAN_BIG,
>> +    };
>> +
>> +There is an additional set of configuration for SerDes2, which supports a
>> +different set of modes. Both configurations should be added to the match
>> +table::
>> +
>> +    { .compatible = "fsl,ls1046-serdes-1", .data = &ls1046a_conf1 },
>> +    { .compatible = "fsl,ls1046-serdes-2", .data = &ls1046a_conf2 },
>> +
>> +Supporting Protocols
>> +--------------------
>> +
>> +Each protocol is a combination of values which must be programmed into the lane
>> +registers. To add a new protocol, first add it to :c:type:`enum lynx_protocol
>> +<lynx_protocol>`. If it is in ``UNSUPPORTED_PROTOS``, remove it. Add a new
>> +entry to `lynx_proto_params`, and populate the appropriate fields. You may need
>> +to add some new members to support new fields. Modify `lynx_lookup_proto` to
>> +map the :c:type:`enum phy_mode <phy_mode>` to :c:type:`enum lynx_protocol
>> +<lynx_protocol>`. Ensure that :c:func:`lynx_proto_mode_mask` and
>> +:c:func:`lynx_proto_mode_shift` have been updated with support for your
>> +protocol.
>> +
>> +You may need to modify :c:func:`lynx_set_mode` in order to support your
>> +procotol. This can happen when you have added members to :c:type:`struct
>> +lynx_proto_params <lynx_proto_params>`. It can also happen if you have specific
>> +clocking requirements, or protocol-specific registers to program.
>> +
>> +Internal API Reference
>> +----------------------
>> +
>> +.. kernel-doc:: drivers/phy/freescale/phy-fsl-lynx-10g.c
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ca95b1833b97..ef65e2acdb48 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7977,6 +7977,12 @@ F:	drivers/ptp/ptp_qoriq.c
>>  F:	drivers/ptp/ptp_qoriq_debugfs.c
>>  F:	include/linux/fsl/ptp_qoriq.h
>>  
>> +FREESCALE QORIQ SERDES DRIVER
>> +M:	Sean Anderson <sean.anderson@seco.com>
>> +S:	Maintained
>> +F:	Documentation/driver-api/phy/qoriq.rst
>> +F:	drivers/phy/freescale/phy-qoriq.c
>> +
>>  FREESCALE QUAD SPI DRIVER
>>  M:	Han Xu <han.xu@nxp.com>
>>  L:	linux-spi@vger.kernel.org
>> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
>> index f9c54cd02036..857b4d123515 100644
>> --- a/drivers/phy/freescale/Kconfig
>> +++ b/drivers/phy/freescale/Kconfig
>> @@ -38,3 +38,22 @@ config PHY_FSL_LYNX_28G
>>  	  found on NXP's Layerscape platforms such as LX2160A.
>>  	  Used to change the protocol running on SerDes lanes at runtime.
>>  	  Only useful for a restricted set of Ethernet protocols.
>> +
>> +config PHY_FSL_LYNX_10G
>> +	tristate "Freescale Layerscale Lynx 10G SerDes support"
>> +	select GENERIC_PHY
>> +	select REGMAP_MMIO
>> +	help
>> +	  This adds support for the Lynx "SerDes" devices found on various QorIQ
>> +	  SoCs. There may be up to four SerDes devices on each SoC, and each
>> +	  device supports up to eight lanes. The SerDes is configured by default
>> +	  by the RCW, but this module is necessary in order to support dynamic
>> +	  reconfiguration (such as to support 1G and 10G ethernet on the same
>> +	  interface). The hardware supports a variety of protocols, including
>> +	  Ethernet, SATA, PCIe, and more exotic links such as Interlaken and
>> +	  Aurora. This driver only supports Ethernet, but it will try not to
>> +	  touch lanes configured for other protocols.
>> +
>> +	  If you have a QorIQ processor and want to dynamically reconfigure your
>> +	  SerDes, say Y. If this driver is compiled as a module, it will be
>> +	  named phy-qoriq.
>> diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
>> index 3518d5dbe8a7..aa4374ed217c 100644
>> --- a/drivers/phy/freescale/Makefile
>> +++ b/drivers/phy/freescale/Makefile
>> @@ -2,4 +2,5 @@
>>  obj-$(CONFIG_PHY_FSL_IMX8MQ_USB)	+= phy-fsl-imx8mq-usb.o
>>  obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY)	+= phy-fsl-imx8-mipi-dphy.o
>>  obj-$(CONFIG_PHY_FSL_IMX8M_PCIE)	+= phy-fsl-imx8m-pcie.o
>> +obj-$(CONFIG_PHY_FSL_LYNX_10G)		+= phy-fsl-lynx-10g.o
>>  obj-$(CONFIG_PHY_FSL_LYNX_28G)		+= phy-fsl-lynx-28g.o
>> diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c
>> new file mode 100644
>> index 000000000000..480bd493fbc2
>> --- /dev/null
>> +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
>> @@ -0,0 +1,1483 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/math64.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/phy.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/regmap.h>
>> +#include <linux/units.h>
>> +
>> +#define PLL_STRIDE	0x20
>> +#define PLLa(a, off)	((a) * PLL_STRIDE + (off))
>> +#define PLLaRSTCTL(a)	PLLa(a, 0x00)
>> +#define PLLaCR0(a)	PLLa(a, 0x04)
>> +
>> +#define PLLaRSTCTL_RSTREQ	BIT(31)
>> +#define PLLaRSTCTL_RST_DONE	BIT(30)
>> +#define PLLaRSTCTL_RST_ERR	BIT(29)
>> +#define PLLaRSTCTL_PLLRST_B	BIT(7)
>> +#define PLLaRSTCTL_SDRST_B	BIT(6)
>> +#define PLLaRSTCTL_SDEN		BIT(5)
>> +
>> +#define PLLaCR0_POFF		BIT(31)
>> +#define PLLaCR0_RFCLK_SEL	GENMASK(30, 28)
>> +#define PLLaCR0_PLL_LCK		BIT(23)
>> +#define PLLaCR0_FRATE_SEL	GENMASK(19, 16)
>> +#define PLLaCR0_DLYDIV_SEL	GENMASK(1, 0)
>> +
>> +#define PCCR_BASE	0x200
>> +#define PCCR_STRIDE	0x4
>> +#define PCCRn(n)	(PCCR_BASE + n * PCCR_STRIDE)
>> +
>> +#define PCCR0_PEXa_MASK		GENMASK(2, 0)
>> +#define PCCR0_PEXa_SHIFT(a)	(28 - (a) * 4)
> 
> use FIELD_GET/PREP instead of defining shifts? That would use MASK to
> extract/set the field

Unfortunately, because the shift is computed at runtime, we can't use
these macros. I originally did it that way, and got some very noisy
warnings.

>> +
>> +#define PCCR2_SATAa_MASK	GENMASK(2, 0)
>> +#define PCCR2_SATAa_SHIFT(a)	(28 - (a) * 4)
>> +
>> +#define PCCR8_SGMIIa_KX		BIT(3)
>> +#define PCCR8_SGMIIa_MASK	GENMASK(3, 0)
>> +#define PCCR8_SGMIIa_SHIFT(a)	(28 - (a) * 4)
>> +
>> +#define PCCR9_QSGMIIa_MASK	GENMASK(2, 0)
>> +#define PCCR9_QSGMIIa_SHIFT(a)	(28 - (a) * 4)
>> +
>> +#define PCCRB_XFIa_MASK		GENMASK(2, 0)
>> +#define PCCRB_XFIa_SHIFT(a)	(28 - (a) * 4)
>> +
>> +#define LANE_BASE	0x800
>> +#define LANE_STRIDE	0x40
>> +#define LNm(m, off)	(LANE_BASE + (m) * LANE_STRIDE + (off))
>> +#define LNmGCR0(m)	LNm(m, 0x00)
>> +#define LNmGCR1(m)	LNm(m, 0x04)
>> +#define LNmSSCR0(m)	LNm(m, 0x0C)
>> +#define LNmRECR0(m)	LNm(m, 0x10)
>> +#define LNmRECR1(m)	LNm(m, 0x14)
>> +#define LNmTECR0(m)	LNm(m, 0x18)
>> +#define LNmSSCR1(m)	LNm(m, 0x1C)
>> +#define LNmTTLCR0(m)	LNm(m, 0x20)
>> +
>> +#define LNmGCR0_RPLL_LES	BIT(31)
>> +#define LNmGCR0_RRAT_SEL	GENMASK(29, 28)
>> +#define LNmGCR0_TPLL_LES	BIT(27)
>> +#define LNmGCR0_TRAT_SEL	GENMASK(25, 24)
>> +#define LNmGCR0_RRST_B		BIT(22)
>> +#define LNmGCR0_TRST_B		BIT(21)
>> +#define LNmGCR0_RX_PD		BIT(20)
>> +#define LNmGCR0_TX_PD		BIT(19)
>> +#define LNmGCR0_IF20BIT_EN	BIT(18)
>> +#define LNmGCR0_FIRST_LANE	BIT(16)
>> +#define LNmGCR0_TTRM_VM_SEL	GENMASK(13, 12)
>> +#define LNmGCR0_PROTS		GENMASK(11, 7)
>> +
>> +#define LNmGCR0_RAT_SEL_SAME		0b00
>> +#define LNmGCR0_RAT_SEL_HALF		0b01
>> +#define LNmGCR0_RAT_SEL_QUARTER		0b10
>> +#define LNmGCR0_RAT_SEL_DOUBLE		0b11
>> +
>> +#define LNmGCR0_PROTS_PCIE		0b00000
>> +#define LNmGCR0_PROTS_SGMII		0b00001
>> +#define LNmGCR0_PROTS_SATA		0b00010
>> +#define LNmGCR0_PROTS_XFI		0b01010
>> +
>> +#define LNmGCR1_RDAT_INV	BIT(31)
>> +#define LNmGCR1_TDAT_INV	BIT(30)
>> +#define LNmGCR1_OPAD_CTL	BIT(26)
>> +#define LNmGCR1_REIDL_TH	GENMASK(22, 20)
>> +#define LNmGCR1_REIDL_EX_SEL	GENMASK(19, 18)
>> +#define LNmGCR1_REIDL_ET_SEL	GENMASK(17, 16)
>> +#define LNmGCR1_REIDL_EX_MSB	BIT(15)
>> +#define LNmGCR1_REIDL_ET_MSB	BIT(14)
>> +#define LNmGCR1_REQ_CTL_SNP	BIT(13)
>> +#define LNmGCR1_REQ_CDR_SNP	BIT(12)
>> +#define LNmGCR1_TRSTDIR		BIT(7)
>> +#define LNmGCR1_REQ_BIN_SNP	BIT(6)
>> +#define LNmGCR1_ISLEW_RCTL	GENMASK(5, 4)
>> +#define LNmGCR1_OSLEW_RCTL	GENMASK(1, 0)
>> +
>> +#define LNmRECR0_GK2OVD		GENMASK(27, 24)
>> +#define LNmRECR0_GK3OVD		GENMASK(19, 16)
>> +#define LNmRECR0_GK2OVD_EN	BIT(15)
>> +#define LNmRECR0_GK3OVD_EN	BIT(16)
>> +#define LNmRECR0_BASE_WAND	GENMASK(11, 10)
>> +#define LNmRECR0_OSETOVD	GENMASK(5, 0)
>> +
>> +#define LNmRECR0_BASE_WAND_OFF		0b00
>> +#define LNmRECR0_BASE_WAND_DEFAULT	0b01
>> +#define LNmRECR0_BASE_WAND_ALTERNATE	0b10
>> +#define LNmRECR0_BASE_WAND_OSETOVD	0b11
>> +
>> +#define LNmTECR0_TEQ_TYPE	GENMASK(29, 28)
>> +#define LNmTECR0_SGN_PREQ	BIT(26)
>> +#define LNmTECR0_RATIO_PREQ	GENMASK(25, 22)
>> +#define LNmTECR0_SGN_POST1Q	BIT(21)
>> +#define LNmTECR0_RATIO_PST1Q	GENMASK(20, 16)
>> +#define LNmTECR0_ADPT_EQ	GENMASK(13, 8)
>> +#define LNmTECR0_AMP_RED	GENMASK(5, 0)
>> +
>> +#define LNmTECR0_TEQ_TYPE_NONE		0b00
>> +#define LNmTECR0_TEQ_TYPE_PRE		0b01
>> +#define LNmTECR0_TEQ_TYPE_BOTH		0b10
>> +
>> +#define LNmTTLCR0_FLT_SEL	GENMASK(29, 24)
>> +
>> +#define PCS_STRIDE	0x10
>> +#define CR_STRIDE	0x4
>> +#define PCSa(a, base, cr)	(base + (a) * PCS_STRIDE + (cr) * CR_STRIDE)
>> +
>> +#define PCSaCR1_MDEV_PORT	GENMASK(31, 27)
>> +
>> +#define SGMII_BASE	0x1800
>> +#define SGMIIaCR1(a)	PCSa(a, SGMII_BASE, 1)
>> +
>> +#define SGMIIaCR1_SGPCS_EN	BIT(11)
>> +
>> +#define QSGMII_OFFSET	0x1880
>> +#define QSGMIIaCR1(a)	PCSa(a, QSGMII_BASE, 1)
>> +
>> +#define XFI_OFFSET	0x1980
>> +#define XFIaCR1(a)	PCSa(a, XFI_BASE, 1)
>> +
>> +/* The maximum number of lanes in a single serdes */
>> +#define MAX_LANES	8
>> +
>> +enum lynx_protocol {
>> +	LYNX_PROTO_NONE = 0,
>> +	LYNX_PROTO_SGMII,
>> +	LYNX_PROTO_SGMII25,
>> +	LYNX_PROTO_1000BASEKX,
>> +	LYNX_PROTO_QSGMII,
>> +	LYNX_PROTO_XFI,
>> +	LYNX_PROTO_10GKR,
>> +	LYNX_PROTO_PCIE, /* Not implemented */
>> +	LYNX_PROTO_SATA, /* Not implemented */
> 
> lets skip that and add when it is implemented

They are necessary to interpret the PCCRs correctly. To ease the
transition, this driver tries to determine if a lane is in-use, and
reserves it (and its PLLs) if that is the case.

>> +	LYNX_PROTO_LAST,
>> +};
>> +
>> +static const char lynx_proto_str[][16] = {
>> +	[LYNX_PROTO_NONE] = "unknown",
>> +	[LYNX_PROTO_SGMII] = "SGMII",
>> +	[LYNX_PROTO_SGMII25] = "2.5G SGMII",
>> +	[LYNX_PROTO_1000BASEKX] = "1000Base-KX",
>> +	[LYNX_PROTO_QSGMII] = "QSGMII",
>> +	[LYNX_PROTO_XFI] = "XFI",
>> +	[LYNX_PROTO_10GKR] = "10GBase-KR",
>> +	[LYNX_PROTO_PCIE] = "PCIe",
>> +	[LYNX_PROTO_SATA] = "SATA",
>> +};
>> +
>> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
>> +#define UNSUPPORTED_PROTOS (PROTO_MASK(SATA) | PROTO_MASK(PCIE))
>> +
>> +/**
>> + * struct lynx_proto_params - Parameters for configuring a protocol
>> + * @frate_khz: The PLL rate, in kHz
>> + * @rat_sel: The divider to get the line rate
>> + * @if20bit: Whether the proto is 20 bits or 10 bits
>> + * @prots: Lane protocol select
>> + * @reidl_th: Receiver electrical idle detection threshold
>> + * @reidl_ex: Exit electrical idle filter
>> + * @reidl_et: Enter idle filter
>> + * @slew: Slew control
>> + * @baseline_wander: Enable baseline wander correction
>> + * @gain: Adaptive equalization gain override
>> + * @offset_override: Adaptive equalization offset override
>> + * @teq: Transmit equalization type (none, precursor, or precursor and
>> + *       postcursor). The next few values are only used for appropriate
>> + *       equalization types.
>> + * @preq_ratio: Ratio of full swing transition bit to pre-cursor
>> + * @postq_ratio: Ratio of full swing transition bit to first post-cursor.
>> + * @adpt_eq: Transmitter Adjustments for 8G/10G
>> + * @amp_red: Overall TX Amplitude Reduction
>> + * @flt_sel: TTL configuration selector
>> + */
>> +struct lynx_proto_params {
>> +	u32 frate_khz;
>> +	u8 rat_sel;
>> +	u8 prots;
>> +	u8 reidl_th;
>> +	u8 reidl_ex;
>> +	u8 reidl_et;
>> +	u8 slew;
>> +	u8 gain;
>> +	u8 baseline_wander;
>> +	u8 offset_override;
>> +	u8 teq;
>> +	u8 preq_ratio;
>> +	u8 postq_ratio;
>> +	u8 adpt_eq;
>> +	u8 amp_red;
>> +	u8 flt_sel;
>> +	bool if20bit;
>> +};
>> +
>> +static const struct lynx_proto_params lynx_proto_params[] = {
>> +	[LYNX_PROTO_SGMII] = {
>> +		.frate_khz = 5000000,
>> +		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
>> +		.if20bit = false,
>> +		.prots = LNmGCR0_PROTS_SGMII,
>> +		.reidl_th = 0b001,
>> +		.reidl_ex = 0b011,
>> +		.reidl_et = 0b100,
>> +		.slew = 0b01,
>> +		.gain = 0b1111,
>> +		.offset_override = 0b0011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_NONE,
>> +		.adpt_eq = 0b110000,
>> +		.amp_red = 0b000110,
>> +		.flt_sel = 0b111001,
>> +	},
>> +	[LYNX_PROTO_1000BASEKX] = {
>> +		.frate_khz = 5000000,
>> +		.rat_sel = LNmGCR0_RAT_SEL_QUARTER,
>> +		.if20bit = false,
>> +		.prots = LNmGCR0_PROTS_SGMII,
>> +		.slew = 0b01,
>> +		.gain = 0b1111,
>> +		.offset_override = 0b0011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_NONE,
>> +		.adpt_eq = 0b110000,
>> +		.flt_sel = 0b111001,
>> +	},
>> +	[LYNX_PROTO_SGMII25] = {
>> +		.frate_khz = 3125000,
>> +		.rat_sel = LNmGCR0_RAT_SEL_SAME,
>> +		.if20bit = false,
>> +		.prots = LNmGCR0_PROTS_SGMII,
>> +		.slew = 0b10,
>> +		.offset_override = 0b0011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
>> +		.postq_ratio = 0b00110,
>> +		.adpt_eq = 0b110000,
>> +	},
>> +	[LYNX_PROTO_QSGMII] = {
>> +		.frate_khz = 5000000,
>> +		.rat_sel = LNmGCR0_RAT_SEL_SAME,
>> +		.if20bit = true,
>> +		.prots = LNmGCR0_PROTS_SGMII,
>> +		.slew = 0b01,
>> +		.offset_override = 0b0011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
>> +		.postq_ratio = 0b00110,
>> +		.adpt_eq = 0b110000,
>> +		.amp_red = 0b000010,
>> +	},
>> +	[LYNX_PROTO_XFI] = {
>> +		.frate_khz = 5156250,
>> +		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
>> +		.if20bit = true,
>> +		.prots = LNmGCR0_PROTS_XFI,
>> +		.slew = 0b01,
>> +		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
>> +		.offset_override = 0b1011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_PRE,
>> +		.postq_ratio = 0b00011,
>> +		.adpt_eq = 0b110000,
>> +		.amp_red = 0b000111,
>> +	},
>> +	[LYNX_PROTO_10GKR] = {
>> +		.frate_khz = 5156250,
>> +		.rat_sel = LNmGCR0_RAT_SEL_DOUBLE,
>> +		.prots = LNmGCR0_PROTS_XFI,
>> +		.slew = 0b01,
>> +		.baseline_wander = LNmRECR0_BASE_WAND_DEFAULT,
>> +		.offset_override = 0b1011111,
>> +		.teq = LNmTECR0_TEQ_TYPE_BOTH,
>> +		.preq_ratio = 0b0011,
>> +		.postq_ratio = 0b01100,
>> +		.adpt_eq = 0b110000,
>> +	},
>> +};
>> +
>> +/**
>> + * struct lynx_mode - A single configuration of a protocol controller
>> + * @protos: A bitmask of the &enum lynx_protocol this mode supports
>> + * @lanes: A bitmask of the lanes which will be used when this config is
>> + *         selected
>> + * @pccr: The number of the PCCR which contains this mode
>> + * @idx: The index of the protocol controller. For example, SGMIIB would have
>> + *       index 1.
>> + * @cfg: The value to program into the controller to select this mode
>> + *
>> + * The serdes has multiple protocol controllers which can be each be selected
>> + * independently. Depending on their configuration, they may use multiple lanes
>> + * at once (e.g. AUI or PCIe x4). Additionally, multiple protocols may be
>> + * supported by a single mode (XFI and 10GKR differ only in their protocol
>> + * parameters).
>> + */
>> +struct lynx_mode {
>> +	u16 protos;
>> +	u8 lanes;
>> +	u8 pccr;
>> +	u8 idx;
>> +	u8 cfg;
>> +};
>> +
>> +static_assert(LYNX_PROTO_LAST - 1 <=
>> +	      sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
>> +static_assert(MAX_LANES <=
>> +	      sizeof_field(struct lynx_mode, lanes) * BITS_PER_BYTE);
>> +
>> +#define CONF(_lanes, _protos, _pccr, _idx, _cfg) { \
>> +	.lanes = _lanes, \
>> +	.protos = _protos, \
>> +	.pccr = _pccr, \
>> +	.idx = _idx, \
>> +	.cfg = _cfg, \
>> +}
>> +
>> +#define CONF_SINGLE(lane, proto, pccr, idx, cfg) \
>> +	CONF(BIT(lane), PROTO_MASK(proto), pccr, idx, cfg)
>> +
>> +#define CONF_1000BASEKX(lane, pccr, idx, cfg) \
>> +	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX), \
>> +	     pccr, idx, cfg)
>> +
>> +#define CONF_SGMII25(lane, pccr, idx, cfg) \
>> +	CONF(BIT(lane), PROTO_MASK(SGMII) | PROTO_MASK(SGMII25), \
>> +	     pccr, idx, cfg)
>> +
>> +#define CONF_SGMII25KX(lane, pccr, idx, cfg) \
>> +	CONF(BIT(lane), \
>> +	     PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX) | PROTO_MASK(SGMII25), \
>> +	     pccr, idx, cfg)
>> +
>> +#define CONF_XFI(lane, pccr, idx, cfg) \
>> +	CONF(BIT(lane), PROTO_MASK(XFI) | PROTO_MASK(10GKR), pccr, idx, cfg)
>> +
>> +/**
>> + * struct lynx_conf - Configuration for a particular serdes
>> + * @modes: Valid protocol controller configurations
>> + * @mode_count: Number of modes in @modes
>> + * @lanes: Number of lanes
>> + * @endian: Endianness of the registers
>> + */
>> +struct lynx_conf {
>> +	const struct lynx_mode *modes;
>> +	size_t mode_count;
>> +	unsigned int lanes;
>> +	enum regmap_endian endian;
>> +};
>> +
>> +struct lynx_priv;
>> +
>> +/**
>> + * struct lynx_clk - Driver data for the PLLs
>> + * @hw: The clock hardware
>> + * @serdes: The parent serdes
>> + * @idx: Which PLL this clock is for
>> + */
>> +struct lynx_clk {
>> +	struct clk_hw hw;
>> +	struct lynx_priv *serdes;
>> +	unsigned int idx;
>> +};
>> +
>> +static struct lynx_clk *lynx_clk_hw_to_priv(struct clk_hw *hw)
>> +{
>> +	return container_of(hw, struct lynx_clk, hw);
>> +}
>> +
>> +/**
>> + * struct lynx_priv - Driver data for the serdes
>> + * @lock: A lock protecting "common" registers in @regmap, as well as the
>> + *        members of this struct. Lane-specific registers are protected by the
>> + *        phy's lock. PLL registers are protected by the clock's lock.
>> + * @pll: The PLL clocks
>> + * @ref: The reference clocks for the PLLs
>> + * @dev: The serdes device
>> + * @regmap: The backing regmap
>> + * @conf: The configuration for this serdes
>> + * @used_lanes: Bitmap of the lanes currently used by phys
>> + * @groups: List of the created groups
>> + */
>> +struct lynx_priv {
>> +	struct mutex lock;
>> +	struct lynx_clk pll[2];
>> +	struct clk *ref[2];
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	const struct lynx_conf *conf;
>> +	unsigned int used_lanes;
>> +	struct list_head groups;
>> +};
>> +
>> +/**
>> + * struct lynx_group - Driver data for a group of lanes
>> + * @groups: List of other groups; protected by @serdes->lock.
>> + * @phy: The associated phy
>> + * @serdes: The parent serdes
>> + * @pll: The currently-used pll
>> + * @first_lane: The first lane in the group
>> + * @last_lane: The last lane in the group
>> + * @proto: The currently-configured protocol
>> + * @users: Number of current users; protected by @serdes->lock.
>> + */
>> +struct lynx_group {
>> +	struct list_head groups;
>> +	struct phy *phy;
>> +	struct lynx_priv *serdes;
>> +	struct clk *pll;
>> +	unsigned int first_lane;
>> +	unsigned int last_lane;
>> +	enum lynx_protocol proto;
>> +	unsigned int users;
>> +};
>> +
>> +static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
>> +{
>> +	unsigned int ret = 0;
>> +
>> +	WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
>> +	return ret;
>> +}
>> +
>> +static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
>> +{
>> +	WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
>> +}
>> +
>> +/* XXX: The output rate is in kHz to avoid overflow on 32-bit arches */
>> +
>> +static void lynx_pll_disable(struct clk_hw *hw)
>> +{
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	struct lynx_priv *serdes = clk->serdes;
>> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
> 
> no need for __func__ in dev_dbg pls

OK

>> +
>> +	rstctl &= ~PLLaRSTCTL_SDRST_B;
>> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
>> +	ndelay(50);
>> +	rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
>> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
>> +	ndelay(100);
>> +}
>> +
>> +static int lynx_pll_enable(struct clk_hw *hw)
>> +{
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	struct lynx_priv *serdes = clk->serdes;
>> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
>> +
>> +	rstctl |= PLLaRSTCTL_RSTREQ;
>> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
>> +
>> +	rstctl &= ~PLLaRSTCTL_RSTREQ;
>> +	rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
>> +	lynx_write(serdes, rstctl, PLLaRSTCTL(clk->idx));
>> +
>> +	/* TODO: wait for the PLL to lock */
> 
> when will this be added?

I'm not sure. I haven't had any issues with this, and waiting on the lock bit is
only mentioned in some datasheets for this SerDes. On the LS1046A for example,
there is no mention of waiting for lock.

>> +
>> +	return 0;
>> +}
>> +
>> +static int lynx_pll_is_enabled(struct clk_hw *hw)
>> +{
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	struct lynx_priv *serdes = clk->serdes;
>> +	u32 rstctl = lynx_read(serdes, PLLaRSTCTL(clk->idx));
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d)\n", __func__, clk->idx);
>> +
>> +	return rstctl & PLLaRSTCTL_RST_DONE && !(rstctl & PLLaRSTCTL_RST_ERR);
>> +}
>> +
>> +static const u32 rfclk_sel_map[8] = {
>> +	[0b000] = 100000000,
>> +	[0b001] = 125000000,
>> +	[0b010] = 156250000,
>> +	[0b011] = 150000000,
>> +};
>> +
>> +/**
>> + * lynx_rfclk_to_sel() - Convert a reference clock rate to a selector
>> + * @rate: The reference clock rate
>> + *
>> + * To allow for some variation in the reference clock rate, up to 100ppm of
>> + * error is allowed.
>> + *
>> + * Return: An appropriate selector for @rate, or -%EINVAL.
>> + */
>> +static int lynx_rfclk_to_sel(u32 rate)
>> +{
>> +	int ret;
>> +
>> +	for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
>> +		u32 rfclk_rate = rfclk_sel_map[ret];
>> +		/* Allow an error of 100ppm */
>> +		u32 error = rfclk_rate / 10000;
>> +
>> +		if (rate > rfclk_rate - error && rate < rfclk_rate + error)
>> +			return ret;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const u32 frate_sel_map[16] = {
>> +	[0b0000] = 5000000,
>> +	[0b0101] = 3750000,
>> +	[0b0110] = 5156250,
>> +	[0b0111] = 4000000,
>> +	[0b1001] = 3125000,
>> +	[0b1010] = 3000000,
>> +};
>> +
>> +/**
>> + * lynx_frate_to_sel() - Convert a VCO clock rate to a selector
>> + * @rate_khz: The VCO frequency, in kHz
>> + *
>> + * Return: An appropriate selector for @rate_khz, or -%EINVAL.
>> + */
>> +static int lynx_frate_to_sel(u32 rate_khz)
>> +{
>> +	int ret;
>> +
>> +	for (ret = 0; ret < ARRAY_SIZE(frate_sel_map); ret++)
>> +		if (frate_sel_map[ret] == rate_khz)
>> +			return ret;
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static u32 lynx_pll_ratio(u32 frate_sel, u32 rfclk_sel)
>> +{
>> +	u64 frate;
>> +	u32 rfclk, error, ratio;
>> +
>> +	frate = frate_sel_map[frate_sel] * (u64)HZ_PER_KHZ;
>> +	rfclk = rfclk_sel_map[rfclk_sel];
>> +
>> +	if (!frate || !rfclk)
>> +		return 0;
>> +
>> +	ratio = div_u64_rem(frate, rfclk, &error);
>> +	if (!error)
>> +		return ratio;
>> +	return 0;
>> +}
>> +
>> +static unsigned long lynx_pll_recalc_rate(struct clk_hw *hw,
>> +					unsigned long parent_rate)
>> +{
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	struct lynx_priv *serdes = clk->serdes;
>> +	u32 cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
>> +	u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
>> +	u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
>> +	unsigned long ret;
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu)\n", __func__,
>> +		clk->idx, parent_rate);
>> +
>> +	ret = mult_frac(parent_rate, lynx_pll_ratio(frate_sel, rfclk_sel),
>> +			 HZ_PER_KHZ);
>> +	return ret;
>> +}
>> +
>> +static long lynx_pll_round_rate(struct clk_hw *hw, unsigned long rate_khz,
>> +			      unsigned long *parent_rate)
>> +{
>> +	int frate_sel, rfclk_sel;
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	u32 ratio;
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
>> +		clk->idx, rate_khz, *parent_rate);
>> +
>> +	frate_sel = lynx_frate_to_sel(rate_khz);
>> +	if (frate_sel < 0)
>> +		return frate_sel;
>> +
>> +	rfclk_sel = lynx_rfclk_to_sel(*parent_rate);
>> +	if (rfclk_sel >= 0) {
>> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> +		if (ratio)
>> +			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
>> +	}
>> +
>> +	for (rfclk_sel = 0;
>> +	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
>> +	     rfclk_sel++) {
>> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> +		if (ratio) {
>> +			*parent_rate = rfclk_sel_map[rfclk_sel];
>> +			return mult_frac(*parent_rate, ratio, HZ_PER_KHZ);
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int lynx_pll_set_rate(struct clk_hw *hw, unsigned long rate_khz,
>> +			   unsigned long parent_rate)
> 
> This really sounds like a clk driver, why is this in phy driver. Ideally
> this should a clock driver. Please move it to one..

(see below)

>> +{
>> +	int frate_sel, rfclk_sel, ret;
>> +	struct lynx_clk *clk = lynx_clk_hw_to_priv(hw);
>> +	struct lynx_priv *serdes = clk->serdes;
>> +	u32 ratio, cr0 = lynx_read(serdes, PLLaCR0(clk->idx));
>> +
>> +	dev_dbg(clk->serdes->dev, "%s(pll%d, %lu, %lu)\n", __func__,
>> +		clk->idx, rate_khz, parent_rate);
>> +
>> +	frate_sel = lynx_frate_to_sel(rate_khz);
>> +	if (frate_sel < 0)
>> +		return frate_sel;
>> +
>> +	/* First try the existing rate */
>> +	rfclk_sel = lynx_rfclk_to_sel(parent_rate);
>> +	if (rfclk_sel >= 0) {
>> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> +		if (ratio)
>> +			goto got_rfclk;
>> +	}
>> +
>> +	for (rfclk_sel = 0;
>> +	     rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
>> +	     rfclk_sel++) {
>> +		ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> +		if (ratio) {
>> +			ret = clk_set_rate(serdes->ref[clk->idx],
>> +					   rfclk_sel_map[rfclk_sel]);
>> +			if (!ret)
>> +				goto got_rfclk;
>> +		}
>> +	}
>> +
>> +	return ret;
>> +
>> +got_rfclk:
>> +	cr0 &= ~(PLLaCR0_RFCLK_SEL | PLLaCR0_FRATE_SEL);
>> +	cr0 |= FIELD_PREP(PLLaCR0_RFCLK_SEL, rfclk_sel);
>> +	cr0 |= FIELD_PREP(PLLaCR0_FRATE_SEL, frate_sel);
>> +	lynx_write(serdes, cr0, PLLaCR0(clk->idx));
>> +	return 0;
>> +}
>> +
>> +static const struct clk_ops lynx_pll_clk_ops = {
>> +	.enable = lynx_pll_enable,
>> +	.disable = lynx_pll_disable,
>> +	.is_enabled = lynx_pll_is_enabled,
>> +	.recalc_rate = lynx_pll_recalc_rate,
>> +	.round_rate = lynx_pll_round_rate,
>> +	.set_rate = lynx_pll_set_rate,
>> +};
> 
> right, this should be a clk driver

Well, it is a clock driver, effectively internal to the SerDes. There are a few
examples of this already (e.g. the qualcomm and cadence phys). It could of course
be split off, but I would prefer that they remained together.

>> +
>> +static struct clk_hw *lynx_clk_get(struct of_phandle_args *clkspec, void *data)
>> +{
>> +	struct lynx_priv *serdes = data;
>> +
>> +	if (clkspec->args_count != 1)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	if (clkspec->args[0] > 1)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	return &serdes->pll[clkspec->args[0]].hw;
>> +}
>> +
>> +/**
>> + * lynx_lane_bitmap() - Get a bitmap for a group of lanes
>> + * @group: The group of lanes
>> + *
>> + * Return: A mask containing all bits between @group->first and @group->last
>> + */
>> +static unsigned int lynx_lane_bitmap(struct lynx_group *group)
>> +{
>> +	if (group->first_lane > group->last_lane)
>> +		return GENMASK(group->first_lane, group->last_lane);
>> +	else
>> +		return GENMASK(group->last_lane, group->first_lane);
>> +}
>> +
>> +static int lynx_init(struct phy *phy)
>> +{
>> +	int ret = 0;
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	struct lynx_priv *serdes = group->serdes;
>> +	unsigned int lane_mask = lynx_lane_bitmap(group);
>> +
>> +	mutex_lock(&serdes->lock);
>> +	if (serdes->used_lanes & lane_mask)
>> +		ret = -EBUSY;
>> +	else
>> +		serdes->used_lanes |= lane_mask;
>> +	mutex_unlock(&serdes->lock);
>> +	return ret;
>> +}
>> +
>> +static int lynx_exit(struct phy *phy)
>> +{
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	struct lynx_priv *serdes = group->serdes;
>> +
>> +	clk_disable_unprepare(group->pll);
>> +	clk_rate_exclusive_put(group->pll);
>> +	group->pll = NULL;
>> +
>> +	mutex_lock(&serdes->lock);
>> +	serdes->used_lanes &= ~lynx_lane_bitmap(group);
>> +	mutex_unlock(&serdes->lock);
>> +	return 0;
>> +}
>> +
>> +/*
>> + * This is tricky. If first_lane=1 and last_lane=0, the condition will see 2,
>> + * 1, 0. But the loop body will see 1, 0. We do this to avoid underflow. We
>> + * can't pull the same trick when incrementing, because then we might have to
>> + * start at -1 if (e.g.) first_lane = 0.
>> + */
>> +#define for_range(val, start, end) \
>> +	for (val = start < end ? start : start + 1; \
>> +	     start < end ? val <= end : val-- > end; \
>> +	     start < end ? val++ : 0)
>> +#define for_each_lane(lane, group) \
>> +	for_range(lane, group->first_lane, group->last_lane)
>> +#define for_each_lane_reverse(lane, group) \
>> +	for_range(lane, group->last_lane, group->first_lane)
>> +
>> +static int lynx_power_on(struct phy *phy)
>> +{
>> +	int i;
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	u32 gcr0;
>> +
>> +	for_each_lane(i, group) {
>> +		gcr0 = lynx_read(group->serdes, LNmGCR0(i));
>> +		gcr0 &= ~(LNmGCR0_RX_PD | LNmGCR0_TX_PD);
>> +		lynx_write(group->serdes, gcr0, LNmGCR0(i));
>> +
>> +		usleep_range(15, 30);
>> +		gcr0 |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
>> +		lynx_write(group->serdes, gcr0, LNmGCR0(i));
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void lynx_power_off_lane(struct lynx_priv *serdes, unsigned int lane)
>> +{
>> +	u32 gcr0 = lynx_read(serdes, LNmGCR0(lane));
>> +
>> +	gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
>> +	gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
>> +	lynx_write(serdes, gcr0, LNmGCR0(lane));
>> +}
>> +
>> +static int lynx_power_off(struct phy *phy)
>> +{
>> +	unsigned int i;
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +
>> +	for_each_lane_reverse(i, group)
>> +		lynx_power_off_lane(group->serdes, i);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * lynx_lookup_proto() - Convert a phy-subsystem mode to a protocol
>> + * @mode: The mode to convert
>> + * @submode: The submode of @mode
>> + *
>> + * Return: A corresponding serdes-specific mode
>> + */
>> +static enum lynx_protocol lynx_lookup_proto(enum phy_mode mode, int submode)
>> +{
>> +	switch (mode) {
>> +	case PHY_MODE_ETHERNET:
>> +		switch (submode) {
>> +		case PHY_INTERFACE_MODE_SGMII:
>> +		case PHY_INTERFACE_MODE_1000BASEX:
>> +			return LYNX_PROTO_SGMII;
>> +		case PHY_INTERFACE_MODE_2500BASEX:
>> +			return LYNX_PROTO_SGMII25;
>> +		case PHY_INTERFACE_MODE_QSGMII:
>> +			return LYNX_PROTO_QSGMII;
>> +		case PHY_INTERFACE_MODE_XGMII:
>> +		case PHY_INTERFACE_MODE_10GBASER:
>> +			return LYNX_PROTO_XFI;
>> +		case PHY_INTERFACE_MODE_10GKR:
>> +			return LYNX_PROTO_10GKR;
>> +		default:
>> +			return LYNX_PROTO_NONE;
>> +		}
>> +	/* Not implemented (yet) */
>> +	case PHY_MODE_PCIE:
>> +	case PHY_MODE_SATA:
>> +	default:
>> +		return LYNX_PROTO_NONE;
>> +	}
>> +}
>> +
>> +/**
>> + * lynx_lookup_mode() - Get the mode for a group/protocol combination
>> + * @group: The group of lanes to use
>> + * @proto: The protocol to use
>> + *
>> + * Return: An appropriate mode to use, or %NULL if none match.
>> + */
>> +static const struct lynx_mode *lynx_lookup_mode(struct lynx_group *group,
>> +					    enum lynx_protocol proto)
>> +{
>> +	int i;
>> +	const struct lynx_conf *conf = group->serdes->conf;
>> +
>> +	for (i = 0; i < conf->mode_count; i++) {
>> +		const struct lynx_mode *mode = &conf->modes[i];
>> +
>> +		if (BIT(proto) & mode->protos &&
>> +		    lynx_lane_bitmap(group) == mode->lanes)
>> +			return mode;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +static int lynx_validate(struct phy *phy, enum phy_mode phy_mode, int submode,
>> +		       union phy_configure_opts *opts)
>> +{
>> +	enum lynx_protocol proto;
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	const struct lynx_mode *mode;
>> +
>> +	proto = lynx_lookup_proto(phy_mode, submode);
>> +	if (proto == LYNX_PROTO_NONE)
>> +		return -EINVAL;
>> +
>> +	/* Nothing to do */
>> +	if (proto == group->proto)
>> +		return 0;
>> +
>> +	mode = lynx_lookup_mode(group, proto);
>> +	if (!mode)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * lynx_proto_mode_mask() - Get the mask for a PCCR config
>> + * @mode: The mode to use
>> + *
>> + * Return: The mask, shifted down to the lsb.
>> + */
>> +static u32 lynx_proto_mode_mask(const struct lynx_mode *mode)
>> +{
>> +	switch (mode->pccr) {
>> +	case 0x0:
>> +		if (mode->protos & PROTO_MASK(PCIE))
>> +			return PCCR0_PEXa_MASK;
>> +		break;
>> +	case 0x2:
>> +		if (mode->protos & PROTO_MASK(SATA))
>> +			return PCCR2_SATAa_MASK;
>> +		break;
>> +	case 0x8:
>> +		if (mode->protos & PROTO_MASK(SGMII))
>> +			return PCCR8_SGMIIa_MASK;
>> +		break;
>> +	case 0x9:
>> +		if (mode->protos & PROTO_MASK(QSGMII))
>> +			return PCCR9_QSGMIIa_MASK;
>> +		break;
>> +	case 0xB:
>> +		if (mode->protos & PROTO_MASK(XFI))
>> +			return PCCRB_XFIa_MASK;
>> +		break;
>> +	}
>> +	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
>> +	       lynx_proto_str[mode->protos], 'A' + mode->idx);
>> +	return 0;
>> +}
>> +
>> +/**
>> + * lynx_proto_mode_shift() - Get the shift for a PCCR config
>> + * @mode: The mode to use
>> + *
>> + * Return: The amount of bits to shift the mask.
>> + */
>> +static u32 lynx_proto_mode_shift(const struct lynx_mode *mode)
>> +{
>> +	switch (mode->pccr) {
>> +	case 0x0:
>> +		if (mode->protos & PROTO_MASK(PCIE))
>> +			return PCCR0_PEXa_SHIFT(mode->idx);
>> +		break;
>> +	case 0x2:
>> +		if (mode->protos & PROTO_MASK(SATA))
>> +			return PCCR2_SATAa_SHIFT(mode->idx);
>> +		break;
>> +	case 0x8:
>> +		if (mode->protos & PROTO_MASK(SGMII))
>> +			return PCCR8_SGMIIa_SHIFT(mode->idx);
>> +		break;
>> +	case 0x9:
>> +		if (mode->protos & PROTO_MASK(QSGMII))
>> +			return PCCR9_QSGMIIa_SHIFT(mode->idx);
>> +		break;
>> +	case 0xB:
>> +		if (mode->protos & PROTO_MASK(XFI))
>> +			return PCCRB_XFIa_SHIFT(mode->idx);
>> +		break;
>> +	}
>> +	pr_err("unknown mode PCCR%X %s%c\n", mode->pccr,
>> +	       lynx_proto_str[mode->protos], 'A' + mode->idx);
>> +	return 0;
>> +}
>> +
>> +/**
>> + * lynx_proto_mode_get() - Get the current config for a PCCR mode
>> + * @mode: The mode to use
>> + * @pccr: The current value of the PCCR
>> + *
>> + * Return: The current value of the PCCR config for this mode
>> + */
>> +static u32 lynx_proto_mode_get(const struct lynx_mode *mode, u32 pccr)
>> +{
>> +	return (pccr >> lynx_proto_mode_shift(mode)) &
>> +	       lynx_proto_mode_mask(mode);
>> +}
>> +
>> +/**
>> + * lynx_proto_mode_prep() - Configure a PCCR for a protocol
>> + * @mode: The mode to use
>> + * @pccr: The current value of the PCCR
>> + * @proto: The protocol to configure
>> + *
>> + * This configures a PCCR for a mode and protocol. To disable a mode, pass
>> + * %LYNX_PROTO_NONE as @proto. If @proto is 1000Base-KX, then the KX bit
>> + * will be set.
>> + *
>> + * Return: The new value for the PCCR
>> + */
>> +static u32 lynx_proto_mode_prep(const struct lynx_mode *mode, u32 pccr,
>> +				enum lynx_protocol proto)
>> +{
>> +	u32 shift = lynx_proto_mode_shift(mode);
>> +
>> +	pccr &= ~(lynx_proto_mode_mask(mode) << shift);
>> +	if (proto != LYNX_PROTO_NONE)
>> +		pccr |= mode->cfg << shift;
>> +
>> +	if (proto == LYNX_PROTO_1000BASEKX) {
>> +		if (mode->pccr == 8)
>> +			pccr |= PCCR8_SGMIIa_KX << shift;
>> +		else
>> +			pr_err("PCCR%X doesn't have a KX bit\n", mode->pccr);
>> +	}
>> +
>> +	return pccr;
>> +}
>> +
>> +#define abs_diff(a, b) ({ \
>> +	typeof(a) _a = (a); \
>> +	typeof(b) _b = (b); \
>> +	_a > _b ? _a - _b : _b - _a; \
>> +})
>> +
>> +static int lynx_set_mode(struct phy *phy, enum phy_mode phy_mode, int submode)
>> +{
>> +	enum lynx_protocol proto;
>> +	const struct lynx_proto_params *params;
>> +	const struct lynx_mode *old_mode = NULL, *new_mode;
>> +	int i, pll, ret;
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	struct lynx_priv *serdes = group->serdes;
>> +	u32 tmp;
>> +	u32 gcr0 = 0, gcr1 = 0, recr0 = 0, tecr0 = 0;
>> +	u32 gcr0_mask = 0, gcr1_mask = 0, recr0_mask = 0, tecr0_mask = 0;
>> +
>> +	proto = lynx_lookup_proto(phy_mode, submode);
>> +	if (proto == LYNX_PROTO_NONE) {
>> +		dev_dbg(&phy->dev, "unknown mode/submode %d/%d\n",
>> +			phy_mode, submode);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Nothing to do */
>> +	if (proto == group->proto)
>> +		return 0;
>> +
>> +	new_mode = lynx_lookup_mode(group, proto);
>> +	if (!new_mode) {
>> +		dev_dbg(&phy->dev, "could not find mode for %s on lanes %u to %u\n",
>> +			lynx_proto_str[proto], group->first_lane,
>> +			group->last_lane);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (group->proto != LYNX_PROTO_NONE) {
>> +		old_mode = lynx_lookup_mode(group, group->proto);
>> +		if (!old_mode) {
>> +			dev_err(&phy->dev, "could not find mode for %s\n",
>> +				lynx_proto_str[group->proto]);
>> +			return -EBUSY;
>> +		}
>> +	}
>> +
>> +	clk_disable_unprepare(group->pll);
>> +	clk_rate_exclusive_put(group->pll);
>> +	group->pll = NULL;
>> +
>> +	/* First, try to use a PLL which already has the correct rate */
>> +	params = &lynx_proto_params[proto];
>> +	for (pll = 0; pll < ARRAY_SIZE(serdes->pll); pll++) {
>> +		struct clk *clk = serdes->pll[pll].hw.clk;
>> +		unsigned long rate = clk_get_rate(clk);
>> +		unsigned long error = abs_diff(rate, params->frate_khz);
>> +
>> +		dev_dbg(&phy->dev, "pll%d has rate %lu\n", pll, rate);
>> +		/* Accept up to 100ppm deviation */
>> +		if ((!error || params->frate_khz / error > 10000) &&
>> +		    !clk_set_rate_exclusive(clk, rate))
>> +			goto got_pll;
>> +		/* Someone else got a different rate first */
>> +	}
>> +
>> +	/* If neither PLL has the right rate, try setting it */
>> +	for (pll = 0; pll < 2; pll++) {
>> +		ret = clk_set_rate_exclusive(serdes->pll[pll].hw.clk,
>> +					     params->frate_khz);
>> +		if (!ret)
>> +			goto got_pll;
>> +	}
>> +
>> +	dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
>> +		params->frate_khz);
>> +	return ret;
>> +
>> +got_pll:
>> +	group->pll = serdes->pll[pll].hw.clk;
>> +	clk_prepare_enable(group->pll);
>> +
>> +	gcr0_mask |= LNmGCR0_RRAT_SEL | LNmGCR0_TRAT_SEL;
>> +	gcr0_mask |= LNmGCR0_RPLL_LES | LNmGCR0_TPLL_LES;
>> +	gcr0_mask |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
>> +	gcr0_mask |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
>> +	gcr0_mask |= LNmGCR0_IF20BIT_EN | LNmGCR0_PROTS;
>> +	gcr0 |= FIELD_PREP(LNmGCR0_RPLL_LES, !pll);
>> +	gcr0 |= FIELD_PREP(LNmGCR0_TPLL_LES, !pll);
>> +	gcr0 |= FIELD_PREP(LNmGCR0_RRAT_SEL, params->rat_sel);
>> +	gcr0 |= FIELD_PREP(LNmGCR0_TRAT_SEL, params->rat_sel);
>> +	gcr0 |= FIELD_PREP(LNmGCR0_IF20BIT_EN, params->if20bit);
>> +	gcr0 |= FIELD_PREP(LNmGCR0_PROTS, params->prots);
>> +
>> +	gcr1_mask |= LNmGCR1_RDAT_INV | LNmGCR1_TDAT_INV;
>> +	gcr1_mask |= LNmGCR1_OPAD_CTL | LNmGCR1_REIDL_TH;
>> +	gcr1_mask |= LNmGCR1_REIDL_EX_SEL | LNmGCR1_REIDL_ET_SEL;
>> +	gcr1_mask |= LNmGCR1_REIDL_EX_MSB | LNmGCR1_REIDL_ET_MSB;
>> +	gcr1_mask |= LNmGCR1_REQ_CTL_SNP | LNmGCR1_REQ_CDR_SNP;
>> +	gcr1_mask |= LNmGCR1_TRSTDIR | LNmGCR1_REQ_BIN_SNP;
>> +	gcr1_mask |= LNmGCR1_ISLEW_RCTL | LNmGCR1_OSLEW_RCTL;
>> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_TH, params->reidl_th);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_SEL, params->reidl_ex & 3);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_SEL, params->reidl_et & 3);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_EX_MSB, params->reidl_ex >> 2);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_REIDL_ET_MSB, params->reidl_et >> 2);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_TRSTDIR,
>> +			   group->first_lane > group->last_lane);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_ISLEW_RCTL, params->slew);
>> +	gcr1 |= FIELD_PREP(LNmGCR1_OSLEW_RCTL, params->slew);
>> +
>> +	recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
>> +	recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
>> +	recr0_mask |= LNmRECR0_BASE_WAND | LNmRECR0_OSETOVD;
>> +	if (params->gain) {
>> +		recr0 |= FIELD_PREP(LNmRECR0_GK2OVD, params->gain);
>> +		recr0 |= FIELD_PREP(LNmRECR0_GK3OVD, params->gain);
>> +		recr0 |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
>> +	}
>> +	recr0 |= FIELD_PREP(LNmRECR0_BASE_WAND, params->baseline_wander);
>> +	recr0 |= FIELD_PREP(LNmRECR0_OSETOVD, params->offset_override);
>> +
>> +	tecr0_mask |= LNmTECR0_TEQ_TYPE;
>> +	tecr0_mask |= LNmTECR0_SGN_PREQ | LNmTECR0_RATIO_PREQ;
>> +	tecr0_mask |= LNmTECR0_SGN_POST1Q | LNmTECR0_RATIO_PST1Q;
>> +	tecr0_mask |= LNmTECR0_ADPT_EQ | LNmTECR0_AMP_RED;
>> +	tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, params->teq);
>> +	if (params->preq_ratio) {
>> +		tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
>> +		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
>> +	}
>> +	if (params->postq_ratio) {
>> +		tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
>> +		tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
>> +	}
>> +	tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
>> +	tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
>> +
>> +	mutex_lock(&serdes->lock);
>> +
>> +	/* Disable the old controller */
>> +	if (old_mode) {
>> +		tmp = lynx_read(serdes, PCCRn(old_mode->pccr));
>> +		tmp = lynx_proto_mode_prep(old_mode, tmp, LYNX_PROTO_NONE);
>> +		lynx_write(serdes, tmp, PCCRn(old_mode->pccr));
>> +
>> +		if (old_mode->protos & PROTO_MASK(SGMII)) {
>> +			tmp = lynx_read(serdes, SGMIIaCR1(old_mode->idx));
>> +			tmp &= SGMIIaCR1_SGPCS_EN;
>> +			lynx_write(serdes, tmp, SGMIIaCR1(old_mode->idx));
>> +		}
>> +	}
>> +
>> +	for_each_lane_reverse(i, group) {
>> +		tmp = lynx_read(serdes, LNmGCR0(i));
>> +		tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
>> +		lynx_write(serdes, tmp, LNmGCR0(i));
>> +		ndelay(50);
>> +
>> +		tmp &= ~gcr0_mask;
>> +		tmp |= gcr0;
>> +		tmp |= FIELD_PREP(LNmGCR0_FIRST_LANE, i == group->first_lane);
>> +		lynx_write(serdes, tmp, LNmGCR0(i));
>> +
>> +		tmp = lynx_read(serdes, LNmGCR1(i));
>> +		tmp &= ~gcr1_mask;
>> +		tmp |= gcr1;
>> +		lynx_write(serdes, tmp, LNmGCR1(i));
>> +
>> +		tmp = lynx_read(serdes, LNmRECR0(i));
>> +		tmp &= ~recr0_mask;
>> +		tmp |= recr0;
>> +		lynx_write(serdes, tmp, LNmRECR0(i));
>> +
>> +		tmp = lynx_read(serdes, LNmTECR0(i));
>> +		tmp &= ~tecr0_mask;
>> +		tmp |= tecr0;
>> +		lynx_write(serdes, tmp, LNmTECR0(i));
>> +
>> +		tmp = lynx_read(serdes, LNmTTLCR0(i));
>> +		tmp &= ~LNmTTLCR0_FLT_SEL;
>> +		tmp |= FIELD_PREP(LNmTTLCR0_FLT_SEL, params->flt_sel);
>> +		lynx_write(serdes, tmp, LNmTTLCR0(i));
>> +
>> +		ndelay(120);
>> +		tmp = lynx_read(serdes, LNmGCR0(i));
>> +		tmp |= LNmGCR0_RRST_B | LNmGCR0_TRST_B;
>> +		lynx_write(serdes, tmp, LNmGCR0(i));
>> +	}
>> +
>> +	if (proto == LYNX_PROTO_1000BASEKX) {
>> +		/* FIXME: this races with clock updates */
>> +		tmp = lynx_read(serdes, PLLaCR0(pll));
>> +		tmp &= ~PLLaCR0_DLYDIV_SEL;
>> +		tmp |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, 1);
>> +		lynx_write(serdes, tmp, PLLaCR0(pll));
>> +	}
>> +
>> +	/* Enable the new controller */
>> +	tmp = lynx_read(serdes, PCCRn(new_mode->pccr));
>> +	tmp = lynx_proto_mode_prep(new_mode, tmp, proto);
>> +	lynx_write(serdes, tmp, PCCRn(new_mode->pccr));
>> +
>> +	if (new_mode->protos & PROTO_MASK(SGMII)) {
>> +		tmp = lynx_read(serdes, SGMIIaCR1(new_mode->idx));
>> +		tmp |= SGMIIaCR1_SGPCS_EN;
>> +		lynx_write(serdes, tmp, SGMIIaCR1(new_mode->idx));
>> +	}
>> +
>> +	mutex_unlock(&serdes->lock);
>> +
>> +	group->proto = proto;
>> +	dev_dbg(&phy->dev, "set mode to %s on lanes %u to %u\n",
>> +		lynx_proto_str[proto], group->first_lane, group->last_lane);
>> +	return 0;
>> +}
>> +
>> +static void lynx_release(struct phy *phy)
>> +{
>> +	struct lynx_group *group = phy_get_drvdata(phy);
>> +	struct lynx_priv *serdes = group->serdes;
>> +
>> +	mutex_lock(&serdes->lock);
>> +	if (--group->users) {
>> +		mutex_unlock(&serdes->lock);
>> +		return;
>> +	}
>> +	list_del(&group->groups);
>> +	mutex_unlock(&serdes->lock);
>> +
>> +	phy_destroy(phy);
>> +	kfree(group);
>> +}
>> +
>> +static const struct phy_ops lynx_phy_ops = {
>> +	.init = lynx_init,
>> +	.exit = lynx_exit,
>> +	.power_on = lynx_power_on,
>> +	.power_off = lynx_power_off,
>> +	.set_mode = lynx_set_mode,
>> +	.validate = lynx_validate,
>> +	.release = lynx_release,
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static struct phy *lynx_xlate(struct device *dev, struct of_phandle_args *args)
>> +{
>> +	struct phy *phy;
>> +	struct list_head *head;
>> +	struct lynx_group *group;
>> +	struct lynx_priv *serdes = dev_get_drvdata(dev);
>> +	unsigned int last_lane;
>> +
>> +	if (args->args_count == 1)
>> +		last_lane = args->args[0];
>> +	else if (args->args_count == 2)
>> +		last_lane = args->args[1];
>> +	else
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	mutex_lock(&serdes->lock);
>> +
>> +	/* Look for an existing group */
>> +	list_for_each(head, &serdes->groups) {
>> +		group = container_of(head, struct lynx_group, groups);
>> +		if (group->first_lane == args->args[0] &&
>> +		    group->last_lane == last_lane) {
>> +			group->users++;
>> +			return group->phy;
>> +		}
>> +	}
>> +
>> +	/* None found, create our own */
>> +	group = kzalloc(sizeof(*group), GFP_KERNEL);
>> +	if (!group) {
>> +		mutex_unlock(&serdes->lock);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	group->serdes = serdes;
>> +	group->first_lane = args->args[0];
>> +	group->last_lane = last_lane;
>> +	group->users = 1;
>> +	phy = phy_create(dev, NULL, &lynx_phy_ops);
>> +	if (IS_ERR(phy)) {
>> +		kfree(group);
>> +	} else {
>> +		group->phy = phy;
>> +		phy_set_drvdata(phy, group);
>> +		list_add(&group->groups, &serdes->groups);
>> +	}
>> +
>> +	mutex_unlock(&serdes->lock);
>> +	return phy;
>> +}
>> +
>> +static int lynx_probe(struct platform_device *pdev)
>> +{
>> +	bool grabbed_clocks = false;
>> +	int i, ret;
>> +	struct device *dev = &pdev->dev;
>> +	struct lynx_priv *serdes;
>> +	struct regmap_config regmap_config = {};
>> +	const struct lynx_conf *conf;
>> +	struct resource *res;
>> +	void __iomem *base;
>> +
>> +	serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
>> +	if (!serdes)
>> +		return -ENOMEM;
>> +	platform_set_drvdata(pdev, serdes);
>> +	mutex_init(&serdes->lock);
>> +	INIT_LIST_HEAD(&serdes->groups);
>> +	serdes->dev = dev;
>> +
>> +	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
>> +	if (IS_ERR(base)) {
>> +		ret = PTR_ERR(base);
>> +		dev_err_probe(dev, ret, "could not get/map registers\n");
>> +		return ret;
>> +	}
>> +
>> +	conf = device_get_match_data(dev);
>> +	serdes->conf = conf;
>> +	regmap_config.reg_bits = 32;
>> +	regmap_config.reg_stride = 4;
>> +	regmap_config.val_bits = 32;
>> +	regmap_config.val_format_endian = conf->endian;
>> +	regmap_config.max_register = res->end - res->start;
>> +	regmap_config.disable_locking = true;
>> +	serdes->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
>> +	if (IS_ERR(serdes->regmap)) {
>> +		ret = PTR_ERR(serdes->regmap);
>> +		dev_err_probe(dev, ret, "could not create regmap\n");
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(serdes->ref); i++) {
>> +		static const char fmt[] = "ref%d";
>> +		char name[sizeof(fmt)];
>> +
>> +		snprintf(name, sizeof(name), fmt, i);
>> +		serdes->ref[i] = devm_clk_get(dev, name);
>> +		if (IS_ERR(serdes->ref[i])) {
>> +			ret = PTR_ERR(serdes->ref[i]);
>> +			dev_err_probe(dev, ret, "could not get %s\n", name);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(serdes->pll); i++) {
>> +		static const char fmt[] = "%s.pll%d";
>> +		char *name;
>> +		const struct clk_hw *ref_hw[] = {
>> +			__clk_get_hw(serdes->ref[i]),
>> +		};
>> +		size_t len;
>> +		struct clk_init_data init = {};
>> +
>> +		len = snprintf(NULL, 0, fmt, pdev->name, i);
>> +		name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
>> +		if (!name)
>> +			return -ENOMEM;
>> +
>> +		snprintf(name, len + 1, fmt, pdev->name, i);
>> +		init.name = name;
>> +		init.ops = &lynx_pll_clk_ops;
>> +		init.parent_hws = ref_hw;
>> +		init.num_parents = 1;
>> +		init.flags = CLK_SET_RATE_GATE | CLK_GET_RATE_NOCACHE;
>> +		init.flags |= CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
>> +
>> +		serdes->pll[i].hw.init = &init;
>> +		serdes->pll[i].serdes = serdes;
>> +		serdes->pll[i].idx = i;
>> +		ret = devm_clk_hw_register(dev, &serdes->pll[i].hw);
>> +		if (ret) {
>> +			dev_err_probe(dev, ret, "could not register %s\n",
>> +				      name);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	ret = devm_of_clk_add_hw_provider(dev, lynx_clk_get, serdes);
>> +	if (ret) {
>> +		dev_err_probe(dev, ret, "could not register clock provider\n");
>> +		return ret;
>> +	}
>> +
>> +	/* Deselect anything configured by the RCW/bootloader */
>> +	for (i = 0; i < conf->mode_count; i++) {
>> +		const struct lynx_mode *mode = &conf->modes[i];
>> +		u32 pccr = lynx_read(serdes, PCCRn(mode->pccr));
>> +
>> +		if (lynx_proto_mode_get(mode, pccr) == mode->cfg) {
>> +			if (mode->protos & UNSUPPORTED_PROTOS) {
>> +				/* Don't mess with modes we don't support */
>> +				serdes->used_lanes |= mode->lanes;
>> +				if (grabbed_clocks)
>> +					continue;
>> +
>> +				grabbed_clocks = true;
>> +				clk_prepare_enable(serdes->pll[0].hw.clk);
>> +				clk_prepare_enable(serdes->pll[1].hw.clk);
>> +				clk_rate_exclusive_get(serdes->pll[0].hw.clk);
>> +				clk_rate_exclusive_get(serdes->pll[1].hw.clk);
>> +			} else {
>> +				/* Otherwise, clear out the existing config */
>> +				pccr = lynx_proto_mode_prep(mode, pccr,
>> +							    LYNX_PROTO_NONE);
>> +				lynx_write(serdes, pccr, PCCRn(mode->pccr));
>> +			}
>> +
>> +			/* Disable the SGMII PCS until we're ready for it */
>> +			if (mode->protos & LYNX_PROTO_SGMII) {
>> +				u32 cr1;
>> +
>> +				cr1 = lynx_read(serdes, SGMIIaCR1(mode->idx));
>> +				cr1 &= ~SGMIIaCR1_SGPCS_EN;
>> +				lynx_write(serdes, cr1, SGMIIaCR1(mode->idx));
>> +			}
>> +		}
>> +	}
>> +
>> +	/* Power off all lanes; used ones will be powered on later */
>> +	for (i = 0; i < conf->lanes; i++)
>> +		lynx_power_off_lane(serdes, i);
>> +
>> +	ret = PTR_ERR_OR_ZERO(devm_of_phy_provider_register(dev, lynx_xlate));
>> +	if (ret)
>> +		dev_err_probe(dev, ret, "could not register phy provider\n");
>> +	else
>> +		dev_info(dev, "probed with %d lanes\n", conf->lanes);
>> +	return ret;
>> +}
>> +
>> +/*
>> + * XXX: For SerDes1, lane A uses pins SD1_RX3_P/N! That is, the lane numbers
>> + * and pin numbers are _reversed_. In addition, the PCCR documentation is
>> + * _inconsistent_ in its usage of these terms!
>> + *
>> + * PCCR "Lane 0" refers to...
>> + * ==== =====================
>> + *    0 Lane A
>> + *    2 Lane A
>> + *    8 Lane A
>> + *    9 Lane A
>> + *    B Lane D!
>> + */
>> +static const struct lynx_mode ls1046a_modes1[] = {
>> +	CONF_SINGLE(1, PCIE, 0x0, 1, 0b001), /* PCIe.1 x1 */
>> +	CONF_1000BASEKX(0, 0x8, 0, 0b001), /* SGMII.6 */
>> +	CONF_SGMII25KX(1, 0x8, 1, 0b001), /* SGMII.5 */
>> +	CONF_SGMII25KX(2, 0x8, 2, 0b001), /* SGMII.10 */
>> +	CONF_SGMII25KX(3, 0x8, 3, 0b001), /* SGMII.9 */
>> +	CONF_SINGLE(1, QSGMII, 0x9, 2, 0b001), /* QSGMII.6,5,10,1 */
>> +	CONF_XFI(2, 0xB, 0, 0b010), /* XFI.10 */
>> +	CONF_XFI(3, 0xB, 1, 0b001), /* XFI.9 */
>> +};
>> +
>> +static const struct lynx_conf ls1046a_conf1 = {
>> +	.modes = ls1046a_modes1,
>> +	.mode_count = ARRAY_SIZE(ls1046a_modes1),
>> +	.lanes = 4,
>> +	.endian = REGMAP_ENDIAN_BIG,
>> +};
>> +
>> +static const struct lynx_mode ls1046a_modes2[] = {
>> +	CONF_SINGLE(0, PCIE, 0x0, 0, 0b001), /* PCIe.1 x1 */
>> +	CONF(GENMASK(3, 0), PROTO_MASK(PCIE), 0x0, 0, 0b011), /* PCIe.1 x4 */
>> +	CONF_SINGLE(2, PCIE, 0x0, 2, 0b001), /* PCIe.2 x1 */
>> +	CONF(GENMASK(3, 2), PROTO_MASK(PCIE), 0x0, 2, 0b010), /* PCIe.3 x2 */
>> +	CONF_SINGLE(3, PCIE, 0x0, 2, 0b011), /* PCIe.3 x1 */
>> +	CONF_SINGLE(3, SATA, 0x2, 0, 0b001), /* SATA */
>> +	CONF_1000BASEKX(1, 0x8, 1, 0b001), /* SGMII.2 */
>> +};
>> +
>> +static const struct lynx_conf ls1046a_conf2 = {
>> +	.modes = ls1046a_modes2,
>> +	.mode_count = ARRAY_SIZE(ls1046a_modes2),
>> +	.lanes = 4,
>> +	.endian = REGMAP_ENDIAN_BIG,
>> +};
>> +
>> +static const struct of_device_id lynx_of_match[] = {
>> +	{ .compatible = "fsl,ls1046a-serdes-1", .data = &ls1046a_conf1 },
>> +	{ .compatible = "fsl,ls1046a-serdes-2", .data = &ls1046a_conf2 },
>> +};
>> +MODULE_DEVICE_TABLE(of, lynx_of_match);
>> +
>> +static struct platform_driver lynx_driver = {
>> +	.probe = lynx_probe,
>> +	.driver = {
>> +		.name = "qoriq_serdes",
>> +		.of_match_table = lynx_of_match,
>> +	},
>> +};
>> +module_platform_driver(lynx_driver);
>> +
>> +MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
>> +MODULE_DESCRIPTION("Lynx 10G SerDes driver");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 2.35.1.1320.gc452695387.dirty
> 

--Sean

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-07-05 15:29     ` Sean Anderson
@ 2022-07-06 16:57       ` Vinod Koul
  2022-07-07 15:00         ` Sean Anderson
  0 siblings, 1 reply; 32+ messages in thread
From: Vinod Koul @ 2022-07-06 16:57 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Ioana Ciornei, Jonathan Corbet,
	Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring,
	devicetree, linux-doc, linux-phy

On 05-07-22, 11:29, Sean Anderson wrote:

> >> +	/* TODO: wait for the PLL to lock */
> > 
> > when will this be added?
> 
> I'm not sure. I haven't had any issues with this, and waiting on the lock bit is
> only mentioned in some datasheets for this SerDes. On the LS1046A for example,
> there is no mention of waiting for lock.

okay maybe remove the comment then?

> >> +static const struct clk_ops lynx_pll_clk_ops = {
> >> +	.enable = lynx_pll_enable,
> >> +	.disable = lynx_pll_disable,
> >> +	.is_enabled = lynx_pll_is_enabled,
> >> +	.recalc_rate = lynx_pll_recalc_rate,
> >> +	.round_rate = lynx_pll_round_rate,
> >> +	.set_rate = lynx_pll_set_rate,
> >> +};
> > 
> > right, this should be a clk driver
> 
> Well, it is a clock driver, effectively internal to the SerDes. There are a few
> examples of this already (e.g. the qualcomm and cadence phys). It could of course
> be split off, but I would prefer that they remained together.

I would prefer clk driver is split and we maintain clean split b/w phy
and clk

-- 
~Vinod

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

* Re: [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver
  2022-07-06 16:57       ` Vinod Koul
@ 2022-07-07 15:00         ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-07-07 15:00 UTC (permalink / raw)
  To: Vinod Koul
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Ioana Ciornei, Jonathan Corbet,
	Kishon Vijay Abraham I, Krzysztof Kozlowski, Rob Herring,
	devicetree, linux-doc, linux-phy

Hi Vinod,

On 7/6/22 12:57 PM, Vinod Koul wrote:
> On 05-07-22, 11:29, Sean Anderson wrote:
> 
>> >> +	/* TODO: wait for the PLL to lock */
>> > 
>> > when will this be added?
>> 
>> I'm not sure. I haven't had any issues with this, and waiting on the lock bit is
>> only mentioned in some datasheets for this SerDes. On the LS1046A for example,
>> there is no mention of waiting for lock.
> 
> okay maybe remove the comment then?

Well, as it happens, on the write before this (where we request the reset), we must
wait for the request to clear before making this write. Since that needed a
read_poll_timeout anyway, I added one for this line as well.

>> >> +static const struct clk_ops lynx_pll_clk_ops = {
>> >> +	.enable = lynx_pll_enable,
>> >> +	.disable = lynx_pll_disable,
>> >> +	.is_enabled = lynx_pll_is_enabled,
>> >> +	.recalc_rate = lynx_pll_recalc_rate,
>> >> +	.round_rate = lynx_pll_round_rate,
>> >> +	.set_rate = lynx_pll_set_rate,
>> >> +};
>> > 
>> > right, this should be a clk driver
>> 
>> Well, it is a clock driver, effectively internal to the SerDes. There are a few
>> examples of this already (e.g. the qualcomm and cadence phys). It could of course
>> be split off, but I would prefer that they remained together.
> 
> I would prefer clk driver is split and we maintain clean split b/w phy
> and clk
> 

OK. I will split this into drivers/phy/freescale/phy-fsl-lynx-10g-clk.c

--Sean

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

* Re: [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties
  2022-06-30 16:11     ` Sean Anderson
@ 2022-07-12 19:36       ` Rob Herring
  2022-07-12 19:56         ` Sean Anderson
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2022-07-12 19:36 UTC (permalink / raw)
  To: Sean Anderson
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Krzysztof Kozlowski, devicetree

On Thu, Jun 30, 2022 at 12:11:10PM -0400, Sean Anderson wrote:
> Hi Rob,
> 
> On 6/30/22 12:01 PM, Rob Herring wrote:
> > On Tue, Jun 28, 2022 at 06:13:32PM -0400, Sean Anderson wrote:
> >> At the moment, mEMACs are configured almost completely based on the
> >> phy-connection-type. That is, if the phy interface is RGMII, it assumed
> >> that RGMII is supported. For some interfaces, it is assumed that the
> >> RCW/bootloader has set up the SerDes properly. This is generally OK, but
> >> restricts runtime reconfiguration. The actual link state is never
> >> reported.
> >> 
> >> To address these shortcomings, the driver will need additional
> >> information. First, it needs to know how to access the PCS/PMAs (in
> >> order to configure them and get the link status). The SGMII PCS/PMA is
> >> the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as
> >> well. The XFI (and 1GBase-KR) PCS/PMA is a c45 "phy" which sits on the
> >> same MDIO bus as SGMII PCS/PMA. By default they will have conflicting
> >> addresses, but they are also not enabled at the same time by default.
> >> Therefore, we can let the XFI PCS/PMA be the default when
> >> phy-connection-type is xgmii. This will allow for
> >> backwards-compatibility.
> >> 
> >> QSGMII, however, cannot work with the current binding. This is because
> >> the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the
> >> moment this is worked around by having every MAC write to the PCS/PMA
> >> addresses (without checking if they are present). This only works if
> >> each MAC has the same configuration, and only if we don't need to know
> >> the status. Because the QSGMII PCS/PMA will typically be located on a
> >> different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback
> >> for the QSGMII PCS/PMA.
> >> 
> >> mEMACs (across all SoCs) support the following protocols:
> >> 
> >> - MII
> >> - RGMII
> >> - SGMII, 1000Base-X, and 1000Base-KX
> >> - 2500Base-X (aka 2.5G SGMII)
> >> - QSGMII
> >> - 10GBase-R (aka XFI) and 10GBase-KR
> >> - XAUI and HiGig
> >> 
> >> Each line documents a set of orthogonal protocols (e.g. XAUI is
> >> supported if and only if HiGig is supported). Additionally,
> >> 
> >> - XAUI implies support for 10GBase-R
> >> - 10GBase-R is supported if and only if RGMII is not supported
> >> - 2500Base-X implies support for 1000Base-X
> >> - MII implies support for RGMII
> >> 
> >> To switch between different protocols, we must reconfigure the SerDes.
> >> This is done by using the standard phys property. We can also use it to
> >> validate whether different protocols are supported (e.g. using
> >> phy_validate). This will work for serial protocols, but not RGMII or
> >> MII. Additionally, we still need to be compatible when there is no
> >> SerDes.
> >> 
> >> While we can detect 10G support by examining the port speed (as set by
> >> fsl,fman-10g-port), we cannot determine support for any of the other
> >> protocols based on the existing binding. In fact, the binding works
> >> against us in some respects, because pcsphy-handle is required even if
> >> there is no possible PCS/PMA for that MAC. To allow for backwards-
> >> compatibility, we use a boolean-style property for RGMII (instead of
> >> presence/absence-style). When the property for RGMII is missing, we will
> >> assume that it is supported. The exception is MII, since no existing
> >> device trees use it (as far as I could tell).
> >> 
> >> Unfortunately, QSGMII support will be broken for old device trees. There
> >> is nothing we can do about this because of the PCS/PMA situation (as
> >> described above).
> >> 
> >> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> >> ---
> >> 
> >> Changes in v2:
> >> - Better document how we select which PCS to use in the default case
> >> 
> >>  .../bindings/net/fsl,fman-dtsec.yaml          | 52 +++++++++++++++++--
> >>  .../devicetree/bindings/net/fsl-fman.txt      |  5 +-
> >>  2 files changed, 51 insertions(+), 6 deletions(-)
> >> 
> >> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> >> index 809df1589f20..ecb772258164 100644
> >> --- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> >> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
> >> @@ -85,9 +85,41 @@ properties:
> >>      $ref: /schemas/types.yaml#/definitions/phandle
> >>      description: A reference to the IEEE1588 timer
> >>  
> >> +  phys:
> >> +    description: A reference to the SerDes lane(s)
> >> +    maxItems: 1
> >> +
> >> +  phy-names:
> >> +    items:
> >> +      - const: serdes
> >> +
> >>    pcsphy-handle:
> >> -    $ref: /schemas/types.yaml#/definitions/phandle
> >> -    description: A reference to the PCS (typically found on the SerDes)
> >> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> >> +    minItems: 1
> >> +    maxItems: 3
> > 
> > What determines how many entries?
> 
> It depends on what the particular MAC supports. From what I can tell, the following
> combinations are valid:
> 
> - Neither SGMII, QSGMII, or XFI
> - Just SGMII
> - Just QSGMII
> - SGMII and QSGMII
> - SGMII and XFI
> - All of SGMII, QSGMII, and XFI
> 
> All of these are used on different SoCs.

So there will be a different PCS device for SGMII, QSGMII, and XFI 
rather than 1 PCS device that supports those 3 interfaces?


> >> +    description: |
> >> +      A reference to the various PCSs (typically found on the SerDes). If
> >> +      pcs-names is absent, and phy-connection-type is "xgmii", then the first
> >> +      reference will be assumed to be for "xfi". Otherwise, if pcs-names is
> >> +      absent, then the first reference will be assumed to be for "sgmii".
> >> +
> >> +  pcs-names:
> >> +    $ref: /schemas/types.yaml#/definitions/string-array
> >> +    minItems: 1
> >> +    maxItems: 3
> >> +    contains:
> >> +      enum:
> >> +        - sgmii
> >> +        - qsgmii
> >> +        - xfi
> > 
> > This means '"foo", "xfi", "bar"' is valid. I think you want to 
> > s/contains/items/.
> > 
> >> +    description: The type of each PCS in pcsphy-handle.
> >> +
> > 
> >> +  rgmii:
> >> +    enum: [0, 1]
> >> +    description: 1 indicates RGMII is supported, and 0 indicates it is not.
> >> +
> >> +  mii:
> >> +    description: If present, indicates that MII is supported.
> > 
> > Types? Need vendor prefixes.
> 
> OK.
> 
> > Are these board specific or SoC specific? Properties are appropriate for 
> > the former. The latter case should be implied by the compatible string.
> 
> Unfortunately, there are not existing specific compatible strings for each
> device in each SoC. I suppose those could be added; however, this basically
> reflects how each device is hooked up. E.g. on one SoC a device would be
> connected to the RGMII pins, but not on another SoC. The MAC itself still
> has hardware support for RGMII, but such a configuration would not function.

A difference in instances on a given SoC would also be reason for 
properties rather than different compatible strings. However, we already 
have such properties. We have 'phy-connection-type' for which mode to 
use. Do you have some need to know all possible modes? I think there was 
something posted to allow 'phy-connection-type' to be an array of 
supported modes instead.

Rob

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

* Re: [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties
  2022-07-12 19:36       ` Rob Herring
@ 2022-07-12 19:56         ` Sean Anderson
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Anderson @ 2022-07-12 19:56 UTC (permalink / raw)
  To: Rob Herring
  Cc: David S . Miller, Jakub Kicinski, Madalin Bucur, netdev,
	Russell King, Paolo Abeni, linux-arm-kernel, Eric Dumazet,
	linux-kernel, Krzysztof Kozlowski, devicetree

Hi Rob,

On 7/12/22 3:36 PM, Rob Herring wrote:
> On Thu, Jun 30, 2022 at 12:11:10PM -0400, Sean Anderson wrote:
>> Hi Rob,
>> 
>> On 6/30/22 12:01 PM, Rob Herring wrote:
>> > On Tue, Jun 28, 2022 at 06:13:32PM -0400, Sean Anderson wrote:
>> >> At the moment, mEMACs are configured almost completely based on the
>> >> phy-connection-type. That is, if the phy interface is RGMII, it assumed
>> >> that RGMII is supported. For some interfaces, it is assumed that the
>> >> RCW/bootloader has set up the SerDes properly. This is generally OK, but
>> >> restricts runtime reconfiguration. The actual link state is never
>> >> reported.
>> >> 
>> >> To address these shortcomings, the driver will need additional
>> >> information. First, it needs to know how to access the PCS/PMAs (in
>> >> order to configure them and get the link status). The SGMII PCS/PMA is
>> >> the only currently-described PCS/PMA. Add the XFI and QSGMII PCS/PMAs as
>> >> well. The XFI (and 1GBase-KR) PCS/PMA is a c45 "phy" which sits on the
>> >> same MDIO bus as SGMII PCS/PMA. By default they will have conflicting
>> >> addresses, but they are also not enabled at the same time by default.
>> >> Therefore, we can let the XFI PCS/PMA be the default when
>> >> phy-connection-type is xgmii. This will allow for
>> >> backwards-compatibility.
>> >> 
>> >> QSGMII, however, cannot work with the current binding. This is because
>> >> the QSGMII PCS/PMAs are only present on one MAC's MDIO bus. At the
>> >> moment this is worked around by having every MAC write to the PCS/PMA
>> >> addresses (without checking if they are present). This only works if
>> >> each MAC has the same configuration, and only if we don't need to know
>> >> the status. Because the QSGMII PCS/PMA will typically be located on a
>> >> different MDIO bus than the MAC's SGMII PCS/PMA, there is no fallback
>> >> for the QSGMII PCS/PMA.
>> >> 
>> >> mEMACs (across all SoCs) support the following protocols:
>> >> 
>> >> - MII
>> >> - RGMII
>> >> - SGMII, 1000Base-X, and 1000Base-KX
>> >> - 2500Base-X (aka 2.5G SGMII)
>> >> - QSGMII
>> >> - 10GBase-R (aka XFI) and 10GBase-KR
>> >> - XAUI and HiGig
>> >> 
>> >> Each line documents a set of orthogonal protocols (e.g. XAUI is
>> >> supported if and only if HiGig is supported). Additionally,
>> >> 
>> >> - XAUI implies support for 10GBase-R
>> >> - 10GBase-R is supported if and only if RGMII is not supported
>> >> - 2500Base-X implies support for 1000Base-X
>> >> - MII implies support for RGMII
>> >> 
>> >> To switch between different protocols, we must reconfigure the SerDes.
>> >> This is done by using the standard phys property. We can also use it to
>> >> validate whether different protocols are supported (e.g. using
>> >> phy_validate). This will work for serial protocols, but not RGMII or
>> >> MII. Additionally, we still need to be compatible when there is no
>> >> SerDes.
>> >> 
>> >> While we can detect 10G support by examining the port speed (as set by
>> >> fsl,fman-10g-port), we cannot determine support for any of the other
>> >> protocols based on the existing binding. In fact, the binding works
>> >> against us in some respects, because pcsphy-handle is required even if
>> >> there is no possible PCS/PMA for that MAC. To allow for backwards-
>> >> compatibility, we use a boolean-style property for RGMII (instead of
>> >> presence/absence-style). When the property for RGMII is missing, we will
>> >> assume that it is supported. The exception is MII, since no existing
>> >> device trees use it (as far as I could tell).
>> >> 
>> >> Unfortunately, QSGMII support will be broken for old device trees. There
>> >> is nothing we can do about this because of the PCS/PMA situation (as
>> >> described above).
>> >> 
>> >> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> >> ---
>> >> 
>> >> Changes in v2:
>> >> - Better document how we select which PCS to use in the default case
>> >> 
>> >>  .../bindings/net/fsl,fman-dtsec.yaml          | 52 +++++++++++++++++--
>> >>  .../devicetree/bindings/net/fsl-fman.txt      |  5 +-
>> >>  2 files changed, 51 insertions(+), 6 deletions(-)
>> >> 
>> >> diff --git a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> >> index 809df1589f20..ecb772258164 100644
>> >> --- a/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> >> +++ b/Documentation/devicetree/bindings/net/fsl,fman-dtsec.yaml
>> >> @@ -85,9 +85,41 @@ properties:
>> >>      $ref: /schemas/types.yaml#/definitions/phandle
>> >>      description: A reference to the IEEE1588 timer
>> >>  
>> >> +  phys:
>> >> +    description: A reference to the SerDes lane(s)
>> >> +    maxItems: 1
>> >> +
>> >> +  phy-names:
>> >> +    items:
>> >> +      - const: serdes
>> >> +
>> >>    pcsphy-handle:
>> >> -    $ref: /schemas/types.yaml#/definitions/phandle
>> >> -    description: A reference to the PCS (typically found on the SerDes)
>> >> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>> >> +    minItems: 1
>> >> +    maxItems: 3
>> > 
>> > What determines how many entries?
>> 
>> It depends on what the particular MAC supports. From what I can tell, the following
>> combinations are valid:
>> 
>> - Neither SGMII, QSGMII, or XFI
>> - Just SGMII
>> - Just QSGMII
>> - SGMII and QSGMII
>> - SGMII and XFI
>> - All of SGMII, QSGMII, and XFI
>> 
>> All of these are used on different SoCs.
> 
> So there will be a different PCS device for SGMII, QSGMII, and XFI 
> rather than 1 PCS device that supports those 3 interfaces?

There were always 3 PCSs. There are two things which let the driver get
away with this. The first is that the default address of PCSs is 0, and
the boot hardware would enable only the PCS which was necessary. So you
could pretend that the same PCS would support both SGMII and XFI. In
fact, you can still do this, since the phy driver I add later in this
series is careful to duplicate this. Eventually, I would like to set the
PCS addresses so that both the SGMII and XFI phys will be accessible at
the same time (removing the need to enable/disable them). This is why I
have allowed for a separate XFI PCS.

When QSGMII is enabled, there are 4 QSGMII PCSs on the MDIO bus of one
of the MACs. The base address is, once again, 0. I believe this
overrides the SGMII PCS. When configuring for QSGMII, the driver would
always write to all QSGMII addresses. The MAC with the PCSs on its MDIO
bus would configure the PCSs for the other MACs as well. The other MACs
effectively made dummy writes (since there was nothing listening).
Unfortunately, this only works for writing registers. In order to get
the link status, we need the real QSGMII PCS.

>> >> +    description: |
>> >> +      A reference to the various PCSs (typically found on the SerDes). If
>> >> +      pcs-names is absent, and phy-connection-type is "xgmii", then the first
>> >> +      reference will be assumed to be for "xfi". Otherwise, if pcs-names is
>> >> +      absent, then the first reference will be assumed to be for "sgmii".
>> >> +
>> >> +  pcs-names:
>> >> +    $ref: /schemas/types.yaml#/definitions/string-array
>> >> +    minItems: 1
>> >> +    maxItems: 3
>> >> +    contains:
>> >> +      enum:
>> >> +        - sgmii
>> >> +        - qsgmii
>> >> +        - xfi
>> > 
>> > This means '"foo", "xfi", "bar"' is valid. I think you want to 
>> > s/contains/items/.
>> > 
>> >> +    description: The type of each PCS in pcsphy-handle.
>> >> +
>> > 
>> >> +  rgmii:
>> >> +    enum: [0, 1]
>> >> +    description: 1 indicates RGMII is supported, and 0 indicates it is not.
>> >> +
>> >> +  mii:
>> >> +    description: If present, indicates that MII is supported.
>> > 
>> > Types? Need vendor prefixes.
>> 
>> OK.
>> 
>> > Are these board specific or SoC specific? Properties are appropriate for 
>> > the former. The latter case should be implied by the compatible string.
>> 
>> Unfortunately, there are not existing specific compatible strings for each
>> device in each SoC. I suppose those could be added; however, this basically
>> reflects how each device is hooked up. E.g. on one SoC a device would be
>> connected to the RGMII pins, but not on another SoC. The MAC itself still
>> has hardware support for RGMII, but such a configuration would not function.
> 
> A difference in instances on a given SoC would also be reason for 
> properties rather than different compatible strings. However, we already 
> have such properties. We have 'phy-connection-type' for which mode to 
> use. Do you have some need to know all possible modes? I think there was 
> something posted to allow 'phy-connection-type' to be an array of 
> supported modes instead.

There's no need to know all possible modes. I can drop this if it is
preferred to use phy-connection-type without other checks.

--Sean

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

end of thread, other threads:[~2022-07-12 19:57 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-28 22:13 [PATCH net-next v2 00/35] [RFT] net: dpaa: Convert to phylink Sean Anderson
2022-06-28 22:13 ` [PATCH net-next v2 01/35] dt-bindings: phy: Add QorIQ SerDes binding Sean Anderson
2022-06-29  2:09   ` Rob Herring
2022-06-30 15:53     ` Sean Anderson
2022-06-30 17:27   ` Rob Herring
2022-06-30 18:01     ` Sean Anderson
2022-06-30 18:08       ` Krzysztof Kozlowski
2022-06-30 18:16         ` Sean Anderson
2022-06-28 22:13 ` [PATCH net-next v2 02/35] dt-bindings: net: Convert FMan MAC bindings to yaml Sean Anderson
2022-06-29  2:09   ` Rob Herring
2022-06-29 14:50   ` Russell King (Oracle)
2022-06-30 14:59     ` Sean Anderson
2022-07-01  0:01   ` Rob Herring
2022-06-28 22:13 ` [PATCH net-next v2 03/35] dt-bindings: net: fman: Add additional interface properties Sean Anderson
2022-06-30 16:01   ` Rob Herring
2022-06-30 16:11     ` Sean Anderson
2022-07-12 19:36       ` Rob Herring
2022-07-12 19:56         ` Sean Anderson
2022-06-28 22:13 ` [PATCH net-next v2 04/35] [RFC] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
2022-06-30 15:56   ` Ioana Ciornei
2022-06-30 18:11     ` Sean Anderson
2022-07-01 10:03       ` Ioana Ciornei
2022-07-01 15:51         ` Sean Anderson
     [not found]         ` <343faa45-4e4a-7a7f-b0c3-fcc9db89e976@seco.com>
2022-07-01 21:04           ` Sean Anderson
2022-07-05  6:12   ` Vinod Koul
2022-07-05 15:29     ` Sean Anderson
2022-07-06 16:57       ` Vinod Koul
2022-07-07 15:00         ` Sean Anderson
2022-06-28 22:14 ` [PATCH net-next v2 32/35] qoriq: Specify which MACs support RGMII Sean Anderson
2022-06-28 22:14 ` [PATCH net-next v2 33/35] qoriq: Add nodes for QSGMII PCSs Sean Anderson
2022-06-28 22:14 ` [PATCH net-next v2 34/35] arm64: dts: ls1046a: Add serdes bindings Sean Anderson
2022-06-28 22:14 ` [PATCH net-next v2 35/35] arm64: dts: ls1046ardb: " Sean Anderson

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).