Skip to content

Commit e978c03

Browse files
committed
net: cadence: macb: implement EEE TX LPI support
commit 0cc425f 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 <claudiu.beznea@tuxon.dev> Reviewed-by: Théo Lebrun <theo.lebrun@bootlin.com> Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
1 parent 335cfbd commit e978c03

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

drivers/net/ethernet/cadence/macb.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@
315315
#define MACB_IRXFCS_SIZE 1
316316

317317
/* GEM specific NCR bitfields. */
318+
#define GEM_TXLPIEN_OFFSET 19
319+
#define GEM_TXLPIEN_SIZE 1
318320
#define GEM_ENABLE_HS_MAC_OFFSET 31
319321
#define GEM_ENABLE_HS_MAC_SIZE 1
320322

@@ -790,6 +792,7 @@
790792
#define MACB_CAPS_NEED_TSUCLK 0x00000400
791793
#define MACB_CAPS_QUEUE_DISABLE 0x00000800
792794
#define MACB_CAPS_QBV 0x00001000
795+
#define MACB_CAPS_EEE 0x00002000
793796
#define MACB_CAPS_PCS 0x01000000
794797
#define MACB_CAPS_HIGH_SPEED 0x02000000
795798
#define MACB_CAPS_CLK_HW_CHG 0x04000000
@@ -1403,6 +1406,11 @@ struct macb {
14031406

14041407
struct work_struct hresp_err_bh_work;
14051408

1409+
/* EEE / LPI state */
1410+
bool eee_active;
1411+
struct delayed_work tx_lpi_work;
1412+
u32 tx_lpi_timer;
1413+
14061414
int rx_bd_rd_prefetch;
14071415
int tx_bd_rd_prefetch;
14081416

drivers/net/ethernet/cadence/macb_main.c

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/clk.h>
1010
#include <linux/clk-provider.h>
1111
#include <linux/crc32.h>
12+
#include <linux/delay.h>
1213
#include <linux/module.h>
1314
#include <linux/moduleparam.h>
1415
#include <linux/kernel.h>
@@ -642,6 +643,107 @@ static const struct phylink_pcs_ops macb_phylink_pcs_ops = {
642643
.pcs_config = macb_pcs_config,
643644
};
644645

646+
static bool macb_tx_lpi_set(struct macb *bp, bool enable)
647+
{
648+
u32 old, ncr;
649+
650+
lockdep_assert_held(&bp->lock);
651+
652+
ncr = macb_readl(bp, NCR);
653+
old = ncr;
654+
if (enable)
655+
ncr |= GEM_BIT(TXLPIEN);
656+
else
657+
ncr &= ~GEM_BIT(TXLPIEN);
658+
if (old != ncr)
659+
macb_writel(bp, NCR, ncr);
660+
661+
return old != ncr;
662+
}
663+
664+
static bool macb_tx_all_queues_idle(struct macb *bp)
665+
{
666+
struct macb_queue *queue;
667+
unsigned int q;
668+
669+
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
670+
if (READ_ONCE(queue->tx_head) != READ_ONCE(queue->tx_tail))
671+
return false;
672+
}
673+
return true;
674+
}
675+
676+
static void macb_tx_lpi_work_fn(struct work_struct *work)
677+
{
678+
struct macb *bp = container_of(work, struct macb, tx_lpi_work.work);
679+
unsigned long flags;
680+
681+
spin_lock_irqsave(&bp->lock, flags);
682+
if (bp->eee_active && macb_tx_all_queues_idle(bp))
683+
macb_tx_lpi_set(bp, true);
684+
spin_unlock_irqrestore(&bp->lock, flags);
685+
}
686+
687+
static void macb_tx_lpi_schedule(struct macb *bp)
688+
{
689+
if (bp->eee_active)
690+
mod_delayed_work(system_wq, &bp->tx_lpi_work,
691+
usecs_to_jiffies(bp->tx_lpi_timer));
692+
}
693+
694+
/* Wake from LPI before transmitting. The MAC must deassert TXLPIEN
695+
* and wait for the PHY to exit LPI before any frame can be sent.
696+
* IEEE 802.3az Tw_sys is ~17us for 1000BASE-T, ~30us for 100BASE-TX;
697+
* we use a conservative 50us.
698+
*/
699+
static void macb_tx_lpi_wake(struct macb *bp)
700+
{
701+
lockdep_assert_held(&bp->lock);
702+
703+
if (!bp->eee_active)
704+
return;
705+
706+
if (!macb_tx_lpi_set(bp, false))
707+
return;
708+
709+
cancel_delayed_work(&bp->tx_lpi_work);
710+
udelay(50);
711+
}
712+
713+
static void macb_mac_disable_tx_lpi(struct phylink_config *config)
714+
{
715+
struct net_device *ndev = to_net_dev(config->dev);
716+
struct macb *bp = netdev_priv(ndev);
717+
unsigned long flags;
718+
719+
cancel_delayed_work_sync(&bp->tx_lpi_work);
720+
721+
spin_lock_irqsave(&bp->lock, flags);
722+
bp->eee_active = false;
723+
macb_tx_lpi_set(bp, false);
724+
spin_unlock_irqrestore(&bp->lock, flags);
725+
}
726+
727+
static int macb_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
728+
bool tx_clk_stop)
729+
{
730+
struct net_device *ndev = to_net_dev(config->dev);
731+
struct macb *bp = netdev_priv(ndev);
732+
unsigned long flags;
733+
734+
spin_lock_irqsave(&bp->lock, flags);
735+
bp->tx_lpi_timer = timer;
736+
bp->eee_active = true;
737+
spin_unlock_irqrestore(&bp->lock, flags);
738+
739+
/* Defer initial LPI entry by 1 second after link-up per
740+
* IEEE 802.3az section 22.7a.
741+
*/
742+
mod_delayed_work(system_wq, &bp->tx_lpi_work, msecs_to_jiffies(1000));
743+
744+
return 0;
745+
}
746+
645747
static void macb_mac_config(struct phylink_config *config, unsigned int mode,
646748
const struct phylink_link_state *state)
647749
{
@@ -806,6 +908,8 @@ static const struct phylink_mac_ops macb_phylink_ops = {
806908
.mac_config = macb_mac_config,
807909
.mac_link_down = macb_mac_link_down,
808910
.mac_link_up = macb_mac_link_up,
911+
.mac_disable_tx_lpi = macb_mac_disable_tx_lpi,
912+
.mac_enable_tx_lpi = macb_mac_enable_tx_lpi,
809913
};
810914

811915
static bool macb_phy_handle_exists(struct device_node *dn)
@@ -901,6 +1005,18 @@ static int macb_mii_probe(struct net_device *dev)
9011005
}
9021006
}
9031007

