realtek: add 10G_QXGMII serdes mode support for RTL930x

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>
This commit is contained in:
Damien Dejean
2025-07-29 18:25:35 +02:00
committed by Robert Marko
parent dca20f91ea
commit ddd82c8b3d
4 changed files with 106 additions and 39 deletions

View 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)

View 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);
}

View 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

View 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,29 +185,9 @@ 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)
{
pr_info("%s %d\n", __func__, mode);
if (sds_num < 0 || sds_num > 11) {
pr_err("Wrong SerDes number: %d\n", sds_num);
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);
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));
}
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)
{
@ -233,6 +220,35 @@ static u32 rtl9300_sds_mode_get(int sds_num)
return v & RTL930X_SDS_MASK;
}
static u32 rtl9300_sds_submode_get(int sds_num)
{
u32 v;
if (sds_num < 2 || sds_num > 9) {
pr_err("%s: unsupported SerDes %d\n", __func__, sds_num);
return 0;
}
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);