@@ -656,6 +656,71 @@ static const struct phylink_pcs_ops macb_phylink_pcs_ops = {
656656 .pcs_config = macb_pcs_config ,
657657};
658658
659+ /* Default TX LPI idle timeout in milliseconds.
660+ * The MAC will enter LPI after this period of TX inactivity.
661+ */
662+ #define MACB_TX_LPI_TIMER_DEFAULT_MS 5
663+
664+ /* PHY wake time from LPI in microseconds.
665+ * IEEE 802.3az: Tw_sys is ~17us for 1000BASE-T, ~30us for 100BASE-TX.
666+ * Use a conservative value to ensure the PHY has fully exited LPI.
667+ */
668+ #define MACB_TX_LPI_WAKE_TIME_US 50
669+
670+ static void macb_tx_lpi_set (struct macb * bp , bool enable )
671+ {
672+ unsigned long flags ;
673+ u32 ncr ;
674+
675+ spin_lock_irqsave (& bp -> lock , flags );
676+
677+ ncr = macb_readl (bp , NCR );
678+ if (enable )
679+ ncr |= GEM_BIT (TXLPIEN );
680+ else
681+ ncr &= ~GEM_BIT (TXLPIEN );
682+ macb_writel (bp , NCR , ncr );
683+
684+ bp -> tx_lpi_enabled = enable ;
685+
686+ spin_unlock_irqrestore (& bp -> lock , flags );
687+
688+ netdev_dbg (bp -> dev , "EEE TX LPI %s\n" ,
689+ enable ? "enabled" : "disabled" );
690+ }
691+
692+ static void macb_tx_lpi_work_fn (struct work_struct * work )
693+ {
694+ struct macb * bp = container_of (work , struct macb , tx_lpi_work .work );
695+
696+ if (bp -> eee_active )
697+ macb_tx_lpi_set (bp , true);
698+ }
699+
700+ /* Called from TX path to wake from LPI before transmitting */
701+ static inline void macb_tx_lpi_wake (struct macb * bp )
702+ {
703+ if (!bp -> tx_lpi_enabled )
704+ return ;
705+
706+ macb_tx_lpi_set (bp , false);
707+ /* Cancel any pending re-entry */
708+ cancel_delayed_work (& bp -> tx_lpi_work );
709+
710+ /* Wait for PHY to exit LPI before transmitting */
711+ udelay (MACB_TX_LPI_WAKE_TIME_US );
712+ }
713+
714+ /* Schedule LPI re-entry after TX idle timeout */
715+ static inline void macb_tx_lpi_schedule (struct macb * bp )
716+ {
717+ if (!bp -> eee_active )
718+ return ;
719+
720+ schedule_delayed_work (& bp -> tx_lpi_work ,
721+ msecs_to_jiffies (bp -> tx_lpi_timer_ms ));
722+ }
723+
659724static void macb_mac_config (struct phylink_config * config , unsigned int mode ,
660725 const struct phylink_link_state * state )
661726{
@@ -728,10 +793,16 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode,
728793 queue_writel (queue , IDR ,
729794 bp -> rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT (HRESP ));
730795
731- /* Disable Rx and Tx */
732- ctrl = macb_readl (bp , NCR ) & ~(MACB_BIT (RE ) | MACB_BIT (TE ));
796+ /* Cancel any pending LPI entry */
797+ cancel_delayed_work (& bp -> tx_lpi_work );
798+
799+ /* Disable TX LPI, Rx, and Tx */
800+ ctrl = macb_readl (bp , NCR ) & ~(GEM_BIT (TXLPIEN ) | MACB_BIT (RE ) | MACB_BIT (TE ));
733801 macb_writel (bp , NCR , ctrl );
734802
803+ bp -> eee_active = false;
804+ bp -> tx_lpi_enabled = false;
805+
735806 netif_tx_stop_all_queues (ndev );
736807}
737808
@@ -799,6 +870,19 @@ static void macb_mac_link_up(struct phylink_config *config,
799870 macb_writel (bp , NCR , ctrl | MACB_BIT (RE ) | MACB_BIT (TE ));
800871
801872 netif_tx_wake_all_queues (ndev );
873+
874+ /* EEE: check if link partner negotiated EEE.
875+ * Per IEEE 802.3az / Microchip GMAC docs: LPI must not be
876+ * requested until the link has been up for at least 1 second.
877+ */
878+ if (phy && (bp -> caps & MACB_CAPS_EEE )) {
879+ bp -> eee_active = phy_init_eee (phy , false) >= 0 &&
880+ phy -> enable_tx_lpi ;
881+ netdev_dbg (ndev , "EEE: active=%d\n" , bp -> eee_active );
882+ if (bp -> eee_active )
883+ schedule_delayed_work (& bp -> tx_lpi_work ,
884+ msecs_to_jiffies (1000 ));
885+ }
802886}
803887
804888static struct phylink_pcs * macb_mac_select_pcs (struct phylink_config * config ,
@@ -1312,6 +1396,11 @@ static int macb_tx_complete(struct macb_queue *queue, int budget)
13121396 CIRC_CNT (queue -> tx_head , queue -> tx_tail ,
13131397 bp -> tx_ring_size ) <= MACB_TX_WAKEUP_THRESH (bp ))
13141398 netif_wake_subqueue (bp -> dev , queue_index );
1399+
1400+ /* Schedule LPI re-entry when TX ring is drained */
1401+ if (queue -> tx_head == queue -> tx_tail )
1402+ macb_tx_lpi_schedule (bp );
1403+
13151404 spin_unlock_irqrestore (& queue -> tx_ptr_lock , flags );
13161405
13171406 return packets ;
@@ -2341,6 +2430,10 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
23412430 bool is_lso ;
23422431 netdev_tx_t ret = NETDEV_TX_OK ;
23432432
2433+ /* Wake from LPI before transmitting */
2434+ if (unlikely (bp -> tx_lpi_enabled ))
2435+ macb_tx_lpi_wake (bp );
2436+
23442437 if (macb_clear_csum (skb )) {
23452438 dev_kfree_skb_any (skb );
23462439 return ret ;
@@ -3064,6 +3157,9 @@ static int macb_open(struct net_device *dev)
30643157 if (err )
30653158 goto phy_off ;
30663159
3160+ if ((bp -> caps & MACB_CAPS_EEE ) && dev -> phydev )
3161+ phy_support_eee (dev -> phydev );
3162+
30673163 netif_tx_start_all_queues (dev );
30683164
30693165 if (bp -> ptp_info )
@@ -3095,6 +3191,8 @@ static int macb_close(struct net_device *dev)
30953191
30963192 netif_tx_stop_all_queues (dev );
30973193
3194+ cancel_delayed_work_sync (& bp -> tx_lpi_work );
3195+
30983196 for (q = 0 , queue = bp -> queues ; q < bp -> num_queues ; ++ q , ++ queue ) {
30993197 napi_disable (& queue -> napi_rx );
31003198 napi_disable (& queue -> napi_tx );
@@ -5338,6 +5436,8 @@ static int macb_probe(struct platform_device *pdev)
53385436 }
53395437
53405438 INIT_WORK (& bp -> hresp_err_bh_work , macb_hresp_error_task );
5439+ INIT_DELAYED_WORK (& bp -> tx_lpi_work , macb_tx_lpi_work_fn );
5440+ bp -> tx_lpi_timer_ms = MACB_TX_LPI_TIMER_DEFAULT_MS ;
53415441
53425442 netdev_info (dev , "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n" ,
53435443 macb_is_gem (bp ) ? "GEM" : "MACB" , macb_readl (bp , MID ),
@@ -5382,6 +5482,7 @@ static void macb_remove(struct platform_device *pdev)
53825482 mdiobus_free (bp -> mii_bus );
53835483
53845484 device_set_wakeup_enable (& bp -> pdev -> dev , 0 );
5485+ cancel_delayed_work_sync (& bp -> tx_lpi_work );
53855486 cancel_work_sync (& bp -> hresp_err_bh_work );
53865487 pm_runtime_disable (& pdev -> dev );
53875488 pm_runtime_dont_use_autosuspend (& pdev -> dev );
0 commit comments