From: Mieczyslaw Nalewaj <namiltd@yahoo.com>
To: contact@c127.dev, "Linus Walleij" <linusw@kernel.org>,
"Alvin Šipraga" <alsi@bang-olufsen.dk>,
"Andrew Lunn" <andrew@lunn.ch>,
"Vladimir Oltean" <olteanv@gmail.com>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Russell King" <linux@armlinux.org.uk>
Cc: Maxime Chevallier <maxime.chevallier@bootlin.com>,
Luiz Angelo Daros de Luca <luizluca@gmail.com>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
Date: Thu, 16 Jul 2026 08:40:15 +0200 [thread overview]
Message-ID: <e252cb3c-bfb2-4d62-aec7-e04b9aafe223@yahoo.com> (raw)
In-Reply-To: <20260711-rtl8367s-sgmii-v6-1-88f7944ddca7@c127.dev>
Hi Johan
On 7/12/2026 6:31 AM, Johan Alvarado via B4 Relay wrote:
> From: Johan Alvarado <contact@c127.dev>
>
> The RTL8367S can mux its embedded SerDes to external interface 1,
> which is typically used to connect the switch to a CPU port. The chip
> info table already declares SGMII as a supported interface mode for
> this chip, but the driver only implements RGMII so far.
>
> Implement SGMII support as a phylink PCS, with the configuration
> sequence derived from the GPL-licensed Realtek rtl8367c vendor driver
> as distributed in the Mercusys MR80X GPL code drop:
>
> - Add accessors for the SerDes indirect access registers (SDS_INDACS),
> through which the SerDes internal registers are reached.
>
> - Register a phylink_pcs for the SerDes, selected from mac_select_pcs
> for the SGMII interface, so the SerDes handling lives in the PCS
> operations rather than in the MAC operations.
>
> - Probe the SerDes tuning variant from the chip option register once
> at setup. The vendor driver keeps two sets of SerDes tuning
> parameters and selects between them based on this option; only the
> variant for a non-zero option (which all RTL8367S parts seen so far
> report) has been validated on hardware, so the SerDes interface
> modes are only advertised in that case. An unsupported variant thus
> fails at phylink validation time instead of at link configuration
> time.
>
> - Keep the embedded DW8051 microcontroller in reset and disabled. The
> vendor driver loads firmware into it to manage the SerDes link, but
> analysis of that firmware shows it only duplicates the link
> management phylink already performs: it polls the port status and
> writes the external interface force registers behind the driver's
> back.
>
> - Clear the line rate bypass bit for the external interface, tune the
> SerDes with the vendor-prescribed parameters, mux the SerDes to MAC8
> in SGMII mode and only then take the SerDes out of reset, as the
> vendor driver does.
>
> - After deasserting the SerDes reset, reset the SerDes data path via
> the SerDes BMCR register to flush the FIFOs and resync the PLL.
> This mirrors what the vendor firmware does right after deasserting
> the SerDes reset, and ensures a clean link state from cold boot.
>
> - Force the SGMII link parameters (link, speed, duplex) in the SDS_MISC
> register from pcs_link_up(). SGMII in-band autonegotiation is not
> implemented, so only fixed-link and conventional PHY setups are
> supported, just like RGMII. This is reported to phylink through
> pcs_inband_caps() returning LINK_INBAND_DISABLE, so phylink never
> selects an in-band-enabled negotiation mode for this PCS.
>
> - Program the SerDes pause enables in SDS_MISC from the resolved
> pause modes when forcing the MAC external interface in mac_link_up,
> as the vendor driver does, rather than leaving whatever state the
> boot firmware left there. Flow control testing shows these bits,
> not the MAC force pause bits, gate pause on the SerDes external
> interface. This is done in the MAC layer because pcs_link_up()
> carries no pause information.
>
> - Implement pcs_get_state() by reading the link status from the
> SerDes, with the forced speed and duplex read back from SDS_MISC.
> Although the supported fixed-link and conventional PHY setups do not
> use it, the PCS owns the SerDes link state, and phylink consults
> pcs_get_state() to track the physical link when operating in in-band
> mode with autonegotiation disabled. The SerDes has no link interrupt
> wired up, so the PCS sets its poll flag.
>
> Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
> the SoC over SGMII.
>
> Suggested-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Suggested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
> Signed-off-by: Johan Alvarado <contact@c127.dev>
> ---
> drivers/net/dsa/realtek/rtl8365mb_main.c | 515 ++++++++++++++++++++++++++++++-
> 1 file changed, 511 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
> index 5ac091bf93c9..ea03c42d0f1a 100644
> --- a/drivers/net/dsa/realtek/rtl8365mb_main.c
> +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
[...]
> +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 int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
> + struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
> + struct realtek_priv *priv;
> + u16 val;
> + int ret;
> + int i;
> +
> + priv = mb->priv;
> +
> + /* 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
> + * already does: it polls the port status and forces the external
> + * interface configuration in the very registers this driver manages.
> + * Letting it run would race with phylink.
> + */
> + ret = regmap_update_bits(priv->map, RTL8365MB_CHIP_RESET_REG,
> + RTL8365MB_CHIP_RESET_DW8051_MASK,
> + RTL8365MB_CHIP_RESET_DW8051_MASK);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0_REG,
> + RTL8365MB_MISC_CFG0_DW8051_EN_MASK, 0);
> + if (ret)
> + return ret;
> +
> + /* The vendor driver clears the line rate bypass for all interface
> + * modes except TMII.
> + */
> + ret = regmap_update_bits(priv->map, RTL8365MB_BYPASS_LINE_RATE_REG,
> + RTL8365MB_SDS_BYPASS_LINE_RATE_MASK, 0);
> + if (ret)
> + 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);
> + if (ret)
> + return ret;
> + }
> +
> + /* Mux the SerDes to MAC8 in SGMII 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);
> + if (ret)
> + return ret;
> +
> + val = RTL8365MB_EXT_PORT_MODE_SGMII
> + << 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),
> + val);
> + if (ret)
> + return ret;
> +
> + /* Take the SerDes out of reset. The vendor driver does this only
> + * after the SerDes mux and the interface mode are configured.
> + */
> + ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_RESET,
> + RTL8365MB_SDS_RESET_DEASSERT);
> + if (ret)
> + return ret;
> +
> + /* Reset the SerDes data path and resync its PLL, mirroring what the
> + * vendor firmware does right after deasserting the SerDes reset.
> + * This flushes the FIFOs and ensures a clean state for the link,
> + * preventing silent drops and CRC errors.
> + */
> + ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
> + RTL8365MB_SDS_BMCR_DPRST_PHASE1);
> + if (ret)
> + return ret;
> +
> + ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
> + RTL8365MB_SDS_BMCR_DPRST_PHASE2);
> + if (ret)
> + return ret;
> +
> + /* Keep SGMII in-band autonegotiation disabled: the link parameters are
> + * forced from rtl8365mb_pcs_link_up() instead.
> + */
> + ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_NWAY, &val);
> + if (ret)
> + return ret;
> +
> + val &= ~RTL8365MB_SDS_NWAY_EN_MASK;
> + val |= RTL8365MB_SDS_NWAY_RESTART_MASK;
> +
> + return rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_NWAY, val);
> +}
For verification on real hardware:
The SGMII/HSGMII link on RTL8367S (and compatible RTL8365MB-VC) can be
intermittently unstable after cold boot, manifesting as egress stalls,
CRC errors or complete packet loss on the SerDes-attached CPU port.
This has been observed as a non-deterministic failure depending on the
PLL lock state at power-on.
Reverse-engineering of the vendor DW8051 firmware blob (Sgmii_Init[])
shows that the vendor driver performs two critical steps after the
SerDes data-path reset (BMCR DPRST_PHASE2) which were missing from the
Linux driver:
1. A ~98 ms delay to let the SerDes PLL fully lock before any further
register access. The vendor firmware uses a timer interrupt to count
this delay; without it the analog front-end may still be settling.
2. Writing a "Local Jam Table" calibration vector to internal ASIC
registers 0x060C-0x060F (values 0x83, 0xAA, 0x7E, 0x80). These
registers configure the SerDes analog equalizer and DC-offset and
are not exposed through the normal SDS_INDACS window. Omitting them
leaves the analog front-end in an uncalibrated state.
Add both steps to rtl8365mb_pcs_config(), immediately after the BMCR
phase-2 data-path reset and before the NWAY configuration. This mirrors
the exact vendor firmware bring-up sequence and eliminates the cold-boot
race.
The 98 ms delay matches the vendor firmware timeout constant
(SGMII_TIMEOUT_98MS). It is a one-time cost during interface bring-up
and ensures reliable link establishment regardless of the PLL state at
reset.
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -95,6 +95,7 @@
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/phylink.h>
+#include <linux/delay.h>
#include "realtek.h"
#include "realtek-smi.h"
@@ -1235,6 +1236,21 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
if (ret)
return ret;
+ /* The vendor firmware waits ~98 ms here before writing the
+ * calibration vector.
+ */
+ usleep_range(98000, 98500);
+
+ /* Vendor firmware writes a "Local Jam Table" to internal ASIC
+ * registers 0x060C-0x060F after the data path reset to calibrate
+ * the SerDes analog front-end. Without this, the link can be
+ * unstable after cold boot.
+ */
+ regmap_write(priv->map, 0x060C, 0x0083);
+ regmap_write(priv->map, 0x060D, 0x00AA);
+ regmap_write(priv->map, 0x060E, 0x007E);
+ regmap_write(priv->map, 0x060F, 0x0080);
+
/* Keep SGMII in-band autonegotiation disabled: the link parameters are
* forced from rtl8365mb_pcs_link_up() instead.
*/
next prev parent reply other threads:[~2026-07-16 6:41 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-12 4:31 [PATCH net-next v6 0/2] net: dsa: realtek: rtl8365mb: add SGMII/HSGMII support for RTL8367S Johan Alvarado via B4 Relay
2026-07-12 4:31 ` Johan Alvarado
2026-07-12 4:31 ` [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII " Johan Alvarado via B4 Relay
2026-07-12 4:31 ` Johan Alvarado
2026-07-12 8:23 ` Maxime Chevallier
2026-07-16 6:40 ` Mieczyslaw Nalewaj [this message]
2026-07-17 12:51 ` Stanislaw
2026-07-17 16:28 ` Mieczyslaw Nalewaj
2026-07-12 4:31 ` [PATCH net-next v6 2/2] net: dsa: realtek: rtl8365mb: add HSGMII " Johan Alvarado via B4 Relay
2026-07-12 4:31 ` Johan Alvarado
2026-07-12 18:05 ` Mieczyslaw Nalewaj
2026-07-14 0:24 ` Johan Alvarado
2026-07-13 8:14 ` Stacho_P
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e252cb3c-bfb2-4d62-aec7-e04b9aafe223@yahoo.com \
--to=namiltd@yahoo.com \
--cc=alsi@bang-olufsen.dk \
--cc=andrew@lunn.ch \
--cc=contact@c127.dev \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=luizluca@gmail.com \
--cc=maxime.chevallier@bootlin.com \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.