]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: pcs: rtl930x: reconfigure PLL of neighbor SerDes when needed 20568/head
authorJan Hoffmann <jan@3e8.eu>
Mon, 27 Oct 2025 17:50:57 +0000 (18:50 +0100)
committerRobert Marko <robimarko@gmail.com>
Fri, 7 Nov 2025 11:22:45 +0000 (12:22 +0100)
On RTL930x, each SerDes pair shares a set of PLLs with different
capabilities (LC PLL: 1G/2.5G/10G, ring PLL: 1G/2.5G). In principle,
this allows any combination of speeds on a SerDes pair. However, it
creates a special case when trying to configure a SerDes for 10G while
the LC PLL is already in use at a slower speed for the neighbor SerDes.
The current implementation just gives up in that case. Instead, free up
the LC PLL by reconfiguring the neighbor SerDes to the ring PLL.

Signed-off-by: Jan Hoffmann <jan@3e8.eu>
Link: https://github.com/openwrt/openwrt/pull/20568
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c

index bbc01671531b50a8ae47b696f9d270094133d2e0..d26b4aab80a2d0a663fd74195147c649d32b0c7f 100644 (file)
@@ -410,6 +410,26 @@ static void rtpcs_930x_sds_set_power(struct rtpcs_ctrl *ctrl, int sds, bool on)
        rtpcs_sds_write_bits(ctrl, sds, 0x20, 0x00, 5, 4, rx_enable);
 }
 
+static void rtpcs_930x_sds_reconfigure_pll(struct rtpcs_ctrl *ctrl, int sds, int pll)
+{
+       int mode, tmp, speed;
+
+       mode = rtpcs_930x_sds_get_internal_mode(ctrl, sds);
+       rtpcs_930x_sds_get_pll_data(ctrl, sds, &tmp, &speed);
+
+       rtpcs_930x_sds_set_power(ctrl, sds, false);
+       rtpcs_930x_sds_set_internal_mode(ctrl, sds, RTL930X_SDS_OFF);
+
+       rtpcs_930x_sds_set_pll_data(ctrl, sds, pll, speed);
+       rtpcs_930x_sds_reset_cmu(ctrl, sds);
+
+       rtpcs_930x_sds_set_internal_mode(ctrl, sds, mode);
+       if (rtpcs_930x_sds_wait_clock_ready(ctrl, sds))
+               pr_err("%s: SDS %d could not sync clock\n", __func__, sds);
+
+       rtpcs_930x_sds_set_power(ctrl, sds, true);
+}
+
 static int rtpcs_930x_sds_config_pll(struct rtpcs_ctrl *ctrl, int sds,
                                     phy_interface_t interface)
 {
@@ -429,6 +449,11 @@ static int rtpcs_930x_sds_config_pll(struct rtpcs_ctrl *ctrl, int sds,
         * - Use ring PLL for slow 1G speeds
         * - Use LC PLL for fast 10G speeds
         * - For 2.5G prefer ring over LC PLL
+        *
+        * However, when we want to configure 10G speed while the other SerDes is already using
+        * the LC PLL for a slower speed, there is no way to avoid reconfiguration. Note that
+        * this can even happen when the other SerDes is not actually in use, because changing
+        * the state of a SerDes back to RTL930X_SDS_OFF is not (yet) implemented.
         */
 
        neighbor_mode = rtpcs_930x_sds_get_internal_mode(ctrl, neighbor);
@@ -451,9 +476,12 @@ static int rtpcs_930x_sds_config_pll(struct rtpcs_ctrl *ctrl, int sds,
                pll = neighbor_pll;
        } else if (neighbor_pll == RTSDS_930X_PLL_RING)
                pll = RTSDS_930X_PLL_LC;
-       else if (speed == RTSDS_930X_PLL_10000)
-               return -ENOTSUPP; /* caller wants 10G but only ring PLL available */
-       else
+       else if (speed == RTSDS_930X_PLL_10000) {
+               pr_info("%s: SDS %d needs LC PLL, reconfigure SDS %d to use ring PLL\n",
+                       __func__, sds, neighbor);
+               rtpcs_930x_sds_reconfigure_pll(ctrl, neighbor, RTSDS_930X_PLL_RING);
+               pll = RTSDS_930X_PLL_LC;
+       } else
                pll = RTSDS_930X_PLL_RING;
 
        rtpcs_930x_sds_set_pll_data(ctrl, sds, pll, speed);