[openwrt/openwrt] realtek: 6.12: refactor EEE for RTL8218B/D and RTL8214FC

LEDE Commits lede-commits at lists.infradead.org
Wed Jun 11 13:27:54 PDT 2025


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/c9e934ffd87774a64fa0c8a2af92373ef1d0894f

commit c9e934ffd87774a64fa0c8a2af92373ef1d0894f
Author: Markus Stockhausen <markus.stockhausen at gmx.de>
AuthorDate: Tue Jun 3 14:57:09 2025 -0400

    realtek: 6.12: refactor EEE for RTL8218B/D and RTL8214FC
    
    Three different code paths for the same phy model. Now the bus
    is prepared to handle c45 (mmd) read/writes correctly. Remove
    the custom implementations and let generic kernel functions do
    their best. To achieve this
    
    - disable the PHY-mode EEE in rtl821x_config_init() as upstream does
    - provide mmd read/write functions that avoid EEE via c45 over c22
    
    While chaning the phy_driver functions sort them alphabetically.
    
    Signed-off-by: Markus Stockhausen <markus.stockhausen at gmx.de>
    Link: https://github.com/openwrt/openwrt/pull/18935
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 .../files-6.12/drivers/net/phy/rtl83xx-phy.c       | 297 +++++----------------
 1 file changed, 71 insertions(+), 226 deletions(-)

diff --git a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
index 8d30348082..c38bc86924 100644
--- a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
+++ b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
@@ -52,6 +52,8 @@ extern int phy_port_read_paged(struct phy_device *phydev, int port, int page, u3
 #define RTL821XINT_MEDIA_PAGE_SELECT	0x1d
 /* external RTL821X PHY uses register 0x1e to select media page */
 #define RTL821XEXT_MEDIA_PAGE_SELECT	0x1e
+#define RTL821X_PHYCR2			0x19
+#define RTL821X_PHYCR2_PHY_EEE_ENABLE	BIT(5)
 
 #define RTL821X_CHIP_ID			0x6276
 
@@ -1160,231 +1162,61 @@ static int rtl8214fc_config_aneg(struct phy_device *phydev)
 	return ret;
 }
 
-/* Enable EEE on the RTL8218B PHYs
- * The method used is not the preferred way (which would be based on the MAC-EEE state,
- * but the only way that works since the kernel first enables EEE in the MAC
- * and then sets up the PHY. The MAC-based approach would require the oppsite.
- */
-static void rtl8218d_eee_set(struct phy_device *phydev, bool enable)
-{
-	u32 val;
-	bool an_enabled;
-
-	pr_debug("In %s %d, enable %d\n", __func__, phydev->mdio.addr, enable);
-	/* Set GPHY page to copper */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER);
-
-	val = phy_read(phydev, MII_BMCR);
-	an_enabled = val & BMCR_ANENABLE;
-
-	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
-	val |= MDIO_EEE_1000T | MDIO_EEE_100TX;
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, enable ? (MDIO_EEE_100TX | MDIO_EEE_1000T) : 0);
-
-	/* 500M EEE ability */
-	val = phy_read_paged(phydev, RTL821X_PAGE_GPHY, 20);
-	if (enable)
-		val |= BIT(7);
-	else
-		val &= ~BIT(7);
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, 20, val);
-
-	/* Restart AN if enabled */
-	if (an_enabled) {
-		val = phy_read(phydev, MII_BMCR);
-		val |= BMCR_ANRESTART;
-		phy_write(phydev, MII_BMCR, val);
-	}
-
-	/* GPHY page back to auto */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO);
-}
-
-static int rtl8218b_get_eee(struct phy_device *phydev, struct ethtool_keee *e)
-{
-	u32 val;
-	int addr = phydev->mdio.addr;
-
-	pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
-
-	/* Set GPHY page to copper */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER);
-
-	val = phy_read_paged(phydev, 7, MDIO_AN_EEE_ADV);
-	if (e->eee_enabled) {
-		/* Verify vs MAC-based EEE */
-		e->eee_enabled = !!(val & BIT(7));
-		if (!e->eee_enabled) {
-			val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25);
-			e->eee_enabled = !!(val & BIT(4));
-		}
-	}
-	pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
-
-	/* GPHY page to auto */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO);
-
-	return 0;
-}
-
-static int rtl8218d_get_eee(struct phy_device *phydev, struct ethtool_keee *e)
+static int rtl821x_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
 {
-	u32 val;
+	struct mii_bus *bus = phydev->mdio.bus;
 	int addr = phydev->mdio.addr;
+	int ret;
 
-	pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
-
-	/* Set GPHY page to copper */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER);
-
-	val = phy_read_paged(phydev, 7, MDIO_AN_EEE_ADV);
-	if (e->eee_enabled)
-		e->eee_enabled = !!(val & BIT(7));
-	pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
-
-	/* GPHY page to auto */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO);
-
-	return 0;
-}
-
-static int rtl8214fc_set_eee(struct phy_device *phydev, struct ethtool_keee *e)
-{
-	u32 poll_state;
-	int port = phydev->mdio.addr;
-	bool an_enabled;
-	u32 val;
-
-	pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled);
-
-	if (rtl8214fc_media_is_fibre(phydev)) {
-		netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port);
-		return -ENOTSUPP;
-	}
-
-	poll_state = disable_polling(port);
-
-	/* Set GPHY page to copper */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER);
-
-	/* Get auto-negotiation status */
-	val = phy_read(phydev, MII_BMCR);
-	an_enabled = val & BMCR_ANENABLE;
-
-	pr_info("%s: aneg: %d\n", __func__, an_enabled);
-	val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25);
-	val &= ~BIT(5);  /* Use MAC-based EEE */
-	phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val);
-
-	/* Enable 100M (bit 1) / 1000M (bit 2) EEE */
-	phy_write_paged(phydev, 7, MDIO_AN_EEE_ADV, e->eee_enabled ? (MDIO_EEE_100TX | MDIO_EEE_1000T) : 0);
+	/*
+	 * The RTL821x PHYs are usually only C22 capable and are defined accordingly in DTS.
+	 * Nevertheless GPL source drops clearly indicate that EEE features can be accessed
+	 * directly via C45. Testing shows that C45 over C22 (as used in kernel EEE framework)
+	 * works as well but only as long as PHY polling is disabled in the SOC. To avoid ugly
+	 * hacks pass through C45 accesses for important EEE registers. Maybe some day the mdio
+	 * bus can intercept these patterns and switch off/on polling on demand. That way this
+	 * phy device driver can avoid handling special cases on its own.
+	 */
 
