From c9e934ffd87774a64fa0c8a2af92373ef1d0894f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 3 Jun 2025 14:57:09 -0400 Subject: [PATCH] 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 Link: https://github.com/openwrt/openwrt/pull/18935 Signed-off-by: Robert Marko --- .../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 8d303480825..c38bc869247 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", -- 2.47.2