]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommbe: ipq9574: align USXGMII bring-up with SSDK 24033/head
authorKenneth Kasilag <kenneth@kasilag.me>
Thu, 2 Jul 2026 11:32:51 +0000 (11:32 +0000)
committerRobert Marko <robimarko@gmail.com>
Mon, 6 Jul 2026 09:53:43 +0000 (11:53 +0200)
Align the USXGMII configuration sequence with the vendor SSDK:

- Reset the PCS PLL by programming the whole reset register with the
  documented assert and release values and 100ms holds, instead of
  toggling a single bit for 1ms and relying on the power-on state of
  the remaining analog enables.

- Keep the uniphy port clocks disabled across the reconfiguration and
  re-enable them after calibration, before releasing the XPCS reset.

- Select in-band link detection explicitly rather than the SFP
  loss-of-signal input, which does not exist on non-SFP ports.

- Wait for the 10GBASE-R receiver to link before enabling the USXGMII
  adaptation layer. Best-effort, since the PHY SerDes may only come
  up later.

Other interface modes keep the previous behaviour.

Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
Link: https://github.com/openwrt/openwrt/pull/24033
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/qualcommbe/patches-6.18/0361-net-pcs-qcom-ipq9574-align-USXGMII-bring-up-with-SSDK.patch [new file with mode: 0644]

