]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommbe: ipq9574: complete USXGMII in-band AN
authorKenneth Kasilag <kenneth@kasilag.me>
Thu, 2 Jul 2026 11:28:36 +0000 (11:28 +0000)
committerRobert Marko <robimarko@gmail.com>
Mon, 6 Jul 2026 09:53:43 +0000 (11:53 +0200)
The XPCS signals USXGMII in-band autonegotiation completion through an
interrupt that is never enabled, waited for or acknowledged, so rate
adaptation never starts and the MAC receives nothing even though the
SerDes achieves block lock. The advertised speed and duplex are also
left at their reset values while autonegotiation runs.

Follow the vendor SSDK sequence: enable the completion interrupt and
advertise 10G/full duplex when configuring the PCS, and on link-up
restart the in-band autonegotiation, wait for completion and
acknowledge it before programming the negotiated speed. The wait is
best-effort, as in the vendor driver.

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/0357-net-pcs-qcom-ipq9574-complete-USXGMII-in-band-AN.patch [new file with mode: 0644]

diff --git a/target/linux/qualcommbe/patches-6.18/0357-net-pcs-qcom-ipq9574-complete-USXGMII-in-band-AN.patch b/target/linux/qualcommbe/patches-6.18/0357-net-pcs-qcom-ipq9574-complete-USXGMII-in-band-AN.patch
new file mode 100644 (file)
index 0000000..cbd179b
--- /dev/null
@@ -0,0 +1,87 @@
+From: Kenneth Kasilag <kenneth@kasilag.me>
+Date: Tue, 30 Jun 2026 00:00:00 +0000
+Subject: [PATCH] net: pcs: qcom-ipq9574: complete USXGMII in-band AN
+
+The XPCS signals USXGMII in-band autonegotiation completion through an
+interrupt that is never enabled, waited for or acknowledged, so rate
+adaptation never starts and the MAC receives nothing even though the
+SerDes achieves block lock. The advertised speed and duplex are also
+left at their reset values while autonegotiation runs.
+
+Follow the vendor SSDK sequence: enable the completion interrupt and
+advertise 10G/full duplex when configuring the PCS, and on link-up
+restart the in-band autonegotiation, wait for completion and
+acknowledge it before programming the negotiated speed. The wait is
+best-effort, as in the vendor driver.
+
+Signed-off-by: Kenneth Kasilag <kenneth@kasilag.me>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 34 ++++++++++++++++++++++++++--
+ 1 file changed, 26 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -95,6 +95,7 @@
+ #define XPCS_MII_CTRL                 0x1f0000
+ #define XPCS_MII1_CTRL(x)             (0x1a0000 + 0x10000 * ((x) - 1))
+ #define XPCS_MII_AN_EN                        BIT(12)
++#define XPCS_MII_AN_RESTART           BIT(9)
+ #define XPCS_DUPLEX_FULL              BIT(8)
+ #define XPCS_SPEED_MASK                       (BIT(13) | BIT(6) | BIT(5))
+ #define XPCS_SPEED_10000              (BIT(13) | BIT(6))
+@@ -107,10 +108,12 @@
+ #define XPCS_MII_AN_CTRL              0x1f8001
+ #define XPCS_MII1_AN_CTRL(x)          (0x1a8001 + 0x10000 * ((x) - 1))
+ #define XPCS_MII_AN_8BIT              BIT(8)
++#define XPCS_MII_AN_INTR_EN           BIT(0)
+ #define XPCS_MII_AN_INTR_STS          0x1f8002
+ #define XPCS_MII1_AN_INTR_STS(x)      (0x1a8002 + 0x10000 * ((x) - 1))
+ #define XPCS_USXG_AN_LINK_STS         BIT(14)
++#define XPCS_MII_AN_CL37_CMPLT                BIT(0)
+ #define XPCS_USXG_AN_SPEED_MASK               GENMASK(12, 10)
+ #define XPCS_USXG_AN_SPEED_10         0
+ #define XPCS_USXG_AN_SPEED_100                1
+@@ -472,12 +475,16 @@ static int ipq_pcs_config_usxgmii(struct
+       }
+       reg = (index == 0) ? XPCS_MII_AN_CTRL : XPCS_MII1_AN_CTRL(index);
+-      ret = regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_8BIT);
++      ret = regmap_set_bits(qpcs->regmap, reg,
++                            XPCS_MII_AN_8BIT | XPCS_MII_AN_INTR_EN);
+       if (ret)
+               return ret;
+       reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_CTRL(index);
+-      return regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_EN);
++      /* Advertise 10G/full while AN runs; link-up applies the result */
++      return regmap_update_bits(qpcs->regmap, reg,
++                                XPCS_MII_AN_EN | XPCS_SPEED_MASK | XPCS_DUPLEX_FULL,
++                                XPCS_MII_AN_EN | XPCS_SPEED_10000 | XPCS_DUPLEX_FULL);
+ }
+ static int ipq_pcs_config_10gbaser(struct ipq_pcs *qpcs)
+@@ -553,6 +560,23 @@ static int ipq_pcs_link_up_config_usxgmi
+       unsigned int reg, val;
+       int ret;
++      /* Rate adaptation is held off until the AN completion is acked:
++       * restart in-band AN, wait for it and ack it.
++       */
++      reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_CTRL(index);
++      ret = regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_RESTART);
++      if (ret)
++              return ret;
++
++      reg = (index == 0) ? XPCS_MII_AN_INTR_STS : XPCS_MII1_AN_INTR_STS(index);
++      ret = regmap_read_poll_timeout(qpcs->regmap, reg, val,
++                                     val & XPCS_MII_AN_CL37_CMPLT,
++                                     1000, 100000);
++      if (ret)
++              dev_warn(qpcs->dev, "USXGMII in-band AN did not complete\n");
++      else
++              regmap_clear_bits(qpcs->regmap, reg, XPCS_MII_AN_CL37_CMPLT);
++
+       switch (speed) {
+       case SPEED_10000:
+               val = XPCS_SPEED_10000;