public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration
@ 2026-02-03  0:21 Sean Anderson
  2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
                   ` (7 more replies)
  0 siblings, 8 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson, Conor Dooley,
	Krzysztof Kozlowski, devicetree

This series completely initializes the GTRs in Linux, making all
bootloader initialization (as performed by init_serdes() in
psu_init_gpl.c) optional. This gives the following advantages:

- On some boards (mine) the reference clocks may not be configured in
  SPL/FSBL. So ILL calibration will fail (and take a long time to do so)
  unless we defer initialization to U-Boot/Linux where the phy driver
  can request the clocks.
- If PCIe/SATA are not used in U-Boot, ILL calibration can be deferred
  until Linux when it can be done it parallel with other initialization.
- We will have flexibility to switch between different configurations at
  runtime. For example, this could allow supporting both SATA and PCIe M.2
  cards with [1].

I have tested this series with DP, PCIe, SGMII, and SATA. USB3 is broken
on my dev board at the moment (independent of this series; need to
investigate) so I have not tested that. I have an equivalent set of
patches for U-Boot that I will try to post soon.

[1] https://lore.kernel.org/linux-pci/20260107-pci-m2-v5-0-8173d8a72641@oss.qualcomm.com/


Sean Anderson (8):
  dt-bindings: pci: xilinx-nwl: Add resets
  phy: zynqmp: Refactor bus width configuration into helper
  phy: zynqmp: Refactor common phy initialization into a helper
  phy: zynqmp: Calibrate ILL if necessary
  phy: zynqmp: Initialize chicken bits
  PCI: xilinx-nwl: Split phy_init from phy_power_on
  PCI: xilinx-nwl: Reset the core during probe
  arm64: zynqmp: Add PCIe resets

 .../bindings/pci/xlnx,nwl-pcie.yaml           |  17 +
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi        |   4 +
 drivers/pci/controller/pcie-xilinx-nwl.c      | 255 +++++++--
 drivers/phy/xilinx/phy-zynqmp.c               | 487 +++++++++++++++++-
 4 files changed, 713 insertions(+), 50 deletions(-)

-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-04  8:32   ` Pandey, Radhey Shyam
  2026-02-10  0:24   ` Rob Herring (Arm)
  2026-02-03  0:21 ` [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper Sean Anderson
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson, Conor Dooley,
	Krzysztof Kozlowski, devicetree

Add resets so we can hold the bridge in reset while we perform phy
calibration.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 .../devicetree/bindings/pci/xlnx,nwl-pcie.yaml  | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
index 9de3c09efb6e..7efb3dd9955f 100644
--- a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
@@ -69,6 +69,18 @@ properties:
   power-domains:
     maxItems: 1
 
+  resets:
+    maxItems: 3
+
+  reset-names:
+    items:
+      - description: APB register block reset
+        const: cfg
+      - description: AXI-PCIe bridge reset
+        const: bridge
+      - description: PCIe MAC reset
+        const: ctrl
+
   iommus:
     maxItems: 1
 
@@ -117,6 +129,7 @@ examples:
     #include <dt-bindings/interrupt-controller/irq.h>
     #include <dt-bindings/phy/phy.h>
     #include <dt-bindings/power/xlnx-zynqmp-power.h>
+    #include <dt-bindings/reset/xlnx-zynqmp-resets.h>
     soc {
         #address-cells = <2>;
         #size-cells = <2>;
@@ -146,6 +159,10 @@ examples:
             msi-parent = <&nwl_pcie>;
             phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
             power-domains = <&zynqmp_firmware PD_PCIE>;
+            resets = <&zynqmp_reset ZYNQMP_RESET_PCIE_CFG>,
+                     <&zynqmp_reset ZYNQMP_RESET_PCIE_BRIDGE>,
+                     <&zynqmp_reset ZYNQMP_RESET_PCIE_CTRL>;
+            reset-names = "cfg", "bridge", "ctrl";
             iommus = <&smmu 0x4d0>;
             pcie_intc: legacy-interrupt-controller {
                 interrupt-controller;
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
  2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-10 15:00   ` Pandey, Radhey Shyam
  2026-02-03  0:21 ` [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper Sean Anderson
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

Split off the bus width configuration into a helper function for reuse.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/phy/xilinx/phy-zynqmp.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index fe6b4925d166..0d3c578d0f3f 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -502,6 +502,17 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy)
 	}
 }
 
+/* Set the bus width */
+static void xpsgtr_phy_init_bus_width(struct xpsgtr_phy *gtr_phy, u32 width)
+{
+	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+	u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane);
+	u32 val = width << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane);
+
+	xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
+	xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
+}
+
 /* Bypass (de)scrambler and 8b/10b decoder and encoder. */
 static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
 {
@@ -535,14 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy)
 /* SGMII-specific initialization. */
 static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
 {
-	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
-	u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane);
-	u32 val = PROT_BUS_WIDTH_10 << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane);
-
-	/* Set SGMII protocol TX and RX bus width to 10 bits. */
-	xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
-	xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
-
+	xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_10);
 	xpsgtr_bypass_scrambler_8b10b(gtr_phy);
 }
 
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
  2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
  2026-02-03  0:21 ` [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-10 15:05   ` Pandey, Radhey Shyam
  2026-02-03  0:21 ` [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary Sean Anderson
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

All lanes undergoing ILL calibration must be initialized. Split off
common phy initialization into a helper so that we can ensure all lanes
are initialized before performing calibration.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/phy/xilinx/phy-zynqmp.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index 0d3c578d0f3f..152af1702bbd 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -520,6 +520,21 @@ static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
 	xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
 }
 
+static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
+{
+	int ret;
+
+	/* Enable coarse code saturation limiting logic. */
+	xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT);
+
+	ret = xpsgtr_configure_pll(gtr_phy);
+	if (ret)
+		return ret;
+
+	xpsgtr_lane_set_protocol(gtr_phy);
+	return 0;
+}
+
 /* DP-specific initialization. */
 static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy)
 {
@@ -682,19 +697,14 @@ static int xpsgtr_phy_init(struct phy *phy)
 		gtr_dev->tx_term_fix = false;
 	}
 
-	/* Enable coarse code saturation limiting logic. */
-	xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT);
-
 	/*
 	 * Configure the PLL, the lane protocol, and perform protocol-specific
 	 * initialization.
 	 */
-	ret = xpsgtr_configure_pll(gtr_phy);
+	ret = xpsgtr_common_init(gtr_phy);
 	if (ret)
 		goto out;
 
-	xpsgtr_lane_set_protocol(gtr_phy);
-
 	switch (gtr_phy->protocol) {
 	case ICM_PROTOCOL_DP:
 		xpsgtr_phy_init_dp(gtr_phy);
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
                   ` (2 preceding siblings ...)
  2026-02-03  0:21 ` [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-10 16:04   ` Pandey, Radhey Shyam
  2026-02-03  0:21 ` [PATCH 5/8] phy: zynqmp: Initialize chicken bits Sean Anderson
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

init_serdes in psu_init_gpl is supposed to calibrate the ILL. However, this
may fail if the reference clock is not running, such as if the clock needs
to be configured on boot. To work around this, add support for ILL
calibration in U-Boot. If the ILL is already calibrated (any non-zero
value) we skip calibration.

The algorithm is substantially the same as serdes_illcalib [1], but it has
been updated for readability (and to remove all the "if (lane0_active)"
conditions). Due to the amount of register fields, many of which are
undocumented (especially the chicken bits), I have mostly used defines only
for the register names. There are certainly areas where register writes are
superfluous, but I have left these in order to minimize deviation from the
procedure in serdes_illcalib.

[1] Example implementation; xpsgtr_phy_illcalib coresponds to
    serdes_illcalib_pcie_gen1:
https://source.denx.de/u-boot/u-boot/-/blob/v2026.01/board/xilinx/zynqmp/zynqmp-zcu208-revA/psu_init_gpl.c?ref_type=tags#L710

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/phy/xilinx/phy-zynqmp.c | 421 +++++++++++++++++++++++++++++++-
 1 file changed, 420 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index 152af1702bbd..854b0ea04648 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -12,6 +12,7 @@
  * PCIe should also work but that is experimental as of now.
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
@@ -31,6 +32,7 @@
  */
 
 /* TX De-emphasis parameters */
+#define L0_TX_ANA_TM_3			0x000c
 #define L0_TX_ANA_TM_18			0x0048
 #define L0_TX_ANA_TM_118		0x01d8
 #define L0_TX_ANA_TM_118_FORCE_17_0	BIT(0)
@@ -50,16 +52,49 @@
 #define L0_TXPMD_TM_45_ENABLE_DP_POST2	BIT(5)
 
 /* PCS control parameters */
+#define L0_TM_ANA_BYP_4			0x1010
+#define L0_TM_ANA_BYP_7			0x1018
 #define L0_TM_DIG_6			0x106c
+#define L0_TM_DIG_22			0x10ac
 #define L0_TM_DIS_DESCRAMBLE_DECODER	0x0f
 #define L0_TX_DIG_61			0x00f4
 #define L0_TM_DISABLE_SCRAMBLE_ENCODER	0x0f
+#define L0_TM_AUX_0			0x10cc
+#define L0_TM_MISC2			0x189c
+#define L0_TM_MISC2_ILL_CAL_BYPASS	BIT(7)
+#define L0_TM_IQ_ILL1			0x18f8
+#define L0_TM_IQ_ILL2			0x18fc
+#define L0_TM_ILL11			0x198c
+#define L0_TM_ILL12			0x1990
+#define L0_TM_E_ILL1			0x1924
+#define L0_TM_E_ILL2			0x1928
+#define L0_TM_IQ_ILL3			0x1900
+#define L0_TM_E_ILL3			0x192c
+#define L0_TM_IQ_ILL7			0x1910
+#define L0_TM_E_ILL7			0x193c
+#define L0_TM_ILL8			0x1980
+#define L0_TM_IQ_ILL8			0x1914
+#define L0_TM_IQ_ILL9			0x1918
+#define L0_TM_EQ0			0x194c
+#define L0_TM_EQ0_EQ_STG2_CTRL_BYP	BIT(5)
+#define L0_TM_EQ1			0x1950
+#define L0_TM_EQ1_EQ_STG2_RL_PROG	GENMASK(1, 0)
+#define L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL	BIT(2)
+#define L0_TM_E_ILL8			0x1940
+#define L0_TM_E_ILL9			0x1944
+#define L0_TM_ILL13			0x1994
+#define L0_TM_CDR5			0x1c14
+#define L0_TM_CDR5_FPHL_FSM_ACC_CYCLES	GENMASK(7, 5)
+#define L0_TM_CDR5_FFL_PH0_INT_GAIN	GENMASK(4, 0)
+#define L0_TM_CDR16			0x1c40
 
 /* PLL Test Mode register parameters */
+#define L0_TM_PLL_DIG_33		0x2084
 #define L0_TM_PLL_DIG_37		0x2094
 #define L0_TM_COARSE_CODE_LIMIT		0x10
 
 /* PLL SSC step size offsets */
+#define L0_PLL_FBDIV_FRAC_3_MSB		0x2360
 #define L0_PLL_SS_STEPS_0_LSB		0x2368
 #define L0_PLL_SS_STEPS_1_MSB		0x236c
 #define L0_PLL_SS_STEP_SIZE_0_LSB	0x2370
@@ -69,6 +104,7 @@
 #define L0_PLL_STATUS_READ_1		0x23e4
 
 /* SSC step size parameters */
+#define TM_FORCE_EN_FRAC		BIT(6)
 #define STEP_SIZE_0_MASK		0xff
 #define STEP_SIZE_1_MASK		0xff
 #define STEP_SIZE_2_MASK		0xff
@@ -76,6 +112,7 @@
 #define STEP_SIZE_SHIFT			8
 #define FORCE_STEP_SIZE			0x10
 #define FORCE_STEPS			0x20
+#define TM_FORCE_EN			BIT(7)
 #define STEPS_0_MASK			0xff
 #define STEPS_1_MASK			0x07
 
@@ -84,6 +121,32 @@
 #define L0_REF_CLK_LCL_SEL		BIT(7)
 #define L0_REF_CLK_SEL_MASK		0x9f
 
+/* Built-in self-test parameters */
+#define L0_BIST_CTRL_1			0x3004
+#define L0_BIST_CTRL_2			0x3008
+#define L0_BIST_RUN_LEN_L		0x300c
+#define L0_BIST_ERR_INJ_POINT_L		0x3010
+#define L0_BIST_RUNLEN_ERR_INJ_H	0x3014
+#define L0_BIST_IDLE_TIME		0x3018
+#define L0_BIST_MARKER_L		0x301c
+#define L0_BIST_IDLE_CHAR_L		0x3020
+#define L0_BIST_MARKER_IDLE_H		0x3024
+#define L0_BIST_LOW_PULSE_TIME		0x3028
+#define L0_BIST_TOTAL_PULSE_TIME	0x302c
+#define L0_BIST_TEST_PAT_1		0x3030
+#define L0_BIST_TEST_PAT_2		0x3034
+#define L0_BIST_TEST_PAT_3		0x3038
+#define L0_BIST_TEST_PAT_4		0x303c
+#define L0_BIST_TEST_PAT_MSBS		0x3040
+#define L0_BIST_PKT_NUM			0x3044
+#define L0_BIST_FRM_IDLE_TIME		0x3048
+#define L0_BIST_PKT_CTR_L		0x304c
+#define L0_BIST_PKT_CTR_H		0x3050
+#define L0_BIST_ERR_CTR_L		0x3054
+#define L0_BIST_ERR_CTR_H		0x3058
+#define L0_BIST_FILLER_OUT		0x3068
+#define L0_BIST_FORCE_MK_RST		0x306c
+
 /* Calibration digital logic parameters */
 #define L3_TM_CALIB_DIG19		0xec4c
 #define L3_CALIB_DONE_STATUS		0xef14
@@ -139,6 +202,9 @@ static const char *const xpsgtr_icm_str[] = {
 #define TM_CMN_RST_SET			0x2
 #define TM_CMN_RST_MASK			0x3
 
+#define LPBK_CTRL0			0x10038
+#define LPBK_CTRL1			0x1003c
+
 /* Bus width parameters */
 #define TX_PROT_BUS_WIDTH		0x10040
 #define RX_PROT_BUS_WIDTH		0x10044
@@ -148,9 +214,13 @@ static const char *const xpsgtr_icm_str[] = {
 #define PROT_BUS_WIDTH_SHIFT(n)		((n) * 2)
 #define PROT_BUS_WIDTH_MASK(n)		GENMASK((n) * 2 + 1, (n) * 2)
 
+#define UPHY_SPARE0			0X10098
+
 /* Number of GT lanes */
 #define NUM_LANES			4
 
+#define SIOU_ECO_0			0x1c
+
 /* SIOU SATA control register */
 #define SATA_CONTROL_OFFSET		0x0100
 
@@ -338,6 +408,33 @@ static void xpsgtr_restore_lane_regs(struct xpsgtr_dev *gtr_dev)
 			     gtr_dev->saved_regs[i]);
 }
 
+static inline void xpsgtr_write_lanes(struct xpsgtr_dev *gtr_dev,
+				     unsigned long *lanes, u32 reg, u32 value)
+{
+	unsigned long lane;
+
+	for_each_set_bit(lane, lanes, NUM_LANES) {
+		void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
+				     + reg;
+
+		writel(value, addr);
+	}
+}
+
+static inline void xpsgtr_clr_set_lanes(struct xpsgtr_dev *gtr_dev,
+				       unsigned long *lanes, u32 reg, u32 clr,
+				       u32 set)
+{
+	unsigned long lane;
+
+	for_each_set_bit(lane, lanes, NUM_LANES) {
+		void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
+				     + reg;
+
+		writel((readl(addr) & ~clr) | set, addr);
+	}
+}
+
 /*
  * Hardware Configuration
  */
@@ -351,7 +448,7 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
 	u8 protocol = gtr_phy->protocol;
 	int ret;
 
-	dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n");
+	dev_vdbg(gtr_dev->dev, "Waiting for PLL lock\n");
 
 	/*
 	 * For DP and PCIe, only the instance 0 PLL is used. Switch to that phy
@@ -520,6 +617,231 @@ static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
 	xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
 }
 
+/* Enable or disable loopback */
+static void xpsgtr_phy_set_loopback(struct xpsgtr_phy *gtr_phy, bool enabled)
+{
+	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+	u32 reg = gtr_phy->lane >= 2 ? LPBK_CTRL1 : LPBK_CTRL0;
+	u32 shift = gtr_phy->lane & 1 ? 4 : 0;
+
+	xpsgtr_clr_set(gtr_dev, reg, 7 << shift, (u32)enabled << shift);
+}
+
+static void xpsgtr_phy_set_ill(struct xpsgtr_phy *gtr_phy, u32 ill, bool gen2)
+{
+	u32 val = 4 + ill * 8;
+
+	if (gen2) {
+		xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL2, val & 0xff);
+		xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0x0f,
+				   1 << (val >> 8));
+	} else {
+		xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL1, val & 0xff);
+		xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0xf0,
+				   (val >> 4) & 0x10);
+	}
+}
+
+static bool xpsgtr_ill_calibrated(struct xpsgtr_phy *gtr_phy)
+{
+	u32 ill1 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL1);
+	u32 ill2 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL2);
+	u32 ill12 = xpsgtr_read_phy(gtr_phy, L0_TM_ILL12);
+
+	dev_dbg(gtr_phy->dev->dev, "lane %u gen1 ILL was %u gen2 ILL was %u\n",
+		gtr_phy->lane, ill1 / 8 + (ill12 & 0x10 ? 32 : 0),
+		ill2 / 8 + (ill12 & 0x02 ? 32 : 0));
+	return ill1 || ill2 || ill12;
+}
+
+static void xpsgtr_init_ill(struct xpsgtr_phy *gtr_phy)
+{
+	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+	struct clk *clk = gtr_dev->clk[gtr_phy->refclk];
+	u32 ill123 = DIV_ROUND_CLOSEST(clk_get_rate(clk), 1000000);
+
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_MISC2, 0, L0_TM_MISC2_ILL_CAL_BYPASS);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL1, ill123);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL2, ill123);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL3, ill123);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL7, 0xf3);
+	xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL7, 0xf3);
+	xpsgtr_write_phy(gtr_phy, L0_TM_ILL8, 0xff);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
+	xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL9, 1);
+	xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL9, 1);
+	xpsgtr_clr_set(gtr_dev, UPHY_SPARE0, BIT(5), 0);
+}
+
+static void xpsgtr_phy_illcalib(struct xpsgtr_dev *gtr_dev,
+				     unsigned long *lanes, bool gen2)
+{
+	bool last_ok[NUM_LANES] = { 0 };
+	int pass[NUM_LANES] = { 0 }, altpass[NUM_LANES] = { 0 };
+	int best[NUM_LANES] = { 0 }, altbest[NUM_LANES] = { 0 };
+	unsigned long lane;
+	int i;
+
+	/* Initialize the BIST */
+	xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 1);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0x20);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0xf4);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0xfb);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0xff);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0x4a);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0x4a);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0x4a);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0x4a);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0x14);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 2);
+	xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
+
+	for (i = 0; i < 64; i++) {
+		bool ok[NUM_LANES];
+
+		for_each_set_bit(lane, lanes, NUM_LANES)
+			xpsgtr_phy_set_ill(&gtr_dev->phys[lane], i, gen2);
+
+		/* Reset lanes */
+		xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x20,
+				     0x10);
+		xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x40);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
+		xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x04);
+		udelay(50);
+		if (gen2)
+			xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0e);
+		xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x06);
+		if (gen2) {
+			xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x04);
+			xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x07);
+			udelay(400);
+			xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x0c);
+			udelay(15);
+			xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0f);
+			udelay(100);
+		}
+
+		if (xpsgtr_wait_pll_lock(gtr_dev->phys[0].phy)) {
+			memset(last_ok, 0, sizeof(last_ok));
+			continue;
+		}
+
+		udelay(50);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0xc0);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x80);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0xc0);
+		udelay(50);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
+		udelay(50);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0);
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0);
+		udelay(500);
+
+		/* Do the BIST */
+		for_each_set_bit(lane, lanes, NUM_LANES) {
+			struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
+			u32 packets, errors;
+
+			xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_10);
+			xpsgtr_phy_set_loopback(gtr_phy, true);
+			xpsgtr_write_phy(gtr_phy, L0_TM_DIG_22, 0x20);
+			xpsgtr_clr_set_phy(gtr_phy, L0_BIST_CTRL_1, 0, 1);
+
+			udelay(200);
+			xpsgtr_write_phy(gtr_phy, L0_BIST_CTRL_1, 0);
+			packets = xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_L);
+			packets |= xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_H) << 8;
+			errors = xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_L);
+			errors |= xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_H) << 8;
+			ok[lane] = packets && !errors;
+
+			dev_dbg(gtr_dev->dev,
+				"lane %lu ILL %d packets %10u errors %10u\n",
+				lane, i, packets, errors);
+		}
+
+		xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
+		xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
+		xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x02);
+
+		for_each_set_bit(lane, lanes, NUM_LANES) {
+			pass[lane] += ok[lane] && last_ok[lane];
+			if (pass[lane] < 4) {
+				if (!ok[lane] && i > 2) {
+					if (altpass[lane] < pass[lane]) {
+						altpass[lane] = pass[lane];
+						altbest[lane] =
+							(i - 1) - (pass[lane] + 1) / 2;
+					}
+					pass[lane] = 0;
+				}
+			} else if (!best[lane] && (!ok[lane] || i == 63) &&
+				   last_ok[lane]) {
+				best[lane] = (i - 1) - (pass[lane] + 1) / 2;
+			}
+		}
+
+		memcpy(last_ok, ok, sizeof(ok));
+	}
+
+	for_each_set_bit(lane, lanes, NUM_LANES) {
+		dev_dbg(gtr_dev->dev, "lane %lu ILL best %d alt best %d\n",
+			lane, best[lane], altbest[lane]);
+
+		xpsgtr_phy_set_ill(&gtr_dev->phys[lane],
+				   best[lane] ?: altbest[lane], gen2);
+	}
+
+	/* Clean up */
+	xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x30, 0);
+	xpsgtr_write(gtr_dev, UPHY_SPARE0, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_L, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_H, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 0);
+	xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0);
+
+	for_each_set_bit(lane, lanes, NUM_LANES) {
+		struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
+
+		xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_20);
+		xpsgtr_phy_set_loopback(gtr_phy, false);
+	}
+}
+
 static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
 {
 	int ret;
@@ -553,6 +875,37 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy)
 {
 	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
 
+	if (!xpsgtr_ill_calibrated(gtr_phy)) {
+		DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
+
+		xpsgtr_init_ill(gtr_phy);
+		xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL3, 100);
+		xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL11, 0xf0, 0x20);
+
+		__set_bit(gtr_phy->lane, lanes);
+		xpsgtr_phy_illcalib(gtr_dev, lanes, false);
+		xpsgtr_phy_set_ill(gtr_phy, 7, true);
+	}
+
+	/* Disable SSC */
+	xpsgtr_write_phy(gtr_phy, L0_PLL_FBDIV_FRAC_3_MSB, TM_FORCE_EN_FRAC);
+	xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, 0, TM_FORCE_EN);
+
+	/* Disable Tx deemphasis */
+	xpsgtr_write_phy(gtr_phy, L0_TM_CDR5,
+			 FIELD_PREP(L0_TM_CDR5_FPHL_FSM_ACC_CYCLES, 7) |
+			 FIELD_PREP(L0_TM_CDR5_FFL_PH0_INT_GAIN, 6));
+	xpsgtr_write_phy(gtr_phy, L0_TM_CDR16, 12);
+
+	/* Configure equalization */
+	xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118,
+			 L0_TX_ANA_TM_118_FORCE_17_0);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ0, 0, L0_TM_EQ0_EQ_STG2_CTRL_BYP);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ1, L0_TM_EQ1_EQ_STG2_RL_PROG,
+			   FIELD_PREP(L0_TM_EQ1_EQ_STG2_RL_PROG, 2) |
+			   L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL);
+	xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, 2); /* -3.5 dB deemphasis */
+
 	xpsgtr_bypass_scrambler_8b10b(gtr_phy);
 
 	writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
@@ -565,6 +918,64 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
 	xpsgtr_bypass_scrambler_8b10b(gtr_phy);
 }
 
+/* PCIe-specific initialization. */
+static int xpsgtr_phy_init_pcie(struct xpsgtr_phy *gtr_phy)
+{
+	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
+	DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
+	unsigned long lane;
+	bool calibrated = false;
+
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
+
+	for (lane = 0; lane < NUM_LANES; lane++) {
+		struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
+
+		if (gtr_phy->protocol != ICM_PROTOCOL_PCIE)
+			continue;
+
+		__set_bit(lane, lanes);
+		calibrated = calibrated || xpsgtr_ill_calibrated(gtr_phy);
+	}
+
+	if (calibrated)
+		return 0;
+
+	/* Write default ILL config */
+	for_each_set_bit(lane, lanes, NUM_LANES) {
+		struct xpsgtr_phy *p = &gtr_dev->phys[lane];
+
+		if (lane != gtr_phy->lane) {
+			int ret = xpsgtr_common_init(p);
+
+			if (ret)
+				return ret;
+		}
+
+		xpsgtr_init_ill(p);
+		xpsgtr_write_phy(p, L0_TM_E_ILL3, 0);
+		xpsgtr_clr_set_phy(p, L0_TM_MISC2, 0,
+				   L0_TM_MISC2_ILL_CAL_BYPASS);
+	}
+
+	/* Perform the ILL calibration procedure */
+	xpsgtr_phy_illcalib(gtr_dev, lanes, false);
+	xpsgtr_phy_illcalib(gtr_dev, lanes, true);
+
+	/* Disable PCIe ECO */
+	writel(1, gtr_dev->siou + SIOU_ECO_0);
+	return 0;
+}
+
+/* USB-specific initialization. */
+static void xpsgtr_phy_init_usb(struct xpsgtr_phy *gtr_phy)
+{
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
+	xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
+	xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
+	xpsgtr_phy_set_ill(gtr_phy, 7, false);
+}
+
 /* Configure TX de-emphasis and margining for DP. */
 static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre,
 				    unsigned int voltage)
@@ -710,6 +1121,10 @@ static int xpsgtr_phy_init(struct phy *phy)
 		xpsgtr_phy_init_dp(gtr_phy);
 		break;
 
+	case ICM_PROTOCOL_PCIE:
+		ret = xpsgtr_phy_init_pcie(gtr_phy);
+		break;
+
 	case ICM_PROTOCOL_SATA:
 		xpsgtr_phy_init_sata(gtr_phy);
 		break;
@@ -717,6 +1132,10 @@ static int xpsgtr_phy_init(struct phy *phy)
 	case ICM_PROTOCOL_SGMII:
 		xpsgtr_phy_init_sgmii(gtr_phy);
 		break;
+
+	case ICM_PROTOCOL_USB:
+		xpsgtr_phy_init_usb(gtr_phy);
+		break;
 	}
 
 out:
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 5/8] phy: zynqmp: Initialize chicken bits
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
                   ` (3 preceding siblings ...)
  2026-02-03  0:21 ` [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-03  0:21 ` [PATCH 6/8] PCI: xilinx-nwl: Split phy_init from phy_power_on Sean Anderson
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

