From c41356933050a7c2d9bc6f144644bdefeefd43be Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Tue, 24 Feb 2026 00:14:12 +0000 Subject: [PATCH] realtek: pcs: rtl930x: split pll config Split up PLL configuration of RTL930x in the two distinct actions of configuring the PLL itself (aka setting its speed, etc.) and selecting which PLL is used by a SerDes. It was found that for both RTL930x and RTL931x, PLL configuration can be combined while selecting the PLL a SerDes uses differs and needs to be implemented variant-specific. Signed-off-by: Jonas Jelonek Link: https://github.com/openwrt/openwrt/pull/22198 Signed-off-by: Hauke Mehrtens --- .../files-6.12/drivers/net/pcs/pcs-rtl-otto.c | 79 ++++++++++++++----- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c index f3796c984a..80d4d46bd5 100644 --- a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c @@ -1239,36 +1239,56 @@ static void rtpcs_930x_sds_rx_reset(struct rtpcs_serdes *sds, rtpcs_sds_write_bits(sds, page, 0x15, 4, 4, 0x0); } -static void rtpcs_930x_sds_get_pll_data(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type *pll, +static int rtpcs_930x_sds_get_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type *pll) +{ + struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); + int pbit = (sds == even_sds) ? 4 : 6; + int pll_sel; + + pll_sel = rtpcs_sds_read_bits(even_sds, 0x20, 0x12, pbit + 1, pbit); + if (pll_sel < 0) + return pll_sel; + + /* bit 0 is force-bit, bit 1 is PLL selector */ + *pll = (enum rtpcs_sds_pll_type)(pll_sel >> 1); + return 0; +} + +static void rtpcs_930x_sds_get_pll_data(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, enum rtpcs_sds_pll_speed *speed) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); - int sbit, pbit = (sds == even_sds) ? 4 : 6; - int pll_val, speed_val; + int sbit, speed_val; /* * PLL data is shared between adjacent SerDes in the even lane. Each SerDes defines * what PLL it needs (ring or LC) while the PLL itself stores the current speed. */ - pll_val = rtpcs_sds_read_bits(even_sds, 0x20, 0x12, pbit + 1, pbit); - - /* bit 0 is force-bit, bit 1 is PLL selector */ - *pll = (enum rtpcs_sds_pll_type)(pll_val >> 1); - - sbit = *pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; + sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; speed_val = rtpcs_sds_read_bits(even_sds, 0x20, 0x12, sbit + 3, sbit); /* bit 0 is force-bit, bits [3:1] are speed selector */ *speed = (enum rtpcs_sds_pll_speed)(speed_val >> 1); } +static int rtpcs_930x_sds_set_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode, + enum rtpcs_sds_pll_type pll) +{ + struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); + int pbit = (sds == even_sds) ? 4 : 6; + + /* Selecting the PLL a SerDes is done in the even lane register */ + + /* bit 0 is force-bit, bit 1 is PLL selector */ + return rtpcs_sds_write_bits(even_sds, 0x20, 0x12, pbit + 1, pbit, (pll << 1) | BIT(0)); +} + static int rtpcs_930x_sds_set_pll_data(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, enum rtpcs_sds_pll_speed speed) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); int sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; - int pbit = (sds == even_sds) ? 4 : 6; if (speed >= RTPCS_SDS_PLL_SPD_END) return -EINVAL; @@ -1287,9 +1307,6 @@ static int rtpcs_930x_sds_set_pll_data(struct rtpcs_serdes *sds, enum rtpcs_sds_ rtpcs_sds_write_bits(even_sds, 0x20, 0x12, 3, 0, 0xf); - /* bit 0 is force-bit, bit 1 is PLL selector */ - rtpcs_sds_write_bits(even_sds, 0x20, 0x12, pbit + 1, pbit, (pll << 1) | BIT(0)); - /* bit 0 is force-bit, bits [3:1] are speed selector */ rtpcs_sds_write_bits(even_sds, 0x20, 0x12, sbit + 3, sbit, (speed << 1) | BIT(0)); @@ -1300,9 +1317,8 @@ static void rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); int reset_sequence[4] = { 3, 2, 3, 1 }; - enum rtpcs_sds_pll_speed speed; enum rtpcs_sds_pll_type pll; - int i, bit; + int ret, i, bit; /* * After the PLL speed has changed, the CMU must take over the new values. The models @@ -1310,7 +1326,10 @@ static void rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds) * to flipping two bits in a special sequence. */ - rtpcs_930x_sds_get_pll_data(sds, &pll, &speed); + ret = rtpcs_930x_sds_get_pll_select(sds, &pll); + if (ret < 0) + return; + bit = pll == RTPCS_SDS_PLL_TYPE_LC ? 2 : 0; for (i = 0; i < ARRAY_SIZE(reset_sequence); i++) @@ -1366,11 +1385,16 @@ static void rtpcs_930x_sds_set_power(struct rtpcs_serdes *sds, bool on) static void rtpcs_930x_sds_reconfigure_pll(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll) { enum rtpcs_sds_pll_speed speed; - enum rtpcs_sds_pll_type tmp; - int mode; + enum rtpcs_sds_pll_type old_pll; + int mode, ret; mode = __rtpcs_930x_sds_get_ip_mode(sds); - rtpcs_930x_sds_get_pll_data(sds, &tmp, &speed); + + ret = rtpcs_930x_sds_get_pll_select(sds, &old_pll); + if (ret < 0) + return; + + rtpcs_930x_sds_get_pll_data(sds, old_pll, &speed); rtpcs_930x_sds_set_power(sds, false); __rtpcs_930x_sds_set_ip_mode(sds, RTPCS_930X_SDS_OFF); @@ -1378,6 +1402,10 @@ static void rtpcs_930x_sds_reconfigure_pll(struct rtpcs_serdes *sds, enum rtpcs_ rtpcs_930x_sds_set_pll_data(sds, pll, speed); rtpcs_930x_sds_reset_cmu(sds); + ret = rtpcs_930x_sds_set_pll_select(sds, sds->hw_mode, pll); + if (ret < 0) + return; + __rtpcs_930x_sds_set_ip_mode(sds, mode); if (rtpcs_930x_sds_wait_clock_ready(sds)) pr_err("%s: SDS %d could not sync clock\n", __func__, sds->id); @@ -1392,7 +1420,7 @@ static int rtpcs_930x_sds_config_pll(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_speed speed, neighbor_speed; enum rtpcs_sds_pll_type pll, neighbor_pll; bool speed_changed = true; - int neighbor_mode; + int neighbor_mode, ret; /* * A SerDes pair on the RTL930x is driven by two PLLs. A low speed ring PLL can generate @@ -1414,7 +1442,12 @@ static int rtpcs_930x_sds_config_pll(struct rtpcs_serdes *sds, */ neighbor_mode = __rtpcs_930x_sds_get_ip_mode(nb_sds); - rtpcs_930x_sds_get_pll_data(nb_sds, &neighbor_pll, &neighbor_speed); + + ret = rtpcs_930x_sds_get_pll_select(sds, &neighbor_pll); + if (ret < 0) + return ret; + + rtpcs_930x_sds_get_pll_data(nb_sds, neighbor_pll, &neighbor_speed); if ((hw_mode == RTPCS_SDS_MODE_1000BASEX) || (hw_mode == RTPCS_SDS_MODE_SGMII)) @@ -1447,6 +1480,10 @@ static int rtpcs_930x_sds_config_pll(struct rtpcs_serdes *sds, if (speed_changed) rtpcs_930x_sds_reset_cmu(sds); + ret = rtpcs_930x_sds_set_pll_select(sds, hw_mode, pll); + if (ret < 0) + return ret; + pr_info("%s: SDS %d using %s PLL for mode %d\n", __func__, sds->id, pll == RTPCS_SDS_PLL_TYPE_LC ? "LC" : "ring", hw_mode);