diff --git a/target/linux/qualcommbe/patches-6.18/0361-net-pcs-qcom-ipq9574-align-USXGMII-bring-up-with-SSDK.patch b/target/linux/qualcommbe/patches-6.18/0361-net-pcs-qcom-ipq9574-align-USXGMII-bring-up-with-SSDK.patch
new file mode 100644 (file)
index 0000000..30cfeb0
--- /dev/null
@@ -0,0 +1,218 @@
+From: Kenneth Kasilag <kenneth@kasilag.me>
+Date: Thu, 02 Jul 2026 00:00:00 +0000
+Subject: [PATCH] net: pcs: qcom-ipq9574: align USXGMII bring-up with vendor SSDK
+
+Align the USXGMII configuration sequence with the SSDK:
+
+- Reset the PCS PLL by programming the whole reset register with the
+  documented assert and release values and 100ms holds, instead of
+  toggling a single bit for 1ms and relying on the power-on state of
+  the remaining analog enables.
+
+- Keep the uniphy port clocks disabled across the reconfiguration and
+  re-enable them after calibration, before releasing the XPCS reset.
+  Restore them on failure as well, so the enable count stays balanced
+  against the pcs_enable/pcs_disable callbacks.
+
+- Select in-band link detection explicitly rather than the SFP
+  loss-of-signal input, which does not exist on non-SFP ports.
+
+- Wait for the 10GBASE-R receiver to link before enabling the USXGMII
+  adaptation layer. Best-effort, since the PHY SerDes may only come
+  up later.
+
+Other interface modes keep the previous behaviour.
+
+Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 104 ++++++++++++++++++++++++-----
+ 1 file changed, 91 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -71,6 +71,12 @@
+ #define PCS_PLL_RESET                 0x780
+ #define PCS_ANA_SW_RESET              BIT(6)
++#define PCS_PLL_RESET_ASSERT          0x2bf
++#define PCS_PLL_RESET_RELEASE         0x2ff
++
++/* LOS-from-SFP select; 0x7 on SFP ports, 0 otherwise */
++#define PCS_LINK_DETECT                       0x570
++#define PCS_DETECT_LOS_FROM_SFP               GENMASK(8, 6)
+ #define XPCS_INDIRECT_ADDR            0x8000
+ #define XPCS_INDIRECT_AHB_ADDR                0x83fc
+@@ -299,6 +305,22 @@ static void ipq_pcs_get_state_10gbaser(s
+       state->pause |= MLO_PAUSE_TXRX_MASK;
+ }
++/* Re-enable the port clocks held off across a USXGMII reconfiguration */
++static int ipq_pcs_restore_port_clks(struct ipq_pcs *qpcs,
++                                   phy_interface_t interface)
++{
++      int ret;
++
++      if (interface != PHY_INTERFACE_MODE_USXGMII || !qpcs->qpcs_mii[0])
++              return 0;
++
++      ret = clk_prepare_enable(qpcs->qpcs_mii[0]->rx_clk);
++      if (ret)
++              return ret;
++
++      return clk_prepare_enable(qpcs->qpcs_mii[0]->tx_clk);
++}
++
+ static int ipq_pcs_config_mode(struct ipq_pcs *qpcs,
+                              phy_interface_t interface)
+ {
+@@ -307,6 +329,12 @@ static int ipq_pcs_config_mode(struct ip
+       bool xpcs_mode = false;
+       int ret;
++      /* The port clocks must be held off across the reconfiguration */
++      if (interface == PHY_INTERFACE_MODE_USXGMII && qpcs->qpcs_mii[0]) {
++              clk_disable_unprepare(qpcs->qpcs_mii[0]->rx_clk);
++              clk_disable_unprepare(qpcs->qpcs_mii[0]->tx_clk);
++      }
++
+       /* Assert XPCS reset */
+       reset_control_assert(qpcs->xpcs_rstc);
+@@ -344,18 +372,19 @@ static int ipq_pcs_config_mode(struct ip
+               xpcs_mode = true;
+               break;
+       default:
+-              return -EOPNOTSUPP;
++              ret = -EOPNOTSUPP;
++              goto err_clk;
+       }
+       ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL, mask, val);
+       if (ret)
+-              return ret;
++              goto err_clk;
+       if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+               ret = regmap_set_bits(qpcs->regmap, PCS_QP_USXG_OPTION,
+                                     PCS_QP_USXG_GMII_SRC_XPCS);
+               if (ret)
+-                      return ret;
++                      goto err_clk;
+       }
+       if (interface == PHY_INTERFACE_MODE_USXGMII)
+@@ -366,7 +395,15 @@ static int ipq_pcs_config_mode(struct ip
+                                        PCS_MISC2_MODE_MASK |
+                                        PCS_MISC2_RATE_MASK, misc2);
+               if (ret)
+-                      return ret;
++                      goto err_clk;
++      }
++
++      /* Link detection is in-band; there is no SFP LOS input here */
++      if (interface == PHY_INTERFACE_MODE_USXGMII) {
++              ret = regmap_update_bits(qpcs->regmap, PCS_LINK_DETECT,
++                                       PCS_DETECT_LOS_FROM_SFP, 0);
++              if (ret)
++                      goto err_clk;
+       }
+       /* Pulse the resets as the vendor SSDK does: both held for
+@@ -382,14 +419,34 @@ static int ipq_pcs_config_mode(struct ip
+       }
+       /* PCS PLL reset */
+-      ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
+-      if (ret)
+-              return ret;
++      if (interface == PHY_INTERFACE_MODE_USXGMII) {
++              /* Program the whole register: the analog enables must not
++               * be left at their power-on state.
++               */
++              ret = regmap_write(qpcs->regmap, PCS_PLL_RESET,
++                                 PCS_PLL_RESET_ASSERT);
++              if (ret)
++                      goto err_clk;
+-      fsleep(20000);
+-      ret = regmap_set_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
+-      if (ret)
+-              return ret;
++              msleep(100);
++              ret = regmap_write(qpcs->regmap, PCS_PLL_RESET,
++                                 PCS_PLL_RESET_RELEASE);
++              if (ret)
++                      goto err_clk;
++
++              msleep(100);
++      } else {
++              ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET,
++                                      PCS_ANA_SW_RESET);
++              if (ret)
++                      goto err_clk;
++
++              fsleep(20000);
++              ret = regmap_set_bits(qpcs->regmap, PCS_PLL_RESET,
++                                    PCS_ANA_SW_RESET);
++              if (ret)
++                      goto err_clk;
++      }
+       /* Wait for calibration completion */
+       ret = regmap_read_poll_timeout(qpcs->regmap, PCS_CALIBRATION,
+@@ -397,9 +454,14 @@ static int ipq_pcs_config_mode(struct ip
+                                      1000, 100000);
+       if (ret) {
+               dev_err(qpcs->dev, "PCS calibration timed-out\n");
+-              return ret;
++              goto err_clk;
+       }
++      /* Re-enable the port clocks disabled at entry */
++      ret = ipq_pcs_restore_port_clks(qpcs, interface);
++      if (ret)
++              return ret;
++
+       qpcs->interface = interface;
+       /* The clock output register is only writable after PLL reset
+@@ -434,6 +496,12 @@ static int ipq_pcs_config_mode(struct ip
+               reset_control_deassert(qpcs->xpcs_rstc);
+       return 0;
++
++err_clk:
++      /* Best-effort restore of the clocks disabled at entry */
++      ipq_pcs_restore_port_clks(qpcs, interface);
++
++      return ret;
+ }
+ static int ipq_pcs_config_sgmii(struct ipq_pcs *qpcs,
+@@ -472,7 +540,7 @@ static int ipq_pcs_config_usxgmii(struct
+                                 int index,
+                                 phy_interface_t interface)
+ {
+-      unsigned int reg;
++      unsigned int reg, val;
+       int ret;
+       /* Configure the XPCS for USXGMII mode if required */
+@@ -481,6 +549,16 @@ static int ipq_pcs_config_usxgmii(struct
+               if (ret)
+                       return ret;
++              /* Best-effort wait for the receiver to link before enabling
++               * adaptation; the PHY SerDes may only come up later.
++               */
++              ret = regmap_read_poll_timeout(qpcs->regmap, XPCS_KR_STS,
++                                             val, val & XPCS_KR_LINK_STS,
++                                             1000, 100000);
++              if (ret)
++                      dev_warn(qpcs->dev,
++                               "10GBASE-R link not up before USXG_EN\n");
++
+               ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
+               if (ret)
+                       return ret;