[PATCH net-next v1 2/2] net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S

From: Johan Alvarado

Date: Wed Jun 10 2026 - 03:56:41 EST



In addition to SGMII, the RTL8367S SerDes also supports HSGMII, which
carries 2.5 Gbps with the same signaling as SGMII at 2.5x clock rate.
The chip info table already declares HSGMII as a supported interface
mode for external interface 1.

Extend the SGMII configuration path to handle HSGMII, which phylink
represents as 2500base-x:

- Use the HSGMII SerDes tuning parameters and external interface mode,
and mux the SerDes to MAC8 in HSGMII mode. The parameters are again
lifted from the GPL-licensed Realtek rtl8367c vendor driver.

- Advertise 2500base-x and MAC_2500FD on ports whose external
interface supports HSGMII.

- Accept SPEED_2500 in the force mode configuration. The MAC speed
field has no 2.5 Gbps value: the rate is determined by the HSGMII
SerDes configuration, and the vendor driver programs the 1 Gbps
value here, so do the same.

Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
the SoC over HSGMII.

Signed-off-by: Johan Alvarado <contact@xxxxxxxx>
---
drivers/net/dsa/realtek/rtl8365mb.c | 61 +++++++++++++++++++++++------
1 file changed, 48 insertions(+), 13 deletions(-)

diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c
index 13ce8e6f1c72..fd8ec8bd6e55 100644
--- a/drivers/net/dsa/realtek/rtl8365mb.c
+++ b/drivers/net/dsa/realtek/rtl8365mb.c
@@ -41,7 +41,7 @@
* matter.
*
* NOTE: Currently, only the RGMII interface is implemented in this driver. On
- * the RTL8367S, the SGMII interface is also supported.
+ * the RTL8367S, the SGMII and HSGMII interfaces are also supported.
*
* The interrupt line is asserted on link UP/DOWN events. The driver creates a
* custom irqchip to handle this interrupt and demultiplex the events by reading
@@ -521,6 +521,13 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_sgmii[] = {
{ 0x0424, 0xD810 }, { 0x002E, 0x83F2 },
};

+/* HSGMII SerDes tuning parameters, lifted from the vendor driver sources */
+static const struct rtl8365mb_jam_tbl_entry rtl8365mb_sds_jam_hsgmii[] = {
+ { 0x0500, 0x82F0 }, { 0x0501, 0xF195 }, { 0x0502, 0x31A2 },
+ { 0x0503, 0x7960 }, { 0x0504, 0x9728 }, { 0x0423, 0x9D85 },
+ { 0x0424, 0xD810 }, { 0x0001, 0x0F80 }, { 0x002E, 0x83F2 },
+};
+
enum rtl8365mb_phy_interface_mode {
RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0,
RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0),
@@ -1128,6 +1135,9 @@ static int rtl8365mb_ext_config_sgmii(struct realtek_priv *priv, int port,
{
const struct rtl8365mb_extint *extint =
rtl8365mb_get_port_extint(priv, port);
+ const struct rtl8365mb_jam_tbl_entry *sds_jam;
+ size_t sds_jam_size;
+ u32 mode;
u16 val;
int ret;
int i;
@@ -1139,6 +1149,16 @@ static int rtl8365mb_ext_config_sgmii(struct realtek_priv *priv, int port,
if (extint->id != 1)
return -EOPNOTSUPP;

+ if (interface == PHY_INTERFACE_MODE_2500BASEX) {
+ sds_jam = rtl8365mb_sds_jam_hsgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_hsgmii);
+ mode = RTL8365MB_EXT_PORT_MODE_HSGMII;
+ } else {
+ sds_jam = rtl8365mb_sds_jam_sgmii;
+ sds_jam_size = ARRAY_SIZE(rtl8365mb_sds_jam_sgmii);
+ mode = RTL8365MB_EXT_PORT_MODE_SGMII;
+ }
+
/* The vendor driver clears the line rate bypass for all interface
* modes except TMII.
*/
@@ -1148,28 +1168,28 @@ static int rtl8365mb_ext_config_sgmii(struct realtek_priv *priv, int port,
return ret;

/* Tune the SerDes with vendor-prescribed parameters */
- for (i = 0; i < ARRAY_SIZE(rtl8365mb_sds_jam_sgmii); i++) {
- ret = rtl8365mb_sds_write(priv, 0,
- rtl8365mb_sds_jam_sgmii[i].reg,
- rtl8365mb_sds_jam_sgmii[i].val);
+ for (i = 0; i < sds_jam_size; i++) {
+ ret = rtl8365mb_sds_write(priv, 0, sds_jam[i].reg,
+ sds_jam[i].val);
if (ret)
return ret;
}

- /* Mux the SerDes to MAC8 in SGMII mode */
+ /* Mux the SerDes to MAC8 in the requested mode */
ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,
- RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK);
+ mode == RTL8365MB_EXT_PORT_MODE_SGMII ?
+ RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK :
+ RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK);
if (ret)
return ret;

ret = regmap_update_bits(
priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id),
RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id),
- RTL8365MB_EXT_PORT_MODE_SGMII
- << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(
- extint->id));
+ mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(
+ extint->id));
if (ret)
return ret;

@@ -1218,7 +1238,8 @@ static int rtl8365mb_ext_config_sgmii(struct realtek_priv *priv, int port,

static bool rtl8365mb_interface_is_serdes(phy_interface_t interface)
{
- return interface == PHY_INTERFACE_MODE_SGMII;
+ return interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_2500BASEX;
}

static int rtl8365mb_sds_config_forcemode(struct realtek_priv *priv, bool link,
@@ -1234,7 +1255,11 @@ static int rtl8365mb_sds_config_forcemode(struct realtek_priv *priv, bool link,
u32 r_speed;

if (link) {
- if (speed == SPEED_1000) {
+ /* The speed field has no value for 2.5 Gbps: the rate is
+ * determined by the HSGMII SerDes configuration, and the
+ * vendor driver programs the 1 Gbps value here.
+ */
+ if (speed == SPEED_2500 || speed == SPEED_1000) {
r_speed = RTL8365MB_PORT_SPEED_1000M;
} else if (speed == SPEED_100) {
r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -1286,7 +1311,11 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
r_rx_pause = rx_pause ? 1 : 0;
r_tx_pause = tx_pause ? 1 : 0;

- if (speed == SPEED_1000) {
+ /* The speed field has no value for 2.5 Gbps: the rate is
+ * determined by the HSGMII SerDes configuration, and the
+ * vendor driver programs the 1 Gbps value here.
+ */
+ if (speed == SPEED_2500 || speed == SPEED_1000) {
r_speed = RTL8365MB_PORT_SPEED_1000M;
} else if (speed == SPEED_100) {
r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -1366,6 +1395,12 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_SGMII)
__set_bit(PHY_INTERFACE_MODE_SGMII,
config->supported_interfaces);
+
+ if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_HSGMII) {
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ config->supported_interfaces);
+ config->mac_capabilities |= MAC_2500FD;
+ }
}

static void rtl8365mb_phylink_mac_config(struct phylink_config *config,
--
2.54.0