These bits are all set by serdes_init(). Move them to the phy driver so
we can skip serdes_init().

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/phy/xilinx/phy-zynqmp.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index 854b0ea04648..1bdf29ba284c 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -54,7 +54,13 @@
 /* PCS control parameters */
 #define L0_TM_ANA_BYP_4			0x1010
 #define L0_TM_ANA_BYP_7			0x1018
+#define L0_TM_ANA_BYP_12		0x102c
+#define L0_TM_ANA_BYP_12_FORCE_UPHY_PSO_HSRXDIG		BIT(6)
+#define L0_TM_ANA_BYP_15		0x1038
+#define L0_TM_ANA_BYP_15_FORCE_UPHY_ENABLE_LOW_LEAKAGE	BIT(6)
 #define L0_TM_DIG_6			0x106c
+#define L0_TM_DIG_8			0x1074
+#define L0_TM_DIG_8_EYESURF		BIT(4)
 #define L0_TM_DIG_22			0x10ac
 #define L0_TM_DIS_DESCRAMBLE_DECODER	0x0f
 #define L0_TX_DIG_61			0x00f4
@@ -82,7 +88,13 @@
 #define L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL	BIT(2)
 #define L0_TM_E_ILL8			0x1940
 #define L0_TM_E_ILL9			0x1944
+#define L0_TM_EQ11			0x1978
+#define L0_TM_EQ11_FORCE_EQ_OFFS_OFF	BIT(4)
 #define L0_TM_ILL13			0x1994
+#define L0_TM_RST_DLY			0x19a4
+#define L0_TM_MISC3			0x19ac
+#define L0_TM_MISC3_CDR_EN_FPL		BIT(1)
+#define L0_TM_MISC3_CDR_EN_FFL		BIT(0)
 #define L0_TM_CDR5			0x1c14
 #define L0_TM_CDR5_FPHL_FSM_ACC_CYCLES	GENMASK(7, 5)
 #define L0_TM_CDR5_FFL_PH0_INT_GAIN	GENMASK(4, 0)
@@ -849,6 +861,18 @@ static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
 	/* Enable coarse code saturation limiting logic. */
 	xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT);
 
+	/* Miscellaneous chicken bits */
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_8, 0, L0_TM_DIG_8_EYESURF);
+	xpsgtr_write_phy(gtr_phy, L0_TM_ILL13, 7);
+	xpsgtr_write_phy(gtr_phy, L0_TM_RST_DLY, 255);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_ANA_BYP_15, 0,
+			   L0_TM_ANA_BYP_15_FORCE_UPHY_ENABLE_LOW_LEAKAGE);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_ANA_BYP_12, 0,
+			   L0_TM_ANA_BYP_12_FORCE_UPHY_PSO_HSRXDIG);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_MISC3, L0_TM_MISC3_CDR_EN_FPL |
+						 L0_TM_MISC3_CDR_EN_FFL, 0);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ11, 0, L0_TM_EQ11_FORCE_EQ_OFFS_OFF);
+
 	ret = xpsgtr_configure_pll(gtr_phy);
 	if (ret)
 		return ret;
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 6/8] PCI: xilinx-nwl: Split phy_init from phy_power_on
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
                   ` (4 preceding siblings ...)
  2026-02-03  0:21 ` [PATCH 5/8] phy: zynqmp: Initialize chicken bits Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-03  0:21 ` [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe Sean Anderson
  2026-02-03  0:21 ` [PATCH 8/8] arm64: zynqmp: Add PCIe resets Sean Anderson
  7 siblings, 0 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

In preparation for reset support, split phy_init from phy_power_on. The
former must be performed while the controller is in reset, while the
latter must be performed while the controller is not in reset.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/pci/controller/pcie-xilinx-nwl.c | 78 +++++++++++++-----------
 1 file changed, 43 insertions(+), 35 deletions(-)

diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
index 7db2c96c6cec..7cfdc21e6f40 100644
--- a/drivers/pci/controller/pcie-xilinx-nwl.c
+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
@@ -517,56 +517,55 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
 
 static void nwl_pcie_phy_power_off(struct nwl_pcie *pcie, int i)
 {
-	int err = phy_power_off(pcie->phy[i]);
+	while (i--) {
+		int err = phy_power_off(pcie->phy[i]);
 
-	if (err)
-		dev_err(pcie->dev, "could not power off phy %d (err=%d)\n", i,
-			err);
+		if (err)
+			dev_err(pcie->dev,
+				"could not power off phy %d (err=%d)\n", i,
+				err);
+	}
 }
 
 static void nwl_pcie_phy_exit(struct nwl_pcie *pcie, int i)
 {
-	int err = phy_exit(pcie->phy[i]);
+	while (i--) {
+		int err = phy_exit(pcie->phy[i]);
 
-	if (err)
-		dev_err(pcie->dev, "could not exit phy %d (err=%d)\n", i, err);
+		if (err)
+			dev_err(pcie->dev, "could not exit phy %d (err=%d)\n",
+				i, err);
+	}
 }
 
