[openwrt/openwrt] realtek: pcs: rtl930x: rework SerDes mode setting

LEDE Commits lede-commits at lists.infradead.org
Sun Jan 18 02:19:18 PST 2026


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/a20ea50dec9cdc5c8b9f4c06bd8a661728128c1c

commit a20ea50dec9cdc5c8b9f4c06bd8a661728128c1c
Author: Jonas Jelonek <jelonek.jonas at gmail.com>
AuthorDate: Sun Jan 11 22:28:35 2026 +0000

    realtek: pcs: rtl930x: rework SerDes mode setting
    
    Rework the SerDes mode setting to have a better logical structure,
    similar to how RTL931x currently does it. Mode setting is grouped into a
    MAC setup and IP core setup. Most modes just need to trigger the MAC
    setup and then they work, otherwise some complex sequence is needed.
    
    To achieve that, rename several functions and group their content
    differently. While at it, rename some constants to use the RTPCS_ prefix.
    
    Signed-off-by: Jonas Jelonek <jelonek.jonas at gmail.com>
    Link: https://github.com/openwrt/openwrt/pull/21565
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 .../files-6.12/drivers/net/pcs/pcs-rtl-otto.c      | 248 +++++++++++++--------
 1 file changed, 156 insertions(+), 92 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 1c2d659144..6def54b3fd 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
@@ -82,21 +82,21 @@
  * 0x17: RXAUI_LITE	0x19: RXAUI_PLUS	0x1a: 10G Base-R
  * 0x1b: 10GR1000BX_AUTO			0x1f: OFF
  */
-#define RTL930X_SDS_MODE_SGMII		0x02
-#define RTL930X_SDS_MODE_1000BASEX	0x04
-#define RTL930X_SDS_MODE_USXGMII	0x0d
-#define RTL930X_SDS_MODE_XGMII		0x10
-#define RTL930X_SDS_MODE_2500BASEX	0x16
-#define RTL930X_SDS_MODE_10GBASER	0x1a
-#define RTL930X_SDS_OFF			0x1f
-#define RTL930X_SDS_MASK		0x1f
+#define RTPCS_930X_SDS_MODE_SGMII	0x02
+#define RTPCS_930X_SDS_MODE_1000BASEX	0x04
+#define RTPCS_930X_SDS_MODE_USXGMII	0x0d
+#define RTPCS_930X_SDS_MODE_XSGMII	0x10
+#define RTPCS_930X_SDS_MODE_2500BASEX	0x16
+#define RTPCS_930X_SDS_MODE_10GBASER	0x1a
+#define RTPCS_930X_SDS_OFF		0x1f
+#define RTPCS_930X_SDS_MASK		0x1f
 
 /* RTL930X SerDes supports two submodes when mode is USXGMII:
  * 0x00: USXGMII (aka USXGMII_SX)
  * 0x02: 10G_QXGMII (aka USXGMII_QX)
  */
-#define RTL930X_SDS_SUBMODE_USXGMII_SX	0x0
-#define RTL930X_SDS_SUBMODE_USXGMII_QX	0x2
+#define RTPCS_930X_SDS_SUBMODE_USXGMII_SX	0x0
+#define RTPCS_930X_SDS_SUBMODE_USXGMII_QX	0x2
 
 #define RTSDS_930X_PLL_1000		0x1
 #define RTSDS_930X_PLL_10000		0x5
@@ -884,74 +884,84 @@ static int rtpcs_839x_setup_serdes(struct rtpcs_serdes *sds,
 
 /* RTL930X */
 
-/* The access registers for SDS_MODE_SEL and the LSB for each SDS within */
-u16 rtpcs_930x_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0,
-			      0x02A4, 0x02A4, 0x0198, 0x0198 };
-u8  rtpcs_930x_sds_lsb[]  = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6};
+static const u16 rtpcs_930x_sds_regs[] = {
+	0x0194, 0x0194, 0x0194, 0x0194,		/* SDS_MODE_SEL_0 */
+	0x02a0, 0x02a0, 0x02a0, 0x02a0,		/* SDS_MODE_SEL_1 */
+	0x02a4, 0x02a4,				/* SDS_MODE_SEL_2 */
+	0x0198, 0x0198				/* SDS_MODE_SEL_3 */
+};
+static const u8 rtpcs_930x_sds_lsb[]  = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6 };
 
