Netdev List
 help / color / mirror / Atom feed
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.
 	 */


  parent reply	other threads:[~2026-07-16  6:41 UTC|newest]

Thread overview: 8+ 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 ` [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII " Johan Alvarado via B4 Relay
2026-07-12  8:23   ` Maxime Chevallier
2026-07-16  6:40   ` Mieczyslaw Nalewaj [this message]
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 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox