* [PATCH net-next v2 1/5] net: cadence: macb: add EEE register definitions and capability flag
2026-02-24 9:18 [PATCH net-next v2 0/5] net: cadence: macb: add IEEE 802.3az EEE support Nicolai Buchwitz
@ 2026-02-24 9:18 ` Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 2/5] net: cadence: macb: add EEE LPI statistics counters Nicolai Buchwitz
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 9:18 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, linux, phil, Nicolai Buchwitz
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 <nb@tipi-net.de>
---
drivers/net/ethernet/cadence/macb.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 87414a2ddf6e..729751d424c2 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -170,6 +170,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 */
@@ -305,6 +309,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
@@ -779,6 +785,7 @@
#define MACB_CAPS_DMA_PTP BIT(22)
#define MACB_CAPS_RSC BIT(23)
#define MACB_CAPS_NO_LSO BIT(24)
+#define MACB_CAPS_EEE BIT(25)
/* LSO settings */
#define MACB_LSO_UFO_ENABLE 0x01
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v2 2/5] net: cadence: macb: add EEE LPI statistics counters
2026-02-24 9:18 [PATCH net-next v2 0/5] net: cadence: macb: add IEEE 802.3az EEE support Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 1/5] net: cadence: macb: add EEE register definitions and capability flag Nicolai Buchwitz
@ 2026-02-24 9:18 ` Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support Nicolai Buchwitz
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 9:18 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, linux, phil, Nicolai Buchwitz
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 <nb@tipi-net.de>
---
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 729751d424c2..e3520e5aec67 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -1050,6 +1050,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
@@ -1149,6 +1153,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)
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support
2026-02-24 9:18 [PATCH net-next v2 0/5] net: cadence: macb: add IEEE 802.3az EEE support Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 1/5] net: cadence: macb: add EEE register definitions and capability flag Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 2/5] net: cadence: macb: add EEE LPI statistics counters Nicolai Buchwitz
@ 2026-02-24 9:18 ` Nicolai Buchwitz
2026-02-24 10:17 ` Russell King (Oracle)
2026-02-24 9:18 ` [PATCH net-next v2 4/5] net: cadence: macb: add ethtool EEE support Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 5/5] net: cadence: macb: enable EEE for Raspberry Pi RP1 Nicolai Buchwitz
4 siblings, 1 reply; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 9:18 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, linux, phil, Nicolai Buchwitz
Implement Energy Efficient Ethernet TX Low Power Idle using phylink's
managed EEE framework. The Cadence GEM MAC has no built-in idle timer
- TXLPIEN (NCR bit 19) immediately blocks all TX when set and the MAC
does NOT auto-wake - so the driver uses a software delayed_work timer
for idle detection.
The TX LPI lifecycle:
- phylink calls mac_enable_tx_lpi() after link-up with the negotiated
timer value. The driver defers the first LPI entry by 1 second per
IEEE 802.3az section 22.7a.
- macb_tx_complete() reschedules the idle timer after each TX drain.
- macb_start_xmit() wakes from LPI by clearing TXLPIEN, cancelling
the pending work, and waiting 50us (conservative Tw_sys) before
initiating the transmit.
- phylink calls mac_disable_tx_lpi() before link-down, which cancels
the work and clears TXLPIEN.
The phylink_config is populated with LPI capabilities (MII, GMII, RGMII
modes; 100FD and 1000FD speeds) and a 250ms default idle timer, gated
on MACB_CAPS_EEE.
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
drivers/net/ethernet/cadence/macb.h | 6 ++
drivers/net/ethernet/cadence/macb_main.c | 110 +++++++++++++++++++++++
2 files changed, 116 insertions(+)
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index e3520e5aec67..a17e537cdeb6 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -1372,6 +1372,12 @@ struct macb {
struct work_struct hresp_err_bh_work;
+ /* EEE / LPI state */
+ bool eee_active;
+ bool tx_lpi_enabled;
+ 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 43cd013bb70e..fb64a97725b1 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -10,6 +10,7 @@
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/crc32.h>
+#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/firmware/xlnx-zynqmp.h>
@@ -589,6 +590,92 @@ static const struct phylink_pcs_ops macb_phylink_pcs_ops = {
.pcs_config = macb_pcs_config,
};
+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);
+}
+
+static bool macb_tx_all_queues_idle(struct macb *bp)
+{
+ unsigned int q;
+
+ for (q = 0; q < bp->num_queues; q++) {
+ struct macb_queue *queue = &bp->queues[q];
+
+ if (queue->tx_head != 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);
+
+ if (bp->eee_active && macb_tx_all_queues_idle(bp))
+ macb_tx_lpi_set(bp, true);
+}
+
+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)
+{
+ if (!bp->tx_lpi_enabled)
+ return;
+
+ macb_tx_lpi_set(bp, false);
+ 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);
+
+ bp->eee_active = false;
+ cancel_delayed_work_sync(&bp->tx_lpi_work);
+ macb_tx_lpi_set(bp, false);
+}
+
+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);
+
+ bp->tx_lpi_timer = timer;
+ bp->eee_active = true;
+
+ /* 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)
{
@@ -753,6 +840,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)
@@ -848,6 +937,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)) {
@@ -1244,6 +1345,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;
}
@@ -2349,6 +2453,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
netdev_tx_sent_queue(netdev_get_tx_queue(bp->dev, queue_index),
skb->len);
+ macb_tx_lpi_wake(bp);
+
spin_lock(&bp->lock);
macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
spin_unlock(&bp->lock);
@@ -3010,6 +3116,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);
@@ -5616,6 +5724,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),
@@ -5659,6 +5768,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);
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support
2026-02-24 9:18 ` [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support Nicolai Buchwitz
@ 2026-02-24 10:17 ` Russell King (Oracle)
2026-02-24 10:40 ` Nicolai Buchwitz
0 siblings, 1 reply; 8+ messages in thread
From: Russell King (Oracle) @ 2026-02-24 10:17 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: netdev, andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, phil
On Tue, Feb 24, 2026 at 10:18:19AM +0100, Nicolai Buchwitz wrote:
> Implement Energy Efficient Ethernet TX Low Power Idle using phylink's
> managed EEE framework. The Cadence GEM MAC has no built-in idle timer
> - TXLPIEN (NCR bit 19) immediately blocks all TX when set and the MAC
> does NOT auto-wake - so the driver uses a software delayed_work timer
> for idle detection.
>
> The TX LPI lifecycle:
> - phylink calls mac_enable_tx_lpi() after link-up with the negotiated
> timer value. The driver defers the first LPI entry by 1 second per
> IEEE 802.3az section 22.7a.
> - macb_tx_complete() reschedules the idle timer after each TX drain.
> - macb_start_xmit() wakes from LPI by clearing TXLPIEN, cancelling
> the pending work, and waiting 50us (conservative Tw_sys) before
> initiating the transmit.
> - phylink calls mac_disable_tx_lpi() before link-down, which cancels
> the work and clears TXLPIEN.
>
> The phylink_config is populated with LPI capabilities (MII, GMII, RGMII
> modes; 100FD and 1000FD speeds) and a 250ms default idle timer, gated
> on MACB_CAPS_EEE.
Much better, thanks. One suggestion:
> +static void macb_tx_lpi_set(struct macb *bp, bool enable)
bool return type.
> +{
> + unsigned long flags;
> + u32 ncr;
u32 old, ncr;
> +
> + spin_lock_irqsave(&bp->lock, flags);
> + 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);
> + bp->tx_lpi_enabled = enable;
No need for tx_lpi_enabled
> + spin_unlock_irqrestore(&bp->lock, flags);
return old != ncr;
> +}
...
> +/* 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)
> +{
> + if (!bp->tx_lpi_enabled)
> + return;
No need for this if().
> +
> + macb_tx_lpi_set(bp, false);
instead:
if (!macb_tx_lpi_set(bp, false))
return;
The presence of the spinlock in macb_tx_lpi_set() suggests that there
could be races, so keeping the state completely inside the spinlock
region would be sensible.
> + cancel_delayed_work(&bp->tx_lpi_work);
I wonder whether this is reliable on its own. cancel_delayed_work()
documentation says:
* Note:
* The work callback function may still be running on return, unless
* it returns %true and the work doesn't re-arm itself. Explicitly flush or
* use cancel_delayed_work_sync() to wait on it.
That means macb_tx_lpi_work_fn() could have just been entered at the
point that cancel_delayed_work() has been called. Would that cause a
problem, e.g. setting LPI mode again, or would we be guaranteed that
macb_tx_all_queues_idle() returns false preventing LPI mode being set
again?
Thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support
2026-02-24 10:17 ` Russell King (Oracle)
@ 2026-02-24 10:40 ` Nicolai Buchwitz
0 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 10:40 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: netdev, andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, phil
On 24.2.2026 11:17, Russell King (Oracle) wrote:
> On Tue, Feb 24, 2026 at 10:18:19AM +0100, Nicolai Buchwitz wrote:
>> Implement Energy Efficient Ethernet TX Low Power Idle using phylink's
>> managed EEE framework. The Cadence GEM MAC has no built-in idle timer
>> - TXLPIEN (NCR bit 19) immediately blocks all TX when set and the MAC
>> does NOT auto-wake - so the driver uses a software delayed_work timer
>> for idle detection.
>>
>> The TX LPI lifecycle:
>> - phylink calls mac_enable_tx_lpi() after link-up with the
>> negotiated
>> timer value. The driver defers the first LPI entry by 1 second per
>> IEEE 802.3az section 22.7a.
>> - macb_tx_complete() reschedules the idle timer after each TX drain.
>> - macb_start_xmit() wakes from LPI by clearing TXLPIEN, cancelling
>> the pending work, and waiting 50us (conservative Tw_sys) before
>> initiating the transmit.
>> - phylink calls mac_disable_tx_lpi() before link-down, which cancels
>> the work and clears TXLPIEN.
>>
>> The phylink_config is populated with LPI capabilities (MII, GMII,
>> RGMII
>> modes; 100FD and 1000FD speeds) and a 250ms default idle timer, gated
>> on MACB_CAPS_EEE.
>
> Much better, thanks. One suggestion:
Thanks for the quick feedback! I will address this in a v3 of this
series.
>
>> +static void macb_tx_lpi_set(struct macb *bp, bool enable)
>
> bool return type.
>
>> +{
>> + unsigned long flags;
>> + u32 ncr;
>
> u32 old, ncr;
>
>> +
>> + spin_lock_irqsave(&bp->lock, flags);
>> + 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);
>> + bp->tx_lpi_enabled = enable;
>
> No need for tx_lpi_enabled
>
>> + spin_unlock_irqrestore(&bp->lock, flags);
>
> return old != ncr;
>
>> +}
>
> ...
>
>> +/* 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)
>> +{
>> + if (!bp->tx_lpi_enabled)
>> + return;
>
> No need for this if().
>
>> +
>> + macb_tx_lpi_set(bp, false);
>
> instead:
> if (!macb_tx_lpi_set(bp, false))
> return;
>
> The presence of the spinlock in macb_tx_lpi_set() suggests that there
> could be races, so keeping the state completely inside the spinlock
> region would be sensible.
>
>> + cancel_delayed_work(&bp->tx_lpi_work);
>
> I wonder whether this is reliable on its own. cancel_delayed_work()
> documentation says:
>
> * Note:
> * The work callback function may still be running on return, unless
> * it returns %true and the work doesn't re-arm itself. Explicitly
> flush or
> * use cancel_delayed_work_sync() to wait on it.
>
> That means macb_tx_lpi_work_fn() could have just been entered at the
> point that cancel_delayed_work() has been called. Would that cause a
> problem, e.g. setting LPI mode again, or would we be guaranteed that
> macb_tx_all_queues_idle() returns false preventing LPI mode being set
> again?
AFAIK, the race shouldn't be an issue here because macb_tx_lpi_wake()
is called from macb_start_xmit() after the TX descriptors have been
written and tx_head has been advanced (there's a wmb() before the
call to ensure visibility). So if macb_tx_lpi_work_fn() happens to
be running concurrently, macb_tx_all_queues_idle() will see
tx_head != tx_tail and return false, preventing LPI from being
re-enabled.
>
> Thanks.
Nicolai
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v2 4/5] net: cadence: macb: add ethtool EEE support
2026-02-24 9:18 [PATCH net-next v2 0/5] net: cadence: macb: add IEEE 802.3az EEE support Nicolai Buchwitz
` (2 preceding siblings ...)
2026-02-24 9:18 ` [PATCH net-next v2 3/5] net: cadence: macb: implement EEE TX LPI support Nicolai Buchwitz
@ 2026-02-24 9:18 ` Nicolai Buchwitz
2026-02-24 9:18 ` [PATCH net-next v2 5/5] net: cadence: macb: enable EEE for Raspberry Pi RP1 Nicolai Buchwitz
4 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 9:18 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, linux, phil, Nicolai Buchwitz
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 - phylink manages the full EEE lifecycle through the
mac_enable_tx_lpi / mac_disable_tx_lpi callbacks.
Both ops are gated on MACB_CAPS_EEE; platforms without the capability
flag return -EOPNOTSUPP.
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
drivers/net/ethernet/cadence/macb_main.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index fb64a97725b1..87097100b88c 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -4032,6 +4032,26 @@ 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);
+
+ if (!(bp->caps & MACB_CAPS_EEE))
+ return -EOPNOTSUPP;
+
+ 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);
+
+ if (!(bp->caps & MACB_CAPS_EEE))
+ return -EOPNOTSUPP;
+
+ return phylink_ethtool_set_eee(bp->phylink, eee);
+}
+
static const struct ethtool_ops gem_ethtool_ops = {
.get_regs_len = macb_get_regs_len,
.get_regs = macb_get_regs,
@@ -4053,6 +4073,8 @@ static const struct ethtool_ops gem_ethtool_ops = {
.get_rxnfc = gem_get_rxnfc,
.set_rxnfc = gem_set_rxnfc,
.get_rx_ring_count = gem_get_rx_ring_count,
+ .get_eee = macb_get_eee,
+ .set_eee = macb_set_eee,
};
static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v2 5/5] net: cadence: macb: enable EEE for Raspberry Pi RP1
2026-02-24 9:18 [PATCH net-next v2 0/5] net: cadence: macb: add IEEE 802.3az EEE support Nicolai Buchwitz
` (3 preceding siblings ...)
2026-02-24 9:18 ` [PATCH net-next v2 4/5] net: cadence: macb: add ethtool EEE support Nicolai Buchwitz
@ 2026-02-24 9:18 ` Nicolai Buchwitz
4 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-02-24 9:18 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, claudiu.beznea, davem, edumazet, kuba,
nicolas.ferre, pabeni, linux, phil, Nicolai Buchwitz
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 <nb@tipi-net.de>
---
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 87097100b88c..2bf27a266e16 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -5516,7 +5516,8 @@ static const struct macb_config eyeq5_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,
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread