From: Yongzhao Chen <yongzhao.derek@gmail.com>
To: Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>
Cc: netdev@vger.kernel.org, "David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
George Moussalem <george.moussalem@outlook.com>,
Ziyang Huang <hzyitc@outlook.com>,
linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH net v2 2/2] net: phy: qcom: at803x: Apply IPQ5018 analog settings at probe
Date: Tue, 29 Sep 2026 00:07:17 +0200 [thread overview]
Message-ID: <20260928220717.939-3-yongzhao.derek@gmail.com> (raw)
In-Reply-To: <20260928220717.939-1-yongzhao.derek@gmail.com>
ipq5018_probe() pulses the PHY reset, and the IPQ5018 internal GE PHY
then starts autonegotiation on its own with its reset-default analog
settings. The LDO, EEE timer, MSE threshold and optional short-cable
DAC values are only written by ipq5018_config_init(), which runs when
the MAC attaches the PHY, usually when the interface is opened.
On a Redmi AX5400 board, the IPQ5018 PHY is connected without a cable
to PHY4 of a QCA8337 switch, and "qcom,dac-preset-short-cable" is set.
Between probe and attach, about 39 s in these boots, both PHYs
resolved 1000BASE-T every 2.5 to 3 s, but the link did not come up.
After about five attempts, SmartSpeed downshifted on both sides at the
same time: the IPQ5018 PHY stopped advertising 1000BASE-T (CTRL1000
0x0200 -> 0x0000), and so did the QCA8337 PHY (0x0600 -> 0x0400). The
soft reset at attach restores the IPQ5018 advertisement, but nothing
restores the QCA8337 side, and the link stayed down, also after
taking the interface down and up again.
Apply the analog settings in probe right after the reset and restart
autonegotiation, so that negotiation runs with them from the start.
Factor the settings into a helper that is also used by
ipq5018_config_init(), and return MDIO errors from it instead of
ignoring them. Probe fails with the error; config_init() returns it.
genphy_restart_aneg() sets ANENABLE and ANRESTART and clears ISOLATE.
With the reset-default BMCR value of 0x1140 read on this board, this
is the same write that was tested (BMCR | BMCR_ANRESTART).
The same values are written on every board with this PHY; only the
time of the write changes. On boards without the DAC property, no
DAC register is written.
The same writes and the autonegotiation restart were tested in probe
on that board, in OpenWrt's Linux 6.18.52 kernel, over 3 warm boots
with and 3 without them. Without them, the link did not come up in
any boot, with both sides downshifted as described above. With them,
1000BASE-T came up in every boot less than 3 s after the QCA8337 PHY
was reset, before the interface was opened, and the QCA8337 PHY kept
advertising 1000BASE-T with SmartSpeed enabled. The DAC values written
in probe were still in place after the BMCR soft reset at attach.
This patch, backported to the same kernel without the downstream
SmartSpeed workaround, then kept the link at 1000BASE-T with SmartSpeed
enabled on that board over a first boot, a power cycle, three reboots,
three interface down/up cycles, three autonegotiation restarts, a
network restart and 10 minutes of operation.
Fixes: d46502279a11 ("net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support")
Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
Assisted-by: LLM
---
drivers/net/phy/qcom/at803x.c | 98 +++++++++++++++++++++++++----------
1 file changed, 70 insertions(+), 28 deletions(-)
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
index cacbadf1f48..09105f1e373 100644
--- a/drivers/net/phy/qcom/at803x.c
+++ b/drivers/net/phy/qcom/at803x.c
@@ -1019,10 +1019,10 @@ static int ipq5018_cable_test_start(struct phy_device *phydev)
return 0;
}
-static int ipq5018_config_init(struct phy_device *phydev)
+static int ipq5018_analog_init(struct phy_device *phydev)
{
struct ipq5018_priv *priv = phydev->priv;
- u16 val;
+ int val, ret;
/*
* set LDO efuse: first temporarily store ANA_DAC_FILTER value from
@@ -1030,39 +1030,66 @@ static int ipq5018_config_init(struct phy_device *phydev)
* is written to
*/
val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
- at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
- IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
- IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
- at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
+ if (val < 0)
+ return val;
+
+ ret = at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER,
+ val);
+ if (ret)
+ return ret;
/* set 8023AZ EEE TX and RX timer values */
- phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER,
- IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL);
- phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER,
- IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL);
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER,
+ IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER,
+ IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL);
+ if (ret)
+ return ret;
/* set MSE threshold values */
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
- IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
- IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
+ ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
+ IPQ5018_PHY_MMD1_MSE_THRESH1,
+ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
+ IPQ5018_PHY_MMD1_MSE_THRESH2,
+ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
+ if (ret)
+ return ret;
/* PHY DAC values are optional and only set in a PHY to PHY link architecture */
- if (priv->set_short_cable_dac) {
- /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
- phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
- IPQ5018_PHY_DAC_MASK,
- FIELD_PREP(IPQ5018_PHY_DAC_MASK,
- IPQ5018_PHY_MMD1_MDAC_VAL));
-
- /* setting EDAC (Error-detection and Correction) in debug register */
- at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
- IPQ5018_PHY_DAC_MASK,
- FIELD_PREP(IPQ5018_PHY_DAC_MASK,
- IPQ5018_PHY_DEBUG_EDAC_VAL));
- }
+ if (!priv->set_short_cable_dac)
+ return 0;
- return 0;
+ /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
+ IPQ5018_PHY_DAC_MASK,
+ FIELD_PREP(IPQ5018_PHY_DAC_MASK,
+ IPQ5018_PHY_MMD1_MDAC_VAL));
+ if (ret)
+ return ret;
+
+ /* setting EDAC (Error-detection and Correction) in debug register */
+ return at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
+ IPQ5018_PHY_DAC_MASK,
+ FIELD_PREP(IPQ5018_PHY_DAC_MASK,
+ IPQ5018_PHY_DEBUG_EDAC_VAL));
+}
+
+static int ipq5018_config_init(struct phy_device *phydev)
+{
+ return ipq5018_analog_init(phydev);
}
static void ipq5018_link_change_notify(struct phy_device *phydev)
@@ -1111,6 +1138,21 @@ static int ipq5018_probe(struct phy_device *phydev)
phydev->priv = priv;
+ /*
+ * The PHY starts autonegotiation as soon as it leaves reset. Apply the
+ * analog settings now instead of waiting for config_init() at attach
+ * time, and restart autonegotiation so that it uses them.
+ */
+ ret = ipq5018_analog_init(phydev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to apply analog settings\n");
+
+ ret = genphy_restart_aneg(phydev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to restart autonegotiation\n");
+
return 0;
}
--
2.43.0
next prev parent reply other threads:[~2026-09-28 22:07 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 22:07 [PATCH net v2 0/2] net: phy: qcom: at803x: IPQ5018 analog initialization fixes Yongzhao Chen
2026-09-28 22:07 ` [PATCH net v2 1/2] net: phy: qcom: at803x: Fix IPQ5018 short-cable DAC values Yongzhao Chen
2026-09-28 22:07 ` Yongzhao Chen [this message]
2026-09-29 0:26 ` [PATCH net v2 2/2] net: phy: qcom: at803x: Apply IPQ5018 analog settings at probe Andrew Lunn
2026-09-30 21:23 ` Yongzhao Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260928220717.939-3-yongzhao.derek@gmail.com \
--to=yongzhao.derek@gmail.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=george.moussalem@outlook.com \
--cc=hkallweit1@gmail.com \
--cc=hzyitc@outlook.com \
--cc=kuba@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®