This patch series addresses a regression in Energy Efficient Ethernet (EEE) handling for KSZ switches with integrated PHYs, introduced in kernel v6.9 by commit fe0d4fd9285e ("net: phy: Keep track of EEE configuration").
The first patch updates the DSA driver to allow phylink to properly manage PHY EEE configuration. Since integrated PHYs handle LPI internally and ports without integrated PHYs do not document MAC-level LPI support, dummy MAC LPI callbacks are provided.
The second patch removes outdated EEE workarounds from the micrel PHY driver, as they are no longer needed with correct phylink handling.
This series addresses the regression for mainline and kernels starting from v6.14. It is not easily possible to fully fix older kernels due to missing infrastructure changes.
Tested on KSZ9893 hardware.
Oleksij Rempel (2): net: dsa: microchip: let phylink manage PHY EEE configuration on KSZ switches net: phy: micrel: remove KSZ9477 EEE quirks now handled by phylink
drivers/net/dsa/microchip/ksz_common.c | 97 ++++++++++++++++++-------- drivers/net/phy/micrel.c | 7 -- include/linux/micrel_phy.h | 1 - 3 files changed, 69 insertions(+), 36 deletions(-)
-- 2.39.5
Phylink expects MAC drivers to provide LPI callbacks to properly manage Energy Efficient Ethernet (EEE) configuration. On KSZ switches with integrated PHYs, LPI is internally handled by hardware, while ports without integrated PHYs have no documented MAC-level LPI support.
Provide dummy mac_disable_tx_lpi() and mac_enable_tx_lpi() callbacks to satisfy phylink requirements. Also, set default EEE capabilities during phylink initialization where applicable.
Since phylink can now gracefully handle optional EEE configuration, remove the need for the MICREL_NO_EEE PHY flag.
This change addresses issues caused by incomplete EEE refactoring introduced in commit fe0d4fd9285e ("net: phy: Keep track of EEE configuration"). It is not easily possible to fix all older kernels, but this patch ensures proper behavior on latest kernels and can be considered for backporting to stable kernels starting from v6.14.
Fixes: fe0d4fd9285e ("net: phy: Keep track of EEE configuration") Signed-off-by: Oleksij Rempel o.rempel@pengutronix.de Cc: stable@vger.kernel.org # v6.14+ --- drivers/net/dsa/microchip/ksz_common.c | 97 ++++++++++++++++++-------- 1 file changed, 69 insertions(+), 28 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b45052497f8a..f4b928e54c5b 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -265,16 +265,48 @@ static void ksz_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface);
+/** + * ksz_phylink_mac_disable_tx_lpi() - Dummy handler to disable TX LPI + * @config: phylink config structure + * + * For ports with integrated PHYs, LPI is managed internally by hardware. + * Ports without integrated PHYs do not document MAC-level LPI support. + * No software action is needed. + */ +static void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config) +{ +} + +/** + * ksz_phylink_mac_enable_tx_lpi() - Dummy handler to enable TX LPI + * @config: phylink config structure + * @timer: timer value before entering LPI + * @tx_clock_stop: whether to stop the TX clock in LPI mode + * + * For ports with integrated PHYs, LPI is managed internally by hardware. + * Ports without integrated PHYs do not document MAC-level LPI support. + * Always return success. + */ +static int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer, bool tx_clock_stop) +{ + return 0; +} + static const struct phylink_mac_ops ksz88x3_phylink_mac_ops = { .mac_config = ksz88x3_phylink_mac_config, .mac_link_down = ksz_phylink_mac_link_down, .mac_link_up = ksz8_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, };
static const struct phylink_mac_ops ksz8_phylink_mac_ops = { .mac_config = ksz_phylink_mac_config, .mac_link_down = ksz_phylink_mac_link_down, .mac_link_up = ksz8_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, };
static const struct ksz_dev_ops ksz88xx_dev_ops = { @@ -358,6 +390,8 @@ static const struct phylink_mac_ops ksz9477_phylink_mac_ops = { .mac_config = ksz_phylink_mac_config, .mac_link_down = ksz_phylink_mac_link_down, .mac_link_up = ksz9477_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, };
static const struct ksz_dev_ops ksz9477_dev_ops = { @@ -401,6 +435,8 @@ static const struct phylink_mac_ops lan937x_phylink_mac_ops = { .mac_config = ksz_phylink_mac_config, .mac_link_down = ksz_phylink_mac_link_down, .mac_link_up = ksz9477_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, };
static const struct ksz_dev_ops lan937x_dev_ops = { @@ -2016,6 +2052,18 @@ static void ksz_phylink_get_caps(struct dsa_switch *ds, int port,
if (dev->dev_ops->get_caps) dev->dev_ops->get_caps(dev, port, config); + + if (ds->ops->support_eee && ds->ops->support_eee(ds, port)) { + memcpy(config->lpi_interfaces, config->supported_interfaces, + sizeof(config->lpi_interfaces)); + + config->lpi_capabilities = MAC_100FD; + if (dev->info->gbit_capable[port]) + config->lpi_capabilities |= MAC_1000FD; + + /* EEE is fully operational */ + config->eee_enabled_default = true; + } }
void ksz_r_mib_stats64(struct ksz_device *dev, int port) @@ -3008,31 +3056,6 @@ static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port) if (!port) return MICREL_KSZ8_P1_ERRATA; break; - case KSZ8567_CHIP_ID: - /* KSZ8567R Errata DS80000752C Module 4 */ - case KSZ8765_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8795_CHIP_ID: - /* KSZ879x/KSZ877x/KSZ876x Errata DS80000687C Module 2 */ - case KSZ9477_CHIP_ID: - /* KSZ9477S Errata DS80000754A Module 4 */ - case KSZ9567_CHIP_ID: - /* KSZ9567S Errata DS80000756A Module 4 */ - case KSZ9896_CHIP_ID: - /* KSZ9896C Errata DS80000757A Module 3 */ - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - /* KSZ9897R Errata DS80000758C Module 4 */ - /* Energy Efficient Ethernet (EEE) feature select must be manually disabled - * The EEE feature is enabled by default, but it is not fully - * operational. It must be manually disabled through register - * controls. If not disabled, the PHY ports can auto-negotiate - * to enable EEE, and this feature can cause link drops when - * linked to another device supporting EEE. - * - * The same item appears in the errata for all switches above. - */ - return MICREL_NO_EEE; }
return 0; @@ -3475,15 +3498,33 @@ static bool ksz_support_eee(struct dsa_switch *ds, int port)
switch (dev->chip_id) { case KSZ8563_CHIP_ID: + case KSZ9563_CHIP_ID: + case KSZ9893_CHIP_ID: + return true; case KSZ8567_CHIP_ID: + /* KSZ8567R Errata DS80000752C Module 4 */ + case KSZ8765_CHIP_ID: + case KSZ8794_CHIP_ID: + case KSZ8795_CHIP_ID: + /* KSZ879x/KSZ877x/KSZ876x Errata DS80000687C Module 2 */ case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: + /* KSZ9477S Errata DS80000754A Module 4 */ case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: + /* KSZ9567S Errata DS80000756A Module 4 */ case KSZ9896_CHIP_ID: + /* KSZ9896C Errata DS80000757A Module 3 */ case KSZ9897_CHIP_ID: case LAN9646_CHIP_ID: - return true; + /* KSZ9897R Errata DS80000758C Module 4 */ + /* Energy Efficient Ethernet (EEE) feature select must be manually disabled + * The EEE feature is enabled by default, but it is not fully + * operational. It must be manually disabled through register + * controls. If not disabled, the PHY ports can auto-negotiate + * to enable EEE, and this feature can cause link drops when + * linked to another device supporting EEE. + * + * The same item appears in the errata for all switches above. + */ }
return false;
+/**
- ksz_phylink_mac_disable_tx_lpi() - Dummy handler to disable TX LPI
- @config: phylink config structure
- For ports with integrated PHYs, LPI is managed internally by hardware.
Could you expand that.
Does it mean the hardware will look at the results of the autoneg and disable/enable LPI depending on those results? I also assume this means it is not possible to force LPI on/off, independent of autoneg?
Andrew
On Mon, Apr 28, 2025 at 06:51:19PM +0200, Andrew Lunn wrote:
+/**
- ksz_phylink_mac_disable_tx_lpi() - Dummy handler to disable TX LPI
- @config: phylink config structure
- For ports with integrated PHYs, LPI is managed internally by hardware.
Could you expand that.
Does it mean the hardware will look at the results of the autoneg and disable/enable LPI depending on those results?
Yes.
I also assume this means it is not possible to force LPI on/off, independent of autoneg?
Correct. set_eee call in this driver is filtering (tx_lpi == false) to reflect HW functionality.
On Mon, Apr 28, 2025 at 08:07:04PM +0200, Oleksij Rempel wrote:
On Mon, Apr 28, 2025 at 06:51:19PM +0200, Andrew Lunn wrote:
+/**
- ksz_phylink_mac_disable_tx_lpi() - Dummy handler to disable TX LPI
- @config: phylink config structure
- For ports with integrated PHYs, LPI is managed internally by hardware.
Could you expand that.
Does it mean the hardware will look at the results of the autoneg and disable/enable LPI depending on those results?
Yes.
I also assume this means it is not possible to force LPI on/off, independent of autoneg?
Correct. set_eee call in this driver is filtering (tx_lpi == false) to reflect HW functionality.
I'll update this patch to include this information.
The KSZ9477 PHY driver contained workarounds for broken EEE capability advertisements by manually masking supported EEE modes and forcibly disabling EEE if MICREL_NO_EEE was set.
With proper MAC-side EEE handling implemented via phylink, these quirks are no longer necessary. Remove MICREL_NO_EEE handling and the use of ksz9477_get_features().
This simplifies the PHY driver and avoids duplicated EEE management logic.
Signed-off-by: Oleksij Rempel o.rempel@pengutronix.de Cc: stable@vger.kernel.org # v6.14+ --- drivers/net/phy/micrel.c | 7 ------- include/linux/micrel_phy.h | 1 - 2 files changed, 8 deletions(-)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 71fb4410c31b..c2e5be404f07 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -2027,12 +2027,6 @@ static int ksz9477_config_init(struct phy_device *phydev) return err; }
- /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes - * in this switch shall be regarded as broken. - */ - if (phydev->dev_flags & MICREL_NO_EEE) - phy_disable_eee(phydev); - return kszphy_config_init(phydev); }
@@ -5698,7 +5692,6 @@ static struct phy_driver ksphy_driver[] = { .handle_interrupt = kszphy_handle_interrupt, .suspend = genphy_suspend, .resume = ksz9477_resume, - .get_features = ksz9477_get_features, } };
module_phy_driver(ksphy_driver); diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h index 591bf5b5e8dc..9af01bdd86d2 100644 --- a/include/linux/micrel_phy.h +++ b/include/linux/micrel_phy.h @@ -44,7 +44,6 @@ #define MICREL_PHY_50MHZ_CLK BIT(0) #define MICREL_PHY_FXEN BIT(1) #define MICREL_KSZ8_P1_ERRATA BIT(2) -#define MICREL_NO_EEE BIT(3)
#define MICREL_KSZ9021_EXTREG_CTRL 0xB #define MICREL_KSZ9021_EXTREG_DATA_WRITE 0xC -- 2.39.5
linux-stable-mirror@lists.linaro.org