-static int nwl_pcie_phy_enable(struct nwl_pcie *pcie)
+static int nwl_pcie_phy_init(struct nwl_pcie *pcie)
 {
 	int i, ret;
 
-	for (i = 0; i < ARRAY_SIZE(pcie->phy); i++) {
+	for (i = ARRAY_SIZE(pcie->phy) - 1; i >= 0; i--) {
 		ret = phy_init(pcie->phy[i]);
-		if (ret)
-			goto err;
-
-		ret = phy_power_on(pcie->phy[i]);
 		if (ret) {
 			nwl_pcie_phy_exit(pcie, i);
-			goto err;
+			return ret;
 		}
 	}
 
 	return 0;
-
-err:
-	while (i--) {
-		nwl_pcie_phy_power_off(pcie, i);
-		nwl_pcie_phy_exit(pcie, i);
-	}
-
-	return ret;
 }
 
-static void nwl_pcie_phy_disable(struct nwl_pcie *pcie)
+static int nwl_pcie_phy_power_on(struct nwl_pcie *pcie)
 {
-	int i;
+	int i, ret;
 
-	for (i = ARRAY_SIZE(pcie->phy); i--;) {
-		nwl_pcie_phy_power_off(pcie, i);
-		nwl_pcie_phy_exit(pcie, i);
+	for (i = ARRAY_SIZE(pcie->phy) - 1; i >= 0; i--) {
+		ret = phy_power_on(pcie->phy[i]);
+		if (ret) {
+			nwl_pcie_phy_power_off(pcie, i);
+			return ret;
+		}
 	}
+
+	return 0;
 }
 
 static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
@@ -859,22 +858,28 @@ static int nwl_pcie_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	err = nwl_pcie_phy_enable(pcie);
+	err = nwl_pcie_phy_init(pcie);
 	if (err) {
-		dev_err(dev, "could not enable PHYs\n");
+		dev_err(dev, "could not init PHYs\n");
 		goto err_clk;
 	}
 
+	err = nwl_pcie_phy_power_on(pcie);
+	if (err) {
+		dev_err(dev, "could not power on PHYs\n");
+		goto err_phy_init;
+	}
+
 	err = nwl_pcie_bridge_init(pcie);
 	if (err) {
 		dev_err(dev, "HW Initialization failed\n");
-		goto err_phy;
+		goto err_phy_power;
 	}
 
 	err = nwl_pcie_init_irq_domain(pcie);
 	if (err) {
 		dev_err(dev, "Failed creating IRQ Domain\n");
-		goto err_phy;
+		goto err_phy_power;
 	}
 
 	bridge->sysdata = pcie;
@@ -884,7 +889,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
 		err = nwl_pcie_enable_msi(pcie);
 		if (err < 0) {
 			dev_err(dev, "failed to enable MSI support: %d\n", err);
-			goto err_phy;
+			goto err_phy_power;
 		}
 	}
 
@@ -892,8 +897,10 @@ static int nwl_pcie_probe(struct platform_device *pdev)
 	if (!err)
 		return 0;
 
-err_phy:
-	nwl_pcie_phy_disable(pcie);
+err_phy_power:
+	nwl_pcie_phy_power_off(pcie, ARRAY_SIZE(pcie->phy));
+err_phy_init:
+	nwl_pcie_phy_exit(pcie, ARRAY_SIZE(pcie->phy));
 err_clk:
 	clk_disable_unprepare(pcie->clk);
 	return err;
@@ -903,7 +910,8 @@ static void nwl_pcie_remove(struct platform_device *pdev)
 {
 	struct nwl_pcie *pcie = platform_get_drvdata(pdev);
 
-	nwl_pcie_phy_disable(pcie);
+	nwl_pcie_phy_power_off(pcie, ARRAY_SIZE(pcie->phy));
+	nwl_pcie_phy_exit(pcie, ARRAY_SIZE(pcie->phy));
 	clk_disable_unprepare(pcie->clk);
 }
 
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
                   ` (5 preceding siblings ...)
  2026-02-03  0:21 ` [PATCH 6/8] PCI: xilinx-nwl: Split phy_init from phy_power_on Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  2026-02-18 16:42   ` Manivannan Sadhasivam
  2026-02-03  0:21 ` [PATCH 8/8] arm64: zynqmp: Add PCIe resets Sean Anderson
  7 siblings, 1 reply; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

The PCIe core must be held in reset when initializing phys.
Assert/deassert the appropriate resets.

Resetting the core also resets the PCIe attributes to their default
values, so initialize those too. For the most part the defaults are
fine, but there are many attributes that default to an endpoint
configuration and must be reprogrammed to function as a root device.
We generally follow the controller programming sequence from UG1085.

Xilinx was extremely imaginative and named all the registers ATTR_1,
ATTR_2 etc. (with the fields organized in alphabetical order) so we
follow the same convention. Only the fields are named, but sometimes a
field is split across multiple registers. All the BARs are unused but
some are repurposed as bridge registers when used as a root port.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 drivers/pci/controller/pcie-xilinx-nwl.c | 177 +++++++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
index 7cfdc21e6f40..b78fbad1efa5 100644
--- a/drivers/pci/controller/pcie-xilinx-nwl.c
+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
@@ -22,6 +22,7 @@
 #include <linux/pci-ecam.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/reset.h>
 #include <linux/irqchip/chained_irq.h>
 
 #include "../pci.h"
@@ -133,6 +134,54 @@
 #define CFG_DMA_REG_BAR			GENMASK(2, 0)
 #define CFG_PCIE_CACHE			GENMASK(7, 0)
 
+#define PCIE_ATTR2_AER_CAP_PERMIT_ROOTERR_UPDATE	BIT(0)
+
+#define PCIE_ATTR25_CPL_TIMEOUT_DISABLE_SUPPORTED	BIT(9)
+#define PCIE_ATTR25_INTX_IMPLEMENTED			BIT(8)
+#define PCIE_ATTR25_CLASS_CODE				GENMASK(7, 0)
+
+#define PCIE_ATTR27_DEV_CAP_ENDPOINT_L1_LATENCY		GENMASK(5, 3)
+
+#define PCIE_ATTR34_HEADER_TYPE				GENMASK(7, 0)
+
+#define PCIE_ATTR35_LINK_CAP_DLL_ACTIVE_REPORTING	BIT(15)
+
+#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED		GENMASK(13, 10)
+#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED_2_5		1
+#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED_5_0		2
+#define PCIE_ATTR37_LINK_CAP_BANDWIDTH_NOTIFICATION	BIT(9)
+
+#define PCIE_ATTR50_CAP_DEVICE_PORT_TYPE		GENMASK(7, 4)
+#define PCIE_ATTR50_CAP_NEXTPTR				GENMASK(15, 8)
+
+#define PCIE_ATTR53_CAP_NEXTPTR				GENMASK(7, 0)
+
+#define PCIE_ATTR93_LL_REPLAY_TIMEOUT_EN		BIT(15)
+
+#define PCIE_ATTR97_LTSSM_MAX_LINK_WIDTH		GENMASK(11, 6)
+#define PCIE_ATTR97_LINK_CAP_MAX_LINK_WIDTH		GENMASK(5, 0)
+
+#define PCIE_ATTR100_UPSTREAM_FACING			BIT(6)
+
+#define PCIE_ATTR101_EN_MSG_ROUTE			GENMASK(15, 5)
+#define PCIE_ATTR101_EN_MSG_ROUTE_PME_TURN_OFF		BIT(15)
+#define PCIE_ATTR101_EN_MSG_ROUTE_UNLOCK		BIT(14)
+#define PCIE_ATTR101_EN_MSG_ROUTE_PME_TO_ACK		BIT(13)
+#define PCIE_ATTR101_EN_MSG_ROUTE_PM_PME		BIT(12)
+#define PCIE_ATTR101_EN_MSG_ROUTE_INTD			BIT(11)
+#define PCIE_ATTR101_EN_MSG_ROUTE_INTC			BIT(10)
+#define PCIE_ATTR101_EN_MSG_ROUTE_INTB			BIT(9)
+#define PCIE_ATTR101_EN_MSG_ROUTE_INTA			BIT(8)
+#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_FATAL		BIT(7)
+#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_NONFATAL		BIT(6)
+#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_COR		BIT(5)
+#define PCIE_ATTR101_DISABLE_BAR_FILTERING		BIT(1)
+
+#define PCIE_ATTR106_VC0_TOTAL_CREDITS_NPH		GENMASK(13, 7)
+#define PCIE_ATTR106_VC0_TOTAL_CREDITS_CH		GENMASK(6, 0)
+
+#define PCIE_ATTR109_VC0_TOTAL_CREDITS_PH		GENMASK(6, 0)
+
 #define INT_PCI_MSI_NR			(2 * 32)
 
 /* Readin the PS_LINKUP */
@@ -159,6 +208,7 @@ struct nwl_pcie {
 	void __iomem *pcireg_base;
 	void __iomem *ecam_base;
 	struct phy *phy[4];
+	struct reset_control *ctrl_reset;
 	phys_addr_t phys_breg_base;	/* Physical Bridge Register Base */
 	phys_addr_t phys_pcie_reg_base;	/* Physical PCIe Controller Base */
 	phys_addr_t phys_ecam_base;	/* Physical Configuration Base */
@@ -173,6 +223,115 @@ struct nwl_pcie {
 	raw_spinlock_t leg_mask_lock;
 };
 
+static void nwl_pcie_write_attr(struct nwl_pcie *pcie, u32 attr, u16 val)
+{
+	writel(val, pcie->pcireg_base + attr * 4);
+}
+
+static void nwl_pcie_modify_attr(struct nwl_pcie *pcie, u32 attr, u16 clear,
+				u16 set)
+{
+	u32 val = readl(pcie->pcireg_base + attr * 4);
+
+	nwl_pcie_write_attr(pcie, attr, (val & ~clear) | set);
+}
+
+static void nwl_pcie_attr_init(struct nwl_pcie *pcie)
+{
+	unsigned int width;
+
+	for (width = ARRAY_SIZE(pcie->phy); width; width--)
+		if (pcie->phy[width - 1])
+			break;
+
+	/* Set TLP header to type-1 */
+	nwl_pcie_modify_attr(pcie, 34, PCIE_ATTR34_HEADER_TYPE, PCI_HEADER_TYPE_BRIDGE);
+	nwl_pcie_modify_attr(pcie, 100, PCIE_ATTR100_UPSTREAM_FACING, 0);
+
+	/* Disable BAR0/1 */
+	nwl_pcie_write_attr(pcie, 7, 0);
+	nwl_pcie_write_attr(pcie, 8, 0);
+	nwl_pcie_write_attr(pcie, 9, 0);
+	nwl_pcie_write_attr(pcie, 10, 0);
+	/* Enable primary/secondary/subordinate busses, disable latency timer */
+	nwl_pcie_write_attr(pcie, 11, 0xffff);
+	nwl_pcie_write_attr(pcie, 12, 0x00ff);
+	nwl_pcie_write_attr(pcie, 13, 0x0000); /* Disable I/O window */
+	nwl_pcie_write_attr(pcie, 14, 0x0000); /* Enable secondary status */
+	/* Enable memory window */
+	nwl_pcie_write_attr(pcie, 15, (u16)PCI_MEMORY_RANGE_MASK);
+	nwl_pcie_write_attr(pcie, 16, (u16)PCI_MEMORY_RANGE_MASK);
+	/* Enable 64-bit prefetchable window */
+	nwl_pcie_write_attr(pcie, 17,
+			    (u16)PCI_PREF_RANGE_MASK | PCI_PREF_RANGE_TYPE_64);
+	nwl_pcie_write_attr(pcie, 18,
+			    (u16)PCI_PREF_RANGE_MASK | PCI_PREF_RANGE_TYPE_64);
+	nwl_pcie_modify_attr(pcie, 101, 0, PCIE_ATTR101_DISABLE_BAR_FILTERING);
+
+	/* Set class code to PCI_CLASS_BRIDGE_PCI_NORMAL */
+	nwl_pcie_write_attr(pcie, 24, PCI_CLASS_BRIDGE_PCI_NORMAL & 0xffff);
+	nwl_pcie_modify_attr(pcie, 25, PCIE_ATTR25_CLASS_CODE,
+			     PCIE_ATTR25_CPL_TIMEOUT_DISABLE_SUPPORTED |
+			     PCI_BASE_CLASS_BRIDGE);
+
+	/* Enable PCIe capability */
+	nwl_pcie_modify_attr(pcie, 53, PCIE_ATTR53_CAP_NEXTPTR, 0x60);
+	nwl_pcie_modify_attr(pcie, 50,
+			     PCIE_ATTR50_CAP_NEXTPTR |
+			     PCIE_ATTR50_CAP_DEVICE_PORT_TYPE,
+			     FIELD_PREP(PCIE_ATTR50_CAP_DEVICE_PORT_TYPE,
+					PCI_EXP_TYPE_ROOT_PORT));
+
+	/* Disable MSI(-X) capability */
+	nwl_pcie_write_attr(pcie, 41, 0);
+	nwl_pcie_write_attr(pcie, 43, 0);
+	nwl_pcie_write_attr(pcie, 44, 0);
+	nwl_pcie_write_attr(pcie, 45, 0);
+	nwl_pcie_write_attr(pcie, 46, 0);
+	nwl_pcie_write_attr(pcie, 48, 0);
+
+	/* Disable DSN capability */
+	nwl_pcie_write_attr(pcie, 31, 0);
+	nwl_pcie_write_attr(pcie, 82, PCI_CFG_SPACE_SIZE);
+
+	/* Enable AER */
+	nwl_pcie_modify_attr(pcie, 2, 0,
+			     PCIE_ATTR2_AER_CAP_PERMIT_ROOTERR_UPDATE);
+
+	/* Disable L1 latency for root port */
+	nwl_pcie_modify_attr(pcie, 27,
+			     PCIE_ATTR27_DEV_CAP_ENDPOINT_L1_LATENCY, 0);
+
+	/* Enable bandwidth notification */
+	nwl_pcie_modify_attr(pcie, 37, 0,
+			     PCIE_ATTR37_LINK_CAP_BANDWIDTH_NOTIFICATION);
+
+	/* Set max link width */
+	nwl_pcie_write_attr(pcie, 97,
+			    FIELD_PREP(PCIE_ATTR97_LTSSM_MAX_LINK_WIDTH, width) |
+			    FIELD_PREP(PCIE_ATTR97_LINK_CAP_MAX_LINK_WIDTH, width));
+
+	/* Route misc. TLPs to controller */
+	nwl_pcie_modify_attr(pcie, 101, PCIE_ATTR101_EN_MSG_ROUTE,
+			     PCIE_ATTR101_EN_MSG_ROUTE_INTA |
+			     PCIE_ATTR101_EN_MSG_ROUTE_INTB |
+			     PCIE_ATTR101_EN_MSG_ROUTE_INTC |
+			     PCIE_ATTR101_EN_MSG_ROUTE_INTD |
+			     PCIE_ATTR101_EN_MSG_ROUTE_PM_PME |
+			     PCIE_ATTR101_EN_MSG_ROUTE_PME_TO_ACK |
+			     PCIE_ATTR101_EN_MSG_ROUTE_UNLOCK |
+			     PCIE_ATTR101_EN_MSG_ROUTE_PME_TURN_OFF);
+
+	/* Initialize completion credits */
+	nwl_pcie_write_attr(pcie, 105, 205); /* CD */
+	nwl_pcie_write_attr(pcie, 106,
+			    FIELD_PREP(PCIE_ATTR106_VC0_TOTAL_CREDITS_NPH, 12) |
+			    FIELD_PREP(PCIE_ATTR106_VC0_TOTAL_CREDITS_CH, 36));
+	nwl_pcie_write_attr(pcie, 107, 24); /* NPD */
+	nwl_pcie_write_attr(pcie, 108, 181); /* PD */
+	nwl_pcie_modify_attr(pcie, 109, PCIE_ATTR109_VC0_TOTAL_CREDITS_PH, 32);
+}
+
 static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off)
 {
 	return readl(pcie->breg_base + off);
@@ -806,6 +965,9 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
 	irq_set_chained_handler_and_data(pcie->irq_intx,
 					 nwl_pcie_leg_handler, pcie);
 
+	pcie->ctrl_reset = devm_reset_control_get_optional(dev, "ctrl");
+	if (IS_ERR(pcie->ctrl_reset))
+		return PTR_ERR(pcie->ctrl_reset);
 
 	for (i = 0; i < ARRAY_SIZE(pcie->phy); i++) {
 		pcie->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
@@ -852,6 +1014,12 @@ static int nwl_pcie_probe(struct platform_device *pdev)
 	if (IS_ERR(pcie->clk))
 		return PTR_ERR(pcie->clk);
 
+	err = reset_control_assert(pcie->ctrl_reset);
+	if (err) {
+		dev_err(dev, "could not enter reset\n");
+		return err;
+	}
+
 	err = clk_prepare_enable(pcie->clk);
 	if (err) {
 		dev_err(dev, "can't enable PCIe ref clock\n");
@@ -864,6 +1032,15 @@ static int nwl_pcie_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
+	if (pcie->ctrl_reset)
+		nwl_pcie_attr_init(pcie);
+
+	err = reset_control_deassert(pcie->ctrl_reset);
+	if (err) {
+		dev_err(dev, "could not release from reset\n");
+		goto err_phy_init;
+	}
+
 	err = nwl_pcie_phy_power_on(pcie);
 	if (err) {
 		dev_err(dev, "could not power on PHYs\n");
-- 
2.35.1.1320.gc452695387.dirty



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

* [PATCH 8/8] arm64: zynqmp: Add PCIe resets
  2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
                   ` (6 preceding siblings ...)
  2026-02-03  0:21 ` [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe Sean Anderson
@ 2026-02-03  0:21 ` Sean Anderson
  7 siblings, 0 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-03  0:21 UTC (permalink / raw)
  To: Laurent Pinchart, Vinod Koul, linux-phy
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi, Radhey Shyam Pandey,
	linux-kernel, Michal Simek, linux-arm-kernel, linux-pci,
	Neil Armstrong, Rob Herring, Thippeswamy Havalige,
	Manivannan Sadhasivam, Bjorn Helgaas, Sean Anderson

Add PCIe reset bindings for the ZynqMP.

Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---

 arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 938b014ca923..178b4c3a7ba4 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -969,6 +969,10 @@ pcie: pcie@fd0e0000 {
 					<0x0 0x0 0x0 0x4 &pcie_intc 0x4>;
 			/* iommus = <&smmu 0x4d0>; */
 			power-domains = <&zynqmp_firmware PD_PCIE>;
+			resets = <&zynqmp_reset ZYNQMP_RESET_PCIE_CFG>,
+				 <&zynqmp_reset ZYNQMP_RESET_PCIE_BRIDGE>,
+				 <&zynqmp_reset ZYNQMP_RESET_PCIE_CTRL>;
+			reset-names = "cfg", "bridge", "ctrl";
 			pcie_intc: legacy-interrupt-controller {
 				interrupt-controller;
 				#address-cells = <0>;
-- 
2.35.1.1320.gc452695387.dirty



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

* RE: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
  2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
@ 2026-02-04  8:32   ` Pandey, Radhey Shyam
  2026-02-05 15:47     ` Sean Anderson
  2026-02-10  0:24   ` Rob Herring (Arm)
  1 sibling, 1 reply; 18+ messages in thread
From: Pandey, Radhey Shyam @ 2026-02-04  8:32 UTC (permalink / raw)
  To: Sean Anderson, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas, Conor Dooley,
	Krzysztof Kozlowski, devicetree@vger.kernel.org

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Sean Anderson <sean.anderson@linux.dev>
> Sent: Tuesday, February 3, 2026 5:51 AM
> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
> <vkoul@kernel.org>; linux-phy@lists.infradead.org
> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>; Conor
> Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>;
> devicetree@vger.kernel.org
> Subject: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
>
> Add resets so we can hold the bridge in reset while we perform phy calibration.

Seems like this should a required property?

Rest looks fine to me.

>
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
>
>  .../devicetree/bindings/pci/xlnx,nwl-pcie.yaml  | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> index 9de3c09efb6e..7efb3dd9955f 100644
> --- a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> +++ b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> @@ -69,6 +69,18 @@ properties:
>    power-domains:
>      maxItems: 1
>
> +  resets:
> +    maxItems: 3
> +
> +  reset-names:
> +    items:
> +      - description: APB register block reset
> +        const: cfg
> +      - description: AXI-PCIe bridge reset
> +        const: bridge
> +      - description: PCIe MAC reset
> +        const: ctrl
> +
>    iommus:
>      maxItems: 1
>
> @@ -117,6 +129,7 @@ examples:
>      #include <dt-bindings/interrupt-controller/irq.h>
>      #include <dt-bindings/phy/phy.h>
>      #include <dt-bindings/power/xlnx-zynqmp-power.h>
> +    #include <dt-bindings/reset/xlnx-zynqmp-resets.h>
>      soc {
>          #address-cells = <2>;
>          #size-cells = <2>;
> @@ -146,6 +159,10 @@ examples:
>              msi-parent = <&nwl_pcie>;
>              phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
>              power-domains = <&zynqmp_firmware PD_PCIE>;
> +            resets = <&zynqmp_reset ZYNQMP_RESET_PCIE_CFG>,
> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_BRIDGE>,
> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_CTRL>;
> +            reset-names = "cfg", "bridge", "ctrl";
>              iommus = <&smmu 0x4d0>;
>              pcie_intc: legacy-interrupt-controller {
>                  interrupt-controller;
> --
> 2.35.1.1320.gc452695387.dirty



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

* Re: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
  2026-02-04  8:32   ` Pandey, Radhey Shyam
@ 2026-02-05 15:47     ` Sean Anderson
  2026-02-18 16:36       ` Manivannan Sadhasivam
  0 siblings, 1 reply; 18+ messages in thread
From: Sean Anderson @ 2026-02-05 15:47 UTC (permalink / raw)
  To: Pandey, Radhey Shyam, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas, Conor Dooley,
	Krzysztof Kozlowski, devicetree@vger.kernel.org

On 2/4/26 03:32, Pandey, Radhey Shyam wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
> 
>> -----Original Message-----
>> From: Sean Anderson <sean.anderson@linux.dev>
>> Sent: Tuesday, February 3, 2026 5:51 AM
>> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
>> <vkoul@kernel.org>; linux-phy@lists.infradead.org
>> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
>> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
>> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
>> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
>> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
>> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
>> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
>> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>; Conor
>> Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>;
>> devicetree@vger.kernel.org
>> Subject: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
>>
>> Add resets so we can hold the bridge in reset while we perform phy calibration.
> 
> Seems like this should a required property?

It's optional as it does not exist in previous versions of the
devicetree. In the past I have received pushback against making these
sort of properties required.

If the resets don't exist we just don't assert them and assume the
bootloader has deasserted them.

--Sean

> Rest looks fine to me.
> 
>>
>> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
>> ---
>>
>>  .../devicetree/bindings/pci/xlnx,nwl-pcie.yaml  | 17 +++++++++++++++++
>>  1 file changed, 17 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
>> b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
>> index 9de3c09efb6e..7efb3dd9955f 100644
>> --- a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
>> +++ b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
>> @@ -69,6 +69,18 @@ properties:
>>    power-domains:
>>      maxItems: 1
>>
>> +  resets:
>> +    maxItems: 3
>> +
>> +  reset-names:
>> +    items:
>> +      - description: APB register block reset
>> +        const: cfg
>> +      - description: AXI-PCIe bridge reset
>> +        const: bridge
>> +      - description: PCIe MAC reset
>> +        const: ctrl
>> +
>>    iommus:
>>      maxItems: 1
>>
>> @@ -117,6 +129,7 @@ examples:
>>      #include <dt-bindings/interrupt-controller/irq.h>
>>      #include <dt-bindings/phy/phy.h>
>>      #include <dt-bindings/power/xlnx-zynqmp-power.h>
>> +    #include <dt-bindings/reset/xlnx-zynqmp-resets.h>
>>      soc {
>>          #address-cells = <2>;
>>          #size-cells = <2>;
>> @@ -146,6 +159,10 @@ examples:
>>              msi-parent = <&nwl_pcie>;
>>              phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
>>              power-domains = <&zynqmp_firmware PD_PCIE>;
>> +            resets = <&zynqmp_reset ZYNQMP_RESET_PCIE_CFG>,
>> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_BRIDGE>,
>> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_CTRL>;
>> +            reset-names = "cfg", "bridge", "ctrl";
>>              iommus = <&smmu 0x4d0>;
>>              pcie_intc: legacy-interrupt-controller {
>>                  interrupt-controller;
>> --
>> 2.35.1.1320.gc452695387.dirty
> 


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

* Re: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
  2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
  2026-02-04  8:32   ` Pandey, Radhey Shyam
@ 2026-02-10  0:24   ` Rob Herring (Arm)
  1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring (Arm) @ 2026-02-10  0:24 UTC (permalink / raw)
  To: Sean Anderson
  Cc: Thippeswamy Havalige, Lorenzo Pieralisi, Michal Simek,
	Krzysztof Wilczyński, Laurent Pinchart, linux-phy,
	Vinod Koul, Neil Armstrong, Conor Dooley, devicetree, linux-pci,
	Bjorn Helgaas, Krzysztof Kozlowski, Radhey Shyam Pandey,
	Manivannan Sadhasivam, linux-arm-kernel, linux-kernel


On Mon, 02 Feb 2026 19:21:21 -0500, Sean Anderson wrote:
> Add resets so we can hold the bridge in reset while we perform phy
> calibration.
> 
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
> 
>  .../devicetree/bindings/pci/xlnx,nwl-pcie.yaml  | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 

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



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

* RE: [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper
  2026-02-03  0:21 ` [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper Sean Anderson
@ 2026-02-10 15:00   ` Pandey, Radhey Shyam
  0 siblings, 0 replies; 18+ messages in thread
From: Pandey, Radhey Shyam @ 2026-02-10 15:00 UTC (permalink / raw)
  To: Sean Anderson, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas

[Public]

> -----Original Message-----
> From: Sean Anderson <sean.anderson@linux.dev>
> Sent: Tuesday, February 3, 2026 5:51 AM
> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
> <vkoul@kernel.org>; linux-phy@lists.infradead.org
> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>
> Subject: [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper
>
> Split off the bus width configuration into a helper function for reuse.
>
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>

Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!
> ---
>
>  drivers/phy/xilinx/phy-zynqmp.c | 20 ++++++++++++--------
>  1 file changed, 12 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
> index fe6b4925d166..0d3c578d0f3f 100644
> --- a/drivers/phy/xilinx/phy-zynqmp.c
> +++ b/drivers/phy/xilinx/phy-zynqmp.c
> @@ -502,6 +502,17 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy
> *gtr_phy)
>       }
>  }
>
> +/* Set the bus width */
> +static void xpsgtr_phy_init_bus_width(struct xpsgtr_phy *gtr_phy, u32 width)
> +{
> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
> +     u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane);
> +     u32 val = width << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane);
> +
> +     xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
> +     xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
> +}
> +
>  /* Bypass (de)scrambler and 8b/10b decoder and encoder. */
>  static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
>  {
> @@ -535,14 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy
> *gtr_phy)
>  /* SGMII-specific initialization. */
>  static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
>  {
> -     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
> -     u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane);
> -     u32 val = PROT_BUS_WIDTH_10 << PROT_BUS_WIDTH_SHIFT(gtr_phy-
> >lane);
> -
> -     /* Set SGMII protocol TX and RX bus width to 10 bits. */
> -     xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
> -     xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
> -
> +     xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_10);
>       xpsgtr_bypass_scrambler_8b10b(gtr_phy);
>  }
>
> --
> 2.35.1.1320.gc452695387.dirty



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

* RE: [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper
  2026-02-03  0:21 ` [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper Sean Anderson
@ 2026-02-10 15:05   ` Pandey, Radhey Shyam
  0 siblings, 0 replies; 18+ messages in thread
From: Pandey, Radhey Shyam @ 2026-02-10 15:05 UTC (permalink / raw)
  To: Sean Anderson, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas

[Public]

> -----Original Message-----
> From: Sean Anderson <sean.anderson@linux.dev>
> Sent: Tuesday, February 3, 2026 5:51 AM
> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
> <vkoul@kernel.org>; linux-phy@lists.infradead.org
> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>
> Subject: [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper
>
> All lanes undergoing ILL calibration must be initialized. Split off
> common phy initialization into a helper so that we can ensure all lanes
> are initialized before performing calibration.
>
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>

Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!
> ---
>
>  drivers/phy/xilinx/phy-zynqmp.c | 22 ++++++++++++++++------
>  1 file changed, 16 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
> index 0d3c578d0f3f..152af1702bbd 100644
> --- a/drivers/phy/xilinx/phy-zynqmp.c
> +++ b/drivers/phy/xilinx/phy-zynqmp.c
> @@ -520,6 +520,21 @@ static void xpsgtr_bypass_scrambler_8b10b(struct
> xpsgtr_phy *gtr_phy)
>       xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61,
> L0_TM_DISABLE_SCRAMBLE_ENCODER);
>  }
>
> +static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
> +{
> +     int ret;
> +
> +     /* Enable coarse code saturation limiting logic. */
> +     xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37,
> L0_TM_COARSE_CODE_LIMIT);
> +
> +     ret = xpsgtr_configure_pll(gtr_phy);
> +     if (ret)
> +             return ret;
> +
> +     xpsgtr_lane_set_protocol(gtr_phy);
> +     return 0;
> +}
> +
>  /* DP-specific initialization. */
>  static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy)
>  {
> @@ -682,19 +697,14 @@ static int xpsgtr_phy_init(struct phy *phy)
>               gtr_dev->tx_term_fix = false;
>       }
>
> -     /* Enable coarse code saturation limiting logic. */
> -     xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37,
> L0_TM_COARSE_CODE_LIMIT);
> -
>       /*
>        * Configure the PLL, the lane protocol, and perform protocol-specific
>        * initialization.
>        */
> -     ret = xpsgtr_configure_pll(gtr_phy);
> +     ret = xpsgtr_common_init(gtr_phy);
>       if (ret)
>               goto out;
>
> -     xpsgtr_lane_set_protocol(gtr_phy);
> -
>       switch (gtr_phy->protocol) {
>       case ICM_PROTOCOL_DP:
>               xpsgtr_phy_init_dp(gtr_phy);
> --
> 2.35.1.1320.gc452695387.dirty



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

* RE: [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary
  2026-02-03  0:21 ` [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary Sean Anderson
@ 2026-02-10 16:04   ` Pandey, Radhey Shyam
  2026-02-10 16:42     ` Sean Anderson
  0 siblings, 1 reply; 18+ messages in thread
From: Pandey, Radhey Shyam @ 2026-02-10 16:04 UTC (permalink / raw)
  To: Sean Anderson, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas

[Public]

> -----Original Message-----
> From: Sean Anderson <sean.anderson@linux.dev>
> Sent: Tuesday, February 3, 2026 5:51 AM
> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
> <vkoul@kernel.org>; linux-phy@lists.infradead.org
> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>
> Subject: [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary
>
> init_serdes in psu_init_gpl is supposed to calibrate the ILL. However, this
> may fail if the reference clock is not running, such as if the clock needs
> to be configured on boot. To work around this, add support for ILL
> calibration in U-Boot. If the ILL is already calibrated (any non-zero
> value) we skip calibration.
>
> The algorithm is substantially the same as serdes_illcalib [1], but it has
> been updated for readability (and to remove all the "if (lane0_active)"
> conditions). Due to the amount of register fields, many of which are
> undocumented (especially the chicken bits), I have mostly used defines only
> for the register names. There are certainly areas where register writes are
> superfluous, but I have left these in order to minimize deviation from the
> procedure in serdes_illcalib.

Please consider splitting the patch in introducing calibrate ILL functions
and then subsequently using them.

How did you validate the changes? functional testing is one part
but I think better to match register configuration done in psu_init vs
this series? Have we tried running on multiple designs having
different GT lane and protocol combinations .

There are multiple magic numbers. Consider renaming it to
meaningful defines.

>
> [1] Example implementation; xpsgtr_phy_illcalib coresponds to
>     serdes_illcalib_pcie_gen1:
> https://source.denx.de/u-boot/u-boot/-/blob/v2026.01/board/xilinx/zynqmp/zynqmp-
> zcu208-revA/psu_init_gpl.c?ref_type=tags#L710
>
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
>
>  drivers/phy/xilinx/phy-zynqmp.c | 421 +++++++++++++++++++++++++++++++-
>  1 file changed, 420 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
> index 152af1702bbd..854b0ea04648 100644
> --- a/drivers/phy/xilinx/phy-zynqmp.c
> +++ b/drivers/phy/xilinx/phy-zynqmp.c
> @@ -12,6 +12,7 @@
>   * PCIe should also work but that is experimental as of now.
>   */
>
> +#include <linux/bitfield.h>
>  #include <linux/clk.h>
>  #include <linux/debugfs.h>
>  #include <linux/delay.h>
> @@ -31,6 +32,7 @@
>   */
>
>  /* TX De-emphasis parameters */
> +#define L0_TX_ANA_TM_3                       0x000c
>  #define L0_TX_ANA_TM_18                      0x0048
>  #define L0_TX_ANA_TM_118             0x01d8
>  #define L0_TX_ANA_TM_118_FORCE_17_0  BIT(0)
> @@ -50,16 +52,49 @@
>  #define L0_TXPMD_TM_45_ENABLE_DP_POST2       BIT(5)
>
>  /* PCS control parameters */
> +#define L0_TM_ANA_BYP_4                      0x1010
> +#define L0_TM_ANA_BYP_7                      0x1018
>  #define L0_TM_DIG_6                  0x106c
> +#define L0_TM_DIG_22                 0x10ac
>  #define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f
>  #define L0_TX_DIG_61                 0x00f4
>  #define L0_TM_DISABLE_SCRAMBLE_ENCODER       0x0f
> +#define L0_TM_AUX_0                  0x10cc
> +#define L0_TM_MISC2                  0x189c
> +#define L0_TM_MISC2_ILL_CAL_BYPASS   BIT(7)
> +#define L0_TM_IQ_ILL1                        0x18f8
> +#define L0_TM_IQ_ILL2                        0x18fc

#define L0_TM_IQ_ILL3                   0x1900

IQ_ILL(n) = 0x18f8 + (n - 1) * 4 ?

Similarly for others  (when applicable)?

> +#define L0_TM_ILL11                  0x198c
> +#define L0_TM_ILL12                  0x1990
> +#define L0_TM_E_ILL1                 0x1924
> +#define L0_TM_E_ILL2                 0x1928
> +#define L0_TM_IQ_ILL3                        0x1900
> +#define L0_TM_E_ILL3                 0x192c
> +#define L0_TM_IQ_ILL7                        0x1910
> +#define L0_TM_E_ILL7                 0x193c
> +#define L0_TM_ILL8                   0x1980
> +#define L0_TM_IQ_ILL8                        0x1914
> +#define L0_TM_IQ_ILL9                        0x1918
> +#define L0_TM_EQ0                    0x194c
> +#define L0_TM_EQ0_EQ_STG2_CTRL_BYP   BIT(5)
> +#define L0_TM_EQ1                    0x1950
> +#define L0_TM_EQ1_EQ_STG2_RL_PROG    GENMASK(1, 0)
> +#define L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL    BIT(2)
> +#define L0_TM_E_ILL8                 0x1940
> +#define L0_TM_E_ILL9                 0x1944
> +#define L0_TM_ILL13                  0x1994
> +#define L0_TM_CDR5                   0x1c14
> +#define L0_TM_CDR5_FPHL_FSM_ACC_CYCLES       GENMASK(7, 5)
> +#define L0_TM_CDR5_FFL_PH0_INT_GAIN  GENMASK(4, 0)
> +#define L0_TM_CDR16                  0x1c40
>
>  /* PLL Test Mode register parameters */
> +#define L0_TM_PLL_DIG_33             0x2084
>  #define L0_TM_PLL_DIG_37             0x2094
>  #define L0_TM_COARSE_CODE_LIMIT              0x10
>
>  /* PLL SSC step size offsets */
> +#define L0_PLL_FBDIV_FRAC_3_MSB              0x2360
>  #define L0_PLL_SS_STEPS_0_LSB                0x2368
>  #define L0_PLL_SS_STEPS_1_MSB                0x236c
>  #define L0_PLL_SS_STEP_SIZE_0_LSB    0x2370
> @@ -69,6 +104,7 @@
>  #define L0_PLL_STATUS_READ_1         0x23e4
>
>  /* SSC step size parameters */
> +#define TM_FORCE_EN_FRAC             BIT(6)
>  #define STEP_SIZE_0_MASK             0xff
>  #define STEP_SIZE_1_MASK             0xff
>  #define STEP_SIZE_2_MASK             0xff
> @@ -76,6 +112,7 @@
>  #define STEP_SIZE_SHIFT                      8
>  #define FORCE_STEP_SIZE                      0x10
>  #define FORCE_STEPS                  0x20
> +#define TM_FORCE_EN                  BIT(7)
>  #define STEPS_0_MASK                 0xff
>  #define STEPS_1_MASK                 0x07
>
> @@ -84,6 +121,32 @@
>  #define L0_REF_CLK_LCL_SEL           BIT(7)
>  #define L0_REF_CLK_SEL_MASK          0x9f
>
> +/* Built-in self-test parameters */
> +#define L0_BIST_CTRL_1                       0x3004
> +#define L0_BIST_CTRL_2                       0x3008
> +#define L0_BIST_RUN_LEN_L            0x300c
> +#define L0_BIST_ERR_INJ_POINT_L              0x3010
> +#define L0_BIST_RUNLEN_ERR_INJ_H     0x3014
> +#define L0_BIST_IDLE_TIME            0x3018
> +#define L0_BIST_MARKER_L             0x301c
> +#define L0_BIST_IDLE_CHAR_L          0x3020
> +#define L0_BIST_MARKER_IDLE_H                0x3024
> +#define L0_BIST_LOW_PULSE_TIME               0x3028
> +#define L0_BIST_TOTAL_PULSE_TIME     0x302c
> +#define L0_BIST_TEST_PAT_1           0x3030
> +#define L0_BIST_TEST_PAT_2           0x3034
> +#define L0_BIST_TEST_PAT_3           0x3038
> +#define L0_BIST_TEST_PAT_4           0x303c
> +#define L0_BIST_TEST_PAT_MSBS                0x3040
> +#define L0_BIST_PKT_NUM                      0x3044
> +#define L0_BIST_FRM_IDLE_TIME                0x3048
> +#define L0_BIST_PKT_CTR_L            0x304c
> +#define L0_BIST_PKT_CTR_H            0x3050
> +#define L0_BIST_ERR_CTR_L            0x3054
> +#define L0_BIST_ERR_CTR_H            0x3058
> +#define L0_BIST_FILLER_OUT           0x3068
> +#define L0_BIST_FORCE_MK_RST         0x306c
> +
>  /* Calibration digital logic parameters */
>  #define L3_TM_CALIB_DIG19            0xec4c
>  #define L3_CALIB_DONE_STATUS         0xef14
> @@ -139,6 +202,9 @@ static const char *const xpsgtr_icm_str[] = {
>  #define TM_CMN_RST_SET                       0x2
>  #define TM_CMN_RST_MASK                      0x3
>
> +#define LPBK_CTRL0                   0x10038
> +#define LPBK_CTRL1                   0x1003c
> +
>  /* Bus width parameters */
>  #define TX_PROT_BUS_WIDTH            0x10040
>  #define RX_PROT_BUS_WIDTH            0x10044
> @@ -148,9 +214,13 @@ static const char *const xpsgtr_icm_str[] = {
>  #define PROT_BUS_WIDTH_SHIFT(n)              ((n) * 2)
>  #define PROT_BUS_WIDTH_MASK(n)               GENMASK((n) * 2 + 1, (n) * 2)
>
> +#define UPHY_SPARE0                  0X10098
> +
>  /* Number of GT lanes */
>  #define NUM_LANES                    4
>
> +#define SIOU_ECO_0                   0x1c
> +
>  /* SIOU SATA control register */
>  #define SATA_CONTROL_OFFSET          0x0100
>
> @@ -338,6 +408,33 @@ static void xpsgtr_restore_lane_regs(struct xpsgtr_dev
> *gtr_dev)
>                            gtr_dev->saved_regs[i]);
>  }
>
> +static inline void xpsgtr_write_lanes(struct xpsgtr_dev *gtr_dev,
> +                                  unsigned long *lanes, u32 reg, u32 value)
> +{
> +     unsigned long lane;
> +
> +     for_each_set_bit(lane, lanes, NUM_LANES) {
> +             void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
> +                                  + reg;
> +
> +             writel(value, addr);
> +     }
> +}
> +
> +static inline void xpsgtr_clr_set_lanes(struct xpsgtr_dev *gtr_dev,
> +                                    unsigned long *lanes, u32 reg, u32 clr,
> +                                    u32 set)
> +{
> +     unsigned long lane;
> +
> +     for_each_set_bit(lane, lanes, NUM_LANES) {
> +             void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
> +                                  + reg;
> +
> +             writel((readl(addr) & ~clr) | set, addr);
> +     }
> +}
> +
>  /*
>   * Hardware Configuration
>   */
> @@ -351,7 +448,7 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
>       u8 protocol = gtr_phy->protocol;
>       int ret;
>
> -     dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n");
> +     dev_vdbg(gtr_dev->dev, "Waiting for PLL lock\n");

Unrelated change.

>
>       /*
>        * For DP and PCIe, only the instance 0 PLL is used. Switch to that phy
> @@ -520,6 +617,231 @@ static void xpsgtr_bypass_scrambler_8b10b(struct
> xpsgtr_phy *gtr_phy)
>       xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61,
> L0_TM_DISABLE_SCRAMBLE_ENCODER);
>  }
>
> +/* Enable or disable loopback */
> +static void xpsgtr_phy_set_loopback(struct xpsgtr_phy *gtr_phy, bool enabled)
> +{
> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
> +     u32 reg = gtr_phy->lane >= 2 ? LPBK_CTRL1 : LPBK_CTRL0;
> +     u32 shift = gtr_phy->lane & 1 ? 4 : 0;
> +
> +     xpsgtr_clr_set(gtr_dev, reg, 7 << shift, (u32)enabled << shift);
> +}
> +
> +static void xpsgtr_phy_set_ill(struct xpsgtr_phy *gtr_phy, u32 ill, bool gen2)
> +{
> +     u32 val = 4 + ill * 8;
> +
> +     if (gen2) {
> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL2, val & 0xff);
> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0x0f,
> +                                1 << (val >> 8));
> +     } else {
> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL1, val & 0xff);
> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0xf0,
> +                                (val >> 4) & 0x10);
> +     }
> +}
> +
> +static bool xpsgtr_ill_calibrated(struct xpsgtr_phy *gtr_phy)
> +{
> +     u32 ill1 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL1);
> +     u32 ill2 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL2);
> +     u32 ill12 = xpsgtr_read_phy(gtr_phy, L0_TM_ILL12);
> +
> +     dev_dbg(gtr_phy->dev->dev, "lane %u gen1 ILL was %u gen2 ILL was
> %u\n",
> +             gtr_phy->lane, ill1 / 8 + (ill12 & 0x10 ? 32 : 0),
> +             ill2 / 8 + (ill12 & 0x02 ? 32 : 0));
> +     return ill1 || ill2 || ill12;
> +}
> +
> +static void xpsgtr_init_ill(struct xpsgtr_phy *gtr_phy)
> +{
> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
> +     struct clk *clk = gtr_dev->clk[gtr_phy->refclk];
> +     u32 ill123 = DIV_ROUND_CLOSEST(clk_get_rate(clk), 1000000);
> +
> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_MISC2, 0,
> L0_TM_MISC2_ILL_CAL_BYPASS);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL1, ill123);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL2, ill123);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL3, ill123);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL7, 0xf3);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL7, 0xf3);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_ILL8, 0xff);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL9, 1);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL9, 1);
> +     xpsgtr_clr_set(gtr_dev, UPHY_SPARE0, BIT(5), 0);
> +}
> +
> +static void xpsgtr_phy_illcalib(struct xpsgtr_dev *gtr_dev,
> +                                  unsigned long *lanes, bool gen2)
> +{
> +     bool last_ok[NUM_LANES] = { 0 };
> +     int pass[NUM_LANES] = { 0 }, altpass[NUM_LANES] = { 0 };
> +     int best[NUM_LANES] = { 0 }, altbest[NUM_LANES] = { 0 };
> +     unsigned long lane;
> +     int i;
> +
> +     /* Initialize the BIST */
> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 1);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0x20);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0xf4);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0xfb);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0xff);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0x4a);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0x4a);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0x4a);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0x4a);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0x14);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 2);
> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
> +
> +     for (i = 0; i < 64; i++) {
> +             bool ok[NUM_LANES];
> +
> +             for_each_set_bit(lane, lanes, NUM_LANES)
> +                     xpsgtr_phy_set_ill(&gtr_dev->phys[lane], i, gen2);
> +
> +             /* Reset lanes */
> +             xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x20,
> +                                  0x10);
> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x40);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x04);
> +             udelay(50);
> +             if (gen2)
> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0e);
> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x06);
> +             if (gen2) {
> +                     xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x04);
> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x07);
> +                     udelay(400);
> +                     xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x0c);
> +                     udelay(15);
> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0f);
> +                     udelay(100);
> +             }
> +
> +             if (xpsgtr_wait_pll_lock(gtr_dev->phys[0].phy)) {
> +                     memset(last_ok, 0, sizeof(last_ok));
> +                     continue;
> +             }
> +
> +             udelay(50);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0xc0);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x80);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0xc0);
> +             udelay(50);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
> +             udelay(50);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0);
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0);
> +             udelay(500);
> +
> +             /* Do the BIST */
> +             for_each_set_bit(lane, lanes, NUM_LANES) {
> +                     struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
> +                     u32 packets, errors;
> +
> +                     xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_10);
> +                     xpsgtr_phy_set_loopback(gtr_phy, true);
> +                     xpsgtr_write_phy(gtr_phy, L0_TM_DIG_22, 0x20);
> +                     xpsgtr_clr_set_phy(gtr_phy, L0_BIST_CTRL_1, 0, 1);
> +
> +                     udelay(200);
> +                     xpsgtr_write_phy(gtr_phy, L0_BIST_CTRL_1, 0);
> +                     packets = xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_L);
> +                     packets |= xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_H)
> << 8;
> +                     errors = xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_L);
> +                     errors |= xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_H)
> << 8;
> +                     ok[lane] = packets && !errors;
> +
> +                     dev_dbg(gtr_dev->dev,
> +                             "lane %lu ILL %d packets %10u errors %10u\n",
> +                             lane, i, packets, errors);
> +             }
> +
> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x02);
> +
> +             for_each_set_bit(lane, lanes, NUM_LANES) {
> +                     pass[lane] += ok[lane] && last_ok[lane];
> +                     if (pass[lane] < 4) {
> +                             if (!ok[lane] && i > 2) {
> +                                     if (altpass[lane] < pass[lane]) {
> +                                             altpass[lane] = pass[lane];
> +                                             altbest[lane] =
> +                                                     (i - 1) - (pass[lane] + 1) / 2;
> +                                     }
> +                                     pass[lane] = 0;
> +                             }
> +                     } else if (!best[lane] && (!ok[lane] || i == 63) &&
> +                                last_ok[lane]) {
> +                             best[lane] = (i - 1) - (pass[lane] + 1) / 2;
> +                     }
> +             }
> +
> +             memcpy(last_ok, ok, sizeof(ok));
> +     }
> +
> +     for_each_set_bit(lane, lanes, NUM_LANES) {
> +             dev_dbg(gtr_dev->dev, "lane %lu ILL best %d alt best %d\n",
> +                     lane, best[lane], altbest[lane]);
> +
> +             xpsgtr_phy_set_ill(&gtr_dev->phys[lane],
> +                                best[lane] ?: altbest[lane], gen2);
> +     }
> +
> +     /* Clean up */
> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x30, 0);
> +     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_L, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_H, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 0);
> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0);
> +
> +     for_each_set_bit(lane, lanes, NUM_LANES) {
> +             struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
> +
> +             xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_20);
> +             xpsgtr_phy_set_loopback(gtr_phy, false);
> +     }
> +}
> +
>  static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
>  {
>       int ret;
> @@ -553,6 +875,37 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy
> *gtr_phy)
>  {
>       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
>
> +     if (!xpsgtr_ill_calibrated(gtr_phy)) {
> +             DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
> +
> +             xpsgtr_init_ill(gtr_phy);
> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL3, 100);
> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL11, 0xf0, 0x20);
> +
> +             __set_bit(gtr_phy->lane, lanes);
> +             xpsgtr_phy_illcalib(gtr_dev, lanes, false);
> +             xpsgtr_phy_set_ill(gtr_phy, 7, true);
> +     }
> +
> +     /* Disable SSC */
> +     xpsgtr_write_phy(gtr_phy, L0_PLL_FBDIV_FRAC_3_MSB,
> TM_FORCE_EN_FRAC);
> +     xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, 0,
> TM_FORCE_EN);
> +
> +     /* Disable Tx deemphasis */
> +     xpsgtr_write_phy(gtr_phy, L0_TM_CDR5,
> +                      FIELD_PREP(L0_TM_CDR5_FPHL_FSM_ACC_CYCLES,
> 7) |
> +                      FIELD_PREP(L0_TM_CDR5_FFL_PH0_INT_GAIN, 6));
> +     xpsgtr_write_phy(gtr_phy, L0_TM_CDR16, 12);
> +
> +     /* Configure equalization */
> +     xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118,
> +                      L0_TX_ANA_TM_118_FORCE_17_0);
> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ0, 0,
> L0_TM_EQ0_EQ_STG2_CTRL_BYP);
> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ1,
> L0_TM_EQ1_EQ_STG2_RL_PROG,
> +                        FIELD_PREP(L0_TM_EQ1_EQ_STG2_RL_PROG, 2) |
> +                        L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL);
> +     xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, 2); /* -3.5 dB deemphasis */
> +
>       xpsgtr_bypass_scrambler_8b10b(gtr_phy);
>
>       writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
> @@ -565,6 +918,64 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy
> *gtr_phy)
>       xpsgtr_bypass_scrambler_8b10b(gtr_phy);
>  }
>
> +/* PCIe-specific initialization. */
> +static int xpsgtr_phy_init_pcie(struct xpsgtr_phy *gtr_phy)
> +{
> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
> +     DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
> +     unsigned long lane;
> +     bool calibrated = false;
> +
> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
> +
> +     for (lane = 0; lane < NUM_LANES; lane++) {
> +             struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
> +
> +             if (gtr_phy->protocol != ICM_PROTOCOL_PCIE)
> +                     continue;
> +
> +             __set_bit(lane, lanes);
> +             calibrated = calibrated || xpsgtr_ill_calibrated(gtr_phy);
> +     }
> +
> +     if (calibrated)
> +             return 0;
> +
> +     /* Write default ILL config */
> +     for_each_set_bit(lane, lanes, NUM_LANES) {
> +             struct xpsgtr_phy *p = &gtr_dev->phys[lane];
> +
> +             if (lane != gtr_phy->lane) {
> +                     int ret = xpsgtr_common_init(p);
> +
> +                     if (ret)
> +                             return ret;
> +             }
> +
> +             xpsgtr_init_ill(p);
> +             xpsgtr_write_phy(p, L0_TM_E_ILL3, 0);
> +             xpsgtr_clr_set_phy(p, L0_TM_MISC2, 0,
> +                                L0_TM_MISC2_ILL_CAL_BYPASS);
> +     }
> +
> +     /* Perform the ILL calibration procedure */
> +     xpsgtr_phy_illcalib(gtr_dev, lanes, false);
> +     xpsgtr_phy_illcalib(gtr_dev, lanes, true);
> +
> +     /* Disable PCIe ECO */
> +     writel(1, gtr_dev->siou + SIOU_ECO_0);
> +     return 0;
> +}
> +
> +/* USB-specific initialization. */
> +static void xpsgtr_phy_init_usb(struct xpsgtr_phy *gtr_phy)
> +{
> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
> +     xpsgtr_phy_set_ill(gtr_phy, 7, false);
> +}
> +
>  /* Configure TX de-emphasis and margining for DP. */
>  static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre,
>                                   unsigned int voltage)
> @@ -710,6 +1121,10 @@ static int xpsgtr_phy_init(struct phy *phy)
>               xpsgtr_phy_init_dp(gtr_phy);
>               break;
>
> +     case ICM_PROTOCOL_PCIE:
> +             ret = xpsgtr_phy_init_pcie(gtr_phy);
> +             break;
> +
>       case ICM_PROTOCOL_SATA:
>               xpsgtr_phy_init_sata(gtr_phy);
>               break;
> @@ -717,6 +1132,10 @@ static int xpsgtr_phy_init(struct phy *phy)
>       case ICM_PROTOCOL_SGMII:
>               xpsgtr_phy_init_sgmii(gtr_phy);
>               break;
> +
> +     case ICM_PROTOCOL_USB:
> +             xpsgtr_phy_init_usb(gtr_phy);
> +             break;
>       }
>
>  out:
> --
> 2.35.1.1320.gc452695387.dirty



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

