From 335cfbd29f4ed7ce97d7446d66e41e1efcfc7480 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Thu, 26 Feb 2026 09:09:37 +0100 Subject: [PATCH 1/9] net: cadence: macb: add EEE LPI statistics counters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 237577e603ce upstream The GEM MAC provides four read-only, clear-on-read LPI statistics registers at offsets 0x270-0x27c: GEM_RXLPI (0x270): RX LPI transition count (16-bit) GEM_RXLPITIME (0x274): cumulative RX LPI time (24-bit) GEM_TXLPI (0x278): TX LPI transition count (16-bit) GEM_TXLPITIME (0x27c): cumulative TX LPI time (24-bit) Add register offset definitions, extend struct gem_stats with corresponding u64 software accumulators, and register the four counters in gem_statistics[] so they appear in ethtool -S output. Because the hardware counters clear on read, the existing macb_update_stats() path accumulates them into the u64 fields on every stats poll, preventing loss between userspace reads. These registers are present on SAMA5D2, SAME70, PIC32CZ, and RP1 variants of the Cadence GEM IP and have been confirmed on RP1 via devmem reads. Reviewed-by: Claudiu Beznea Reviewed-by: Théo Lebrun Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 2c814a2d06b745..abf5778beb0cf9 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 /* RX LPI Transitions */ +#define GEM_RXLPITIME 0x0274 /* RX LPI Time */ +#define GEM_TXLPI 0x0278 /* TX LPI Transitions */ +#define GEM_TXLPITIME 0x027c /* TX LPI Time */ #define GEM_DCFG1 0x0280 /* Design Config 1 */ #define GEM_DCFG2 0x0284 /* Design Config 2 */ #define GEM_DCFG3 0x0288 /* Design Config 3 */ @@ -1066,6 +1070,10 @@ struct gem_stats { u64 rx_ip_header_checksum_errors; u64 rx_tcp_checksum_errors; u64 rx_udp_checksum_errors; + u64 rx_lpi_transitions; + u64 rx_lpi_time; + u64 tx_lpi_transitions; + u64 tx_lpi_time; }; /* Describes the name and offset of an individual statistic register, as @@ -1165,6 +1173,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 e978c03a47f898df8a0a63e84bd5eaff8a775ef5 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Thu, 26 Feb 2026 09:26:52 +0100 Subject: [PATCH 2/9] net: cadence: macb: implement EEE TX LPI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0cc425f18f59 upstream The GEM MAC has hardware LPI registers (NCR bit 19: TXLPIEN) but no built-in idle timer, so asserting TXLPIEN blocks all TX immediately with no automatic wake. A software idle timer is required, as noted in Microchip documentation (section 40.6.19): "It is best to use firmware to control LPI." Implement phylink managed EEE using the mac_enable_tx_lpi and mac_disable_tx_lpi callbacks: - macb_tx_lpi_set(): sets or clears TXLPIEN; requires bp->lock to be held by the caller (asserted with lockdep_assert_held). Returns bool indicating whether the register actually changed, avoiding redundant writes and unnecessary udelay on the xmit fast path. - macb_tx_lpi_work_fn(): delayed_work handler that enters LPI if all TX queues are idle and EEE is still active. Takes bp->lock with irqsave before calling macb_tx_lpi_set(). - macb_tx_lpi_schedule(): arms the work timer using the LPI timer value provided by phylink (default 250 ms). Called from macb_tx_complete() after each TX drain so the idle countdown restarts whenever the ring goes quiet. - macb_tx_lpi_wake(): called from macb_start_xmit() under bp->lock, immediately before TSTART. Returns early if eee_active is false to avoid a register read on the common path when EEE is disabled. Clears TXLPIEN and applies a 50 us udelay for PHY wake (IEEE 802.3az Tw_sys_tx is 16.5 us for 1000BASE-T / 30 us for 100BASE-TX; GEM has no hardware enforcement). Only delays when TXLPIEN was actually set. The delay is placed after tx_head is advanced so the work_fn's queue-idle check sees a non-empty ring and cannot race back into LPI before the frame is transmitted. - mac_enable_tx_lpi: stores the timer and sets eee_active under bp->lock, then defers the first LPI entry by 1 second per IEEE 802.3az section 22.7a. - mac_disable_tx_lpi: cancels the work (sync, without the lock to avoid deadlock with the work_fn), then takes bp->lock to clear eee_active and deassert TXLPIEN. Populate phylink_config lpi_interfaces (MII, GMII, RGMII variants) and lpi_capabilities (MAC_100FD | MAC_1000FD) so phylink can negotiate EEE with the PHY and call the callbacks appropriately. Set lpi_timer_default to 250000 us and eee_enabled_default to true. Reviewed-by: Claudiu Beznea Reviewed-by: Théo Lebrun Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb.h | 8 ++ drivers/net/ethernet/cadence/macb_main.c | 124 +++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index abf5778beb0cf9..8c344f70849fb2 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -315,6 +315,8 @@ #define MACB_IRXFCS_SIZE 1 /* GEM specific NCR bitfields. */ +#define GEM_TXLPIEN_OFFSET 19 +#define GEM_TXLPIEN_SIZE 1 #define GEM_ENABLE_HS_MAC_OFFSET 31 #define GEM_ENABLE_HS_MAC_SIZE 1 @@ -790,6 +792,7 @@ #define MACB_CAPS_NEED_TSUCLK 0x00000400 #define MACB_CAPS_QUEUE_DISABLE 0x00000800 #define MACB_CAPS_QBV 0x00001000 +#define MACB_CAPS_EEE 0x00002000 #define MACB_CAPS_PCS 0x01000000 #define MACB_CAPS_HIGH_SPEED 0x02000000 #define MACB_CAPS_CLK_HW_CHG 0x04000000 @@ -1403,6 +1406,11 @@ struct macb { struct work_struct hresp_err_bh_work; + /* EEE / LPI state */ + bool eee_active; + struct delayed_work tx_lpi_work; + u32 tx_lpi_timer; + int rx_bd_rd_prefetch; int tx_bd_rd_prefetch; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index b0b32012b4e017..4ab160956d67ca 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -642,6 +643,107 @@ static const struct phylink_pcs_ops macb_phylink_pcs_ops = { .pcs_config = macb_pcs_config, }; +static bool macb_tx_lpi_set(struct macb *bp, bool enable) +{ + u32 old, ncr; + + lockdep_assert_held(&bp->lock); + + ncr = macb_readl(bp, NCR); + old = ncr; + if (enable) + ncr |= GEM_BIT(TXLPIEN); + else + ncr &= ~GEM_BIT(TXLPIEN); + if (old != ncr) + macb_writel(bp, NCR, ncr); + + return old != ncr; +} + +static bool macb_tx_all_queues_idle(struct macb *bp) +{ + struct macb_queue *queue; + unsigned int q; + + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + if (READ_ONCE(queue->tx_head) != READ_ONCE(queue->tx_tail)) + return false; + } + return true; +} + +static void macb_tx_lpi_work_fn(struct work_struct *work) +{ + struct macb *bp = container_of(work, struct macb, tx_lpi_work.work); + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + if (bp->eee_active && macb_tx_all_queues_idle(bp)) + macb_tx_lpi_set(bp, true); + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void macb_tx_lpi_schedule(struct macb *bp) +{ + if (bp->eee_active) + mod_delayed_work(system_wq, &bp->tx_lpi_work, + usecs_to_jiffies(bp->tx_lpi_timer)); +} + +/* Wake from LPI before transmitting. The MAC must deassert TXLPIEN + * and wait for the PHY to exit LPI before any frame can be sent. + * IEEE 802.3az Tw_sys is ~17us for 1000BASE-T, ~30us for 100BASE-TX; + * we use a conservative 50us. + */ +static void macb_tx_lpi_wake(struct macb *bp) +{ + lockdep_assert_held(&bp->lock); + + if (!bp->eee_active) + return; + + if (!macb_tx_lpi_set(bp, false)) + return; + + cancel_delayed_work(&bp->tx_lpi_work); + udelay(50); +} + +static void macb_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct macb *bp = netdev_priv(ndev); + unsigned long flags; + + cancel_delayed_work_sync(&bp->tx_lpi_work); + + spin_lock_irqsave(&bp->lock, flags); + bp->eee_active = false; + macb_tx_lpi_set(bp, false); + spin_unlock_irqrestore(&bp->lock, flags); +} + +static int macb_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, + bool tx_clk_stop) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct macb *bp = netdev_priv(ndev); + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + bp->tx_lpi_timer = timer; + bp->eee_active = true; + spin_unlock_irqrestore(&bp->lock, flags); + + /* Defer initial LPI entry by 1 second after link-up per + * IEEE 802.3az section 22.7a. + */ + mod_delayed_work(system_wq, &bp->tx_lpi_work, msecs_to_jiffies(1000)); + + return 0; +} + static void macb_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -806,6 +908,8 @@ static const struct phylink_mac_ops macb_phylink_ops = { .mac_config = macb_mac_config, .mac_link_down = macb_mac_link_down, .mac_link_up = macb_mac_link_up, + .mac_disable_tx_lpi = macb_mac_disable_tx_lpi, + .mac_enable_tx_lpi = macb_mac_enable_tx_lpi, }; static bool macb_phy_handle_exists(struct device_node *dn) @@ -901,6 +1005,18 @@ static int macb_mii_probe(struct net_device *dev) } } + /* Configure EEE LPI if supported */ + if (bp->caps & MACB_CAPS_EEE) { + __set_bit(PHY_INTERFACE_MODE_MII, + bp->phylink_config.lpi_interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, + bp->phylink_config.lpi_interfaces); + phy_interface_set_rgmii(bp->phylink_config.lpi_interfaces); + bp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD; + bp->phylink_config.lpi_timer_default = 250000; + bp->phylink_config.eee_enabled_default = true; + } + bp->phylink = phylink_create(&bp->phylink_config, bp->pdev->dev.fwnode, bp->phy_interface, &macb_phylink_ops); if (IS_ERR(bp->phylink)) { @@ -1302,6 +1418,9 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) netif_wake_subqueue(bp->dev, queue_index); spin_unlock_irqrestore(&queue->tx_ptr_lock, flags); + if (packets) + macb_tx_lpi_schedule(bp); + return packets; } @@ -2411,6 +2530,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) skb->len); spin_lock(&bp->lock); + macb_tx_lpi_wake(bp); /* TSTART write might get dropped, so make the IRQ retrigger a buffer read */ if (macb_readl(bp, TSR) & MACB_BIT(TGO)) @@ -3108,6 +3228,8 @@ static int macb_close(struct net_device *dev) netdev_tx_reset_queue(netdev_get_tx_queue(dev, q)); } + cancel_delayed_work_sync(&bp->tx_lpi_work); + phylink_stop(bp->phylink); phylink_disconnect_phy(bp->phylink); @@ -5746,6 +5868,7 @@ 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); 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), @@ -5790,6 +5913,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 bd1ce010c2bac89b86651995efbeea67e9283af9 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Fri, 27 Feb 2026 09:21:58 +0100 Subject: [PATCH 3/9] net: cadence: macb: add ethtool EEE support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 61332b78761c upstream Implement get_eee and set_eee ethtool ops for GEM as simple passthroughs to phylink_ethtool_get_eee() and phylink_ethtool_set_eee(). No MACB_CAPS_EEE guard is needed: phylink returns -EOPNOTSUPP from both ops when mac_supports_eee is false, which is the case when lpi_capabilities and lpi_interfaces are not populated. Those fields are only set when MACB_CAPS_EEE is present (previous patch), so phylink already handles the unsupported case correctly. Reviewed-by: Claudiu Beznea Reviewed-by: Théo Lebrun Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 4ab160956d67ca..97af860b19651f 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4186,6 +4186,20 @@ static const struct ethtool_ops macb_ethtool_ops = { .set_ringparam = macb_set_ringparam, }; +static int macb_get_eee(struct net_device *dev, struct ethtool_keee *eee) +{ + struct macb *bp = netdev_priv(dev); + + return phylink_ethtool_get_eee(bp->phylink, eee); +} + +static int macb_set_eee(struct net_device *dev, struct ethtool_keee *eee) +{ + struct macb *bp = netdev_priv(dev); + + return phylink_ethtool_set_eee(bp->phylink, eee); +} + static const struct ethtool_ops gem_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS, @@ -4210,6 +4224,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, }; static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) From 3a0bcc7d7a1f94532ea83c4eeecc18521249e162 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Fri, 27 Feb 2026 09:22:32 +0100 Subject: [PATCH 4/9] net: cadence: macb: enable EEE for Raspberry Pi RP1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 92ba3307431a upstream Set MACB_CAPS_EEE for the Raspberry Pi 5 RP1 southbridge (Cadence GEM_GXL rev 0x00070109 paired with BCM54213PE PHY). EEE has been verified on RP1 hardware: the LPI counter registers at 0x270-0x27c return valid data, the TXLPIEN bit in NCR (bit 19) controls LPI transmission correctly, and ethtool --show-eee reports the negotiated state after link-up. Other GEM variants that share the same LPI register layout (SAMA5D2, SAME70, PIC32CZ) can be enabled by adding MACB_CAPS_EEE to their respective config entries once tested. Reviewed-by: Claudiu Beznea Reviewed-by: Théo Lebrun 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 97af860b19651f..34e9e35d6c787e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -5626,7 +5626,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 f7576c7831a15df88cf38d609b5dc8c77ba683ae Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 2 Mar 2026 21:49:39 +0100 Subject: [PATCH 5/9] net: phy: broadcom: fix BCM54213PE per-PHY init never called MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCM54213PE (PHY_ID = 0x600d84a2) and BCM54210E share the same model ID when masked by PHY_ID_MATCH_MODEL_MASK (0xfffffff0): both reduce to 0x600d84a0. The dispatch switch in bcm54xx_config_init() switches on phydev->drv->phy_id & PHY_ID_MATCH_MODEL_MASK, so the separate case PHY_ID_BCM54213PE: could never match — the expression always evaluated to 0x600d84a0, not 0x600d84a2. bcm54213pe_config_init() was silently never called; BCM54213PE instead fell through to the BCM54210E path. Replace the dead case label with an exact driver ID check inside the BCM54210E case, which already handles the same model ID family. Fixes: 1001c6f53765 ("phy: broadcom: Add bcm54213pe configuration") Signed-off-by: Nicolai Buchwitz --- drivers/net/phy/broadcom.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 428ff5a01b0885..f2d8f4336589cd 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -498,14 +498,17 @@ static int bcm54xx_config_init(struct phy_device *phydev) err = bcm54xx_config_clock_delay(phydev); break; case PHY_ID_BCM54210E: - err = bcm54210e_config_init(phydev); + /* BCM54213PE shares the same model ID as BCM54210E; use the + * exact driver ID to dispatch to the right per-PHY init. + */ + if (phydev->drv->phy_id == PHY_ID_BCM54213PE) + err = bcm54213pe_config_init(phydev); + else + err = bcm54210e_config_init(phydev); break; case PHY_ID_BCM54612E: err = bcm54612e_config_init(phydev); break; - case PHY_ID_BCM54213PE: - err = bcm54213pe_config_init(phydev); - break; case PHY_ID_BCM54616S: err = bcm54616s_config_init(phydev); break; From 89d02e4dc84af79f01c51cc9f1f76b868ec0401a Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 2 Mar 2026 21:50:25 +0100 Subject: [PATCH 6/9] net: phy: broadcom: disable AutogrEEEn mode on BCM54xx BCM54xx PHYs default to AutogrEEEn mode (MII_BUF_CNTL_0 bit 0), which manages EEE Low Power Idle autonomously without forwarding LPI signaling to the MAC over the RGMII interface. This prevents the MAC from tracking LPI activity and controlling TX LPI entry/exit. Unconditionally clear the AutogrEEEn enable bit during config_init to switch all BCM54xx PHYs to Native EEE mode. In Native EEE mode the MAC controls TX LPI and the PHY forwards received LPI on the RGMII interface, allowing MACs with IEEE 802.3az support to observe RX LPI transitions. Signed-off-by: Nicolai Buchwitz --- drivers/net/phy/broadcom.c | 9 +++++++++ include/linux/brcmphy.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index f2d8f4336589cd..a4d8c4d6f19fbf 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -490,6 +490,15 @@ static int bcm54xx_config_init(struct phy_device *phydev) if (of_property_read_bool(np, "brcm,powerdown-enable")) phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE; + /* Disable AutogrEEEn and switch to Native EEE mode so the MAC + * can control LPI signaling and observe RX LPI on the RGMII + * interface. + */ + err = bcm_phy_modify_exp(phydev, BCM54XX_TOP_MISC_MII_BUF_CNTL0, + BCM54XX_MII_BUF_CNTL0_AUTOGREEEN_EN, 0); + if (err) + return err; + bcm54xx_adjust_rxrefclk(phydev); switch (phydev->drv->phy_id & PHY_ID_MATCH_MODEL_MASK) { diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 97e6a716740bd7..5f9034cc92b86b 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -267,6 +267,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) From 625baaec1bb3c6ca5855e2898be1d3e2bcc10caa Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 9 Mar 2026 12:21:46 +0100 Subject: [PATCH 7/9] ARM: dts: bcm2712: re-enable EEE on Pi5 and CM5 PHY Reverts 85b196fde0da ("dts: cm5/pi5: Disable EEE on rp1"). Now that the macb driver supports EEE via the phylink managed EEE API, the BCM54213PE PHY can advertise EEE correctly and the MAC will handle TX LPI entry/exit properly. Remove the eee-broken-1000t and eee-broken-100tx properties that were added as a workaround when the macb driver lacked EEE support. Signed-off-by: Nicolai Buchwitz --- 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 a9c9eb9780451c..c8908f6b3143b5 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -176,8 +176,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 7558978cbf92ba..dfad85a666a8a6 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi @@ -167,8 +167,6 @@ rp1_target: &pcie2 { brcm,powerdown-enable; interrupt-parent = <&rp1_gpio>; interrupts = <37 IRQ_TYPE_LEVEL_LOW>; - eee-broken-1000t; - eee-broken-100tx; }; }; From 0dd66e0173048fa9d7fce109d0381d4f4600ae09 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 9 Mar 2026 12:31:58 +0100 Subject: [PATCH 8/9] ARM: dts: bcm2712: add eee dtparam for Pi5 and CM5 Add an eee dtparam to allow users to disable EEE on the BCM54213PE PHY without recompiling the device tree. Setting dtparam=eee=off adds the eee-broken-1000t and eee-broken-100tx properties to the PHY node which prevents EEE advertisement and negotiation. EEE is enabled by default (dtparam=eee=on). Signed-off-by: Nicolai Buchwitz --- arch/arm/boot/dts/overlays/README | 2 +- arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index f01783f58b53c4..7ab189972291e7 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -234,7 +234,7 @@ Params: eee Enable Energy Efficient Ethernet support for compatible devices (default "on"). See also - "tx_lpi_timer". Pi3B+, Pi4 and CM4 only. + "tx_lpi_timer". Pi3B+, Pi4, CM4, Pi5 and CM5. enable_eeprom Set to "on" to enable the onboard bootloader EEPROM by driving GPIO 57 low diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi index e6a12bd2b941dd..2fa5eb16ae08a6 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi @@ -189,6 +189,8 @@ watchdog: &pm {}; drm_fb2_rp1_dsi0 = <&aliases>, "drm-fb2=",&dsi0; drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1; drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4; + eee = <&phy1>,"eee-broken-1000t!", + <&phy1>,"eee-broken-100tx!"; eth_led0 = <&phy1>,"led-modes:0"; eth_led1 = <&phy1>,"led-modes:4"; eth_max_speed = <&phy1>,"max-speed:0"; From da3ab17911b218a44ab9fc458aa6bf3995dd09fc Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 9 Mar 2026 12:45:17 +0100 Subject: [PATCH 9/9] ARM: dts: bcm2711: replace genet.eee bootarg with eee-broken dtparam Reverts 42b77dd35267 ("net: bcmgenet: Add 'eee' module parameter") for the DTS side. The downstream driver patch can be dropped as the same functionality is now handled entirely in device tree, removing one downstream-only driver change from the tree. Move the eee dtparam from the shared bcm2711-rpi-ds.dtsi into the individual Pi4 and CM4 device trees (where phy1 is defined), using eee-broken-1000t and eee-broken-100tx properties on the PHY node. This prevents EEE advertisement at the PHY level without requiring driver modifications. CM4S is excluded as it has no onboard Ethernet. Setting dtparam=eee=off adds the eee-broken properties to the PHY node which prevents EEE negotiation entirely. EEE is enabled by default (dtparam=eee=on). Signed-off-by: Nicolai Buchwitz --- arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 2 ++ arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 2 ++ arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts index e376a5bf891307..f3820405615274 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts @@ -504,6 +504,8 @@ i2c_csi_dsi0: &i2c0 { pwr_led_activelow = <&led_pwr>,"gpios:8"; pwr_led_trigger = <&led_pwr>,"linux,default-trigger"; + eee = <&phy1>,"eee-broken-1000t!", + <&phy1>,"eee-broken-100tx!"; eth_led0 = <&phy1>,"led-modes:0"; eth_led1 = <&phy1>,"led-modes:4"; eth_max_speed = <&phy1>,"max-speed:0"; diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts index 14cdca845300c4..22624338e74f6f 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts @@ -474,6 +474,8 @@ i2c_csi_dsi0: &i2c0 { pwr_led_activelow = <&led_pwr>,"gpios:8"; pwr_led_trigger = <&led_pwr>,"linux,default-trigger"; + eee = <&phy1>,"eee-broken-1000t!", + <&phy1>,"eee-broken-100tx!"; eth_led0 = <&phy1>,"led-modes:0"; eth_led1 = <&phy1>,"led-modes:4"; eth_max_speed = <&phy1>,"max-speed:0"; diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi index 8cc1ef5bf6d0c5..74cad51d333088 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi @@ -8,7 +8,6 @@ __overrides__ { arm_freq; - eee = <&chosen>,"bootargs{on='',off='genet.eee=N'}"; hdmi = <&hdmi0>,"status", <&hdmi1>,"status"; nvmem_cust_rw = <&nvmem_cust>,"rw?";