qualcommbe: ipq9574: align USXGMII bring-up with SSDK
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>
This commit is contained in:
committed by
Robert Marko
parent
d9a98ed59d
commit
c16ef4f318
+218
@@ -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;
|
||||
Reference in New Issue
Block a user