1008+
/* Configure EEE LPI if supported */
1009+
if (bp->caps & MACB_CAPS_EEE) {
1010+
__set_bit(PHY_INTERFACE_MODE_MII,
1011+
bp->phylink_config.lpi_interfaces);
1012+
__set_bit(PHY_INTERFACE_MODE_GMII,
1013+
bp->phylink_config.lpi_interfaces);
1014+
phy_interface_set_rgmii(bp->phylink_config.lpi_interfaces);
1015+
bp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
1016+
bp->phylink_config.lpi_timer_default = 250000;
1017+
bp->phylink_config.eee_enabled_default = true;
1018+
}
1019+
9041020
bp->phylink = phylink_create(&bp->phylink_config, bp->pdev->dev.fwnode,
9051021
bp->phy_interface, &macb_phylink_ops);
9061022
if (IS_ERR(bp->phylink)) {
@@ -1302,6 +1418,9 @@ static int macb_tx_complete(struct macb_queue *queue, int budget)
13021418
netif_wake_subqueue(bp->dev, queue_index);
13031419
spin_unlock_irqrestore(&queue->tx_ptr_lock, flags);
13041420

1421+
if (packets)
1422+
macb_tx_lpi_schedule(bp);
1423+
13051424
return packets;
13061425
}
13071426

@@ -2411,6 +2530,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
24112530
skb->len);
24122531

24132532
spin_lock(&bp->lock);
2533+
macb_tx_lpi_wake(bp);
24142534

24152535
/* TSTART write might get dropped, so make the IRQ retrigger a buffer read */
24162536
if (macb_readl(bp, TSR) & MACB_BIT(TGO))
@@ -3108,6 +3228,8 @@ static int macb_close(struct net_device *dev)
31083228
netdev_tx_reset_queue(netdev_get_tx_queue(dev, q));
31093229
}
31103230

3231+
cancel_delayed_work_sync(&bp->tx_lpi_work);
3232+
31113233
phylink_stop(bp->phylink);
31123234
phylink_disconnect_phy(bp->phylink);
31133235

@@ -5746,6 +5868,7 @@ static int macb_probe(struct platform_device *pdev)
57465868
}
57475869

57485870
INIT_WORK(&bp->hresp_err_bh_work, macb_hresp_error_task);
5871+
INIT_DELAYED_WORK(&bp->tx_lpi_work, macb_tx_lpi_work_fn);
57495872

57505873
netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
57515874
macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
@@ -5790,6 +5913,7 @@ static void macb_remove(struct platform_device *pdev)
57905913
mdiobus_free(bp->mii_bus);
57915914

57925915
device_set_wakeup_enable(&bp->pdev->dev, 0);
5916+
cancel_delayed_work_sync(&bp->tx_lpi_work);
57935917
cancel_work_sync(&bp->hresp_err_bh_work);
57945918
pm_runtime_disable(&pdev->dev);
57955919
pm_runtime_dont_use_autosuspend(&pdev->dev);

0 commit comments

Comments
 (0)