* Re: [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary
  2026-02-10 16:04   ` Pandey, Radhey Shyam
@ 2026-02-10 16:42     ` Sean Anderson
  0 siblings, 0 replies; 18+ messages in thread
From: Sean Anderson @ 2026-02-10 16:42 UTC (permalink / raw)
  To: Pandey, Radhey Shyam, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org
  Cc: Krzysztof Wilczyński, Lorenzo Pieralisi,
	linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy,
	Manivannan Sadhasivam, Bjorn Helgaas

On 2/10/26 11:04, Pandey, Radhey Shyam wrote:
> [Public]
> 
>> -----Original Message-----
>> From: Sean Anderson <sean.anderson@linux.dev>
>> Sent: Tuesday, February 3, 2026 5:51 AM
>> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
>> <vkoul@kernel.org>; linux-phy@lists.infradead.org
>> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
>> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
>> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
>> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
>> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
>> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
>> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
>> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>
>> Subject: [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary
>>
>> init_serdes in psu_init_gpl is supposed to calibrate the ILL. However, this
>> may fail if the reference clock is not running, such as if the clock needs
>> to be configured on boot. To work around this, add support for ILL
>> calibration in U-Boot. If the ILL is already calibrated (any non-zero
>> value) we skip calibration.
>>
>> The algorithm is substantially the same as serdes_illcalib [1], but it has
>> been updated for readability (and to remove all the "if (lane0_active)"
>> conditions). Due to the amount of register fields, many of which are
>> undocumented (especially the chicken bits), I have mostly used defines only
>> for the register names. There are certainly areas where register writes are
>> superfluous, but I have left these in order to minimize deviation from the
>> procedure in serdes_illcalib.
> 
> Please consider splitting the patch in introducing calibrate ILL functions
> and then subsequently using them.

OK

> How did you validate the changes? functional testing is one part
> but I think better to match register configuration done in psu_init vs
> this series?

I've done this in the past, but I didn't do so with this version of the
driver. I'll make sure to do this comparison for v2. FWIW the ILL
values achieved through this procedure are fairly close to the values I
got from the firmware.

> Have we tried running on multiple designs having
> different GT lane and protocol combinations .

Currently tested with SATA on GTR2 (on [1]) and PCIe on GTR0/1 (custom
design). I've also tested SGMII and DP on the other lanes (although not
USB as mentioned in the cover letter). I'd definitely appreciate if you
(or anyone else) could test on some other boards.

[1] https://developer.seco.com/hardware/product/computer-on-modules/smarc-modules/som-smarc-zu-b71/

> There are multiple magic numbers. Consider renaming it to
> meaningful defines.

Some of the fields have very long names. E.g. L0_TX_ANA_TM_3 has two
fields that we set: TX_serializer_enable and
force_TX_serializer_enabled. Setting it would look like

			xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3,
					   L0_TX_ANA_TM_3_TX_SERIALIZER_ENABLE |
					   L0_TX_ANA_TM_3_FORCE_TX_SERIALIZER_ENABLE);

