realtek: phy: fix RTL8218E XSGMII initialization

Commit c8c187f0f0 ("realtek: add support for RTL8218E") added the
PHY identifier and reused the generic RTL821x callbacks. This is enough
when the bootloader has already configured the package, but it leaves the
MAC-side SerDes uninitialized without that setup. As a result, the XSGMII
link is not established after a cold boot and/or without any
pre-established PHY setup.

Add package-level initialization on the base PHY address and gate it on
the strapped XSGMII mode. Apply the minimal required subset of the vendor
configuration, reset the active SerDes block, and force XSGMII mode by
disabling SerDes autonegotiation. QSGMII remains unsupported.

This completes RTL8218E support without relying on bootloader PHY setup
while avoiding the large, mostly unnecessary vendor register table. This
minimal sequence has been established through various testing iterations
on two devices.

Fixes: c8c187f0f0 ("realtek: add support for RTL8218E")
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25255
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
This commit is contained in:
Jonas Jelonek committed 2026-09-18 22:31:21 +02:00
1 parent a7c5436a32
commit b6ba4e9142
1 file changed
+43 -1
@@ -449,6 +449,48 @@ static int rtl821x_config_init(struct phy_device *phydev)
return 0;
}
static int rtl8218e_config_init(struct phy_device *phydev)
{
int oldpage, oldxpage;
bool is_xsgmii;
rtl821x_config_init(phydev);
if (phydev->mdio.addr % 8)
return 0;
oldpage = phy_read(phydev, RTL821x_PAGE_SELECT);
oldxpage = phy_read(phydev, RTL821x_EXT_PAGE_SELECT);
phy_write(phydev, RTL821x_EXT_PAGE_SELECT, 0x8);
is_xsgmii = (phy_read_paged(phydev, 0x260, 0x12) & 0xf0) == 0xb0;
/* QSGMII configuration is not currently supported. */
if (!is_xsgmii)
goto restore;
/* Apply the minimal vendor XSGMII configuration, undocumented magic. */
phy_write_paged(phydev, 0x4b8, 0x10, 0x8714);
phy_write_paged(phydev, 0x4b8, 0x12, 0x7230);
phy_write_paged(phydev, 0x4be, 0x16, 0x1000);
phy_write_paged(phydev, 0x4bf, 0x11, 0x7000);
phy_write_paged(phydev, 0x4bf, 0x16, 0x0042);
phy_write_paged(phydev, 0x4ba, 0x14, 0x9d04);
/* Reset SerDes 0. */
phy_write_paged(phydev, 0x400, 0x10, 0x1700);
phy_write_paged(phydev, 0x400, 0x10, 0x1703);
/* Disable SerDes 0 XSGMII autonegotiation. */
phy_modify_paged(phydev, 0x400, 0x12, GENMASK(9, 8), BIT(8));
restore:
phy_write(phydev, RTL821x_EXT_PAGE_SELECT, oldxpage);
phy_write(phydev, RTL821x_PAGE_SELECT, oldpage);
return 0;
}
static int rtl8218d_config_init(struct phy_device *phydev)
{
int oldpage, oldxpage;
@@ -681,7 +723,7 @@ static struct phy_driver rtl83xx_phy_driver[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_RTL8218E),
.name = "REALTEK RTL8218E",
.config_init = rtl821x_config_init,
.config_init = rtl8218e_config_init,
.features = PHY_GBIT_FEATURES,
.probe = rtl8218x_phy_probe,
.read_mmd = rtl821x_read_mmd,