From: Konrad Knitter <[email protected]>

The ethtool setter uses the link-partner AN ability bit to decide whether a
request to disable autonegotiation can be handled. That bit is valid only
while the link is up, so a link-down request skips the configuration update
and returns success.

Allow link-down requests through to PHY capability validation. On legacy
MACs, retain the existing restriction for PHYs that advertise autoneg.
Generic MACs configure Clause 37 through low_power_ctrl_an, and must keep
AUTO_LINK_UPDT set so firmware applies the change. Handle that field for
both enable and disable requests.

Fixes: 48cb27f2fd18 ("ice: Implement handlers for ethtool PHY/link operations")
Signed-off-by: Konrad Knitter <[email protected]>
Signed-off-by: Aleksandr Loktionov <[email protected]>
---
Changes since v3:
- Allow link-down disable requests without relying on stale partner state.
- Keep the generic-MAC enable bypass; validate E830 Autoneg support before
        restoring Clause 37 when its cached bit is clear.
- Avoid marking unchanged Clause 37 requests as autoneg changes.
- Correct the Fixes tag to the commit that introduced the ethtool handler.

 drivers/net/ethernet/intel/ice/ice_ethtool.c | 57 +++++++++++++++++++-
 1 file changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c 
b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 8b7d7be..080a65a 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -2510,6 +2510,28 @@ ice_ksettings_find_adv_link_speed(const struct 
ethtool_link_ksettings *ks)
        return adv_link_speed;
 }
 
+/**
+ * ice_autoneg_disable_allowed - check if autoneg can be disabled
+ * @p: port info
+ *
+ * Check if autonegotiation can be disabled based on link state.
+ * ICE_AQ_LP_AN_ABILITY is only valid when the link is up, so let the
+ * MAC-specific handling below decide whether a link-down request is supported.
+ *
+ * Return: true if autoneg has completed, the link is down, or the link
+ *         partner does not advertise autonegotiation capability.
+ */
+static bool ice_autoneg_disable_allowed(struct ice_port_info *p)
+{
+       u8 an_info = p->phy.link_info.an_info;
+
+       if (an_info & ICE_AQ_AN_COMPLETED)
+               return true;
+       /* ICE_AQ_LP_AN_ABILITY is only valid when link is up */
+       return !(p->phy.link_info.link_info & ICE_AQ_LINK_UP) ||
+              !(an_info & ICE_AQ_LP_AN_ABILITY);
+}
+
 /**
  * ice_setup_autoneg
  * @p: port info
@@ -2535,12 +2557,31 @@ ice_setup_autoneg(struct ice_port_info *p, struct 
ethtool_link_ksettings *ks,
        if (autoneg_enabled == AUTONEG_ENABLE) {
                /* If autoneg was not already enabled */
                if (!(p->phy.link_info.an_info & ICE_AQ_AN_COMPLETED)) {
+                       if (ice_is_generic_mac(p->hw)) {
+                               if (!(config->low_power_ctrl_an &
+                                     ICE_AQC_PHY_AN_EN_CLAUSE37)) {
+                                       config->low_power_ctrl_an |=
+                                               ICE_AQC_PHY_AN_EN_CLAUSE37;
+                                       config->caps |=
+                                               ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
+                                       *autoneg_changed = 1;
+                               }
+                               return 0;
+                       }
                        /* If autoneg is not supported, return error */
                        if (!ethtool_link_ksettings_test_link_mode(ks,
                                                                   supported,
                                                                   Autoneg)) {
                                netdev_info(netdev, "Autoneg not supported on 
this phy.\n");
                                err = -EINVAL;
+                       } else if (p->hw->mac_type == ICE_MAC_E830) {
+                               if (!(config->low_power_ctrl_an &
+                                     ICE_AQC_PHY_AN_EN_CLAUSE37)) {
+                                       config->low_power_ctrl_an |=
+                                               ICE_AQC_PHY_AN_EN_CLAUSE37;
+                                       config->caps |= 
ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
+                                       *autoneg_changed = 1;
+                               }
                        } else {
                                /* Autoneg is allowed to change */
                                config->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
@@ -2548,8 +2589,20 @@ ice_setup_autoneg(struct ice_port_info *p, struct 
ethtool_link_ksettings *ks,
                        }
                }
        } else {
-               /* If autoneg is currently enabled */
-               if (p->phy.link_info.an_info & ICE_AQ_AN_COMPLETED) {
+               /* If autoneg completed or link partner does not support AN */
+               if (ice_autoneg_disable_allowed(p)) {
+                       if (ice_is_generic_mac(p->hw) ||
+                           p->hw->mac_type == ICE_MAC_E830) {
+                               if (config->low_power_ctrl_an &
+                                   ICE_AQC_PHY_AN_EN_CLAUSE37) {
+                                       config->low_power_ctrl_an &=
+                                               ~ICE_AQC_PHY_AN_EN_CLAUSE37;
+                                       config->caps |=
+                                               ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
+                                       *autoneg_changed = 1;
+                               }
+                               return 0;
+                       }
                        /* If autoneg is supported 10GBASE_T is the only PHY
                         * that can disable it, so otherwise return error
                         */
-- 
2.52.0

Reply via email to