]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: add 10G_QXGMII serdes mode support for RTL930x 20472/head
authorDamien Dejean <dam.dejean@gmail.com>
Tue, 29 Jul 2025 16:25:35 +0000 (18:25 +0200)
committerRobert Marko <robimarko@gmail.com>
Sun, 26 Oct 2025 10:24:51 +0000 (11:24 +0100)
In Realtek implementation USXGMII is divided in submodes:
 - USXGMII_SX: 10G single link, equivalent of PHY_INTERFACE_MODE_USXGMII
 - USXGMII_DX: 10G two links (2*5G ?),
 - USXGMII_QX: 10G four links, presumably 4*2.5G, used with the RTL8224,
   equivalent of PHY_INTERFACE_MODE_10G_QXGMII.

This CL adds the 10_GQXGMII modes to the RTL930x implementation. In
particular the "mode set" function is extended to support both simple
mode set, and force mode set depending on the mode according to
dal_longan_sds_mode_set [1].

[1] https://github.com/ddejean/dms-1250-oss-release/blob/main/sdk/sdk_rtk_switch/rtk-sdk/src/dal/longan/dal_longan_sds.c#L1746

Signed-off-by: Damien Dejean <dam.dejean@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/20472
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/common.c
target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c
target/linux/realtek/files-6.12/drivers/net/mdio/mdio-realtek-otto.c
target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c

index 8b782b5c14cd932cc58964bafc65c28dbfeee3bb..a073a1a5d245b6d642c4b29cb08c5dbdb6781cfa 100644 (file)
@@ -359,12 +359,8 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
 
                if (of_get_phy_mode(dn, &interface))
                        interface = PHY_INTERFACE_MODE_NA;
-
-               if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
-                       interface = PHY_INTERFACE_MODE_USXGMII;
-                       dev_warn(priv->dev, "handle mode 10g-qsxgmii internally as usxgmii for now\n");
-               }
-
+               if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
+                       priv->ports[pn].is2G5 = true;
                if (interface == PHY_INTERFACE_MODE_USXGMII)
                        priv->ports[pn].is2G5 = priv->ports[pn].is10G = true;
                if (interface == PHY_INTERFACE_MODE_10GBASER)
index fd8205326190a893d247f2aca0518b6c9dca2bdc..6cae6a8f0cdc04adee41c047dd35aa55cd3c9e6c 100644 (file)
@@ -653,6 +653,7 @@ static void rtldsa_93xx_phylink_get_caps(struct dsa_switch *ds, int port,
        __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
        __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces);
        __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces);
+       __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces);
 }
 
 static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
@@ -726,7 +727,8 @@ static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
            (state->interface == PHY_INTERFACE_MODE_1000BASEX ||
             state->interface == PHY_INTERFACE_MODE_SGMII ||
             state->interface == PHY_INTERFACE_MODE_2500BASEX ||
-            state->interface == PHY_INTERFACE_MODE_10GBASER))
+            state->interface == PHY_INTERFACE_MODE_10GBASER ||
+            state->interface == PHY_INTERFACE_MODE_10G_QXGMII))
                rtl9300_serdes_setup(port, sds_num, state->interface);
 }
 
index ea28055c401f25439348a6bc47c97525d37d7531..0f55bf51f48fddcdba1a4d14658dc4470978aaf8 100644 (file)
@@ -1562,11 +1562,6 @@ static int rtmdio_probe(struct platform_device *pdev)
                        priv->interfaces[pn] = PHY_INTERFACE_MODE_NA;
                dev_dbg(dev, "phy mode of port %d is %s\n", pn, phy_modes(priv->interfaces[pn]));
 
-               if (priv->interfaces[pn] == PHY_INTERFACE_MODE_10G_QXGMII) {
-                       priv->interfaces[pn] = PHY_INTERFACE_MODE_USXGMII;
-                       dev_warn(dev, "handle mode 10g-qsxgmii internally as usxgmii for now\n");
-               }
-
                /*
                 * TODO: The MDIO driver does not need any info about the SerDes. As long as
                 * the PCS driver cannot completely control the SerDes, look up the information
index 9392dd0a38ad1e50ac1a9e08904a5176a85c7021..7feceda6f8a74997d94822e55b0ef1084ed65990 100644 (file)
@@ -77,6 +77,13 @@ extern int rtmdio_930x_write_sds_phy(int sds, int page, int regnum, u16 val);
 #define RTL930X_SDS_OFF                        0x1f
 #define RTL930X_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 RTSDS_930X_PLL_1000            0x1
 #define RTSDS_930X_PLL_10000           0x5
 #define RTSDS_930X_PLL_2500            0x3
@@ -178,10 +185,11 @@ u16 rtl9300_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a
                           0x02A4, 0x02A4, 0x0198, 0x0198 };
 u8  rtl9300_sds_lsb[]  = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6};
 
-/* Reset the SerDes by powering it off and set a new operation mode
- * of the SerDes.
- */
-static void rtl9300_sds_rst(int sds_num, u32 mode)
+u16 rtl9300_sds_submode_regs[] = { 0x1cc, 0x1cc, 0x2d8, 0x2d8, 0x2d8, 0x2d8,
+                               0x2d8, 0x2d8};
+u8  rtl9300_sds_submode_lsb[]  = { 0, 5, 0, 5, 10, 15, 20, 25 };
+
+void rtl9300_sds_set(int sds_num, u32 mode)
 {
        pr_info("%s %d\n", __func__, mode);
        if (sds_num < 0 || sds_num > 11) {
@@ -189,11 +197,6 @@ static void rtl9300_sds_rst(int sds_num, u32 mode)
                return;
        }
 
-       sw_w32_mask(RTL930X_SDS_MASK << rtl9300_sds_lsb[sds_num],
-                   RTL930X_SDS_OFF << rtl9300_sds_lsb[sds_num],
-                   rtl9300_sds_regs[sds_num]);
-       mdelay(10);
-
        sw_w32_mask(RTL930X_SDS_MASK << rtl9300_sds_lsb[sds_num], mode << rtl9300_sds_lsb[sds_num],
                    rtl9300_sds_regs[sds_num]);
        mdelay(10);
@@ -202,37 +205,50 @@ static void rtl9300_sds_rst(int sds_num, u32 mode)
                 sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4));
 }
 
-void rtl9300_sds_set(int sds_num, u32 mode)
+static u32 rtl9300_sds_mode_get(int sds_num)
 {
-       pr_info("%s %d\n", __func__, mode);
+       u32 v;
+
        if (sds_num < 0 || sds_num > 11) {
                pr_err("Wrong SerDes number: %d\n", sds_num);
-               return;
+               return 0;
        }
 
-       sw_w32_mask(RTL930X_SDS_MASK << rtl9300_sds_lsb[sds_num], mode << rtl9300_sds_lsb[sds_num],
-                   rtl9300_sds_regs[sds_num]);
-       mdelay(10);
+       v = sw_r32(rtl9300_sds_regs[sds_num]);
+       v >>= rtl9300_sds_lsb[sds_num];
 
-       pr_debug("%s: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n", __func__,
-                sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4));
+       return v & RTL930X_SDS_MASK;
 }
 
-static u32 rtl9300_sds_mode_get(int sds_num)
+static u32 rtl9300_sds_submode_get(int sds_num)
 {
        u32 v;
 
-       if (sds_num < 0 || sds_num > 11) {
-               pr_err("Wrong SerDes number: %d\n", sds_num);
+       if (sds_num < 2 || sds_num > 9) {
+               pr_err("%s: unsupported SerDes %d\n", __func__, sds_num);
                return 0;
        }
 
-       v = sw_r32(rtl9300_sds_regs[sds_num]);
-       v >>= rtl9300_sds_lsb[sds_num];
+       v = sw_r32(rtl9300_sds_submode_regs[sds_num]);
+       v >>= rtl9300_sds_submode_lsb[sds_num];
 
        return v & RTL930X_SDS_MASK;
 }
 
+static void rtsds_930x_submode_set(int sds, u32 submode) {
+       if (sds < 2 || sds > 9) {
+               pr_err("%s: submode unsupported on serdes %d\n", __func__, sds);
+               return;
+       }
+       if (submode != RTL930X_SDS_SUBMODE_USXGMII_SX &&
+           submode != RTL930X_SDS_SUBMODE_USXGMII_QX) {
+               pr_err("%s: unsupported submode 0x%x\n", __func__, submode);
+       }
+       sw_w32_mask(RTL930X_SDS_MASK << rtl9300_sds_submode_lsb[sds-2],
+               submode << rtl9300_sds_submode_lsb[sds-2],
+               rtl9300_sds_submode_regs[sds-2]);
+}
+
 /* Read the link and speed status of the 2 internal SGMII/1000Base-X
  * ports of the RTL838x SoCs
  */
@@ -1068,7 +1084,7 @@ static int rtl9300_read_status(struct phy_device *phydev)
        struct device *dev = &phydev->mdio.dev;
        int phy_addr = phydev->mdio.addr;
        struct device_node *dn;
-       u32 sds_num = 0, status, latch_status, mode;
+       u32 sds_num = 0, status, latch_status, latch_status1, mode, submode;
 
        if (dev->of_node) {
                dn = dev->of_node;
@@ -1093,6 +1109,14 @@ static int rtl9300_read_status(struct phy_device *phydev)
                latch_status = rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2);
                status |= rtl9300_sds_field_r(sds_num, 0x5, 0, 12, 12);
                latch_status |= rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2);
+       } else if (mode == RTL930X_SDS_MODE_USXGMII) {
+               submode = rtl9300_sds_submode_get(sds_num);
+               status = rtl9300_sds_field_r(sds_num, 0x5, 0, 12, 12);
+               latch_status = rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2);
+               latch_status1 = rtl9300_sds_field_r(sds_num, 0x7, 21, 14, 4);
+               status |= rtl9300_sds_field_r(sds_num, 0x5, 0, 12, 12);
+               latch_status |= rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2);
+               latch_status1 |= rtl9300_sds_field_r(sds_num, 0x7, 21, 14, 4);
        } else {
                status = rtl9300_sds_field_r(sds_num, 0x1, 29, 8, 0);
                latch_status = rtl9300_sds_field_r(sds_num, 0x1, 30, 8, 0);
@@ -1107,6 +1131,13 @@ static int rtl9300_read_status(struct phy_device *phydev)
                if (mode == RTL930X_SDS_MODE_10GBASER) {
                        phydev->speed = SPEED_10000;
                        phydev->interface = PHY_INTERFACE_MODE_10GBASER;
+               } else if (mode == RTL930X_SDS_MODE_USXGMII) {
+                       phydev->speed = SPEED_10000;
+                       if (submode == RTL930X_SDS_SUBMODE_USXGMII_QX) {
+                               phydev->interface = PHY_INTERFACE_MODE_10G_QXGMII;
+                       } else {
+                               phydev->interface = PHY_INTERFACE_MODE_USXGMII;
+                       }
                } else {
                        phydev->speed = SPEED_1000;
                        phydev->interface = PHY_INTERFACE_MODE_1000BASEX;
@@ -1382,6 +1413,45 @@ static void rtsds_930x_force_mode(int sds, phy_interface_t interface)
        rtl930x_sds_rx_rst(sds, interface);
 }
 
+static void rtsds_930x_mode_set(int sds, phy_interface_t phy_mode)
+{
+       u32 mode;
+       u32 submode;
+
+       if (sds < 0 || sds > 11) {
+               pr_err("%s: invalid SerDes number: %d\n", __func__, sds);
+               return;
+       }
+
+       switch(phy_mode) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
+       case PHY_INTERFACE_MODE_10GBASER:
+               rtsds_930x_force_mode(sds, phy_mode);
+               return;
+       case PHY_INTERFACE_MODE_10G_QXGMII:
+               mode = RTL930X_SDS_MODE_USXGMII;
+               submode = RTL930X_SDS_SUBMODE_USXGMII_QX;
+               break;
+       default:
+               pr_warn("%s: unsupported mode %s\n", __func__, phy_modes(phy_mode));
+               return;
+       }
+
+       /* SerDes off first. */
+       rtl9300_sds_set(sds, RTL930X_SDS_OFF);
+
+       /* Set the mode. */
+       rtl9300_sds_set(sds, mode);
+
+       /* Set the submode if needed. */
+       if (phy_mode == PHY_INTERFACE_MODE_10G_QXGMII) {
+               rtsds_930x_submode_set(sds, submode);
+       }
+}
+
+
 static void rtl9300_sds_tx_config(int sds, phy_interface_t phy_if)
 {
        /* parameters: rtl9303_80G_txParam_s2 */
@@ -1409,6 +1479,7 @@ static void rtl9300_sds_tx_config(int sds, phy_interface_t phy_if)
                break;
        case PHY_INTERFACE_MODE_10GBASER:
        case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10G_QXGMII:
        case PHY_INTERFACE_MODE_XGMII:
                pre_en = 0;
                pre_amp = 0;
@@ -2250,6 +2321,7 @@ static int rtl9300_sds_sym_err_reset(int sds_num, phy_interface_t phy_mode)
 
        case PHY_INTERFACE_MODE_1000BASEX:
        case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_10G_QXGMII:
                rtl9300_sds_field_w(sds_num, 0x1, 24, 2, 0, 0);
                rtl9300_sds_field_w(sds_num, 0x1, 3, 15, 8, 0);
                rtl9300_sds_field_w(sds_num, 0x1, 2, 15, 0, 0);
@@ -2269,6 +2341,7 @@ static u32 rtl9300_sds_sym_err_get(int sds_num, phy_interface_t phy_mode)
 
        switch (phy_mode) {
        case PHY_INTERFACE_MODE_XGMII:
+       case PHY_INTERFACE_MODE_10G_QXGMII:
                break;
 
        case PHY_INTERFACE_MODE_1000BASEX:
@@ -2307,8 +2380,9 @@ static int rtl9300_sds_check_calibration(int sds_num, phy_interface_t phy_mode)
                }
                break;
        case PHY_INTERFACE_MODE_10GBASER:
+       case PHY_INTERFACE_MODE_10G_QXGMII:
                if (errors2 > 0) {
-                       pr_info("%s 10GBASER error rate too high\n", __func__);
+                       pr_info("%s: %s error rate too high\n", __func__, phy_modes(phy_mode));
                        return 1;
                }
                break;
@@ -2354,7 +2428,7 @@ int rtl9300_serdes_setup(int port, int sds_num, phy_interface_t phy_mode)
        int calib_tries = 0;
 
        /* Turn Off Serdes */
-       rtl9300_sds_rst(sds_num, RTL930X_SDS_OFF);
+       rtl9300_sds_set(sds_num, RTL930X_SDS_OFF);
 
        /* Apply serdes patches */
        rtsds_930x_patch_serdes(sds_num, phy_mode);
@@ -2378,7 +2452,7 @@ int rtl9300_serdes_setup(int port, int sds_num, phy_interface_t phy_mode)
        sw_w32_mask(1, 0, RTL930X_MAC_FORCE_MODE_CTRL + 4 * port);
 
        /* Enable SDS in desired mode */
-       rtsds_930x_force_mode(sds_num, phy_mode);
+       rtsds_930x_mode_set(sds_num, phy_mode);
 
        /* Enable Fiber RX */
        rtl9300_sds_field_w(sds_num, 0x20, 2, 12, 12, 0);