and there's really no way to get that under 80 characters. This is
probably the area where I could most improve things. But I'm not a fan
of tripling the line count since I think it makes it harder to follow
the flow of the initialization.

Other fields have poor documentation. E.g. L0_TM_IQ_ILL8 has a single
field ill_bypass_iq_polytrim_val documented as "IQ ILL polytrim bypass
value." I have no idea what this field does or why it needs to be set to
0xf3 so I can't really define a meaningful constant for it.

Lastly, some fields are not documented at all, notably UPHY_SPARE0.

In these latter two cases, I have not guessed at the semantics, but I'd
more than welcome any additional info.

>>
>> [1] Example implementation; xpsgtr_phy_illcalib coresponds to
>>     serdes_illcalib_pcie_gen1:
>> https://source.denx.de/u-boot/u-boot/-/blob/v2026.01/board/xilinx/zynqmp/zynqmp-
>> zcu208-revA/psu_init_gpl.c?ref_type=tags#L710
>>
>> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
>> ---
>>
>>  drivers/phy/xilinx/phy-zynqmp.c | 421 +++++++++++++++++++++++++++++++-
>>  1 file changed, 420 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
>> index 152af1702bbd..854b0ea04648 100644
>> --- a/drivers/phy/xilinx/phy-zynqmp.c
>> +++ b/drivers/phy/xilinx/phy-zynqmp.c
>> @@ -12,6 +12,7 @@
>>   * PCIe should also work but that is experimental as of now.
>>   */
>>
>> +#include <linux/bitfield.h>
>>  #include <linux/clk.h>
>>  #include <linux/debugfs.h>
>>  #include <linux/delay.h>
>> @@ -31,6 +32,7 @@
>>   */
>>
>>  /* TX De-emphasis parameters */
>> +#define L0_TX_ANA_TM_3                       0x000c
>>  #define L0_TX_ANA_TM_18                      0x0048
>>  #define L0_TX_ANA_TM_118             0x01d8
>>  #define L0_TX_ANA_TM_118_FORCE_17_0  BIT(0)
>> @@ -50,16 +52,49 @@
>>  #define L0_TXPMD_TM_45_ENABLE_DP_POST2       BIT(5)
>>
>>  /* PCS control parameters */
>> +#define L0_TM_ANA_BYP_4                      0x1010
>> +#define L0_TM_ANA_BYP_7                      0x1018
>>  #define L0_TM_DIG_6                  0x106c
>> +#define L0_TM_DIG_22                 0x10ac
>>  #define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f
>>  #define L0_TX_DIG_61                 0x00f4
>>  #define L0_TM_DISABLE_SCRAMBLE_ENCODER       0x0f
>> +#define L0_TM_AUX_0                  0x10cc
>> +#define L0_TM_MISC2                  0x189c
>> +#define L0_TM_MISC2_ILL_CAL_BYPASS   BIT(7)
>> +#define L0_TM_IQ_ILL1                        0x18f8
>> +#define L0_TM_IQ_ILL2                        0x18fc
> 
> #define L0_TM_IQ_ILL3                   0x1900
> 
> IQ_ILL(n) = 0x18f8 + (n - 1) * 4 ?
> 
> Similarly for others  (when applicable)?

