diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index cb306f9e80cc..bf0c6a04481e 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -1452,6 +1452,12 @@ static int bcm54811_read_status(struct phy_device *phydev) return genphy_read_status(phydev); } +static int bcm54xx_disable_autonomous_eee(struct phy_device *phydev) +{ + return bcm_phy_modify_exp(phydev, BCM54XX_TOP_MISC_MII_BUF_CNTL0, + BCM54XX_MII_BUF_CNTL0_AUTOGREEEN_EN, 0); +} + static struct phy_driver broadcom_drivers[] = { { PHY_ID_MATCH_MODEL(PHY_ID_BCM5411), @@ -1495,6 +1501,7 @@ static struct phy_driver broadcom_drivers[] = { .get_wol = bcm54xx_phy_get_wol, .set_wol = bcm54xx_phy_set_wol, .led_brightness_set = bcm_phy_led_brightness_set, + .disable_autonomous_eee = bcm54xx_disable_autonomous_eee, }, { PHY_ID_MATCH_MODEL(PHY_ID_BCM5461), .name = "Broadcom BCM5461", diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0edff47478c2..cda4abf4e68c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1375,6 +1375,14 @@ int phy_init_hw(struct phy_device *phydev) return ret; } + /* Re-apply autonomous EEE disable after soft reset */ + if (phydev->autonomous_eee_disabled && + phydev->drv->disable_autonomous_eee) { + ret = phydev->drv->disable_autonomous_eee(phydev); + if (ret) + return ret; + } + return 0; } EXPORT_SYMBOL(phy_init_hw); @@ -2898,6 +2906,20 @@ void phy_support_eee(struct phy_device *phydev) linkmode_copy(phydev->advertising_eee, phydev->supported_eee); phydev->eee_cfg.tx_lpi_enabled = true; phydev->eee_cfg.eee_enabled = true; + + /* If the PHY supports autonomous EEE, disable it so the MAC can + * manage LPI signaling instead. The flag is stored so it can be + * re-applied after a PHY soft reset (e.g. suspend/resume). + */ + if (phydev->drv && phydev->drv->disable_autonomous_eee) { + int ret = phydev->drv->disable_autonomous_eee(phydev); + + if (ret) + phydev_warn(phydev, "Failed to disable autonomous EEE: %pe\n", + ERR_PTR(ret)); + else + phydev->autonomous_eee_disabled = true; + } } EXPORT_SYMBOL(phy_support_eee); diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index bdb1221023fb..9c26531abe4a 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -726,9 +726,8 @@ static int rtl8211f_config_aldps(struct phy_device *phydev) return phy_modify(phydev, RTL8211F_PHYCR1, mask, mask); } -static int rtl8211f_config_phy_eee(struct phy_device *phydev) +static int rtl8211f_disable_autonomous_eee(struct phy_device *phydev) { - /* Disable PHY-mode EEE so LPI is passed to the MAC */ return phy_modify(phydev, RTL8211F_PHYCR2, RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); } @@ -866,7 +865,7 @@ static int rtl8211f_config_init(struct phy_device *phydev) return ret; } - return rtl8211f_config_phy_eee(phydev); + return 0; } static int rtl821x_suspend(struct phy_device *phydev) @@ -2460,6 +2459,7 @@ static struct phy_driver realtek_drvs[] = { .led_hw_is_supported = rtl8211x_led_hw_is_supported, .led_hw_control_get = rtl8211f_led_hw_control_get, .led_hw_control_set = rtl8211f_led_hw_control_set, + .disable_autonomous_eee = rtl8211f_disable_autonomous_eee, }, { PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID), .name = "RTL8211F-VD Gigabit Ethernet", @@ -2476,6 +2476,7 @@ static struct phy_driver realtek_drvs[] = { .led_hw_is_supported = rtl8211x_led_hw_is_supported, .led_hw_control_get = rtl8211f_led_hw_control_get, .led_hw_control_set = rtl8211f_led_hw_control_set, + .disable_autonomous_eee = rtl8211f_disable_autonomous_eee, }, { .name = "Generic FE-GE Realtek PHY", .match_phy_device = rtlgen_match_phy_device, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 115a964f3006..174687c4c80a 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -266,6 +266,9 @@ #define BCM54XX_TOP_MISC_IDDQ_SD (1 << 2) #define BCM54XX_TOP_MISC_IDDQ_SR (1 << 3) +#define BCM54XX_TOP_MISC_MII_BUF_CNTL0 (MII_BCM54XX_EXP_SEL_TOP + 0x00) +#define BCM54XX_MII_BUF_CNTL0_AUTOGREEEN_EN BIT(0) + #define BCM54XX_TOP_MISC_LED_CTL (MII_BCM54XX_EXP_SEL_TOP + 0x0C) #define BCM54XX_LED4_SEL_INTR BIT(1) diff --git a/include/linux/phy.h b/include/linux/phy.h index 5de4b172cd0b..199a7aaa341b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -612,6 +612,8 @@ struct phy_oatc14_sqi_capability { * @advertising_eee: Currently advertised EEE linkmodes * @enable_tx_lpi: When True, MAC should transmit LPI to PHY * @eee_active: phylib private state, indicating that EEE has been negotiated + * @autonomous_eee_disabled: Set when autonomous EEE has been disabled, + * used to re-apply after PHY soft reset * @eee_cfg: User configuration of EEE * @lp_advertising: Current link partner advertised linkmodes * @host_interfaces: PHY interface modes supported by host @@ -739,6 +741,7 @@ struct phy_device { __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_disabled_modes); bool enable_tx_lpi; bool eee_active; + bool autonomous_eee_disabled; struct eee_config eee_cfg; /* Host supported PHY interface types. Should be ignored if empty. */ @@ -1359,6 +1362,17 @@ struct phy_driver { void (*get_stats)(struct phy_device *dev, struct ethtool_stats *stats, u64 *data); + /** + * @disable_autonomous_eee: Disable PHY-autonomous EEE + * + * Some PHYs manage EEE autonomously, preventing the MAC from + * controlling LPI signaling. This callback disables autonomous + * EEE at the PHY. + * + * Return: 0 on success, negative errno on failure. + */ + int (*disable_autonomous_eee)(struct phy_device *dev); + /* Get and Set PHY tunables */ /** @get_tunable: Return the value of a tunable */ int (*get_tunable)(struct phy_device *dev,