[PATCH net-next v5 2/2] net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S
From: Johan Alvarado <hidden>
Date: 2026-07-09 20:26:09
Also in:
lkml
Subsystem:
networking drivers, networking [dsa], realtek rtl83xx smi dsa router chips, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Vladimir Oltean, Linus Walleij, Alvin Šipraga, Linus Torvalds
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 SerDes PCS to handle HSGMII, which phylink represents as 2500base-x: - Select the HSGMII SerDes tuning parameters and external interface mode, and mux the SerDes to MAC8 in HSGMII mode, from pcs_config() according to the interface. The parameters are again lifted from the GPL-licensed Realtek rtl8367c vendor driver, and again only cover the tuning variant for a non-zero chip option, so the mode is gated on the option probed at setup. - Advertise 2500base-x and MAC_2500FD on ports whose external interface supports HSGMII. - Accept SPEED_2500 in the forced link 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. - Raise the port 6 ingress and egress rate limiters to their maximum at setup time, as the vendor switch init does unconditionally for the whole chip family. The chip resets them to 0x1FFFF (~1.048 Gbps in units of 8 Kbps), which caps the aggregate HSGMII throughput at roughly 1 Gbps. The vendor documentation describes the reset default as disabling the limiter, but the cap is real: on an RTL8367S-based Mercusys MR85X running an OpenWrt backport of this series, several clients on 1 Gbps user ports were limited to about 1.02 Gbps combined across the HSGMII CPU port until these limiters were raised, after which throughput reached about 2 Gbps [1]. The related HSGMII scheduler line rate (LINE_RATE_HSG_H) is already set to its maximum by the common init jam table. Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to the SoC over HSGMII. Link: https://github.com/openwrt/openwrt/pull/19445#issuecomment-4505613294 [1] Suggested-by: Luiz Angelo Daros de Luca <redacted> Suggested-by: Mieczyslaw Nalewaj <redacted> Signed-off-by: Johan Alvarado <redacted> --- drivers/net/dsa/realtek/rtl8365mb_main.c | 134 ++++++++++++++++++++--- 1 file changed, 118 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index ea03c42d0f1a..d1ba0cc9426f 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c@@ -40,8 +40,8 @@ * driver has only been tested with a fixed-link, but in principle it should not * matter. * - * NOTE: Currently, only the RGMII and SGMII interfaces are implemented in this - * driver. + * NOTE: Currently, only the RGMII, SGMII and HSGMII interfaces are implemented + * in this driver. * * 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
@@ -251,6 +251,18 @@ #define RTL8365MB_BYPASS_LINE_RATE_REG 0x03F7 #define RTL8365MB_BYPASS_LINE_RATE_MASK(_port) BIT((_port) - 5) +/* Port 6 ingress and egress rate limiter registers. Each limit is a 19-bit + * value in units of 8 Kbps, split across a 16-bit LSB register (CTRL0) and a + * 3-bit MSB field (CTRL1). The chip resets them to 0x1FFFF; see + * rtl8365mb_sds_raise_rate_limits(). + */ +#define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL0_REG 0x00CF +#define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_REG 0x00D0 +#define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_MASK 0x0007 +#define RTL8365MB_PORT6_EGRESSBW_CTRL0_REG 0x0398 +#define RTL8365MB_PORT6_EGRESSBW_CTRL1_REG 0x0399 +#define RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK 0x0007 + /* SerDes indirect access registers */ #define RTL8365MB_SDS_INDACS_CMD_REG 0x6600 #define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK 0x0100
@@ -637,6 +649,18 @@ 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. As + * with the SGMII table, the vendor driver keeps several variants and selects + * one based on the chip option register; these are the values for a non-zero + * option, which is what RTL8367S parts seen so far report. See + * rtl8365mb_sds_probe_option(). + */ +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),
@@ -1242,20 +1266,70 @@ static int rtl8365mb_sds_probe_option(struct realtek_priv *priv) return 0; } +/* The vendor driver raises the port 6 ingress and egress rate limiters to + * their maximum in its switch init, unconditionally for the whole chip + * family. The chip reset in rtl8365mb_setup() puts them back to their reset + * default of 0x1FFFF, a ~1.048 Gbps limit which caps the aggregate + * throughput of an HSGMII CPU port at roughly 1 Gbps. The vendor + * documentation describes the reset default as disabling the limiter, but + * the cap has been observed on hardware. Raise them likewise, to 0x7FFFF + * (~4.19 Gbps, above the HSGMII line rate). The related HSGMII scheduler + * line rate register (LINE_RATE_HSG_H, 0x03FA) is already set to its + * maximum by the common init jam table. + */ +static int rtl8365mb_sds_raise_rate_limits(struct realtek_priv *priv) +{ + int ret; + + ret = regmap_write(priv->map, RTL8365MB_INGRESSBW_PORT6_RATE_CTRL0_REG, + 0xFFFF); + if (ret) + return ret; + + ret = regmap_update_bits(priv->map, + RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_REG, + RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_MASK, + RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_MASK); + if (ret) + return ret; + + ret = regmap_write(priv->map, RTL8365MB_PORT6_EGRESSBW_CTRL0_REG, + 0xFFFF); + if (ret) + return ret; + + return regmap_update_bits(priv->map, RTL8365MB_PORT6_EGRESSBW_CTRL1_REG, + RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK, + RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK); +} + static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) { + const struct rtl8365mb_jam_tbl_entry *sds_jam; const int id = RTL8365MB_SDS_EXT_INTERFACE_ID; struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs); struct realtek_priv *priv; + size_t sds_jam_size; + u32 mode; u16 val; int ret; int i; priv = mb->priv; + 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; + } + /* Hold the embedded DW8051 microcontroller in reset and keep it * disabled. The vendor driver loads firmware into it to manage the * SerDes link, but the firmware only duplicates work that phylink
@@ -1283,24 +1357,24 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 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, - 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, 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; - val = RTL8365MB_EXT_PORT_MODE_SGMII - << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id); + val = mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id); ret = regmap_update_bits(priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id), RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
@@ -1346,7 +1420,8 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 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 unsigned int rtl8365mb_pcs_inband_caps(struct phylink_pcs *pcs,
@@ -1401,7 +1476,9 @@ static void rtl8365mb_pcs_get_state(struct phylink_pcs *pcs, switch (FIELD_GET(RTL8365MB_SDS_MISC_SGMII_SPD_MASK, val)) { case RTL8365MB_PORT_SPEED_1000M: - state->speed = SPEED_1000; + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; break; case RTL8365MB_PORT_SPEED_100M: state->speed = SPEED_100;
@@ -1426,7 +1503,11 @@ static void rtl8365mb_pcs_link_up(struct phylink_pcs *pcs, u32 r_speed; int ret; - 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;
@@ -1486,7 +1567,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;
@@ -1569,6 +1654,13 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, mb->sds_supported) __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); + + if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_HSGMII && + mb->sds_supported) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; + } } static struct phylink_pcs *
@@ -1610,8 +1702,8 @@ static void rtl8365mb_phylink_mac_config(struct phylink_config *config, return; } - /* SGMII is handled by the SerDes PCS, configured through the - * phylink_pcs ops, so there is nothing to do here for it. + /* SGMII and 2500base-x are handled by the SerDes PCS, configured + * through the phylink_pcs ops, so nothing to do here for them. */ if (rtl8365mb_interface_is_serdes(state->interface)) return;
@@ -2940,6 +3032,16 @@ static int rtl8365mb_setup(struct dsa_switch *ds) goto out_error; } + if (mb->sds_supported) { + ret = rtl8365mb_sds_raise_rate_limits(priv); + if (ret) { + dev_err(priv->dev, + "failed to raise port rate limits: %pe\n", + ERR_PTR(ret)); + goto out_error; + } + } + /* Set up cascading IRQs */ ret = rtl8365mb_irq_setup(priv); if (ret == -EPROBE_DEFER)
--
2.55.0