OK

>> +#define L0_TM_ILL11                  0x198c
>> +#define L0_TM_ILL12                  0x1990
>> +#define L0_TM_E_ILL1                 0x1924
>> +#define L0_TM_E_ILL2                 0x1928
>> +#define L0_TM_IQ_ILL3                        0x1900
>> +#define L0_TM_E_ILL3                 0x192c
>> +#define L0_TM_IQ_ILL7                        0x1910
>> +#define L0_TM_E_ILL7                 0x193c
>> +#define L0_TM_ILL8                   0x1980
>> +#define L0_TM_IQ_ILL8                        0x1914
>> +#define L0_TM_IQ_ILL9                        0x1918
>> +#define L0_TM_EQ0                    0x194c
>> +#define L0_TM_EQ0_EQ_STG2_CTRL_BYP   BIT(5)
>> +#define L0_TM_EQ1                    0x1950
>> +#define L0_TM_EQ1_EQ_STG2_RL_PROG    GENMASK(1, 0)
>> +#define L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL    BIT(2)
>> +#define L0_TM_E_ILL8                 0x1940
>> +#define L0_TM_E_ILL9                 0x1944
>> +#define L0_TM_ILL13                  0x1994
>> +#define L0_TM_CDR5                   0x1c14
>> +#define L0_TM_CDR5_FPHL_FSM_ACC_CYCLES       GENMASK(7, 5)
>> +#define L0_TM_CDR5_FFL_PH0_INT_GAIN  GENMASK(4, 0)
>> +#define L0_TM_CDR16                  0x1c40
>>
>>  /* PLL Test Mode register parameters */
>> +#define L0_TM_PLL_DIG_33             0x2084
>>  #define L0_TM_PLL_DIG_37             0x2094
>>  #define L0_TM_COARSE_CODE_LIMIT              0x10
>>
>>  /* PLL SSC step size offsets */
>> +#define L0_PLL_FBDIV_FRAC_3_MSB              0x2360
>>  #define L0_PLL_SS_STEPS_0_LSB                0x2368
>>  #define L0_PLL_SS_STEPS_1_MSB                0x236c
>>  #define L0_PLL_SS_STEP_SIZE_0_LSB    0x2370
>> @@ -69,6 +104,7 @@
>>  #define L0_PLL_STATUS_READ_1         0x23e4
>>
>>  /* SSC step size parameters */
>> +#define TM_FORCE_EN_FRAC             BIT(6)
>>  #define STEP_SIZE_0_MASK             0xff
>>  #define STEP_SIZE_1_MASK             0xff
>>  #define STEP_SIZE_2_MASK             0xff
>> @@ -76,6 +112,7 @@
>>  #define STEP_SIZE_SHIFT                      8
>>  #define FORCE_STEP_SIZE                      0x10
>>  #define FORCE_STEPS                  0x20
>> +#define TM_FORCE_EN                  BIT(7)
>>  #define STEPS_0_MASK                 0xff
>>  #define STEPS_1_MASK                 0x07
>>
>> @@ -84,6 +121,32 @@
>>  #define L0_REF_CLK_LCL_SEL           BIT(7)
>>  #define L0_REF_CLK_SEL_MASK          0x9f
>>
>> +/* Built-in self-test parameters */
>> +#define L0_BIST_CTRL_1                       0x3004
>> +#define L0_BIST_CTRL_2                       0x3008
>> +#define L0_BIST_RUN_LEN_L            0x300c
>> +#define L0_BIST_ERR_INJ_POINT_L              0x3010
>> +#define L0_BIST_RUNLEN_ERR_INJ_H     0x3014
>> +#define L0_BIST_IDLE_TIME            0x3018
>> +#define L0_BIST_MARKER_L             0x301c
>> +#define L0_BIST_IDLE_CHAR_L          0x3020
>> +#define L0_BIST_MARKER_IDLE_H                0x3024
>> +#define L0_BIST_LOW_PULSE_TIME               0x3028
>> +#define L0_BIST_TOTAL_PULSE_TIME     0x302c
>> +#define L0_BIST_TEST_PAT_1           0x3030
>> +#define L0_BIST_TEST_PAT_2           0x3034
>> +#define L0_BIST_TEST_PAT_3           0x3038
>> +#define L0_BIST_TEST_PAT_4           0x303c
>> +#define L0_BIST_TEST_PAT_MSBS                0x3040
>> +#define L0_BIST_PKT_NUM                      0x3044
>> +#define L0_BIST_FRM_IDLE_TIME                0x3048
>> +#define L0_BIST_PKT_CTR_L            0x304c
>> +#define L0_BIST_PKT_CTR_H            0x3050
>> +#define L0_BIST_ERR_CTR_L            0x3054
>> +#define L0_BIST_ERR_CTR_H            0x3058
>> +#define L0_BIST_FILLER_OUT           0x3068
>> +#define L0_BIST_FORCE_MK_RST         0x306c
>> +
>>  /* Calibration digital logic parameters */
>>  #define L3_TM_CALIB_DIG19            0xec4c
>>  #define L3_CALIB_DONE_STATUS         0xef14
>> @@ -139,6 +202,9 @@ static const char *const xpsgtr_icm_str[] = {
>>  #define TM_CMN_RST_SET                       0x2
>>  #define TM_CMN_RST_MASK                      0x3
>>
>> +#define LPBK_CTRL0                   0x10038
>> +#define LPBK_CTRL1                   0x1003c
>> +
>>  /* Bus width parameters */
>>  #define TX_PROT_BUS_WIDTH            0x10040
>>  #define RX_PROT_BUS_WIDTH            0x10044
>> @@ -148,9 +214,13 @@ static const char *const xpsgtr_icm_str[] = {
>>  #define PROT_BUS_WIDTH_SHIFT(n)              ((n) * 2)
>>  #define PROT_BUS_WIDTH_MASK(n)               GENMASK((n) * 2 + 1, (n) * 2)
>>
>> +#define UPHY_SPARE0                  0X10098
>> +
>>  /* Number of GT lanes */
>>  #define NUM_LANES                    4
>>
>> +#define SIOU_ECO_0                   0x1c
>> +
>>  /* SIOU SATA control register */
>>  #define SATA_CONTROL_OFFSET          0x0100
>>
>> @@ -338,6 +408,33 @@ static void xpsgtr_restore_lane_regs(struct xpsgtr_dev
>> *gtr_dev)
>>                            gtr_dev->saved_regs[i]);
>>  }
>>
>> +static inline void xpsgtr_write_lanes(struct xpsgtr_dev *gtr_dev,
>> +                                  unsigned long *lanes, u32 reg, u32 value)
>> +{
>> +     unsigned long lane;
>> +
>> +     for_each_set_bit(lane, lanes, NUM_LANES) {
>> +             void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
>> +                                  + reg;
>> +
>> +             writel(value, addr);
>> +     }
>> +}
>> +
>> +static inline void xpsgtr_clr_set_lanes(struct xpsgtr_dev *gtr_dev,
>> +                                    unsigned long *lanes, u32 reg, u32 clr,
>> +                                    u32 set)
>> +{
>> +     unsigned long lane;
>> +
>> +     for_each_set_bit(lane, lanes, NUM_LANES) {
>> +             void __iomem *addr = gtr_dev->serdes + lane * PHY_REG_OFFSET
>> +                                  + reg;
>> +
>> +             writel((readl(addr) & ~clr) | set, addr);
>> +     }
>> +}
>> +
>>  /*
>>   * Hardware Configuration
>>   */
>> @@ -351,7 +448,7 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)
>>       u8 protocol = gtr_phy->protocol;
>>       int ret;
>>
>> -     dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n");
>> +     dev_vdbg(gtr_dev->dev, "Waiting for PLL lock\n");
> 
> Unrelated change.

We reconfigure the PLL many times during ILL training, and this debug
output makes it difficult to read the ILL calibration debug.

