* [PATCH v14 01/15] dt-bindings: phy: Add 2500BASE-X and 10GBASE-R
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-04-13 16:05 ` [PATCH v14 02/15] dt-bindings: phy: Add Lynx 10G phy binding Sean Anderson
` (14 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Rob Herring, Camelia Alexandra Groza, Bagas Sanjaya,
Ioana Ciornei, linuxppc-dev, Rob Herring, linux-arm-kernel
This adds some modes necessary for Lynx 10G support. 2500BASE-X, also
known as 2.5G SGMII, is 1000BASE-X/SGMII overclocked to 3.125 GHz, with
autonegotiation disabled. 10GBASE-R, also known as XFI, is the protocol
spoken between the PMA and PMD ethernet layers for 10GBASE-T and
10GBASE-S/L/E. It is typically used to communicate directly with SFP+
modules, or with 10GBASE-T phys.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
Acked-by: Rob Herring <robh@kernel.org>
---
PR increasing phy-type maximum [1].
If this commit could be applied sooner rather than later, I'd appreciate
it. This should help avoid another respin if someone else adds another
phy type.
[1] https://github.com/devicetree-org/dt-schema/pull/85
(no changes since v6)
Changes in v6:
- Bump PHY_TYPE_2500BASEX to 13, since PHY_TYPE_USXGMII was added in the
meantime
Changes in v4:
- New
include/dt-bindings/phy/phy.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h
index 6b901b342348..5b2b674d8d25 100644
--- a/include/dt-bindings/phy/phy.h
+++ b/include/dt-bindings/phy/phy.h
@@ -23,5 +23,7 @@
#define PHY_TYPE_DPHY 10
#define PHY_TYPE_CPHY 11
#define PHY_TYPE_USXGMII 12
+#define PHY_TYPE_2500BASEX 13
+#define PHY_TYPE_10GBASER 14
#endif /* _DT_BINDINGS_PHY */
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 02/15] dt-bindings: phy: Add Lynx 10G phy binding
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
2023-04-13 16:05 ` [PATCH v14 01/15] dt-bindings: phy: Add 2500BASE-X and 10GBASE-R Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-04-13 16:05 ` [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml Sean Anderson
` (13 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Michael Turquette, linux-clk, Stephen Boyd, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei,
linuxppc-dev, Rob Herring, linux-arm-kernel
This adds a binding for the SerDes module found on QorIQ processors.
Each phy is a subnode of the top-level device, possibly supporting
multiple lanes and protocols. This "thick" #phy-cells is used due to
allow for better organization of parameters. Note that the particular
parameters necessary to select a protocol-controller/lane combination
vary across different SoCs, and even within different SerDes on the same
SoC.
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.
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>
Reviewed-by: Rob Herring <robh@kernel.org>
---
(no changes since v9)
Changes in v9:
- Add fsl,unused-lanes-reserved to allow for a gradual transition
between firmware and Linux control of the SerDes
- Change phy-type back to fsl,type, as I was getting the error
'#phy-cells' is a dependency of 'phy-type'
Changes in v7:
- Use double quotes everywhere in yaml
Changes in v6:
- fsl,type -> phy-type
Changes in v4:
- Use subnodes to describe lane configuration, instead of describing
PCCRs. This is the same style used by phy-cadence-sierra et al.
Changes in v3:
- Manually expand yaml references
- Add mode configuration to device tree
Changes in v2:
- Rename to fsl,lynx-10g.yaml
- Refer to the device in the documentation, rather than the binding
- Move compatible first
- Document phy cells in the description
- 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.
- Remove minItems
- Use list for clock-names
- Fix example binding having too many cells in regs
- Add #clock-cells. This will allow using assigned-clocks* to configure
the PLLs.
- Document the structure of the compatible strings
.../devicetree/bindings/phy/fsl,lynx-10g.yaml | 248 ++++++++++++++++++
1 file changed, 248 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..7c364f7de85c
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
@@ -0,0 +1,248 @@
+# 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:
+ items:
+ - enum:
+ - fsl,ls1046a-serdes
+ - fsl,ls1088a-serdes
+ - const: fsl,lynx-10g
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ "#clock-cells":
+ const: 1
+ description: |
+ The cell contains an ID as described in dt-bindings/clock/fsl,lynx-10g.h.
+ Note that when assigning a rate to a PLL, the PLL's rate is divided by
+ 1000 to avoid overflow. A rate of 5000000 corresponds to 5GHz.
+
+ clocks:
+ maxItems: 2
+ description: |
+ Clock for each PLL reference clock input.
+
+ clock-names:
+ minItems: 2
+ maxItems: 2
+ items:
+ enum:
+ - ref0
+ - ref1
+
+ fsl,unused-lanes-reserved:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: |
+ Unused lanes are reserved for firmware use, and should not be disabled.
+ Normally, groups containing unused lanes may be reconfigured or disabled
+ to save power. However, when this property is present, unused lanes will
+ not be touched until they are used by another driver. This allows
+ migrating from firmware control of lanes to driver control.
+
+ Lanes not present in any group will never be modified, regardless of the
+ presence of this property.
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ "^phy@":
+ type: object
+
+ description: |
+ A contiguous group of lanes which will be configured together. Each group
+ corresponds to one phy device. Lanes not described by any group will be
+ left as-is.
+
+ properties:
+ "#phy-cells":
+ const: 0
+
+ reg:
+ minItems: 1
+ maxItems: 8
+ description:
+ The lanes in the group. These must be listed in order. The first lane
+ will have the FIRST_LANE bit set in GCR0. The order of lanes also
+ determines the reset order (TRSTDIR).
+
+ patternProperties:
+ "^(q?sgmii|xfi)":
+ type: object
+
+ description: |
+ A protocol controller which may control the group of lanes. Each
+ controller is selected through the PCCRs. In addition to protocols
+ desired for use by the OS, protocols which may have been configured
+ by the bootloader must also be described. This ensures that only one
+ protocol controller is attached to a group of lanes at once.
+
+ properties:
+ fsl,pccr:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ The index of the PCCR which configures this protocol controller.
+ This is the same as the register name suffix. For example, PCCR8
+ would use a value of 8 for an offset of 0x220 (0x200 + 4 * 8).
+
+ fsl,index:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ The index of the protocol controller. This corresponds to the
+ suffix in the documentation. For example, PEXa would be 0, PEXb
+ 1, etc. Generally, higher fields occupy lower bits.
+
+ fsl,cfg:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 1
+ description: |
+ The configuration value to program into the protocol controller
+ field.
+
+ fsl,type:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum:
+ - 8 # PHY_TYPE_SGMII
+ - 9 # PHY_TYPE_QSGMII
+ - 13 # PHY_TYPE_2500BASEX
+ - 14 # PHY_TYPE_10GBASER
+ description: |
+ The category of protocols supported by this controller. See
+ "dt-bindings/phy/phy.h" for the relevant definitions. Individual
+ protocols are selected by the phy consumer. The availability of
+ 1000BASE-KX and 10GBASE-KR depends on the SoC.
+
+ - PHY_TYPE_SGMII: 1000BASE-X, SGMII, and 1000BASE-KX
+ - PHY_TYPE_2500BASEX: 2500BASE-X, 1000BASE-X, SGMII, and
+ 1000BASE-KX
+ - PHY_TYPE_QSGMII: QSGMII
+ - PHY_TYPE_10GBASER: 10GBASE-R and 10GBASE-KR
+
+ required:
+ - fsl,pccr
+ - fsl,index
+ - fsl,cfg
+ - fsl,type
+
+ additionalProperties: false
+
+ required:
+ - "#phy-cells"
+ - reg
+
+ additionalProperties: false
+
+required:
+ - "#address-cells"
+ - "#clock-cells"
+ - "#size-cells"
+ - compatible
+ - clocks
+ - clock-names
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/phy/phy.h>
+
+ serdes1: serdes@1ea0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+ compatible = "fsl,ls1046a-serdes", "fsl,lynx-10g";
+ reg = <0x1ea0000 0x2000>;
+ clocks = <&clk_100mhz>, <&clk_156mhz>;
+ clock-names = "ref0", "ref1";
+
+ serdes1_0: phy@0 {
+ #phy-cells = <0>;
+ reg = <0>;
+
+ /* SGMII.6 */
+ sgmii-0 {
+ fsl,pccr = <0x8>;
+ fsl,index = <0>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_SGMII>;
+ };
+ };
+
+ serdes1_1: phy@1 {
+ #phy-cells = <0>;
+ reg = <1>;
+
+ /* SGMII.5 */
+ sgmii-1 {
+ fsl,pccr = <0x8>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+ };
+
+ serdes1_2: phy@2 {
+ #phy-cells = <0>;
+ reg = <2>;
+
+ /* SGMII.10 */
+ sgmii-2 {
+ fsl,pccr = <0x8>;
+ fsl,index = <2>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* XFI.10 */
+ xfi-0 {
+ fsl,pccr = <0xb>;
+ fsl,index = <0>;
+ fsl,cfg = <0x2>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+
+ serdes1_3: phy@3 {
+ #phy-cells = <0>;
+ reg = <3>;
+
+ /* SGMII.9 */
+ sgmii-3 {
+ fsl,pccr = <0x8>;
+ fsl,index = <3>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* XFI.9 */
+ xfi-1 {
+ fsl,pccr = <0xb>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+ };
+...
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
2023-04-13 16:05 ` [PATCH v14 01/15] dt-bindings: phy: Add 2500BASE-X and 10GBASE-R Sean Anderson
2023-04-13 16:05 ` [PATCH v14 02/15] dt-bindings: phy: Add Lynx 10G phy binding Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-04-18 20:37 ` Rob Herring
2023-05-11 9:18 ` Bartosz Golaszewski
2023-04-13 16:05 ` [PATCH v14 04/15] dt-bindings: gpio-mmio: Add compatible for QIXIS Sean Anderson
` (12 subsequent siblings)
15 siblings, 2 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Fernández Rojas, Krzysztof Kozlowski,
Madalin Bucur, Sean Anderson, Linus Walleij, Jonas Gorski,
linux-gpio, Rob Herring, Camelia Alexandra Groza, Bagas Sanjaya,
Ioana Ciornei, linuxppc-dev, Bartosz Golaszewski,
linux-arm-kernel
This is a generic binding for simple MMIO GPIO controllers. Although we
have a single driver for these controllers, they were previously spread
over several files. Consolidate them. The register descriptions are
adapted from the comments in the source. There is no set order for the
registers, and some registers may be omitted. Because of this, reg-names
is mandatory, and no order is specified.
Rename brcm,bcm6345-gpio to brcm,bcm63xx-gpio to reflect that bcm6345
has moved.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
Linus or Bartosz, feel free to pick this up as the rest of this series
may not be merged any time soon.
Changes in v14:
- Fix incorrect $id
Changes in v13:
- Fix references to brcm,bcm63xx-gpio.yaml (neé brcm,bcm6345-gpio)
Changes in v12:
- Put compatible first
- Keep gpio-controller to one line
- Add little-endian property
- Alphabetize compatibles
- Remove some comments
- Remove some examples with insufficient novelty
Changes in v11:
- Keep empty (or almost-empty) properties on a single line
- Don't use | unnecessarily
- Use gpio as the node name for examples
- Rename brcm,bcm6345-gpio.yaml to brcm,bcm63xx-gpio.yaml
Changes in v10:
- New
...m6345-gpio.yaml => brcm,bcm63xx-gpio.yaml} | 18 +--
.../devicetree/bindings/gpio/gpio-mmio.yaml | 117 ++++++++++++++++++
.../bindings/gpio/ni,169445-nand-gpio.txt | 38 ------
.../devicetree/bindings/gpio/wd,mbl-gpio.txt | 38 ------
.../mfd/brcm,bcm6318-gpio-sysctl.yaml | 4 +-
.../mfd/brcm,bcm63268-gpio-sysctl.yaml | 4 +-
.../mfd/brcm,bcm6328-gpio-sysctl.yaml | 4 +-
.../mfd/brcm,bcm6358-gpio-sysctl.yaml | 4 +-
.../mfd/brcm,bcm6362-gpio-sysctl.yaml | 4 +-
.../mfd/brcm,bcm6368-gpio-sysctl.yaml | 4 +-
10 files changed, 131 insertions(+), 104 deletions(-)
rename Documentation/devicetree/bindings/gpio/{brcm,bcm6345-gpio.yaml => brcm,bcm63xx-gpio.yaml} (75%)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
delete mode 100644 Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
delete mode 100644 Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml b/Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
similarity index 75%
rename from Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml
rename to Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
index 4d69f79df859..62fcc2bd5d80 100644
--- a/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
@@ -1,10 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
-$id: http://devicetree.org/schemas/gpio/brcm,bcm6345-gpio.yaml#
+$id: http://devicetree.org/schemas/gpio/brcm,bcm63xx-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Broadcom BCM6345 GPIO controller
+title: Broadcom BCM63xx GPIO controller
maintainers:
- Álvaro Fernández Rojas <noltari@gmail.com>
@@ -18,8 +18,6 @@ description: |+
BCM6338 have 8-bit data and dirout registers, where GPIO state can be read
and/or written, and the direction changed from input to output.
- BCM6345 have 16-bit data and dirout registers, where GPIO state can be read
- and/or written, and the direction changed from input to output.
BCM6318, BCM6328, BCM6358, BCM6362, BCM6368 and BCM63268 have 32-bit data
and dirout registers, where GPIO state can be read and/or written, and the
direction changed from input to output.
@@ -29,7 +27,6 @@ properties:
enum:
- brcm,bcm6318-gpio
- brcm,bcm6328-gpio
- - brcm,bcm6345-gpio
- brcm,bcm6358-gpio
- brcm,bcm6362-gpio
- brcm,bcm6368-gpio
@@ -63,17 +60,6 @@ required:
additionalProperties: false
examples:
- - |
- gpio@fffe0406 {
- compatible = "brcm,bcm6345-gpio";
- reg-names = "dirout", "dat";
- reg = <0xfffe0406 2>, <0xfffe040a 2>;
- native-endian;
-
- gpio-controller;
- #gpio-cells = <2>;
- };
-
- |
gpio@0 {
compatible = "brcm,bcm63268-gpio";
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
new file mode 100644
index 000000000000..b394e058256e
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/gpio-mmio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic MMIO GPIO
+
+maintainers:
+ - Linus Walleij <linus.walleij@linaro.org>
+ - Bartosz Golaszewski <brgl@bgdev.pl>
+
+description:
+ Some simple GPIO controllers may consist of a single data register or a pair
+ of set/clear-bit registers. Such controllers are common for glue logic in
+ FPGAs or ASICs. Commonly, these controllers are accessed over memory-mapped
+ NAND-style parallel busses.
+
+properties:
+ compatible:
+ enum:
+ - brcm,bcm6345-gpio
+ - ni,169445-nand-gpio
+ - wd,mbl-gpio # Western Digital MyBook Live memory-mapped GPIO controller
+
+ big-endian: true
+
+ '#gpio-cells':
+ const: 2
+
+ gpio-controller: true
+
+ little-endian: true
+
+ reg:
+ minItems: 1
+ description:
+ A list of registers in the controller. The width of each register is
+ determined by its size. All registers must have the same width. The number
+ of GPIOs is set by the width, with bit 0 corresponding to GPIO 0.
+ items:
+ - description:
+ Register to READ the value of the GPIO lines. If GPIO line is high,
+ the bit will be set. If the GPIO line is low, the bit will be cleared.
+ This register may also be used to drive GPIOs if the SET register is
+ omitted.
+ - description:
+ Register to SET the value of the GPIO lines. Setting a bit in this
+ register will drive the GPIO line high.
+ - description:
+ Register to CLEAR the value of the GPIO lines. Setting a bit in this
+ register will drive the GPIO line low. If this register is omitted,
+ the SET register will be used to clear the GPIO lines as well, by
+ actively writing the line with 0.
+ - description:
+ Register to set the line as OUTPUT. Setting a bit in this register
+ will turn that line into an output line. Conversely, clearing a bit
+ will turn that line into an input.
+ - description:
+ Register to set this line as INPUT. Setting a bit in this register
+ will turn that line into an input line. Conversely, clearing a bit
+ will turn that line into an output.
+
+ reg-names:
+ minItems: 1
+ maxItems: 5
+ items:
+ enum:
+ - dat
+ - set
+ - clr
+ - dirout
+ - dirin
+
+ native-endian: true
+
+ no-output:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ If this property is present, the controller cannot drive the GPIO lines.
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - '#gpio-cells'
+ - gpio-controller
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio@1f300010 {
+ compatible = "ni,169445-nand-gpio";
+ reg = <0x1f300010 0x4>;
+ reg-names = "dat";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio@e0100000 {
+ compatible = "wd,mbl-gpio";
+ reg-names = "dat";
+ reg = <0xe0100000 0x1>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ no-output;
+ };
+
+ gpio@fffe0406 {
+ compatible = "brcm,bcm6345-gpio";
+ reg-names = "dirout", "dat";
+ reg = <0xfffe0406 2>, <0xfffe040a 2>;
+ native-endian;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt b/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
deleted file mode 100644
index ca2f8c745a27..000000000000
--- a/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Bindings for the National Instruments 169445 GPIO NAND controller
-
-The 169445 GPIO NAND controller has two memory mapped GPIO registers, one
-for input (the ready signal) and one for output (control signals). It is
-intended to be used with the GPIO NAND driver.
-
-Required properties:
- - compatible: should be "ni,169445-nand-gpio"
- - reg-names: must contain
- "dat" - data register
- - reg: address + size pairs describing the GPIO register sets;
- order must correspond with the order of entries in reg-names
- - #gpio-cells: must be set to 2. The first cell is the pin number and
- the second cell is used to specify the gpio polarity:
- 0 = active high
- 1 = active low
- - gpio-controller: Marks the device node as a gpio controller.
-
-Optional properties:
- - no-output: disables driving output on the pins
-
-Examples:
- gpio1: nand-gpio-out@1f300010 {
- compatible = "ni,169445-nand-gpio";
- reg = <0x1f300010 0x4>;
- reg-names = "dat";
- gpio-controller;
- #gpio-cells = <2>;
- };
-
- gpio2: nand-gpio-in@1f300014 {
- compatible = "ni,169445-nand-gpio";
- reg = <0x1f300014 0x4>;
- reg-names = "dat";
- gpio-controller;
- #gpio-cells = <2>;
- no-output;
- };
diff --git a/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt b/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
deleted file mode 100644
index 038c3a6a1f4d..000000000000
--- a/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Bindings for the Western Digital's MyBook Live memory-mapped GPIO controllers.
-
-The Western Digital MyBook Live has two memory-mapped GPIO controllers.
-Both GPIO controller only have a single 8-bit data register, where GPIO
-state can be read and/or written.
-
-Required properties:
- - compatible: should be "wd,mbl-gpio"
- - reg-names: must contain
- "dat" - data register
- - reg: address + size pairs describing the GPIO register sets;
- order must correspond with the order of entries in reg-names
- - #gpio-cells: must be set to 2. The first cell is the pin number and
- the second cell is used to specify the gpio polarity:
- 0 = active high
- 1 = active low
- - gpio-controller: Marks the device node as a gpio controller.
-
-Optional properties:
- - no-output: GPIOs are read-only.
-
-Examples:
- gpio0: gpio0@e0000000 {
- compatible = "wd,mbl-gpio";
- reg-names = "dat";
- reg = <0xe0000000 0x1>;
- #gpio-cells = <2>;
- gpio-controller;
- };
-
- gpio1: gpio1@e0100000 {
- compatible = "wd,mbl-gpio";
- reg-names = "dat";
- reg = <0xe0100000 0x1>;
- #gpio-cells = <2>;
- gpio-controller;
- no-output;
- };
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
index 148f1da47603..9f9a14af875e 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml
index 7e582243ea76..803277dd2725 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml
index 2230848e11c3..b9a6856ce970 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml
index c06693b6f7aa..4651fe4dde07 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml
index c560bede0e37..0330b621fd38 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml
index c534f5f2404e..82d3e4415bda 100644
--- a/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml
+++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml
@@ -35,11 +35,11 @@ patternProperties:
"^gpio@[0-9a-f]+$":
# Child node
type: object
- $ref: "../gpio/brcm,bcm6345-gpio.yaml"
+ $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
description:
GPIO controller for the SoC GPIOs. This child node definition
should follow the bindings specified in
- Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml.
+ Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml.
"^pinctrl@[0-9a-f]+$":
# Child node
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* Re: [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml
2023-04-13 16:05 ` [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml Sean Anderson
@ 2023-04-18 20:37 ` Rob Herring
2023-05-11 9:18 ` Bartosz Golaszewski
1 sibling, 0 replies; 64+ messages in thread
From: Rob Herring @ 2023-04-18 20:37 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Fernández Rojas,
Krzysztof Kozlowski, Madalin Bucur, Linus Walleij, Jonas Gorski,
linux-gpio, Vinod Koul, Camelia Alexandra Groza, Bagas Sanjaya,
Ioana Ciornei, linux-phy, linuxppc-dev, Bartosz Golaszewski,
linux-arm-kernel
On Thu, Apr 13, 2023 at 12:05:55PM -0400, Sean Anderson wrote:
> This is a generic binding for simple MMIO GPIO controllers. Although we
> have a single driver for these controllers, they were previously spread
> over several files. Consolidate them. The register descriptions are
> adapted from the comments in the source. There is no set order for the
> registers, and some registers may be omitted. Because of this, reg-names
> is mandatory, and no order is specified.
>
> Rename brcm,bcm6345-gpio to brcm,bcm63xx-gpio to reflect that bcm6345
> has moved.
>
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> Linus or Bartosz, feel free to pick this up as the rest of this series
> may not be merged any time soon.
>
> Changes in v14:
> - Fix incorrect $id
>
> Changes in v13:
> - Fix references to brcm,bcm63xx-gpio.yaml (neé brcm,bcm6345-gpio)
>
> Changes in v12:
> - Put compatible first
> - Keep gpio-controller to one line
> - Add little-endian property
> - Alphabetize compatibles
> - Remove some comments
> - Remove some examples with insufficient novelty
>
> Changes in v11:
> - Keep empty (or almost-empty) properties on a single line
> - Don't use | unnecessarily
> - Use gpio as the node name for examples
> - Rename brcm,bcm6345-gpio.yaml to brcm,bcm63xx-gpio.yaml
>
> Changes in v10:
> - New
>
> ...m6345-gpio.yaml => brcm,bcm63xx-gpio.yaml} | 18 +--
> .../devicetree/bindings/gpio/gpio-mmio.yaml | 117 ++++++++++++++++++
> .../bindings/gpio/ni,169445-nand-gpio.txt | 38 ------
> .../devicetree/bindings/gpio/wd,mbl-gpio.txt | 38 ------
> .../mfd/brcm,bcm6318-gpio-sysctl.yaml | 4 +-
> .../mfd/brcm,bcm63268-gpio-sysctl.yaml | 4 +-
> .../mfd/brcm,bcm6328-gpio-sysctl.yaml | 4 +-
> .../mfd/brcm,bcm6358-gpio-sysctl.yaml | 4 +-
> .../mfd/brcm,bcm6362-gpio-sysctl.yaml | 4 +-
> .../mfd/brcm,bcm6368-gpio-sysctl.yaml | 4 +-
> 10 files changed, 131 insertions(+), 104 deletions(-)
> rename Documentation/devicetree/bindings/gpio/{brcm,bcm6345-gpio.yaml => brcm,bcm63xx-gpio.yaml} (75%)
> create mode 100644 Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
> delete mode 100644 Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
> delete mode 100644 Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml b/Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
> similarity index 75%
> rename from Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml
> rename to Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
> index 4d69f79df859..62fcc2bd5d80 100644
> --- a/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.yaml
> +++ b/Documentation/devicetree/bindings/gpio/brcm,bcm63xx-gpio.yaml
> @@ -1,10 +1,10 @@
> # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> %YAML 1.2
> ---
> -$id: http://devicetree.org/schemas/gpio/brcm,bcm6345-gpio.yaml#
> +$id: http://devicetree.org/schemas/gpio/brcm,bcm63xx-gpio.yaml#
> $schema: http://devicetree.org/meta-schemas/core.yaml#
>
> -title: Broadcom BCM6345 GPIO controller
> +title: Broadcom BCM63xx GPIO controller
>
> maintainers:
> - Álvaro Fernández Rojas <noltari@gmail.com>
> @@ -18,8 +18,6 @@ description: |+
>
> BCM6338 have 8-bit data and dirout registers, where GPIO state can be read
> and/or written, and the direction changed from input to output.
> - BCM6345 have 16-bit data and dirout registers, where GPIO state can be read
> - and/or written, and the direction changed from input to output.
> BCM6318, BCM6328, BCM6358, BCM6362, BCM6368 and BCM63268 have 32-bit data
> and dirout registers, where GPIO state can be read and/or written, and the
> direction changed from input to output.
> @@ -29,7 +27,6 @@ properties:
> enum:
> - brcm,bcm6318-gpio
> - brcm,bcm6328-gpio
> - - brcm,bcm6345-gpio
> - brcm,bcm6358-gpio
> - brcm,bcm6362-gpio
> - brcm,bcm6368-gpio
> @@ -63,17 +60,6 @@ required:
> additionalProperties: false
>
> examples:
> - - |
> - gpio@fffe0406 {
> - compatible = "brcm,bcm6345-gpio";
> - reg-names = "dirout", "dat";
> - reg = <0xfffe0406 2>, <0xfffe040a 2>;
> - native-endian;
> -
> - gpio-controller;
> - #gpio-cells = <2>;
> - };
> -
> - |
> gpio@0 {
> compatible = "brcm,bcm63268-gpio";
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
> new file mode 100644
> index 000000000000..b394e058256e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
> @@ -0,0 +1,117 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/gpio/gpio-mmio.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic MMIO GPIO
> +
> +maintainers:
> + - Linus Walleij <linus.walleij@linaro.org>
> + - Bartosz Golaszewski <brgl@bgdev.pl>
> +
> +description:
> + Some simple GPIO controllers may consist of a single data register or a pair
> + of set/clear-bit registers. Such controllers are common for glue logic in
> + FPGAs or ASICs. Commonly, these controllers are accessed over memory-mapped
> + NAND-style parallel busses.
> +
> +properties:
> + compatible:
> + enum:
> + - brcm,bcm6345-gpio
> + - ni,169445-nand-gpio
> + - wd,mbl-gpio # Western Digital MyBook Live memory-mapped GPIO controller
> +
> + big-endian: true
> +
> + '#gpio-cells':
> + const: 2
> +
> + gpio-controller: true
> +
> + little-endian: true
> +
> + reg:
> + minItems: 1
> + description:
> + A list of registers in the controller. The width of each register is
> + determined by its size. All registers must have the same width. The number
> + of GPIOs is set by the width, with bit 0 corresponding to GPIO 0.
> + items:
> + - description:
> + Register to READ the value of the GPIO lines. If GPIO line is high,
> + the bit will be set. If the GPIO line is low, the bit will be cleared.
> + This register may also be used to drive GPIOs if the SET register is
> + omitted.
> + - description:
> + Register to SET the value of the GPIO lines. Setting a bit in this
> + register will drive the GPIO line high.
> + - description:
> + Register to CLEAR the value of the GPIO lines. Setting a bit in this
> + register will drive the GPIO line low. If this register is omitted,
> + the SET register will be used to clear the GPIO lines as well, by
> + actively writing the line with 0.
> + - description:
> + Register to set the line as OUTPUT. Setting a bit in this register
> + will turn that line into an output line. Conversely, clearing a bit
> + will turn that line into an input.
> + - description:
> + Register to set this line as INPUT. Setting a bit in this register
> + will turn that line into an input line. Conversely, clearing a bit
> + will turn that line into an output.
> +
> + reg-names:
> + minItems: 1
> + maxItems: 5
> + items:
> + enum:
> + - dat
> + - set
> + - clr
> + - dirout
> + - dirin
> +
> + native-endian: true
> +
> + no-output:
> + $ref: /schemas/types.yaml#/definitions/flag
> + description:
> + If this property is present, the controller cannot drive the GPIO lines.
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - '#gpio-cells'
> + - gpio-controller
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + gpio@1f300010 {
> + compatible = "ni,169445-nand-gpio";
> + reg = <0x1f300010 0x4>;
> + reg-names = "dat";
> + gpio-controller;
> + #gpio-cells = <2>;
> + };
> +
> + gpio@e0100000 {
> + compatible = "wd,mbl-gpio";
> + reg-names = "dat";
> + reg = <0xe0100000 0x1>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + no-output;
> + };
> +
> + gpio@fffe0406 {
> + compatible = "brcm,bcm6345-gpio";
> + reg-names = "dirout", "dat";
> + reg = <0xfffe0406 2>, <0xfffe040a 2>;
> + native-endian;
> + gpio-controller;
> + #gpio-cells = <2>;
> + };
> diff --git a/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt b/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
> deleted file mode 100644
> index ca2f8c745a27..000000000000
> --- a/Documentation/devicetree/bindings/gpio/ni,169445-nand-gpio.txt
> +++ /dev/null
> @@ -1,38 +0,0 @@
> -Bindings for the National Instruments 169445 GPIO NAND controller
> -
> -The 169445 GPIO NAND controller has two memory mapped GPIO registers, one
> -for input (the ready signal) and one for output (control signals). It is
> -intended to be used with the GPIO NAND driver.
> -
> -Required properties:
> - - compatible: should be "ni,169445-nand-gpio"
> - - reg-names: must contain
> - "dat" - data register
> - - reg: address + size pairs describing the GPIO register sets;
> - order must correspond with the order of entries in reg-names
> - - #gpio-cells: must be set to 2. The first cell is the pin number and
> - the second cell is used to specify the gpio polarity:
> - 0 = active high
> - 1 = active low
> - - gpio-controller: Marks the device node as a gpio controller.
> -
> -Optional properties:
> - - no-output: disables driving output on the pins
> -
> -Examples:
> - gpio1: nand-gpio-out@1f300010 {
> - compatible = "ni,169445-nand-gpio";
> - reg = <0x1f300010 0x4>;
> - reg-names = "dat";
> - gpio-controller;
> - #gpio-cells = <2>;
> - };
> -
> - gpio2: nand-gpio-in@1f300014 {
> - compatible = "ni,169445-nand-gpio";
> - reg = <0x1f300014 0x4>;
> - reg-names = "dat";
> - gpio-controller;
> - #gpio-cells = <2>;
> - no-output;
> - };
> diff --git a/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt b/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
> deleted file mode 100644
> index 038c3a6a1f4d..000000000000
> --- a/Documentation/devicetree/bindings/gpio/wd,mbl-gpio.txt
> +++ /dev/null
> @@ -1,38 +0,0 @@
> -Bindings for the Western Digital's MyBook Live memory-mapped GPIO controllers.
> -
> -The Western Digital MyBook Live has two memory-mapped GPIO controllers.
> -Both GPIO controller only have a single 8-bit data register, where GPIO
> -state can be read and/or written.
> -
> -Required properties:
> - - compatible: should be "wd,mbl-gpio"
> - - reg-names: must contain
> - "dat" - data register
> - - reg: address + size pairs describing the GPIO register sets;
> - order must correspond with the order of entries in reg-names
> - - #gpio-cells: must be set to 2. The first cell is the pin number and
> - the second cell is used to specify the gpio polarity:
> - 0 = active high
> - 1 = active low
> - - gpio-controller: Marks the device node as a gpio controller.
> -
> -Optional properties:
> - - no-output: GPIOs are read-only.
> -
> -Examples:
> - gpio0: gpio0@e0000000 {
> - compatible = "wd,mbl-gpio";
> - reg-names = "dat";
> - reg = <0xe0000000 0x1>;
> - #gpio-cells = <2>;
> - gpio-controller;
> - };
> -
> - gpio1: gpio1@e0100000 {
> - compatible = "wd,mbl-gpio";
> - reg-names = "dat";
> - reg = <0xe0100000 0x1>;
> - #gpio-cells = <2>;
> - gpio-controller;
> - no-output;
> - };
> diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
> index 148f1da47603..9f9a14af875e 100644
> --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
> +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml
> @@ -35,11 +35,11 @@ patternProperties:
> "^gpio@[0-9a-f]+$":
> # Child node
> type: object
> - $ref: "../gpio/brcm,bcm6345-gpio.yaml"
> + $ref: "../gpio/brcm,bcm63xx-gpio.yaml"
If you respin, please drop the quotes here and the other spots.
Reviewed-by: Rob Herring <robh@kernel.org>
Rob
^ permalink raw reply [flat|nested] 64+ messages in thread* Re: [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml
2023-04-13 16:05 ` [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml Sean Anderson
2023-04-18 20:37 ` Rob Herring
@ 2023-05-11 9:18 ` Bartosz Golaszewski
1 sibling, 0 replies; 64+ messages in thread
From: Bartosz Golaszewski @ 2023-05-11 9:18 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Fernández Rojas,
Krzysztof Kozlowski, Madalin Bucur, Linus Walleij, Jonas Gorski,
linux-gpio, Vinod Koul, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linux-phy, linuxppc-dev,
linux-arm-kernel
On Thu, Apr 13, 2023 at 6:06 PM Sean Anderson <sean.anderson@seco.com> wrote:
>
> This is a generic binding for simple MMIO GPIO controllers. Although we
> have a single driver for these controllers, they were previously spread
> over several files. Consolidate them. The register descriptions are
> adapted from the comments in the source. There is no set order for the
> registers, and some registers may be omitted. Because of this, reg-names
> is mandatory, and no order is specified.
>
> Rename brcm,bcm6345-gpio to brcm,bcm63xx-gpio to reflect that bcm6345
> has moved.
>
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> Linus or Bartosz, feel free to pick this up as the rest of this series
> may not be merged any time soon.
>
Applied, thanks!
Bart
^ permalink raw reply [flat|nested] 64+ messages in thread
* [PATCH v14 04/15] dt-bindings: gpio-mmio: Add compatible for QIXIS
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (2 preceding siblings ...)
2023-04-13 16:05 ` [PATCH v14 03/15] dt-bindings: Convert gpio-mmio to yaml Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-04-13 16:05 ` [PATCH v14 05/15] dt-bindings: clock: Add ids for Lynx 10g PLLs Sean Anderson
` (11 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Rob Herring, Camelia Alexandra Groza, Bagas Sanjaya,
Ioana Ciornei, linuxppc-dev, Rob Herring, linux-arm-kernel
NXP has a "QIXIS" FPGA on several of their reference design boards. On
the LS1088ARDB there are several registers which control GPIOs. These
can be modeled with the MMIO GPIO driver.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
(no changes since v10)
Changes in v10:
- New
.../devicetree/bindings/gpio/gpio-mmio.yaml | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
index b394e058256e..5abf3dabcf39 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-mmio.yaml
@@ -18,10 +18,16 @@ description:
properties:
compatible:
- enum:
- - brcm,bcm6345-gpio
- - ni,169445-nand-gpio
- - wd,mbl-gpio # Western Digital MyBook Live memory-mapped GPIO controller
+ oneOf:
+ - enum:
+ - brcm,bcm6345-gpio
+ - ni,169445-nand-gpio
+ - wd,mbl-gpio # Western Digital MyBook Live memory-mapped GPIO controller
+ - items:
+ - enum:
+ - fsl,fpga-qixis-los-stat
+ - fsl,fpga-qixis-brdcfg9
+ - const: ni,169445-nand-gpio
big-endian: true
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 05/15] dt-bindings: clock: Add ids for Lynx 10g PLLs
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (3 preceding siblings ...)
2023-04-13 16:05 ` [PATCH v14 04/15] dt-bindings: gpio-mmio: Add compatible for QIXIS Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-04-13 16:05 ` [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver Sean Anderson
` (10 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Michael Turquette, linux-clk, Stephen Boyd, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei,
linuxppc-dev, Rob Herring, linux-arm-kernel
This adds ids for the Lynx 10g SerDes's internal PLLs. These may be used
with assigned-clock* to specify a particular frequency to use. For
example, to set the second PLL (at offset 0x20)'s frequency, use
LYNX10G_PLLa(1). These are for use only in the device tree, and are not
otherwise used by the driver.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
Acked-by: Rob Herring <robh@kernel.org>
---
(no changes since v6)
Changes in v6:
- frequence -> frequency
Changes in v5:
- Update commit description
- Dual id header
Changes in v4:
- New
include/dt-bindings/clock/fsl,lynx-10g.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 include/dt-bindings/clock/fsl,lynx-10g.h
diff --git a/include/dt-bindings/clock/fsl,lynx-10g.h b/include/dt-bindings/clock/fsl,lynx-10g.h
new file mode 100644
index 000000000000..15362ae85304
--- /dev/null
+++ b/include/dt-bindings/clock/fsl,lynx-10g.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#ifndef __DT_BINDINGS_CLK_LYNX_10G_H
+#define __DT_BINDINGS_CLK_LYNX_10G_H
+
+#define LYNX10G_CLKS_PER_PLL 2
+
+#define LYNX10G_PLLa(a) ((a) * LYNX10G_CLKS_PER_PLL)
+#define LYNX10G_PLLa_EX_DLY(a) ((a) * LYNX10G_CLKS_PER_PLL + 1)
+
+#endif /* __DT_BINDINGS_CLK_LYNX_10G_H */
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (4 preceding siblings ...)
2023-04-13 16:05 ` [PATCH v14 05/15] dt-bindings: clock: Add ids for Lynx 10g PLLs Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-05-08 9:15 ` Vinod Koul
2023-04-13 16:05 ` [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
` (9 subsequent siblings)
15 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Michael Turquette, Stephen Boyd, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei,
linuxppc-dev, linux-clk, linux-arm-kernel
This adds support for the PLLs found in Lynx 10G "SerDes" devices found on
various NXP QorIQ SoCs. There are two PLLs in each SerDes. This driver has
been split from the main PHY driver to allow for better review, even though
these PLLs are not present anywhere else besides the SerDes. An auxiliary
device is not used as it offers no benefits over a function call (and there
is no need to have a separate device).
The PLLs are modeled as clocks proper to let us take advantage of the
existing clock infrastructure. I have not given the same treatment to the
per-lane clocks 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.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v10)
Changes in v10:
- Remove unnecessary inclusion of clk.h
- Don't gate clocks in compatibility mode
Changes in v9:
- Convert some u32s to unsigned long to match arguments
- Switch from round_rate to determine_rate
- Drop explicit reference to reference clock
- Use .parent_names when requesting parents
- Use devm_clk_hw_get_clk to pass clocks back to serdes
- Fix indentation
- Split off from following patch to allow for better review
MAINTAINERS | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-fsl-lynx-10g.c | 510 +++++++++++++++++++++++++++++++++
drivers/phy/freescale/Kconfig | 6 +
include/linux/phy/lynx-10g.h | 16 ++
5 files changed, 540 insertions(+)
create mode 100644 drivers/clk/clk-fsl-lynx-10g.c
create mode 100644 include/linux/phy/lynx-10g.h
diff --git a/MAINTAINERS b/MAINTAINERS
index fce67b74e4a2..8da893681de6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12195,6 +12195,13 @@ S: Maintained
W: http://linux-test-project.github.io/
T: git https://github.com/linux-test-project/ltp.git
+LYNX 10G SERDES DRIVER
+M: Sean Anderson <sean.anderson@seco.com>
+S: Maintained
+F: drivers/clk/clk-fsl-lynx-10g.c
+F: include/dt-bindings/clock/fsl,lynx-10g.h
+F: include/linux/phy/lynx-10g.h
+
LYNX 28G SERDES PHY DRIVER
M: Ioana Ciornei <ioana.ciornei@nxp.com>
L: netdev@vger.kernel.org
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e3ca0d058a25..eebed69f6c58 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o
obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
obj-$(CONFIG_COMMON_CLK_FSL_FLEXSPI) += clk-fsl-flexspi.o
+obj-$(CONFIG_PHY_FSL_LYNX_10G) += clk-fsl-lynx-10g.o
obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
diff --git a/drivers/clk/clk-fsl-lynx-10g.c b/drivers/clk/clk-fsl-lynx-10g.c
new file mode 100644
index 000000000000..78357303b578
--- /dev/null
+++ b/drivers/clk/clk-fsl-lynx-10g.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ *
+ * This file contains the implementation for the PLLs found on Lynx 10G phys.
+ *
+ * XXX: The VCO rate of the PLLs can exceed ~4GHz, which is the maximum rate
+ * expressable in an unsigned long. To work around this, rates are specified in
+ * kHz. This is as if there was a division by 1000 in the PLL.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/bitfield.h>
+#include <linux/math64.h>
+#include <linux/phy/lynx-10g.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <dt-bindings/clock/fsl,lynx-10g.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 PLLaRSTCTL_ENABLE_SET (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_PLLRST_B | \
+ PLLaRSTCTL_SDRST_B | PLLaRSTCTL_SDEN)
+#define PLLaRSTCTL_ENABLE_MASK (PLLaRSTCTL_ENABLE_SET | PLLaRSTCTL_RST_ERR)
+
+#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 PLLaCR0_DLYDIV_SEL_16 0b01
+
+/**
+ * struct lynx_clk - Driver data for the PLLs
+ * @pll: The PLL clock
+ * @ex_dly: The "PLLa_ex_dly_clk" clock
+ * @dev: The serdes device
+ * @regmap: Our registers
+ * @idx: Which PLL this clock is for
+ */
+struct lynx_clk {
+ struct clk_hw pll, ex_dly;
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int idx;
+};
+
+static u32 lynx_read(struct lynx_clk *clk, u32 reg)
+{
+ unsigned int ret = 0;
+
+ WARN_ON_ONCE(regmap_read(clk->regmap, reg, &ret));
+ return ret;
+}
+
+static void lynx_write(struct lynx_clk *clk, u32 val, u32 reg)
+{
+ WARN_ON_ONCE(regmap_write(clk->regmap, reg, val));
+}
+
+static struct lynx_clk *lynx_pll_to_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct lynx_clk, pll);
+}
+
+static struct lynx_clk *lynx_ex_dly_to_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct lynx_clk, ex_dly);
+}
+
+static void lynx_pll_stop(struct lynx_clk *clk)
+{
+ u32 rstctl;
+
+ rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
+ rstctl &= ~PLLaRSTCTL_SDRST_B;
+ lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
+
+ ndelay(50);
+
+ rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
+ rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
+ lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
+
+ ndelay(100);
+}
+
+static void lynx_pll_disable(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_pll_to_clk(hw);
+ u32 cr0;
+
+ dev_dbg(clk->dev, "disable pll%d\n", clk->idx);
+
+ lynx_pll_stop(clk);
+
+ cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+ cr0 |= PLLaCR0_POFF;
+ lynx_write(clk, cr0, PLLaCR0(clk->idx));
+}
+
+static int lynx_pll_reset(struct lynx_clk *clk)
+{
+ int ret;
+ u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
+
+ rstctl |= PLLaRSTCTL_RSTREQ;
+ lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
+ ret = read_poll_timeout(lynx_read, rstctl,
+ rstctl & (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_RST_ERR),
+ 100, 5000, true, clk, PLLaRSTCTL(clk->idx));
+ if (rstctl & PLLaRSTCTL_RST_ERR)
+ ret = -EIO;
+ if (ret) {
+ dev_err(clk->dev, "pll%d reset failed\n", clk->idx);
+ return ret;
+ }
+
+ rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
+ lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
+ return 0;
+}
+
+static int lynx_pll_prepare(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_pll_to_clk(hw);
+ u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
+ u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+
+ /*
+ * "Enabling" the PLL involves resetting it (and all attached lanes).
+ * Avoid doing this if we are already enabled.
+ */
+ if (!(cr0 & PLLaCR0_POFF) &&
+ (rstctl & PLLaRSTCTL_ENABLE_MASK) == PLLaRSTCTL_ENABLE_SET) {
+ dev_dbg(clk->dev, "pll%d already prepared\n", clk->idx);
+ return 0;
+ }
+
+ dev_dbg(clk->dev, "prepare pll%d\n", clk->idx);
+
+ cr0 &= ~PLLaCR0_POFF;
+ lynx_write(clk, cr0, PLLaCR0(clk->idx));
+
+ return lynx_pll_reset(clk);
+}
+
+static int lynx_pll_is_enabled(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_pll_to_clk(hw);
+ u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+ bool enabled = !(cr0 & PLLaCR0_POFF);
+
+ dev_dbg(clk->dev, "pll%d %s enabled\n", clk->idx,
+ enabled ? "is" : "is not");
+
+ return enabled;
+}
+
+static const unsigned long 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(unsigned long rate)
+{
+ int ret;
+
+ for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
+ unsigned long rfclk_rate = rfclk_sel_map[ret];
+ /* Allow an error of 100ppm */
+ unsigned long error = rfclk_rate / 10000;
+
+ if (abs(rate - rfclk_rate) < error)
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const unsigned long 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(unsigned long 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_pll_to_clk(hw);
+ u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+ u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
+ u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
+ u32 ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+ unsigned long ret;
+
+ /* Ensure that the parent matches our rfclk selector */
+ if (rfclk_sel == lynx_rfclk_to_sel(parent_rate))
+ ret = mult_frac(parent_rate, ratio, HZ_PER_KHZ);
+ else
+ ret = 0;
+
+ dev_dbg(clk->dev, "recalc pll%d new=%llu parent=%lu\n", clk->idx,
+ (u64)ret * HZ_PER_KHZ, parent_rate);
+ return ret;
+}
+
+static int lynx_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ int frate_sel, rfclk_sel;
+ struct lynx_clk *clk = lynx_pll_to_clk(hw);
+ u32 ratio;
+
+ dev_dbg(clk->dev, "round pll%d new=%llu parent=%lu\n", clk->idx,
+ (u64)req->rate * HZ_PER_KHZ, req->best_parent_rate);
+
+ frate_sel = lynx_frate_to_sel(req->rate);
+ if (frate_sel < 0)
+ return frate_sel;
+
+ /* Try the current parent rate */
+ rfclk_sel = lynx_rfclk_to_sel(req->best_parent_rate);
+ if (rfclk_sel >= 0) {
+ ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+ if (ratio) {
+ req->rate = mult_frac(req->best_parent_rate, ratio,
+ HZ_PER_KHZ);
+ return 0;
+ }
+ }
+
+ /* Try all possible parent rates */
+ for (rfclk_sel = 0;
+ rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
+ rfclk_sel++) {
+ unsigned long new_parent_rate;
+
+ ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+ if (!ratio)
+ continue;
+
+ /* Ensure the reference clock can produce this rate */
+ new_parent_rate = rfclk_sel_map[rfclk_sel];
+ new_parent_rate = clk_hw_round_rate(req->best_parent_hw,
+ new_parent_rate);
+ if (rfclk_sel != lynx_rfclk_to_sel(new_parent_rate))
+ continue;
+
+ req->rate = mult_frac(new_parent_rate, ratio, HZ_PER_KHZ);
+ req->best_parent_rate = new_parent_rate;
+ return 0;
+ }
+
+ 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;
+ struct lynx_clk *clk = lynx_pll_to_clk(hw);
+ u32 ratio, cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+
+ dev_dbg(clk->dev, "set rate pll%d new=%llu parent=%lu\n", clk->idx,
+ (u64)rate_khz * HZ_PER_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)
+ return rfclk_sel;
+
+ ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
+ if (!ratio)
+ return -EINVAL;
+
+ lynx_pll_stop(clk);
+ 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(clk, cr0, PLLaCR0(clk->idx));
+ /* Don't bother resetting if it's off */
+ if (cr0 & PLLaCR0_POFF)
+ return 0;
+ return lynx_pll_reset(clk);
+}
+
+static const struct clk_ops lynx_pll_clk_ops = {
+ .prepare = lynx_pll_prepare,
+ .disable = lynx_pll_disable,
+ .is_enabled = lynx_pll_is_enabled,
+ .recalc_rate = lynx_pll_recalc_rate,
+ .determine_rate = lynx_pll_determine_rate,
+ .set_rate = lynx_pll_set_rate,
+};
+
+static void lynx_ex_dly_disable(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
+ u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+
+ cr0 &= ~PLLaCR0_DLYDIV_SEL;
+ lynx_write(clk, PLLaCR0(clk->idx), cr0);
+}
+
+static int lynx_ex_dly_enable(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
+ u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
+
+ cr0 &= ~PLLaCR0_DLYDIV_SEL;
+ cr0 |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, PLLaCR0_DLYDIV_SEL_16);
+ lynx_write(clk, PLLaCR0(clk->idx), cr0);
+ return 0;
+}
+
+static int lynx_ex_dly_is_enabled(struct clk_hw *hw)
+{
+ struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
+
+ return lynx_read(clk, PLLaCR0(clk->idx)) & PLLaCR0_DLYDIV_SEL;
+}
+
+static unsigned long lynx_ex_dly_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate / 16;
+}
+
+static const struct clk_ops lynx_ex_dly_clk_ops = {
+ .enable = lynx_ex_dly_enable,
+ .disable = lynx_ex_dly_disable,
+ .is_enabled = lynx_ex_dly_is_enabled,
+ .recalc_rate = lynx_ex_dly_recalc_rate,
+};
+
+static int lynx_clk_init(struct clk_hw_onecell_data *hw_data,
+ struct device *dev, struct regmap *regmap,
+ unsigned int index, bool compat)
+{
+ const struct clk_hw *ex_dly_parents;
+ struct clk_parent_data pll_parents[1] = { };
+ struct clk_init_data pll_init = {
+ .ops = &lynx_pll_clk_ops,
+ .parent_data = pll_parents,
+ .num_parents = 1,
+ .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT |
+ CLK_OPS_PARENT_ENABLE,
+ };
+ struct clk_init_data ex_dly_init = {
+ .ops = &lynx_ex_dly_clk_ops,
+ .parent_hws = &ex_dly_parents,
+ .num_parents = 1,
+ };
+ struct lynx_clk *clk;
+ int ret;
+
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+
+ clk->dev = dev;
+ clk->regmap = regmap;
+ clk->idx = index;
+
+ pll_parents[0].fw_name = kasprintf(GFP_KERNEL, "ref%d", index);
+ pll_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_khz", dev_name(dev),
+ index);
+ ex_dly_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_ex_dly_khz",
+ dev_name(dev), index);
+ if (!pll_parents[0].fw_name || !pll_init.name || !ex_dly_init.name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (compat)
+ pll_init.flags |= CLK_IGNORE_UNUSED;
+ clk->pll.init = &pll_init;
+ ret = devm_clk_hw_register(dev, &clk->pll);
+ if (ret) {
+ dev_err_probe(dev, ret, "could not register %s\n",
+ pll_init.name);
+ goto out;
+ }
+
+ ex_dly_parents = &clk->pll;
+ clk->ex_dly.init = &ex_dly_init;
+ ret = devm_clk_hw_register(dev, &clk->ex_dly);
+ if (ret)
+ dev_err_probe(dev, ret, "could not register %s\n",
+ ex_dly_init.name);
+
+ hw_data->hws[LYNX10G_PLLa(index)] = &clk->pll;
+ hw_data->hws[LYNX10G_PLLa_EX_DLY(index)] = &clk->ex_dly;
+
+out:
+ kfree(pll_parents[0].fw_name);
+ kfree(pll_init.name);
+ kfree(ex_dly_init.name);
+ return ret;
+}
+
+#define NUM_PLLS 2
+#define NUM_CLKS (NUM_PLLS * LYNX10G_CLKS_PER_PLL)
+
+int lynx_clks_init(struct device *dev, struct regmap *regmap,
+ struct clk *plls[2], struct clk *ex_dlys[2], bool compat)
+{
+ int ret, i;
+ struct clk_hw_onecell_data *hw_data;
+
+ hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, NUM_CLKS),
+ GFP_KERNEL);
+ if (!hw_data)
+ return -ENOMEM;
+ hw_data->num = NUM_CLKS;
+
+ for (i = 0; i < NUM_PLLS; i++) {
+ ret = lynx_clk_init(hw_data, dev, regmap, i, compat);
+ if (ret)
+ return ret;
+
+ plls[i] = devm_clk_hw_get_clk(dev,
+ hw_data->hws[LYNX10G_PLLa(i)],
+ NULL);
+ if (IS_ERR(plls[i]))
+ return PTR_ERR(plls[i]);
+
+ ex_dlys[i] = devm_clk_hw_get_clk(dev,
+ hw_data->hws[LYNX10G_PLLa_EX_DLY(i)],
+ NULL);
+ if (IS_ERR(ex_dlys[i]))
+ return PTR_ERR(plls[i]);
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
+ if (ret)
+ dev_err_probe(dev, ret, "could not register clock provider\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lynx_clks_init);
+
+MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
+MODULE_DESCRIPTION("Lynx 10G PLL driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 853958fb2c06..5d461232276f 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -47,3 +47,9 @@ 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
+ depends on COMMON_CLK
+ depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
+ select REGMAP_MMIO
diff --git a/include/linux/phy/lynx-10g.h b/include/linux/phy/lynx-10g.h
new file mode 100644
index 000000000000..b7b80b3ee988
--- /dev/null
+++ b/include/linux/phy/lynx-10g.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#ifndef LYNX_10G
+#define LYNX_10G
+
+struct clk;
+struct device;
+struct regmap;
+
+int lynx_clks_init(struct device *dev, struct regmap *regmap,
+ struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
+
+#endif /* LYNX 10G */
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-04-13 16:05 ` [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver Sean Anderson
@ 2023-05-08 9:15 ` Vinod Koul
2023-05-08 15:31 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vinod Koul @ 2023-05-08 9:15 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 13-04-23, 12:05, Sean Anderson wrote:
> This adds support for the PLLs found in Lynx 10G "SerDes" devices found on
> various NXP QorIQ SoCs. There are two PLLs in each SerDes. This driver has
> been split from the main PHY driver to allow for better review, even though
> these PLLs are not present anywhere else besides the SerDes. An auxiliary
> device is not used as it offers no benefits over a function call (and there
> is no need to have a separate device).
>
> The PLLs are modeled as clocks proper to let us take advantage of the
> existing clock infrastructure. I have not given the same treatment to the
> per-lane clocks 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.
>
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>
> ---
>
> (no changes since v10)
>
> Changes in v10:
> - Remove unnecessary inclusion of clk.h
> - Don't gate clocks in compatibility mode
>
> Changes in v9:
> - Convert some u32s to unsigned long to match arguments
> - Switch from round_rate to determine_rate
> - Drop explicit reference to reference clock
> - Use .parent_names when requesting parents
> - Use devm_clk_hw_get_clk to pass clocks back to serdes
> - Fix indentation
> - Split off from following patch to allow for better review
>
> MAINTAINERS | 7 +
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-fsl-lynx-10g.c | 510 +++++++++++++++++++++++++++++++++
> drivers/phy/freescale/Kconfig | 6 +
> include/linux/phy/lynx-10g.h | 16 ++
> 5 files changed, 540 insertions(+)
> create mode 100644 drivers/clk/clk-fsl-lynx-10g.c
> create mode 100644 include/linux/phy/lynx-10g.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index fce67b74e4a2..8da893681de6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12195,6 +12195,13 @@ S: Maintained
> W: http://linux-test-project.github.io/
> T: git https://github.com/linux-test-project/ltp.git
>
> +LYNX 10G SERDES DRIVER
> +M: Sean Anderson <sean.anderson@seco.com>
> +S: Maintained
> +F: drivers/clk/clk-fsl-lynx-10g.c
> +F: include/dt-bindings/clock/fsl,lynx-10g.h
> +F: include/linux/phy/lynx-10g.h
> +
> LYNX 28G SERDES PHY DRIVER
> M: Ioana Ciornei <ioana.ciornei@nxp.com>
> L: netdev@vger.kernel.org
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index e3ca0d058a25..eebed69f6c58 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
> obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o
> obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
> obj-$(CONFIG_COMMON_CLK_FSL_FLEXSPI) += clk-fsl-flexspi.o
> +obj-$(CONFIG_PHY_FSL_LYNX_10G) += clk-fsl-lynx-10g.o
> obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
> obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
> obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
> diff --git a/drivers/clk/clk-fsl-lynx-10g.c b/drivers/clk/clk-fsl-lynx-10g.c
> new file mode 100644
> index 000000000000..78357303b578
> --- /dev/null
> +++ b/drivers/clk/clk-fsl-lynx-10g.c
> @@ -0,0 +1,510 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + *
> + * This file contains the implementation for the PLLs found on Lynx 10G phys.
> + *
> + * XXX: The VCO rate of the PLLs can exceed ~4GHz, which is the maximum rate
> + * expressable in an unsigned long. To work around this, rates are specified in
> + * kHz. This is as if there was a division by 1000 in the PLL.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/bitfield.h>
> +#include <linux/math64.h>
> +#include <linux/phy/lynx-10g.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/units.h>
> +#include <dt-bindings/clock/fsl,lynx-10g.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 PLLaRSTCTL_ENABLE_SET (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_PLLRST_B | \
> + PLLaRSTCTL_SDRST_B | PLLaRSTCTL_SDEN)
> +#define PLLaRSTCTL_ENABLE_MASK (PLLaRSTCTL_ENABLE_SET | PLLaRSTCTL_RST_ERR)
> +
> +#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 PLLaCR0_DLYDIV_SEL_16 0b01
> +
> +/**
> + * struct lynx_clk - Driver data for the PLLs
> + * @pll: The PLL clock
> + * @ex_dly: The "PLLa_ex_dly_clk" clock
> + * @dev: The serdes device
> + * @regmap: Our registers
> + * @idx: Which PLL this clock is for
> + */
> +struct lynx_clk {
> + struct clk_hw pll, ex_dly;
> + struct device *dev;
> + struct regmap *regmap;
> + unsigned int idx;
> +};
> +
> +static u32 lynx_read(struct lynx_clk *clk, u32 reg)
> +{
> + unsigned int ret = 0;
> +
> + WARN_ON_ONCE(regmap_read(clk->regmap, reg, &ret));
> + return ret;
> +}
> +
> +static void lynx_write(struct lynx_clk *clk, u32 val, u32 reg)
> +{
> + WARN_ON_ONCE(regmap_write(clk->regmap, reg, val));
> +}
> +
> +static struct lynx_clk *lynx_pll_to_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct lynx_clk, pll);
> +}
> +
> +static struct lynx_clk *lynx_ex_dly_to_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct lynx_clk, ex_dly);
> +}
> +
> +static void lynx_pll_stop(struct lynx_clk *clk)
> +{
> + u32 rstctl;
> +
> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + rstctl &= ~PLLaRSTCTL_SDRST_B;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> +
> + ndelay(50);
> +
> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> +
> + ndelay(100);
> +}
> +
> +static void lynx_pll_disable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 cr0;
> +
> + dev_dbg(clk->dev, "disable pll%d\n", clk->idx);
> +
> + lynx_pll_stop(clk);
> +
> + cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + cr0 |= PLLaCR0_POFF;
> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
> +}
> +
> +static int lynx_pll_reset(struct lynx_clk *clk)
> +{
> + int ret;
> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> +
> + rstctl |= PLLaRSTCTL_RSTREQ;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> + ret = read_poll_timeout(lynx_read, rstctl,
> + rstctl & (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_RST_ERR),
> + 100, 5000, true, clk, PLLaRSTCTL(clk->idx));
> + if (rstctl & PLLaRSTCTL_RST_ERR)
> + ret = -EIO;
> + if (ret) {
> + dev_err(clk->dev, "pll%d reset failed\n", clk->idx);
> + return ret;
> + }
> +
> + rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
> + return 0;
> +}
> +
> +static int lynx_pll_prepare(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + /*
> + * "Enabling" the PLL involves resetting it (and all attached lanes).
> + * Avoid doing this if we are already enabled.
> + */
> + if (!(cr0 & PLLaCR0_POFF) &&
> + (rstctl & PLLaRSTCTL_ENABLE_MASK) == PLLaRSTCTL_ENABLE_SET) {
> + dev_dbg(clk->dev, "pll%d already prepared\n", clk->idx);
> + return 0;
> + }
> +
> + dev_dbg(clk->dev, "prepare pll%d\n", clk->idx);
> +
> + cr0 &= ~PLLaCR0_POFF;
> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
> +
> + return lynx_pll_reset(clk);
> +}
> +
> +static int lynx_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + bool enabled = !(cr0 & PLLaCR0_POFF);
> +
> + dev_dbg(clk->dev, "pll%d %s enabled\n", clk->idx,
> + enabled ? "is" : "is not");
> +
> + return enabled;
> +}
> +
> +static const unsigned long 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(unsigned long rate)
> +{
> + int ret;
> +
> + for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
> + unsigned long rfclk_rate = rfclk_sel_map[ret];
> + /* Allow an error of 100ppm */
> + unsigned long error = rfclk_rate / 10000;
> +
> + if (abs(rate - rfclk_rate) < error)
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const unsigned long 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(unsigned long 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_pll_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> + u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
> + u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
> + u32 ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + unsigned long ret;
> +
> + /* Ensure that the parent matches our rfclk selector */
> + if (rfclk_sel == lynx_rfclk_to_sel(parent_rate))
> + ret = mult_frac(parent_rate, ratio, HZ_PER_KHZ);
> + else
> + ret = 0;
> +
> + dev_dbg(clk->dev, "recalc pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)ret * HZ_PER_KHZ, parent_rate);
> + return ret;
> +}
> +
> +static int lynx_pll_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + int frate_sel, rfclk_sel;
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 ratio;
> +
> + dev_dbg(clk->dev, "round pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)req->rate * HZ_PER_KHZ, req->best_parent_rate);
> +
> + frate_sel = lynx_frate_to_sel(req->rate);
> + if (frate_sel < 0)
> + return frate_sel;
> +
> + /* Try the current parent rate */
> + rfclk_sel = lynx_rfclk_to_sel(req->best_parent_rate);
> + if (rfclk_sel >= 0) {
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (ratio) {
> + req->rate = mult_frac(req->best_parent_rate, ratio,
> + HZ_PER_KHZ);
> + return 0;
> + }
> + }
> +
> + /* Try all possible parent rates */
> + for (rfclk_sel = 0;
> + rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
> + rfclk_sel++) {
> + unsigned long new_parent_rate;
> +
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (!ratio)
> + continue;
> +
> + /* Ensure the reference clock can produce this rate */
> + new_parent_rate = rfclk_sel_map[rfclk_sel];
> + new_parent_rate = clk_hw_round_rate(req->best_parent_hw,
> + new_parent_rate);
> + if (rfclk_sel != lynx_rfclk_to_sel(new_parent_rate))
> + continue;
> +
> + req->rate = mult_frac(new_parent_rate, ratio, HZ_PER_KHZ);
> + req->best_parent_rate = new_parent_rate;
> + return 0;
> + }
> +
> + 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;
> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
> + u32 ratio, cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + dev_dbg(clk->dev, "set rate pll%d new=%llu parent=%lu\n", clk->idx,
> + (u64)rate_khz * HZ_PER_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)
> + return rfclk_sel;
> +
> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
> + if (!ratio)
> + return -EINVAL;
> +
> + lynx_pll_stop(clk);
> + 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(clk, cr0, PLLaCR0(clk->idx));
> + /* Don't bother resetting if it's off */
> + if (cr0 & PLLaCR0_POFF)
> + return 0;
> + return lynx_pll_reset(clk);
> +}
> +
> +static const struct clk_ops lynx_pll_clk_ops = {
> + .prepare = lynx_pll_prepare,
> + .disable = lynx_pll_disable,
> + .is_enabled = lynx_pll_is_enabled,
> + .recalc_rate = lynx_pll_recalc_rate,
> + .determine_rate = lynx_pll_determine_rate,
> + .set_rate = lynx_pll_set_rate,
> +};
> +
> +static void lynx_ex_dly_disable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
> +}
> +
> +static int lynx_ex_dly_enable(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
> +
> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
> + cr0 |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, PLLaCR0_DLYDIV_SEL_16);
> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
> + return 0;
> +}
> +
> +static int lynx_ex_dly_is_enabled(struct clk_hw *hw)
> +{
> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
> +
> + return lynx_read(clk, PLLaCR0(clk->idx)) & PLLaCR0_DLYDIV_SEL;
> +}
> +
> +static unsigned long lynx_ex_dly_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + return parent_rate / 16;
> +}
> +
> +static const struct clk_ops lynx_ex_dly_clk_ops = {
> + .enable = lynx_ex_dly_enable,
> + .disable = lynx_ex_dly_disable,
> + .is_enabled = lynx_ex_dly_is_enabled,
> + .recalc_rate = lynx_ex_dly_recalc_rate,
> +};
> +
> +static int lynx_clk_init(struct clk_hw_onecell_data *hw_data,
> + struct device *dev, struct regmap *regmap,
> + unsigned int index, bool compat)
> +{
> + const struct clk_hw *ex_dly_parents;
> + struct clk_parent_data pll_parents[1] = { };
> + struct clk_init_data pll_init = {
> + .ops = &lynx_pll_clk_ops,
> + .parent_data = pll_parents,
> + .num_parents = 1,
> + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT |
> + CLK_OPS_PARENT_ENABLE,
> + };
> + struct clk_init_data ex_dly_init = {
> + .ops = &lynx_ex_dly_clk_ops,
> + .parent_hws = &ex_dly_parents,
> + .num_parents = 1,
> + };
> + struct lynx_clk *clk;
> + int ret;
> +
> + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
> + if (!clk)
> + return -ENOMEM;
> +
> + clk->dev = dev;
> + clk->regmap = regmap;
> + clk->idx = index;
> +
> + pll_parents[0].fw_name = kasprintf(GFP_KERNEL, "ref%d", index);
> + pll_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_khz", dev_name(dev),
> + index);
> + ex_dly_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_ex_dly_khz",
> + dev_name(dev), index);
> + if (!pll_parents[0].fw_name || !pll_init.name || !ex_dly_init.name) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + if (compat)
> + pll_init.flags |= CLK_IGNORE_UNUSED;
> + clk->pll.init = &pll_init;
> + ret = devm_clk_hw_register(dev, &clk->pll);
> + if (ret) {
> + dev_err_probe(dev, ret, "could not register %s\n",
> + pll_init.name);
> + goto out;
> + }
> +
> + ex_dly_parents = &clk->pll;
> + clk->ex_dly.init = &ex_dly_init;
> + ret = devm_clk_hw_register(dev, &clk->ex_dly);
> + if (ret)
> + dev_err_probe(dev, ret, "could not register %s\n",
> + ex_dly_init.name);
> +
> + hw_data->hws[LYNX10G_PLLa(index)] = &clk->pll;
> + hw_data->hws[LYNX10G_PLLa_EX_DLY(index)] = &clk->ex_dly;
> +
> +out:
> + kfree(pll_parents[0].fw_name);
> + kfree(pll_init.name);
> + kfree(ex_dly_init.name);
> + return ret;
> +}
> +
> +#define NUM_PLLS 2
> +#define NUM_CLKS (NUM_PLLS * LYNX10G_CLKS_PER_PLL)
> +
> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat)
> +{
> + int ret, i;
> + struct clk_hw_onecell_data *hw_data;
> +
> + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, NUM_CLKS),
> + GFP_KERNEL);
> + if (!hw_data)
> + return -ENOMEM;
> + hw_data->num = NUM_CLKS;
> +
> + for (i = 0; i < NUM_PLLS; i++) {
> + ret = lynx_clk_init(hw_data, dev, regmap, i, compat);
> + if (ret)
> + return ret;
> +
> + plls[i] = devm_clk_hw_get_clk(dev,
> + hw_data->hws[LYNX10G_PLLa(i)],
> + NULL);
> + if (IS_ERR(plls[i]))
> + return PTR_ERR(plls[i]);
> +
> + ex_dlys[i] = devm_clk_hw_get_clk(dev,
> + hw_data->hws[LYNX10G_PLLa_EX_DLY(i)],
> + NULL);
> + if (IS_ERR(ex_dlys[i]))
> + return PTR_ERR(plls[i]);
> + }
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
> + if (ret)
> + dev_err_probe(dev, ret, "could not register clock provider\n");
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(lynx_clks_init);
> +
> +MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
> +MODULE_DESCRIPTION("Lynx 10G PLL driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> index 853958fb2c06..5d461232276f 100644
> --- a/drivers/phy/freescale/Kconfig
> +++ b/drivers/phy/freescale/Kconfig
> @@ -47,3 +47,9 @@ 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
> + depends on COMMON_CLK
> + depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
> + select REGMAP_MMIO
Why is this change in clk driver part?
> diff --git a/include/linux/phy/lynx-10g.h b/include/linux/phy/lynx-10g.h
> new file mode 100644
> index 000000000000..b7b80b3ee988
> --- /dev/null
> +++ b/include/linux/phy/lynx-10g.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + */
> +
> +#ifndef LYNX_10G
> +#define LYNX_10G
> +
> +struct clk;
> +struct device;
> +struct regmap;
> +
> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
so you have an exported symbol for clk driver init in phy driver header?
can you please explain why..?
> +
> +#endif /* LYNX 10G */
> --
> 2.35.1.1320.gc452695387.dirty
--
~Vinod
^ permalink raw reply [flat|nested] 64+ messages in thread* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-08 9:15 ` Vinod Koul
@ 2023-05-08 15:31 ` Sean Anderson
2023-05-09 13:00 ` Vinod Koul
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-08 15:31 UTC (permalink / raw)
To: Vinod Koul
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 5/8/23 05:15, Vinod Koul wrote:
> On 13-04-23, 12:05, Sean Anderson wrote:
>> This adds support for the PLLs found in Lynx 10G "SerDes" devices found on
>> various NXP QorIQ SoCs. There are two PLLs in each SerDes. This driver has
>> been split from the main PHY driver to allow for better review, even though
>> these PLLs are not present anywhere else besides the SerDes. An auxiliary
>> device is not used as it offers no benefits over a function call (and there
>> is no need to have a separate device).
>>
>> The PLLs are modeled as clocks proper to let us take advantage of the
>> existing clock infrastructure. I have not given the same treatment to the
>> per-lane clocks 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.
>>
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>>
>> ---
>>
>> (no changes since v10)
>>
>> Changes in v10:
>> - Remove unnecessary inclusion of clk.h
>> - Don't gate clocks in compatibility mode
>>
>> Changes in v9:
>> - Convert some u32s to unsigned long to match arguments
>> - Switch from round_rate to determine_rate
>> - Drop explicit reference to reference clock
>> - Use .parent_names when requesting parents
>> - Use devm_clk_hw_get_clk to pass clocks back to serdes
>> - Fix indentation
>> - Split off from following patch to allow for better review
>>
>> MAINTAINERS | 7 +
>> drivers/clk/Makefile | 1 +
>> drivers/clk/clk-fsl-lynx-10g.c | 510 +++++++++++++++++++++++++++++++++
>> drivers/phy/freescale/Kconfig | 6 +
>> include/linux/phy/lynx-10g.h | 16 ++
>> 5 files changed, 540 insertions(+)
>> create mode 100644 drivers/clk/clk-fsl-lynx-10g.c
>> create mode 100644 include/linux/phy/lynx-10g.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index fce67b74e4a2..8da893681de6 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12195,6 +12195,13 @@ S: Maintained
>> W: https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=http%3a%2f%2flinux%2dtest%2dproject.github.io&umid=eea5aa47-e59c-4621-9990-899dbf66d77b&auth=d807158c60b7d2502abde8a2fc01f40662980862-79710d1cff1d8502466ef54536bf4f8832e88cfd
>> T: git https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2fgithub.com%2flinux%2dtest%2dproject%2fltp.git&umid=eea5aa47-e59c-4621-9990-899dbf66d77b&auth=d807158c60b7d2502abde8a2fc01f40662980862-3742f3f51fa3244f5c96d5ba988f63d472b08938
>>
>> +LYNX 10G SERDES DRIVER
>> +M: Sean Anderson <sean.anderson@seco.com>
>> +S: Maintained
>> +F: drivers/clk/clk-fsl-lynx-10g.c
>> +F: include/dt-bindings/clock/fsl,lynx-10g.h
>> +F: include/linux/phy/lynx-10g.h
>> +
>> LYNX 28G SERDES PHY DRIVER
>> M: Ioana Ciornei <ioana.ciornei@nxp.com>
>> L: netdev@vger.kernel.org
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index e3ca0d058a25..eebed69f6c58 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
>> obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o
>> obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
>> obj-$(CONFIG_COMMON_CLK_FSL_FLEXSPI) += clk-fsl-flexspi.o
>> +obj-$(CONFIG_PHY_FSL_LYNX_10G) += clk-fsl-lynx-10g.o
>> obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
>> obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
>> obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
>> diff --git a/drivers/clk/clk-fsl-lynx-10g.c b/drivers/clk/clk-fsl-lynx-10g.c
>> new file mode 100644
>> index 000000000000..78357303b578
>> --- /dev/null
>> +++ b/drivers/clk/clk-fsl-lynx-10g.c
>> @@ -0,0 +1,510 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
>> + *
>> + * This file contains the implementation for the PLLs found on Lynx 10G phys.
>> + *
>> + * XXX: The VCO rate of the PLLs can exceed ~4GHz, which is the maximum rate
>> + * expressable in an unsigned long. To work around this, rates are specified in
>> + * kHz. This is as if there was a division by 1000 in the PLL.
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/device.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/math64.h>
>> +#include <linux/phy/lynx-10g.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/units.h>
>> +#include <dt-bindings/clock/fsl,lynx-10g.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 PLLaRSTCTL_ENABLE_SET (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_PLLRST_B | \
>> + PLLaRSTCTL_SDRST_B | PLLaRSTCTL_SDEN)
>> +#define PLLaRSTCTL_ENABLE_MASK (PLLaRSTCTL_ENABLE_SET | PLLaRSTCTL_RST_ERR)
>> +
>> +#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 PLLaCR0_DLYDIV_SEL_16 0b01
>> +
>> +/**
>> + * struct lynx_clk - Driver data for the PLLs
>> + * @pll: The PLL clock
>> + * @ex_dly: The "PLLa_ex_dly_clk" clock
>> + * @dev: The serdes device
>> + * @regmap: Our registers
>> + * @idx: Which PLL this clock is for
>> + */
>> +struct lynx_clk {
>> + struct clk_hw pll, ex_dly;
>> + struct device *dev;
>> + struct regmap *regmap;
>> + unsigned int idx;
>> +};
>> +
>> +static u32 lynx_read(struct lynx_clk *clk, u32 reg)
>> +{
>> + unsigned int ret = 0;
>> +
>> + WARN_ON_ONCE(regmap_read(clk->regmap, reg, &ret));
>> + return ret;
>> +}
>> +
>> +static void lynx_write(struct lynx_clk *clk, u32 val, u32 reg)
>> +{
>> + WARN_ON_ONCE(regmap_write(clk->regmap, reg, val));
>> +}
>> +
>> +static struct lynx_clk *lynx_pll_to_clk(struct clk_hw *hw)
>> +{
>> + return container_of(hw, struct lynx_clk, pll);
>> +}
>> +
>> +static struct lynx_clk *lynx_ex_dly_to_clk(struct clk_hw *hw)
>> +{
>> + return container_of(hw, struct lynx_clk, ex_dly);
>> +}
>> +
>> +static void lynx_pll_stop(struct lynx_clk *clk)
>> +{
>> + u32 rstctl;
>> +
>> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
>> + rstctl &= ~PLLaRSTCTL_SDRST_B;
>> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
>> +
>> + ndelay(50);
>> +
>> + rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
>> + rstctl &= ~(PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B);
>> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
>> +
>> + ndelay(100);
>> +}
>> +
>> +static void lynx_pll_disable(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
>> + u32 cr0;
>> +
>> + dev_dbg(clk->dev, "disable pll%d\n", clk->idx);
>> +
>> + lynx_pll_stop(clk);
>> +
>> + cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> + cr0 |= PLLaCR0_POFF;
>> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
>> +}
>> +
>> +static int lynx_pll_reset(struct lynx_clk *clk)
>> +{
>> + int ret;
>> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
>> +
>> + rstctl |= PLLaRSTCTL_RSTREQ;
>> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
>> + ret = read_poll_timeout(lynx_read, rstctl,
>> + rstctl & (PLLaRSTCTL_RST_DONE | PLLaRSTCTL_RST_ERR),
>> + 100, 5000, true, clk, PLLaRSTCTL(clk->idx));
>> + if (rstctl & PLLaRSTCTL_RST_ERR)
>> + ret = -EIO;
>> + if (ret) {
>> + dev_err(clk->dev, "pll%d reset failed\n", clk->idx);
>> + return ret;
>> + }
>> +
>> + rstctl |= PLLaRSTCTL_SDEN | PLLaRSTCTL_PLLRST_B | PLLaRSTCTL_SDRST_B;
>> + lynx_write(clk, rstctl, PLLaRSTCTL(clk->idx));
>> + return 0;
>> +}
>> +
>> +static int lynx_pll_prepare(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
>> + u32 rstctl = lynx_read(clk, PLLaRSTCTL(clk->idx));
>> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> +
>> + /*
>> + * "Enabling" the PLL involves resetting it (and all attached lanes).
>> + * Avoid doing this if we are already enabled.
>> + */
>> + if (!(cr0 & PLLaCR0_POFF) &&
>> + (rstctl & PLLaRSTCTL_ENABLE_MASK) == PLLaRSTCTL_ENABLE_SET) {
>> + dev_dbg(clk->dev, "pll%d already prepared\n", clk->idx);
>> + return 0;
>> + }
>> +
>> + dev_dbg(clk->dev, "prepare pll%d\n", clk->idx);
>> +
>> + cr0 &= ~PLLaCR0_POFF;
>> + lynx_write(clk, cr0, PLLaCR0(clk->idx));
>> +
>> + return lynx_pll_reset(clk);
>> +}
>> +
>> +static int lynx_pll_is_enabled(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
>> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> + bool enabled = !(cr0 & PLLaCR0_POFF);
>> +
>> + dev_dbg(clk->dev, "pll%d %s enabled\n", clk->idx,
>> + enabled ? "is" : "is not");
>> +
>> + return enabled;
>> +}
>> +
>> +static const unsigned long 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(unsigned long rate)
>> +{
>> + int ret;
>> +
>> + for (ret = 0; ret < ARRAY_SIZE(rfclk_sel_map); ret++) {
>> + unsigned long rfclk_rate = rfclk_sel_map[ret];
>> + /* Allow an error of 100ppm */
>> + unsigned long error = rfclk_rate / 10000;
>> +
>> + if (abs(rate - rfclk_rate) < error)
>> + return ret;
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static const unsigned long 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(unsigned long 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_pll_to_clk(hw);
>> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> + u32 frate_sel = FIELD_GET(PLLaCR0_FRATE_SEL, cr0);
>> + u32 rfclk_sel = FIELD_GET(PLLaCR0_RFCLK_SEL, cr0);
>> + u32 ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> + unsigned long ret;
>> +
>> + /* Ensure that the parent matches our rfclk selector */
>> + if (rfclk_sel == lynx_rfclk_to_sel(parent_rate))
>> + ret = mult_frac(parent_rate, ratio, HZ_PER_KHZ);
>> + else
>> + ret = 0;
>> +
>> + dev_dbg(clk->dev, "recalc pll%d new=%llu parent=%lu\n", clk->idx,
>> + (u64)ret * HZ_PER_KHZ, parent_rate);
>> + return ret;
>> +}
>> +
>> +static int lynx_pll_determine_rate(struct clk_hw *hw,
>> + struct clk_rate_request *req)
>> +{
>> + int frate_sel, rfclk_sel;
>> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
>> + u32 ratio;
>> +
>> + dev_dbg(clk->dev, "round pll%d new=%llu parent=%lu\n", clk->idx,
>> + (u64)req->rate * HZ_PER_KHZ, req->best_parent_rate);
>> +
>> + frate_sel = lynx_frate_to_sel(req->rate);
>> + if (frate_sel < 0)
>> + return frate_sel;
>> +
>> + /* Try the current parent rate */
>> + rfclk_sel = lynx_rfclk_to_sel(req->best_parent_rate);
>> + if (rfclk_sel >= 0) {
>> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> + if (ratio) {
>> + req->rate = mult_frac(req->best_parent_rate, ratio,
>> + HZ_PER_KHZ);
>> + return 0;
>> + }
>> + }
>> +
>> + /* Try all possible parent rates */
>> + for (rfclk_sel = 0;
>> + rfclk_sel < ARRAY_SIZE(rfclk_sel_map);
>> + rfclk_sel++) {
>> + unsigned long new_parent_rate;
>> +
>> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> + if (!ratio)
>> + continue;
>> +
>> + /* Ensure the reference clock can produce this rate */
>> + new_parent_rate = rfclk_sel_map[rfclk_sel];
>> + new_parent_rate = clk_hw_round_rate(req->best_parent_hw,
>> + new_parent_rate);
>> + if (rfclk_sel != lynx_rfclk_to_sel(new_parent_rate))
>> + continue;
>> +
>> + req->rate = mult_frac(new_parent_rate, ratio, HZ_PER_KHZ);
>> + req->best_parent_rate = new_parent_rate;
>> + return 0;
>> + }
>> +
>> + 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;
>> + struct lynx_clk *clk = lynx_pll_to_clk(hw);
>> + u32 ratio, cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> +
>> + dev_dbg(clk->dev, "set rate pll%d new=%llu parent=%lu\n", clk->idx,
>> + (u64)rate_khz * HZ_PER_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)
>> + return rfclk_sel;
>> +
>> + ratio = lynx_pll_ratio(frate_sel, rfclk_sel);
>> + if (!ratio)
>> + return -EINVAL;
>> +
>> + lynx_pll_stop(clk);
>> + 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(clk, cr0, PLLaCR0(clk->idx));
>> + /* Don't bother resetting if it's off */
>> + if (cr0 & PLLaCR0_POFF)
>> + return 0;
>> + return lynx_pll_reset(clk);
>> +}
>> +
>> +static const struct clk_ops lynx_pll_clk_ops = {
>> + .prepare = lynx_pll_prepare,
>> + .disable = lynx_pll_disable,
>> + .is_enabled = lynx_pll_is_enabled,
>> + .recalc_rate = lynx_pll_recalc_rate,
>> + .determine_rate = lynx_pll_determine_rate,
>> + .set_rate = lynx_pll_set_rate,
>> +};
>> +
>> +static void lynx_ex_dly_disable(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
>> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> +
>> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
>> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
>> +}
>> +
>> +static int lynx_ex_dly_enable(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
>> + u32 cr0 = lynx_read(clk, PLLaCR0(clk->idx));
>> +
>> + cr0 &= ~PLLaCR0_DLYDIV_SEL;
>> + cr0 |= FIELD_PREP(PLLaCR0_DLYDIV_SEL, PLLaCR0_DLYDIV_SEL_16);
>> + lynx_write(clk, PLLaCR0(clk->idx), cr0);
>> + return 0;
>> +}
>> +
>> +static int lynx_ex_dly_is_enabled(struct clk_hw *hw)
>> +{
>> + struct lynx_clk *clk = lynx_ex_dly_to_clk(hw);
>> +
>> + return lynx_read(clk, PLLaCR0(clk->idx)) & PLLaCR0_DLYDIV_SEL;
>> +}
>> +
>> +static unsigned long lynx_ex_dly_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + return parent_rate / 16;
>> +}
>> +
>> +static const struct clk_ops lynx_ex_dly_clk_ops = {
>> + .enable = lynx_ex_dly_enable,
>> + .disable = lynx_ex_dly_disable,
>> + .is_enabled = lynx_ex_dly_is_enabled,
>> + .recalc_rate = lynx_ex_dly_recalc_rate,
>> +};
>> +
>> +static int lynx_clk_init(struct clk_hw_onecell_data *hw_data,
>> + struct device *dev, struct regmap *regmap,
>> + unsigned int index, bool compat)
>> +{
>> + const struct clk_hw *ex_dly_parents;
>> + struct clk_parent_data pll_parents[1] = { };
>> + struct clk_init_data pll_init = {
>> + .ops = &lynx_pll_clk_ops,
>> + .parent_data = pll_parents,
>> + .num_parents = 1,
>> + .flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT |
>> + CLK_OPS_PARENT_ENABLE,
>> + };
>> + struct clk_init_data ex_dly_init = {
>> + .ops = &lynx_ex_dly_clk_ops,
>> + .parent_hws = &ex_dly_parents,
>> + .num_parents = 1,
>> + };
>> + struct lynx_clk *clk;
>> + int ret;
>> +
>> + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
>> + if (!clk)
>> + return -ENOMEM;
>> +
>> + clk->dev = dev;
>> + clk->regmap = regmap;
>> + clk->idx = index;
>> +
>> + pll_parents[0].fw_name = kasprintf(GFP_KERNEL, "ref%d", index);
>> + pll_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_khz", dev_name(dev),
>> + index);
>> + ex_dly_init.name = kasprintf(GFP_KERNEL, "%s.pll%d_ex_dly_khz",
>> + dev_name(dev), index);
>> + if (!pll_parents[0].fw_name || !pll_init.name || !ex_dly_init.name) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + if (compat)
>> + pll_init.flags |= CLK_IGNORE_UNUSED;
>> + clk->pll.init = &pll_init;
>> + ret = devm_clk_hw_register(dev, &clk->pll);
>> + if (ret) {
>> + dev_err_probe(dev, ret, "could not register %s\n",
>> + pll_init.name);
>> + goto out;
>> + }
>> +
>> + ex_dly_parents = &clk->pll;
>> + clk->ex_dly.init = &ex_dly_init;
>> + ret = devm_clk_hw_register(dev, &clk->ex_dly);
>> + if (ret)
>> + dev_err_probe(dev, ret, "could not register %s\n",
>> + ex_dly_init.name);
>> +
>> + hw_data->hws[LYNX10G_PLLa(index)] = &clk->pll;
>> + hw_data->hws[LYNX10G_PLLa_EX_DLY(index)] = &clk->ex_dly;
>> +
>> +out:
>> + kfree(pll_parents[0].fw_name);
>> + kfree(pll_init.name);
>> + kfree(ex_dly_init.name);
>> + return ret;
>> +}
>> +
>> +#define NUM_PLLS 2
>> +#define NUM_CLKS (NUM_PLLS * LYNX10G_CLKS_PER_PLL)
>> +
>> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
>> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat)
>> +{
>> + int ret, i;
>> + struct clk_hw_onecell_data *hw_data;
>> +
>> + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, NUM_CLKS),
>> + GFP_KERNEL);
>> + if (!hw_data)
>> + return -ENOMEM;
>> + hw_data->num = NUM_CLKS;
>> +
>> + for (i = 0; i < NUM_PLLS; i++) {
>> + ret = lynx_clk_init(hw_data, dev, regmap, i, compat);
>> + if (ret)
>> + return ret;
>> +
>> + plls[i] = devm_clk_hw_get_clk(dev,
>> + hw_data->hws[LYNX10G_PLLa(i)],
>> + NULL);
>> + if (IS_ERR(plls[i]))
>> + return PTR_ERR(plls[i]);
>> +
>> + ex_dlys[i] = devm_clk_hw_get_clk(dev,
>> + hw_data->hws[LYNX10G_PLLa_EX_DLY(i)],
>> + NULL);
>> + if (IS_ERR(ex_dlys[i]))
>> + return PTR_ERR(plls[i]);
>> + }
>> +
>> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
>> + if (ret)
>> + dev_err_probe(dev, ret, "could not register clock provider\n");
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(lynx_clks_init);
>> +
>> +MODULE_AUTHOR("Sean Anderson <sean.anderson@seco.com>");
>> +MODULE_DESCRIPTION("Lynx 10G PLL driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
>> index 853958fb2c06..5d461232276f 100644
>> --- a/drivers/phy/freescale/Kconfig
>> +++ b/drivers/phy/freescale/Kconfig
>> @@ -47,3 +47,9 @@ 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
>> + depends on COMMON_CLK
>> + depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
>> + select REGMAP_MMIO
>
> Why is this change in clk driver part?
So the condition in the Makefile has an associated Kconfig.
>> diff --git a/include/linux/phy/lynx-10g.h b/include/linux/phy/lynx-10g.h
>> new file mode 100644
>> index 000000000000..b7b80b3ee988
>> --- /dev/null
>> +++ b/include/linux/phy/lynx-10g.h
>> @@ -0,0 +1,16 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
>> + */
>> +
>> +#ifndef LYNX_10G
>> +#define LYNX_10G
>> +
>> +struct clk;
>> +struct device;
>> +struct regmap;
>> +
>> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
>> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
>
> so you have an exported symbol for clk driver init in phy driver header?
> can you please explain why..?
So that it can be called at the appropriate time during the phy's probe function.
This is really an integral part of the phy driver, but I was directed to split it
off and put it in another subsystem's directory.
--Sean
>> +
>> +#endif /* LYNX 10G */
>> --
>> 2.35.1.1320.gc452695387.dirty
>
^ permalink raw reply [flat|nested] 64+ messages in thread* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-08 15:31 ` Sean Anderson
@ 2023-05-09 13:00 ` Vinod Koul
2023-05-09 15:26 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vinod Koul @ 2023-05-09 13:00 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 08-05-23, 11:31, Sean Anderson wrote:
> On 5/8/23 05:15, Vinod Koul wrote:
> >> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> >> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
> >
> > so you have an exported symbol for clk driver init in phy driver header?
> > can you please explain why..?
>
> So that it can be called at the appropriate time during the phy's probe function.
>
> This is really an integral part of the phy driver, but I was directed to split it
> off and put it in another subsystem's directory.
That is right clock should be belong to clk driver. IIUC the hardware is
phy along with clocks and you are doing the clk init. I think that may
not be correct model, you should really have a device tree node to
represent the clock and the phy node
What stops this from being modelled as it is in the hardware?
--
~Vinod
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-09 13:00 ` Vinod Koul
@ 2023-05-09 15:26 ` Sean Anderson
2023-05-16 13:22 ` Vinod Koul
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-09 15:26 UTC (permalink / raw)
To: Vinod Koul
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 5/9/23 09:00, Vinod Koul wrote:
> On 08-05-23, 11:31, Sean Anderson wrote:
>> On 5/8/23 05:15, Vinod Koul wrote:
>
>> >> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
>> >> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
>> >
>> > so you have an exported symbol for clk driver init in phy driver header?
>> > can you please explain why..?
>>
>> So that it can be called at the appropriate time during the phy's probe function.
>>
>> This is really an integral part of the phy driver, but I was directed to split it
>> off and put it in another subsystem's directory.
>
> That is right clock should be belong to clk driver. IIUC the hardware is
> phy along with clocks and you are doing the clk init. I think that may
> not be correct model, you should really have a device tree node to
> represent the clock and the phy node
>
>
> What stops this from being modelled as it is in the hardware?
It *is* modeled as it is in hardware. With just the serdes compatible,
we have all the information we need to create the clocks. So we do so.
There's no need for a separate device just to create four clocks.
These clocks cannot be used by any other device (except possibly by
putting a lane into test mode). So there is no benefit from making them
a separate device, except an increase in complexity due to ordering and
dynamic lookup. By doing things this way we know that either there was
an error or the clocks all exist, and the lifetime of the clocks matches
the serdes.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-09 15:26 ` Sean Anderson
@ 2023-05-16 13:22 ` Vinod Koul
2023-05-16 15:11 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vinod Koul @ 2023-05-16 13:22 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 09-05-23, 11:26, Sean Anderson wrote:
> On 5/9/23 09:00, Vinod Koul wrote:
> > On 08-05-23, 11:31, Sean Anderson wrote:
> >> On 5/8/23 05:15, Vinod Koul wrote:
> >
> >> >> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> >> >> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
> >> >
> >> > so you have an exported symbol for clk driver init in phy driver header?
> >> > can you please explain why..?
> >>
> >> So that it can be called at the appropriate time during the phy's probe function.
> >>
> >> This is really an integral part of the phy driver, but I was directed to split it
> >> off and put it in another subsystem's directory.
> >
> > That is right clock should be belong to clk driver. IIUC the hardware is
> > phy along with clocks and you are doing the clk init. I think that may
> > not be correct model, you should really have a device tree node to
> > represent the clock and the phy node
> >
> >
> > What stops this from being modelled as it is in the hardware?
>
> It *is* modeled as it is in hardware. With just the serdes compatible,
> we have all the information we need to create the clocks. So we do so.
> There's no need for a separate device just to create four clocks.
>
> These clocks cannot be used by any other device (except possibly by
> putting a lane into test mode). So there is no benefit from making them
> a separate device, except an increase in complexity due to ordering and
> dynamic lookup. By doing things this way we know that either there was
> an error or the clocks all exist, and the lifetime of the clocks matches
> the serdes.
Okay that does makes sense.
In that case why should this not be in the phy driver itself. There are
already few examples og phy drivers having inbuild clk where is makes
sense. You register the clk_ops as part of phy driver
Thanks
--
~Vinod
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-16 13:22 ` Vinod Koul
@ 2023-05-16 15:11 ` Sean Anderson
2023-05-16 16:32 ` Vinod Koul
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-16 15:11 UTC (permalink / raw)
To: Vinod Koul
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 5/16/23 09:22, Vinod Koul wrote:
> On 09-05-23, 11:26, Sean Anderson wrote:
>> On 5/9/23 09:00, Vinod Koul wrote:
>> > On 08-05-23, 11:31, Sean Anderson wrote:
>> >> On 5/8/23 05:15, Vinod Koul wrote:
>> >
>> >> >> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
>> >> >> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
>> >> >
>> >> > so you have an exported symbol for clk driver init in phy driver header?
>> >> > can you please explain why..?
>> >>
>> >> So that it can be called at the appropriate time during the phy's probe function.
>> >>
>> >> This is really an integral part of the phy driver, but I was directed to split it
>> >> off and put it in another subsystem's directory.
>> >
>> > That is right clock should be belong to clk driver. IIUC the hardware is
>> > phy along with clocks and you are doing the clk init. I think that may
>> > not be correct model, you should really have a device tree node to
>> > represent the clock and the phy node
>> >
>> >
>> > What stops this from being modelled as it is in the hardware?
>>
>> It *is* modeled as it is in hardware. With just the serdes compatible,
>> we have all the information we need to create the clocks. So we do so.
>> There's no need for a separate device just to create four clocks.
>>
>> These clocks cannot be used by any other device (except possibly by
>> putting a lane into test mode). So there is no benefit from making them
>> a separate device, except an increase in complexity due to ordering and
>> dynamic lookup. By doing things this way we know that either there was
>> an error or the clocks all exist, and the lifetime of the clocks matches
>> the serdes.
>
> Okay that does makes sense.
>
> In that case why should this not be in the phy driver itself. There are
> already few examples og phy drivers having inbuild clk where is makes
> sense. You register the clk_ops as part of phy driver
You told me to do it this way
https://lore.kernel.org/linux-phy/YsW+4fm%2F613ByK09@matsya/
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver
2023-05-16 15:11 ` Sean Anderson
@ 2023-05-16 16:32 ` Vinod Koul
0 siblings, 0 replies; 64+ messages in thread
From: Vinod Koul @ 2023-05-16 16:32 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Stephen Boyd, Michael Turquette, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-clk, linux-arm-kernel
On 16-05-23, 11:11, Sean Anderson wrote:
> On 5/16/23 09:22, Vinod Koul wrote:
> > On 09-05-23, 11:26, Sean Anderson wrote:
> >> On 5/9/23 09:00, Vinod Koul wrote:
> >> > On 08-05-23, 11:31, Sean Anderson wrote:
> >> >> On 5/8/23 05:15, Vinod Koul wrote:
> >> >
> >> >> >> +int lynx_clks_init(struct device *dev, struct regmap *regmap,
> >> >> >> + struct clk *plls[2], struct clk *ex_dlys[2], bool compat);
> >> >> >
> >> >> > so you have an exported symbol for clk driver init in phy driver header?
> >> >> > can you please explain why..?
> >> >>
> >> >> So that it can be called at the appropriate time during the phy's probe function.
> >> >>
> >> >> This is really an integral part of the phy driver, but I was directed to split it
> >> >> off and put it in another subsystem's directory.
> >> >
> >> > That is right clock should be belong to clk driver. IIUC the hardware is
> >> > phy along with clocks and you are doing the clk init. I think that may
> >> > not be correct model, you should really have a device tree node to
> >> > represent the clock and the phy node
> >> >
> >> >
> >> > What stops this from being modelled as it is in the hardware?
> >>
> >> It *is* modeled as it is in hardware. With just the serdes compatible,
> >> we have all the information we need to create the clocks. So we do so.
> >> There's no need for a separate device just to create four clocks.
> >>
> >> These clocks cannot be used by any other device (except possibly by
> >> putting a lane into test mode). So there is no benefit from making them
> >> a separate device, except an increase in complexity due to ordering and
> >> dynamic lookup. By doing things this way we know that either there was
> >> an error or the clocks all exist, and the lifetime of the clocks matches
> >> the serdes.
> >
> > Okay that does makes sense.
> >
> > In that case why should this not be in the phy driver itself. There are
> > already few examples og phy drivers having inbuild clk where is makes
> > sense. You register the clk_ops as part of phy driver
>
> You told me to do it this way
I agree that was not the right recommendation back then and your view
was right
--
~Vinod
^ permalink raw reply [flat|nested] 64+ messages in thread
* [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (5 preceding siblings ...)
2023-04-13 16:05 ` [PATCH v14 06/15] clk: Add Lynx 10G SerDes PLL driver Sean Anderson
@ 2023-04-13 16:05 ` Sean Anderson
2023-05-08 9:22 ` Vinod Koul
2023-04-13 16:06 ` [PATCH v14 08/15] phy: lynx10g: Enable by default on Layerscape Sean Anderson
` (8 subsequent siblings)
15 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:05 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Jonathan Corbet, linux-doc, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
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. It is present on QorIQ
T-Series and Layerscape processors. 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 and LS1088A.
Additionally, I have only added support for Ethernet protocols. There is
not a great need for dynamic reconfiguration for other protocols (except
perhaps for M.2 cards), 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. Most of these protocols should work "as-is", but
10GBASE-KR will need PCS support for link training.
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.
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). The earlier P-series processors appear to be similar, but
have a different overall register layout (using "banks" instead of
separate SerDes). Perhaps this those use a "5G Lynx SerDes."
Note that while I have used FIELD_GET/FIELD_PREP where possible, these
macros require const values for the field. This is incompatible with
dynamicly-generated fields, such as when the field is determined by a
variable. In these cases, I have used traditional shift/mask techniques.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
Changes in v14:
- Add note about (lack of) use of FIELD_GET/PREP
Changes in v10:
- Fix debugging print with incorrect error variable
Changes in v9:
- Split off clock "driver" into its own patch to allow for better
review.
- Add ability to defer lane initialization to phy_init. This allows
for easier transitioning between firmware-managed serdes and Linux-
managed serdes, as the consumer (such as dpaa2, which knows what the
firmware is doing) has the last say on who gets control.
- phy-type -> fsl,phy
Changes in v8:
- Remove unused variable from lynx_ls_mode_init
Changes in v7:
- Break out call order into generic documentation
- Refuse to switch "major" protocols
- Update Kconfig to reflect restrictions
- Remove set/clear of "pcs reset" bit, since it doesn't seem to fix
anything.
Changes in v6:
- Update MAINTAINERS to include new files
- Include bitfield.h and slab.h to allow compilation on non-arm64
arches.
- Depend on COMMON_CLK and either layerscape/ppc
Changes in v5:
- Remove references to PHY_INTERFACE_MODE_1000BASEKX to allow this
series to be applied directly to linux/master.
- Add fsl,lynx-10g.h to MAINTAINERS
Changes in v4:
- Rework all debug statements to remove use of __func__. Additional
information has been provided as necessary.
- Consider alternative parent rates in round_rate and not in set_rate.
Trying to modify out parent's rate in set_rate will deadlock.
- Explicitly perform a stop/reset sequence in set_rate. This way we
always ensure that the PLL is properly stopped.
- Set the power-down bit when disabling the PLL. We can do this now that
enable/disable aren't abused during the set rate sequence.
- Fix typos in QSGMII_OFFSET and XFI_OFFSET
- Rename LNmTECR0_TEQ_TYPE_PRE to LNmTECR0_TEQ_TYPE_POST to better
reflect its function (adding post-cursor equalization).
- Use of_clk_hw_onecell_get instead of a custom function.
- Return struct clks from lynx_clks_init instead of embedding lynx_clk
in lynx_priv.
- Rework PCCR helper functions; T-series SoCs differ from Layerscape SoCs
primarily in the layout and offset of the PCCRs. This will help bring a
cleaner abstraction layer. The caps have been removed, since this handles the
only current usage.
- Convert to use new binding format. As a result of this, we no longer need to
have protocols for PCIe or SATA. Additionally, modes now live in lynx_group
instead of lynx_priv.
- Remove teq from lynx_proto_params, since it can be determined from
preq_ratio/postq_ratio.
- Fix an early return from lynx_set_mode not releasing serdes->lock.
- Rename lynx_priv.conf to .cfg, since I kept mistyping it.
Changes in v3:
- Rename remaining references to QorIQ SerDes to Lynx 10G
- Fix PLL enable sequence by waiting for our reset request to be cleared
before continuing. Do the same for the lock, even though it isn't as
critical. Because we will delay for 1.5ms on average, use prepare
instead of enable so we can sleep.
- Document the status of each protocol
- Fix offset of several bitfields in RECR0
- Take into account PLLRST_B, SDRST_B, and SDEN when considering whether
a PLL is "enabled."
- Only power off unused lanes.
- Split mode lane mask into first/last lane (like group)
- Read modes from device tree
- Use caps to determine whether KX/KR are supported
- Move modes to lynx_priv
- Ensure that the protocol controller is not already in-use when we try
to configure a new mode. This should only occur if the device tree is
misconfigured (e.g. when QSGMII is selected on two lanes but there is
only one QSGMII controller).
- Split PLL drivers off into their own file
- Add clock for "ext_dly" instead of writing the bit directly (and
racing with any clock code).
- Use kasprintf instead of open-coding the snprintf dance
- Support 1000BASE-KX in lynx_lookup_proto. This still requires PCS
support, so nothing is truly "enabled" yet.
Changes in v2:
- Rename driver to Lynx 10G (etc.)
- Fix not clearing group->pll after disabling it
- Support 1 and 2 phy-cells
- Power off lanes during probe
- Clear SGMIIaCR1_PCS_EN during probe
- Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
- Handle 1000BASE-KX in lynx_proto_mode_prep
Documentation/driver-api/phy/index.rst | 1 +
Documentation/driver-api/phy/lynx_10g.rst | 58 +
MAINTAINERS | 2 +
drivers/phy/freescale/Kconfig | 18 +-
drivers/phy/freescale/Makefile | 1 +
drivers/phy/freescale/phy-fsl-lynx-10g.c | 1224 +++++++++++++++++++++
6 files changed, 1303 insertions(+), 1 deletion(-)
create mode 100644 Documentation/driver-api/phy/lynx_10g.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..c9b7a4698dab 100644
--- a/Documentation/driver-api/phy/index.rst
+++ b/Documentation/driver-api/phy/index.rst
@@ -7,6 +7,7 @@ Generic PHY Framework
.. toctree::
phy
+ lynx_10g
samsung-usb2
.. only:: subproject and html
diff --git a/Documentation/driver-api/phy/lynx_10g.rst b/Documentation/driver-api/phy/lynx_10g.rst
new file mode 100644
index 000000000000..17f9a9580e24
--- /dev/null
+++ b/Documentation/driver-api/phy/lynx_10g.rst
@@ -0,0 +1,58 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+Lynx 10G Phy (QorIQ SerDes)
+===========================
+
+Using this phy
+--------------
+
+: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>`, containing the
+number of lanes in each device, the endianness of the device, and the helper
+functions to use when selecting protocol controllers. For example, the
+configuration for the LS1046A is::
+
+ static const struct lynx_cfg ls1046a_cfg = {
+ .lanes = 4,
+ .endian = REGMAP_ENDIAN_BIG,
+ .mode_conflict = lynx_ls_mode_conflict,
+ .mode_apply = lynx_ls_mode_apply,
+ .mode_init = lynx_ls_mode_init,
+ };
+
+The ``mode_`` functions will generally be common to all SoCs in a series (e.g.
+all Layerscape SoCs or all T-series SoCs).
+
+In addition, you will need to add a device node as documented in
+``Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml``. This lets the
+driver know which lanes are available to configure.
+
+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>`. Add a new entry to ``lynx_proto_params``, and populate the
+appropriate fields. Modify ``lynx_lookup_proto`` to map the :c:type:`enum
+phy_mode <phy_mode>` to :c:type:`enum lynx_protocol <lynx_protocol>`. Finally,
+update the ``mode_conflict``, ``mode_apply``, and ``mode_init`` helpers to
+support your protocol.
+
+You may need to modify :c:func:`lynx_set_mode` in order to support your
+protocol. 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 8da893681de6..870014ab14aa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12198,7 +12198,9 @@ T: git https://github.com/linux-test-project/ltp.git
LYNX 10G SERDES DRIVER
M: Sean Anderson <sean.anderson@seco.com>
S: Maintained
+F: Documentation/driver-api/phy/lynx_10g.rst
F: drivers/clk/clk-fsl-lynx-10g.c
+F: drivers/phy/freescale/phy-fsl-lynx-10g.c
F: include/dt-bindings/clock/fsl,lynx-10g.h
F: include/linux/phy/lynx-10g.h
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 5d461232276f..6bebe00f5889 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -49,7 +49,23 @@ config PHY_FSL_LYNX_28G
Only useful for a restricted set of Ethernet protocols.
config PHY_FSL_LYNX_10G
- tristate
+ tristate "Freescale QorIQ Lynx 10G SerDes support"
depends on COMMON_CLK
depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
+ 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
+ some modes (such as 2.5G SGMII or 1000BASE-KX), or clock setups (as
+ only as subset of clock configurations are supported by the RCW).
+ 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-fsl-lynx-10g and clk-fsl-lynx-10g.
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
index cedb328bc4d2..32ad795be7c6 100644
--- a/drivers/phy/freescale/Makefile
+++ b/drivers/phy/freescale/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.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..880f718b387f
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
@@ -0,0 +1,1224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+ *
+ * This driver is for the Lynx 10G phys found on many QorIQ devices, including
+ * the Layerscape series.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/phy/lynx-10g.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#define TCALCR 0x90
+#define TCALCR1 0x94
+#define RCALCR 0xa0
+#define RCALCR1 0xa4
+
+#define CALCR_CALRST_B BIT(27)
+
+#define LS_PCCR_BASE 0x200
+#define PCCR_STRIDE 0x4
+
+#define LS_PCCRa(a) (LS_PCCR_BASE + (a) * PCCR_STRIDE)
+
+#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_RXEQ_BST BIT(28)
+#define LNmRECR0_GK2OVD GENMASK(27, 24)
+#define LNmRECR0_GK3OVD GENMASK(19, 16)
+#define LNmRECR0_GK2OVD_EN BIT(15)
+#define LNmRECR0_GK3OVD_EN BIT(14)
+#define LNmRECR0_OSETOVD_EN BIT(13)
+#define LNmRECR0_BASE_WAND GENMASK(11, 10)
+#define LNmRECR0_OSETOVD GENMASK(6, 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_POST 0b01
+#define LNmTECR0_TEQ_TYPE_BOTH 0b10
+
+#define LNmTTLCR0_FLT_SEL GENMASK(29, 24)
+
+#define LS_SGMII_BASE 0x1800
+#define LS_QSGMII_BASE 0x1880
+#define LS_XFI_BASE 0x1980
+
+#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 LS_SGMIIaCR1(a) PCSa(a, LS_SGMII_BASE, 1)
+#define SGMIIaCR1_SGPCS_EN BIT(11)
+
+enum lynx_protocol {
+ LYNX_PROTO_NONE = 0,
+ LYNX_PROTO_SGMII,
+ LYNX_PROTO_SGMII25, /* Not tested */
+ LYNX_PROTO_1000BASEKX, /* Not tested */
+ LYNX_PROTO_QSGMII, /* Not tested */
+ LYNX_PROTO_XFI,
+ LYNX_PROTO_10GKR, /* Link training unimplemented */
+ 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",
+};
+
+#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
+
+/**
+ * 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
+ * @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 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,
+ .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,
+ .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,
+ .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,
+ .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,
+ .postq_ratio = 0b00011,
+ .adpt_eq = 0b110000,
+ .amp_red = 0b000111,
+ },
+ [LYNX_PROTO_10GKR] = {
+ .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,
+ .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
+ * @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 pccr;
+ u8 idx;
+ u8 cfg;
+};
+
+static_assert(LYNX_PROTO_LAST - 1 <=
+ sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
+
+struct lynx_priv;
+
+/**
+ * struct lynx_cfg - Configuration for a particular serdes
+ * @lanes: Number of lanes
+ * @endian: Endianness of the registers
+ * @mode_conflict: Determine whether a protocol controller is already in use
+ * (by another group).
+ * @mode_apply: Apply a given protocol. This includes programming the
+ * appropriate config into the PCCR, as well as enabling/disabling
+ * any other registers (such as the enabling MDIO access).
+ * %LYNX_PROTO_NONE may be used to clear any associated registers.
+ * @mode_init: Finish initializing a mode. All fields are filled in except for
+ * protos. Type is one of PHY_TYPE_*. mode->protos should be filled
+ * in, and the other fields should be sanity-checked.
+ */
+struct lynx_cfg {
+ unsigned int lanes;
+ enum regmap_endian endian;
+ bool (*mode_conflict)(struct lynx_priv *serdes,
+ const struct lynx_mode *mode);
+ void (*mode_apply)(struct lynx_priv *serdes,
+ const struct lynx_mode *mode,
+ enum lynx_protocol proto);
+ int (*mode_init)(struct lynx_priv *serdes, struct lynx_mode *mode,
+ int type);
+};
+
+/**
+ * struct lynx_group - Driver data for a group of lanes
+ * @serdes: The parent serdes
+ * @pll: The currently-used pll
+ * @ex_dly: The ex_dly clock, if used
+ * @modes: Valid protocol controller configurations
+ * @mode_count: Number of modes in @modes
+ * @first_lane: The first lane in the group
+ * @last_lane: The last lane in the group
+ * @proto: The currently-configured protocol
+ * @initialized: Whether the complete state of @modes has been set
+ * @prots: The protocol set up by the RCW
+ */
+struct lynx_group {
+ struct lynx_priv *serdes;
+ struct clk *pll, *ex_dly;
+ const struct lynx_mode *modes;
+ size_t mode_count;
+ unsigned int first_lane;
+ unsigned int last_lane;
+ enum lynx_protocol proto;
+ bool initialized;
+ u8 prots;
+};
+
+/**
+ * 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.
+ * @dev: The serdes device
+ * @regmap: The backing regmap
+ * @cfg: SoC-specific configuration
+ * @plls: The PLLs
+ * @ex_dlys: The "ex_dly" clocks
+ * @groups: Groups in the serdes
+ * @group_count: Number of groups in @groups
+ * @used_lanes: Bitmap of the lanes currently used by phys
+ */
+struct lynx_priv {
+ struct mutex lock;
+ struct device *dev;
+ struct regmap *regmap;
+ const struct lynx_cfg *cfg;
+ struct clk *plls[2], *ex_dlys[2];
+ struct lynx_group *groups;
+ unsigned int group_count;
+ unsigned int used_lanes;
+};
+
+static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
+{
+ unsigned int ret = 0;
+
+ WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
+ dev_vdbg(serdes->dev, "%.8x <= %.8x\n", ret, reg);
+ return ret;
+}
+
+static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
+{
+ dev_vdbg(serdes->dev, "%.8x => %.8x\n", val, reg);
+ WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
+}
+
+/*
+ * 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_group(struct lynx_group *group)
+{
+ int i;
+
+ for_each_lane_reverse(i, group) {
+ u32 gcr0 = lynx_read(group->serdes, LNmGCR0(i));
+
+ gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
+ gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
+ lynx_write(group->serdes, gcr0, LNmGCR0(i));
+ }
+}
+
+static int lynx_power_off(struct phy *phy)
+{
+ lynx_power_off_group(phy_get_drvdata(phy));
+ return 0;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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;
+
+ for (i = 0; i < group->mode_count; i++) {
+ const struct lynx_mode *mode = &group->modes[i];
+
+ if (BIT(proto) & mode->protos)
+ return mode;
+ }
+
+ return NULL;
+}
+
+/**
+ * lynx_init_late() - Initialize group modes after probe()
+ * @group: The group of lanes to initialize
+ *
+ * Disable all modes for a group, taking care not to disable other groups'
+ * current modes. This ensures that whenever we select a mode, nothing else is
+ * interfering. Then, turn off the group.
+ *
+ * Return: 0 on success, or -%ENOMEM
+ */
+static int lynx_init_late(struct lynx_group *group)
+{
+ int i, j;
+ struct lynx_priv *serdes = group->serdes;
+ const struct lynx_mode **modes;
+
+ modes = kcalloc(serdes->group_count, sizeof(*modes), GFP_KERNEL);
+ if (!modes)
+ return -ENOMEM;
+
+ for (i = 0; i < serdes->group_count; i++)
+ modes[i] = lynx_lookup_mode(&serdes->groups[i],
+ serdes->groups[i].proto);
+
+ for (i = 0; i < group->mode_count; i++) {
+ for (j = 0; j < serdes->group_count; j++) {
+ if (!modes[j])
+ continue;
+
+ if (group->modes[i].pccr == modes[j]->pccr &&
+ group->modes[i].idx == modes[j]->idx)
+ goto skip;
+ }
+
+ serdes->cfg->mode_apply(serdes, &group->modes[i],
+ LYNX_PROTO_NONE);
+skip: ;
+ }
+
+ kfree(modes);
+ lynx_power_off_group(group);
+ group->initialized = true;
+ return 0;
+}
+
+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 {
+ if (!group->initialized)
+ ret = lynx_init_late(group);
+
+ if (!ret)
+ 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->ex_dly);
+ group->ex_dly = NULL;
+
+ 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;
+}
+
+/**
+ * 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;
+ }
+ default:
+ return LYNX_PROTO_NONE;
+ }
+}
+
+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;
+
+ /*
+ * FIXME: At the moment we don't support switching between major
+ * protocols. From what I can tell, the serdes is working fine, but
+ * something goes wrong in the PCS.
+ */
+ if (lynx_proto_params[proto].prots != group->prots)
+ return -EINVAL;
+
+ mode = lynx_lookup_mode(group, proto);
+ if (!mode)
+ return -EINVAL;
+
+ return 0;
+}
+
+#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, teq;
+ 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;
+ }
+ }
+
+ mutex_lock(&serdes->lock);
+ if (serdes->cfg->mode_conflict(serdes, new_mode)) {
+ dev_dbg(&phy->dev, "%s%c already in use\n",
+ lynx_proto_str[__ffs(new_mode->protos)],
+ 'A' + new_mode->idx);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ clk_disable_unprepare(group->ex_dly);
+ group->ex_dly = NULL;
+
+ 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->plls); pll++) {
+ struct clk *clk = serdes->plls[pll];
+ 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 (error=%lu)\n", pll,
+ rate, error);
+ /* Accept up to 100ppm deviation */
+ if (error && params->frate_khz / error < 10000)
+ continue;
+
+ if (!clk_set_rate_exclusive(clk, rate))
+ goto got_pll;
+ /*
+ * Someone else got a different rate first (or there was some
+ * other error)
+ */
+ }
+
+ /* If neither PLL has the right rate, try setting it */
+ for (pll = 0; pll < 2; pll++) {
+ ret = clk_set_rate_exclusive(serdes->plls[pll],
+ params->frate_khz);
+ if (!ret)
+ goto got_pll;
+ }
+
+ dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
+ params->frate_khz);
+ goto out;
+
+got_pll:
+ group->pll = serdes->plls[pll];
+ ret = clk_prepare_enable(group->pll);
+ if (ret)
+ goto out;
+
+ 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_RXEQ_BST | LNmRECR0_BASE_WAND;
+ recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
+ recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
+ recr0_mask |= LNmRECR0_OSETOVD_EN | 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;
+ teq = LNmTECR0_TEQ_TYPE_NONE;
+ if (params->postq_ratio) {
+ teq = LNmTECR0_TEQ_TYPE_POST;
+ tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
+ tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
+ }
+ if (params->preq_ratio) {
+ teq = LNmTECR0_TEQ_TYPE_BOTH;
+ tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
+ tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
+ }
+ tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, teq);
+ tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
+ tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
+
+ for_each_lane(i, group) {
+ tmp = lynx_read(serdes, LNmGCR0(i));
+ tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
+ lynx_write(serdes, tmp, LNmGCR0(i));
+ }
+
+ ndelay(50);
+
+ /* Disable the old controller */
+ if (old_mode)
+ serdes->cfg->mode_apply(serdes, old_mode, LYNX_PROTO_NONE);
+
+ for_each_lane(i, group) {
+ tmp = lynx_read(serdes, LNmGCR0(i));
+ 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);
+
+ 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));
+ }
+
+ /* Enable the new controller */
+ serdes->cfg->mode_apply(serdes, new_mode, proto);
+ if (proto == LYNX_PROTO_1000BASEKX) {
+ group->ex_dly = serdes->ex_dlys[pll];
+ /* This should never fail since it's from our internal driver */
+ WARN_ON_ONCE(clk_prepare_enable(group->ex_dly));
+ }
+ 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);
+
+out:
+ mutex_unlock(&serdes->lock);
+ return ret;
+}
+
+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,
+ .owner = THIS_MODULE,
+};
+
+static int lynx_read_u32(struct device *dev, struct fwnode_handle *fwnode,
+ const char *prop, u32 *val)
+{
+ int ret;
+
+ ret = fwnode_property_read_u32(fwnode, prop, val);
+ if (ret)
+ dev_err(dev, "could not read %s from %pfwP: %d\n", prop,
+ fwnode, ret);
+ return ret;
+}
+
+static int lynx_probe_group(struct lynx_priv *serdes, struct lynx_group *group,
+ struct fwnode_handle *fwnode, bool initialize)
+{
+ int i, lane_count, ret;
+ struct device *dev = serdes->dev;
+ struct fwnode_handle *mode_node;
+ struct lynx_mode *modes;
+ struct phy *phy;
+ u32 *lanes = NULL;
+
+ group->serdes = serdes;
+
+ lane_count = fwnode_property_count_u32(fwnode, "reg");
+ if (lane_count < 0) {
+ dev_err(dev, "could not read %s from %pfwP: %d\n",
+ "reg", fwnode, lane_count);
+ return lane_count;
+ }
+
+ lanes = kcalloc(lane_count, sizeof(*lanes), GFP_KERNEL);
+ if (!lanes)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(fwnode, "reg", lanes, lane_count);
+ if (ret) {
+ dev_err(dev, "could not read %s from %pfwP: %d\n",
+ "reg", fwnode, ret);
+ goto out;
+ }
+
+ group->first_lane = lanes[0];
+ group->last_lane = lanes[lane_count - 1];
+ for (i = 0; i < lane_count; i++) {
+ u32 prots, gcr0;
+
+ if (lanes[i] > serdes->cfg->lanes) {
+ ret = -EINVAL;
+ dev_err(dev, "lane %d not in range 0 to %u\n",
+ i, serdes->cfg->lanes);
+ goto out;
+ }
+
+ if (lanes[i] != group->first_lane +
+ i * !!(group->last_lane - group->first_lane)) {
+ ret = -EINVAL;
+ dev_err(dev, "lane %d is not monotonic\n", i);
+ goto out;
+ }
+
+ gcr0 = lynx_read(serdes, LNmGCR0(lanes[i]));
+ prots = FIELD_GET(LNmGCR0_PROTS, gcr0);
+ if (i && group->prots != prots) {
+ ret = -EIO;
+ dev_err(dev, "lane %d protocol does not match lane 0\n",
+ lanes[i]);
+ goto out;
+ }
+ group->prots = prots;
+ }
+
+ fwnode_for_each_child_node(fwnode, mode_node)
+ group->mode_count++;
+
+ modes = devm_kcalloc(dev, group->mode_count, sizeof(*group->modes),
+ GFP_KERNEL);
+ if (!modes) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ i = 0;
+ fwnode_for_each_child_node(fwnode, mode_node) {
+ struct lynx_mode *mode = &modes[i++];
+ u32 val;
+
+ ret = lynx_read_u32(dev, mode_node, "fsl,pccr", &val);
+ if (ret)
+ goto out;
+ mode->pccr = val;
+
+ ret = lynx_read_u32(dev, mode_node, "fsl,index", &val);
+ if (ret)
+ goto out;
+ mode->idx = val;
+
+ ret = lynx_read_u32(dev, mode_node, "fsl,cfg", &val);
+ if (ret)
+ goto out;
+ mode->cfg = val;
+
+ ret = lynx_read_u32(dev, mode_node, "fsl,type", &val);
+ if (ret)
+ goto out;
+
+ ret = serdes->cfg->mode_init(serdes, mode, val);
+ if (ret)
+ goto out;
+
+ dev_dbg(dev, "mode PCCR%X.%s%c_CFG=%x on lanes %u to %u\n",
+ mode->pccr, lynx_proto_str[__ffs(mode->protos)],
+ 'A' + mode->idx, mode->cfg, group->first_lane,
+ group->last_lane);
+ }
+
+ WARN_ON(i != group->mode_count);
+ group->modes = modes;
+
+ if (initialize) {
+ /* Deselect anything configured by the RCW/bootloader */
+ for (i = 0; i < group->mode_count; i++)
+ serdes->cfg->mode_apply(serdes, &group->modes[i],
+ LYNX_PROTO_NONE);
+
+ /* Disable the lanes for now */
+ lynx_power_off_group(group);
+ group->initialized = true;
+ }
+
+ phy = devm_phy_create(dev, to_of_node(fwnode), &lynx_phy_ops);
+ ret = PTR_ERR_OR_ZERO(phy);
+ if (ret)
+ dev_err_probe(dev, ret, "could not create phy\n");
+ else
+ phy_set_drvdata(phy, group);
+
+out:
+ kfree(lanes);
+ return ret;
+}
+
+static int lynx_probe(struct platform_device *pdev)
+{
+ bool compat;
+ int ret, i = 0;
+ struct device *dev = &pdev->dev;
+ struct fwnode_handle *group_node;
+ struct lynx_priv *serdes;
+ struct phy_provider *provider;
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .disable_locking = true,
+ };
+ struct resource *res;
+ void __iomem *base;
+
+ serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
+ if (!serdes)
+ return -ENOMEM;
+
+ serdes->dev = dev;
+ platform_set_drvdata(pdev, serdes);
+ mutex_init(&serdes->lock);
+ serdes->cfg = device_get_match_data(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;
+ }
+
+ regmap_config.val_format_endian = serdes->cfg->endian;
+ regmap_config.max_register = res->end - res->start;
+ serdes->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
+ if (IS_ERR(serdes->regmap)) {
+ ret = PTR_ERR(serdes->regmap);
+ dev_err_probe(dev, ret, "could not create regmap\n");
+ return ret;
+ }
+
+ compat = device_property_present(dev, "fsl,unused-lanes-reserved");
+ ret = lynx_clks_init(dev, serdes->regmap, serdes->plls,
+ serdes->ex_dlys, compat);
+ if (ret)
+ return ret;
+
+ serdes->group_count = device_get_child_node_count(dev);
+ serdes->groups = devm_kcalloc(dev, serdes->group_count,
+ sizeof(*serdes->groups), GFP_KERNEL);
+ if (!serdes->groups)
+ return -ENOMEM;
+
+ device_for_each_child_node(dev, group_node) {
+ ret = lynx_probe_group(serdes, &serdes->groups[i++],
+ group_node, !compat);
+ if (ret)
+ return ret;
+ }
+ WARN_ON(i != serdes->group_count);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ ret = PTR_ERR_OR_ZERO(provider);
+ if (ret)
+ dev_err_probe(dev, ret, "could not register phy provider\n");
+ else
+ dev_info(dev, "probed with %u lanes and %u groups\n",
+ serdes->cfg->lanes, serdes->group_count);
+ return ret;
+}
+
+/*
+ * These are common helpers for the PCCRs found on (most) Layerscape SoCs.
+ * There is an earlier layout used on most T-series SoCs, as well as the
+ * LS1020A/21A/22A.
+ */
+
+static int lynx_ls_pccr_params(const struct lynx_mode *mode, u32 *off,
+ u32 *shift, u32 *mask)
+{
+ if (mode->protos & PROTO_MASK(SGMII)) {
+ *off = LS_PCCRa(0x8);
+ *mask = PCCR8_SGMIIa_MASK;
+ *shift = PCCR8_SGMIIa_SHIFT(mode->idx);
+ } else if (mode->protos & PROTO_MASK(QSGMII)) {
+ *off = LS_PCCRa(0x9);
+ *mask = PCCR9_QSGMIIa_MASK;
+ *shift = PCCR9_QSGMIIa_SHIFT(mode->idx);
+ } else if (mode->protos & PROTO_MASK(XFI)) {
+ *off = LS_PCCRa(0xB);
+ *mask = PCCRB_XFIa_MASK;
+ *shift = PCCRB_XFIa_SHIFT(mode->idx);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool lynx_ls_mode_conflict(struct lynx_priv *serdes,
+ const struct lynx_mode *mode)
+{
+ u32 off, shift, mask;
+
+ if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
+ return true;
+
+ return (lynx_read(serdes, off) >> shift) & mask;
+}
+
+static void lynx_ls_mode_apply(struct lynx_priv *serdes,
+ const struct lynx_mode *mode,
+ enum lynx_protocol proto)
+{
+ u32 pccr, off, shift, mask;
+
+ if (WARN_ON_ONCE(proto != LYNX_PROTO_NONE &&
+ !(mode->protos & BIT(proto))))
+ return;
+ if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
+ return;
+
+ dev_dbg(serdes->dev, "applying %s to PCCR%X.%s%c_CFG\n",
+ lynx_proto_str[proto], mode->pccr,
+ lynx_proto_str[__ffs(mode->protos)], 'A' + mode->idx);
+
+ pccr = lynx_read(serdes, off);
+ pccr &= ~(mask << shift);
+ if (proto != LYNX_PROTO_NONE)
+ pccr |= mode->cfg << shift;
+
+ if (proto == LYNX_PROTO_1000BASEKX)
+ pccr |= PCCR8_SGMIIa_KX << shift;
+ lynx_write(serdes, pccr, off);
+
+ if (mode->protos & PROTO_MASK(SGMII)) {
+ u32 cr1 = lynx_read(serdes, LS_SGMIIaCR1(mode->idx));
+
+ cr1 &= ~SGMIIaCR1_SGPCS_EN;
+ cr1 |= proto == LYNX_PROTO_NONE ? 0 : SGMIIaCR1_SGPCS_EN;
+ lynx_write(serdes, cr1, LS_SGMIIaCR1(mode->idx));
+ }
+}
+
+static int lynx_ls_mode_init(struct lynx_priv *serdes, struct lynx_mode *mode,
+ int type)
+{
+ u32 max = 0, off, shift, mask;
+
+ if (mode->pccr >= 0x10) {
+ dev_err(serdes->dev, "PCCR index %u too large\n", mode->pccr);
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case PHY_TYPE_2500BASEX:
+ mode->protos = PROTO_MASK(SGMII25);
+ fallthrough;
+ case PHY_TYPE_SGMII:
+ max = 8;
+ mode->protos |= PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX);
+ break;
+ case PHY_TYPE_QSGMII:
+ max = 4;
+ mode->protos = PROTO_MASK(QSGMII);
+ break;
+ case PHY_TYPE_10GBASER:
+ max = 8;
+ mode->protos = PROTO_MASK(XFI) | PROTO_MASK(10GKR);
+ break;
+ default:
+ dev_err(serdes->dev, "unknown mode type %d\n", type);
+ return -EINVAL;
+ }
+
+ if (mode->idx >= max) {
+ dev_err(serdes->dev, "%s index %u too large\n",
+ lynx_proto_str[__ffs(mode->protos)], mode->idx);
+ return -EINVAL;
+ }
+
+ if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
+ return -EINVAL;
+
+ if (!mode->cfg || mode->cfg & ~mask) {
+ dev_err(serdes->dev, "bad value %x for %s%c_CFG\n",
+ mode->cfg, lynx_proto_str[__ffs(mode->protos)],
+ 'A' + mode->idx);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct lynx_cfg ls1046a_cfg = {
+ .lanes = 4,
+ .endian = REGMAP_ENDIAN_BIG,
+ .mode_conflict = lynx_ls_mode_conflict,
+ .mode_apply = lynx_ls_mode_apply,
+ .mode_init = lynx_ls_mode_init,
+};
+
+static const struct lynx_cfg ls1088a_cfg = {
+ .lanes = 4,
+ .endian = REGMAP_ENDIAN_LITTLE,
+ .mode_conflict = lynx_ls_mode_conflict,
+ .mode_apply = lynx_ls_mode_apply,
+ .mode_init = lynx_ls_mode_init,
+};
+
+static const struct of_device_id lynx_of_match[] = {
+ { .compatible = "fsl,ls1046a-serdes", .data = &ls1046a_cfg },
+ { .compatible = "fsl,ls1088a-serdes", .data = &ls1088a_cfg },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lynx_of_match);
+
+static struct platform_driver lynx_driver = {
+ .probe = lynx_probe,
+ .driver = {
+ .name = "lynx_10g",
+ .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] 64+ messages in thread* Re: [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver
2023-04-13 16:05 ` [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
@ 2023-05-08 9:22 ` Vinod Koul
2023-05-08 15:28 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vinod Koul @ 2023-05-08 9:22 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Jonathan Corbet, linux-doc, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-arm-kernel
On 13-04-23, 12:05, 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. It is present on QorIQ
> T-Series and Layerscape processors. 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 and LS1088A.
> Additionally, I have only added support for Ethernet protocols. There is
> not a great need for dynamic reconfiguration for other protocols (except
> perhaps for M.2 cards), 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. Most of these protocols should work "as-is", but
> 10GBASE-KR will need PCS support for link training.
>
> 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.
>
> 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). The earlier P-series processors appear to be similar, but
> have a different overall register layout (using "banks" instead of
> separate SerDes). Perhaps this those use a "5G Lynx SerDes."
>
> Note that while I have used FIELD_GET/FIELD_PREP where possible, these
> macros require const values for the field. This is incompatible with
> dynamicly-generated fields, such as when the field is determined by a
> variable. In these cases, I have used traditional shift/mask techniques.
>
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
>
> Changes in v14:
> - Add note about (lack of) use of FIELD_GET/PREP
>
> Changes in v10:
> - Fix debugging print with incorrect error variable
>
> Changes in v9:
> - Split off clock "driver" into its own patch to allow for better
> review.
> - Add ability to defer lane initialization to phy_init. This allows
> for easier transitioning between firmware-managed serdes and Linux-
> managed serdes, as the consumer (such as dpaa2, which knows what the
> firmware is doing) has the last say on who gets control.
> - phy-type -> fsl,phy
>
> Changes in v8:
> - Remove unused variable from lynx_ls_mode_init
>
> Changes in v7:
> - Break out call order into generic documentation
> - Refuse to switch "major" protocols
> - Update Kconfig to reflect restrictions
> - Remove set/clear of "pcs reset" bit, since it doesn't seem to fix
> anything.
>
> Changes in v6:
> - Update MAINTAINERS to include new files
> - Include bitfield.h and slab.h to allow compilation on non-arm64
> arches.
> - Depend on COMMON_CLK and either layerscape/ppc
>
> Changes in v5:
> - Remove references to PHY_INTERFACE_MODE_1000BASEKX to allow this
> series to be applied directly to linux/master.
> - Add fsl,lynx-10g.h to MAINTAINERS
>
> Changes in v4:
> - Rework all debug statements to remove use of __func__. Additional
> information has been provided as necessary.
> - Consider alternative parent rates in round_rate and not in set_rate.
> Trying to modify out parent's rate in set_rate will deadlock.
> - Explicitly perform a stop/reset sequence in set_rate. This way we
> always ensure that the PLL is properly stopped.
> - Set the power-down bit when disabling the PLL. We can do this now that
> enable/disable aren't abused during the set rate sequence.
> - Fix typos in QSGMII_OFFSET and XFI_OFFSET
> - Rename LNmTECR0_TEQ_TYPE_PRE to LNmTECR0_TEQ_TYPE_POST to better
> reflect its function (adding post-cursor equalization).
> - Use of_clk_hw_onecell_get instead of a custom function.
> - Return struct clks from lynx_clks_init instead of embedding lynx_clk
> in lynx_priv.
> - Rework PCCR helper functions; T-series SoCs differ from Layerscape SoCs
> primarily in the layout and offset of the PCCRs. This will help bring a
> cleaner abstraction layer. The caps have been removed, since this handles the
> only current usage.
> - Convert to use new binding format. As a result of this, we no longer need to
> have protocols for PCIe or SATA. Additionally, modes now live in lynx_group
> instead of lynx_priv.
> - Remove teq from lynx_proto_params, since it can be determined from
> preq_ratio/postq_ratio.
> - Fix an early return from lynx_set_mode not releasing serdes->lock.
> - Rename lynx_priv.conf to .cfg, since I kept mistyping it.
>
> Changes in v3:
> - Rename remaining references to QorIQ SerDes to Lynx 10G
> - Fix PLL enable sequence by waiting for our reset request to be cleared
> before continuing. Do the same for the lock, even though it isn't as
> critical. Because we will delay for 1.5ms on average, use prepare
> instead of enable so we can sleep.
> - Document the status of each protocol
> - Fix offset of several bitfields in RECR0
> - Take into account PLLRST_B, SDRST_B, and SDEN when considering whether
> a PLL is "enabled."
> - Only power off unused lanes.
> - Split mode lane mask into first/last lane (like group)
> - Read modes from device tree
> - Use caps to determine whether KX/KR are supported
> - Move modes to lynx_priv
> - Ensure that the protocol controller is not already in-use when we try
> to configure a new mode. This should only occur if the device tree is
> misconfigured (e.g. when QSGMII is selected on two lanes but there is
> only one QSGMII controller).
> - Split PLL drivers off into their own file
> - Add clock for "ext_dly" instead of writing the bit directly (and
> racing with any clock code).
> - Use kasprintf instead of open-coding the snprintf dance
> - Support 1000BASE-KX in lynx_lookup_proto. This still requires PCS
> support, so nothing is truly "enabled" yet.
>
> Changes in v2:
> - Rename driver to Lynx 10G (etc.)
> - Fix not clearing group->pll after disabling it
> - Support 1 and 2 phy-cells
> - Power off lanes during probe
> - Clear SGMIIaCR1_PCS_EN during probe
> - Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
> - Handle 1000BASE-KX in lynx_proto_mode_prep
>
> Documentation/driver-api/phy/index.rst | 1 +
> Documentation/driver-api/phy/lynx_10g.rst | 58 +
> MAINTAINERS | 2 +
> drivers/phy/freescale/Kconfig | 18 +-
> drivers/phy/freescale/Makefile | 1 +
> drivers/phy/freescale/phy-fsl-lynx-10g.c | 1224 +++++++++++++++++++++
> 6 files changed, 1303 insertions(+), 1 deletion(-)
> create mode 100644 Documentation/driver-api/phy/lynx_10g.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..c9b7a4698dab 100644
> --- a/Documentation/driver-api/phy/index.rst
> +++ b/Documentation/driver-api/phy/index.rst
> @@ -7,6 +7,7 @@ Generic PHY Framework
> .. toctree::
>
> phy
> + lynx_10g
> samsung-usb2
>
> .. only:: subproject and html
> diff --git a/Documentation/driver-api/phy/lynx_10g.rst b/Documentation/driver-api/phy/lynx_10g.rst
> new file mode 100644
> index 000000000000..17f9a9580e24
> --- /dev/null
> +++ b/Documentation/driver-api/phy/lynx_10g.rst
> @@ -0,0 +1,58 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===========================
> +Lynx 10G Phy (QorIQ SerDes)
> +===========================
> +
> +Using this phy
> +--------------
> +
> +: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>`, containing the
> +number of lanes in each device, the endianness of the device, and the helper
> +functions to use when selecting protocol controllers. For example, the
> +configuration for the LS1046A is::
> +
> + static const struct lynx_cfg ls1046a_cfg = {
> + .lanes = 4,
> + .endian = REGMAP_ENDIAN_BIG,
> + .mode_conflict = lynx_ls_mode_conflict,
> + .mode_apply = lynx_ls_mode_apply,
> + .mode_init = lynx_ls_mode_init,
> + };
> +
> +The ``mode_`` functions will generally be common to all SoCs in a series (e.g.
> +all Layerscape SoCs or all T-series SoCs).
> +
> +In addition, you will need to add a device node as documented in
> +``Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml``. This lets the
> +driver know which lanes are available to configure.
> +
> +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>`. Add a new entry to ``lynx_proto_params``, and populate the
> +appropriate fields. Modify ``lynx_lookup_proto`` to map the :c:type:`enum
> +phy_mode <phy_mode>` to :c:type:`enum lynx_protocol <lynx_protocol>`. Finally,
> +update the ``mode_conflict``, ``mode_apply``, and ``mode_init`` helpers to
> +support your protocol.
> +
> +You may need to modify :c:func:`lynx_set_mode` in order to support your
> +protocol. 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 8da893681de6..870014ab14aa 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12198,7 +12198,9 @@ T: git https://github.com/linux-test-project/ltp.git
> LYNX 10G SERDES DRIVER
> M: Sean Anderson <sean.anderson@seco.com>
> S: Maintained
> +F: Documentation/driver-api/phy/lynx_10g.rst
> F: drivers/clk/clk-fsl-lynx-10g.c
> +F: drivers/phy/freescale/phy-fsl-lynx-10g.c
> F: include/dt-bindings/clock/fsl,lynx-10g.h
> F: include/linux/phy/lynx-10g.h
>
> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> index 5d461232276f..6bebe00f5889 100644
> --- a/drivers/phy/freescale/Kconfig
> +++ b/drivers/phy/freescale/Kconfig
> @@ -49,7 +49,23 @@ config PHY_FSL_LYNX_28G
> Only useful for a restricted set of Ethernet protocols.
>
> config PHY_FSL_LYNX_10G
> - tristate
> + tristate "Freescale QorIQ Lynx 10G SerDes support"
> depends on COMMON_CLK
> depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
> + 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
> + some modes (such as 2.5G SGMII or 1000BASE-KX), or clock setups (as
> + only as subset of clock configurations are supported by the RCW).
> + 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-fsl-lynx-10g and clk-fsl-lynx-10g.
> diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
> index cedb328bc4d2..32ad795be7c6 100644
> --- a/drivers/phy/freescale/Makefile
> +++ b/drivers/phy/freescale/Makefile
> @@ -3,4 +3,5 @@ obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
> obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.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..880f718b387f
> --- /dev/null
> +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
> @@ -0,0 +1,1224 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
> + *
> + * This driver is for the Lynx 10G phys found on many QorIQ devices, including
> + * the Layerscape series.
> + */
> +
> +#include <dt-bindings/phy/phy.h>
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy.h>
> +#include <linux/phy/lynx-10g.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +
> +#define TCALCR 0x90
> +#define TCALCR1 0x94
> +#define RCALCR 0xa0
> +#define RCALCR1 0xa4
> +
> +#define CALCR_CALRST_B BIT(27)
> +
> +#define LS_PCCR_BASE 0x200
> +#define PCCR_STRIDE 0x4
> +
> +#define LS_PCCRa(a) (LS_PCCR_BASE + (a) * PCCR_STRIDE)
> +
> +#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_RXEQ_BST BIT(28)
> +#define LNmRECR0_GK2OVD GENMASK(27, 24)
> +#define LNmRECR0_GK3OVD GENMASK(19, 16)
> +#define LNmRECR0_GK2OVD_EN BIT(15)
> +#define LNmRECR0_GK3OVD_EN BIT(14)
> +#define LNmRECR0_OSETOVD_EN BIT(13)
> +#define LNmRECR0_BASE_WAND GENMASK(11, 10)
> +#define LNmRECR0_OSETOVD GENMASK(6, 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_POST 0b01
> +#define LNmTECR0_TEQ_TYPE_BOTH 0b10
> +
> +#define LNmTTLCR0_FLT_SEL GENMASK(29, 24)
> +
> +#define LS_SGMII_BASE 0x1800
> +#define LS_QSGMII_BASE 0x1880
> +#define LS_XFI_BASE 0x1980
> +
> +#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 LS_SGMIIaCR1(a) PCSa(a, LS_SGMII_BASE, 1)
> +#define SGMIIaCR1_SGPCS_EN BIT(11)
> +
> +enum lynx_protocol {
> + LYNX_PROTO_NONE = 0,
> + LYNX_PROTO_SGMII,
> + LYNX_PROTO_SGMII25, /* Not tested */
> + LYNX_PROTO_1000BASEKX, /* Not tested */
> + LYNX_PROTO_QSGMII, /* Not tested */
> + LYNX_PROTO_XFI,
> + LYNX_PROTO_10GKR, /* Link training unimplemented */
> + 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",
> +};
> +
> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
> +
> +/**
> + * 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
> + * @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 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,
> + .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,
> + .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,
> + .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,
> + .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,
> + .postq_ratio = 0b00011,
> + .adpt_eq = 0b110000,
> + .amp_red = 0b000111,
> + },
> + [LYNX_PROTO_10GKR] = {
> + .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,
> + .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
> + * @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 pccr;
> + u8 idx;
> + u8 cfg;
> +};
> +
> +static_assert(LYNX_PROTO_LAST - 1 <=
> + sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
> +
> +struct lynx_priv;
> +
> +/**
> + * struct lynx_cfg - Configuration for a particular serdes
> + * @lanes: Number of lanes
> + * @endian: Endianness of the registers
> + * @mode_conflict: Determine whether a protocol controller is already in use
> + * (by another group).
> + * @mode_apply: Apply a given protocol. This includes programming the
> + * appropriate config into the PCCR, as well as enabling/disabling
> + * any other registers (such as the enabling MDIO access).
> + * %LYNX_PROTO_NONE may be used to clear any associated registers.
> + * @mode_init: Finish initializing a mode. All fields are filled in except for
> + * protos. Type is one of PHY_TYPE_*. mode->protos should be filled
> + * in, and the other fields should be sanity-checked.
> + */
> +struct lynx_cfg {
> + unsigned int lanes;
> + enum regmap_endian endian;
> + bool (*mode_conflict)(struct lynx_priv *serdes,
> + const struct lynx_mode *mode);
> + void (*mode_apply)(struct lynx_priv *serdes,
> + const struct lynx_mode *mode,
> + enum lynx_protocol proto);
> + int (*mode_init)(struct lynx_priv *serdes, struct lynx_mode *mode,
> + int type);
> +};
> +
> +/**
> + * struct lynx_group - Driver data for a group of lanes
> + * @serdes: The parent serdes
> + * @pll: The currently-used pll
> + * @ex_dly: The ex_dly clock, if used
> + * @modes: Valid protocol controller configurations
> + * @mode_count: Number of modes in @modes
> + * @first_lane: The first lane in the group
> + * @last_lane: The last lane in the group
> + * @proto: The currently-configured protocol
> + * @initialized: Whether the complete state of @modes has been set
> + * @prots: The protocol set up by the RCW
> + */
> +struct lynx_group {
> + struct lynx_priv *serdes;
> + struct clk *pll, *ex_dly;
> + const struct lynx_mode *modes;
> + size_t mode_count;
> + unsigned int first_lane;
> + unsigned int last_lane;
> + enum lynx_protocol proto;
> + bool initialized;
> + u8 prots;
> +};
> +
> +/**
> + * 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.
> + * @dev: The serdes device
> + * @regmap: The backing regmap
> + * @cfg: SoC-specific configuration
> + * @plls: The PLLs
> + * @ex_dlys: The "ex_dly" clocks
> + * @groups: Groups in the serdes
> + * @group_count: Number of groups in @groups
> + * @used_lanes: Bitmap of the lanes currently used by phys
> + */
> +struct lynx_priv {
> + struct mutex lock;
> + struct device *dev;
> + struct regmap *regmap;
> + const struct lynx_cfg *cfg;
> + struct clk *plls[2], *ex_dlys[2];
> + struct lynx_group *groups;
> + unsigned int group_count;
> + unsigned int used_lanes;
> +};
> +
> +static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
> +{
> + unsigned int ret = 0;
> +
> + WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
> + dev_vdbg(serdes->dev, "%.8x <= %.8x\n", ret, reg);
> + return ret;
> +}
> +
> +static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
> +{
> + dev_vdbg(serdes->dev, "%.8x => %.8x\n", val, reg);
> + WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
> +}
> +
> +/*
> + * 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_group(struct lynx_group *group)
> +{
> + int i;
> +
> + for_each_lane_reverse(i, group) {
> + u32 gcr0 = lynx_read(group->serdes, LNmGCR0(i));
> +
> + gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
> + gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
> + lynx_write(group->serdes, gcr0, LNmGCR0(i));
> + }
> +}
> +
> +static int lynx_power_off(struct phy *phy)
> +{
> + lynx_power_off_group(phy_get_drvdata(phy));
> + return 0;
> +}
> +
> +/**
> + * 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);
> +}
> +
> +/**
> + * 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;
> +
> + for (i = 0; i < group->mode_count; i++) {
> + const struct lynx_mode *mode = &group->modes[i];
> +
> + if (BIT(proto) & mode->protos)
> + return mode;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * lynx_init_late() - Initialize group modes after probe()
> + * @group: The group of lanes to initialize
> + *
> + * Disable all modes for a group, taking care not to disable other groups'
> + * current modes. This ensures that whenever we select a mode, nothing else is
> + * interfering. Then, turn off the group.
> + *
> + * Return: 0 on success, or -%ENOMEM
> + */
> +static int lynx_init_late(struct lynx_group *group)
> +{
> + int i, j;
> + struct lynx_priv *serdes = group->serdes;
> + const struct lynx_mode **modes;
> +
> + modes = kcalloc(serdes->group_count, sizeof(*modes), GFP_KERNEL);
> + if (!modes)
> + return -ENOMEM;
> +
> + for (i = 0; i < serdes->group_count; i++)
> + modes[i] = lynx_lookup_mode(&serdes->groups[i],
> + serdes->groups[i].proto);
> +
> + for (i = 0; i < group->mode_count; i++) {
> + for (j = 0; j < serdes->group_count; j++) {
> + if (!modes[j])
> + continue;
> +
> + if (group->modes[i].pccr == modes[j]->pccr &&
> + group->modes[i].idx == modes[j]->idx)
> + goto skip;
> + }
> +
> + serdes->cfg->mode_apply(serdes, &group->modes[i],
> + LYNX_PROTO_NONE);
> +skip: ;
> + }
> +
> + kfree(modes);
> + lynx_power_off_group(group);
> + group->initialized = true;
> + return 0;
> +}
> +
> +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 {
> + if (!group->initialized)
> + ret = lynx_init_late(group);
> +
> + if (!ret)
> + 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->ex_dly);
> + group->ex_dly = NULL;
> +
> + 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;
> +}
> +
> +/**
> + * 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;
> + }
> + default:
> + return LYNX_PROTO_NONE;
> + }
> +}
> +
> +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;
> +
> + /*
> + * FIXME: At the moment we don't support switching between major
> + * protocols. From what I can tell, the serdes is working fine, but
> + * something goes wrong in the PCS.
> + */
> + if (lynx_proto_params[proto].prots != group->prots)
> + return -EINVAL;
> +
> + mode = lynx_lookup_mode(group, proto);
> + if (!mode)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +#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, teq;
> + 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;
> + }
> + }
> +
> + mutex_lock(&serdes->lock);
> + if (serdes->cfg->mode_conflict(serdes, new_mode)) {
> + dev_dbg(&phy->dev, "%s%c already in use\n",
> + lynx_proto_str[__ffs(new_mode->protos)],
> + 'A' + new_mode->idx);
> + ret = -EBUSY;
> + goto out;
> + }
what are the cases that you envision to have a mode_conflict?
> +
> + clk_disable_unprepare(group->ex_dly);
> + group->ex_dly = NULL;
> +
> + 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->plls); pll++) {
> + struct clk *clk = serdes->plls[pll];
> + 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 (error=%lu)\n", pll,
> + rate, error);
> + /* Accept up to 100ppm deviation */
> + if (error && params->frate_khz / error < 10000)
> + continue;
> +
> + if (!clk_set_rate_exclusive(clk, rate))
> + goto got_pll;
> + /*
> + * Someone else got a different rate first (or there was some
> + * other error)
> + */
> + }
> +
> + /* If neither PLL has the right rate, try setting it */
> + for (pll = 0; pll < 2; pll++) {
> + ret = clk_set_rate_exclusive(serdes->plls[pll],
> + params->frate_khz);
> + if (!ret)
> + goto got_pll;
> + }
> +
> + dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
> + params->frate_khz);
> + goto out;
> +
> +got_pll:
> + group->pll = serdes->plls[pll];
> + ret = clk_prepare_enable(group->pll);
> + if (ret)
> + goto out;
> +
> + 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_RXEQ_BST | LNmRECR0_BASE_WAND;
> + recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
> + recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
> + recr0_mask |= LNmRECR0_OSETOVD_EN | 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;
> + teq = LNmTECR0_TEQ_TYPE_NONE;
> + if (params->postq_ratio) {
> + teq = LNmTECR0_TEQ_TYPE_POST;
> + tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
> + tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
> + }
> + if (params->preq_ratio) {
> + teq = LNmTECR0_TEQ_TYPE_BOTH;
> + tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
> + tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
> + }
> + tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, teq);
> + tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
> + tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
> +
> + for_each_lane(i, group) {
> + tmp = lynx_read(serdes, LNmGCR0(i));
> + tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
> + lynx_write(serdes, tmp, LNmGCR0(i));
> + }
> +
> + ndelay(50);
> +
> + /* Disable the old controller */
> + if (old_mode)
> + serdes->cfg->mode_apply(serdes, old_mode, LYNX_PROTO_NONE);
> +
> + for_each_lane(i, group) {
> + tmp = lynx_read(serdes, LNmGCR0(i));
> + 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);
> +
> + 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));
> + }
> +
> + /* Enable the new controller */
> + serdes->cfg->mode_apply(serdes, new_mode, proto);
> + if (proto == LYNX_PROTO_1000BASEKX) {
> + group->ex_dly = serdes->ex_dlys[pll];
> + /* This should never fail since it's from our internal driver */
> + WARN_ON_ONCE(clk_prepare_enable(group->ex_dly));
> + }
> + 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);
> +
> +out:
> + mutex_unlock(&serdes->lock);
> + return ret;
> +}
> +
> +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,
> + .owner = THIS_MODULE,
> +};
> +
> +static int lynx_read_u32(struct device *dev, struct fwnode_handle *fwnode,
> + const char *prop, u32 *val)
> +{
> + int ret;
> +
> + ret = fwnode_property_read_u32(fwnode, prop, val);
> + if (ret)
> + dev_err(dev, "could not read %s from %pfwP: %d\n", prop,
> + fwnode, ret);
> + return ret;
> +}
> +
> +static int lynx_probe_group(struct lynx_priv *serdes, struct lynx_group *group,
> + struct fwnode_handle *fwnode, bool initialize)
> +{
> + int i, lane_count, ret;
> + struct device *dev = serdes->dev;
> + struct fwnode_handle *mode_node;
> + struct lynx_mode *modes;
> + struct phy *phy;
> + u32 *lanes = NULL;
> +
> + group->serdes = serdes;
> +
> + lane_count = fwnode_property_count_u32(fwnode, "reg");
> + if (lane_count < 0) {
> + dev_err(dev, "could not read %s from %pfwP: %d\n",
> + "reg", fwnode, lane_count);
> + return lane_count;
> + }
> +
> + lanes = kcalloc(lane_count, sizeof(*lanes), GFP_KERNEL);
> + if (!lanes)
> + return -ENOMEM;
> +
> + ret = fwnode_property_read_u32_array(fwnode, "reg", lanes, lane_count);
> + if (ret) {
> + dev_err(dev, "could not read %s from %pfwP: %d\n",
> + "reg", fwnode, ret);
> + goto out;
> + }
> +
> + group->first_lane = lanes[0];
> + group->last_lane = lanes[lane_count - 1];
> + for (i = 0; i < lane_count; i++) {
> + u32 prots, gcr0;
> +
> + if (lanes[i] > serdes->cfg->lanes) {
> + ret = -EINVAL;
> + dev_err(dev, "lane %d not in range 0 to %u\n",
> + i, serdes->cfg->lanes);
> + goto out;
> + }
> +
> + if (lanes[i] != group->first_lane +
> + i * !!(group->last_lane - group->first_lane)) {
> + ret = -EINVAL;
> + dev_err(dev, "lane %d is not monotonic\n", i);
> + goto out;
> + }
> +
> + gcr0 = lynx_read(serdes, LNmGCR0(lanes[i]));
> + prots = FIELD_GET(LNmGCR0_PROTS, gcr0);
> + if (i && group->prots != prots) {
> + ret = -EIO;
> + dev_err(dev, "lane %d protocol does not match lane 0\n",
> + lanes[i]);
> + goto out;
> + }
> + group->prots = prots;
> + }
> +
> + fwnode_for_each_child_node(fwnode, mode_node)
> + group->mode_count++;
> +
> + modes = devm_kcalloc(dev, group->mode_count, sizeof(*group->modes),
> + GFP_KERNEL);
> + if (!modes) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + i = 0;
> + fwnode_for_each_child_node(fwnode, mode_node) {
> + struct lynx_mode *mode = &modes[i++];
> + u32 val;
> +
> + ret = lynx_read_u32(dev, mode_node, "fsl,pccr", &val);
> + if (ret)
> + goto out;
> + mode->pccr = val;
> +
> + ret = lynx_read_u32(dev, mode_node, "fsl,index", &val);
> + if (ret)
> + goto out;
> + mode->idx = val;
> +
> + ret = lynx_read_u32(dev, mode_node, "fsl,cfg", &val);
> + if (ret)
> + goto out;
> + mode->cfg = val;
> +
> + ret = lynx_read_u32(dev, mode_node, "fsl,type", &val);
> + if (ret)
> + goto out;
> +
> + ret = serdes->cfg->mode_init(serdes, mode, val);
> + if (ret)
> + goto out;
> +
> + dev_dbg(dev, "mode PCCR%X.%s%c_CFG=%x on lanes %u to %u\n",
> + mode->pccr, lynx_proto_str[__ffs(mode->protos)],
> + 'A' + mode->idx, mode->cfg, group->first_lane,
> + group->last_lane);
> + }
> +
> + WARN_ON(i != group->mode_count);
> + group->modes = modes;
> +
> + if (initialize) {
> + /* Deselect anything configured by the RCW/bootloader */
> + for (i = 0; i < group->mode_count; i++)
> + serdes->cfg->mode_apply(serdes, &group->modes[i],
> + LYNX_PROTO_NONE);
> +
> + /* Disable the lanes for now */
> + lynx_power_off_group(group);
> + group->initialized = true;
> + }
> +
> + phy = devm_phy_create(dev, to_of_node(fwnode), &lynx_phy_ops);
> + ret = PTR_ERR_OR_ZERO(phy);
> + if (ret)
> + dev_err_probe(dev, ret, "could not create phy\n");
> + else
> + phy_set_drvdata(phy, group);
> +
> +out:
> + kfree(lanes);
> + return ret;
> +}
> +
> +static int lynx_probe(struct platform_device *pdev)
> +{
> + bool compat;
> + int ret, i = 0;
> + struct device *dev = &pdev->dev;
> + struct fwnode_handle *group_node;
> + struct lynx_priv *serdes;
> + struct phy_provider *provider;
> + struct regmap_config regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .disable_locking = true,
> + };
> + struct resource *res;
> + void __iomem *base;
> +
> + serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
> + if (!serdes)
> + return -ENOMEM;
> +
> + serdes->dev = dev;
> + platform_set_drvdata(pdev, serdes);
> + mutex_init(&serdes->lock);
> + serdes->cfg = device_get_match_data(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;
> + }
> +
> + regmap_config.val_format_endian = serdes->cfg->endian;
> + regmap_config.max_register = res->end - res->start;
> + serdes->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
> + if (IS_ERR(serdes->regmap)) {
> + ret = PTR_ERR(serdes->regmap);
> + dev_err_probe(dev, ret, "could not create regmap\n");
> + return ret;
> + }
> +
> + compat = device_property_present(dev, "fsl,unused-lanes-reserved");
> + ret = lynx_clks_init(dev, serdes->regmap, serdes->plls,
> + serdes->ex_dlys, compat);
> + if (ret)
> + return ret;
> +
> + serdes->group_count = device_get_child_node_count(dev);
> + serdes->groups = devm_kcalloc(dev, serdes->group_count,
> + sizeof(*serdes->groups), GFP_KERNEL);
> + if (!serdes->groups)
> + return -ENOMEM;
> +
> + device_for_each_child_node(dev, group_node) {
> + ret = lynx_probe_group(serdes, &serdes->groups[i++],
> + group_node, !compat);
> + if (ret)
> + return ret;
> + }
> + WARN_ON(i != serdes->group_count);
> +
> + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> + ret = PTR_ERR_OR_ZERO(provider);
> + if (ret)
> + dev_err_probe(dev, ret, "could not register phy provider\n");
> + else
> + dev_info(dev, "probed with %u lanes and %u groups\n",
> + serdes->cfg->lanes, serdes->group_count);
> + return ret;
> +}
> +
> +/*
> + * These are common helpers for the PCCRs found on (most) Layerscape SoCs.
> + * There is an earlier layout used on most T-series SoCs, as well as the
> + * LS1020A/21A/22A.
> + */
> +
> +static int lynx_ls_pccr_params(const struct lynx_mode *mode, u32 *off,
> + u32 *shift, u32 *mask)
> +{
> + if (mode->protos & PROTO_MASK(SGMII)) {
> + *off = LS_PCCRa(0x8);
> + *mask = PCCR8_SGMIIa_MASK;
> + *shift = PCCR8_SGMIIa_SHIFT(mode->idx);
> + } else if (mode->protos & PROTO_MASK(QSGMII)) {
> + *off = LS_PCCRa(0x9);
> + *mask = PCCR9_QSGMIIa_MASK;
> + *shift = PCCR9_QSGMIIa_SHIFT(mode->idx);
> + } else if (mode->protos & PROTO_MASK(XFI)) {
> + *off = LS_PCCRa(0xB);
> + *mask = PCCRB_XFIa_MASK;
> + *shift = PCCRB_XFIa_SHIFT(mode->idx);
> + } else {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static bool lynx_ls_mode_conflict(struct lynx_priv *serdes,
> + const struct lynx_mode *mode)
> +{
> + u32 off, shift, mask;
> +
> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
> + return true;
> +
> + return (lynx_read(serdes, off) >> shift) & mask;
> +}
> +
> +static void lynx_ls_mode_apply(struct lynx_priv *serdes,
> + const struct lynx_mode *mode,
> + enum lynx_protocol proto)
> +{
> + u32 pccr, off, shift, mask;
> +
> + if (WARN_ON_ONCE(proto != LYNX_PROTO_NONE &&
> + !(mode->protos & BIT(proto))))
> + return;
> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
> + return;
> +
> + dev_dbg(serdes->dev, "applying %s to PCCR%X.%s%c_CFG\n",
> + lynx_proto_str[proto], mode->pccr,
> + lynx_proto_str[__ffs(mode->protos)], 'A' + mode->idx);
> +
> + pccr = lynx_read(serdes, off);
> + pccr &= ~(mask << shift);
> + if (proto != LYNX_PROTO_NONE)
> + pccr |= mode->cfg << shift;
> +
> + if (proto == LYNX_PROTO_1000BASEKX)
> + pccr |= PCCR8_SGMIIa_KX << shift;
> + lynx_write(serdes, pccr, off);
> +
> + if (mode->protos & PROTO_MASK(SGMII)) {
> + u32 cr1 = lynx_read(serdes, LS_SGMIIaCR1(mode->idx));
> +
> + cr1 &= ~SGMIIaCR1_SGPCS_EN;
> + cr1 |= proto == LYNX_PROTO_NONE ? 0 : SGMIIaCR1_SGPCS_EN;
> + lynx_write(serdes, cr1, LS_SGMIIaCR1(mode->idx));
> + }
> +}
> +
> +static int lynx_ls_mode_init(struct lynx_priv *serdes, struct lynx_mode *mode,
> + int type)
> +{
> + u32 max = 0, off, shift, mask;
> +
> + if (mode->pccr >= 0x10) {
> + dev_err(serdes->dev, "PCCR index %u too large\n", mode->pccr);
> + return -EINVAL;
> + }
> +
> + switch (type) {
> + case PHY_TYPE_2500BASEX:
> + mode->protos = PROTO_MASK(SGMII25);
> + fallthrough;
> + case PHY_TYPE_SGMII:
> + max = 8;
> + mode->protos |= PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX);
> + break;
> + case PHY_TYPE_QSGMII:
> + max = 4;
> + mode->protos = PROTO_MASK(QSGMII);
> + break;
> + case PHY_TYPE_10GBASER:
> + max = 8;
> + mode->protos = PROTO_MASK(XFI) | PROTO_MASK(10GKR);
> + break;
> + default:
> + dev_err(serdes->dev, "unknown mode type %d\n", type);
> + return -EINVAL;
> + }
> +
> + if (mode->idx >= max) {
> + dev_err(serdes->dev, "%s index %u too large\n",
> + lynx_proto_str[__ffs(mode->protos)], mode->idx);
> + return -EINVAL;
> + }
> +
> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
> + return -EINVAL;
> +
> + if (!mode->cfg || mode->cfg & ~mask) {
> + dev_err(serdes->dev, "bad value %x for %s%c_CFG\n",
> + mode->cfg, lynx_proto_str[__ffs(mode->protos)],
> + 'A' + mode->idx);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static const struct lynx_cfg ls1046a_cfg = {
> + .lanes = 4,
> + .endian = REGMAP_ENDIAN_BIG,
> + .mode_conflict = lynx_ls_mode_conflict,
> + .mode_apply = lynx_ls_mode_apply,
> + .mode_init = lynx_ls_mode_init,
> +};
> +
> +static const struct lynx_cfg ls1088a_cfg = {
> + .lanes = 4,
> + .endian = REGMAP_ENDIAN_LITTLE,
> + .mode_conflict = lynx_ls_mode_conflict,
> + .mode_apply = lynx_ls_mode_apply,
> + .mode_init = lynx_ls_mode_init,
So you have cfg with mode_xxx pointing to same functions for both of the
versions you support... so question is why do this and not call the
functions directly?
> +};
> +
> +static const struct of_device_id lynx_of_match[] = {
> + { .compatible = "fsl,ls1046a-serdes", .data = &ls1046a_cfg },
> + { .compatible = "fsl,ls1088a-serdes", .data = &ls1088a_cfg },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, lynx_of_match);
> +
> +static struct platform_driver lynx_driver = {
> + .probe = lynx_probe,
> + .driver = {
> + .name = "lynx_10g",
> + .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] 64+ messages in thread* Re: [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver
2023-05-08 9:22 ` Vinod Koul
@ 2023-05-08 15:28 ` Sean Anderson
2023-05-16 13:36 ` Vinod Koul
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-08 15:28 UTC (permalink / raw)
To: Vinod Koul
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Jonathan Corbet, linux-doc, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-arm-kernel
On 5/8/23 05:22, Vinod Koul wrote:
> On 13-04-23, 12:05, 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. It is present on QorIQ
>> T-Series and Layerscape processors. 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 and LS1088A.
>> Additionally, I have only added support for Ethernet protocols. There is
>> not a great need for dynamic reconfiguration for other protocols (except
>> perhaps for M.2 cards), 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. Most of these protocols should work "as-is", but
>> 10GBASE-KR will need PCS support for link training.
>>
>> 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.
>>
>> 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). The earlier P-series processors appear to be similar, but
>> have a different overall register layout (using "banks" instead of
>> separate SerDes). Perhaps this those use a "5G Lynx SerDes."
>>
>> Note that while I have used FIELD_GET/FIELD_PREP where possible, these
>> macros require const values for the field. This is incompatible with
>> dynamicly-generated fields, such as when the field is determined by a
>> variable. In these cases, I have used traditional shift/mask techniques.
>>
>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>> ---
>>
>> Changes in v14:
>> - Add note about (lack of) use of FIELD_GET/PREP
>>
>> Changes in v10:
>> - Fix debugging print with incorrect error variable
>>
>> Changes in v9:
>> - Split off clock "driver" into its own patch to allow for better
>> review.
>> - Add ability to defer lane initialization to phy_init. This allows
>> for easier transitioning between firmware-managed serdes and Linux-
>> managed serdes, as the consumer (such as dpaa2, which knows what the
>> firmware is doing) has the last say on who gets control.
>> - phy-type -> fsl,phy
>>
>> Changes in v8:
>> - Remove unused variable from lynx_ls_mode_init
>>
>> Changes in v7:
>> - Break out call order into generic documentation
>> - Refuse to switch "major" protocols
>> - Update Kconfig to reflect restrictions
>> - Remove set/clear of "pcs reset" bit, since it doesn't seem to fix
>> anything.
>>
>> Changes in v6:
>> - Update MAINTAINERS to include new files
>> - Include bitfield.h and slab.h to allow compilation on non-arm64
>> arches.
>> - Depend on COMMON_CLK and either layerscape/ppc
>>
>> Changes in v5:
>> - Remove references to PHY_INTERFACE_MODE_1000BASEKX to allow this
>> series to be applied directly to linux/master.
>> - Add fsl,lynx-10g.h to MAINTAINERS
>>
>> Changes in v4:
>> - Rework all debug statements to remove use of __func__. Additional
>> information has been provided as necessary.
>> - Consider alternative parent rates in round_rate and not in set_rate.
>> Trying to modify out parent's rate in set_rate will deadlock.
>> - Explicitly perform a stop/reset sequence in set_rate. This way we
>> always ensure that the PLL is properly stopped.
>> - Set the power-down bit when disabling the PLL. We can do this now that
>> enable/disable aren't abused during the set rate sequence.
>> - Fix typos in QSGMII_OFFSET and XFI_OFFSET
>> - Rename LNmTECR0_TEQ_TYPE_PRE to LNmTECR0_TEQ_TYPE_POST to better
>> reflect its function (adding post-cursor equalization).
>> - Use of_clk_hw_onecell_get instead of a custom function.
>> - Return struct clks from lynx_clks_init instead of embedding lynx_clk
>> in lynx_priv.
>> - Rework PCCR helper functions; T-series SoCs differ from Layerscape SoCs
>> primarily in the layout and offset of the PCCRs. This will help bring a
>> cleaner abstraction layer. The caps have been removed, since this handles the
>> only current usage.
>> - Convert to use new binding format. As a result of this, we no longer need to
>> have protocols for PCIe or SATA. Additionally, modes now live in lynx_group
>> instead of lynx_priv.
>> - Remove teq from lynx_proto_params, since it can be determined from
>> preq_ratio/postq_ratio.
>> - Fix an early return from lynx_set_mode not releasing serdes->lock.
>> - Rename lynx_priv.conf to .cfg, since I kept mistyping it.
>>
>> Changes in v3:
>> - Rename remaining references to QorIQ SerDes to Lynx 10G
>> - Fix PLL enable sequence by waiting for our reset request to be cleared
>> before continuing. Do the same for the lock, even though it isn't as
>> critical. Because we will delay for 1.5ms on average, use prepare
>> instead of enable so we can sleep.
>> - Document the status of each protocol
>> - Fix offset of several bitfields in RECR0
>> - Take into account PLLRST_B, SDRST_B, and SDEN when considering whether
>> a PLL is "enabled."
>> - Only power off unused lanes.
>> - Split mode lane mask into first/last lane (like group)
>> - Read modes from device tree
>> - Use caps to determine whether KX/KR are supported
>> - Move modes to lynx_priv
>> - Ensure that the protocol controller is not already in-use when we try
>> to configure a new mode. This should only occur if the device tree is
>> misconfigured (e.g. when QSGMII is selected on two lanes but there is
>> only one QSGMII controller).
>> - Split PLL drivers off into their own file
>> - Add clock for "ext_dly" instead of writing the bit directly (and
>> racing with any clock code).
>> - Use kasprintf instead of open-coding the snprintf dance
>> - Support 1000BASE-KX in lynx_lookup_proto. This still requires PCS
>> support, so nothing is truly "enabled" yet.
>>
>> Changes in v2:
>> - Rename driver to Lynx 10G (etc.)
>> - Fix not clearing group->pll after disabling it
>> - Support 1 and 2 phy-cells
>> - Power off lanes during probe
>> - Clear SGMIIaCR1_PCS_EN during probe
>> - Rename LYNX_PROTO_UNKNOWN to LYNX_PROTO_NONE
>> - Handle 1000BASE-KX in lynx_proto_mode_prep
>>
>> Documentation/driver-api/phy/index.rst | 1 +
>> Documentation/driver-api/phy/lynx_10g.rst | 58 +
>> MAINTAINERS | 2 +
>> drivers/phy/freescale/Kconfig | 18 +-
>> drivers/phy/freescale/Makefile | 1 +
>> drivers/phy/freescale/phy-fsl-lynx-10g.c | 1224 +++++++++++++++++++++
>> 6 files changed, 1303 insertions(+), 1 deletion(-)
>> create mode 100644 Documentation/driver-api/phy/lynx_10g.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..c9b7a4698dab 100644
>> --- a/Documentation/driver-api/phy/index.rst
>> +++ b/Documentation/driver-api/phy/index.rst
>> @@ -7,6 +7,7 @@ Generic PHY Framework
>> .. toctree::
>>
>> phy
>> + lynx_10g
>> samsung-usb2
>>
>> .. only:: subproject and html
>> diff --git a/Documentation/driver-api/phy/lynx_10g.rst b/Documentation/driver-api/phy/lynx_10g.rst
>> new file mode 100644
>> index 000000000000..17f9a9580e24
>> --- /dev/null
>> +++ b/Documentation/driver-api/phy/lynx_10g.rst
>> @@ -0,0 +1,58 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +===========================
>> +Lynx 10G Phy (QorIQ SerDes)
>> +===========================
>> +
>> +Using this phy
>> +--------------
>> +
>> +: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>`, containing the
>> +number of lanes in each device, the endianness of the device, and the helper
>> +functions to use when selecting protocol controllers. For example, the
>> +configuration for the LS1046A is::
>> +
>> + static const struct lynx_cfg ls1046a_cfg = {
>> + .lanes = 4,
>> + .endian = REGMAP_ENDIAN_BIG,
>> + .mode_conflict = lynx_ls_mode_conflict,
>> + .mode_apply = lynx_ls_mode_apply,
>> + .mode_init = lynx_ls_mode_init,
>> + };
>> +
>> +The ``mode_`` functions will generally be common to all SoCs in a series (e.g.
>> +all Layerscape SoCs or all T-series SoCs).
>> +
>> +In addition, you will need to add a device node as documented in
>> +``Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml``. This lets the
>> +driver know which lanes are available to configure.
>> +
>> +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>`. Add a new entry to ``lynx_proto_params``, and populate the
>> +appropriate fields. Modify ``lynx_lookup_proto`` to map the :c:type:`enum
>> +phy_mode <phy_mode>` to :c:type:`enum lynx_protocol <lynx_protocol>`. Finally,
>> +update the ``mode_conflict``, ``mode_apply``, and ``mode_init`` helpers to
>> +support your protocol.
>> +
>> +You may need to modify :c:func:`lynx_set_mode` in order to support your
>> +protocol. 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 8da893681de6..870014ab14aa 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12198,7 +12198,9 @@ T: git https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2fgithub.com%2flinux%2dtest%2dproject%2fltp.git&umid=394d3091-283e-42bc-ac1a-f7e4ac9ff0b7&auth=d807158c60b7d2502abde8a2fc01f40662980862-97fd27a257b9136870bfe8eda3b1eecd6052c43a
>> LYNX 10G SERDES DRIVER
>> M: Sean Anderson <sean.anderson@seco.com>
>> S: Maintained
>> +F: Documentation/driver-api/phy/lynx_10g.rst
>> F: drivers/clk/clk-fsl-lynx-10g.c
>> +F: drivers/phy/freescale/phy-fsl-lynx-10g.c
>> F: include/dt-bindings/clock/fsl,lynx-10g.h
>> F: include/linux/phy/lynx-10g.h
>>
>> diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
>> index 5d461232276f..6bebe00f5889 100644
>> --- a/drivers/phy/freescale/Kconfig
>> +++ b/drivers/phy/freescale/Kconfig
>> @@ -49,7 +49,23 @@ config PHY_FSL_LYNX_28G
>> Only useful for a restricted set of Ethernet protocols.
>>
>> config PHY_FSL_LYNX_10G
>> - tristate
>> + tristate "Freescale QorIQ Lynx 10G SerDes support"
>> depends on COMMON_CLK
>> depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
>> + 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
>> + some modes (such as 2.5G SGMII or 1000BASE-KX), or clock setups (as
>> + only as subset of clock configurations are supported by the RCW).
>> + 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-fsl-lynx-10g and clk-fsl-lynx-10g.
>> diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
>> index cedb328bc4d2..32ad795be7c6 100644
>> --- a/drivers/phy/freescale/Makefile
>> +++ b/drivers/phy/freescale/Makefile
>> @@ -3,4 +3,5 @@ obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
>> obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.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..880f718b387f
>> --- /dev/null
>> +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
>> @@ -0,0 +1,1224 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
>> + *
>> + * This driver is for the Lynx 10G phys found on many QorIQ devices, including
>> + * the Layerscape series.
>> + */
>> +
>> +#include <dt-bindings/phy/phy.h>
>> +#include <linux/clk.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/phy.h>
>> +#include <linux/phy/lynx-10g.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/regmap.h>
>> +
>> +#define TCALCR 0x90
>> +#define TCALCR1 0x94
>> +#define RCALCR 0xa0
>> +#define RCALCR1 0xa4
>> +
>> +#define CALCR_CALRST_B BIT(27)
>> +
>> +#define LS_PCCR_BASE 0x200
>> +#define PCCR_STRIDE 0x4
>> +
>> +#define LS_PCCRa(a) (LS_PCCR_BASE + (a) * PCCR_STRIDE)
>> +
>> +#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_RXEQ_BST BIT(28)
>> +#define LNmRECR0_GK2OVD GENMASK(27, 24)
>> +#define LNmRECR0_GK3OVD GENMASK(19, 16)
>> +#define LNmRECR0_GK2OVD_EN BIT(15)
>> +#define LNmRECR0_GK3OVD_EN BIT(14)
>> +#define LNmRECR0_OSETOVD_EN BIT(13)
>> +#define LNmRECR0_BASE_WAND GENMASK(11, 10)
>> +#define LNmRECR0_OSETOVD GENMASK(6, 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_POST 0b01
>> +#define LNmTECR0_TEQ_TYPE_BOTH 0b10
>> +
>> +#define LNmTTLCR0_FLT_SEL GENMASK(29, 24)
>> +
>> +#define LS_SGMII_BASE 0x1800
>> +#define LS_QSGMII_BASE 0x1880
>> +#define LS_XFI_BASE 0x1980
>> +
>> +#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 LS_SGMIIaCR1(a) PCSa(a, LS_SGMII_BASE, 1)
>> +#define SGMIIaCR1_SGPCS_EN BIT(11)
>> +
>> +enum lynx_protocol {
>> + LYNX_PROTO_NONE = 0,
>> + LYNX_PROTO_SGMII,
>> + LYNX_PROTO_SGMII25, /* Not tested */
>> + LYNX_PROTO_1000BASEKX, /* Not tested */
>> + LYNX_PROTO_QSGMII, /* Not tested */
>> + LYNX_PROTO_XFI,
>> + LYNX_PROTO_10GKR, /* Link training unimplemented */
>> + 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",
>> +};
>> +
>> +#define PROTO_MASK(proto) BIT(LYNX_PROTO_##proto)
>> +
>> +/**
>> + * 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
>> + * @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 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,
>> + .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,
>> + .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,
>> + .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,
>> + .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,
>> + .postq_ratio = 0b00011,
>> + .adpt_eq = 0b110000,
>> + .amp_red = 0b000111,
>> + },
>> + [LYNX_PROTO_10GKR] = {
>> + .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,
>> + .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
>> + * @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 pccr;
>> + u8 idx;
>> + u8 cfg;
>> +};
>> +
>> +static_assert(LYNX_PROTO_LAST - 1 <=
>> + sizeof_field(struct lynx_mode, protos) * BITS_PER_BYTE);
>> +
>> +struct lynx_priv;
>> +
>> +/**
>> + * struct lynx_cfg - Configuration for a particular serdes
>> + * @lanes: Number of lanes
>> + * @endian: Endianness of the registers
>> + * @mode_conflict: Determine whether a protocol controller is already in use
>> + * (by another group).
>> + * @mode_apply: Apply a given protocol. This includes programming the
>> + * appropriate config into the PCCR, as well as enabling/disabling
>> + * any other registers (such as the enabling MDIO access).
>> + * %LYNX_PROTO_NONE may be used to clear any associated registers.
>> + * @mode_init: Finish initializing a mode. All fields are filled in except for
>> + * protos. Type is one of PHY_TYPE_*. mode->protos should be filled
>> + * in, and the other fields should be sanity-checked.
>> + */
>> +struct lynx_cfg {
>> + unsigned int lanes;
>> + enum regmap_endian endian;
>> + bool (*mode_conflict)(struct lynx_priv *serdes,
>> + const struct lynx_mode *mode);
>> + void (*mode_apply)(struct lynx_priv *serdes,
>> + const struct lynx_mode *mode,
>> + enum lynx_protocol proto);
>> + int (*mode_init)(struct lynx_priv *serdes, struct lynx_mode *mode,
>> + int type);
>> +};
>> +
>> +/**
>> + * struct lynx_group - Driver data for a group of lanes
>> + * @serdes: The parent serdes
>> + * @pll: The currently-used pll
>> + * @ex_dly: The ex_dly clock, if used
>> + * @modes: Valid protocol controller configurations
>> + * @mode_count: Number of modes in @modes
>> + * @first_lane: The first lane in the group
>> + * @last_lane: The last lane in the group
>> + * @proto: The currently-configured protocol
>> + * @initialized: Whether the complete state of @modes has been set
>> + * @prots: The protocol set up by the RCW
>> + */
>> +struct lynx_group {
>> + struct lynx_priv *serdes;
>> + struct clk *pll, *ex_dly;
>> + const struct lynx_mode *modes;
>> + size_t mode_count;
>> + unsigned int first_lane;
>> + unsigned int last_lane;
>> + enum lynx_protocol proto;
>> + bool initialized;
>> + u8 prots;
>> +};
>> +
>> +/**
>> + * 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.
>> + * @dev: The serdes device
>> + * @regmap: The backing regmap
>> + * @cfg: SoC-specific configuration
>> + * @plls: The PLLs
>> + * @ex_dlys: The "ex_dly" clocks
>> + * @groups: Groups in the serdes
>> + * @group_count: Number of groups in @groups
>> + * @used_lanes: Bitmap of the lanes currently used by phys
>> + */
>> +struct lynx_priv {
>> + struct mutex lock;
>> + struct device *dev;
>> + struct regmap *regmap;
>> + const struct lynx_cfg *cfg;
>> + struct clk *plls[2], *ex_dlys[2];
>> + struct lynx_group *groups;
>> + unsigned int group_count;
>> + unsigned int used_lanes;
>> +};
>> +
>> +static u32 lynx_read(struct lynx_priv *serdes, u32 reg)
>> +{
>> + unsigned int ret = 0;
>> +
>> + WARN_ON_ONCE(regmap_read(serdes->regmap, reg, &ret));
>> + dev_vdbg(serdes->dev, "%.8x <= %.8x\n", ret, reg);
>> + return ret;
>> +}
>> +
>> +static void lynx_write(struct lynx_priv *serdes, u32 val, u32 reg)
>> +{
>> + dev_vdbg(serdes->dev, "%.8x => %.8x\n", val, reg);
>> + WARN_ON_ONCE(regmap_write(serdes->regmap, reg, val));
>> +}
>> +
>> +/*
>> + * 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_group(struct lynx_group *group)
>> +{
>> + int i;
>> +
>> + for_each_lane_reverse(i, group) {
>> + u32 gcr0 = lynx_read(group->serdes, LNmGCR0(i));
>> +
>> + gcr0 |= LNmGCR0_RX_PD | LNmGCR0_TX_PD;
>> + gcr0 &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
>> + lynx_write(group->serdes, gcr0, LNmGCR0(i));
>> + }
>> +}
>> +
>> +static int lynx_power_off(struct phy *phy)
>> +{
>> + lynx_power_off_group(phy_get_drvdata(phy));
>> + return 0;
>> +}
>> +
>> +/**
>> + * 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);
>> +}
>> +
>> +/**
>> + * 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;
>> +
>> + for (i = 0; i < group->mode_count; i++) {
>> + const struct lynx_mode *mode = &group->modes[i];
>> +
>> + if (BIT(proto) & mode->protos)
>> + return mode;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/**
>> + * lynx_init_late() - Initialize group modes after probe()
>> + * @group: The group of lanes to initialize
>> + *
>> + * Disable all modes for a group, taking care not to disable other groups'
>> + * current modes. This ensures that whenever we select a mode, nothing else is
>> + * interfering. Then, turn off the group.
>> + *
>> + * Return: 0 on success, or -%ENOMEM
>> + */
>> +static int lynx_init_late(struct lynx_group *group)
>> +{
>> + int i, j;
>> + struct lynx_priv *serdes = group->serdes;
>> + const struct lynx_mode **modes;
>> +
>> + modes = kcalloc(serdes->group_count, sizeof(*modes), GFP_KERNEL);
>> + if (!modes)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < serdes->group_count; i++)
>> + modes[i] = lynx_lookup_mode(&serdes->groups[i],
>> + serdes->groups[i].proto);
>> +
>> + for (i = 0; i < group->mode_count; i++) {
>> + for (j = 0; j < serdes->group_count; j++) {
>> + if (!modes[j])
>> + continue;
>> +
>> + if (group->modes[i].pccr == modes[j]->pccr &&
>> + group->modes[i].idx == modes[j]->idx)
>> + goto skip;
>> + }
>> +
>> + serdes->cfg->mode_apply(serdes, &group->modes[i],
>> + LYNX_PROTO_NONE);
>> +skip: ;
>> + }
>> +
>> + kfree(modes);
>> + lynx_power_off_group(group);
>> + group->initialized = true;
>> + return 0;
>> +}
>> +
>> +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 {
>> + if (!group->initialized)
>> + ret = lynx_init_late(group);
>> +
>> + if (!ret)
>> + 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->ex_dly);
>> + group->ex_dly = NULL;
>> +
>> + 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;
>> +}
>> +
>> +/**
>> + * 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;
>> + }
>> + default:
>> + return LYNX_PROTO_NONE;
>> + }
>> +}
>> +
>> +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;
>> +
>> + /*
>> + * FIXME: At the moment we don't support switching between major
>> + * protocols. From what I can tell, the serdes is working fine, but
>> + * something goes wrong in the PCS.
>> + */
>> + if (lynx_proto_params[proto].prots != group->prots)
>> + return -EINVAL;
>> +
>> + mode = lynx_lookup_mode(group, proto);
>> + if (!mode)
>> + return -EINVAL;
>> +
>> + return 0;
>> +}
>> +
>> +#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, teq;
>> + 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;
>> + }
>> + }
>> +
>> + mutex_lock(&serdes->lock);
>> + if (serdes->cfg->mode_conflict(serdes, new_mode)) {
>> + dev_dbg(&phy->dev, "%s%c already in use\n",
>> + lynx_proto_str[__ffs(new_mode->protos)],
>> + 'A' + new_mode->idx);
>> + ret = -EBUSY;
>> + goto out;
>> + }
>
> what are the cases that you envision to have a mode_conflict?
On the B4860, SGMIIa can be used by either MAC1 or MAC5, depending on the value
of SGMIIA_CFG. Similarly for SGMIIb.
>> +
>> + clk_disable_unprepare(group->ex_dly);
>> + group->ex_dly = NULL;
>> +
>> + 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->plls); pll++) {
>> + struct clk *clk = serdes->plls[pll];
>> + 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 (error=%lu)\n", pll,
>> + rate, error);
>> + /* Accept up to 100ppm deviation */
>> + if (error && params->frate_khz / error < 10000)
>> + continue;
>> +
>> + if (!clk_set_rate_exclusive(clk, rate))
>> + goto got_pll;
>> + /*
>> + * Someone else got a different rate first (or there was some
>> + * other error)
>> + */
>> + }
>> +
>> + /* If neither PLL has the right rate, try setting it */
>> + for (pll = 0; pll < 2; pll++) {
>> + ret = clk_set_rate_exclusive(serdes->plls[pll],
>> + params->frate_khz);
>> + if (!ret)
>> + goto got_pll;
>> + }
>> +
>> + dev_dbg(&phy->dev, "could not get a pll at %ukHz\n",
>> + params->frate_khz);
>> + goto out;
>> +
>> +got_pll:
>> + group->pll = serdes->plls[pll];
>> + ret = clk_prepare_enable(group->pll);
>> + if (ret)
>> + goto out;
>> +
>> + 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_RXEQ_BST | LNmRECR0_BASE_WAND;
>> + recr0_mask |= LNmRECR0_GK2OVD | LNmRECR0_GK3OVD;
>> + recr0_mask |= LNmRECR0_GK2OVD_EN | LNmRECR0_GK3OVD_EN;
>> + recr0_mask |= LNmRECR0_OSETOVD_EN | 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;
>> + teq = LNmTECR0_TEQ_TYPE_NONE;
>> + if (params->postq_ratio) {
>> + teq = LNmTECR0_TEQ_TYPE_POST;
>> + tecr0 |= FIELD_PREP(LNmTECR0_SGN_POST1Q, 1);
>> + tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PST1Q, params->postq_ratio);
>> + }
>> + if (params->preq_ratio) {
>> + teq = LNmTECR0_TEQ_TYPE_BOTH;
>> + tecr0 |= FIELD_PREP(LNmTECR0_SGN_PREQ, 1);
>> + tecr0 |= FIELD_PREP(LNmTECR0_RATIO_PREQ, params->preq_ratio);
>> + }
>> + tecr0 |= FIELD_PREP(LNmTECR0_TEQ_TYPE, teq);
>> + tecr0 |= FIELD_PREP(LNmTECR0_ADPT_EQ, params->adpt_eq);
>> + tecr0 |= FIELD_PREP(LNmTECR0_AMP_RED, params->amp_red);
>> +
>> + for_each_lane(i, group) {
>> + tmp = lynx_read(serdes, LNmGCR0(i));
>> + tmp &= ~(LNmGCR0_RRST_B | LNmGCR0_TRST_B);
>> + lynx_write(serdes, tmp, LNmGCR0(i));
>> + }
>> +
>> + ndelay(50);
>> +
>> + /* Disable the old controller */
>> + if (old_mode)
>> + serdes->cfg->mode_apply(serdes, old_mode, LYNX_PROTO_NONE);
>> +
>> + for_each_lane(i, group) {
>> + tmp = lynx_read(serdes, LNmGCR0(i));
>> + 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);
>> +
>> + 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));
>> + }
>> +
>> + /* Enable the new controller */
>> + serdes->cfg->mode_apply(serdes, new_mode, proto);
>> + if (proto == LYNX_PROTO_1000BASEKX) {
>> + group->ex_dly = serdes->ex_dlys[pll];
>> + /* This should never fail since it's from our internal driver */
>> + WARN_ON_ONCE(clk_prepare_enable(group->ex_dly));
>> + }
>> + 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);
>> +
>> +out:
>> + mutex_unlock(&serdes->lock);
>> + return ret;
>> +}
>> +
>> +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,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static int lynx_read_u32(struct device *dev, struct fwnode_handle *fwnode,
>> + const char *prop, u32 *val)
>> +{
>> + int ret;
>> +
>> + ret = fwnode_property_read_u32(fwnode, prop, val);
>> + if (ret)
>> + dev_err(dev, "could not read %s from %pfwP: %d\n", prop,
>> + fwnode, ret);
>> + return ret;
>> +}
>> +
>> +static int lynx_probe_group(struct lynx_priv *serdes, struct lynx_group *group,
>> + struct fwnode_handle *fwnode, bool initialize)
>> +{
>> + int i, lane_count, ret;
>> + struct device *dev = serdes->dev;
>> + struct fwnode_handle *mode_node;
>> + struct lynx_mode *modes;
>> + struct phy *phy;
>> + u32 *lanes = NULL;
>> +
>> + group->serdes = serdes;
>> +
>> + lane_count = fwnode_property_count_u32(fwnode, "reg");
>> + if (lane_count < 0) {
>> + dev_err(dev, "could not read %s from %pfwP: %d\n",
>> + "reg", fwnode, lane_count);
>> + return lane_count;
>> + }
>> +
>> + lanes = kcalloc(lane_count, sizeof(*lanes), GFP_KERNEL);
>> + if (!lanes)
>> + return -ENOMEM;
>> +
>> + ret = fwnode_property_read_u32_array(fwnode, "reg", lanes, lane_count);
>> + if (ret) {
>> + dev_err(dev, "could not read %s from %pfwP: %d\n",
>> + "reg", fwnode, ret);
>> + goto out;
>> + }
>> +
>> + group->first_lane = lanes[0];
>> + group->last_lane = lanes[lane_count - 1];
>> + for (i = 0; i < lane_count; i++) {
>> + u32 prots, gcr0;
>> +
>> + if (lanes[i] > serdes->cfg->lanes) {
>> + ret = -EINVAL;
>> + dev_err(dev, "lane %d not in range 0 to %u\n",
>> + i, serdes->cfg->lanes);
>> + goto out;
>> + }
>> +
>> + if (lanes[i] != group->first_lane +
>> + i * !!(group->last_lane - group->first_lane)) {
>> + ret = -EINVAL;
>> + dev_err(dev, "lane %d is not monotonic\n", i);
>> + goto out;
>> + }
>> +
>> + gcr0 = lynx_read(serdes, LNmGCR0(lanes[i]));
>> + prots = FIELD_GET(LNmGCR0_PROTS, gcr0);
>> + if (i && group->prots != prots) {
>> + ret = -EIO;
>> + dev_err(dev, "lane %d protocol does not match lane 0\n",
>> + lanes[i]);
>> + goto out;
>> + }
>> + group->prots = prots;
>> + }
>> +
>> + fwnode_for_each_child_node(fwnode, mode_node)
>> + group->mode_count++;
>> +
>> + modes = devm_kcalloc(dev, group->mode_count, sizeof(*group->modes),
>> + GFP_KERNEL);
>> + if (!modes) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + i = 0;
>> + fwnode_for_each_child_node(fwnode, mode_node) {
>> + struct lynx_mode *mode = &modes[i++];
>> + u32 val;
>> +
>> + ret = lynx_read_u32(dev, mode_node, "fsl,pccr", &val);
>> + if (ret)
>> + goto out;
>> + mode->pccr = val;
>> +
>> + ret = lynx_read_u32(dev, mode_node, "fsl,index", &val);
>> + if (ret)
>> + goto out;
>> + mode->idx = val;
>> +
>> + ret = lynx_read_u32(dev, mode_node, "fsl,cfg", &val);
>> + if (ret)
>> + goto out;
>> + mode->cfg = val;
>> +
>> + ret = lynx_read_u32(dev, mode_node, "fsl,type", &val);
>> + if (ret)
>> + goto out;
>> +
>> + ret = serdes->cfg->mode_init(serdes, mode, val);
>> + if (ret)
>> + goto out;
>> +
>> + dev_dbg(dev, "mode PCCR%X.%s%c_CFG=%x on lanes %u to %u\n",
>> + mode->pccr, lynx_proto_str[__ffs(mode->protos)],
>> + 'A' + mode->idx, mode->cfg, group->first_lane,
>> + group->last_lane);
>> + }
>> +
>> + WARN_ON(i != group->mode_count);
>> + group->modes = modes;
>> +
>> + if (initialize) {
>> + /* Deselect anything configured by the RCW/bootloader */
>> + for (i = 0; i < group->mode_count; i++)
>> + serdes->cfg->mode_apply(serdes, &group->modes[i],
>> + LYNX_PROTO_NONE);
>> +
>> + /* Disable the lanes for now */
>> + lynx_power_off_group(group);
>> + group->initialized = true;
>> + }
>> +
>> + phy = devm_phy_create(dev, to_of_node(fwnode), &lynx_phy_ops);
>> + ret = PTR_ERR_OR_ZERO(phy);
>> + if (ret)
>> + dev_err_probe(dev, ret, "could not create phy\n");
>> + else
>> + phy_set_drvdata(phy, group);
>> +
>> +out:
>> + kfree(lanes);
>> + return ret;
>> +}
>> +
>> +static int lynx_probe(struct platform_device *pdev)
>> +{
>> + bool compat;
>> + int ret, i = 0;
>> + struct device *dev = &pdev->dev;
>> + struct fwnode_handle *group_node;
>> + struct lynx_priv *serdes;
>> + struct phy_provider *provider;
>> + struct regmap_config regmap_config = {
>> + .reg_bits = 32,
>> + .reg_stride = 4,
>> + .val_bits = 32,
>> + .disable_locking = true,
>> + };
>> + struct resource *res;
>> + void __iomem *base;
>> +
>> + serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
>> + if (!serdes)
>> + return -ENOMEM;
>> +
>> + serdes->dev = dev;
>> + platform_set_drvdata(pdev, serdes);
>> + mutex_init(&serdes->lock);
>> + serdes->cfg = device_get_match_data(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;
>> + }
>> +
>> + regmap_config.val_format_endian = serdes->cfg->endian;
>> + regmap_config.max_register = res->end - res->start;
>> + serdes->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
>> + if (IS_ERR(serdes->regmap)) {
>> + ret = PTR_ERR(serdes->regmap);
>> + dev_err_probe(dev, ret, "could not create regmap\n");
>> + return ret;
>> + }
>> +
>> + compat = device_property_present(dev, "fsl,unused-lanes-reserved");
>> + ret = lynx_clks_init(dev, serdes->regmap, serdes->plls,
>> + serdes->ex_dlys, compat);
>> + if (ret)
>> + return ret;
>> +
>> + serdes->group_count = device_get_child_node_count(dev);
>> + serdes->groups = devm_kcalloc(dev, serdes->group_count,
>> + sizeof(*serdes->groups), GFP_KERNEL);
>> + if (!serdes->groups)
>> + return -ENOMEM;
>> +
>> + device_for_each_child_node(dev, group_node) {
>> + ret = lynx_probe_group(serdes, &serdes->groups[i++],
>> + group_node, !compat);
>> + if (ret)
>> + return ret;
>> + }
>> + WARN_ON(i != serdes->group_count);
>> +
>> + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> + ret = PTR_ERR_OR_ZERO(provider);
>> + if (ret)
>> + dev_err_probe(dev, ret, "could not register phy provider\n");
>> + else
>> + dev_info(dev, "probed with %u lanes and %u groups\n",
>> + serdes->cfg->lanes, serdes->group_count);
>> + return ret;
>> +}
>> +
>> +/*
>> + * These are common helpers for the PCCRs found on (most) Layerscape SoCs.
>> + * There is an earlier layout used on most T-series SoCs, as well as the
>> + * LS1020A/21A/22A.
>> + */
>> +
>> +static int lynx_ls_pccr_params(const struct lynx_mode *mode, u32 *off,
>> + u32 *shift, u32 *mask)
>> +{
>> + if (mode->protos & PROTO_MASK(SGMII)) {
>> + *off = LS_PCCRa(0x8);
>> + *mask = PCCR8_SGMIIa_MASK;
>> + *shift = PCCR8_SGMIIa_SHIFT(mode->idx);
>> + } else if (mode->protos & PROTO_MASK(QSGMII)) {
>> + *off = LS_PCCRa(0x9);
>> + *mask = PCCR9_QSGMIIa_MASK;
>> + *shift = PCCR9_QSGMIIa_SHIFT(mode->idx);
>> + } else if (mode->protos & PROTO_MASK(XFI)) {
>> + *off = LS_PCCRa(0xB);
>> + *mask = PCCRB_XFIa_MASK;
>> + *shift = PCCRB_XFIa_SHIFT(mode->idx);
>> + } else {
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static bool lynx_ls_mode_conflict(struct lynx_priv *serdes,
>> + const struct lynx_mode *mode)
>> +{
>> + u32 off, shift, mask;
>> +
>> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
>> + return true;
>> +
>> + return (lynx_read(serdes, off) >> shift) & mask;
>> +}
>> +
>> +static void lynx_ls_mode_apply(struct lynx_priv *serdes,
>> + const struct lynx_mode *mode,
>> + enum lynx_protocol proto)
>> +{
>> + u32 pccr, off, shift, mask;
>> +
>> + if (WARN_ON_ONCE(proto != LYNX_PROTO_NONE &&
>> + !(mode->protos & BIT(proto))))
>> + return;
>> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
>> + return;
>> +
>> + dev_dbg(serdes->dev, "applying %s to PCCR%X.%s%c_CFG\n",
>> + lynx_proto_str[proto], mode->pccr,
>> + lynx_proto_str[__ffs(mode->protos)], 'A' + mode->idx);
>> +
>> + pccr = lynx_read(serdes, off);
>> + pccr &= ~(mask << shift);
>> + if (proto != LYNX_PROTO_NONE)
>> + pccr |= mode->cfg << shift;
>> +
>> + if (proto == LYNX_PROTO_1000BASEKX)
>> + pccr |= PCCR8_SGMIIa_KX << shift;
>> + lynx_write(serdes, pccr, off);
>> +
>> + if (mode->protos & PROTO_MASK(SGMII)) {
>> + u32 cr1 = lynx_read(serdes, LS_SGMIIaCR1(mode->idx));
>> +
>> + cr1 &= ~SGMIIaCR1_SGPCS_EN;
>> + cr1 |= proto == LYNX_PROTO_NONE ? 0 : SGMIIaCR1_SGPCS_EN;
>> + lynx_write(serdes, cr1, LS_SGMIIaCR1(mode->idx));
>> + }
>> +}
>> +
>> +static int lynx_ls_mode_init(struct lynx_priv *serdes, struct lynx_mode *mode,
>> + int type)
>> +{
>> + u32 max = 0, off, shift, mask;
>> +
>> + if (mode->pccr >= 0x10) {
>> + dev_err(serdes->dev, "PCCR index %u too large\n", mode->pccr);
>> + return -EINVAL;
>> + }
>> +
>> + switch (type) {
>> + case PHY_TYPE_2500BASEX:
>> + mode->protos = PROTO_MASK(SGMII25);
>> + fallthrough;
>> + case PHY_TYPE_SGMII:
>> + max = 8;
>> + mode->protos |= PROTO_MASK(SGMII) | PROTO_MASK(1000BASEKX);
>> + break;
>> + case PHY_TYPE_QSGMII:
>> + max = 4;
>> + mode->protos = PROTO_MASK(QSGMII);
>> + break;
>> + case PHY_TYPE_10GBASER:
>> + max = 8;
>> + mode->protos = PROTO_MASK(XFI) | PROTO_MASK(10GKR);
>> + break;
>> + default:
>> + dev_err(serdes->dev, "unknown mode type %d\n", type);
>> + return -EINVAL;
>> + }
>> +
>> + if (mode->idx >= max) {
>> + dev_err(serdes->dev, "%s index %u too large\n",
>> + lynx_proto_str[__ffs(mode->protos)], mode->idx);
>> + return -EINVAL;
>> + }
>> +
>> + if (WARN_ON_ONCE(lynx_ls_pccr_params(mode, &off, &shift, &mask)))
>> + return -EINVAL;
>> +
>> + if (!mode->cfg || mode->cfg & ~mask) {
>> + dev_err(serdes->dev, "bad value %x for %s%c_CFG\n",
>> + mode->cfg, lynx_proto_str[__ffs(mode->protos)],
>> + 'A' + mode->idx);
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct lynx_cfg ls1046a_cfg = {
>> + .lanes = 4,
>> + .endian = REGMAP_ENDIAN_BIG,
>> + .mode_conflict = lynx_ls_mode_conflict,
>> + .mode_apply = lynx_ls_mode_apply,
>> + .mode_init = lynx_ls_mode_init,
>> +};
>> +
>> +static const struct lynx_cfg ls1088a_cfg = {
>> + .lanes = 4,
>> + .endian = REGMAP_ENDIAN_LITTLE,
>> + .mode_conflict = lynx_ls_mode_conflict,
>> + .mode_apply = lynx_ls_mode_apply,
>> + .mode_init = lynx_ls_mode_init,
>
> So you have cfg with mode_xxx pointing to same functions for both of the
> versions you support... so question is why do this and not call the
> functions directly?
The intention is to make it easy to add support for T-series processors,
which have a different PCCR layout.
>> +};
>> +
>> +static const struct of_device_id lynx_of_match[] = {
>> + { .compatible = "fsl,ls1046a-serdes", .data = &ls1046a_cfg },
>> + { .compatible = "fsl,ls1088a-serdes", .data = &ls1088a_cfg },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, lynx_of_match);
>> +
>> +static struct platform_driver lynx_driver = {
>> + .probe = lynx_probe,
>> + .driver = {
>> + .name = "lynx_10g",
>> + .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] 64+ messages in thread* Re: [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver
2023-05-08 15:28 ` Sean Anderson
@ 2023-05-16 13:36 ` Vinod Koul
2023-05-16 15:12 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vinod Koul @ 2023-05-16 13:36 UTC (permalink / raw)
To: Sean Anderson
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Jonathan Corbet, linux-doc, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-arm-kernel
On 08-05-23, 11:28, Sean Anderson wrote:
> On 5/8/23 05:22, Vinod Koul wrote:
> > On 13-04-23, 12:05, Sean Anderson wrote:
> >> +static const struct lynx_cfg ls1088a_cfg = {
> >> + .lanes = 4,
> >> + .endian = REGMAP_ENDIAN_LITTLE,
> >> + .mode_conflict = lynx_ls_mode_conflict,
> >> + .mode_apply = lynx_ls_mode_apply,
> >> + .mode_init = lynx_ls_mode_init,
> >
> > So you have cfg with mode_xxx pointing to same functions for both of the
> > versions you support... so question is why do this and not call the
> > functions directly?
>
> The intention is to make it easy to add support for T-series processors,
> which have a different PCCR layout.
And when is that going to come. I would prefer this to be added when we
have users being added
--
~Vinod
^ permalink raw reply [flat|nested] 64+ messages in thread* Re: [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver
2023-05-16 13:36 ` Vinod Koul
@ 2023-05-16 15:12 ` Sean Anderson
0 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-05-16 15:12 UTC (permalink / raw)
To: Vinod Koul
Cc: Kishon Vijay Abraham I, devicetree, Krzysztof Kozlowski,
Madalin Bucur, Jonathan Corbet, linux-doc, Rob Herring,
Camelia Alexandra Groza, Bagas Sanjaya, Ioana Ciornei, linux-phy,
linuxppc-dev, linux-arm-kernel
On 5/16/23 09:36, Vinod Koul wrote:
> On 08-05-23, 11:28, Sean Anderson wrote:
>> On 5/8/23 05:22, Vinod Koul wrote:
>> > On 13-04-23, 12:05, Sean Anderson wrote:
>
>> >> +static const struct lynx_cfg ls1088a_cfg = {
>> >> + .lanes = 4,
>> >> + .endian = REGMAP_ENDIAN_LITTLE,
>> >> + .mode_conflict = lynx_ls_mode_conflict,
>> >> + .mode_apply = lynx_ls_mode_apply,
>> >> + .mode_init = lynx_ls_mode_init,
>> >
>> > So you have cfg with mode_xxx pointing to same functions for both of the
>> > versions you support... so question is why do this and not call the
>> > functions directly?
>>
>> The intention is to make it easy to add support for T-series processors,
>> which have a different PCCR layout.
>
> And when is that going to come. I would prefer this to be added when we
> have users being added
>
*shrug*
I can call them directly if you like.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* [PATCH v14 08/15] phy: lynx10g: Enable by default on Layerscape
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (6 preceding siblings ...)
2023-04-13 16:05 ` [PATCH v14 07/15] phy: fsl: Add Lynx 10G SerDes driver Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 09/15] arm64: dts: ls1046a: Add serdes nodes Sean Anderson
` (7 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
The next few patches will break ethernet if the serdes is not enabled,
so enable the serdes driver by default on Layerscape.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v10)
Changes in v10:
- New
drivers/phy/freescale/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 6bebe00f5889..b396162dc859 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -54,6 +54,7 @@ config PHY_FSL_LYNX_10G
depends on ARCH_LAYERSCAPE || PPC || COMPILE_TEST
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] 64+ messages in thread* [PATCH v14 09/15] arm64: dts: ls1046a: Add serdes nodes
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (7 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 08/15] phy: lynx10g: Enable by default on Layerscape Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 10/15] arm64: dts: ls1046ardb: Add serdes descriptions Sean Anderson
` (6 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
This adds nodes 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>
---
(no changes since v10)
Changes in v10:
- Move serdes bindings to SoC dtsi
- Add support for all (ethernet) serdes modes
- Refer to "nodes" instead of "bindings"
- Move compatible/reg first
Changes in v4:
- Convert to new bindings
Changes in v3:
- Describe modes in device tree
Changes in v2:
- Use one phy cell for SerDes1, since no lanes can be grouped
- Disable SerDes by default to prevent breaking boards inadvertently.
.../arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 111 ++++++++++++++++++
1 file changed, 111 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index a01e3cfec77f..f6361fafaef7 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -12,6 +12,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/phy/phy.h>
/ {
compatible = "fsl,ls1046a";
@@ -424,6 +425,116 @@ sfp: efuse@1e80000 {
clock-names = "sfp";
};
+ serdes1: serdes@1ea0000 {
+ compatible = "fsl,ls1046a-serdes", "fsl,lynx-10g";
+ reg = <0x0 0x1ea0000 0x0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+ status = "disabled";
+
+ /*
+ * XXX: 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!
+ */
+ serdes1_A: phy@0 {
+ #phy-cells = <0>;
+ reg = <0>;
+
+ /* SGMII.6 */
+ sgmii-0 {
+ fsl,pccr = <0x8>;
+ fsl,index = <0>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_SGMII>;
+ };
+ };
+
+ serdes1_B: phy@1 {
+ #phy-cells = <0>;
+ reg = <1>;
+
+ /* SGMII.5 */
+ sgmii-1 {
+ fsl,pccr = <0x8>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* QSGMII.6,5,10,1 */
+ qsgmii-1 {
+ fsl,pccr = <0x9>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_QSGMII>;
+ };
+
+ /* TODO: PCIe.1 */
+ };
+
+ serdes1_C: phy@2 {
+ #phy-cells = <0>;
+ reg = <2>;
+
+ /* SGMII.10 */
+ sgmii-2 {
+ fsl,pccr = <0x8>;
+ fsl,index = <2>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* XFI.10 */
+ xfi-0 {
+ fsl,pccr = <0xb>;
+ fsl,index = <0>;
+ fsl,cfg = <0x2>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+
+ serdes1_D: phy@3 {
+ #phy-cells = <0>;
+ reg = <3>;
+
+ /* SGMII.9 */
+ sgmii-3 {
+ fsl,pccr = <0x8>;
+ fsl,index = <3>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* XFI.9 */
+ xfi-1 {
+ fsl,pccr = <0xb>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+ };
+
+ serdes2: serdes@1eb0000 {
+ compatible = "fsl,ls1046a-serdes", "fsl,lynx-10g";
+ reg = <0x0 0x1eb0000 0x0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+ 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] 64+ messages in thread* [PATCH v14 10/15] arm64: dts: ls1046ardb: Add serdes descriptions
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (8 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 09/15] arm64: dts: ls1046a: Add serdes nodes Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 11/15] arm64: dts: ls1088a: Add serdes nodes Sean Anderson
` (5 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
This adds appropriate descriptions 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.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v10)
Changes in v10:
- Move serdes descriptions to SoC dtsi
- Don't use /clocks
- Use "descriptions" instead of "bindings"
- Split off defconfig change into separate patch
Changes in v9:
- Fix name of phy mode node
- phy-type -> fsl,phy
Changes in v8:
- Rename serdes phy handles to use _A, _B, etc. instead of _0, _1, etc.
This should help remind readers that the numbering corresponds to the
physical layout of the registers, and not the lane (pin) number.
Changes in v6:
- XGI.9 -> XFI.9
Changes in v4:
- Convert to new bindings
.../boot/dts/freescale/fsl-ls1046a-rdb.dts | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
index 07f6cc6e354a..0d6dcfd1630a 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb.dts
@@ -26,6 +26,24 @@ aliases {
chosen {
stdout-path = "serial0:115200n8";
};
+
+ 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";
};
&duart0 {
@@ -140,21 +158,29 @@ ethernet@e6000 {
ethernet@e8000 {
phy-handle = <&sgmii_phy1>;
phy-connection-type = "sgmii";
+ phys = <&serdes1_B>;
+ phy-names = "serdes";
};
ethernet@ea000 {
phy-handle = <&sgmii_phy2>;
phy-connection-type = "sgmii";
+ phys = <&serdes1_A>;
+ phy-names = "serdes";
};
ethernet@f0000 { /* 10GEC1 */
phy-handle = <&aqr106_phy>;
phy-connection-type = "xgmii";
+ phys = <&serdes1_D>;
+ phy-names = "serdes";
};
ethernet@f2000 { /* 10GEC2 */
phy-connection-type = "10gbase-r";
managed = "in-band-status";
+ phys = <&serdes1_C>;
+ phy-names = "serdes";
};
mdio@fc000 {
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 11/15] arm64: dts: ls1088a: Add serdes nodes
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (9 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 10/15] arm64: dts: ls1046ardb: Add serdes descriptions Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 12/15] arm64: dts: ls1088a: Prevent PCSs from probing as phys Sean Anderson
` (4 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
This adds nodes 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>
---
(no changes since v10)
Changes in v10:
- Move serdes bindings to SoC dtsi
- Add support for all (ethernet) serdes modes
- Refer to "nodes" instead of "bindings"
- Move compatible/reg first
Changes in v4:
- Convert to new bindings
Changes in v3:
- New
.../arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 126 ++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
index e5fb137ac02b..59b401daad4d 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
@@ -9,6 +9,7 @@
*/
#include <dt-bindings/clock/fsl,qoriq-clockgen.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/phy/phy.h>
#include <dt-bindings/thermal/thermal.h>
/ {
@@ -238,6 +239,131 @@ reset: syscon@1e60000 {
reg = <0x0 0x1e60000 0x0 0x10000>;
};
+ serdes1: serdes@1ea0000 {
+ compatible = "fsl,ls1088a-serdes", "fsl,lynx-10g";
+ reg = <0x0 0x1ea0000 0x0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+ status = "disabled";
+
+ /*
+ * XXX: Lane A uses pins SD1_RX3_P/N! That is, the lane
+ * numbers and pin numbers are _reversed_.
+ */
+ serdes1_A: phy@0 {
+ #phy-cells = <0>;
+ reg = <0>;
+
+ /* SG3 */
+ sgmii-0 {
+ fsl,pccr = <0x8>;
+ fsl,index = <0>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_SGMII>;
+ };
+
+ /* QSGb */
+ qsgmii-0 {
+ fsl,pccr = <0x9>;
+ fsl,index = <0>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_QSGMII>;
+ };
+ };
+
+ serdes1_B: phy@1 {
+ #phy-cells = <0>;
+ reg = <1>;
+
+ /* SG7 */
+ sgmii-1 {
+ fsl,pccr = <0x8>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_SGMII>;
+ };
+
+ /* QSGa */
+ qsgmii-1 {
+ fsl,pccr = <0x9>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_QSGMII>;
+ };
+
+ /* TODO: PCIe1 */
+ };
+
+ serdes1_C: phy@2 {
+ #phy-cells = <0>;
+ reg = <2>;
+
+ /* SG1 */
+ sgmii-2 {
+ fsl,pccr = <0x8>;
+ fsl,index = <2>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /*
+ * XFI1
+ * Table 23-1 and section 23.5.16.4 disagree;
+ * this reflects the table.
+ *
+ * fsl,cfg is documented as 1, but it is set to
+ * 2 by the RCW! This is the same as the
+ * LS1046A.
+ */
+ xfi-0 {
+ fsl,pccr = <0xb>;
+ fsl,index = <0>;
+ fsl,cfg = <0x2>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+
+ serdes1_D: phy@3 {
+ #phy-cells = <0>;
+ reg = <3>;
+
+ /* SG2 */
+ sgmii-3 {
+ fsl,pccr = <0x8>;
+ fsl,index = <3>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_2500BASEX>;
+ };
+
+ /* QSGb */
+ qsgmii-0 {
+ fsl,pccr = <0x9>;
+ fsl,index = <0>;
+ fsl,cfg = <0x2>;
+ fsl,type = <PHY_TYPE_QSGMII>;
+ };
+
+ /* XFI2 */
+ xfi-1 {
+ fsl,pccr = <0xb>;
+ fsl,index = <1>;
+ fsl,cfg = <0x1>;
+ fsl,type = <PHY_TYPE_10GBASER>;
+ };
+ };
+
+ };
+
+ serdes2: serdes@1eb0000 {
+ compatible = "fsl,ls1088a-serdes", "fsl,lynx-10g";
+ reg = <0x0 0x1eb0000 0x0 0x2000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <1>;
+ status = "disabled";
+ };
+
isc: syscon@1f70000 {
compatible = "fsl,ls1088a-isc", "syscon";
reg = <0x0 0x1f70000 0x0 0x10000>;
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 12/15] arm64: dts: ls1088a: Prevent PCSs from probing as phys
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (10 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 11/15] arm64: dts: ls1088a: Add serdes nodes Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 13/15] arm64: dts: ls1088ardb: Remove aquantia interrupt Sean Anderson
` (3 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
The internal PCSs are not always accessible during boot (such as if the
serdes has deselected the appropriate link mode). Give them appropriate
compatible strings so they don't automatically (fail to) probe as
genphys.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v8)
Changes in v8:
- New
.../arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 30 ++++++++++++-------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
index 59b401daad4d..bbc714f84577 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi
@@ -932,7 +932,8 @@ pcs_mdio1: mdio@8c07000 {
#size-cells = <0>;
status = "disabled";
- pcs1: ethernet-phy@0 {
+ pcs1: ethernet-pcs@0 {
+ compatible = "fsl,lynx-pcs";
reg = <0>;
};
};
@@ -945,7 +946,8 @@ pcs_mdio2: mdio@8c0b000 {
#size-cells = <0>;
status = "disabled";
- pcs2: ethernet-phy@0 {
+ pcs2: ethernet-pcs@0 {
+ compatible = "fsl,lynx-pcs";
reg = <0>;
};
};
@@ -958,19 +960,23 @@ pcs_mdio3: mdio@8c0f000 {
#size-cells = <0>;
status = "disabled";
- pcs3_0: ethernet-phy@0 {
+ pcs3_0: ethernet-pcs@0 {
+ compatible = "fsl,lynx-pcs";
reg = <0>;
};
- pcs3_1: ethernet-phy@1 {
+ pcs3_1: ethernet-pcs@1 {
+ compatible = "fsl,lynx-pcs";
reg = <1>;
};
- pcs3_2: ethernet-phy@2 {
+ pcs3_2: ethernet-pcs@2 {
+ compatible = "fsl,lynx-pcs";
reg = <2>;
};
- pcs3_3: ethernet-phy@3 {
+ pcs3_3: ethernet-pcs@3 {
+ compatible = "fsl,lynx-pcs";
reg = <3>;
};
};
@@ -983,19 +989,23 @@ pcs_mdio7: mdio@8c1f000 {
#size-cells = <0>;
status = "disabled";
- pcs7_0: ethernet-phy@0 {
+ pcs7_0: ethernet-pcs@0 {
+ compatible = "fsl,lynx-pcs";
reg = <0>;
};
- pcs7_1: ethernet-phy@1 {
+ pcs7_1: ethernet-pcs@1 {
+ compatible = "fsl,lynx-pcs";
reg = <1>;
};
- pcs7_2: ethernet-phy@2 {
+ pcs7_2: ethernet-pcs@2 {
+ compatible = "fsl,lynx-pcs";
reg = <2>;
};
- pcs7_3: ethernet-phy@3 {
+ pcs7_3: ethernet-pcs@3 {
+ compatible = "fsl,lynx-pcs";
reg = <3>;
};
};
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 13/15] arm64: dts: ls1088ardb: Remove aquantia interrupt
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (11 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 12/15] arm64: dts: ls1088a: Prevent PCSs from probing as phys Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 14/15] arm64: dts: ls1088ardb: Add SFP cage Sean Anderson
` (2 subsequent siblings)
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
On my board I have never been able to get this interrupt to work. As
such, the link does not come up. To fix this, remove the interrupt,
forcing polling mode. It has been reported that this interrupt works on
other boards. However, switching to polling will only result in a modest
decrease in link up/down delay (.5s on average).
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v13)
Changes in v13:
- Split interrupt changes off from serdes support
arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
index ee8e932628d1..391c2b8afa81 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
@@ -128,7 +128,6 @@ &emdio2 {
mdio2_aquantia_phy: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c45";
- interrupts-extended = <&extirq 2 IRQ_TYPE_LEVEL_LOW>;
reg = <0x0>;
};
};
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 14/15] arm64: dts: ls1088ardb: Add SFP cage
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (12 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 13/15] arm64: dts: ls1088ardb: Remove aquantia interrupt Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-13 16:06 ` [PATCH v14 15/15] arm64: dts: ls1088ardb: Add serdes descriptions Sean Anderson
2023-04-25 19:50 ` [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Vladimir Oltean
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
dpmac1 defaults to a fixed link. However, it has an SFP cage, so we can
determine more about the link (such as whether it's up/down) by
describing it. The GPIOs are part of the "QIXIS" FPGA. For now, just
model them as individual registers.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v13)
Changes in v13:
- Split off SFP addition from serdes support
.../boot/dts/freescale/fsl-ls1088a-rdb.dts | 51 ++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
index 391c2b8afa81..9fb1960f1258 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
@@ -10,11 +10,27 @@
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
+
#include "fsl-ls1088a.dtsi"
/ {
model = "LS1088A RDB Board";
compatible = "fsl,ls1088a-rdb", "fsl,ls1088a";
+
+ sfp_slot: sfp {
+ compatible = "sff,sfp";
+ i2c-bus = <&sfp_i2c>;
+ los-gpios = <&los_stat 5 GPIO_ACTIVE_HIGH>;
+ tx-fault-gpios = <&los_stat 4 GPIO_ACTIVE_HIGH>;
+ tx-disable-gpios = <&brdcfg9 4 GPIO_ACTIVE_HIGH>;
+ };
+};
+
+&dpmac1 {
+ managed = "in-band-status";
+ pcs-handle = <&pcs1>;
+ sfp = <&sfp_slot>;
};
&dpmac2 {
@@ -170,6 +186,12 @@ rtc@51 {
interrupts-extended = <&extirq 0 IRQ_TYPE_LEVEL_LOW>;
};
};
+
+ sfp_i2c: i2c@6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x6>;
+ };
};
};
@@ -184,8 +206,31 @@ nand@0,0 {
};
fpga: board-control@2,0 {
- compatible = "fsl,ls1088ardb-fpga", "fsl,fpga-qixis";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,ls1088ardb-fpga", "fsl,fpga-qixis",
+ "simple-bus";
reg = <0x2 0x0 0x0000100>;
+ ranges = <0x0 0x2 0x0 0x0000100>;
+
+ los_stat: gpio-controller@1d {
+ #gpio-cells = <2>;
+ compatible = "fsl,fpga-qixis-los-stat",
+ "ni,169445-nand-gpio";
+ reg = <0x1d 0x1>;
+ reg-names = "dat";
+ gpio-controller;
+ no-output;
+ };
+
+ brdcfg9: gpio-controller@59 {
+ #gpio-cells = <2>;
+ compatible = "fsl,fpga-qixis-brdcfg9",
+ "ni,169445-nand-gpio";
+ reg = <0x59 0x1>;
+ reg-names = "dat";
+ gpio-controller;
+ };
};
};
@@ -202,6 +247,10 @@ &esdhc {
status = "okay";
};
+&pcs_mdio1 {
+ status = "okay";
+};
+
&pcs_mdio2 {
status = "okay";
};
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* [PATCH v14 15/15] arm64: dts: ls1088ardb: Add serdes descriptions
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (13 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 14/15] arm64: dts: ls1088ardb: Add SFP cage Sean Anderson
@ 2023-04-13 16:06 ` Sean Anderson
2023-04-25 19:50 ` [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Vladimir Oltean
15 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-04-13 16:06 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, linux-phy
Cc: devicetree, Krzysztof Kozlowski, Madalin Bucur, Sean Anderson,
Shawn Guo, Li Yang, Rob Herring, Camelia Alexandra Groza,
Bagas Sanjaya, Ioana Ciornei, linuxppc-dev, linux-arm-kernel
This adds serdes support to the LS1088ARDB. I have tested the QSGMII
ports as well as the two 10G ports. Linux hangs around when the serdes
is initialized if the si5341 is enabled with the in-tree driver, so I
have modeled it as a two fixed clocks instead.
To enable serdes support, the DPC needs to set the macs to
MAC_LINK_TYPE_BACKPLANE. All MACs using the same QSGMII should be
converted at once. Additionally, in order to change interface types, the
MC firmware must support DPAA2_MAC_FEATURE_PROTOCOL_CHANGE.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v13)
Changes in v13:
- Split off interrupt and SFP changes into separate commits
Changes in v10:
- Move serdes bindings to SoC dtsi
- Use "descriptions" instead of "bindings"
- Don't use /clocks
- Add missing gpio-controller properties
Changes in v9:
- Add fsl,unused-lanes-reserved to allow a gradual transition, depending
on the mac link type.
- Remove unused clocks
- Fix some phy mode node names
- phy-type -> fsl,phy
Changes in v8:
- Rename serdes phy handles like the LS1046A
- Add SFP slot binding
- Fix incorrect lane ordering (it's backwards on the LS1088A just like it is in
the LS1046A).
- Fix duplicated lane 2 (it should have been lane 3).
- Fix incorrectly-documented value for XFI1.
- Remove interrupt for aquantia phy. It never fired for whatever reason,
preventing the link from coming up.
- Add GPIOs for QIXIS FPGA.
- Enable MAC1 PCS
- Remove si5341 binding
Changes in v4:
- Convert to new bindings
.../boot/dts/freescale/fsl-ls1088a-rdb.dts | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
index 9fb1960f1258..ede537b644e8 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
@@ -18,6 +18,18 @@ / {
model = "LS1088A RDB Board";
compatible = "fsl,ls1088a-rdb", "fsl,ls1088a";
+ 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>;
+ };
+
sfp_slot: sfp {
compatible = "sff,sfp";
i2c-bus = <&sfp_i2c>;
@@ -27,16 +39,26 @@ sfp_slot: sfp {
};
};
+&serdes1 {
+ clocks = <&clk_100mhz>, <&clk_156mhz>;
+ clock-names = "ref0", "ref1";
+ fsl,unused-lanes-reserved;
+ status = "okay";
+};
+
&dpmac1 {
managed = "in-band-status";
pcs-handle = <&pcs1>;
+ phys = <&serdes1_C>;
sfp = <&sfp_slot>;
};
&dpmac2 {
phy-handle = <&mdio2_aquantia_phy>;
phy-connection-type = "10gbase-r";
+ managed = "in-band-status";
pcs-handle = <&pcs2>;
+ phys = <&serdes1_D>;
};
&dpmac3 {
@@ -44,6 +66,7 @@ &dpmac3 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs3_0>;
+ phys = <&serdes1_A>;
};
&dpmac4 {
@@ -51,6 +74,7 @@ &dpmac4 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs3_1>;
+ phys = <&serdes1_A>;
};
&dpmac5 {
@@ -58,6 +82,7 @@ &dpmac5 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs3_2>;
+ phys = <&serdes1_A>;
};
&dpmac6 {
@@ -65,6 +90,7 @@ &dpmac6 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs3_3>;
+ phys = <&serdes1_A>;
};
&dpmac7 {
@@ -72,6 +98,7 @@ &dpmac7 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs7_0>;
+ phys = <&serdes1_B>;
};
&dpmac8 {
@@ -79,6 +106,7 @@ &dpmac8 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs7_1>;
+ phys = <&serdes1_B>;
};
&dpmac9 {
@@ -86,6 +114,7 @@ &dpmac9 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs7_2>;
+ phys = <&serdes1_B>;
};
&dpmac10 {
@@ -93,6 +122,7 @@ &dpmac10 {
phy-connection-type = "qsgmii";
managed = "in-band-status";
pcs-handle = <&pcs7_3>;
+ phys = <&serdes1_B>;
};
&emdio1 {
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 64+ messages in thread* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-13 16:05 [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Sean Anderson
` (14 preceding siblings ...)
2023-04-13 16:06 ` [PATCH v14 15/15] arm64: dts: ls1088ardb: Add serdes descriptions Sean Anderson
@ 2023-04-25 19:50 ` Vladimir Oltean
2023-04-25 20:22 ` Sean Anderson
15 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-04-25 19:50 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Sean Anderson, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hello,
On Thu, 13 Apr 2023 12:05:52 -0400, Sean Anderson wrote:
> This series is ready for review by the phy maintainers. I have addressed
> all known feedback and there are no outstanding issues.
>
> Major reconfiguration of baud rate (e.g. 1G->10G) does not work.
>
> There are several stand-alone commits in this series. Please feel free
> to pick them as appropriate. In particular, commits 1, 3, 4, 12, 13, and
> 14 are all good candidates for picking.
>
> - Although this is untested, it should support 2.5G SGMII as well as
> 1000BASE-KX. The latter needs MAC and PCS support, but the former
> should work out of the box.
> - It allows for clock configurations not supported by the RCW. This is
> very useful if you want to use e.g. SRDS_PRTCL_S1=0x3333 and =0x1133
> on the same board. This is because the former setting will use PLL1
> as the 1G reference, but the latter will use PLL1 as the 10G
> reference. Because we can reconfigure the PLLs, it is possible to
> always use PLL1 as the 1G reference.
I am an engineer working for NXP and I have access to the majority of
hardware that includes the Lynx 10G SERDES, as well as to block guides
that are not visible to customers, and to people from the hardware
design and validation teams.
I have an interest in adding a driver for this SERDES to support dynamic
Ethernet protocol reconfiguration, and perhaps the internal PHY for
copper backplane modes (1000Base-KX, 10GBase-KR) and its link training
procedure - although the latter will need more work.
I would like to thank you for starting the effort, but please, stop
submitting code for modes that are untested, and do not submit features
that do not work. If you do not have the time to fix up the loose ends
for this patch submission now, you won't have the time to debug
regressions on boards you might not even have access to, which worked
fine previously due to the static RCW/PBL configuration.
I have downloaded your patches, and although I have objections to some
of them already, I will be in the process of evaluating, testing,
changing them, for the coming weeks, perhaps even more. Please consider
this a NACK for the current patch set due to the SERDES related material,
although the unrelated patches (like "dt-bindings: Convert gpio-mmio to
yaml") can and should have been submitted separately, so they can be
analyzed by their respective maintainers based on their own merit and
not as part of an overall problematic set.
^ permalink raw reply [flat|nested] 64+ messages in thread* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-25 19:50 ` [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes Vladimir Oltean
@ 2023-04-25 20:22 ` Sean Anderson
2023-04-26 10:51 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-04-25 20:22 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 4/25/23 15:50, Vladimir Oltean wrote:
> Hello,
>
> On Thu, 13 Apr 2023 12:05:52 -0400, Sean Anderson wrote:
>> This series is ready for review by the phy maintainers. I have addressed
>> all known feedback and there are no outstanding issues.
>>
>> Major reconfiguration of baud rate (e.g. 1G->10G) does not work.
>>
>> There are several stand-alone commits in this series. Please feel free
>> to pick them as appropriate. In particular, commits 1, 3, 4, 12, 13, and
>> 14 are all good candidates for picking.
>>
>> - Although this is untested, it should support 2.5G SGMII as well as
>> 1000BASE-KX. The latter needs MAC and PCS support, but the former
>> should work out of the box.
>> - It allows for clock configurations not supported by the RCW. This is
>> very useful if you want to use e.g. SRDS_PRTCL_S1=0x3333 and =0x1133
>> on the same board. This is because the former setting will use PLL1
>> as the 1G reference, but the latter will use PLL1 as the 10G
>> reference. Because we can reconfigure the PLLs, it is possible to
>> always use PLL1 as the 1G reference.
>
> I am an engineer working for NXP and I have access to the majority of
> hardware that includes the Lynx 10G SERDES, as well as to block guides
> that are not visible to customers, and to people from the hardware
> design and validation teams.
>
> I have an interest in adding a driver for this SERDES to support dynamic
> Ethernet protocol reconfiguration, and perhaps the internal PHY for
> copper backplane modes (1000Base-KX, 10GBase-KR) and its link training
> procedure - although the latter will need more work.
>
> I would like to thank you for starting the effort, but please, stop
> submitting code for modes that are untested, and do not submit features
> that do not work.
The features which do not work (major protocol changes) are disabled :)
If it would cause this series to be immediately merged, I would remove
KX/KR and 2.5G which are the only untested link modes.
That said, PCS support is necessary for these modes, so it is not even
possible to select them.
> If you do not have the time to fix up the loose ends
> for this patch submission now
I have time to fix up any loose ends preventing this series from being
applied. However, I am not very sympathetic to larger requests, since
there has been extensive time to supply feedback already.
> , you won't have the time to debug
> regressions on boards you might not even have access to, which worked
> fine previously due to the static RCW/PBL configuration.
I have gotten no substantive feedback on this driver. I have been
working on this series since June last year, and no one has commented on
the core algotithms thus far. My capacity for making large changes has
decreased because the project funding the development has ended. I
appreciate that you are taking interest now, but frankly I think it is
rather past time...
> I have downloaded your patches, and although I have objections to some
> of them already, I will be in the process of evaluating, testing,
> changing them, for the coming weeks, perhaps even more. Please consider
> this a NACK for the current patch set due to the SERDES related material,
> although the unrelated patches (like "dt-bindings: Convert gpio-mmio to
> yaml") can and should have been submitted separately, so they can be
> analyzed by their respective maintainers based on their own merit and
> not as part of an overall problematic set.
This patchset has been ready to merge for several revisions now. I do
not consider it problematic. However, I do consider the (nonexistant)
review process for this subsystem extremely problematic.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-25 20:22 ` Sean Anderson
@ 2023-04-26 10:51 ` Vladimir Oltean
2023-04-26 14:50 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-04-26 10:51 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Tue, Apr 25, 2023 at 04:22:32PM -0400, Sean Anderson wrote:
> The features which do not work (major protocol changes) are disabled :)
>
> If it would cause this series to be immediately merged, I would remove
> KX/KR and 2.5G which are the only untested link modes.
>
> That said, PCS support is necessary for these modes, so it is not even
> possible to select them.
>
> > If you do not have the time to fix up the loose ends
> > for this patch submission now
>
> I have time to fix up any loose ends preventing this series from being
> applied. However, I am not very sympathetic to larger requests, since
> there has been extensive time to supply feedback already.
>
> > , you won't have the time to debug
> > regressions on boards you might not even have access to, which worked
> > fine previously due to the static RCW/PBL configuration.
>
> I have gotten no substantive feedback on this driver. I have been
> working on this series since June last year, and no one has commented on
> the core algotithms thus far. My capacity for making large changes has
> decreased because the project funding the development has ended. I
> appreciate that you are taking interest now, but frankly I think it is
> rather past time...
>
> > I have downloaded your patches, and although I have objections to some
> > of them already, I will be in the process of evaluating, testing,
> > changing them, for the coming weeks, perhaps even more. Please consider
> > this a NACK for the current patch set due to the SERDES related material,
> > although the unrelated patches (like "dt-bindings: Convert gpio-mmio to
> > yaml") can and should have been submitted separately, so they can be
> > analyzed by their respective maintainers based on their own merit and
> > not as part of an overall problematic set.
>
> This patchset has been ready to merge for several revisions now. I do
> not consider it problematic. However, I do consider the (nonexistant)
> review process for this subsystem extremely problematic.
To be very clear, the "larger request" which you are unsympathetic to is
to wait. I didn't ask you to change anything.
I need to catch up with 14 rounds of patches from you and with the
discussions that took place on each version, and understand how you
responded to feedback like "don't remove PHY interrupts without finding
out why they don't work" and "doesn't changing PLL frequencies on the
fly affect other lanes that use those PLLs, like PCIe?". The cognitive
dissonance between this and you saying that the review process for this
subsystem is absent is mind boggling. You are sufficiently averse to
feedback that's not the feedback you want to hear, that it's hard to
find a common ground.
It's naive to expect that the silicon vendor will respond positively to
a change of such magnitude as this one, which was written using the
"works for me" work ethics, but which the silicon vendor will have to
work with, afterwards. The only reason I sent my previous email was to
announce you in advance that I have managed to carve out a sufficient
amount of time to explore the topic in detail, and to stop you from
pushing this forward in this state. I thought you would understand the
concept of engineers being unable to easily reserve large chunks of time
for a given project, after all, you brought this argument with your own
company...
Even if the SERDES and PLL drivers "work for you" in the current form,
I doubt the usefulness of a PLL driver if you have to disconnect the
SoC's reset request signal on the board to not be stuck in a reboot loop.
https://lore.kernel.org/linux-arm-kernel/d3163201-2012-6cf9-c798-916bab9c7f72@seco.com/
Even so, I have not said anything definitive, I have just requested time
to take your proposal at face value, and understand whether there is any
other alternative.
I would advise you to consider whether your follow-up emails on this
topic encourage a collaborative atmosphere.
I will come back when I have tested the driver in a reasonable amount of
setups.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-26 10:51 ` Vladimir Oltean
@ 2023-04-26 14:50 ` Sean Anderson
2023-04-29 17:24 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-04-26 14:50 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 4/26/23 06:51, Vladimir Oltean wrote:
> On Tue, Apr 25, 2023 at 04:22:32PM -0400, Sean Anderson wrote:
>> The features which do not work (major protocol changes) are disabled :)
>>
>> If it would cause this series to be immediately merged, I would remove
>> KX/KR and 2.5G which are the only untested link modes.
>>
>> That said, PCS support is necessary for these modes, so it is not even
>> possible to select them.
>>
>>> If you do not have the time to fix up the loose ends
>>> for this patch submission now
>>
>> I have time to fix up any loose ends preventing this series from being
>> applied. However, I am not very sympathetic to larger requests, since
>> there has been extensive time to supply feedback already.
>>
>>> , you won't have the time to debug
>>> regressions on boards you might not even have access to, which worked
>>> fine previously due to the static RCW/PBL configuration.
>>
>> I have gotten no substantive feedback on this driver. I have been
>> working on this series since June last year, and no one has commented on
>> the core algotithms thus far. My capacity for making large changes has
>> decreased because the project funding the development has ended. I
>> appreciate that you are taking interest now, but frankly I think it is
>> rather past time...
>>
>>> I have downloaded your patches, and although I have objections to some
>>> of them already, I will be in the process of evaluating, testing,
>>> changing them, for the coming weeks, perhaps even more. Please consider
>>> this a NACK for the current patch set due to the SERDES related material,
>>> although the unrelated patches (like "dt-bindings: Convert gpio-mmio to
>>> yaml") can and should have been submitted separately, so they can be
>>> analyzed by their respective maintainers based on their own merit and
>>> not as part of an overall problematic set.
>>
>> This patchset has been ready to merge for several revisions now. I do
>> not consider it problematic. However, I do consider the (nonexistant)
>> review process for this subsystem extremely problematic.
>
> To be very clear, the "larger request" which you are unsympathetic to is
> to wait. I didn't ask you to change anything.
The maintainers in this subsystem refuse to review any patches which have
any kind of feedback. I have been trying to get them to look at this series
for literal months. So saying things like "I don't like this series, have
a NACK" seriously delays the process of getting feedback...
> I need to catch up with 14 rounds of patches from you and with the
> discussions that took place on each version, and understand how you
> responded to feedback like "don't remove PHY interrupts without finding
> out why they don't work"
All I can say is that
- It doesn't work on my board
- The traces are on the bottom of the PCB
- The signal goes through an FPGA which (unlike the LS1046ARDB) is closed-source
- The alternative is polling once a second (not terribly intensive)
I think it's very reasonable to make this change. Anyway, it's in a separate
patch so that it can be applied independently.
and "doesn't changing PLL frequencies on the
> fly affect other lanes that use those PLLs, like PCIe?".
This driver is not enable on any boards with PCIe on the same serdes. Therefore,
this is irrelevant for the initial submission.
> The cognitive
> dissonance between this and you saying that the review process for this
> subsystem is absent is mind boggling. You are sufficiently averse to
> feedback that's not the feedback you want to hear, that it's hard to
> find a common ground.
I'm opposed to nebulous 11th hour objections.
> It's naive to expect that the silicon vendor will respond positively to
> a change of such magnitude as this one, which was written using the
> "works for me" work ethics, but which the silicon vendor will have to
> work with, afterwards. The only reason I sent my previous email was to
> announce you in advance that I have managed to carve out a sufficient
> amount of time to explore the topic in detail, and to stop you from
> pushing this forward in this state. I thought you would understand the
> concept of engineers being unable to easily reserve large chunks of time
> for a given project, after all, you brought this argument with your own
> company...
>
> Even if the SERDES and PLL drivers "work for you" in the current form,
> I doubt the usefulness of a PLL driver if you have to disconnect the
> SoC's reset request signal on the board to not be stuck in a reboot loop.
I would like to emphasize that this has *nothing to do with this driver*.
This behavior is part of the boot ROM (or something like it) and occurs before
any user code has ever executed. The problem of course is that certain RCWs
expect the reference clocks to be in certain (incompatible) configurations,
and will fail the boot without a lock. I think this is rather silly (since
you only need PLL lock when you actually want to use the serdes), but that's
how it is. And of course, this is only necessary because I was unable to get
major reconfiguration to work. In an ideal world, you could always boot with
the same RCW (with PLL config matching the board) and choose the major protocol
at runtime.
> https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2flore.kernel.org%2flinux%2darm%2dkernel%2fd3163201%2d2012%2d6cf9%2dc798%2d916bab9c7f72%40seco.com%2f&umid=b7a538d4-355b-4a0a-bac8-26f0fa0a74c0&auth=d807158c60b7d2502abde8a2fc01f40662980862-e02727a0e4438c72b5e854cb20b9de5379470fd9
> Even so, I have not said anything definitive, I have just requested time
> to take your proposal at face value, and understand whether there is any
> other alternative.
>
> I would advise you to consider whether your follow-up emails on this
> topic encourage a collaborative atmosphere.
Sorry, I was a bit standoffish, but frankly I don't think leading off with
"NACK, no feedback for you today" is particularly collaborative either.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-26 14:50 ` Sean Anderson
@ 2023-04-29 17:24 ` Vladimir Oltean
2023-05-01 15:03 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-04-29 17:24 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Wed, Apr 26, 2023 at 10:50:17AM -0400, Sean Anderson wrote:
> > I need to catch up with 14 rounds of patches from you and with the
> > discussions that took place on each version, and understand how you
> > responded to feedback like "don't remove PHY interrupts without finding
> > out why they don't work"
>
> All I can say is that
>
> - It doesn't work on my board
> - The traces are on the bottom of the PCB
> - The signal goes through an FPGA which (unlike the LS1046ARDB) is closed-source
I don't understand the distinction you are making here. Are the sources
for QIXIS bit streams public for any Layerscape board?
> - The alternative is polling once a second (not terribly intensive)
It makes a difference to performance (forwarded packets per second), believe it or not.
>
> I think it's very reasonable to make this change. Anyway, it's in a separate
> patch so that it can be applied independently.
Perhaps better phrased: "discussed separately"...
> > Even if the SERDES and PLL drivers "work for you" in the current form,
> > I doubt the usefulness of a PLL driver if you have to disconnect the
> > SoC's reset request signal on the board to not be stuck in a reboot loop.
>
> I would like to emphasize that this has *nothing to do with this driver*.
> This behavior is part of the boot ROM (or something like it) and occurs before
> any user code has ever executed. The problem of course is that certain RCWs
> expect the reference clocks to be in certain (incompatible) configurations,
> and will fail the boot without a lock. I think this is rather silly (since
> you only need PLL lock when you actually want to use the serdes), but that's
> how it is. And of course, this is only necessary because I was unable to get
> major reconfiguration to work. In an ideal world, you could always boot with
> the same RCW (with PLL config matching the board) and choose the major protocol
> at runtime.
Could you please tell me what are the reference clock frequencies that
your board provides at boot time to the 2 PLLs, and which SERDES
protocol out of those 2 (1133 and 3333) boots correctly (no RESET_REQ
hacks necessary) with those refclks? I will try to get a LS1046A-QDS
where I boot from the same refclk + SERDES protocol configuration as
you, and use PBI commands in the RCW to reconfigure the lanes (PLL
selection and protocol registers) for the other mode, while keeping the
FRATE_SEL of the PLLs unmodified.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-04-29 17:24 ` Vladimir Oltean
@ 2023-05-01 15:03 ` Sean Anderson
2023-05-22 14:42 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-01 15:03 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 4/29/23 13:24, Vladimir Oltean wrote:
> On Wed, Apr 26, 2023 at 10:50:17AM -0400, Sean Anderson wrote:
>> > I need to catch up with 14 rounds of patches from you and with the
>> > discussions that took place on each version, and understand how you
>> > responded to feedback like "don't remove PHY interrupts without finding
>> > out why they don't work"
>>
>> All I can say is that
>>
>> - It doesn't work on my board
>> - The traces are on the bottom of the PCB
>> - The signal goes through an FPGA which (unlike the LS1046ARDB) is closed-source
>
> I don't understand the distinction you are making here. Are the sources
> for QIXIS bit streams public for any Layerscape board?
Correct. The sources for the LS1046ARDB QIXIS are available for download.
>> - The alternative is polling once a second (not terribly intensive)
>
> It makes a difference to performance (forwarded packets per second), believe it or not.
I don't. Please elaborate how link status latency from the phy affects performance.
>>
>> I think it's very reasonable to make this change. Anyway, it's in a separate
>> patch so that it can be applied independently.
>
> Perhaps better phrased: "discussed separately"...
>
>> > Even if the SERDES and PLL drivers "work for you" in the current form,
>> > I doubt the usefulness of a PLL driver if you have to disconnect the
>> > SoC's reset request signal on the board to not be stuck in a reboot loop.
>>
>> I would like to emphasize that this has *nothing to do with this driver*.
>> This behavior is part of the boot ROM (or something like it) and occurs before
>> any user code has ever executed. The problem of course is that certain RCWs
>> expect the reference clocks to be in certain (incompatible) configurations,
>> and will fail the boot without a lock. I think this is rather silly (since
>> you only need PLL lock when you actually want to use the serdes), but that's
>> how it is. And of course, this is only necessary because I was unable to get
>> major reconfiguration to work. In an ideal world, you could always boot with
>> the same RCW (with PLL config matching the board) and choose the major protocol
>> at runtime.
>
> Could you please tell me what are the reference clock frequencies that
> your board provides at boot time to the 2 PLLs, and which SERDES
> protocol out of those 2 (1133 and 3333) boots correctly (no RESET_REQ
> hacks necessary) with those refclks? I will try to get a LS1046A-QDS
> where I boot from the same refclk + SERDES protocol configuration as
> you, and use PBI commands in the RCW to reconfigure the lanes (PLL
> selection and protocol registers) for the other mode, while keeping the
> FRATE_SEL of the PLLs unmodified.
From table 31-1 in the RM, the PLL mapping for 1133 is 2211, and the
PLL mapping for 3333 is 2222. As a consequence, for 1133, PLL 2 must be
156.25 MHz and PLL 1 must be either 100 or 125 MHz. And for 3333, PLL 2
must be either 100 or 125 MHz, and PLL 1 should be shut down (as it is
unused). This conflict for PLL 2 means that the same reference clock
configuration cannot work for both 1133 and 3333. In one of the
configurations, SRDS_RST_RR will be set in RSTRQSR1. On our board,
reference clock 1 is 156.25 MHz, and reference clock 2 is 125 MHz.
Therefore, 3333 will fail to boot. Unfortunately, this reset request
occurs before any user-configurable code has run (except the RCW), so
it is not possible to fix this issue with e.g. PBI.
--Sean
not
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-05-01 15:03 ` Sean Anderson
@ 2023-05-22 14:42 ` Sean Anderson
2023-05-22 15:00 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-05-22 14:42 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Vladmir,
On 5/1/23 11:03, Sean Anderson wrote:
> On 4/29/23 13:24, Vladimir Oltean wrote:
>> On Wed, Apr 26, 2023 at 10:50:17AM -0400, Sean Anderson wrote:
>>> > I need to catch up with 14 rounds of patches from you and with the
>>> > discussions that took place on each version, and understand how you
>>> > responded to feedback like "don't remove PHY interrupts without finding
>>> > out why they don't work"
>>>
>>> All I can say is that
>>>
>>> - It doesn't work on my board
>>> - The traces are on the bottom of the PCB
>>> - The signal goes through an FPGA which (unlike the LS1046ARDB) is closed-source
>>
>> I don't understand the distinction you are making here. Are the sources
>> for QIXIS bit streams public for any Layerscape board?
>
> Correct. The sources for the LS1046ARDB QIXIS are available for download.
>
>>> - The alternative is polling once a second (not terribly intensive)
>>
>> It makes a difference to performance (forwarded packets per second), believe it or not.
>
> I don't. Please elaborate how link status latency from the phy affects performance.
>
>>>
>>> I think it's very reasonable to make this change. Anyway, it's in a separate
>>> patch so that it can be applied independently.
>>
>> Perhaps better phrased: "discussed separately"...
>>
>>> > Even if the SERDES and PLL drivers "work for you" in the current form,
>>> > I doubt the usefulness of a PLL driver if you have to disconnect the
>>> > SoC's reset request signal on the board to not be stuck in a reboot loop.
>>>
>>> I would like to emphasize that this has *nothing to do with this driver*.
>>> This behavior is part of the boot ROM (or something like it) and occurs before
>>> any user code has ever executed. The problem of course is that certain RCWs
>>> expect the reference clocks to be in certain (incompatible) configurations,
>>> and will fail the boot without a lock. I think this is rather silly (since
>>> you only need PLL lock when you actually want to use the serdes), but that's
>>> how it is. And of course, this is only necessary because I was unable to get
>>> major reconfiguration to work. In an ideal world, you could always boot with
>>> the same RCW (with PLL config matching the board) and choose the major protocol
>>> at runtime.
>>
>> Could you please tell me what are the reference clock frequencies that
>> your board provides at boot time to the 2 PLLs, and which SERDES
>> protocol out of those 2 (1133 and 3333) boots correctly (no RESET_REQ
>> hacks necessary) with those refclks? I will try to get a LS1046A-QDS
>> where I boot from the same refclk + SERDES protocol configuration as
>> you, and use PBI commands in the RCW to reconfigure the lanes (PLL
>> selection and protocol registers) for the other mode, while keeping the
>> FRATE_SEL of the PLLs unmodified.
>
> From table 31-1 in the RM, the PLL mapping for 1133 is 2211, and the
> PLL mapping for 3333 is 2222. As a consequence, for 1133, PLL 2 must be
> 156.25 MHz and PLL 1 must be either 100 or 125 MHz. And for 3333, PLL 2
> must be either 100 or 125 MHz, and PLL 1 should be shut down (as it is
> unused). This conflict for PLL 2 means that the same reference clock
> configuration cannot work for both 1133 and 3333. In one of the
> configurations, SRDS_RST_RR will be set in RSTRQSR1. On our board,
> reference clock 1 is 156.25 MHz, and reference clock 2 is 125 MHz.
> Therefore, 3333 will fail to boot. Unfortunately, this reset request
> occurs before any user-configurable code has run (except the RCW), so
> it is not possible to fix this issue with e.g. PBI.
Have you had a chance to review this driver?
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-05-22 14:42 ` Sean Anderson
@ 2023-05-22 15:00 ` Vladimir Oltean
2023-06-09 19:19 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-05-22 15:00 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, May 22, 2023 at 10:42:04AM -0400, Sean Anderson wrote:
> Have you had a chance to review this driver?
Partially / too little (and no, I don't have an answer yet). I am
debugging a SERDES protocol change procedure from XFI to SGMII.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-05-22 15:00 ` Vladimir Oltean
@ 2023-06-09 19:19 ` Sean Anderson
2023-06-10 22:21 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-06-09 19:19 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 5/22/23 11:00, Vladimir Oltean wrote:
> On Mon, May 22, 2023 at 10:42:04AM -0400, Sean Anderson wrote:
>> Have you had a chance to review this driver?
>
> Partially / too little (and no, I don't have an answer yet). I am
> debugging a SERDES protocol change procedure from XFI to SGMII.
I'd just like to reiterate that, like I said in the cover letter, I
believe this driver still has value even if it cannot yet perform
protocol switching.
Please send me your feedback, and I will try and incorporate it into the
next revision. Previously, you said you had major objections to the
contents of this series, but you still have not listed them.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-09 19:19 ` Sean Anderson
@ 2023-06-10 22:21 ` Vladimir Oltean
2023-06-12 14:35 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-06-10 22:21 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hello Sean,
On Fri, Jun 09, 2023 at 03:19:22PM -0400, Sean Anderson wrote:
> On 5/22/23 11:00, Vladimir Oltean wrote:
> > On Mon, May 22, 2023 at 10:42:04AM -0400, Sean Anderson wrote:
> >> Have you had a chance to review this driver?
> >
> > Partially / too little (and no, I don't have an answer yet). I am
> > debugging a SERDES protocol change procedure from XFI to SGMII.
>
> I'd just like to reiterate that, like I said in the cover letter, I
> believe this driver still has value even if it cannot yet perform
> protocol switching.
>
> Please send me your feedback, and I will try and incorporate it into the
> next revision. Previously, you said you had major objections to the
> contents of this series, but you still have not listed them.
And if SERDES protocol switching was not physically possible, would this
patch set still have value?
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-10 22:21 ` Vladimir Oltean
@ 2023-06-12 14:35 ` Sean Anderson
2023-06-12 16:33 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-06-12 14:35 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 6/10/23 18:21, Vladimir Oltean wrote:
> Hello Sean,
>
> On Fri, Jun 09, 2023 at 03:19:22PM -0400, Sean Anderson wrote:
>> On 5/22/23 11:00, Vladimir Oltean wrote:
>> > On Mon, May 22, 2023 at 10:42:04AM -0400, Sean Anderson wrote:
>> >> Have you had a chance to review this driver?
>> >
>> > Partially / too little (and no, I don't have an answer yet). I am
>> > debugging a SERDES protocol change procedure from XFI to SGMII.
>>
>> I'd just like to reiterate that, like I said in the cover letter, I
>> believe this driver still has value even if it cannot yet perform
>> protocol switching.
>>
>> Please send me your feedback, and I will try and incorporate it into the
>> next revision. Previously, you said you had major objections to the
>> contents of this series, but you still have not listed them.
>
> And if SERDES protocol switching was not physically possible, would this
> patch set still have value?
Yes. To e.g. set up SGMII25 or to fix the clocking situation.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-12 14:35 ` Sean Anderson
@ 2023-06-12 16:33 ` Vladimir Oltean
2023-06-12 20:46 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-06-12 16:33 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Jun 12, 2023 at 10:35:21AM -0400, Sean Anderson wrote:
> > And if SERDES protocol switching was not physically possible, would this
> > patch set still have value?
>
> Yes. To e.g. set up SGMII25 or to fix the clocking situation.
Let me analyze the reasons one by one.
The clocking situation only exists due to a hope that protocol changing
would be possible. If you were sure it wasn't possible, your board would
have had refclks set up for protocol 3333 via the supported PLL mapping 2222.
In essence, I consider this almost a non-argument, as it fixes a
self-inflicted problem.
Have you actually tested changing individual lanes from SGMII to SGMII 2.5?
I know it works on LS1028A, but I don't know if that's also true on LS1046A.
Your comments do say "LYNX_PROTO_SGMII25, /* Not tested */".
Assuming a start from SERDES protocol 3333 with PLL mapping 2222,
this protocol change implies powering on PLL 1 (reset state machine
wants it to be disabled) and moving those lanes from PLL 2 to PLL 1.
At first sight you might appear to have a point related to the fact that
PLL register writes are necessary, and thus this whole shebang is necessary.
But this can all be done using PBI commands, with the added benefit that
U-Boot can also use those SERDES networking ports, and not just Linux.
You can use the RCW+PBL specific to your board to inform the SoC that
your platform's refclk 1 is 156 MHz (something which the reset state
machine seems unable to learn, with protocol 0x3333). You don't have to
put that in the device tree. You don't have to push code to any open
source project to expose your platform specific details. Then, just like
in the case of the Lynx 28G driver on LX2160, the SERDES driver could
just treat the PLL configuration as read-only, which would greatly
simplify things and eliminate the need for a clk driver.
Here is an illustrative example (sorry, I don't have a board with the
right refclk on that PLL, to verify all the way):
Add this to ./serdes_10g.rcw in the root of the qoriq-rcw repository:
/*
* Registers for the Lynx 10G SERDES block.
*
* Must be included by an SoC-specific header that defines the
* SRDS_BASE value.
*/
#define PLLnRSTCTL(n) (SRDS_BASE + (0x20 * (n)))
#define PLLnCR0(n) (SRDS_BASE + (0x20 * (n)) + 0x0004)
#define POFF(x) (((x) << 31) & 0x80000000)
#define REFCLK_SEL(x) (((x) << 28) & 0x70000000)
#define REFCLK_EN(x) (((x) << 27) & 0x08000000)
#define FRATE_SEL(x) (((x) << 16) & 0x000f0000)
#define DLYDIV_SEL(x) ((x) & 0x00000003)
#define PCCR8 (SRDS_BASE + 0x0220)
#define SGMIIA_KX(x) (((x) << 31) & 0x80000000)
#define SGMIIA_CFG(x) (((x) << 28) & 0x70000000)
#define SGMIIB_KX(x) (((x) << 27) & 0x08000000)
#define SGMIIB_CFG(x) (((x) << 24) & 0x07000000)
#define SGMIIC_KX(x) (((x) << 23) & 0x00800000)
#define SGMIIC_CFG(x) (((x) << 20) & 0x00700000)
#define SGMIID_KX(x) (((x) << 19) & 0x00080000)
#define SGMIID_CFG(x) (((x) << 16) & 0x00070000)
#define SGMIIE_KX(x) (((x) << 15) & 0x00008000)
#define SGMIIE_CFG(x) (((x) << 12) & 0x00007000)
#define SGMIIF_KX(x) (((x) << 11) & 0x00000800)
#define SGMIIF_CFG(x) (((x) << 8) & 0x00000700)
#define SGMIIG_KX(x) (((x) << 7) & 0x00000080)
#define SGMIIG_CFG(x) (((x) << 4) & 0x00000070)
#define SGMIIH_KX(x) (((x) << 3) & 0x00000008)
#define SGMIIH_CFG(x) ((x) & 0x00000007)
#define PCCRB (SRDS_BASE + 0x022c)
#define XFIA_CFG(x) (((x) << 28) & 0x70000000)
#define XFIB_CFG(x) (((x) << 24) & 0x07000000)
#define XFIC_CFG(x) (((x) << 20) & 0x00700000)
#define XFID_CFG(x) (((x) << 16) & 0x00070000)
#define XFIE_CFG(x) (((x) << 12) & 0x00007000)
#define XFIF_CFG(x) (((x) << 8) & 0x00000700)
#define XFIG_CFG(x) (((x) << 4) & 0x00000070)
#define XFIH_CFG(x) ((x) & 0x00000007)
#define LNmGCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0800)
#define RPLL_LES(x) (((x) << 31) & 0x80000000)
#define RRAT_SEL(x) (((x) << 28) & 0x30000000)
#define TPLL_LES(x) (((x) << 27) & 0x08000000)
#define TRAT_SEL(x) (((x) << 24) & 0x03000000)
#define RRST_B(x) (((x) << 22) & 0x00400000)
#define TRST_B(x) (((x) << 21) & 0x00200000)
#define RX_PD(x) (((x) << 20) & 0x00100000)
#define TX_PD(x) (((x) << 19) & 0x00080000)
#define IF20BIT_EN(x) (((x) << 18) & 0x00040000)
#define FIRST_LANE(x) (((x) << 16) & 0x00010000)
#define GCR0_RSV 0x1000
#define PROTS(x) (((x) << 7) & 0x00000f80)
#define LNmGCR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0804)
#define RDAT_INV(x) (((x) << 31) & 0x80000000)
#define TDAT_INV(x) (((x) << 30) & 0x40000000)
#define OPAD_CTL(x) (((x) << 26) & 0x04000000)
#define REIDL_TH(x) (((x) << 20) & 0x00700000)
#define REIDL_EX_SEL(x) (((x) << 18) & 0x000C0000)
#define REIDL_ET_SEL(x) (((x) << 16) & 0x00030000)
#define REIDL_EX_MSB(x) (((x) << 15) & 0x00008000)
#define REIDL_ET_MSB(x) (((x) << 14) & 0x00004000)
#define REQ_CTL_SNP(x) (((x) << 13) & 0x00002000)
#define REQ_CDR_SNP(x) (((x) << 12) & 0x00001000)
#define TRSTDIR(x) (((x) << 7) & 0x00000080)
#define REQ_BIN_SNP(x) (((x) << 6) & 0x00000040)
#define ISLEW_RCTL(x) (((x) << 4) & 0x00000030)
#define GCR1_RSV 0x8
#define OSLEW_RCTL(x) ((x) & 0x3)
#define LNmRECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0810)
#define RXEQ_BST(x) (((x) << 28) & 0x10000000)
#define GK2OVD(x) (((x) << 24) & 0x0f000000)
#define GK3OVD(x) (((x) << 16) & 0x000f0000)
#define GK2OVD_EN(x) (((x) << 15) & 0x00008000)
#define GK3OVD_EN(x) (((x) << 14) & 0x00004000)
#define OSETOVD_EN(x) (((x) << 13) & 0x00002000)
#define BASE_WAND(x) (((x) << 10) & 0x00000c00)
#define OSETOVD(x) ((x) & 0x0000007F)
#define LNmTECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0818)
#define TEQ_TYPE(x) (((x) << 28) & 0x30000000)
#define SGN_PREQ(x) (((x) << 26) & 0x04000000)
#define RATIO_PREQ(x) (((x) << 22) & 0x03C00000)
#define SGN_POST1Q(x) (((x) << 21) & 0x00200000)
#define RATIO_PST1Q(x) (((x) << 16) & 0x001F0000)
#define ADPT_EQ(x) (((x) << 8) & 0x00003F00)
#define AMP_RED(x) ((x) & 0x0000003f)
#define LNmTTLCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0820)
#define LNmTCSR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0830)
#define LNmTCSR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0834)
#define LNmTCSR2(m) (SRDS_BASE + (0x40 * (m)) + 0x0838)
#define LNmTCSR3(m) (SRDS_BASE + (0x40 * (m)) + 0x083c)
#define SGMIIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1804)
#define SGMII_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
#define SGPCS_EN(x) (((x) << 11) & 0x00000800)
#define XFIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1984)
#define XFI_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
Add this to ./ls1046ardb/serdes_pll1_power_on.rcw in the same repo
(and finish the writes):
/*
* Assuming that the SoC starts from SERDES1 protocol 0x3333, power on PLL 1,
* which is required by the reset state machine to be disabled.
*/
#define SRDS_BASE 0xea0000 /* SERDES 1 */
#include <../serdes_10g.rcw>
.pbi
write PLLnCR0(0), POFF(0) | REFCLK_SEL(2) | REFCLK_EN(0)
wait 2500
write PLLnRSTCTL(0), ...
.end
Add this at the end of your board RCW:
#include <../ls1046ardb/serdes_pll1_power_on.rcw>
In short, I believe the reasons you have cited do not justify the
complexity of the solution that you propose.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-12 16:33 ` Vladimir Oltean
@ 2023-06-12 20:46 ` Sean Anderson
2023-06-13 14:27 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-06-12 20:46 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 6/12/23 12:33, Vladimir Oltean wrote:
> On Mon, Jun 12, 2023 at 10:35:21AM -0400, Sean Anderson wrote:
>> > And if SERDES protocol switching was not physically possible, would this
>> > patch set still have value?
More to the point, did you make any progress in this area?
>> Yes. To e.g. set up SGMII25 or to fix the clocking situation.
>
> Let me analyze the reasons one by one.
>
> The clocking situation only exists due to a hope that protocol changing
> would be possible. If you were sure it wasn't possible, your board would
> have had refclks set up for protocol 3333 via the supported PLL mapping 2222.
> In essence, I consider this almost a non-argument, as it fixes a
> self-inflicted problem.
2222 also does not work, as outlined above.
The clocking is incompatible, and must be fixed by software.
The only thing self-inflicted is NXP's conflicting design.
> Have you actually tested changing individual lanes from SGMII to SGMII 2.5?
> I know it works on LS1028A, but I don't know if that's also true on LS1046A.
> Your comments do say "LYNX_PROTO_SGMII25, /* Not tested */".
Not yet.
This driver would also be a good place to add the KR link training with
NXP tried to upstream a few years ago.
> Assuming a start from SERDES protocol 3333 with PLL mapping 2222,
> this protocol change implies powering on PLL 1 (reset state machine
> wants it to be disabled) and moving those lanes from PLL 2 to PLL 1.
>
> At first sight you might appear to have a point related to the fact that
> PLL register writes are necessary, and thus this whole shebang is necessary.
> But this can all be done using PBI commands, with the added benefit that
> U-Boot can also use those SERDES networking ports, and not just Linux.
> You can use the RCW+PBL specific to your board to inform the SoC that
> your platform's refclk 1 is 156 MHz (something which the reset state
> machine seems unable to learn, with protocol 0x3333). You don't have to
> put that in the device tree. You don't have to push code to any open
> source project to expose your platform specific details. Then, just like
> in the case of the Lynx 28G driver on LX2160, the SERDES driver could
> just treat the PLL configuration as read-only, which would greatly
> simplify things and eliminate the need for a clk driver.
>
> Here is an illustrative example (sorry, I don't have a board with the
> right refclk on that PLL, to verify all the way):
>
> ... snip ...
(which of course complicates the process of building the PBIs...)
> In short, I believe the reasons you have cited do not justify the
> complexity of the solution that you propose.
The work is done, and it is certainly more flexible than what you
propose. E.g. imagine a clock which needs to be configured before it has
the correct frequency. Such a thing is difficult to do in a PBI solution,
but is trivial in Linux.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-12 20:46 ` Sean Anderson
@ 2023-06-13 14:27 ` Vladimir Oltean
2023-08-10 10:26 ` Vladimir Oltean
2023-08-11 15:08 ` Vladimir Oltean
0 siblings, 2 replies; 64+ messages in thread
From: Vladimir Oltean @ 2023-06-13 14:27 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Jun 12, 2023 at 04:46:16PM -0400, Sean Anderson wrote:
> On 6/12/23 12:33, Vladimir Oltean wrote:
> > On Mon, Jun 12, 2023 at 10:35:21AM -0400, Sean Anderson wrote:
> >> > And if SERDES protocol switching was not physically possible, would this
> >> > patch set still have value?
>
> More to the point, did you make any progress in this area?
Aha. It is two email exchanges later that you've thought of asking that.
No, I haven't made progress. Though I managed to get some extra pointers
and things to try out, and at this stage I am the limiting factor. I will
let you know once the investigation has reached a conclusion.
So many things depend on whether major SERDES protocol reconfiguration
is possible, that if I were you, my question would be just that, instead
of the equivalent of "Vladimir, when can my driver be merged ???".
I understand how incredibly frustrating it is that the responses are
coming in so slowly, and this is the reason why I am responding at all
to your pings even if I have essentially nothing new to say.
And it's not that you don't care whether protocol reconfiguration is
possible or not - you do, but at the same time you're overcome with this
strong attachment to your solution just because it is yours, that I
don't know how to deal with.
> 2222 also does not work, as outlined above.
>
> The clocking is incompatible, and must be fixed by software.
>
> The only thing self-inflicted is NXP's conflicting design.
The way things are supposed to work (*if* this works at all) is that the
reset state machine starts with a supported PLL / refclk configuration
that permits a certain subset of protocols, and the SERDES protocols can
be changed from the reset state, as desired, for the individual lanes.
What is self-inflicted is that the refclks on your board design are not
supportable by any reset state machine configuration, and wiring them
that way was a conscious decision. Did your company's board designers
receive the recommendation to disconnect RESET_REQ from NXP, and has NXP
said that the end result will be something that continues to be supportable?
I've searched the customer support database and the answer seems to be no.
In any case, if you have to disconnect RESET_REQ from the SoC to make
the driver in this form useful, then... yeah, no. You're obviously free
to do whatever you want with your products, but that's not how mainline
Linux works, it needs to be useful beyond you.
If protocol switching works, I will maintain that your board should have
wired the refclks to PLLs the other way around (which is supported by
the RCW), and then proceed to do protocol switching to reach the desired
configuration.
So you can see that many things change based on whether protocol reconfig
is possible or not. The patch set presented here looks exactly like the
product of someone infatuated with form over substance, and who misses
the overall picture. My belief is that if you shift your attitude from
"is the work done?" to "is the work generally useful?", the interactions
will improve by a lot.
> > Have you actually tested changing individual lanes from SGMII to SGMII 2.5?
> > I know it works on LS1028A, but I don't know if that's also true on LS1046A.
> > Your comments do say "LYNX_PROTO_SGMII25, /* Not tested */".
>
> Not yet.
Aha. So concretely, what makes you so confident with moving this forward?
You like the code to just be there, no?
It helps your board, I understand. But not everything has to stay in
mainline if it's not useful for others, and I've proposed an alternative
solution which I'm not sure how seriously you took.
> This driver would also be a good place to add the KR link training with
> NXP tried to upstream a few years ago.
Well, speaking as someone who is now also tasked with the copper backplane
support, believe me that I know that, and this is why I'm so desperate
with the logic you're trying to push forward. It's clear that we should
try to collaborate rather than try to push individualistic non-tested
non-solutions.
> > Assuming a start from SERDES protocol 3333 with PLL mapping 2222,
> > this protocol change implies powering on PLL 1 (reset state machine
> > wants it to be disabled) and moving those lanes from PLL 2 to PLL 1.
> >
> > At first sight you might appear to have a point related to the fact that
> > PLL register writes are necessary, and thus this whole shebang is necessary.
> > But this can all be done using PBI commands, with the added benefit that
> > U-Boot can also use those SERDES networking ports, and not just Linux.
> > You can use the RCW+PBL specific to your board to inform the SoC that
> > your platform's refclk 1 is 156 MHz (something which the reset state
> > machine seems unable to learn, with protocol 0x3333). You don't have to
> > put that in the device tree. You don't have to push code to any open
> > source project to expose your platform specific details. Then, just like
> > in the case of the Lynx 28G driver on LX2160, the SERDES driver could
> > just treat the PLL configuration as read-only, which would greatly
> > simplify things and eliminate the need for a clk driver.
> >
> > Here is an illustrative example (sorry, I don't have a board with the
> > right refclk on that PLL, to verify all the way):
> >
> > ... snip ...
>
> (which of course complicates the process of building the PBIs...)
Maybe this is the language barrier, but what are you trying to say here?
> > In short, I believe the reasons you have cited do not justify the
> > complexity of the solution that you propose.
>
> The work is done,
:)
> and it is certainly more flexible than what you propose. E.g. imagine
> a clock which needs to be configured before it has the correct
> frequency. Such a thing is difficult to do in a PBI solution, but is
> trivial in Linux.
So "a clock which needs to be configured" means a programmable clock
generator that provides the refclk to the PLL, correct?
With QorIQ/Layerscape SoCs, I imagine that case would typically be done
by a board CPLD during the boot sequence, because if the PLL refclks
don't have the right frequency, the main SoC doesn't start. You've hacked
the RESET_REQ signal so you don't have that problem. But surprise -
others haven't - so it's not exactly a relevant argument in favor.
It's clear that the hardware has limitations that have impact upon the
way in which both boards and software is designed. Because you don't
know whether protocol reconfig is possible, you don't know *if* it is
possible, *under what circumstances* it will be possible. What if XFI
only works with 156 MHz provided to PLL 2, and not to PLL 1? You're
trying to push a non-final solution as a final solution.
The programmable refclk generator that is reconfigured at runtime is
another can of worms, which I'm not sure can be feasibly implemented on
this hardware given the fact that multiple lanes share one PLL, and
there are many more lanes (4 or 8) than PLLs (2). Again, I need to see
some practical functionality that this patch set provides.
Do you _actually_ have a programmable clock generator that the CPLD
can't program, or is this a "what if" situation?
More to the point of the "flexibility" that you claim. You haven't
addressed my point of networking over the SERDES ports in U-Boot.
You don't need that - I'm clear with that, otherwise we'd be having this
conversation on the U-Boot mailing lists - but what if others, wanting
to follow your solution, would? Would you see that as an opportunity to
write even more code?
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-13 14:27 ` Vladimir Oltean
@ 2023-08-10 10:26 ` Vladimir Oltean
2023-08-10 19:58 ` Sean Anderson
2023-08-11 15:08 ` Vladimir Oltean
1 sibling, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-10 10:26 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Sean,
On Tue, Jun 13, 2023 at 05:27:54PM +0300, Vladimir Oltean wrote:
> The way things are supposed to work (*if* this works at all) is that the
> reset state machine starts with a supported PLL / refclk configuration
> that permits a certain subset of protocols, and the SERDES protocols can
> be changed from the reset state, as desired, for the individual lanes.
>
> What is self-inflicted is that the refclks on your board design are not
> supportable by any reset state machine configuration, and wiring them
> that way was a conscious decision. Did your company's board designers
> receive the recommendation to disconnect RESET_REQ from NXP, and has NXP
> said that the end result will be something that continues to be supportable?
> I've searched the customer support database and the answer seems to be no.
> In any case, if you have to disconnect RESET_REQ from the SoC to make
> the driver in this form useful, then... yeah, no. You're obviously free
> to do whatever you want with your products, but that's not how mainline
> Linux works, it needs to be useful beyond you.
>
> If protocol switching works, I will maintain that your board should have
> wired the refclks to PLLs the other way around (which is supported by
> the RCW), and then proceed to do protocol switching to reach the desired
> configuration.
It was quite a journey to piece everything together.
There is one thing to be mentioned right from the start, and that is
that on some SoCs (including the LS1043A and LS1046A), the SerDes data
path is partially controlled by the RCW, and thus, dynamically performing
a major SerDes protocol reconfiguration requires a RCW override procedure
(undocumented in the SerDes reconfiguration steps, but all the info you
need is summarized below).
The DCFG block has a set of RCWSR0 - RCWSR15 read-only status registers
relative to DCFG_CCSR. What we don't document in the SoC RM, but I got
permission to say here, is that the DCFG block exposes a second set of
Expert Mode registers in the DCSR address space (base address 2000_0000h).
The DCFG_DCSR register region spans from offset 2_0000h to 2_05AC into
the DCSR base address. At the exact same offsets into DCFG_DCSR as
RCWSR0 - RCWSR15 are into DCFG_CCSR (aka 100h-13ch), one can find the
shadow RCWCR0 - RCWCR15 (Reset Control Word Control Register) registers
which are also writable. There is a one-to-one mapping between each
register (and field) in RCWSR and RCWCR, so I won't detail the
contents of the RCWCR registers, because we document RCWSR fully.
RCW override means modifying some of the RCWCR registers. In this
particular case, finalizing the major SerDes reconfiguration requires
overriding SRDS_PRTCL_S1 in RCWCR5 with the new per-lane settings, to mux
the correct PCS to the MAC. In the general case, random RCW overrides
that don't come from the hardware validation team are unsupported by
NXP, and you should expect the procedure to yield unpredictable results.
I don't know how much of the above steps is applicable to the other 10G Lynx
SoCs (LS1088, LS2088 etc). Not all SoCs may require RCW override, and
the RCW override procedure may not be the same. That is TBD, and we'd
appreciate if support for other SoCs than the LS1046 to be added no
earlier than when we have a validated SerDes reconfiguration procedure.
I believe this is enough information to permit the creation of a Linux
driver on the DCFG_DCSR registers which permits RCW override at runtime.
It seems this will be necessary at least on LS1046.
We should discuss who and when picks up your patches, removes the
unsupported, untested and unnecessary code and adds the RCW override
procedure. It can be you, it can also be someone from NXP. If it will be
you, please let me know, because there are concrete implementation
choices I want to leave comments on. There is also the previous
observation from Ioana that you should not delete PHY interrupts without
finding out why they don't work.
Only what was thoroughly tested and is based on a hardware validated
procedure should be submitted. This means only a 1G <-> 10G transition
on LS1046 for now.
Nonetheless, below is a functional example of how NXP would recommend
you to achieve the desired PLL mapping for any RCW-based SerDes protocol.
My testing platform was the LS1046A-QDS with PLL1 at 100 MHz and PLL2 at
156.25 MHz. I believe that this should eliminate the need for a clk
driver for the PLLs, and should make your Ethernet lanes usable much
earlier than Linux. That being said, our position at NXP is that you
don't need a clk driver for the PLLs, and I would like to see the clk
portion removed from future patch revisions.
This patch is on top of https://github.com/nxp-qoriq/rcw/tree/lf-6.1.22-2.0.0
From 9f90d6805883f23a898f9d66826f89b7ba73afe3 Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Thu, 25 May 2023 11:23:41 +0300
Subject: [PATCH] LS1046A: implement a PBI-based SerDes protocol switching
mechanism
The LS1046A reset state machine is a bit limited in the SRDS_PRTCL_S1
protocol combinations that it offers. For example, it offers protocol
0x3333 (4x SGMII) only with PLL mapping 2222, and that is good as long
as dynamic protocol switching between 1G (SGMII) and 10G (XFI) isn't
desired at runtime.
If that is taken into account as an additional constraint, then we need
PLL2 (yes, specifically 2) to have a refclk of 156.25 MHz, and that is
in conflict with the PLL mapping of the aforementioned 0x3333, because
1G (SGMII) can only work with a PLL refclk of 100 or 125 MHz, so that's
what has to be fed into PLL 2.
Dynamic frequency switching of PLLs is hard and out of question.
It is desirable to be able to use PLL2 for the 156.25 MHz refclk
frequency (for the 10G link modes), and PLL1 for the 100 MHz refclk
frequency (for the 1G link modes). It turns out, this is possible, even
if not 100% natively through the reset state machine built-in options.
The strategy is to pick a pair of SerDes refclk frequencies in the RCW
that is correct for the given board (thus allowing the SerDes PLLs to
lock, and the SoC to boot) as a first step. The SerDes protocol can be
absolutely anything as long as the PLL frequencies are right, because as
a second step, we'll be fixing up the SerDes lane registers for the
desired final protocol, to appear as if we had that protocol natively.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
.../rcw_1600.rcw | 99 +++++++++++++++++
ls1046ardb/serdes_1133_to_3333.rcw | 73 +++++++++++++
serdes_10g.rcw | 102 ++++++++++++++++++
3 files changed, 274 insertions(+)
create mode 100644 ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
create mode 100644 ls1046ardb/serdes_1133_to_3333.rcw
create mode 100644 serdes_10g.rcw
diff --git a/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw b/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
new file mode 100644
index 000000000000..2bbc0163392d
--- /dev/null
+++ b/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
@@ -0,0 +1,99 @@
+/*
+ * LS1046AQDS RCW for SerDes Protocol 0x3333_5559 derived from 0x1133_5559
+ *
+ * 42G configuration -- 2 RGMII + 4 SGMII (reconfigurable to XFI) + 3 PCIe + SATA
+ *
+ * Frequencies:
+ *
+ * Sys Clock: 100 MHz
+ * DDR_Refclock: 100 MHz
+ *
+ * Core -- 1600 MHz (Mul 16)
+ * Platform -- 600 MHz (Mul 6)
+ * DDR -- 2100 MT/s (Mul 21)
+ * FMan -- 700 MHz (CGA2 /2)
+ * XFI -- 156.25 MHz (10.3125G)
+ * SGMII -- 100 MHz (5G)
+ * PCIE -- 100 MHz (5G)
+ * eSDHC -- 1400 MHz (CGA2 /1)
+ *
+ * Hardware Accelerator Block Cluster Group A Mux Clock:
+ * FMan - HWA_CGA_M1_CLK_SEL = 6 - Async mode, CGA PLL 2 /2 is clock
+ * eSDHC, QSPI - HWA_CGA_M2_CLK_SEL = 1 - Async mode, CGA PLL 2 /1 is clock
+ *
+ * Serdes Lanes vs Slot information
+ * Serdes1 Lane 0 (D) - Starts as XFI9, switches to SGMII9
+ * Serdes1 Lane 1 (C) - Starts as XFI10, switches to SGMII10
+ * Serdes1 Lane 2 (B) - SGMII5, Slot 1
+ * Serdes1 Lane 3 (A) - SGMII6, Slot 1
+ *
+ * Serdes2 Lane 0 (A) - PCIe1 Gen2 x1, Slot 3
+ * Serdes2 Lane 1 (B) - PCIe2 Gen2 x1, Slot 4
+ * Serdes2 Lane 2 (C) - PCIe3 Gen2 x1, Slot 5
+ * Serdes2 Lane 3 (D) - SATA
+ *
+ * PLL mapping: starts as 2211_2221, ends as 1111_2221
+ *
+ * Serdes 1:
+ * PLL mapping: 1111
+ * SRDS_PLL_REF_CLK_SEL_S1 : 0b'01
+ * SerDes 1, PLL1[160] : 0 - 100MHz for SGMII and PCIe
+ * SerDes 1, PLL2[161] : 1 - 156.25MHz for XFI
+ * SRDS_PLL_PD_S1 : 0b'0
+ * SerDes 1, PLL1 : 0 - not powered down
+ * SerDes 1, PLL2 : 0 - not powered down
+ * SRDS_DIV_PEX_S1 :
+ * Only used for PEX, not used.
+ *
+ * Serdes 2:
+ * PLL mapping: 2221
+ * SRDS_PLL_REF_CLK_SEL_S2 : 0b'00
+ * SerDes 2, PLL1[162] : 0 - 100MHz for SATA
+ * SerDes 2, PLL2[163] : 0 - 100MHz for PCIe
+ * SRDS_PLL_PD_S2 : 0b'00
+ * SerDes 2, PLL1 : 0 - not powered down
+ * SerDes 2, PLL2 : 0 - not powered down
+ * SRDS_DIV_PEX_S2 : 0b'01
+ * 00 - train up to max rate of 8G
+ * 01 - train up to max rate of 5G
+ * 10 - train up to max rate of 2.5G
+ *
+ * DDR clock:
+ * DDR_REFCLK_SEL : 1 - DDRCLK pin provides the reference clock to the DDR PLL
+ *
+ */
+
+#include <../ls1046ardb/ls1046a.rcwi>
+
+SYS_PLL_RAT=6
+MEM_PLL_RAT=21
+CGA_PLL1_RAT=16
+CGA_PLL2_RAT=14
+SRDS_PRTCL_S1=4403
+SRDS_PRTCL_S2=21849
+SRDS_PLL_REF_CLK_SEL_S1=1
+SRDS_PLL_REF_CLK_SEL_S2=0
+SRDS_DIV_PEX_S1=1
+SRDS_DIV_PEX_S2=1
+DDR_FDBK_MULT=2
+DDR_REFCLK_SEL=1
+PBI_SRC=14
+IFC_MODE=37
+HWA_CGA_M1_CLK_SEL=6
+DRAM_LAT=1
+UART_BASE=7
+IRQ_OUT=1
+LVDD_VSEL=1
+TVDD_VSEL=0
+DVDD_VSEL=2
+EVDD_VSEL=2
+IIC2_EXT=1
+SYSCLK_FREQ=600
+HWA_CGA_M2_CLK_SEL=1
+
+#include <../ls1046ardb/cci_barrier_disable.rcw>
+#include <../ls1046ardb/usb_phy_freq.rcw>
+#include <../ls1046ardb/uboot_address.rcw>
+#include <../ls1046ardb/serdes_sata.rcw>
+#include <../ls1046ardb/a009531.rcw>
+#include <../ls1046ardb/serdes_1133_to_3333.rcw>
diff --git a/ls1046ardb/serdes_1133_to_3333.rcw b/ls1046ardb/serdes_1133_to_3333.rcw
new file mode 100644
index 000000000000..ffd548a73675
--- /dev/null
+++ b/ls1046ardb/serdes_1133_to_3333.rcw
@@ -0,0 +1,73 @@
+/*
+ * Change protocols on SerDes1 from 1133 to 3333, and their PLL mappings from
+ * 2211 to 1111. This is useful because, although the reset state machine has a
+ * native 0x3333 SerDes protocol option, the PLL mapping of that is 2222.
+ * This non-native option frees up PLL 2, and it can be provisioned e.g. with a
+ * 156.25 MHz for any lanes that might want to switch to XFI at runtime.
+ */
+
+#define SRDS_BASE 0xea0000 /* SerDes 1 relative to CCSR_BASE */
+#include <../serdes_10g.rcw>
+
+/* For writing outside the CCSR space (in DCSR), an indirect access method is
+ * used. The SCFG_ALTCBAR register (field ALTCFG) holds the upper 24 bits of
+ * the 48-bit address, and the awrite PBL instruction gets the lower 24 bits of
+ * the address that is relative to that. Here we work with 32-bit addresses,
+ * so we only care about the upper 8 bits.
+ */
+#define SCFG_ALTCBAR 0x570158
+#define ALTCFG(x) (((x) << 8) & 0xffffff00)
+#define DCFG_DCSR_RCWCR5 0x20140110
+#define RCWCR5_SRDS_PRTCL_S1(x) (((x) << 16) & 0xffff0000)
+#define RCWCR5_SRDS_PRTCL_S2(x) ((x) & 0xffff)
+#define upper_8_bits(x) (((x) & 0xff000000) >> 24)
+#define lower_24_bits(x) ((x) & 0xffffff)
+
+#define GCR0_SGMII_FROM_PLL1 RPLL_LES(1) | RRAT_SEL(2) | \
+ TPLL_LES(1) | TRAT_SEL(2) | \
+ FIRST_LANE(1) | PROTS(1)
+
+.pbi
+
+write LNmGCR0(2), RRST_B(0) | TRST_B(0)
+write LNmGCR0(3), RRST_B(0) | TRST_B(0)
+
+wait 50
+
+write LNmGCR0(2), GCR0_SGMII_FROM_PLL1
+write LNmGCR0(3), GCR0_SGMII_FROM_PLL1
+
+write LNmGCR1(2), REIDL_TH(1) | REIDL_EX_SEL(3) | REIDL_ET_MSB(1) | \
+ ISLEW_RCTL(1) | OSLEW_RCTL(1)
+write LNmGCR1(3), REIDL_TH(1) | REIDL_EX_SEL(3) | REIDL_ET_MSB(1) | \
+ ISLEW_RCTL(1) | OSLEW_RCTL(1)
+
+write LNmRECR0(2), GK2OVD_EN(1) | GK2OVD(15) | GK3OVD_EN(1) | GK3OVD(15)
+write LNmRECR0(3), GK2OVD_EN(1) | GK2OVD(15) | GK3OVD_EN(1) | GK3OVD(15)
+
+write LNmTECR0(2), ADPT_EQ(48) | AMP_RED(6)
+write LNmTECR0(3), ADPT_EQ(48) | AMP_RED(6)
+
+/* LS1046A requires RCW override to reconfigure the mux between
+ * the PCS and the MAC.
+ */
+write SCFG_ALTCBAR, ALTCFG(upper_8_bits(DCFG_DCSR_RCWCR5))
+flush
+awrite lower_24_bits(DCFG_DCSR_RCWCR5), RCWCR5_SRDS_PRTCL_S1(0x3333) | \
+ RCWCR5_SRDS_PRTCL_S2(0x5559)
+
+/* PCCRB: 0x21000000 -> 0x00000000 */
+write PCCRB, XFIA_CFG(0) | XFIB_CFG(0)
+
+/* PCCR8: 0x11000000 -> 0x11110000 */
+write PCCR8, SGMIIA_CFG(1) | SGMIIB_CFG(1) | SGMIIC_CFG(1) | SGMIID_CFG(1)
+
+write SGMIIaCR1(2), SGMII_MDEV_PORT(0) | SGPCS_EN(1)
+write SGMIIaCR1(3), SGMII_MDEV_PORT(0) | SGPCS_EN(1)
+
+wait 120
+
+write LNmGCR0(2), GCR0_SGMII_FROM_PLL1 | RRST_B(1) | TRST_B(1)
+write LNmGCR0(3), GCR0_SGMII_FROM_PLL1 | RRST_B(1) | TRST_B(1)
+
+.end
diff --git a/serdes_10g.rcw b/serdes_10g.rcw
new file mode 100644
index 000000000000..714d53fde8af
--- /dev/null
+++ b/serdes_10g.rcw
@@ -0,0 +1,102 @@
+/*
+ * Registers for the Lynx 10G SerDes block.
+ *
+ * Must be included by an SoC-specific header that defines the
+ * SRDS_BASE value.
+ */
+
+#define PLLnRSTCTL(n) (SRDS_BASE + (0x20 * (n)))
+#define PLLnCR0(n) (SRDS_BASE + (0x20 * (n)) + 0x0004)
+#define POFF(x) (((x) << 31) & 0x80000000)
+#define REFCLK_SEL(x) (((x) << 28) & 0x70000000)
+#define REFCLK_EN(x) (((x) << 27) & 0x08000000)
+#define FRATE_SEL(x) (((x) << 16) & 0x000f0000)
+#define DLYDIV_SEL(x) ((x) & 0x00000003)
+#define PCCR8 (SRDS_BASE + 0x0220)
+#define SGMIIA_KX(x) (((x) << 31) & 0x80000000)
+#define SGMIIA_CFG(x) (((x) << 28) & 0x70000000)
+#define SGMIIB_KX(x) (((x) << 27) & 0x08000000)
+#define SGMIIB_CFG(x) (((x) << 24) & 0x07000000)
+#define SGMIIC_KX(x) (((x) << 23) & 0x00800000)
+#define SGMIIC_CFG(x) (((x) << 20) & 0x00700000)
+#define SGMIID_KX(x) (((x) << 19) & 0x00080000)
+#define SGMIID_CFG(x) (((x) << 16) & 0x00070000)
+#define SGMIIE_KX(x) (((x) << 15) & 0x00008000)
+#define SGMIIE_CFG(x) (((x) << 12) & 0x00007000)
+#define SGMIIF_KX(x) (((x) << 11) & 0x00000800)
+#define SGMIIF_CFG(x) (((x) << 8) & 0x00000700)
+#define SGMIIG_KX(x) (((x) << 7) & 0x00000080)
+#define SGMIIG_CFG(x) (((x) << 4) & 0x00000070)
+#define SGMIIH_KX(x) (((x) << 3) & 0x00000008)
+#define SGMIIH_CFG(x) ((x) & 0x00000007)
+#define PCCRB (SRDS_BASE + 0x022c)
+#define XFIA_CFG(x) (((x) << 28) & 0x70000000)
+#define XFIB_CFG(x) (((x) << 24) & 0x07000000)
+#define XFIC_CFG(x) (((x) << 20) & 0x00700000)
+#define XFID_CFG(x) (((x) << 16) & 0x00070000)
+#define XFIE_CFG(x) (((x) << 12) & 0x00007000)
+#define XFIF_CFG(x) (((x) << 8) & 0x00000700)
+#define XFIG_CFG(x) (((x) << 4) & 0x00000070)
+#define XFIH_CFG(x) ((x) & 0x00000007)
+#define LNmGCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0800)
+#define RPLL_LES(x) (((x) << 31) & 0x80000000)
+#define RRAT_SEL(x) (((x) << 28) & 0x30000000)
+#define TPLL_LES(x) (((x) << 27) & 0x08000000)
+#define TRAT_SEL(x) (((x) << 24) & 0x03000000)
+#define RRST_B(x) (((x) << 22) & 0x00400000)
+#define TRST_B(x) (((x) << 21) & 0x00200000)
+#define RX_PD(x) (((x) << 20) & 0x00100000)
+#define TX_PD(x) (((x) << 19) & 0x00080000)
+#define IF20BIT_EN(x) (((x) << 18) & 0x00040000)
+#define FIRST_LANE(x) (((x) << 16) & 0x00010000)
+#define GCR0_RSV 0x1000
+#define PROTS(x) (((x) << 7) & 0x00000f80)
+#define LNmGCR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0804)
+#define RDAT_INV(x) (((x) << 31) & 0x80000000)
+#define TDAT_INV(x) (((x) << 30) & 0x40000000)
+#define OPAD_CTL(x) (((x) << 26) & 0x04000000)
+#define REIDL_TH(x) (((x) << 20) & 0x00700000)
+#define REIDL_EX_SEL(x) (((x) << 18) & 0x000C0000)
+#define REIDL_ET_SEL(x) (((x) << 16) & 0x00030000)
+#define REIDL_EX_MSB(x) (((x) << 15) & 0x00008000)
+#define REIDL_ET_MSB(x) (((x) << 14) & 0x00004000)
+#define REQ_CTL_SNP(x) (((x) << 13) & 0x00002000)
+#define REQ_CDR_SNP(x) (((x) << 12) & 0x00001000)
+#define TRSTDIR(x) (((x) << 7) & 0x00000080)
+#define REQ_BIN_SNP(x) (((x) << 6) & 0x00000040)
+#define ISLEW_RCTL(x) (((x) << 4) & 0x00000030)
+#define GCR1_RSV 0x8
+#define OSLEW_RCTL(x) ((x) & 0x3)
+#define LNmRECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0810)
+#define RXEQ_BST(x) (((x) << 28) & 0x10000000)
+#define GK2OVD(x) (((x) << 24) & 0x0f000000)
+#define GK3OVD(x) (((x) << 16) & 0x000f0000)
+#define GK2OVD_EN(x) (((x) << 15) & 0x00008000)
+#define GK3OVD_EN(x) (((x) << 14) & 0x00004000)
+#define OSETOVD_EN(x) (((x) << 13) & 0x00002000)
+#define BASE_WAND(x) (((x) << 10) & 0x00000c00)
+#define OSETOVD(x) ((x) & 0x0000007F)
+#define LNmTECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0818)
+#define TEQ_TYPE(x) (((x) << 28) & 0x30000000)
+#define SGN_PREQ(x) (((x) << 26) & 0x04000000)
+#define RATIO_PREQ(x) (((x) << 22) & 0x03C00000)
+#define SGN_POST1Q(x) (((x) << 21) & 0x00200000)
+#define RATIO_PST1Q(x) (((x) << 16) & 0x001F0000)
+#define ADPT_EQ(x) (((x) << 8) & 0x00003F00)
+#define AMP_RED(x) ((x) & 0x0000003f)
+#define LNmTTLCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0820)
+#define LNmTCSR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0830)
+#define LNmTCSR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0834)
+#define LNmTCSR2(m) (SRDS_BASE + (0x40 * (m)) + 0x0838)
+#define LNmTCSR3(m) (SRDS_BASE + (0x40 * (m)) + 0x083c)
+#define SGMIIaCR0(a) (SRDS_BASE + (0x10 * (a)) + 0x1800)
+#define RST_SGM(x) (((x) << 31) & 0x80000000)
+#define PD_SGM(x) (((x) << 30) & 0x40000000)
+#define SGMIIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1804)
+#define SGMII_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
+#define SGPCS_EN(x) (((x) << 11) & 0x00000800)
+#define XFIaCR0(a) (SRDS_BASE + (0x10 * (a)) + 0x1980)
+#define RST_XFI(x) (((x) << 31) & 0x80000000)
+#define PD_XFI(x) (((x) << 30) & 0x40000000)
+#define XFIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1984)
+#define XFI_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
--
2.34.1
> > This driver would also be a good place to add the KR link training with
> > NXP tried to upstream a few years ago.
>
> Well, speaking as someone who is now also tasked with the copper backplane
> support, believe me that I know that, and this is why I'm so desperate
> with the logic you're trying to push forward. It's clear that we should
> try to collaborate rather than try to push individualistic non-tested
> non-solutions.
Speaking of this, I will send an RFC in the upcoming days which proposes
a model for the copper backplane PHY support on LX2160A and its already
existing Lynx 28G SerDes driver.
I will concern myself with porting that support on Lynx 10G only once
the model I propose turns out to be acceptable for both the network PHY
as well as the generic PHY maintainers.
^ permalink raw reply related [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-10 10:26 ` Vladimir Oltean
@ 2023-08-10 19:58 ` Sean Anderson
2023-08-11 16:12 ` Vladimir Oltean
2023-09-13 22:02 ` Vladimir Oltean
0 siblings, 2 replies; 64+ messages in thread
From: Sean Anderson @ 2023-08-10 19:58 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Vladimir,
On 8/10/23 06:26, Vladimir Oltean wrote:
> Hi Sean,
>
> On Tue, Jun 13, 2023 at 05:27:54PM +0300, Vladimir Oltean wrote:
>> The way things are supposed to work (*if* this works at all) is that the
>> reset state machine starts with a supported PLL / refclk configuration
>> that permits a certain subset of protocols, and the SERDES protocols can
>> be changed from the reset state, as desired, for the individual lanes.
>>
>> What is self-inflicted is that the refclks on your board design are not
>> supportable by any reset state machine configuration, and wiring them
>> that way was a conscious decision. Did your company's board designers
>> receive the recommendation to disconnect RESET_REQ from NXP, and has NXP
>> said that the end result will be something that continues to be supportable?
>> I've searched the customer support database and the answer seems to be no.
>> In any case, if you have to disconnect RESET_REQ from the SoC to make
>> the driver in this form useful, then... yeah, no. You're obviously free
>> to do whatever you want with your products, but that's not how mainline
>> Linux works, it needs to be useful beyond you.
As explained previously (and noted by yourself below) 1G and 10G RCWs
have mutally-incompatible clocking requirements. Now that you have
documented an alternate solution, it is possible to boot up with one RCW
and switch to another. But without that it was not possible to have one
board support both RCWs (without e.g. a microcontroller or FPGA to
configure the clock generator before releasing the processor reset). I
do not think that the silicon should assert the reset request line if
the serdes doesn't lock, but it does and it can't really be disabled.
As it happens, our board is set up so that the reference clocks are
configured for 10G by default. During this boot, reset request is never
requested. If we did not have to support software configuration of the
serdes speed (to support 1G SFPs) we would not have to disconnect reset
request.
That said, I have evaluated the various reasons that reset request can
be asserted, and I have determined that for our product they are not
necessary. The only major limitation is the lack of a watchdog, but that
was not a requirement for us. Because of this, using a GPIO for reset is
sufficient and neatly avoids the issue.
We did not see the need to contact NXP support because we internally
came up with a reliable solution. I suspect that they would not have
suggested the solution you present below, but if you think otherwise I
will try them in the future :)
I would appreciate if you are not so derisive in your comments. I do not
like reading your emails because they are very abrasive.
>> If protocol switching works, I will maintain that your board should have
>> wired the refclks to PLLs the other way around (which is supported by
>> the RCW), and then proceed to do protocol switching to reach the desired
>> configuration.
>
> It was quite a journey to piece everything together.
>
> There is one thing to be mentioned right from the start, and that is
> that on some SoCs (including the LS1043A and LS1046A), the SerDes data
> path is partially controlled by the RCW, and thus, dynamically performing
> a major SerDes protocol reconfiguration requires a RCW override procedure
> (undocumented in the SerDes reconfiguration steps, but all the info you
> need is summarized below).
>
> The DCFG block has a set of RCWSR0 - RCWSR15 read-only status registers
> relative to DCFG_CCSR. What we don't document in the SoC RM, but I got
> permission to say here, is that the DCFG block exposes a second set of
> Expert Mode registers in the DCSR address space (base address 2000_0000h).
> The DCFG_DCSR register region spans from offset 2_0000h to 2_05AC into
> the DCSR base address. At the exact same offsets into DCFG_DCSR as
> RCWSR0 - RCWSR15 are into DCFG_CCSR (aka 100h-13ch), one can find the
> shadow RCWCR0 - RCWCR15 (Reset Control Word Control Register) registers
> which are also writable. There is a one-to-one mapping between each
> register (and field) in RCWSR and RCWCR, so I won't detail the
> contents of the RCWCR registers, because we document RCWSR fully.
>
> RCW override means modifying some of the RCWCR registers. In this
> particular case, finalizing the major SerDes reconfiguration requires
> overriding SRDS_PRTCL_S1 in RCWCR5 with the new per-lane settings, to mux
> the correct PCS to the MAC. In the general case, random RCW overrides
> that don't come from the hardware validation team are unsupported by
> NXP, and you should expect the procedure to yield unpredictable results.
Good to see these finally documented (in some form or other). Perhaps
these could also be used to enable e.g. a UART in the hard-coded RCW
without requiring e.g. JTAG...
> I don't know how much of the above steps is applicable to the other 10G Lynx
> SoCs (LS1088, LS2088 etc). Not all SoCs may require RCW override, and
> the RCW override procedure may not be the same. That is TBD, and we'd
> appreciate if support for other SoCs than the LS1046 to be added no
> earlier than when we have a validated SerDes reconfiguration procedure.
>
> I believe this is enough information to permit the creation of a Linux
> driver on the DCFG_DCSR registers which permits RCW override at runtime.
> It seems this will be necessary at least on LS1046.
>
> We should discuss who and when picks up your patches, removes the
> unsupported, untested and unnecessary code and adds the RCW override
> procedure.
Well, while I don't agree with your characterization (since this code
was indeed tested by me and was indeed necessary), I am glad to see that
you think there is a path forward.
> It can be you, it can also be someone from NXP. If it will be
> you, please let me know, because there are concrete implementation
> choices I want to leave comments on.
I can look into doing this. It will be in my free time, so it will
likely be a bit before I can update this series.
> There is also the previous observation from Ioana that you should not
> delete PHY interrupts without finding out why they don't work.
Well, if you have a better solution, please let me know. The interrupt
does not work in real hardware.
I was hampered in my efforts to determine the cause because the interrupt
passes through an FPGA to which I lack the HDL. So far, I have not seen
any argument against polling except that we do not understand the
problem yet. However, I have not seen any other analysis of the problem
either.
> Only what was thoroughly tested and is based on a hardware validated
> procedure should be submitted. This means only a 1G <-> 10G transition
> on LS1046 for now.
>
> Nonetheless, below is a functional example of how NXP would recommend
> you to achieve the desired PLL mapping for any RCW-based SerDes protocol.
> My testing platform was the LS1046A-QDS with PLL1 at 100 MHz and PLL2 at
> 156.25 MHz. I believe that this should eliminate the need for a clk
> driver for the PLLs, and should make your Ethernet lanes usable much
> earlier than Linux. That being said, our position at NXP is that you
> don't need a clk driver for the PLLs, and I would like to see the clk
> portion removed from future patch revisions.
I have not had any issues with clocking. This is actually one of the
areas where the reference manual is sufficient to create a working
driver. Adding flexibility here is very useful, because we can solve
hardware problems in software. This can reduce e.g. board respins, and
allow for more interesting clocking solutions (such as allowing clock
generators which must be configured before use).
> This patch is on top of https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2fgithub.com%2fnxp%2dqoriq%2frcw%2ftree%2flf%2d6.1.22%2d2.0.0&umid=9173333f-cda0-48a2-a652-ccb5b892d77e&auth=d807158c60b7d2502abde8a2fc01f40662980862-d3ec43c9e78658dbb409cef29887d136eca21c6a
>
> From 9f90d6805883f23a898f9d66826f89b7ba73afe3 Mon Sep 17 00:00:00 2001
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> Date: Thu, 25 May 2023 11:23:41 +0300
> Subject: [PATCH] LS1046A: implement a PBI-based SerDes protocol switching
> mechanism
>
> The LS1046A reset state machine is a bit limited in the SRDS_PRTCL_S1
> protocol combinations that it offers. For example, it offers protocol
> 0x3333 (4x SGMII) only with PLL mapping 2222, and that is good as long
> as dynamic protocol switching between 1G (SGMII) and 10G (XFI) isn't
> desired at runtime.
>
> If that is taken into account as an additional constraint, then we need
> PLL2 (yes, specifically 2) to have a refclk of 156.25 MHz, and that is
> in conflict with the PLL mapping of the aforementioned 0x3333, because
> 1G (SGMII) can only work with a PLL refclk of 100 or 125 MHz, so that's
> what has to be fed into PLL 2.
>
> Dynamic frequency switching of PLLs is hard and out of question.
>
> It is desirable to be able to use PLL2 for the 156.25 MHz refclk
> frequency (for the 10G link modes), and PLL1 for the 100 MHz refclk
> frequency (for the 1G link modes). It turns out, this is possible, even
> if not 100% natively through the reset state machine built-in options.
>
> The strategy is to pick a pair of SerDes refclk frequencies in the RCW
> that is correct for the given board (thus allowing the SerDes PLLs to
> lock, and the SoC to boot) as a first step. The SerDes protocol can be
> absolutely anything as long as the PLL frequencies are right, because as
> a second step, we'll be fixing up the SerDes lane registers for the
> desired final protocol, to appear as if we had that protocol natively.
>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> .../rcw_1600.rcw | 99 +++++++++++++++++
> ls1046ardb/serdes_1133_to_3333.rcw | 73 +++++++++++++
> serdes_10g.rcw | 102 ++++++++++++++++++
> 3 files changed, 274 insertions(+)
> create mode 100644 ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
> create mode 100644 ls1046ardb/serdes_1133_to_3333.rcw
> create mode 100644 serdes_10g.rcw
>
> diff --git a/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw b/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
> new file mode 100644
> index 000000000000..2bbc0163392d
> --- /dev/null
> +++ b/ls1046aqds/RR_FFSNPPPH_1133_5559_to_3333_5559/rcw_1600.rcw
> @@ -0,0 +1,99 @@
> +/*
> + * LS1046AQDS RCW for SerDes Protocol 0x3333_5559 derived from 0x1133_5559
> + *
> + * 42G configuration -- 2 RGMII + 4 SGMII (reconfigurable to XFI) + 3 PCIe + SATA
> + *
> + * Frequencies:
> + *
> + * Sys Clock: 100 MHz
> + * DDR_Refclock: 100 MHz
> + *
> + * Core -- 1600 MHz (Mul 16)
> + * Platform -- 600 MHz (Mul 6)
> + * DDR -- 2100 MT/s (Mul 21)
> + * FMan -- 700 MHz (CGA2 /2)
> + * XFI -- 156.25 MHz (10.3125G)
> + * SGMII -- 100 MHz (5G)
> + * PCIE -- 100 MHz (5G)
> + * eSDHC -- 1400 MHz (CGA2 /1)
> + *
> + * Hardware Accelerator Block Cluster Group A Mux Clock:
> + * FMan - HWA_CGA_M1_CLK_SEL = 6 - Async mode, CGA PLL 2 /2 is clock
> + * eSDHC, QSPI - HWA_CGA_M2_CLK_SEL = 1 - Async mode, CGA PLL 2 /1 is clock
> + *
> + * Serdes Lanes vs Slot information
> + * Serdes1 Lane 0 (D) - Starts as XFI9, switches to SGMII9
> + * Serdes1 Lane 1 (C) - Starts as XFI10, switches to SGMII10
> + * Serdes1 Lane 2 (B) - SGMII5, Slot 1
> + * Serdes1 Lane 3 (A) - SGMII6, Slot 1
> + *
> + * Serdes2 Lane 0 (A) - PCIe1 Gen2 x1, Slot 3
> + * Serdes2 Lane 1 (B) - PCIe2 Gen2 x1, Slot 4
> + * Serdes2 Lane 2 (C) - PCIe3 Gen2 x1, Slot 5
> + * Serdes2 Lane 3 (D) - SATA
> + *
> + * PLL mapping: starts as 2211_2221, ends as 1111_2221
> + *
> + * Serdes 1:
> + * PLL mapping: 1111
> + * SRDS_PLL_REF_CLK_SEL_S1 : 0b'01
> + * SerDes 1, PLL1[160] : 0 - 100MHz for SGMII and PCIe
> + * SerDes 1, PLL2[161] : 1 - 156.25MHz for XFI
> + * SRDS_PLL_PD_S1 : 0b'0
> + * SerDes 1, PLL1 : 0 - not powered down
> + * SerDes 1, PLL2 : 0 - not powered down
> + * SRDS_DIV_PEX_S1 :
> + * Only used for PEX, not used.
> + *
> + * Serdes 2:
> + * PLL mapping: 2221
> + * SRDS_PLL_REF_CLK_SEL_S2 : 0b'00
> + * SerDes 2, PLL1[162] : 0 - 100MHz for SATA
> + * SerDes 2, PLL2[163] : 0 - 100MHz for PCIe
> + * SRDS_PLL_PD_S2 : 0b'00
> + * SerDes 2, PLL1 : 0 - not powered down
> + * SerDes 2, PLL2 : 0 - not powered down
> + * SRDS_DIV_PEX_S2 : 0b'01
> + * 00 - train up to max rate of 8G
> + * 01 - train up to max rate of 5G
> + * 10 - train up to max rate of 2.5G
> + *
> + * DDR clock:
> + * DDR_REFCLK_SEL : 1 - DDRCLK pin provides the reference clock to the DDR PLL
> + *
> + */
> +
> +#include <../ls1046ardb/ls1046a.rcwi>
> +
> +SYS_PLL_RAT=6
> +MEM_PLL_RAT=21
> +CGA_PLL1_RAT=16
> +CGA_PLL2_RAT=14
> +SRDS_PRTCL_S1=4403
> +SRDS_PRTCL_S2=21849
I know it is not typical for NXP RCWs, but your rcw tool supports using
hex/binary prefixes. Thus, you could rewrite the above lines as
SRDS_PRTCL_S1=0x1133
SRDS_PRTCL_S2=0x5559
IMO this is much easier to read, since it matches the documentation.
> +SRDS_PLL_REF_CLK_SEL_S1=1
> +SRDS_PLL_REF_CLK_SEL_S2=0
> +SRDS_DIV_PEX_S1=1
> +SRDS_DIV_PEX_S2=1
> +DDR_FDBK_MULT=2
> +DDR_REFCLK_SEL=1
> +PBI_SRC=14
As another example, here you can do e.g.
PBI_SRC=0b1110
which better matches the documentation.
> +IFC_MODE=37
> +HWA_CGA_M1_CLK_SEL=6
> +DRAM_LAT=1
> +UART_BASE=7
> +IRQ_OUT=1
> +LVDD_VSEL=1
> +TVDD_VSEL=0
> +DVDD_VSEL=2
> +EVDD_VSEL=2
> +IIC2_EXT=1
> +SYSCLK_FREQ=600
> +HWA_CGA_M2_CLK_SEL=1
> +
> +#include <../ls1046ardb/cci_barrier_disable.rcw>
> +#include <../ls1046ardb/usb_phy_freq.rcw>
> +#include <../ls1046ardb/uboot_address.rcw>
> +#include <../ls1046ardb/serdes_sata.rcw>
> +#include <../ls1046ardb/a009531.rcw>
> +#include <../ls1046ardb/serdes_1133_to_3333.rcw>
> diff --git a/ls1046ardb/serdes_1133_to_3333.rcw b/ls1046ardb/serdes_1133_to_3333.rcw
> new file mode 100644
> index 000000000000..ffd548a73675
> --- /dev/null
> +++ b/ls1046ardb/serdes_1133_to_3333.rcw
> @@ -0,0 +1,73 @@
> +/*
> + * Change protocols on SerDes1 from 1133 to 3333, and their PLL mappings from
> + * 2211 to 1111. This is useful because, although the reset state machine has a
> + * native 0x3333 SerDes protocol option, the PLL mapping of that is 2222.
> + * This non-native option frees up PLL 2, and it can be provisioned e.g. with a
> + * 156.25 MHz for any lanes that might want to switch to XFI at runtime.
> + */
> +
> +#define SRDS_BASE 0xea0000 /* SerDes 1 relative to CCSR_BASE */
> +#include <../serdes_10g.rcw>
> +
> +/* For writing outside the CCSR space (in DCSR), an indirect access method is
> + * used. The SCFG_ALTCBAR register (field ALTCFG) holds the upper 24 bits of
> + * the 48-bit address, and the awrite PBL instruction gets the lower 24 bits of
> + * the address that is relative to that. Here we work with 32-bit addresses,
> + * so we only care about the upper 8 bits.
> + */
> +#define SCFG_ALTCBAR 0x570158
> +#define ALTCFG(x) (((x) << 8) & 0xffffff00)
> +#define DCFG_DCSR_RCWCR5 0x20140110
> +#define RCWCR5_SRDS_PRTCL_S1(x) (((x) << 16) & 0xffff0000)
> +#define RCWCR5_SRDS_PRTCL_S2(x) ((x) & 0xffff)
> +#define upper_8_bits(x) (((x) & 0xff000000) >> 24)
> +#define lower_24_bits(x) ((x) & 0xffffff)
> +
> +#define GCR0_SGMII_FROM_PLL1 RPLL_LES(1) | RRAT_SEL(2) | \
> + TPLL_LES(1) | TRAT_SEL(2) | \
> + FIRST_LANE(1) | PROTS(1)
> +
> +.pbi
> +
> +write LNmGCR0(2), RRST_B(0) | TRST_B(0)
> +write LNmGCR0(3), RRST_B(0) | TRST_B(0)
> +
> +wait 50
> +
> +write LNmGCR0(2), GCR0_SGMII_FROM_PLL1
> +write LNmGCR0(3), GCR0_SGMII_FROM_PLL1
> +
> +write LNmGCR1(2), REIDL_TH(1) | REIDL_EX_SEL(3) | REIDL_ET_MSB(1) | \
> + ISLEW_RCTL(1) | OSLEW_RCTL(1)
> +write LNmGCR1(3), REIDL_TH(1) | REIDL_EX_SEL(3) | REIDL_ET_MSB(1) | \
> + ISLEW_RCTL(1) | OSLEW_RCTL(1)
> +
> +write LNmRECR0(2), GK2OVD_EN(1) | GK2OVD(15) | GK3OVD_EN(1) | GK3OVD(15)
> +write LNmRECR0(3), GK2OVD_EN(1) | GK2OVD(15) | GK3OVD_EN(1) | GK3OVD(15)
> +
> +write LNmTECR0(2), ADPT_EQ(48) | AMP_RED(6)
> +write LNmTECR0(3), ADPT_EQ(48) | AMP_RED(6)
> +
> +/* LS1046A requires RCW override to reconfigure the mux between
> + * the PCS and the MAC.
> + */
> +write SCFG_ALTCBAR, ALTCFG(upper_8_bits(DCFG_DCSR_RCWCR5))
> +flush
> +awrite lower_24_bits(DCFG_DCSR_RCWCR5), RCWCR5_SRDS_PRTCL_S1(0x3333) | \
> + RCWCR5_SRDS_PRTCL_S2(0x5559)
> +
> +/* PCCRB: 0x21000000 -> 0x00000000 */
> +write PCCRB, XFIA_CFG(0) | XFIB_CFG(0)
> +
> +/* PCCR8: 0x11000000 -> 0x11110000 */
> +write PCCR8, SGMIIA_CFG(1) | SGMIIB_CFG(1) | SGMIIC_CFG(1) | SGMIID_CFG(1)
> +
> +write SGMIIaCR1(2), SGMII_MDEV_PORT(0) | SGPCS_EN(1)
> +write SGMIIaCR1(3), SGMII_MDEV_PORT(0) | SGPCS_EN(1)
> +
> +wait 120
> +
> +write LNmGCR0(2), GCR0_SGMII_FROM_PLL1 | RRST_B(1) | TRST_B(1)
> +write LNmGCR0(3), GCR0_SGMII_FROM_PLL1 | RRST_B(1) | TRST_B(1)
> +
> +.end
> diff --git a/serdes_10g.rcw b/serdes_10g.rcw
> new file mode 100644
> index 000000000000..714d53fde8af
> --- /dev/null
> +++ b/serdes_10g.rcw
> @@ -0,0 +1,102 @@
> +/*
> + * Registers for the Lynx 10G SerDes block.
> + *
> + * Must be included by an SoC-specific header that defines the
> + * SRDS_BASE value.
> + */
> +
> +#define PLLnRSTCTL(n) (SRDS_BASE + (0x20 * (n)))
> +#define PLLnCR0(n) (SRDS_BASE + (0x20 * (n)) + 0x0004)
> +#define POFF(x) (((x) << 31) & 0x80000000)
> +#define REFCLK_SEL(x) (((x) << 28) & 0x70000000)
> +#define REFCLK_EN(x) (((x) << 27) & 0x08000000)
> +#define FRATE_SEL(x) (((x) << 16) & 0x000f0000)
> +#define DLYDIV_SEL(x) ((x) & 0x00000003)
> +#define PCCR8 (SRDS_BASE + 0x0220)
> +#define SGMIIA_KX(x) (((x) << 31) & 0x80000000)
> +#define SGMIIA_CFG(x) (((x) << 28) & 0x70000000)
> +#define SGMIIB_KX(x) (((x) << 27) & 0x08000000)
> +#define SGMIIB_CFG(x) (((x) << 24) & 0x07000000)
> +#define SGMIIC_KX(x) (((x) << 23) & 0x00800000)
> +#define SGMIIC_CFG(x) (((x) << 20) & 0x00700000)
> +#define SGMIID_KX(x) (((x) << 19) & 0x00080000)
> +#define SGMIID_CFG(x) (((x) << 16) & 0x00070000)
> +#define SGMIIE_KX(x) (((x) << 15) & 0x00008000)
> +#define SGMIIE_CFG(x) (((x) << 12) & 0x00007000)
> +#define SGMIIF_KX(x) (((x) << 11) & 0x00000800)
> +#define SGMIIF_CFG(x) (((x) << 8) & 0x00000700)
> +#define SGMIIG_KX(x) (((x) << 7) & 0x00000080)
> +#define SGMIIG_CFG(x) (((x) << 4) & 0x00000070)
> +#define SGMIIH_KX(x) (((x) << 3) & 0x00000008)
> +#define SGMIIH_CFG(x) ((x) & 0x00000007)
> +#define PCCRB (SRDS_BASE + 0x022c)
> +#define XFIA_CFG(x) (((x) << 28) & 0x70000000)
> +#define XFIB_CFG(x) (((x) << 24) & 0x07000000)
> +#define XFIC_CFG(x) (((x) << 20) & 0x00700000)
> +#define XFID_CFG(x) (((x) << 16) & 0x00070000)
> +#define XFIE_CFG(x) (((x) << 12) & 0x00007000)
> +#define XFIF_CFG(x) (((x) << 8) & 0x00000700)
> +#define XFIG_CFG(x) (((x) << 4) & 0x00000070)
> +#define XFIH_CFG(x) ((x) & 0x00000007)
> +#define LNmGCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0800)
> +#define RPLL_LES(x) (((x) << 31) & 0x80000000)
> +#define RRAT_SEL(x) (((x) << 28) & 0x30000000)
> +#define TPLL_LES(x) (((x) << 27) & 0x08000000)
> +#define TRAT_SEL(x) (((x) << 24) & 0x03000000)
> +#define RRST_B(x) (((x) << 22) & 0x00400000)
> +#define TRST_B(x) (((x) << 21) & 0x00200000)
> +#define RX_PD(x) (((x) << 20) & 0x00100000)
> +#define TX_PD(x) (((x) << 19) & 0x00080000)
> +#define IF20BIT_EN(x) (((x) << 18) & 0x00040000)
> +#define FIRST_LANE(x) (((x) << 16) & 0x00010000)
> +#define GCR0_RSV 0x1000
> +#define PROTS(x) (((x) << 7) & 0x00000f80)
> +#define LNmGCR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0804)
> +#define RDAT_INV(x) (((x) << 31) & 0x80000000)
> +#define TDAT_INV(x) (((x) << 30) & 0x40000000)
> +#define OPAD_CTL(x) (((x) << 26) & 0x04000000)
> +#define REIDL_TH(x) (((x) << 20) & 0x00700000)
> +#define REIDL_EX_SEL(x) (((x) << 18) & 0x000C0000)
> +#define REIDL_ET_SEL(x) (((x) << 16) & 0x00030000)
> +#define REIDL_EX_MSB(x) (((x) << 15) & 0x00008000)
> +#define REIDL_ET_MSB(x) (((x) << 14) & 0x00004000)
> +#define REQ_CTL_SNP(x) (((x) << 13) & 0x00002000)
> +#define REQ_CDR_SNP(x) (((x) << 12) & 0x00001000)
> +#define TRSTDIR(x) (((x) << 7) & 0x00000080)
> +#define REQ_BIN_SNP(x) (((x) << 6) & 0x00000040)
> +#define ISLEW_RCTL(x) (((x) << 4) & 0x00000030)
> +#define GCR1_RSV 0x8
> +#define OSLEW_RCTL(x) ((x) & 0x3)
> +#define LNmRECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0810)
> +#define RXEQ_BST(x) (((x) << 28) & 0x10000000)
> +#define GK2OVD(x) (((x) << 24) & 0x0f000000)
> +#define GK3OVD(x) (((x) << 16) & 0x000f0000)
> +#define GK2OVD_EN(x) (((x) << 15) & 0x00008000)
> +#define GK3OVD_EN(x) (((x) << 14) & 0x00004000)
> +#define OSETOVD_EN(x) (((x) << 13) & 0x00002000)
> +#define BASE_WAND(x) (((x) << 10) & 0x00000c00)
> +#define OSETOVD(x) ((x) & 0x0000007F)
> +#define LNmTECR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0818)
> +#define TEQ_TYPE(x) (((x) << 28) & 0x30000000)
> +#define SGN_PREQ(x) (((x) << 26) & 0x04000000)
> +#define RATIO_PREQ(x) (((x) << 22) & 0x03C00000)
> +#define SGN_POST1Q(x) (((x) << 21) & 0x00200000)
> +#define RATIO_PST1Q(x) (((x) << 16) & 0x001F0000)
> +#define ADPT_EQ(x) (((x) << 8) & 0x00003F00)
> +#define AMP_RED(x) ((x) & 0x0000003f)
> +#define LNmTTLCR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0820)
> +#define LNmTCSR0(m) (SRDS_BASE + (0x40 * (m)) + 0x0830)
> +#define LNmTCSR1(m) (SRDS_BASE + (0x40 * (m)) + 0x0834)
> +#define LNmTCSR2(m) (SRDS_BASE + (0x40 * (m)) + 0x0838)
> +#define LNmTCSR3(m) (SRDS_BASE + (0x40 * (m)) + 0x083c)
> +#define SGMIIaCR0(a) (SRDS_BASE + (0x10 * (a)) + 0x1800)
> +#define RST_SGM(x) (((x) << 31) & 0x80000000)
> +#define PD_SGM(x) (((x) << 30) & 0x40000000)
> +#define SGMIIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1804)
> +#define SGMII_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
> +#define SGPCS_EN(x) (((x) << 11) & 0x00000800)
> +#define XFIaCR0(a) (SRDS_BASE + (0x10 * (a)) + 0x1980)
> +#define RST_XFI(x) (((x) << 31) & 0x80000000)
> +#define PD_XFI(x) (((x) << 30) & 0x40000000)
> +#define XFIaCR1(a) (SRDS_BASE + (0x10 * (a)) + 0x1984)
> +#define XFI_MDEV_PORT(x) (((x) << 27) & 0xf8000000)
> --
> 2.34.1
>
>> > This driver would also be a good place to add the KR link training with
>> > NXP tried to upstream a few years ago.
>>
>> Well, speaking as someone who is now also tasked with the copper backplane
>> support, believe me that I know that, and this is why I'm so desperate
>> with the logic you're trying to push forward. It's clear that we should
>> try to collaborate rather than try to push individualistic non-tested
>> non-solutions.
>
> Speaking of this, I will send an RFC in the upcoming days which proposes
> a model for the copper backplane PHY support on LX2160A and its already
> existing Lynx 28G SerDes driver.
>
> I will concern myself with porting that support on Lynx 10G only once
> the model I propose turns out to be acceptable for both the network PHY
> as well as the generic PHY maintainers.
Well, as far as I know this driver is only a few changes from being
acceptable to these maintainers as well :)
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-10 19:58 ` Sean Anderson
@ 2023-08-11 16:12 ` Vladimir Oltean
2023-09-13 22:02 ` Vladimir Oltean
1 sibling, 0 replies; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-11 16:12 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Sean,
On Thu, Aug 10, 2023 at 03:58:36PM -0400, Sean Anderson wrote:
> As explained previously (and noted by yourself below) 1G and 10G RCWs
> have mutally-incompatible clocking requirements. Now that you have
> documented an alternate solution, it is possible to boot up with one RCW
> and switch to another. But without that it was not possible to have one
> board support both RCWs (without e.g. a microcontroller or FPGA to
> configure the clock generator before releasing the processor reset). I
> do not think that the silicon should assert the reset request line if
> the serdes doesn't lock, but it does and it can't really be disabled.
>
> As it happens, our board is set up so that the reference clocks are
> configured for 10G by default. During this boot, reset request is never
> requested. If we did not have to support software configuration of the
> serdes speed (to support 1G SFPs) we would not have to disconnect reset
> request.
>
> That said, I have evaluated the various reasons that reset request can
> be asserted, and I have determined that for our product they are not
> necessary. The only major limitation is the lack of a watchdog, but that
> was not a requirement for us. Because of this, using a GPIO for reset is
> sufficient and neatly avoids the issue.
I would like to pause here and highlight the existence of the so-called
XY problem: https://en.wikipedia.org/wiki/XY_problem
| The XY problem is a communication problem encountered in help desk,
| technical support, software engineering, or customer service situations
| where the question is about an end user's attempted solution (Y) rather
| than the root problem itself (X).
You admitted that you needed to solve problem X (software reconfiguration
of the SerDes speed between 1G and 10G), and that has led you to problem
Y (giving the PLLs some refclk frequencies which aren't supported at
power-on reset, and thus, are also not validated by NXP). You've presented
to the Linux mailing lists a driver which solves problem Y, but not X.
I gave you a solution to problem X which doesn't even trigger problem Y.
Furthermore, I gave you a solution to problem Y which is much simpler
than yours. On the 12th of June, in Message-ID: 20230612163353.dwouatvqbuo6h4ea@skbuf,
I explained that if you absolutely insist to use the unsupported PLL
refclks, you can use PBL commands to change the PLL settings (so that
they lock) at power-on reset time. The advantage is that both U-Boot and
Linux will work without having to make any modification to the PLLs,
just treat them as read-only.
As it happens, at NXP we also want to solve problem X in a generic way
(aka we need a procedure that works for all customers, and not just for
your board), and we want to do so in a way that the hardware validation
team agrees with. Thus, the SoC needs to accept the PLL refclks at
power-on reset time. The solution we came up with is the one presented
to you yesterday. It makes your PLL clk driver unnecessary. No one will
come after you if you keep it in your Linux tree and use it, but it is
unnecessary.
> > Nonetheless, below is a functional example of how NXP would recommend
> > you to achieve the desired PLL mapping for any RCW-based SerDes protocol.
> > My testing platform was the LS1046A-QDS with PLL1 at 100 MHz and PLL2 at
> > 156.25 MHz. I believe that this should eliminate the need for a clk
> > driver for the PLLs, and should make your Ethernet lanes usable much
> > earlier than Linux. That being said, our position at NXP is that you
> > don't need a clk driver for the PLLs, and I would like to see the clk
> > portion removed from future patch revisions.
>
> I have not had any issues with clocking. This is actually one of the
> areas where the reference manual is sufficient to create a working
> driver. Adding flexibility here is very useful, because we can solve
> hardware problems in software. This can reduce e.g. board respins, and
> allow for more interesting clocking solutions (such as allowing clock
> generators which must be configured before use).
I am waiting for someone to come up with a real life use case that
justifies those "more interesting clocking solutions".
Please correct me if you think I am wrong, but as things stand, the
SerDes PLL clk driver is now a solution waiting for a problem. It can
wait for that problem out of tree.
> > There is also the previous observation from Ioana that you should not
> > delete PHY interrupts without finding out why they don't work.
>
> Well, if you have a better solution, please let me know. The interrupt
> does not work in real hardware.
>
> I was hampered in my efforts to determine the cause because the interrupt
> passes through an FPGA to which I lack the HDL. So far, I have not seen
> any argument against polling except that we do not understand the
> problem yet. However, I have not seen any other analysis of the problem
> either.
Out of respect for the topic at hand, ask for help in a separate thread,
open an NXP support ticket - do something to split it from the SerDes
work which it is unrelated with. You were told that part of the reason
for the NACK is the fact that you are grouping unrelated things together,
and you did nothing about it.
> > +SRDS_PRTCL_S1=4403
> > +SRDS_PRTCL_S2=21849
>
> I know it is not typical for NXP RCWs, but your rcw tool supports using
> hex/binary prefixes. Thus, you could rewrite the above lines as
>
> SRDS_PRTCL_S1=0x1133
> SRDS_PRTCL_S2=0x5559
>
> IMO this is much easier to read, since it matches the documentation.
Ok, thanks.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-10 19:58 ` Sean Anderson
2023-08-11 16:12 ` Vladimir Oltean
@ 2023-09-13 22:02 ` Vladimir Oltean
1 sibling, 0 replies; 64+ messages in thread
From: Vladimir Oltean @ 2023-09-13 22:02 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Sean,
On Thu, Aug 10, 2023 at 03:58:36PM -0400, Sean Anderson wrote:
> I can look into doing this. It will be in my free time, so it will
> likely be a bit before I can update this series.
I was expecting you'd ask some clarification questions about the RCW
override procedure that I've informally described over email, so I guess
you haven't spent any more time on this.
I'm letting you know that very soon, I will have to start my work on
porting the backplane driver posted here:
https://patchwork.kernel.org/project/netdevbpf/cover/20230817150644.3605105-1-vladimir.oltean@nxp.com/
to the Lynx 10G SoCs. And for that, I need a SerDes driver as a base :)
I was wondering how inclined are you to respond positively to the
feedback that the lynx-10g driver should have a look and feel as close
as possible to lynx-28g, given that they're very similar.
Because internally within NXP, we do have a version of the lynx-10g
driver which is contemporary with lynx-28g from mainline, but we didn't
publish it because protocol changes didn't work (for the same reason
that they don't work with your driver). With that driver, you can think
of the feedback about the similar look and feel as being "implicitly applied"
(being written by the same author), so I'm starting to consider more and
more seriously the option of basing my work on that instead of your v14
(on which I'd need to spend extra time to modify the dt-bindings with PCCRs,
concept of lane groups, concept of PLL CCF driver, etc).
What are your thoughts?
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-06-13 14:27 ` Vladimir Oltean
2023-08-10 10:26 ` Vladimir Oltean
@ 2023-08-11 15:08 ` Vladimir Oltean
2023-08-11 15:43 ` Sean Anderson
1 sibling, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-11 15:08 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Sean,
On Tue, Jun 13, 2023 at 05:27:54PM +0300, Vladimir Oltean wrote:
> > > At first sight you might appear to have a point related to the fact that
> > > PLL register writes are necessary, and thus this whole shebang is necessary.
> > > But this can all be done using PBI commands, with the added benefit that
> > > U-Boot can also use those SERDES networking ports, and not just Linux.
> > > You can use the RCW+PBL specific to your board to inform the SoC that
> > > your platform's refclk 1 is 156 MHz (something which the reset state
> > > machine seems unable to learn, with protocol 0x3333). You don't have to
> > > put that in the device tree. You don't have to push code to any open
> > > source project to expose your platform specific details. Then, just like
> > > in the case of the Lynx 28G driver on LX2160, the SERDES driver could
> > > just treat the PLL configuration as read-only, which would greatly
> > > simplify things and eliminate the need for a clk driver.
> > >
> > > Here is an illustrative example (sorry, I don't have a board with the
> > > right refclk on that PLL, to verify all the way):
> > >
> > > ... snip ...
> >
> > (which of course complicates the process of building the PBIs...)
>
> Maybe this is the language barrier, but what are you trying to say here?
I said that I don't understand. Can you please clarify what you were
trying to transmit with this comment?
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-11 15:08 ` Vladimir Oltean
@ 2023-08-11 15:43 ` Sean Anderson
2023-08-11 16:36 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-08-11 15:43 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/11/23 11:08, Vladimir Oltean wrote:
> Hi Sean,
>
> On Tue, Jun 13, 2023 at 05:27:54PM +0300, Vladimir Oltean wrote:
>> > > At first sight you might appear to have a point related to the fact that
>> > > PLL register writes are necessary, and thus this whole shebang is necessary.
>> > > But this can all be done using PBI commands, with the added benefit that
>> > > U-Boot can also use those SERDES networking ports, and not just Linux.
>> > > You can use the RCW+PBL specific to your board to inform the SoC that
>> > > your platform's refclk 1 is 156 MHz (something which the reset state
>> > > machine seems unable to learn, with protocol 0x3333). You don't have to
>> > > put that in the device tree. You don't have to push code to any open
>> > > source project to expose your platform specific details. Then, just like
>> > > in the case of the Lynx 28G driver on LX2160, the SERDES driver could
>> > > just treat the PLL configuration as read-only, which would greatly
>> > > simplify things and eliminate the need for a clk driver.
>> > >
>> > > Here is an illustrative example (sorry, I don't have a board with the
>> > > right refclk on that PLL, to verify all the way):
>> > >
>> > > ... snip ...
>> >
>> > (which of course complicates the process of building the PBIs...)
>>
>> Maybe this is the language barrier, but what are you trying to say here?
>
> I said that I don't understand. Can you please clarify what you were
> trying to transmit with this comment?
Well, right now I produce my RCWs by generating a second RCW with e.g.
1133 replaced by 3333. But doing this is a more involved change. Just a
minor issue, really. That said, I don't think this is the best approach
moving forward. Much like many other platforms, users should be able to
plug in an SFP module and Linux should configure things appropriately.
An RCW approach requires some kind of configuration tool to swap out the
bootloaders, which isn't as good of a user experience.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-11 15:43 ` Sean Anderson
@ 2023-08-11 16:36 ` Vladimir Oltean
2023-08-21 12:49 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-11 16:36 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Fri, Aug 11, 2023 at 11:43:01AM -0400, Sean Anderson wrote:
> >> > > Here is an illustrative example (sorry, I don't have a board with the
> >> > > right refclk on that PLL, to verify all the way):
> >> > >
> >> > > ... snip ...
> >> >
> >> > (which of course complicates the process of building the PBIs...)
> >>
> >> Maybe this is the language barrier, but what are you trying to say here?
> >
> > I said that I don't understand. Can you please clarify what you were
> > trying to transmit with this comment?
>
> Well, right now I produce my RCWs by generating a second RCW with e.g.
> 1133 replaced by 3333. But doing this is a more involved change. Just a
> minor issue, really.
Ok, so a comment related to your private build environment, and not a
counter-argument to the solution. It wasn't clear.
> That said, I don't think this is the best approach moving forward.
> Much like many other platforms, users should be able to plug in an SFP
> module and Linux should configure things appropriately. An RCW
> approach requires some kind of configuration tool to swap out the
> bootloaders, which isn't as good of a user experience.
I am quite familiar with the SFP use case you are talking about. SolidRun
requested that on LX2160 with the Lynx 28G driver, and that works fine
there. What I am proposing is for the exact same approach to be used
with Lynx 10G as well.
Let me explain that approach, because your mention of "swapping out the
bootloaders" makes it appear as if you are not visualising what I am
proposing.
The Lynx SerDes family has 2 PLLs, and more lanes (4 or 8). Each lane
uses one PLL or the other, to derive its protocol frequency. Through the
RCW, you provision the 2 PLL frequencies that may be used by the lanes
at runtime.
The Lynx 28G SerDes driver reads the PLL frequencies in
lynx_28g_pll_read_configuration(), and determines the interface modes
supportable by each PLL (this is used by phylink). But it never changes
those PLL frequencies, since that operation is practically impossible in
the general sense (PLLs are shared by multiple lanes, so changing a PLL
frequency disrupts all lanes that use it).
So, you see, the RCW approach that I am proposing is not the RCW
approach that you are thinking about. I was never suggesting to swap the
bootloader when the user plugs in a different SFP. I am just suggesting
that the RCW is provisioned for 2 different PLL frequencies, and those PLL
frequencies give you 2 networking protocol speed options. Depending on
what each lane needs (the information comes from phylink), you can move
each lane to one PLL or the other, at runtime, in the Linux SerDes driver,
to support the speeds of 1G and 10G, or 10G and 25G. You can't get more
than 2 speeds at the same time with just 2 PLLs, and this is an
intrinsic limitation of the fact that there are just 2 PLLs.
If the RCW and board do not provision the PLL frequencies for more
than 1 lane speed, then no dynamic protocol switching is possible, and
the supported_interfaces of each lane contains a single bit: the
reset-time protocol.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-11 16:36 ` Vladimir Oltean
@ 2023-08-21 12:49 ` Vladimir Oltean
2023-08-21 17:45 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-21 12:49 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
Hi Sean,
On Fri, Aug 11, 2023 at 07:36:37PM +0300, Vladimir Oltean wrote:
> Let me explain that approach, because your mention of "swapping out the
> bootloaders" makes it appear as if you are not visualising what I am
> proposing.
>
> The Lynx SerDes family has 2 PLLs, and more lanes (4 or 8). Each lane
> uses one PLL or the other, to derive its protocol frequency. Through the
> RCW, you provision the 2 PLL frequencies that may be used by the lanes
> at runtime.
>
> The Lynx 28G SerDes driver reads the PLL frequencies in
> lynx_28g_pll_read_configuration(), and determines the interface modes
> supportable by each PLL (this is used by phylink). But it never changes
> those PLL frequencies, since that operation is practically impossible in
> the general sense (PLLs are shared by multiple lanes, so changing a PLL
> frequency disrupts all lanes that use it).
Is my high-level feedback clear and actionable to you? I am suggesting
to keep the look and feel the same between the lynx-10g and lynx-28g
drivers, and to not use "fsl,type" protocols listed in the device tree
as the immutable source of information for populating mode->protos, but
instead the current PLL frequency configuration. So this implies that I
am requesting that the dt-bindings should not contain a listing of the
supported protocols.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 12:49 ` Vladimir Oltean
@ 2023-08-21 17:45 ` Sean Anderson
2023-08-21 18:13 ` Ioana Ciornei
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-08-21 17:45 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/21/23 08:49, Vladimir Oltean wrote:
> Hi Sean,
>
> On Fri, Aug 11, 2023 at 07:36:37PM +0300, Vladimir Oltean wrote:
>> Let me explain that approach, because your mention of "swapping out the
>> bootloaders" makes it appear as if you are not visualising what I am
>> proposing.
>>
>> The Lynx SerDes family has 2 PLLs, and more lanes (4 or 8). Each lane
>> uses one PLL or the other, to derive its protocol frequency. Through the
>> RCW, you provision the 2 PLL frequencies that may be used by the lanes
>> at runtime.
>>
>> The Lynx 28G SerDes driver reads the PLL frequencies in
>> lynx_28g_pll_read_configuration(), and determines the interface modes
>> supportable by each PLL (this is used by phylink). But it never changes
>> those PLL frequencies, since that operation is practically impossible in
>> the general sense (PLLs are shared by multiple lanes, so changing a PLL
>> frequency disrupts all lanes that use it).
>
> Is my high-level feedback clear and actionable to you? I am suggesting
> to keep the look and feel the same between the lynx-10g and lynx-28g
> drivers, and to not use "fsl,type" protocols listed in the device tree
> as the immutable source of information for populating mode->protos, but
> instead the current PLL frequency configuration. So this implies that I
> am requesting that the dt-bindings should not contain a listing of the
> supported protocols.
Well, we have two pieces of information we need
- What values do we need to program in the PCCRs to select a particular
mode? This includes whether to e.g. set the KX bits.
- Implied by the above, what protocols are supported on which lanes?
This is not strictly necessary, but will certainly solve a lot of
headscratching.
This information varies between different socs, and different serdes on
the same socs. We can't really look at the RCW or the clocks and figure
out what we need to program. So what are our options?
- We can have a separate compatible for each serdes on each SoC (e.g.
"fsl,lynx-10g-a"). This was rejected by the devicetree maintainers.
- We can have one compatible for each SoC, and determine the serdes
based on the address. I would like to avoid this...
- We can stick all the details which vary between serdes/socs into the
device tree. This is very flexible, since supporting new SoCs is
mostly a matter of adding a new compatible and writing a new
devicetree. On the other hand, if you have a bug in your devicetree,
it's not easy to fix it in the kernel.
- Just don't support protocol switching. The 28G driver does this, which
is why it only has one compatible. However, supporting protocol
switching is a core goal of this driver, so dropping support is not an
option.
I'm open to any other suggestions you have, but this was the best way I
could figure out to get this information to the driver in a way which
would be acceptable to the devicetree folks.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 17:45 ` Sean Anderson
@ 2023-08-21 18:13 ` Ioana Ciornei
2023-08-21 18:20 ` Vladimir Oltean
2023-08-21 18:46 ` Sean Anderson
0 siblings, 2 replies; 64+ messages in thread
From: Ioana Ciornei @ 2023-08-21 18:13 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Vladimir Oltean, Michael Turquette, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Aug 21, 2023 at 01:45:44PM -0400, Sean Anderson wrote:
> Well, we have two pieces of information we need
>
> - What values do we need to program in the PCCRs to select a particular
> mode? This includes whether to e.g. set the KX bits.
> - Implied by the above, what protocols are supported on which lanes?
> This is not strictly necessary, but will certainly solve a lot of
> headscratching.
>
> This information varies between different socs, and different serdes on
> the same socs. We can't really look at the RCW or the clocks and figure
> out what we need to program. So what are our options?
>
> - We can have a separate compatible for each serdes on each SoC (e.g.
> "fsl,lynx-10g-a"). This was rejected by the devicetree maintainers.
> - We can have one compatible for each SoC, and determine the serdes
> based on the address. I would like to avoid this...
To me this really seems like a straightforward approach.
> - We can stick all the details which vary between serdes/socs into the
> device tree. This is very flexible, since supporting new SoCs is
> mostly a matter of adding a new compatible and writing a new
> devicetree. On the other hand, if you have a bug in your devicetree,
> it's not easy to fix it in the kernel.
> - Just don't support protocol switching. The 28G driver does this, which
> is why it only has one compatible. However, supporting protocol
> switching is a core goal of this driver, so dropping support is not an
> option.
>
The Lynx 28G SerDes driver does support protocol switching.
How did you arrive at the opposite conclusion?
The initial commit on the driver is even part of a patch set named
"dpaa2-mac: add support for changing the protocol at runtime". In
upstream it only supports the 1G <-> 10G transition but I have some
patches on the way to also support 25G.
Ioana
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 18:13 ` Ioana Ciornei
@ 2023-08-21 18:20 ` Vladimir Oltean
2023-08-21 18:46 ` Sean Anderson
1 sibling, 0 replies; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-21 18:20 UTC (permalink / raw)
To: Ioana Ciornei
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Krzysztof Kozlowski, Jonas Gorski, linux-phy,
linux-clk, Kishon Vijay Abraham I, Jonathan Corbet, Sean Anderson,
Bartosz Golaszewski, linux-doc, Camelia Alexandra Groza,
Linus Walleij, devicetree, linux-gpio, Rob Herring,
linux-arm-kernel, Stephen Boyd, linuxppc-dev, Li Yang, Vinod Koul,
Shawn Guo
On Mon, Aug 21, 2023 at 09:13:49PM +0300, Ioana Ciornei wrote:
> > - We can have one compatible for each SoC, and determine the serdes
> > based on the address. I would like to avoid this...
>
> To me this really seems like a straightforward approach.
+1
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 18:13 ` Ioana Ciornei
2023-08-21 18:20 ` Vladimir Oltean
@ 2023-08-21 18:46 ` Sean Anderson
2023-08-21 19:58 ` Vladimir Oltean
2023-08-22 14:55 ` Ioana Ciornei
1 sibling, 2 replies; 64+ messages in thread
From: Sean Anderson @ 2023-08-21 18:46 UTC (permalink / raw)
To: Ioana Ciornei
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Vladimir Oltean, Michael Turquette, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/21/23 14:13, Ioana Ciornei wrote:
> On Mon, Aug 21, 2023 at 01:45:44PM -0400, Sean Anderson wrote:
>> Well, we have two pieces of information we need
>>
>> - What values do we need to program in the PCCRs to select a particular
>> mode? This includes whether to e.g. set the KX bits.
>> - Implied by the above, what protocols are supported on which lanes?
>> This is not strictly necessary, but will certainly solve a lot of
>> headscratching.
>>
>> This information varies between different socs, and different serdes on
>> the same socs. We can't really look at the RCW or the clocks and figure
>> out what we need to program. So what are our options?
>>
>> - We can have a separate compatible for each serdes on each SoC (e.g.
>> "fsl,lynx-10g-a"). This was rejected by the devicetree maintainers.
>> - We can have one compatible for each SoC, and determine the serdes
>> based on the address. I would like to avoid this...
>
> To me this really seems like a straightforward approach.
Indeed it would be straightforward, but what's the point of having a
devicetree in the first place then? We could just go back to being a
(non-dt) platform device.
>> - We can stick all the details which vary between serdes/socs into the
>> device tree. This is very flexible, since supporting new SoCs is
>> mostly a matter of adding a new compatible and writing a new
>> devicetree. On the other hand, if you have a bug in your devicetree,
>> it's not easy to fix it in the kernel.
>> - Just don't support protocol switching. The 28G driver does this, which
>> is why it only has one compatible. However, supporting protocol
>> switching is a core goal of this driver, so dropping support is not an
>> option.
>>
>
> The Lynx 28G SerDes driver does support protocol switching.
> How did you arrive at the opposite conclusion?
Sorry, it's been a while and I just did a quick look-over, and noticed
there was no configuration for different SoCs.
After further review, it seems the reason 28g can get away without this
is because there's a one-to-one mapping between protocol controllers and
lanes. Unfortunately, that regularity is not present for 10g.
--Sean
> The initial commit on the driver is even part of a patch set named
> "dpaa2-mac: add support for changing the protocol at runtime". In
> upstream it only supports the 1G <-> 10G transition but I have some
> patches on the way to also support 25G.
>
> Ioana
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 18:46 ` Sean Anderson
@ 2023-08-21 19:58 ` Vladimir Oltean
2023-08-21 21:06 ` Sean Anderson
2023-08-22 14:55 ` Ioana Ciornei
1 sibling, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-21 19:58 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
> After further review, it seems the reason 28g can get away without this
> is because there's a one-to-one mapping between protocol controllers and
> lanes. Unfortunately, that regularity is not present for 10g.
>
> --Sean
There are some things I saw in your phy-fsl-lynx-10g.c driver and device
tree bindings that I don't understand (the concept of lane groups), and
I'm not sure if they're related with what you're saying here, so if you
could elaborate a bit more (ideally with an example) on the one-to-one
mapping and the specific problems it causes, it would be great.
I may be off with my understanding of the regularity you are talking about,
but the LX2160 (and Lynx 28G block) also has multi-lane protocols like 40G,
100G, assuming that's what you are talking about. I haven't started yet
working on those for the mtip_backplane driver, but I'm not currently
seeing a problem with the architecture where a phy_device represents a
single lane that's part of a multi-lane port, and not an entire group.
In my imagination, there are 2 cases:
- all 4 lanes are managed by the single dpaa2-mac consumer (which has 4
phandles, and iterates over them with a "for" loop)
- each of the 4 lanes is managed by the respective backplane AN/LT core,
and thus, there's one phandle to each lane
I sketched some dt-bindings for the second case here, so I guess it must
be the first scenario that's somehow problematic?
https://patchwork.kernel.org/project/netdevbpf/patch/20230817150644.3605105-9-vladimir.oltean@nxp.com/
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 19:58 ` Vladimir Oltean
@ 2023-08-21 21:06 ` Sean Anderson
2023-08-21 22:48 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-08-21 21:06 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/21/23 15:58, Vladimir Oltean wrote:
> On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
>> After further review, it seems the reason 28g can get away without this
>> is because there's a one-to-one mapping between protocol controllers and
>> lanes. Unfortunately, that regularity is not present for 10g.
>>
>> --Sean
>
> There are some things I saw in your phy-fsl-lynx-10g.c driver and device
> tree bindings that I don't understand (the concept of lane groups)
Each lane group corresponds to a phy device (struct phy). For protocols
like PCIe or XAUI which can use multiple lanes, this lets the driver
coordinate configuring all lanes at once in the correct order.
> and
> I'm not sure if they're related with what you're saying here, so if you
> could elaborate a bit more (ideally with an example) on the one-to-one
> mapping and the specific problems it causes, it would be great.
For e.g. the LS2088A, SerDes1 lanes H-A use SG1-8 and XFI1-8. SerDes2
lanes A-H use SG9-16 and XFI9-16. Each lane has its own controller, and
the mapping is 1-to-1. In the PCCRs, each controller uses the same
value, and is mapped in a regular way. So you can go directly from the
lane number to the right value to mask in the PCCR, with a very simple
translation scheme.
For e.g. the LS1046A, SerDes1 lane D uses XFI.9 (aka XFIA) and lane C
uses XFI.10 (aka XFIB). This is the opposite of how SerDes1 lanes A-D
use SGMII.9, .10, .5, and .6 (aka SGMIIA-D).
For e.g. the T4240, SerDes1 lanes A-H use sg1.5, .6, .10, .9, .1, .2,
.3, .4 (aka SGMII E, F, H, G, A, B, C, D).
For e.g. the B4860, SerDes lanes A uses sgmii1 or sgmii5 and B uses
sgmii2 or sgmii6. The MAC selected is determined based on the value
programmed into PCCR2.
While I appreciate that your hardware engineers did a better job for
28g, many 10g serdes arbitrarily map lanes to protocol controllers.
I think the mapping is too irregular to tame, and it is better to say
"if you want this configuration, program this value".
> I may be off with my understanding of the regularity you are talking about,
> but the LX2160 (and Lynx 28G block) also has multi-lane protocols like 40G,
> 100G, assuming that's what you are talking about. I haven't started yet
> working on those for the mtip_backplane driver, but I'm not currently
> seeing a problem with the architecture where a phy_device represents a
> single lane that's part of a multi-lane port, and not an entire group.
Resetting one lane in a group will reset the rest, which could confuse
the driver. Additionally, treating the lanes as one phy lets us set the
reset direction and first lane bits correctly.
> In my imagination, there are 2 cases:
> - all 4 lanes are managed by the single dpaa2-mac consumer (which has 4
> phandles, and iterates over them with a "for" loop)
> - each of the 4 lanes is managed by the respective backplane AN/LT core,
> and thus, there's one phandle to each lane
By doing the grouping in the driver, we also simplify the consumer
implementation. The MAC can always use a single phy, without worrying
about the actual number of lanes. This matches the hardware, since the
MAC is going to talk XGMII (or whatever) to the protocol controller
anyway.
I think it will be a lot easier to add multi-lane support with this
method because it gives the driver more information about what's going
on. The driver can control the whole configuration/reset process and the
timing.
> I sketched some dt-bindings for the second case here, so I guess it must
> be the first scenario that's somehow problematic?
> https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2fpatchwork.kernel.org%2fproject%2fnetdevbpf%2fpatch%2f20230817150644.3605105%2d9%2dvladimir.oltean%40nxp.com%2f&umid=9e644233-009e-4197-a266-5d9a85eb1148&auth=d807158c60b7d2502abde8a2fc01f40662980862-cc1d5330d84af8fa40745b165a44849db50f8a67
Yes, no issues with the second case.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 21:06 ` Sean Anderson
@ 2023-08-21 22:48 ` Vladimir Oltean
2023-08-21 23:39 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-21 22:48 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Aug 21, 2023 at 05:06:46PM -0400, Sean Anderson wrote:
> On 8/21/23 15:58, Vladimir Oltean wrote:
> > On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
> >> After further review, it seems the reason 28g can get away without this
> >> is because there's a one-to-one mapping between protocol controllers and
> >> lanes. Unfortunately, that regularity is not present for 10g.
> >>
> >> --Sean
> >
> > There are some things I saw in your phy-fsl-lynx-10g.c driver and device
> > tree bindings that I don't understand (the concept of lane groups)
>
> Each lane group corresponds to a phy device (struct phy). For protocols
> like PCIe or XAUI which can use multiple lanes, this lets the driver
> coordinate configuring all lanes at once in the correct order.
For PCIe I admit that I don't know. I don't even know if there's any
valid (current or future) use case for having the PCIe controller have a
phandle to the SerDes. Everything else below in my reply assumes Ethernet.
> > and
> > I'm not sure if they're related with what you're saying here, so if you
> > could elaborate a bit more (ideally with an example) on the one-to-one
> > mapping and the specific problems it causes, it would be great.
>
> For e.g. the LS2088A, SerDes1 lanes H-A use SG1-8 and XFI1-8. SerDes2
> lanes A-H use SG9-16 and XFI9-16. Each lane has its own controller, and
> the mapping is 1-to-1. In the PCCRs, each controller uses the same
> value, and is mapped in a regular way. So you can go directly from the
> lane number to the right value to mask in the PCCR, with a very simple
> translation scheme.
>
> For e.g. the LS1046A, SerDes1 lane D uses XFI.9 (aka XFIA) and lane C
> uses XFI.10 (aka XFIB). This is the opposite of how SerDes1 lanes A-D
> use SGMII.9, .10, .5, and .6 (aka SGMIIA-D).
>
> For e.g. the T4240, SerDes1 lanes A-H use sg1.5, .6, .10, .9, .1, .2,
> .3, .4 (aka SGMII E, F, H, G, A, B, C, D).
>
> For e.g. the B4860, SerDes lanes A uses sgmii1 or sgmii5 and B uses
> sgmii2 or sgmii6. The MAC selected is determined based on the value
> programmed into PCCR2.
It's so exceedingly unlikely that B4860 will gain support for anything
new, that it's not even worth talking about it, or even considering it
in the design of a new driver. Just forget about it.
Let's concentrate on the Layerscapes, and on the T series to the extent
that we're not going out of our way to support them with a fairly simple
design.
In the Lynx 10G block guide that I have, PCCR2 is a register that does
something completely different from Ethernet. I'm not sure if B4860 has
a Lynx 10G block and not something else.
> While I appreciate that your hardware engineers did a better job for
> 28g, many 10g serdes arbitrarily map lanes to protocol controllers.
> I think the mapping is too irregular to tame, and it is better to say
> "if you want this configuration, program this value".
Ok, but that's a lateral argument (or I'm not understanding the connection).
Some maintainers (Mark Brown for sure, from my personal experience) prefer
that expert-level knowledge of hardware should be hardcoded into the kernel
driver based on known stuff such as the SoC-specific compatible string.
I certainly share the same view.
With your case, I think that argument is even more pertinent, because
IIUC, the lane group protocols won't be put in the SoC .dtsi (so as to
be written only once), but in the board device trees (since the
available protocols invariably depend upon the board provisioning).
So, non-expert board device tree writers will have to know what's with
the PCCR stuff. Pretty brain-intensive.
> > I may be off with my understanding of the regularity you are talking about,
> > but the LX2160 (and Lynx 28G block) also has multi-lane protocols like 40G,
> > 100G, assuming that's what you are talking about. I haven't started yet
> > working on those for the mtip_backplane driver, but I'm not currently
> > seeing a problem with the architecture where a phy_device represents a
> > single lane that's part of a multi-lane port, and not an entire group.
>
> Resetting one lane in a group will reset the rest, which could confuse
> the driver. Additionally, treating the lanes as one phy lets us set the
> reset direction and first lane bits correctly.
Yeah, in theory that is probably correct, but in practice can't we hide
our head in the sand and say that the "phys" phandles have to have the
lanes in the same order as LNmGCR0[1STLANE] expects them (which is also
the "natural" order as the SoC RM describes it)? With a "for" loop
implementation in the MAC, that would work just fine 100% of the time.
Doing more intricate massaging of the "phys" in the consumer, and you're
just asking for trouble. My 2 cents.
Sure, it's the same kind of ask of a board device tree writer as "hey,
please give me a good PCCR value", but I honestly think that the head
scratching will be much less severe.
> > In my imagination, there are 2 cases:
> > - all 4 lanes are managed by the single dpaa2-mac consumer (which has 4
> > phandles, and iterates over them with a "for" loop)
> > - each of the 4 lanes is managed by the respective backplane AN/LT core,
> > and thus, there's one phandle to each lane
>
> By doing the grouping in the driver, we also simplify the consumer
> implementation. The MAC can always use a single phy, without worrying
> about the actual number of lanes. This matches the hardware, since the
> MAC is going to talk XGMII (or whatever) to the protocol controller
> anyway.
XGMII is the link between the MAC and the PCS, but the "phys" phandle to
the SerDes gives insight to the MAC driver way beyond the PCS layer.
That kinda invalidates the idea that "you don't need to worry about the
actual number of lanes". When you're a MAC driver with an XLAUI link and
you need insight into the PMA/PMD layer, you'd better not be surprised
about the fact that there are 4 lanes, at the very least?
> I think it will be a lot easier to add multi-lane support with this
> method because it gives the driver more information about what's going
> on. The driver can control the whole configuration/reset process and the
> timing.
Also, I'm thinking that if you support multi-lane (which dpaa2-mac currently
doesn't, even in LSDK), you can't avoid multiple "phys" phandles in dpaa2-mac,
and a "for" loop, eventually, anyway. That's because if your lanes have protocol
retimers on them, those are going to be modeled as generic PHYs too. And those
will not be bundled in these "groups", because they might be one chip per lane.
The retimer thing isn't theoretical, but, due to reasons independent of NXP,
we lack the ability to provide an upstream user of the "lane retimer as
generic PHY" functionality in dpaa2-mac. So it stays downstream for now.
https://github.com/nxp-qoriq/linux/commit/627c5f626a13657f46f68b90882f329310e0e22f
So, if you're thinking of easing the work of the consumer side, I'm not
sure that the gains will be that high.
Otherwise, I will repeat the idea that lynx-10g and lynx-28g should be
treated in unison, because some drivers (dpaa2-mac, mtip_backplane) will
have to interface with both, and I don't really believe that major deviations
in software architecture between the 2 SerDes drivers are justifiable in
any way (multi-protocol handled differently, for example).
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 22:48 ` Vladimir Oltean
@ 2023-08-21 23:39 ` Sean Anderson
2023-08-21 23:59 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-08-21 23:39 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/21/23 18:48, Vladimir Oltean wrote:
> On Mon, Aug 21, 2023 at 05:06:46PM -0400, Sean Anderson wrote:
>> On 8/21/23 15:58, Vladimir Oltean wrote:
>> > On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
>> >> After further review, it seems the reason 28g can get away without this
>> >> is because there's a one-to-one mapping between protocol controllers and
>> >> lanes. Unfortunately, that regularity is not present for 10g.
>> >>
>> >> --Sean
>> >
>> > There are some things I saw in your phy-fsl-lynx-10g.c driver and device
>> > tree bindings that I don't understand (the concept of lane groups)
>>
>> Each lane group corresponds to a phy device (struct phy). For protocols
>> like PCIe or XAUI which can use multiple lanes, this lets the driver
>> coordinate configuring all lanes at once in the correct order.
>
> For PCIe I admit that I don't know. I don't even know if there's any
> valid (current or future) use case for having the PCIe controller have a
> phandle to the SerDes. Everything else below in my reply assumes Ethernet.
One use case could be selecting PCIe/SATA as appropriate for an M.2
card. Another use case could be allocating lanes to different slots
based on card presence (e.g. 1 card use x4, 2+ cards use x1). I recently
bought a motherboard whose manual says
| PCI_E4 [normally x4] will run x1 speed when installing devices in PCI_E2/
| PCI_E3/ PCI_E5 slot [all x1].
which implies exactly this sort of design.
>> > and
>> > I'm not sure if they're related with what you're saying here, so if you
>> > could elaborate a bit more (ideally with an example) on the one-to-one
>> > mapping and the specific problems it causes, it would be great.
>>
>> For e.g. the LS2088A, SerDes1 lanes H-A use SG1-8 and XFI1-8. SerDes2
>> lanes A-H use SG9-16 and XFI9-16. Each lane has its own controller, and
>> the mapping is 1-to-1. In the PCCRs, each controller uses the same
>> value, and is mapped in a regular way. So you can go directly from the
>> lane number to the right value to mask in the PCCR, with a very simple
>> translation scheme.
>>
>> For e.g. the LS1046A, SerDes1 lane D uses XFI.9 (aka XFIA) and lane C
>> uses XFI.10 (aka XFIB). This is the opposite of how SerDes1 lanes A-D
>> use SGMII.9, .10, .5, and .6 (aka SGMIIA-D).
>>
>> For e.g. the T4240, SerDes1 lanes A-H use sg1.5, .6, .10, .9, .1, .2,
>> .3, .4 (aka SGMII E, F, H, G, A, B, C, D).
>>
>> For e.g. the B4860, SerDes lanes A uses sgmii1 or sgmii5 and B uses
>> sgmii2 or sgmii6. The MAC selected is determined based on the value
>> programmed into PCCR2.
>
> It's so exceedingly unlikely that B4860 will gain support for anything
> new, that it's not even worth talking about it, or even considering it
> in the design of a new driver. Just forget about it.
>
> Let's concentrate on the Layerscapes, and on the T series to the extent
> that we're not going out of our way to support them with a fairly simple
> design.
Well, these are just easy ways to show the oddities in the PCCRs. I
could have made the same points with the various Layerscapes.
> In the Lynx 10G block guide that I have, PCCR2 is a register that does
> something completely different from Ethernet. I'm not sure if B4860 has
> a Lynx 10G block and not something else.
T-series SoCs use a different PCCR layout than Layerscape, despite using
the same registers in the rest of the SerDes. The LS1021 also uses this
scheme, so it's not just T-series (but it's close enough). This is why I
had an option for different PCCR configuration functions in earlier
versions of this series, so if someone wanted they could easily add
T-series support.
>> While I appreciate that your hardware engineers did a better job for
>> 28g, many 10g serdes arbitrarily map lanes to protocol controllers.
>> I think the mapping is too irregular to tame, and it is better to say
>> "if you want this configuration, program this value".
>
> Ok, but that's a lateral argument (or I'm not understanding the connection).
To restate, there's no systemic organization to the PCCRs. The driver
configuration should reflect this and allow arbitrary mappings.
> Some maintainers (Mark Brown for sure, from my personal experience) prefer
> that expert-level knowledge of hardware should be hardcoded into the kernel
> driver based on known stuff such as the SoC-specific compatible string.
> I certainly share the same view.
>
> With your case, I think that argument is even more pertinent, because
> IIUC, the lane group protocols won't be put in the SoC .dtsi (so as to
> be written only once), but in the board device trees (since the
> available protocols invariably depend upon the board provisioning).
> So, non-expert board device tree writers will have to know what's with
> the PCCR stuff. Pretty brain-intensive.
The idea is to stick all the PCCR stuff in the SoC devicetree, and the
board-level devicetrees just set up the clocks and pick which MACs use
which phys. Have a look at patches 10 and 15 of this series for some
typical board configurations. I think it's pretty simple, since all the
complexity is in the SoC dt.
That said, I originally had the driver set up so that everything was
based on the compatible, but I had to change that because it was nacked
by the devicetree people.
>> > I may be off with my understanding of the regularity you are talking about,
>> > but the LX2160 (and Lynx 28G block) also has multi-lane protocols like 40G,
>> > 100G, assuming that's what you are talking about. I haven't started yet
>> > working on those for the mtip_backplane driver, but I'm not currently
>> > seeing a problem with the architecture where a phy_device represents a
>> > single lane that's part of a multi-lane port, and not an entire group.
>>
>> Resetting one lane in a group will reset the rest, which could confuse
>> the driver. Additionally, treating the lanes as one phy lets us set the
>> reset direction and first lane bits correctly.
>
> Yeah, in theory that is probably correct, but in practice can't we hide
> our head in the sand and say that the "phys" phandles have to have the
> lanes in the same order as LNmGCR0[1STLANE] expects them (which is also
> the "natural" order as the SoC RM describes it)? With a "for" loop
> implementation in the MAC, that would work just fine 100% of the time.
> Doing more intricate massaging of the "phys" in the consumer, and you're
> just asking for trouble. My 2 cents.
Yeah, but from the perspective of the driver, if we have one phy per
lane we don't get any indication from the subsystem that we are doing a
multi-lane configuration. So we need to stick this sort of thing into
the phy handle. But IMO we can do all this in the driver and the board
integrator never has to worry about it.
> Sure, it's the same kind of ask of a board device tree writer as "hey,
> please give me a good PCCR value", but I honestly think that the head
> scratching will be much less severe.
>
>> > In my imagination, there are 2 cases:
>> > - all 4 lanes are managed by the single dpaa2-mac consumer (which has 4
>> > phandles, and iterates over them with a "for" loop)
>> > - each of the 4 lanes is managed by the respective backplane AN/LT core,
>> > and thus, there's one phandle to each lane
>>
>> By doing the grouping in the driver, we also simplify the consumer
>> implementation. The MAC can always use a single phy, without worrying
>> about the actual number of lanes. This matches the hardware, since the
>> MAC is going to talk XGMII (or whatever) to the protocol controller
>> anyway.
>
> XGMII is the link between the MAC and the PCS, but the "phys" phandle to
> the SerDes gives insight to the MAC driver way beyond the PCS layer.
> That kinda invalidates the idea that "you don't need to worry about the
> actual number of lanes". When you're a MAC driver with an XLAUI link and
> you need insight into the PMA/PMD layer, you'd better not be surprised
> about the fact that there are 4 lanes, at the very least?
Well, this is why they are condensed into a "lane group". From the MAC's
perspective the whole thing is opaque, and gets handled by the phy
subsystem.
>> I think it will be a lot easier to add multi-lane support with this
>> method because it gives the driver more information about what's going
>> on. The driver can control the whole configuration/reset process and the
>> timing.
>
> Also, I'm thinking that if you support multi-lane (which dpaa2-mac currently
> doesn't, even in LSDK), you can't avoid multiple "phys" phandles in dpaa2-mac,
> and a "for" loop, eventually, anyway. That's because if your lanes have protocol
> retimers on them, those are going to be modeled as generic PHYs too. And those
> will not be bundled in these "groups", because they might be one chip per lane.
>
> The retimer thing isn't theoretical, but, due to reasons independent of NXP,
> we lack the ability to provide an upstream user of the "lane retimer as
> generic PHY" functionality in dpaa2-mac. So it stays downstream for now.
> https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2fgithub.com%2fnxp%2dqoriq%2flinux%2fcommit%2f627c5f626a13657f46f68b90882f329310e0e22f&umid=54bd2b00-07e7-4f55-8e2a-ed7b7cf7d142&auth=d807158c60b7d2502abde8a2fc01f40662980862-a6780f8e227e850b7785d5afbae1abed18ded9d9
>
> So, if you're thinking of easing the work of the consumer side, I'm not
> sure that the gains will be that high.
Well, FWIW those serdes don't need good coordination. That said, I
think it might be interesting to have some subsystem-level support for
this, much like clock groups.
> Otherwise, I will repeat the idea that lynx-10g and lynx-28g should be
> treated in unison, because some drivers (dpaa2-mac, mtip_backplane) will
> have to interface with both, and I don't really believe that major deviations
> in software architecture between the 2 SerDes drivers are justifiable in
> any way (multi-protocol handled differently, for example).
Well, I think we should take the opportunity to think about the hardware
which exists and how we plan to model it. IMO grouping lanes into a
single phy simplifies both the phy driver and the mac driver.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 23:39 ` Sean Anderson
@ 2023-08-21 23:59 ` Vladimir Oltean
2023-08-24 22:09 ` Sean Anderson
0 siblings, 1 reply; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-21 23:59 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Aug 21, 2023 at 07:39:15PM -0400, Sean Anderson wrote:
> Well, I think we should take the opportunity to think about the hardware
> which exists and how we plan to model it. IMO grouping lanes into a
> single phy simplifies both the phy driver and the mac driver.
Ok, but ungrouped for backplane and grouped for !backplane? For the KR
link modes, parallel link training, with separate consumers per lanes in
a group, will be needed per lane.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 23:59 ` Vladimir Oltean
@ 2023-08-24 22:09 ` Sean Anderson
2023-08-25 14:43 ` Vladimir Oltean
0 siblings, 1 reply; 64+ messages in thread
From: Sean Anderson @ 2023-08-24 22:09 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/21/23 19:59, Vladimir Oltean wrote:
> On Mon, Aug 21, 2023 at 07:39:15PM -0400, Sean Anderson wrote:
>> Well, I think we should take the opportunity to think about the hardware
>> which exists and how we plan to model it. IMO grouping lanes into a
>> single phy simplifies both the phy driver and the mac driver.
>
> Ok, but ungrouped for backplane and grouped for !backplane? For the KR
> link modes, parallel link training, with separate consumers per lanes in
> a group, will be needed per lane.
Hm, this is the sort of thing I hadn't considered since separate link
training isn't necessary for lynx 10g. But couldn't this be done by
adding a "lane" parameter to phy_configure_opts_xgkr?
Although, I am not sure how the driver is supposed to figure out what
coefficients to use. c73 implies that the training frame should be sent
on each lane. So I expected that there would be four copies of the
link coefficient registers. However, when reading the LX2160ARM, I only
saw one set of registers (e.g. 26.6.3.3). So is link training done
serially? I didn't see anything like a "lane select" field.
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-24 22:09 ` Sean Anderson
@ 2023-08-25 14:43 ` Vladimir Oltean
0 siblings, 0 replies; 64+ messages in thread
From: Vladimir Oltean @ 2023-08-25 14:43 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Michael Turquette, Ioana Ciornei, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Thu, Aug 24, 2023 at 06:09:52PM -0400, Sean Anderson wrote:
> On 8/21/23 19:59, Vladimir Oltean wrote:
> > On Mon, Aug 21, 2023 at 07:39:15PM -0400, Sean Anderson wrote:
> >> Well, I think we should take the opportunity to think about the hardware
> >> which exists and how we plan to model it. IMO grouping lanes into a
> >> single phy simplifies both the phy driver and the mac driver.
> >
> > Ok, but ungrouped for backplane and grouped for !backplane? For the KR
> > link modes, parallel link training, with separate consumers per lanes in
> > a group, will be needed per lane.
>
> Hm, this is the sort of thing I hadn't considered since separate link
> training isn't necessary for lynx 10g. But couldn't this be done by
> adding a "lane" parameter to phy_configure_opts_xgkr?
>
> Although, I am not sure how the driver is supposed to figure out what
> coefficients to use. c73 implies that the training frame should be sent
> on each lane. So I expected that there would be four copies of the
> link coefficient registers. However, when reading the LX2160ARM, I only
> saw one set of registers (e.g. 26.6.3.3). So is link training done
> serially? I didn't see anything like a "lane select" field.
>
> --Sean
There is one AN/LT block replicated for each lane, even for multi-lane
backplane protocols. The primary (first) AN/LT block handles C73 autoneg
+ C73 link training on that lane, and the secondary AN/LT blocks handle
just link training on their respective lanes.
In other words, each AN/LT block needs to interact with just its lane
(SerDes PHY). A "lane" parameter could be added to phy_configure_opts_xgkr
to work around the "grouped lanes" design, but it would complicate the
consumer implementation, since the AN/LT block does not otherwise need
to know what is the index of the SerDes lane it is attached to (so it
would need something like an extra device tree property).
C72 link training is independent on each lane, has independent AN/LT
block MDIO registers, independent SerDes lane registers, and independent
training frame exchanges. There is no "lane select" field.
You can see the "LX2160A lanes A, B, C, D with SerDes 1 protocol 19:
dpmac2 uses 40GBase-KR4" example in my backplane dt-bindings patch,
which shows how on dpmac2's internal MDIO bus, there are AN/LT devices
at MDIO addresses 7, 6, 5 and 4, one for each lane.
I know that Lynx 10G doesn't do multi-lane backplane, but I wouldn't
want Lynx 10G and Lynx 28G to have different designs when it comes to
their handling of multi-lane. A single design that works for both would
be great.
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-21 18:46 ` Sean Anderson
2023-08-21 19:58 ` Vladimir Oltean
@ 2023-08-22 14:55 ` Ioana Ciornei
2023-08-24 20:54 ` Sean Anderson
1 sibling, 1 reply; 64+ messages in thread
From: Ioana Ciornei @ 2023-08-22 14:55 UTC (permalink / raw)
To: Sean Anderson
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Vladimir Oltean, Michael Turquette, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
> On 8/21/23 14:13, Ioana Ciornei wrote:
> > On Mon, Aug 21, 2023 at 01:45:44PM -0400, Sean Anderson wrote:
> >> Well, we have two pieces of information we need
> >>
> >> - What values do we need to program in the PCCRs to select a particular
> >> mode? This includes whether to e.g. set the KX bits.
> >> - Implied by the above, what protocols are supported on which lanes?
> >> This is not strictly necessary, but will certainly solve a lot of
> >> headscratching.
> >>
> >> This information varies between different socs, and different serdes on
> >> the same socs. We can't really look at the RCW or the clocks and figure
> >> out what we need to program. So what are our options?
> >>
> >> - We can have a separate compatible for each serdes on each SoC (e.g.
> >> "fsl,lynx-10g-a"). This was rejected by the devicetree maintainers.
I previously took this statement at face value and didn't further
investigate. After a bit of digging through the first versions of this
patch set it's evident that you left out a big piece of information.
The devicetree maintainers have indeed rejected compatible strings of
the "fsl,<soc-name>-serdes-<instance>" form but they also suggested to
move the numbering to a property instead:
https://lore.kernel.org/all/db9d9455-37af-1616-8f7f-3d752e7930f1@linaro.org/
But instead of doing that, you chose to move all the different details
that vary between SerDes blocks/SoCs from the driver to the DTS. I don't
see that this was done in response to explicit feedback.
> >> - We can have one compatible for each SoC, and determine the serdes
> >> based on the address. I would like to avoid this...
> >
> > To me this really seems like a straightforward approach.
>
> Indeed it would be straightforward, but what's the point of having a
> devicetree in the first place then? We could just go back to being a
> (non-dt) platform device.
>
I am confused why you are now so adamant to have these details into the
DTS. Your first approach was to put them into the driver, not the DTS:
https://lore.kernel.org/netdev/20220628221404.1444200-5-sean.anderson@seco.com/
And this approach could still work now and get accepted by the device
tree maintainers. The only change that would be needed is to add a
property like "fsl,serdes-block-id = <1>".
> >> - We can stick all the details which vary between serdes/socs into the
> >> device tree. This is very flexible, since supporting new SoCs is
> >> mostly a matter of adding a new compatible and writing a new
> >> devicetree. On the other hand, if you have a bug in your devicetree,
> >> it's not easy to fix it in the kernel.
> >> - Just don't support protocol switching. The 28G driver does this, which
> >> is why it only has one compatible. However, supporting protocol
> >> switching is a core goal of this driver, so dropping support is not an
> >> option.
> >>
Ioana
^ permalink raw reply [flat|nested] 64+ messages in thread
* Re: [PATCH v14 00/15] phy: Add support for Lynx 10G SerDes
2023-08-22 14:55 ` Ioana Ciornei
@ 2023-08-24 20:54 ` Sean Anderson
0 siblings, 0 replies; 64+ messages in thread
From: Sean Anderson @ 2023-08-24 20:54 UTC (permalink / raw)
To: Ioana Ciornei
Cc: Fernández Rojas, Bagas Sanjaya, Madalin Bucur,
Vladimir Oltean, Michael Turquette, Krzysztof Kozlowski,
Jonas Gorski, linux-phy, linux-clk, Kishon Vijay Abraham I,
Jonathan Corbet, Bartosz Golaszewski, linux-doc,
Camelia Alexandra Groza, Linus Walleij, devicetree, linux-gpio,
Rob Herring, linux-arm-kernel, Stephen Boyd, linuxppc-dev,
Li Yang, Vinod Koul, Shawn Guo
On 8/22/23 10:55, Ioana Ciornei wrote:
> On Mon, Aug 21, 2023 at 02:46:53PM -0400, Sean Anderson wrote:
>> On 8/21/23 14:13, Ioana Ciornei wrote:
>> > On Mon, Aug 21, 2023 at 01:45:44PM -0400, Sean Anderson wrote:
>> >> Well, we have two pieces of information we need
>> >>
>> >> - What values do we need to program in the PCCRs to select a particular
>> >> mode? This includes whether to e.g. set the KX bits.
>> >> - Implied by the above, what protocols are supported on which lanes?
>> >> This is not strictly necessary, but will certainly solve a lot of
>> >> headscratching.
>> >>
>> >> This information varies between different socs, and different serdes on
>> >> the same socs. We can't really look at the RCW or the clocks and figure
>> >> out what we need to program. So what are our options?
>> >>
>> >> - We can have a separate compatible for each serdes on each SoC (e.g.
>> >> "fsl,lynx-10g-a"). This was rejected by the devicetree maintainers.
>
> I previously took this statement at face value and didn't further
> investigate. After a bit of digging through the first versions of this
> patch set it's evident that you left out a big piece of information.
>
> The devicetree maintainers have indeed rejected compatible strings of
> the "fsl,<soc-name>-serdes-<instance>" form but they also suggested to
> move the numbering to a property instead:
>
> https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2flore.kernel.org%2fall%2fdb9d9455%2d37af%2d1616%2d8f7f%2d3d752e7930f1%40linaro.org%2f&umid=2d629417-3b95-49e4-8cdb-34737cc93582&auth=d807158c60b7d2502abde8a2fc01f40662980862-895c2dfe1c33719569d44ae2b51e21f626f39d39
>
> But instead of doing that, you chose to move all the different details
> that vary between SerDes blocks/SoCs from the driver to the DTS. I don't
> see that this was done in response to explicit feedback.
>
>> >> - We can have one compatible for each SoC, and determine the serdes
>> >> based on the address. I would like to avoid this...
>> >
>> > To me this really seems like a straightforward approach.
>>
>> Indeed it would be straightforward, but what's the point of having a
>> devicetree in the first place then? We could just go back to being a
>> (non-dt) platform device.
>>
>
> I am confused why you are now so adamant to have these details into the
> DTS. Your first approach was to put them into the driver, not the DTS:
>
> https://cas5-0-urlprotect.trendmicro.com:443/wis/clicktime/v1/query?url=https%3a%2f%2flore.kernel.org%2fnetdev%2f20220628221404.1444200%2d5%2dsean.anderson%40seco.com%2f&umid=2d629417-3b95-49e4-8cdb-34737cc93582&auth=d807158c60b7d2502abde8a2fc01f40662980862-64ea8fa45172282f676b7463a5401e8a7c5bdcbf
>
> And this approach could still work now and get accepted by the device
> tree maintainers. The only change that would be needed is to add a
> property like "fsl,serdes-block-id = <1>".
https://lore.kernel.org/linux-phy/1c2bbc12-0aa5-6d2a-c701-577ce70f7502@linaro.org/
Despite what he says in your link, I explicitly proposed doing exactly
that and he rejected it. I suspect that despite accusing me of
"twisting" the conversation, he did not clearly remember that
exchange...
That said, maybe we could do something like
serdes: phy@1ea0000 {
compatible = "fsl,ls1046a-serdes";
reg = <0x1ea0000 0x1000>, <0x1eb0000 0x1000>;
};
--Sean
^ permalink raw reply [flat|nested] 64+ messages in thread