-	/* 500M EEE ability */
-	val = phy_read_paged(phydev, RTL821X_PAGE_GPHY, 20);
-	if (e->eee_enabled)
-		val |= BIT(7);
+	if ((devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) ||
+	    (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) ||
+	    (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE))
+		ret = __mdiobus_c45_read(bus, addr, devnum, regnum);
 	else
-		val &= ~BIT(7);
-
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, 20, val);
-
-	/* Restart AN if enabled */
-	if (an_enabled) {
-		pr_info("%s: doing aneg\n", __func__);
-		val = phy_read(phydev, MII_BMCR);
-		val |= BMCR_ANRESTART;
-		phy_write(phydev, MII_BMCR, val);
-	}
-
-	/* GPHY page back to auto */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO);
-
-	resume_polling(poll_state);
+		ret = -EOPNOTSUPP;
 
-	return 0;
+	return ret;
 }
 
-static int rtl8214fc_get_eee(struct phy_device *phydev, struct ethtool_keee *e)
+static int rtl821x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val)
 {
+	struct mii_bus *bus = phydev->mdio.bus;
 	int addr = phydev->mdio.addr;
+	int ret;
 
-	pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled);
-	if (rtl8214fc_media_is_fibre(phydev)) {
-		netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr);
-		return -ENOTSUPP;
-	}
+	/* see rtl821x_read_mmd() */
+	if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV)
+		ret = __mdiobus_c45_write(bus, addr, devnum, regnum, val);
+	else
+		ret = -EOPNOTSUPP;
 
-	return rtl8218b_get_eee(phydev, e);
+	return ret;
 }
 
-static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_keee *e)
+static int rtl8214fc_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
 {
-	int port = phydev->mdio.addr;
-	u64 poll_state;
-	u32 val;
-	bool an_enabled;
-
-	pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
-
-	poll_state = disable_polling(port);
-
-	/* Set GPHY page to copper */
-	phy_write(phydev, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER);
-	val = phy_read(phydev, MII_BMCR);
-	an_enabled = val & BMCR_ANENABLE;
-
-	if (e->eee_enabled) {
-		/* 100/1000M EEE Capability */
-		phy_write(phydev, 13, 0x0007);
-		phy_write(phydev, 14, 0x003C);
-		phy_write(phydev, 13, 0x4007);
-		phy_write(phydev, 14, 0x0006);
-
-		val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25);
-		val |= BIT(4);
-		phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val);
-	} else {
-		/* 100/1000M EEE Capability */
-		phy_write(phydev, 13, 0x0007);
-		phy_write(phydev, 14, 0x003C);
-		phy_write(phydev, 13, 0x0007);
-		phy_write(phydev, 14, 0x0000);
-
-		val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25);
-		val &= ~BIT(4);
-		phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val);
-	}
-
-	/* Restart AN if enabled */
-	if (an_enabled) {
-		val = phy_read(phydev, MII_BMCR);
-		val |= BMCR_ANRESTART;
-		phy_write(phydev, MII_BMCR, val);
-	}
-
-	/* GPHY page back to auto */
-	phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO);
-
-	pr_info("%s done\n", __func__);
-	resume_polling(poll_state);
+	if (__rtl8214fc_media_is_fibre(phydev))
+		return -EOPNOTSUPP;
 
-	return 0;
+	return rtl821x_read_mmd(phydev, devnum, regnum);
 }
 
-static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_keee *e)
+static int rtl8214fc_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val)
 {
-	int addr = phydev->mdio.addr;
-	u64 poll_state;
-
-	pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
-
-	poll_state = disable_polling(addr);
-
-	rtl8218d_eee_set(phydev, (bool) e->eee_enabled);
-
-	resume_polling(poll_state);
+	if (__rtl8214fc_media_is_fibre(phydev))
+		return -EOPNOTSUPP;
 
-	return 0;
+	return rtl821x_write_mmd(phydev, devnum, regnum, val);
 }
 
 static int rtl8380_configure_rtl8214c(struct phy_device *phydev)
@@ -3884,6 +3716,15 @@ static int rtl8218d_phy_probe(struct phy_device *phydev)
 	return 0;
 }
 
+static int rtl821x_config_init(struct phy_device *phydev)
+{
+	/* Disable PHY-mode EEE so LPI is passed to the MAC */
+	phy_modify_paged(phydev, RTL821X_PAGE_MAC, RTL821X_PHYCR2,
+			 RTL821X_PHYCR2_PHY_EEE_ENABLE, 0);
+
+	return 0;
+}
+
 static int rtl838x_serdes_probe(struct phy_device *phydev)
 {
 	int addr = phydev->mdio.addr;
@@ -3955,41 +3796,57 @@ static struct phy_driver rtl83xx_phy_driver[] = {
 		.match_phy_device = rtl8214fc_match_phy_device,
 		.name		= "Realtek RTL8214FC",
 		.config_aneg	= rtl8214fc_config_aneg,
-		.get_eee	= rtl8214fc_get_eee,
+		.config_init	= rtl821x_config_init,
 		.get_features	= rtl8214fc_get_features,
 		.get_tunable    = rtl8214fc_get_tunable,
 		.probe		= rtl8214fc_phy_probe,
+		.read_mmd	= rtl8214fc_read_mmd,
 		.read_page	= rtl821x_read_page,
 		.read_status    = rtl8214fc_read_status,
 		.resume		= rtl8214fc_resume,
-		.set_eee	= rtl8214fc_set_eee,
 		.set_tunable	= rtl8214fc_set_tunable,
 		.suspend	= rtl8214fc_suspend,
+		.write_mmd	= rtl8214fc_write_mmd,
 		.write_page	= rtl821x_write_page,
 	},
 	{
 		.match_phy_device = rtl8218b_ext_match_phy_device,
 		.name		= "Realtek RTL8218B (external)",
+		.config_init	= rtl821x_config_init,
 		.features	= PHY_GBIT_FEATURES,
 		.probe		= rtl8218b_ext_phy_probe,
+		.read_mmd	= rtl821x_read_mmd,
 		.read_page	= rtl821x_read_page,
-		.write_page	= rtl821x_write_page,
+		.resume		= genphy_resume,
 		.suspend	= genphy_suspend,
+		.write_mmd	= rtl821x_write_mmd,
+		.write_page	= rtl821x_write_page,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
+		.name		= "Realtek RTL8218B (internal)",
+		.config_init	= rtl821x_config_init,
+		.features	= PHY_GBIT_FEATURES,
+		.probe		= rtl8218b_int_phy_probe,
+		.read_mmd	= rtl821x_read_mmd,
+		.read_page	= rtl821x_read_page,
 		.resume		= genphy_resume,
-		.set_eee	= rtl8218b_set_eee,
-		.get_eee	= rtl8218b_get_eee,
+		.suspend	= genphy_suspend,
+		.write_mmd	= rtl821x_write_mmd,
+		.write_page	= rtl821x_write_page,
 	},
 	{
 		PHY_ID_MATCH_EXACT(PHY_ID_RTL8218D),
 		.name		= "REALTEK RTL8218D",
+		.config_init	= rtl821x_config_init,
 		.features	= PHY_GBIT_FEATURES,
 		.probe		= rtl8218d_phy_probe,
+		.read_mmd	= rtl821x_read_mmd,
 		.read_page	= rtl821x_read_page,
-		.write_page	= rtl821x_write_page,
-		.suspend	= genphy_suspend,
 		.resume		= genphy_resume,
-		.set_eee	= rtl8218d_set_eee,
-		.get_eee	= rtl8218d_get_eee,
+		.suspend	= genphy_suspend,
+		.write_mmd	= rtl821x_write_mmd,
+		.write_page	= rtl821x_write_page,
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8221B),
@@ -4017,18 +3874,6 @@ static struct phy_driver rtl83xx_phy_driver[] = {
 		.set_eee	= rtl8226_set_eee,
 		.get_eee	= rtl8226_get_eee,
 	},
-	{
-		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
-		.name		= "Realtek RTL8218B (internal)",
-		.features	= PHY_GBIT_FEATURES,
-		.probe		= rtl8218b_int_phy_probe,
-		.read_page	= rtl821x_read_page,
-		.write_page	= rtl821x_write_page,
-		.suspend	= genphy_suspend,
-		.resume		= genphy_resume,
-		.set_eee	= rtl8218b_set_eee,
-		.get_eee	= rtl8218b_get_eee,
-	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
 		.name		= "Realtek RTL8380 SERDES",




More information about the lede-commits mailing list