>>
>>       /*
>>        * For DP and PCIe, only the instance 0 PLL is used. Switch to that phy
>> @@ -520,6 +617,231 @@ static void xpsgtr_bypass_scrambler_8b10b(struct
>> xpsgtr_phy *gtr_phy)
>>       xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61,
>> L0_TM_DISABLE_SCRAMBLE_ENCODER);
>>  }
>>
>> +/* Enable or disable loopback */
>> +static void xpsgtr_phy_set_loopback(struct xpsgtr_phy *gtr_phy, bool enabled)
>> +{
>> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
>> +     u32 reg = gtr_phy->lane >= 2 ? LPBK_CTRL1 : LPBK_CTRL0;
>> +     u32 shift = gtr_phy->lane & 1 ? 4 : 0;
>> +
>> +     xpsgtr_clr_set(gtr_dev, reg, 7 << shift, (u32)enabled << shift);
>> +}
>> +
>> +static void xpsgtr_phy_set_ill(struct xpsgtr_phy *gtr_phy, u32 ill, bool gen2)
>> +{
>> +     u32 val = 4 + ill * 8;
>> +
>> +     if (gen2) {
>> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL2, val & 0xff);
>> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0x0f,
>> +                                1 << (val >> 8));
>> +     } else {
>> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL1, val & 0xff);
>> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL12, 0xf0,
>> +                                (val >> 4) & 0x10);
>> +     }
>> +}
>> +
>> +static bool xpsgtr_ill_calibrated(struct xpsgtr_phy *gtr_phy)
>> +{
>> +     u32 ill1 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL1);
>> +     u32 ill2 = xpsgtr_read_phy(gtr_phy, L0_TM_E_ILL2);
>> +     u32 ill12 = xpsgtr_read_phy(gtr_phy, L0_TM_ILL12);
>> +
>> +     dev_dbg(gtr_phy->dev->dev, "lane %u gen1 ILL was %u gen2 ILL was
>> %u\n",
>> +             gtr_phy->lane, ill1 / 8 + (ill12 & 0x10 ? 32 : 0),
>> +             ill2 / 8 + (ill12 & 0x02 ? 32 : 0));
>> +     return ill1 || ill2 || ill12;
>> +}
>> +
>> +static void xpsgtr_init_ill(struct xpsgtr_phy *gtr_phy)
>> +{
>> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
>> +     struct clk *clk = gtr_dev->clk[gtr_phy->refclk];
>> +     u32 ill123 = DIV_ROUND_CLOSEST(clk_get_rate(clk), 1000000);
>> +
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_MISC2, 0,
>> L0_TM_MISC2_ILL_CAL_BYPASS);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL1, ill123);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL2, ill123);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL3, ill123);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL7, 0xf3);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL7, 0xf3);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_ILL8, 0xff);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL9, 1);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL9, 1);
>> +     xpsgtr_clr_set(gtr_dev, UPHY_SPARE0, BIT(5), 0);
>> +}
>> +
>> +static void xpsgtr_phy_illcalib(struct xpsgtr_dev *gtr_dev,
>> +                                  unsigned long *lanes, bool gen2)
>> +{
>> +     bool last_ok[NUM_LANES] = { 0 };
>> +     int pass[NUM_LANES] = { 0 }, altpass[NUM_LANES] = { 0 };
>> +     int best[NUM_LANES] = { 0 }, altbest[NUM_LANES] = { 0 };
>> +     unsigned long lane;
>> +     int i;
>> +
>> +     /* Initialize the BIST */
>> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 1);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0x20);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0xf4);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0xfb);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0xff);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0x4a);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0x4a);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0x4a);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0x4a);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0x14);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 2);
>> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0xe0, 0);
>> +
>> +     for (i = 0; i < 64; i++) {
>> +             bool ok[NUM_LANES];
>> +
>> +             for_each_set_bit(lane, lanes, NUM_LANES)
>> +                     xpsgtr_phy_set_ill(&gtr_dev->phys[lane], i, gen2);
>> +
>> +             /* Reset lanes */
>> +             xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x20,
>> +                                  0x10);
>> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x40);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
>> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x04);
>> +             udelay(50);
>> +             if (gen2)
>> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0e);
>> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x06);
>> +             if (gen2) {
>> +                     xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x04);
>> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x07);
>> +                     udelay(400);
>> +                     xpsgtr_write_lanes(gtr_dev, lanes, L0_TX_ANA_TM_3, 0x0c);
>> +                     udelay(15);
>> +                     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x0f);
>> +                     udelay(100);
>> +             }
>> +
>> +             if (xpsgtr_wait_pll_lock(gtr_dev->phys[0].phy)) {
>> +                     memset(last_ok, 0, sizeof(last_ok));
>> +                     continue;
>> +             }
>> +
>> +             udelay(50);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0xc0);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0x80);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0xc0);
>> +             udelay(50);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0x80);
>> +             udelay(50);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_4, 0);
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_PLL_DIG_33, 0);
>> +             udelay(500);
>> +
>> +             /* Do the BIST */
>> +             for_each_set_bit(lane, lanes, NUM_LANES) {
>> +                     struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
>> +                     u32 packets, errors;
>> +
>> +                     xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_10);
>> +                     xpsgtr_phy_set_loopback(gtr_phy, true);
>> +                     xpsgtr_write_phy(gtr_phy, L0_TM_DIG_22, 0x20);
>> +                     xpsgtr_clr_set_phy(gtr_phy, L0_BIST_CTRL_1, 0, 1);
>> +
>> +                     udelay(200);
>> +                     xpsgtr_write_phy(gtr_phy, L0_BIST_CTRL_1, 0);
>> +                     packets = xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_L);
>> +                     packets |= xpsgtr_read_phy(gtr_phy, L0_BIST_PKT_CTR_H)
>> << 8;
>> +                     errors = xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_L);
>> +                     errors |= xpsgtr_read_phy(gtr_phy, L0_BIST_ERR_CTR_H)
>> << 8;
>> +                     ok[lane] = packets && !errors;
>> +
>> +                     dev_dbg(gtr_dev->dev,
>> +                             "lane %lu ILL %d packets %10u errors %10u\n",
>> +                             lane, i, packets, errors);
>> +             }
>> +
>> +             xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
>> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x00);
>> +             xpsgtr_write(gtr_dev, UPHY_SPARE0, 0x02);
>> +
>> +             for_each_set_bit(lane, lanes, NUM_LANES) {
>> +                     pass[lane] += ok[lane] && last_ok[lane];
>> +                     if (pass[lane] < 4) {
>> +                             if (!ok[lane] && i > 2) {
>> +                                     if (altpass[lane] < pass[lane]) {
>> +                                             altpass[lane] = pass[lane];
>> +                                             altbest[lane] =
>> +                                                     (i - 1) - (pass[lane] + 1) / 2;
>> +                                     }
>> +                                     pass[lane] = 0;
>> +                             }
>> +                     } else if (!best[lane] && (!ok[lane] || i == 63) &&
>> +                                last_ok[lane]) {
>> +                             best[lane] = (i - 1) - (pass[lane] + 1) / 2;
>> +                     }
>> +             }
>> +
>> +             memcpy(last_ok, ok, sizeof(ok));
>> +     }
>> +
>> +     for_each_set_bit(lane, lanes, NUM_LANES) {
>> +             dev_dbg(gtr_dev->dev, "lane %lu ILL best %d alt best %d\n",
>> +                     lane, best[lane], altbest[lane]);
>> +
>> +             xpsgtr_phy_set_ill(&gtr_dev->phys[lane],
>> +                                best[lane] ?: altbest[lane], gen2);
>> +     }
>> +
>> +     /* Clean up */
>> +     xpsgtr_clr_set_lanes(gtr_dev, lanes, L0_TM_ANA_BYP_7, 0x30, 0);
>> +     xpsgtr_write(gtr_dev, UPHY_SPARE0, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_1, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_CTRL_2, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUN_LEN_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_INJ_POINT_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_RUNLEN_ERR_INJ_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_IDLE_CHAR_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_MARKER_IDLE_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_LOW_PULSE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TOTAL_PULSE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_1, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_2, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_3, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_4, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_TEST_PAT_MSBS, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_NUM, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FRM_IDLE_TIME, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_PKT_CTR_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_L, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_ERR_CTR_H, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FILLER_OUT, 1);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_BIST_FORCE_MK_RST, 0);
>> +     xpsgtr_write_lanes(gtr_dev, lanes, L0_TM_DIG_22, 0);
>> +
>> +     for_each_set_bit(lane, lanes, NUM_LANES) {
>> +             struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
>> +
>> +             xpsgtr_phy_init_bus_width(gtr_phy, PROT_BUS_WIDTH_20);
>> +             xpsgtr_phy_set_loopback(gtr_phy, false);
>> +     }
>> +}
>> +
>>  static int xpsgtr_common_init(struct xpsgtr_phy *gtr_phy)
>>  {
>>       int ret;
>> @@ -553,6 +875,37 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy
>> *gtr_phy)
>>  {
>>       struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
>>
>> +     if (!xpsgtr_ill_calibrated(gtr_phy)) {
>> +             DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
>> +
>> +             xpsgtr_init_ill(gtr_phy);
>> +             xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL3, 100);
>> +             xpsgtr_clr_set_phy(gtr_phy, L0_TM_ILL11, 0xf0, 0x20);
>> +
>> +             __set_bit(gtr_phy->lane, lanes);
>> +             xpsgtr_phy_illcalib(gtr_dev, lanes, false);
>> +             xpsgtr_phy_set_ill(gtr_phy, 7, true);
>> +     }
>> +
>> +     /* Disable SSC */
>> +     xpsgtr_write_phy(gtr_phy, L0_PLL_FBDIV_FRAC_3_MSB,
>> TM_FORCE_EN_FRAC);
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, 0,
>> TM_FORCE_EN);
>> +
>> +     /* Disable Tx deemphasis */
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_CDR5,
>> +                      FIELD_PREP(L0_TM_CDR5_FPHL_FSM_ACC_CYCLES,
>> 7) |
>> +                      FIELD_PREP(L0_TM_CDR5_FFL_PH0_INT_GAIN, 6));
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_CDR16, 12);
>> +
>> +     /* Configure equalization */
>> +     xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118,
>> +                      L0_TX_ANA_TM_118_FORCE_17_0);
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ0, 0,
>> L0_TM_EQ0_EQ_STG2_CTRL_BYP);
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_EQ1,
>> L0_TM_EQ1_EQ_STG2_RL_PROG,
>> +                        FIELD_PREP(L0_TM_EQ1_EQ_STG2_RL_PROG, 2) |
>> +                        L0_TM_EQ1_EQ_STG2_PREAMP_MODE_VAL);
>> +     xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, 2); /* -3.5 dB deemphasis */
>> +
>>       xpsgtr_bypass_scrambler_8b10b(gtr_phy);
>>
>>       writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
>> @@ -565,6 +918,64 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy
>> *gtr_phy)
>>       xpsgtr_bypass_scrambler_8b10b(gtr_phy);
>>  }
>>
>> +/* PCIe-specific initialization. */
>> +static int xpsgtr_phy_init_pcie(struct xpsgtr_phy *gtr_phy)
>> +{
>> +     struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
>> +     DECLARE_BITMAP(lanes, NUM_LANES) = { 0 };
>> +     unsigned long lane;
>> +     bool calibrated = false;
>> +
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
>> +
>> +     for (lane = 0; lane < NUM_LANES; lane++) {
>> +             struct xpsgtr_phy *gtr_phy = &gtr_dev->phys[lane];
>> +
>> +             if (gtr_phy->protocol != ICM_PROTOCOL_PCIE)
>> +                     continue;
>> +
>> +             __set_bit(lane, lanes);
>> +             calibrated = calibrated || xpsgtr_ill_calibrated(gtr_phy);
>> +     }
>> +
>> +     if (calibrated)
>> +             return 0;
>> +
>> +     /* Write default ILL config */
>> +     for_each_set_bit(lane, lanes, NUM_LANES) {
>> +             struct xpsgtr_phy *p = &gtr_dev->phys[lane];
>> +
>> +             if (lane != gtr_phy->lane) {
>> +                     int ret = xpsgtr_common_init(p);
>> +
>> +                     if (ret)
>> +                             return ret;
>> +             }
>> +
>> +             xpsgtr_init_ill(p);
>> +             xpsgtr_write_phy(p, L0_TM_E_ILL3, 0);
>> +             xpsgtr_clr_set_phy(p, L0_TM_MISC2, 0,
>> +                                L0_TM_MISC2_ILL_CAL_BYPASS);
>> +     }
>> +
>> +     /* Perform the ILL calibration procedure */
>> +     xpsgtr_phy_illcalib(gtr_dev, lanes, false);
>> +     xpsgtr_phy_illcalib(gtr_dev, lanes, true);
>> +
>> +     /* Disable PCIe ECO */
>> +     writel(1, gtr_dev->siou + SIOU_ECO_0);
>> +     return 0;
>> +}
>> +
>> +/* USB-specific initialization. */
>> +static void xpsgtr_phy_init_usb(struct xpsgtr_phy *gtr_phy)
>> +{
>> +     xpsgtr_clr_set_phy(gtr_phy, L0_TM_AUX_0, 0, 0x20);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_IQ_ILL8, 0xf3);
>> +     xpsgtr_write_phy(gtr_phy, L0_TM_E_ILL8, 0xf3);
>> +     xpsgtr_phy_set_ill(gtr_phy, 7, false);
>> +}
>> +
>>  /* Configure TX de-emphasis and margining for DP. */
>>  static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre,
>>                                   unsigned int voltage)
>> @@ -710,6 +1121,10 @@ static int xpsgtr_phy_init(struct phy *phy)
>>               xpsgtr_phy_init_dp(gtr_phy);
>>               break;
>>
>> +     case ICM_PROTOCOL_PCIE:
>> +             ret = xpsgtr_phy_init_pcie(gtr_phy);
>> +             break;
>> +
>>       case ICM_PROTOCOL_SATA:
>>               xpsgtr_phy_init_sata(gtr_phy);
>>               break;
>> @@ -717,6 +1132,10 @@ static int xpsgtr_phy_init(struct phy *phy)
>>       case ICM_PROTOCOL_SGMII:
>>               xpsgtr_phy_init_sgmii(gtr_phy);
>>               break;
>> +
>> +     case ICM_PROTOCOL_USB:
>> +             xpsgtr_phy_init_usb(gtr_phy);
>> +             break;
>>       }
>>
>>  out:
>> --
>> 2.35.1.1320.gc452695387.dirty
> 


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

* Re: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
  2026-02-05 15:47     ` Sean Anderson
@ 2026-02-18 16:36       ` Manivannan Sadhasivam
  0 siblings, 0 replies; 18+ messages in thread
From: Manivannan Sadhasivam @ 2026-02-18 16:36 UTC (permalink / raw)
  To: Sean Anderson
  Cc: Pandey, Radhey Shyam, Laurent Pinchart, Vinod Koul,
	linux-phy@lists.infradead.org, Krzysztof Wilczyński,
	Lorenzo Pieralisi, linux-kernel@vger.kernel.org, Simek, Michal,
	linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
	Neil Armstrong, Rob Herring, Havalige, Thippeswamy, Bjorn Helgaas,
	Conor Dooley, Krzysztof Kozlowski, devicetree@vger.kernel.org

