From 66e3fed8403b1aa51659777441655bb94519badc Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Jun 2025 00:42:18 +0200 Subject: [PATCH 1/8] Revert "dts: cm5/pi5: Disable EEE on rp1" This reverts commit 85b196fde0dae6941e54290324e89d57b2d8ab90. --- arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 2 -- arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 -- 2 files changed, 4 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts index 2d56aa951fd28f..2939214f23ade4 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -175,8 +175,6 @@ rp1_target: &pcie2 { phy1: ethernet-phy@1 { reg = <0x1>; brcm,powerdown-enable; - eee-broken-1000t; - eee-broken-100tx; }; }; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi index e35d3abba20bc7..3afce9befb338f 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi @@ -166,8 +166,6 @@ rp1_target: &pcie2 { brcm,powerdown-enable; interrupt-parent = <&rp1_gpio>; interrupts = <37 IRQ_TYPE_LEVEL_LOW>; - eee-broken-1000t; - eee-broken-100tx; }; }; From 36452ce73f5525618ac9cd83b0abf05498fa85dc Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:26:03 +0100 Subject: [PATCH 2/8] net: cadence: macb: add ethtool nway_reset support Wire phy_ethtool_nway_reset() as the .nway_reset ethtool operation, allowing userspace to restart PHY autonegotiation via 'ethtool -r'. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index d06b6f2e52c863..cde9e88a5ec9cc 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -3912,6 +3912,7 @@ static const struct ethtool_ops gem_ethtool_ops = { .set_ringparam = macb_set_ringparam, .get_rxnfc = gem_get_rxnfc, .set_rxnfc = gem_set_rxnfc, + .nway_reset = phy_ethtool_nway_reset, }; static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) From 62125fb2ac85bb5085f93db97eb04027fb5de63a Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:19:04 +0100 Subject: [PATCH 3/8] net: cadence: macb: add EEE register definitions and capability flag Add register and bitfield definitions for the Cadence GEM MAC's IEEE 802.3az Energy Efficient Ethernet (EEE) support: - LPI statistics counter registers (GEM_RXLPI, GEM_RXLPITIME, GEM_TXLPI, GEM_TXLPITIME) at offsets 0x270-0x27c - TX LPI enable bitfield (GEM_TXLPIEN) in the NCR register (bit 19), which directly asserts/deasserts LPI on the transmit path - MACB_CAPS_EEE capability flag to gate EEE support per platform These registers are present in all Cadence GEM revisions that support EEE (verified on SAMA5D2, SAME70, PIC32CZ, and RP1 variants). No functional change. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index f797244f576416..8ffe48c1dc5b33 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -176,6 +176,10 @@ #define GEM_PCSANNPTX 0x021c /* PCS AN Next Page TX */ #define GEM_PCSANNPLP 0x0220 /* PCS AN Next Page LP */ #define GEM_PCSANEXTSTS 0x023c /* PCS AN Extended Status */ +#define GEM_RXLPI 0x0270 /* EEE RX LPI Transitions */ +#define GEM_RXLPITIME 0x0274 /* EEE RX LPI Time */ +#define GEM_TXLPI 0x0278 /* EEE TX LPI Transitions */ +#define GEM_TXLPITIME 0x027c /* EEE TX LPI Time */ #define GEM_DCFG1 0x0280 /* Design Config 1 */ #define GEM_DCFG2 0x0284 /* Design Config 2 */ #define GEM_DCFG3 0x0288 /* Design Config 3 */ @@ -258,6 +262,10 @@ #define MACB_MIIONRGMII_OFFSET 28 /* MII Usage on RGMII Interface */ #define MACB_MIIONRGMII_SIZE 1 +/* GEM specific NCR bitfields. */ +#define GEM_TXLPIEN_OFFSET 19 /* TX LPI Enable */ +#define GEM_TXLPIEN_SIZE 1 + /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ #define MACB_SPD_SIZE 1 @@ -756,6 +764,7 @@ #define MACB_CAPS_MIIONRGMII 0x00000200 #define MACB_CAPS_NEED_TSUCLK 0x00000400 #define MACB_CAPS_QUEUE_DISABLE 0x00000800 +#define MACB_CAPS_EEE 0x00001000 #define MACB_CAPS_PCS 0x01000000 #define MACB_CAPS_HIGH_SPEED 0x02000000 #define MACB_CAPS_CLK_HW_CHG 0x04000000 From bf51831f1dd4e2577dc72dfb2ca5713779d43ea8 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:19:46 +0100 Subject: [PATCH 4/8] net: cadence: macb: add EEE LPI statistics counters Expose the GEM MAC's EEE Low Power Idle hardware counters through ethtool -S: - rx_lpi_transitions: number of RX LPI entry events - rx_lpi_time: cumulative time spent in RX LPI - tx_lpi_transitions: number of TX LPI entry events (TXLPIEN 0->1) - tx_lpi_time: cumulative time in TX LPI These are clear-on-read hardware registers at offsets 0x270-0x27c, automatically collected by the existing gem_statistics read loop. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 8ffe48c1dc5b33..60d266447b4a50 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1047,6 +1047,10 @@ struct gem_stats { u32 rx_ip_header_checksum_errors; u32 rx_tcp_checksum_errors; u32 rx_udp_checksum_errors; + u32 rx_lpi_transitions; + u32 rx_lpi_time; + u32 tx_lpi_transitions; + u32 tx_lpi_time; }; /* Describes the name and offset of an individual statistic register, as @@ -1146,6 +1150,10 @@ static const struct gem_statistic gem_statistics[] = { GEM_BIT(NDS_RXERR)), GEM_STAT_TITLE_BITS(RXUDPCCNT, "rx_udp_checksum_errors", GEM_BIT(NDS_RXERR)), + GEM_STAT_TITLE(RXLPI, "rx_lpi_transitions"), + GEM_STAT_TITLE(RXLPITIME, "rx_lpi_time"), + GEM_STAT_TITLE(TXLPI, "tx_lpi_transitions"), + GEM_STAT_TITLE(TXLPITIME, "tx_lpi_time"), }; #define GEM_STATS_LEN ARRAY_SIZE(gem_statistics) From 1cab109dae2f0c3863673aabf3105efcaf0499d2 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:23:13 +0100 Subject: [PATCH 5/8] net: cadence: macb: implement EEE TX LPI support Implement software-managed TX Low Power Idle (LPI) for the Cadence GEM MAC as part of IEEE 802.3az Energy Efficient Ethernet support. The GEM MAC has no built-in idle timer - the TXLPIEN bit (NCR bit 19) immediately asserts LPI and blocks all TX while set. The MAC does not auto-wake for transmit. Per Microchip GMAC documentation (section 40.6.19): "It is best to use firmware to control LPI." This patch implements a software idle timer using delayed_work: - On TX completion with an empty ring, schedule LPI entry after a configurable idle timeout (default 250ms). The work function verifies all TX queues are truly idle before entering LPI to prevent entering LPI while traffic is still active. - On TX start, wake from LPI by clearing TXLPIEN, cancelling any pending re-entry, and waiting 50us for the PHY to exit LPI (conservative vs IEEE 802.3az Tw_sys of ~17us/~30us) - On link up, check EEE negotiation via phy_init_eee() and defer first LPI entry by 1 second per IEEE 802.3az requirements - On link down, immediately cancel pending work and clear TXLPIEN The timer value is configurable at runtime via ethtool --set-eee tx-timer. The implementation is gated on MACB_CAPS_EEE so platforms must explicitly opt in via their macb_config. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb.h | 6 ++ drivers/net/ethernet/cadence/macb_main.c | 120 ++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 60d266447b4a50..bb186807e33c7e 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1358,6 +1358,12 @@ struct macb { struct macb_ptp_info *ptp_info; /* macb-ptp interface */ + /* EEE / LPI state */ + bool eee_active; + bool tx_lpi_enabled; + struct delayed_work tx_lpi_work; + unsigned int tx_lpi_timer_ms; /* idle timeout before LPI */ + struct phy *sgmii_phy; /* for ZynqMP SGMII mode */ #ifdef MACB_EXT_DESC diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index cde9e88a5ec9cc..02bf59c3f4a2a7 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -656,6 +656,86 @@ static const struct phylink_pcs_ops macb_phylink_pcs_ops = { .pcs_config = macb_pcs_config, }; +/* Default TX LPI idle timeout in milliseconds. + * The MAC will enter LPI after this period of TX inactivity. + */ +#define MACB_TX_LPI_TIMER_DEFAULT_MS 250 + +/* PHY wake time from LPI in microseconds. + * IEEE 802.3az: Tw_sys is ~17us for 1000BASE-T, ~30us for 100BASE-TX. + * Use a conservative value to ensure the PHY has fully exited LPI. + */ +#define MACB_TX_LPI_WAKE_TIME_US 50 + +static void macb_tx_lpi_set(struct macb *bp, bool enable) +{ + unsigned long flags; + u32 ncr; + + spin_lock_irqsave(&bp->lock, flags); + + ncr = macb_readl(bp, NCR); + if (enable) + ncr |= GEM_BIT(TXLPIEN); + else + ncr &= ~GEM_BIT(TXLPIEN); + macb_writel(bp, NCR, ncr); + + bp->tx_lpi_enabled = enable; + + spin_unlock_irqrestore(&bp->lock, flags); + + netdev_dbg(bp->dev, "EEE TX LPI %s\n", + enable ? "enabled" : "disabled"); +} + +/* Schedule LPI re-entry after TX idle timeout */ +static inline void macb_tx_lpi_schedule(struct macb *bp) +{ + if (!bp->eee_active) + return; + + mod_delayed_work(system_wq, &bp->tx_lpi_work, + msecs_to_jiffies(bp->tx_lpi_timer_ms)); +} + +static void macb_tx_lpi_work_fn(struct work_struct *work) +{ + struct macb *bp = container_of(work, struct macb, tx_lpi_work.work); + unsigned int q; + + if (!bp->eee_active) + return; + + /* Only enter LPI if all TX queues are truly idle. The timer may + * have been scheduled when one queue drained but traffic resumed + * before the timer fired. + */ + for (q = 0; q < bp->num_queues; q++) { + if (bp->queues[q].tx_head != bp->queues[q].tx_tail) { + /* TX still active, reschedule and check again later */ + macb_tx_lpi_schedule(bp); + return; + } + } + + macb_tx_lpi_set(bp, true); +} + +/* Called from TX path to wake from LPI before transmitting */ +static inline void macb_tx_lpi_wake(struct macb *bp) +{ + if (!bp->tx_lpi_enabled) + return; + + macb_tx_lpi_set(bp, false); + /* Cancel any pending re-entry */ + cancel_delayed_work(&bp->tx_lpi_work); + + /* Wait for PHY to exit LPI before transmitting */ + udelay(MACB_TX_LPI_WAKE_TIME_US); +} + static void macb_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -728,10 +808,16 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, queue_writel(queue, IDR, bp->rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT(HRESP)); - /* Disable Rx and Tx */ - ctrl = macb_readl(bp, NCR) & ~(MACB_BIT(RE) | MACB_BIT(TE)); + /* Cancel any pending LPI entry */ + cancel_delayed_work(&bp->tx_lpi_work); + + /* Disable TX LPI, Rx, and Tx */ + ctrl = macb_readl(bp, NCR) & ~(GEM_BIT(TXLPIEN) | MACB_BIT(RE) | MACB_BIT(TE)); macb_writel(bp, NCR, ctrl); + bp->eee_active = false; + bp->tx_lpi_enabled = false; + netif_tx_stop_all_queues(ndev); } @@ -799,6 +885,19 @@ static void macb_mac_link_up(struct phylink_config *config, macb_writel(bp, NCR, ctrl | MACB_BIT(RE) | MACB_BIT(TE)); netif_tx_wake_all_queues(ndev); + + /* EEE: check if link partner negotiated EEE. + * Per IEEE 802.3az / Microchip GMAC docs: LPI must not be + * requested until the link has been up for at least 1 second. + */ + if (phy && (bp->caps & MACB_CAPS_EEE)) { + bp->eee_active = phy_init_eee(phy, false) >= 0 && + phy->enable_tx_lpi; + netdev_dbg(ndev, "EEE: active=%d\n", bp->eee_active); + if (bp->eee_active) + schedule_delayed_work(&bp->tx_lpi_work, + msecs_to_jiffies(1000)); + } } static struct phylink_pcs *macb_mac_select_pcs(struct phylink_config *config, @@ -1312,6 +1411,11 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) CIRC_CNT(queue->tx_head, queue->tx_tail, bp->tx_ring_size) <= MACB_TX_WAKEUP_THRESH(bp)) netif_wake_subqueue(bp->dev, queue_index); + + /* Schedule LPI re-entry when TX ring is drained */ + if (queue->tx_head == queue->tx_tail) + macb_tx_lpi_schedule(bp); + spin_unlock_irqrestore(&queue->tx_ptr_lock, flags); return packets; @@ -2341,6 +2445,10 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) bool is_lso; netdev_tx_t ret = NETDEV_TX_OK; + /* Wake from LPI before transmitting */ + if (unlikely(bp->tx_lpi_enabled)) + macb_tx_lpi_wake(bp); + if (macb_clear_csum(skb)) { dev_kfree_skb_any(skb); return ret; @@ -3064,6 +3172,9 @@ static int macb_open(struct net_device *dev) if (err) goto phy_off; + if ((bp->caps & MACB_CAPS_EEE) && dev->phydev) + phy_support_eee(dev->phydev); + netif_tx_start_all_queues(dev); if (bp->ptp_info) @@ -3095,6 +3206,8 @@ static int macb_close(struct net_device *dev) netif_tx_stop_all_queues(dev); + cancel_delayed_work_sync(&bp->tx_lpi_work); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { napi_disable(&queue->napi_rx); napi_disable(&queue->napi_tx); @@ -5338,6 +5451,8 @@ static int macb_probe(struct platform_device *pdev) } INIT_WORK(&bp->hresp_err_bh_work, macb_hresp_error_task); + INIT_DELAYED_WORK(&bp->tx_lpi_work, macb_tx_lpi_work_fn); + bp->tx_lpi_timer_ms = MACB_TX_LPI_TIMER_DEFAULT_MS; netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n", macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID), @@ -5382,6 +5497,7 @@ static void macb_remove(struct platform_device *pdev) mdiobus_free(bp->mii_bus); device_set_wakeup_enable(&bp->pdev->dev, 0); + cancel_delayed_work_sync(&bp->tx_lpi_work); cancel_work_sync(&bp->hresp_err_bh_work); pm_runtime_disable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); From 6c903ed286b49b9544c5cbf5716d532accebec26 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:27:19 +0100 Subject: [PATCH 6/8] net: cadence: macb: add ethtool EEE support Implement ethtool get_eee and set_eee operations for the Cadence GEM MAC, delegating to phylink for PHY-level EEE negotiation state. The MAC-level LPI control (TXLPIEN) is not manipulated directly in the ethtool ops. Instead, phylink_ethtool_set_eee() updates the PHY's EEE advertisement, which triggers link renegotiation. The mac_link_up callback then checks the negotiated EEE state and enables LPI accordingly. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 02bf59c3f4a2a7..b44febeafe4cee 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -3610,6 +3610,42 @@ static int macb_set_ringparam(struct net_device *netdev, return 0; } +static int macb_get_eee(struct net_device *ndev, struct ethtool_keee *edata) +{ + struct macb *bp = netdev_priv(ndev); + int ret; + + if (!(bp->caps & MACB_CAPS_EEE)) + return -EOPNOTSUPP; + + ret = phylink_ethtool_get_eee(bp->phylink, edata); + if (ret) + return ret; + + edata->tx_lpi_timer = bp->tx_lpi_timer_ms * 1000; + + return 0; +} + +static int macb_set_eee(struct net_device *ndev, struct ethtool_keee *edata) +{ + struct macb *bp = netdev_priv(ndev); + + if (!(bp->caps & MACB_CAPS_EEE)) + return -EOPNOTSUPP; + + if (edata->tx_lpi_timer) + bp->tx_lpi_timer_ms = edata->tx_lpi_timer / 1000; + + /* + * Don't directly control TXLPIEN here. phylink_ethtool_set_eee() + * updates the PHY, which will bounce the link if tx_lpi_enabled + * changes. That triggers mac_link_down/mac_link_up where we + * enable/disable TXLPIEN based on the negotiated state. + */ + return phylink_ethtool_set_eee(bp->phylink, edata); +} + #ifdef CONFIG_MACB_USE_HWSTAMP static unsigned int gem_get_tsu_rate(struct macb *bp) { @@ -4025,6 +4061,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .set_ringparam = macb_set_ringparam, .get_rxnfc = gem_get_rxnfc, .set_rxnfc = gem_set_rxnfc, + .get_eee = macb_get_eee, + .set_eee = macb_set_eee, .nway_reset = phy_ethtool_nway_reset, }; From 71ac13d1fdae33e355ca1982282ea49c4217a6dd Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 18 Feb 2026 16:28:12 +0100 Subject: [PATCH 7/8] net: cadence: macb: enable EEE for Raspberry Pi RP1 Enable IEEE 802.3az Energy Efficient Ethernet on the Raspberry Pi 5's RP1 southbridge by adding MACB_CAPS_EEE to its platform config. The RP1 contains a Cadence GEM_GXL MAC (revision 0x00070109) paired with a Broadcom BCM54213PE PHY, both of which support EEE at 1000BASE-T and 100BASE-TX. Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index b44febeafe4cee..cfc9d80b15b51b 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -5231,7 +5231,8 @@ static const struct macb_config versal_config = { static const struct macb_config raspberrypi_rp1_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP, + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_EEE, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, From f09a3e621c142d8f49160f9e207c00bb3eb9a50a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 16 Nov 2024 21:52:15 +0100 Subject: [PATCH 8/8] net: phy: ensure that genphy_c45_an_config_eee_aneg() sees new value of phydev->eee_cfg.eee_enabled This is a follow-up to 41ffcd95015f ("net: phy: fix phylib's dual eee_enabled") and resolves an issue with genphy_c45_an_config_eee_aneg() (called from genphy_c45_ethtool_set_eee) not seeing the new value of phydev->eee_cfg.eee_enabled. Fixes: 49168d1980e2 ("net: phy: Add phy_support_eee() indicating MAC support EEE") Signed-off-by: Heiner Kallweit Reported-by: Choong Yong Liang Reviewed-by: Russell King (Oracle) Signed-off-by: David S. Miller (cherry picked from commit f26a29a038ee3982099e867bec2260576a19720f) --- drivers/net/phy/phy.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 707a6ff5242bd8..2979cfd1f36320 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1728,7 +1728,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); * phy_ethtool_set_eee_noneg - Adjusts MAC LPI configuration without PHY * renegotiation * @phydev: pointer to the target PHY device structure - * @data: pointer to the ethtool_keee structure containing the new EEE settings + * @old_cfg: pointer to the eee_config structure containing the old EEE settings * * This function updates the Energy Efficient Ethernet (EEE) configuration * for cases where only the MAC's Low Power Idle (LPI) configuration changes, @@ -1739,11 +1739,10 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); * configuration. */ static void phy_ethtool_set_eee_noneg(struct phy_device *phydev, - struct ethtool_keee *data) + const struct eee_config *old_cfg) { - if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled || - phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) { - eee_to_eeecfg(&phydev->eee_cfg, data); + if (phydev->eee_cfg.tx_lpi_enabled != old_cfg->tx_lpi_enabled || + phydev->eee_cfg.tx_lpi_timer != old_cfg->tx_lpi_timer) { phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg); if (phydev->link) { phydev->link = false; @@ -1763,18 +1762,23 @@ static void phy_ethtool_set_eee_noneg(struct phy_device *phydev, */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_keee *data) { + struct eee_config old_cfg; int ret; if (!phydev->drv) return -EIO; mutex_lock(&phydev->lock); + + old_cfg = phydev->eee_cfg; + eee_to_eeecfg(&phydev->eee_cfg, data); + ret = genphy_c45_ethtool_set_eee(phydev, data); - if (ret >= 0) { - if (ret == 0) - phy_ethtool_set_eee_noneg(phydev, data); - eee_to_eeecfg(&phydev->eee_cfg, data); - } + if (ret == 0) + phy_ethtool_set_eee_noneg(phydev, &old_cfg); + else if (ret < 0) + phydev->eee_cfg = old_cfg; + mutex_unlock(&phydev->lock); return ret < 0 ? ret : 0;