-u16 rtpcs_930x_sds_submode_regs[] = { 0x1cc, 0x1cc, 0x2d8, 0x2d8, 0x2d8, 0x2d8,
-				      0x2d8, 0x2d8};
-u8  rtpcs_930x_sds_submode_lsb[]  = { 0, 5, 0, 5, 10, 15, 20, 25 };
+static const u16 rtpcs_930x_sds_submode_regs[] = {
+	0x1cc, 0x1cc,				/* SDS_SUBMODE_CTRL0 */
+	0x2d8, 0x2d8, 0x2d8, 0x2d8,0x2d8, 0x2d8 /* SDS_SUBMODE_CTRL1 */
+};
+static const u8 rtpcs_930x_sds_submode_lsb[] = { 0, 5, 0, 5, 10, 15, 20, 25 };
 
-static void __rtpcs_930x_sds_set_mac_mode(struct rtpcs_serdes *sds, u32 mode)
+__always_unused
+static int __rtpcs_930x_sds_get_mac_mode(struct rtpcs_serdes *sds)
 {
 	u8 sds_id = sds->id;
+	int mode_val, ret;
 
-	pr_info("%s %d\n", __func__, mode);
+	ret = regmap_read(sds->ctrl->map, rtpcs_930x_sds_regs[sds_id], &mode_val);
+	if (ret < 0)
+		return ret;
 
-	regmap_write_bits(sds->ctrl->map, rtpcs_930x_sds_regs[sds_id],
-			  RTL930X_SDS_MASK << rtpcs_930x_sds_lsb[sds_id],
-			  mode << rtpcs_930x_sds_lsb[sds_id]);
-	mdelay(10);
+	mode_val >>= rtpcs_930x_sds_lsb[sds_id];
+	return mode_val & RTPCS_930X_SDS_MASK;
 }
 
-__always_unused
-static u32 __rtpcs_930x_sds_get_mac_mode(struct rtpcs_serdes *sds)
+static int __rtpcs_930x_sds_set_mac_mode(struct rtpcs_serdes *sds, u32 mode)
 {
 	u8 sds_id = sds->id;
-	u32 v;
+	int ret;
 
-	regmap_read(sds->ctrl->map, rtpcs_930x_sds_regs[sds_id], &v);
-	v >>= rtpcs_930x_sds_lsb[sds_id];
+	ret = regmap_write_bits(sds->ctrl->map, rtpcs_930x_sds_regs[sds_id],
+				RTPCS_930X_SDS_MASK << rtpcs_930x_sds_lsb[sds_id],
+				mode << rtpcs_930x_sds_lsb[sds_id]);
+	mdelay(10);
 
-	return v & RTL930X_SDS_MASK;
+	return ret;
 }
 
 __always_unused
-static u32 __rtpcs_930x_sds_get_usxgmii_submode(struct rtpcs_serdes *sds)
+static int __rtpcs_930x_sds_get_usxgmii_submode(struct rtpcs_serdes *sds)
 {
 	u8 sds_id = sds->id;
-	u32 v;
+	int submode, ret;
 
 	if (sds_id < 2 || sds_id > 9) {
-		pr_err("%s: unsupported SerDes %d\n", __func__, sds_id);
-		return 0;
+		pr_err("%s: SerDes %u doesn't support USXGMII submode\n", __func__, sds_id);
+		return -ENOTSUPP;
 	}
 
-	regmap_read(sds->ctrl->map, rtpcs_930x_sds_submode_regs[sds_id], &v);
-	v >>= rtpcs_930x_sds_submode_lsb[sds_id];
+	ret = regmap_read(sds->ctrl->map, rtpcs_930x_sds_submode_regs[sds_id], &submode);
+	if (ret < 0)
+		return ret;
 
-	return v & RTL930X_SDS_MASK;
+	submode >>= rtpcs_930x_sds_submode_lsb[sds_id];
+	return submode & RTPCS_930X_SDS_MASK;
 }
 
-static void __rtpcs_930x_sds_set_usxgmii_submode(struct rtpcs_serdes *sds,
-						 u32 submode)
+static int __rtpcs_930x_sds_set_usxgmii_submode(struct rtpcs_serdes *sds, u32 submode)
 {
 	u8 sds_id = sds->id;
 
 	if (sds_id < 2 || sds_id > 9) {
-		pr_err("%s: submode unsupported on serdes %d\n", __func__, sds_id);
-		return;
+		pr_err("%s: SerDes %u doesn't support USXGMII submode\n", __func__, sds_id);
+		return -ENOTSUPP;
 	}
 
-	if (submode != RTL930X_SDS_SUBMODE_USXGMII_SX &&
-	    submode != RTL930X_SDS_SUBMODE_USXGMII_QX) {
+	if (submode != RTPCS_930X_SDS_SUBMODE_USXGMII_SX &&
+	    submode != RTPCS_930X_SDS_SUBMODE_USXGMII_QX) {
 		pr_err("%s: unsupported submode 0x%x\n", __func__, submode);
+		return -ENOTSUPP;
 	}
 
-	regmap_write_bits(sds->ctrl->map, rtpcs_930x_sds_submode_regs[sds_id - 2],
-			  RTL930X_SDS_MASK << rtpcs_930x_sds_submode_lsb[sds_id - 2],
-			  submode << rtpcs_930x_sds_submode_lsb[sds_id - 2]);
+	return regmap_write_bits(sds->ctrl->map, rtpcs_930x_sds_submode_regs[sds_id - 2],
+				 RTPCS_930X_SDS_MASK << rtpcs_930x_sds_submode_lsb[sds_id - 2],
+				 submode << rtpcs_930x_sds_submode_lsb[sds_id - 2]);
 }
 
 static void rtpcs_930x_sds_rx_reset(struct rtpcs_serdes *sds,
@@ -1064,10 +1074,10 @@ static int __rtpcs_930x_sds_get_ip_mode(struct rtpcs_serdes *sds)
 	return rtpcs_sds_read_bits(sds, 0x1f, 0x09, 11, 7);
 }
 
-static void __rtpcs_930x_sds_set_ip_mode(struct rtpcs_serdes *sds, int mode)
+static int __rtpcs_930x_sds_set_ip_mode(struct rtpcs_serdes *sds, u32 mode)
 {
-	rtpcs_sds_write_bits(sds, 0x1f, 0x09, 6, 6, 0x1); /* Force mode enable */
-	rtpcs_sds_write_bits(sds, 0x1f, 0x09, 11, 7, mode);
+	/* BIT(0) is force mode enable bit */
+	return rtpcs_sds_write_bits(sds, 0x1f, 0x09, 11, 6, (mode & 0x1f) << 1 | BIT(0));
 }
 
 static void rtpcs_930x_sds_set_power(struct rtpcs_serdes *sds, bool on)
@@ -1087,7 +1097,7 @@ static void rtpcs_930x_sds_reconfigure_pll(struct rtpcs_serdes *sds, int pll)
 	rtpcs_930x_sds_get_pll_data(sds, &tmp, &speed);
 
 	rtpcs_930x_sds_set_power(sds, false);
-	__rtpcs_930x_sds_set_ip_mode(sds, RTL930X_SDS_OFF);
+	__rtpcs_930x_sds_set_ip_mode(sds, RTPCS_930X_SDS_OFF);
 
 	rtpcs_930x_sds_set_pll_data(sds, pll, speed);
 	rtpcs_930x_sds_reset_cmu(sds);
@@ -1202,10 +1212,33 @@ static int rtpcs_930x_sds_init_state_machine(struct rtpcs_serdes *sds,
 	return ret;
 }
 
-static void rtpcs_930x_sds_force_mode(struct rtpcs_serdes *sds,
+static int rtpcs_930x_sds_get_hw_mode_val(enum rtpcs_sds_mode hw_mode)
+{
+	switch (hw_mode) {
+	case RTPCS_SDS_MODE_OFF:
+		return RTPCS_930X_SDS_OFF;
+	case RTPCS_SDS_MODE_SGMII:
+		return RTPCS_930X_SDS_MODE_SGMII;
+	case RTPCS_SDS_MODE_1000BASEX:
+		return RTPCS_930X_SDS_MODE_1000BASEX;
+	case RTPCS_SDS_MODE_2500BASEX:
+		return RTPCS_930X_SDS_MODE_2500BASEX;
+	case RTPCS_SDS_MODE_10GBASER:
+		return RTPCS_930X_SDS_MODE_10GBASER;
+	case RTPCS_SDS_MODE_XSGMII:
+		return RTPCS_930X_SDS_MODE_XSGMII;
+	case RTPCS_SDS_MODE_USXGMII_10GSXGMII:
+	case RTPCS_SDS_MODE_USXGMII_10GQXGMII:
+		return RTPCS_930X_SDS_MODE_USXGMII;
+	default:
+		return -ENOTSUPP;
+	}
+}
+
+static int rtpcs_930x_sds_set_ip_mode(struct rtpcs_serdes *sds,
 				      enum rtpcs_sds_mode hw_mode)
 {
-	u32 mode_val;
+	int mode_val, ret;
 
 	/*
 	 * TODO: Usually one would expect that it is enough to modify the SDS_MODE_SEL_*
@@ -1214,38 +1247,29 @@ static void rtpcs_930x_sds_force_mode(struct rtpcs_serdes *sds,
 	 * if this sequence should quit early in case of errors.
 	 */
 
-	switch (hw_mode) {
-	case RTPCS_SDS_MODE_SGMII:
-		mode_val = RTL930X_SDS_MODE_SGMII;
-		break;
-	case RTPCS_SDS_MODE_1000BASEX:
-		mode_val = RTL930X_SDS_MODE_1000BASEX;
-		break;
-	case RTPCS_SDS_MODE_2500BASEX:
-		mode_val = RTL930X_SDS_MODE_2500BASEX;
-		break;
-	case RTPCS_SDS_MODE_10GBASER:
-		mode_val = RTL930X_SDS_MODE_10GBASER;
-		break;
-	case RTPCS_SDS_MODE_OFF:
-		mode_val = RTL930X_SDS_OFF;
-		break;
-	default:
+	mode_val = rtpcs_930x_sds_get_hw_mode_val(hw_mode);
+	if (mode_val < 0) {
 		pr_err("%s: SDS %d does not support mode %d\n", __func__,
 		       sds->id, hw_mode);
-		return;
+		return mode_val;
 	}
 
 	rtpcs_930x_sds_set_power(sds, false);
-	__rtpcs_930x_sds_set_ip_mode(sds, RTL930X_SDS_OFF);
+	ret = __rtpcs_930x_sds_set_ip_mode(sds, RTPCS_930X_SDS_OFF);
+	if (ret < 0)
+		return ret;
+
 	if (hw_mode == RTPCS_SDS_MODE_OFF)
-		return;
+		return 0;
 
 	if (rtpcs_930x_sds_config_pll(sds, hw_mode))
 		pr_err("%s: SDS %d could not configure PLL for mode %d\n", __func__,
 		       sds->id, hw_mode);
 
-	__rtpcs_930x_sds_set_ip_mode(sds, mode_val);
+	ret = __rtpcs_930x_sds_set_ip_mode(sds, mode_val);
+	if (ret < 0)
+		return ret;
+
 	if (rtpcs_930x_sds_wait_clock_ready(sds))
 		pr_err("%s: SDS %d could not sync clock\n", __func__, sds->id);
 
@@ -1255,38 +1279,72 @@ static void rtpcs_930x_sds_force_mode(struct rtpcs_serdes *sds,
 
 	rtpcs_930x_sds_set_power(sds, true);
 	rtpcs_930x_sds_rx_reset(sds, hw_mode);
+	return 0;
 }
 
-static void rtpcs_930x_sds_mode_set(struct rtpcs_serdes *sds,
-				    enum rtpcs_sds_mode hw_mode)
+static int rtpcs_930x_sds_set_mac_mode(struct rtpcs_serdes *sds,
+				       enum rtpcs_sds_mode hw_mode)
 {
-	u32 mode_val, submode_val;
+	int submode_val = -1;
+	int mode_val, ret;
+
+	mode_val = rtpcs_930x_sds_get_hw_mode_val(hw_mode);
+	if (mode_val < 0)
+		return mode_val;
 
 	switch (hw_mode) {
-	case RTPCS_SDS_MODE_SGMII:
-	case RTPCS_SDS_MODE_1000BASEX:
-	case RTPCS_SDS_MODE_2500BASEX:
-	case RTPCS_SDS_MODE_10GBASER:
-		rtpcs_930x_sds_force_mode(sds, hw_mode);
-		return;
+	case RTPCS_SDS_MODE_USXGMII_10GSXGMII:
+		submode_val = RTPCS_930X_SDS_SUBMODE_USXGMII_SX;
+		break;
 	case RTPCS_SDS_MODE_USXGMII_10GQXGMII:
-		mode_val = RTL930X_SDS_MODE_USXGMII;
-		submode_val = RTL930X_SDS_SUBMODE_USXGMII_QX;
+		submode_val = RTPCS_930X_SDS_SUBMODE_USXGMII_QX;
 		break;
 	default:
-		pr_warn("%s: unsupported mode %d\n", __func__, hw_mode);
-		return;
+		break;
 	}
 
 	/* SerDes off first. */
-	__rtpcs_930x_sds_set_mac_mode(sds, RTL930X_SDS_OFF);
+	ret = __rtpcs_930x_sds_set_mac_mode(sds, RTPCS_930X_SDS_OFF);
+	if (ret < 0)
+		return ret;
+
+	if (hw_mode == RTPCS_SDS_MODE_OFF)
+		return 0;
 
 	/* Set the mode. */
-	__rtpcs_930x_sds_set_mac_mode(sds, mode_val);
+	ret = __rtpcs_930x_sds_set_mac_mode(sds, mode_val);
+	if (ret < 0)
+		return ret;
 
 	/* Set the submode if needed. */
-	if (hw_mode == RTPCS_SDS_MODE_USXGMII_10GQXGMII)
-		__rtpcs_930x_sds_set_usxgmii_submode(sds, submode_val);
+	if (submode_val >= 0) {
+		ret = __rtpcs_930x_sds_set_usxgmii_submode(sds, submode_val);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rtpcs_930x_sds_set_mode(struct rtpcs_serdes *sds,
+				   enum rtpcs_sds_mode hw_mode)
+{
+	/*
+	 * Several modes can be configured via MAC setup, just by setting
+	 * a register to a specific value and the MAC will configure
+	 * "everything" as needed. For some modes, this seems incomplete and
+	 * we need to do manual configuration in the SerDes IP core itself.
+	 */
+
+	switch (hw_mode) {
+	case RTPCS_SDS_MODE_SGMII:
+	case RTPCS_SDS_MODE_1000BASEX:
+	case RTPCS_SDS_MODE_2500BASEX:
+	case RTPCS_SDS_MODE_10GBASER:
+		return rtpcs_930x_sds_set_ip_mode(sds, hw_mode);
+	default:
+		return rtpcs_930x_sds_set_mac_mode(sds, hw_mode);
+	}
 }
 
 static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds,
@@ -2112,7 +2170,7 @@ static void rtpcs_930x_sds_do_rx_calibration(struct rtpcs_serdes *sds,
 	mdelay(20);
 
 	/* Do this only for 10GR mode, SDS active in mode 0x1a */
-	if (rtpcs_sds_read_bits(sds, 0x1f, 9, 11, 7) == RTL930X_SDS_MODE_10GBASER) {
+	if (rtpcs_sds_read_bits(sds, 0x1f, 9, 11, 7) == RTPCS_930X_SDS_MODE_10GBASER) {
 		pr_info("%s: SDS enabled\n", __func__);
 		latch_sts = rtpcs_sds_read_bits(sds, 0x4, 1, 2, 2);
 		mdelay(1);
@@ -2542,7 +2600,9 @@ static int rtpcs_930x_setup_serdes(struct rtpcs_serdes *sds,
 	}
 
 	/* Turn Off Serdes */
-	__rtpcs_930x_sds_set_mac_mode(sds, RTL930X_SDS_OFF);
+	ret = rtpcs_930x_sds_set_mode(sds, RTPCS_SDS_MODE_OFF);
+	if (ret < 0)
+		return ret;
 
 	/* Apply serdes patches */
 	rtpcs_930x_sds_patch(sds, hw_mode);
@@ -2559,7 +2619,11 @@ static int rtpcs_930x_setup_serdes(struct rtpcs_serdes *sds,
 	rtpcs_930x_sds_set_polarity(sds, sds->tx_pol_inv, sds->rx_pol_inv);
 
 	/* Enable SDS in desired mode */
-	rtpcs_930x_sds_mode_set(sds, hw_mode);
+	ret = rtpcs_930x_sds_set_mode(sds, hw_mode);
+	if (ret < 0)
+		return ret;
+
+	sds->hw_mode = hw_mode;
 
 	/* Enable Fiber RX */
 	rtpcs_sds_write_bits(sds, 0x20, 2, 12, 12, 0);




More information about the lede-commits mailing list