Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,6 @@ rp1_target: &pcie2 {
phy1: ethernet-phy@1 {
reg = <0x1>;
brcm,powerdown-enable;
eee-broken-1000t;
eee-broken-100tx;
};
};

Expand Down
2 changes: 0 additions & 2 deletions arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};

Expand Down
23 changes: 23 additions & 0 deletions drivers/net/ethernet/cadence/macb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1038,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
Expand Down Expand Up @@ -1137,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)
Expand Down Expand Up @@ -1341,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
Expand Down
147 changes: 144 additions & 3 deletions drivers/net/ethernet/cadence/macb_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,71 @@
.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 500

/* 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");
}

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_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);
}

/* 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_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
Expand Down Expand Up @@ -728,10 +793,16 @@
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);
}

Expand Down Expand Up @@ -799,6 +870,19 @@
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,
Expand Down Expand Up @@ -1312,6 +1396,11 @@
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;
Expand Down Expand Up @@ -2341,6 +2430,10 @@
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;
Expand Down Expand Up @@ -3064,6 +3157,9 @@
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)
Expand Down Expand Up @@ -3095,6 +3191,8 @@

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);
Expand Down Expand Up @@ -3497,6 +3595,42 @@
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()

Check failure on line 3626 in drivers/net/ethernet/cadence/macb_main.c

View workflow job for this annotation

GitHub Actions / checkpatch review

WARNING: networking block comments don't use an empty /* line, use /* Comment...
* 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)
{
Expand Down Expand Up @@ -3912,6 +4046,9 @@
.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,
};

static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
Expand Down Expand Up @@ -5079,7 +5216,8 @@
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,
Expand Down Expand Up @@ -5337,6 +5475,8 @@
}

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),
Expand Down Expand Up @@ -5381,6 +5521,7 @@
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);
Expand Down
24 changes: 14 additions & 10 deletions drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Loading