On Thu, Feb 05, 2026 at 10:47:21AM -0500, Sean Anderson wrote:
> On 2/4/26 03:32, Pandey, Radhey Shyam wrote:
> > [AMD Official Use Only - AMD Internal Distribution Only]
> > 
> >> -----Original Message-----
> >> From: Sean Anderson <sean.anderson@linux.dev>
> >> Sent: Tuesday, February 3, 2026 5:51 AM
> >> To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Vinod Koul
> >> <vkoul@kernel.org>; linux-phy@lists.infradead.org
> >> Cc: Krzysztof Wilczyński <kwilczynski@kernel.org>; Lorenzo Pieralisi
> >> <lpieralisi@kernel.org>; Pandey, Radhey Shyam
> >> <radhey.shyam.pandey@amd.com>; linux-kernel@vger.kernel.org; Simek, Michal
> >> <michal.simek@amd.com>; linux-arm-kernel@lists.infradead.org; linux-
> >> pci@vger.kernel.org; Neil Armstrong <neil.armstrong@linaro.org>; Rob Herring
> >> <robh@kernel.org>; Havalige, Thippeswamy <thippeswamy.havalige@amd.com>;
> >> Manivannan Sadhasivam <mani@kernel.org>; Bjorn Helgaas
> >> <bhelgaas@google.com>; Sean Anderson <sean.anderson@linux.dev>; Conor
> >> Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>;
> >> devicetree@vger.kernel.org
> >> Subject: [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets
> >>
> >> Add resets so we can hold the bridge in reset while we perform phy calibration.
> > 
> > Seems like this should a required property?
> 
> It's optional as it does not exist in previous versions of the
> devicetree. In the past I have received pushback against making these
> sort of properties required.
> 
> If the resets don't exist we just don't assert them and assume the
> bootloader has deasserted them.
> 

If the resets are pretty much required for the hardware functionality, we can
mark them as required in the binding and accept the ABI breakage. This scenario
keeps coming with devicetree as the initial devicetree bindings lacked full
hardware description in most of the cases.

- Mani

> --Sean
> 
> > Rest looks fine to me.
> > 
> >>
> >> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> >> ---
> >>
> >>  .../devicetree/bindings/pci/xlnx,nwl-pcie.yaml  | 17 +++++++++++++++++
> >>  1 file changed, 17 insertions(+)
> >>
> >> diff --git a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> >> b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> >> index 9de3c09efb6e..7efb3dd9955f 100644
> >> --- a/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> >> +++ b/Documentation/devicetree/bindings/pci/xlnx,nwl-pcie.yaml
> >> @@ -69,6 +69,18 @@ properties:
> >>    power-domains:
> >>      maxItems: 1
> >>
> >> +  resets:
> >> +    maxItems: 3
> >> +
> >> +  reset-names:
> >> +    items:
> >> +      - description: APB register block reset
> >> +        const: cfg
> >> +      - description: AXI-PCIe bridge reset
> >> +        const: bridge
> >> +      - description: PCIe MAC reset
> >> +        const: ctrl
> >> +
> >>    iommus:
> >>      maxItems: 1
> >>
> >> @@ -117,6 +129,7 @@ examples:
> >>      #include <dt-bindings/interrupt-controller/irq.h>
> >>      #include <dt-bindings/phy/phy.h>
> >>      #include <dt-bindings/power/xlnx-zynqmp-power.h>
> >> +    #include <dt-bindings/reset/xlnx-zynqmp-resets.h>
> >>      soc {
> >>          #address-cells = <2>;
> >>          #size-cells = <2>;
> >> @@ -146,6 +159,10 @@ examples:
> >>              msi-parent = <&nwl_pcie>;
> >>              phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
> >>              power-domains = <&zynqmp_firmware PD_PCIE>;
> >> +            resets = <&zynqmp_reset ZYNQMP_RESET_PCIE_CFG>,
> >> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_BRIDGE>,
> >> +                     <&zynqmp_reset ZYNQMP_RESET_PCIE_CTRL>;
> >> +            reset-names = "cfg", "bridge", "ctrl";
> >>              iommus = <&smmu 0x4d0>;
> >>              pcie_intc: legacy-interrupt-controller {
> >>                  interrupt-controller;
> >> --
> >> 2.35.1.1320.gc452695387.dirty
> > 

-- 
மணிவண்ணன் சதாசிவம்


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

* Re: [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe
  2026-02-03  0:21 ` [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe Sean Anderson
@ 2026-02-18 16:42   ` Manivannan Sadhasivam
  0 siblings, 0 replies; 18+ messages in thread
From: Manivannan Sadhasivam @ 2026-02-18 16:42 UTC (permalink / raw)
  To: Sean Anderson, Radhey Shyam Pandey, Michal Simek,
	Thippeswamy Havalige
  Cc: Laurent Pinchart, Vinod Koul, linux-phy,
	Krzysztof Wilczyński, Lorenzo Pieralisi, linux-kernel,
	linux-arm-kernel, linux-pci, Neil Armstrong, Rob Herring,
	Bjorn Helgaas

On Mon, Feb 02, 2026 at 07:21:27PM -0500, Sean Anderson wrote:
> The PCIe core must be held in reset when initializing phys.
> Assert/deassert the appropriate resets.
> 
> Resetting the core also resets the PCIe attributes to their default
> values, so initialize those too. For the most part the defaults are
> fine, but there are many attributes that default to an endpoint
> configuration and must be reprogrammed to function as a root device.
> We generally follow the controller programming sequence from UG1085.
> 
> Xilinx was extremely imaginative and named all the registers ATTR_1,
> ATTR_2 etc. (with the fields organized in alphabetical order) so we
> follow the same convention. Only the fields are named, but sometimes a
> field is split across multiple registers. All the BARs are unused but
> some are repurposed as bridge registers when used as a root port.
> 

Can someone from AMD/Xilinx review this patch?

- Mani

> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
> 
>  drivers/pci/controller/pcie-xilinx-nwl.c | 177 +++++++++++++++++++++++
>  1 file changed, 177 insertions(+)
> 
> diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
> index 7cfdc21e6f40..b78fbad1efa5 100644
> --- a/drivers/pci/controller/pcie-xilinx-nwl.c
> +++ b/drivers/pci/controller/pcie-xilinx-nwl.c
> @@ -22,6 +22,7 @@
>  #include <linux/pci-ecam.h>
>  #include <linux/phy/phy.h>
>  #include <linux/platform_device.h>
> +#include <linux/reset.h>
>  #include <linux/irqchip/chained_irq.h>
>  
>  #include "../pci.h"
> @@ -133,6 +134,54 @@
>  #define CFG_DMA_REG_BAR			GENMASK(2, 0)
>  #define CFG_PCIE_CACHE			GENMASK(7, 0)
>  
> +#define PCIE_ATTR2_AER_CAP_PERMIT_ROOTERR_UPDATE	BIT(0)
> +
> +#define PCIE_ATTR25_CPL_TIMEOUT_DISABLE_SUPPORTED	BIT(9)
> +#define PCIE_ATTR25_INTX_IMPLEMENTED			BIT(8)
> +#define PCIE_ATTR25_CLASS_CODE				GENMASK(7, 0)
> +
> +#define PCIE_ATTR27_DEV_CAP_ENDPOINT_L1_LATENCY		GENMASK(5, 3)
> +
> +#define PCIE_ATTR34_HEADER_TYPE				GENMASK(7, 0)
> +
> +#define PCIE_ATTR35_LINK_CAP_DLL_ACTIVE_REPORTING	BIT(15)
> +
> +#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED		GENMASK(13, 10)
> +#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED_2_5		1
> +#define PCIE_ATTR37_LINK_CAP_MAX_LINK_SPEED_5_0		2
> +#define PCIE_ATTR37_LINK_CAP_BANDWIDTH_NOTIFICATION	BIT(9)
> +
> +#define PCIE_ATTR50_CAP_DEVICE_PORT_TYPE		GENMASK(7, 4)
> +#define PCIE_ATTR50_CAP_NEXTPTR				GENMASK(15, 8)
> +
> +#define PCIE_ATTR53_CAP_NEXTPTR				GENMASK(7, 0)
> +
> +#define PCIE_ATTR93_LL_REPLAY_TIMEOUT_EN		BIT(15)
> +
> +#define PCIE_ATTR97_LTSSM_MAX_LINK_WIDTH		GENMASK(11, 6)
> +#define PCIE_ATTR97_LINK_CAP_MAX_LINK_WIDTH		GENMASK(5, 0)
> +
> +#define PCIE_ATTR100_UPSTREAM_FACING			BIT(6)
> +
> +#define PCIE_ATTR101_EN_MSG_ROUTE			GENMASK(15, 5)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_PME_TURN_OFF		BIT(15)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_UNLOCK		BIT(14)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_PME_TO_ACK		BIT(13)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_PM_PME		BIT(12)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_INTD			BIT(11)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_INTC			BIT(10)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_INTB			BIT(9)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_INTA			BIT(8)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_FATAL		BIT(7)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_NONFATAL		BIT(6)
> +#define PCIE_ATTR101_EN_MSG_ROUTE_ERR_COR		BIT(5)
> +#define PCIE_ATTR101_DISABLE_BAR_FILTERING		BIT(1)
> +
> +#define PCIE_ATTR106_VC0_TOTAL_CREDITS_NPH		GENMASK(13, 7)
> +#define PCIE_ATTR106_VC0_TOTAL_CREDITS_CH		GENMASK(6, 0)
> +
> +#define PCIE_ATTR109_VC0_TOTAL_CREDITS_PH		GENMASK(6, 0)
> +
>  #define INT_PCI_MSI_NR			(2 * 32)
>  
>  /* Readin the PS_LINKUP */
> @@ -159,6 +208,7 @@ struct nwl_pcie {
>  	void __iomem *pcireg_base;
>  	void __iomem *ecam_base;
>  	struct phy *phy[4];
> +	struct reset_control *ctrl_reset;
>  	phys_addr_t phys_breg_base;	/* Physical Bridge Register Base */
>  	phys_addr_t phys_pcie_reg_base;	/* Physical PCIe Controller Base */
>  	phys_addr_t phys_ecam_base;	/* Physical Configuration Base */
> @@ -173,6 +223,115 @@ struct nwl_pcie {
>  	raw_spinlock_t leg_mask_lock;
>  };
>  
> +static void nwl_pcie_write_attr(struct nwl_pcie *pcie, u32 attr, u16 val)
> +{
> +	writel(val, pcie->pcireg_base + attr * 4);
> +}
> +
> +static void nwl_pcie_modify_attr(struct nwl_pcie *pcie, u32 attr, u16 clear,
> +				u16 set)
> +{
> +	u32 val = readl(pcie->pcireg_base + attr * 4);
> +
> +	nwl_pcie_write_attr(pcie, attr, (val & ~clear) | set);
> +}
> +
> +static void nwl_pcie_attr_init(struct nwl_pcie *pcie)
> +{
> +	unsigned int width;
> +
> +	for (width = ARRAY_SIZE(pcie->phy); width; width--)
> +		if (pcie->phy[width - 1])
> +			break;
> +
> +	/* Set TLP header to type-1 */
> +	nwl_pcie_modify_attr(pcie, 34, PCIE_ATTR34_HEADER_TYPE, PCI_HEADER_TYPE_BRIDGE);
> +	nwl_pcie_modify_attr(pcie, 100, PCIE_ATTR100_UPSTREAM_FACING, 0);
> +
> +	/* Disable BAR0/1 */
> +	nwl_pcie_write_attr(pcie, 7, 0);
> +	nwl_pcie_write_attr(pcie, 8, 0);
> +	nwl_pcie_write_attr(pcie, 9, 0);
> +	nwl_pcie_write_attr(pcie, 10, 0);
> +	/* Enable primary/secondary/subordinate busses, disable latency timer */
> +	nwl_pcie_write_attr(pcie, 11, 0xffff);
> +	nwl_pcie_write_attr(pcie, 12, 0x00ff);
> +	nwl_pcie_write_attr(pcie, 13, 0x0000); /* Disable I/O window */
> +	nwl_pcie_write_attr(pcie, 14, 0x0000); /* Enable secondary status */
> +	/* Enable memory window */
> +	nwl_pcie_write_attr(pcie, 15, (u16)PCI_MEMORY_RANGE_MASK);
> +	nwl_pcie_write_attr(pcie, 16, (u16)PCI_MEMORY_RANGE_MASK);
> +	/* Enable 64-bit prefetchable window */
> +	nwl_pcie_write_attr(pcie, 17,
> +			    (u16)PCI_PREF_RANGE_MASK | PCI_PREF_RANGE_TYPE_64);
> +	nwl_pcie_write_attr(pcie, 18,
> +			    (u16)PCI_PREF_RANGE_MASK | PCI_PREF_RANGE_TYPE_64);
> +	nwl_pcie_modify_attr(pcie, 101, 0, PCIE_ATTR101_DISABLE_BAR_FILTERING);
> +
> +	/* Set class code to PCI_CLASS_BRIDGE_PCI_NORMAL */
> +	nwl_pcie_write_attr(pcie, 24, PCI_CLASS_BRIDGE_PCI_NORMAL & 0xffff);
> +	nwl_pcie_modify_attr(pcie, 25, PCIE_ATTR25_CLASS_CODE,
> +			     PCIE_ATTR25_CPL_TIMEOUT_DISABLE_SUPPORTED |
> +			     PCI_BASE_CLASS_BRIDGE);
> +
> +	/* Enable PCIe capability */
> +	nwl_pcie_modify_attr(pcie, 53, PCIE_ATTR53_CAP_NEXTPTR, 0x60);
> +	nwl_pcie_modify_attr(pcie, 50,
> +			     PCIE_ATTR50_CAP_NEXTPTR |
> +			     PCIE_ATTR50_CAP_DEVICE_PORT_TYPE,
> +			     FIELD_PREP(PCIE_ATTR50_CAP_DEVICE_PORT_TYPE,
> +					PCI_EXP_TYPE_ROOT_PORT));
> +
> +	/* Disable MSI(-X) capability */
> +	nwl_pcie_write_attr(pcie, 41, 0);
> +	nwl_pcie_write_attr(pcie, 43, 0);
> +	nwl_pcie_write_attr(pcie, 44, 0);
> +	nwl_pcie_write_attr(pcie, 45, 0);
> +	nwl_pcie_write_attr(pcie, 46, 0);
> +	nwl_pcie_write_attr(pcie, 48, 0);
> +
> +	/* Disable DSN capability */
> +	nwl_pcie_write_attr(pcie, 31, 0);
> +	nwl_pcie_write_attr(pcie, 82, PCI_CFG_SPACE_SIZE);
> +
> +	/* Enable AER */
> +	nwl_pcie_modify_attr(pcie, 2, 0,
> +			     PCIE_ATTR2_AER_CAP_PERMIT_ROOTERR_UPDATE);
> +
> +	/* Disable L1 latency for root port */
> +	nwl_pcie_modify_attr(pcie, 27,
> +			     PCIE_ATTR27_DEV_CAP_ENDPOINT_L1_LATENCY, 0);
> +
> +	/* Enable bandwidth notification */
> +	nwl_pcie_modify_attr(pcie, 37, 0,
> +			     PCIE_ATTR37_LINK_CAP_BANDWIDTH_NOTIFICATION);
> +
> +	/* Set max link width */
> +	nwl_pcie_write_attr(pcie, 97,
> +			    FIELD_PREP(PCIE_ATTR97_LTSSM_MAX_LINK_WIDTH, width) |
> +			    FIELD_PREP(PCIE_ATTR97_LINK_CAP_MAX_LINK_WIDTH, width));
> +
> +	/* Route misc. TLPs to controller */
> +	nwl_pcie_modify_attr(pcie, 101, PCIE_ATTR101_EN_MSG_ROUTE,
> +			     PCIE_ATTR101_EN_MSG_ROUTE_INTA |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_INTB |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_INTC |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_INTD |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_PM_PME |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_PME_TO_ACK |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_UNLOCK |
> +			     PCIE_ATTR101_EN_MSG_ROUTE_PME_TURN_OFF);
> +
> +	/* Initialize completion credits */
> +	nwl_pcie_write_attr(pcie, 105, 205); /* CD */
> +	nwl_pcie_write_attr(pcie, 106,
> +			    FIELD_PREP(PCIE_ATTR106_VC0_TOTAL_CREDITS_NPH, 12) |
> +			    FIELD_PREP(PCIE_ATTR106_VC0_TOTAL_CREDITS_CH, 36));
> +	nwl_pcie_write_attr(pcie, 107, 24); /* NPD */
> +	nwl_pcie_write_attr(pcie, 108, 181); /* PD */
> +	nwl_pcie_modify_attr(pcie, 109, PCIE_ATTR109_VC0_TOTAL_CREDITS_PH, 32);
> +}
> +
>  static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off)
>  {
>  	return readl(pcie->breg_base + off);
> @@ -806,6 +965,9 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
>  	irq_set_chained_handler_and_data(pcie->irq_intx,
>  					 nwl_pcie_leg_handler, pcie);
>  
> +	pcie->ctrl_reset = devm_reset_control_get_optional(dev, "ctrl");
> +	if (IS_ERR(pcie->ctrl_reset))
> +		return PTR_ERR(pcie->ctrl_reset);
>  
>  	for (i = 0; i < ARRAY_SIZE(pcie->phy); i++) {
>  		pcie->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
> @@ -852,6 +1014,12 @@ static int nwl_pcie_probe(struct platform_device *pdev)
>  	if (IS_ERR(pcie->clk))
>  		return PTR_ERR(pcie->clk);
>  
> +	err = reset_control_assert(pcie->ctrl_reset);
> +	if (err) {
> +		dev_err(dev, "could not enter reset\n");
> +		return err;
> +	}
> +
>  	err = clk_prepare_enable(pcie->clk);
>  	if (err) {
>  		dev_err(dev, "can't enable PCIe ref clock\n");
> @@ -864,6 +1032,15 @@ static int nwl_pcie_probe(struct platform_device *pdev)
>  		goto err_clk;
>  	}
>  
> +	if (pcie->ctrl_reset)
> +		nwl_pcie_attr_init(pcie);
> +
> +	err = reset_control_deassert(pcie->ctrl_reset);
> +	if (err) {
> +		dev_err(dev, "could not release from reset\n");
> +		goto err_phy_init;
> +	}
> +
>  	err = nwl_pcie_phy_power_on(pcie);
>  	if (err) {
>  		dev_err(dev, "could not power on PHYs\n");
> -- 
> 2.35.1.1320.gc452695387.dirty
> 

-- 
மணிவண்ணன் சதாசிவம்


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

end of thread, other threads:[~2026-02-18 16:43 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-03  0:21 [PATCH 0/8] phy: zynqmp: Perform complete initialization, including ILL calibration Sean Anderson
2026-02-03  0:21 ` [PATCH 1/8] dt-bindings: pci: xilinx-nwl: Add resets Sean Anderson
2026-02-04  8:32   ` Pandey, Radhey Shyam
2026-02-05 15:47     ` Sean Anderson
2026-02-18 16:36       ` Manivannan Sadhasivam
2026-02-10  0:24   ` Rob Herring (Arm)
2026-02-03  0:21 ` [PATCH 2/8] phy: zynqmp: Refactor bus width configuration into helper Sean Anderson
2026-02-10 15:00   ` Pandey, Radhey Shyam
2026-02-03  0:21 ` [PATCH 3/8] phy: zynqmp: Refactor common phy initialization into a helper Sean Anderson
2026-02-10 15:05   ` Pandey, Radhey Shyam
2026-02-03  0:21 ` [PATCH 4/8] phy: zynqmp: Calibrate ILL if necessary Sean Anderson
2026-02-10 16:04   ` Pandey, Radhey Shyam
2026-02-10 16:42     ` Sean Anderson
2026-02-03  0:21 ` [PATCH 5/8] phy: zynqmp: Initialize chicken bits Sean Anderson
2026-02-03  0:21 ` [PATCH 6/8] PCI: xilinx-nwl: Split phy_init from phy_power_on Sean Anderson
2026-02-03  0:21 ` [PATCH 7/8] PCI: xilinx-nwl: Reset the core during probe Sean Anderson
2026-02-18 16:42   ` Manivannan Sadhasivam
2026-02-03  0:21 ` [PATCH 8/8] arm64: zynqmp: Add PCIe resets Sean Anderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox