Commit a3cc3f42 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-bcmgenet-revise-suspend-resume'

Doug Berger says:

====================
net: bcmgenet: revise suspend/resume

This commit set updates the GENET driver to reduce the delay to
resume the ethernet link when the Wake on Lan features are used.

In addition, the encoding of hardware versioning and features is
revised to avoid some redundancy and improve readability as well
as remove a warning that occurred for the BCM7712 device which
updated the device major version while maintaining compatibility
with the driver.

The assignment of hardware descriptor rings was modified to
simplify programming and to allow support for the hardware
RX_CLS_FLOW_DISC filter action.
====================

Link: https://patch.msgid.link/20250306192643.2383632-1-opendmb@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 64fdb808 254f3239
Loading
Loading
Loading
Loading
+494 −595

File changed.

Preview size limit exceeded, changes collapsed.

+38 −14
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2014-2024 Broadcom
 * Copyright (c) 2014-2025 Broadcom
 */

#ifndef __BCMGENET_H__
@@ -18,6 +18,9 @@

#include "../unimac.h"

/* Maximum number of hardware queues, downsized if needed */
#define GENET_MAX_MQ_CNT	4

/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC				256

@@ -271,6 +274,8 @@ struct bcmgenet_mib_counters {
/* Only valid for GENETv3+ */
#define UMAC_IRQ_MDIO_DONE		(1 << 23)
#define UMAC_IRQ_MDIO_ERROR		(1 << 24)
#define UMAC_IRQ_MDIO_EVENT		(UMAC_IRQ_MDIO_DONE | \
					 UMAC_IRQ_MDIO_ERROR)

/* INTRL2 instance 1 definitions */
#define UMAC_IRQ1_TX_INTR_MASK		0xFFFF
@@ -476,6 +481,7 @@ enum bcmgenet_version {
#define GENET_HAS_EXT		(1 << 1)
#define GENET_HAS_MDIO_INTR	(1 << 2)
#define GENET_HAS_MOCA_LINK_DET	(1 << 3)
#define GENET_HAS_EPHY_16NM	(1 << 4)

/* BCMGENET hardware parameters, keep this structure nicely aligned
 * since it is going to be used in hot paths
@@ -496,7 +502,6 @@ struct bcmgenet_hw_params {
	u32		rdma_offset;
	u32		tdma_offset;
	u32		words_per_bd;
	u32		flags;
};

struct bcmgenet_skb_cb {
@@ -513,7 +518,6 @@ struct bcmgenet_tx_ring {
	unsigned long	packets;
	unsigned long	bytes;
	unsigned int	index;		/* ring index */
	unsigned int	queue;		/* queue index */
	struct enet_cb	*cbs;		/* tx ring buffer control block*/
	unsigned int	size;		/* size of each tx ring */
	unsigned int    clean_ptr;      /* Tx ring clean pointer */
@@ -523,8 +527,6 @@ struct bcmgenet_tx_ring {
	unsigned int	prod_index;	/* Tx ring producer index SW copy */
	unsigned int	cb_ptr;		/* Tx ring initial CB ptr */
	unsigned int	end_ptr;	/* Tx ring end CB ptr */
	void (*int_enable)(struct bcmgenet_tx_ring *);
	void (*int_disable)(struct bcmgenet_tx_ring *);
	struct bcmgenet_priv *priv;
};

@@ -553,8 +555,6 @@ struct bcmgenet_rx_ring {
	struct bcmgenet_net_dim dim;
	u32		rx_max_coalesced_frames;
	u32		rx_coalesce_usecs;
	void (*int_enable)(struct bcmgenet_rx_ring *);
	void (*int_disable)(struct bcmgenet_rx_ring *);
	struct bcmgenet_priv *priv;
};

@@ -583,7 +583,7 @@ struct bcmgenet_priv {
	struct enet_cb *tx_cbs;
	unsigned int num_tx_bds;

	struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
	struct bcmgenet_tx_ring tx_rings[GENET_MAX_MQ_CNT + 1];

	/* receive variables */
	void __iomem *rx_bds;
@@ -593,10 +593,11 @@ struct bcmgenet_priv {
	struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
	struct list_head rxnfc_list;

	struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
	struct bcmgenet_rx_ring rx_rings[GENET_MAX_MQ_CNT + 1];

	/* other misc variables */
	struct bcmgenet_hw_params *hw_params;
	const struct bcmgenet_hw_params *hw_params;
	u32 flags;
	unsigned autoneg_pause:1;
	unsigned tx_pause:1;
	unsigned rx_pause:1;
@@ -615,7 +616,6 @@ struct bcmgenet_priv {
	phy_interface_t phy_interface;
	int phy_addr;
	int ext_phy;
	bool ephy_16nm;

	/* Interrupt variables */
	struct work_struct bcmgenet_irq_work;
@@ -643,13 +643,37 @@ struct bcmgenet_priv {
	struct clk *clk_wol;
	u32 wolopts;
	u8 sopass[SOPASS_MAX];
	bool wol_active;

	struct bcmgenet_mib_counters mib;

	struct ethtool_keee eee;
};

static inline bool bcmgenet_has_40bits(struct bcmgenet_priv *priv)
{
	return !!(priv->flags & GENET_HAS_40BITS);
}

static inline bool bcmgenet_has_ext(struct bcmgenet_priv *priv)
{
	return !!(priv->flags & GENET_HAS_EXT);
}

static inline bool bcmgenet_has_mdio_intr(struct bcmgenet_priv *priv)
{
	return !!(priv->flags & GENET_HAS_MDIO_INTR);
}

static inline bool bcmgenet_has_moca_link_det(struct bcmgenet_priv *priv)
{
	return !!(priv->flags & GENET_HAS_MOCA_LINK_DET);
}

static inline bool bcmgenet_has_ephy_16nm(struct bcmgenet_priv *priv)
{
	return !!(priv->flags & GENET_HAS_EPHY_16NM);
}

#define GENET_IO_MACRO(name, offset)					\
static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv,	\
					u32 off)			\
@@ -702,7 +726,7 @@ void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
				enum bcmgenet_power_mode mode);
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
			      enum bcmgenet_power_mode mode);

void bcmgenet_eee_enable_set(struct net_device *dev, bool enable,
+42 −47
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
 *
 * Copyright (c) 2014-2024 Broadcom
 * Copyright (c) 2014-2025 Broadcom
 */

#define pr_fmt(fmt)				"bcmgenet_wol: " fmt
@@ -145,8 +145,7 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
				enum bcmgenet_power_mode mode)
{
	struct net_device *dev = priv->dev;
	struct bcmgenet_rxnfc_rule *rule;
	u32 reg, hfb_ctrl_reg, hfb_enable = 0;
	u32 reg, hfb_ctrl_reg;
	int retries = 0;

	if (mode != GENET_POWER_WOL_MAGIC) {
@@ -154,18 +153,6 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
		return -EINVAL;
	}

	/* Can't suspend with WoL if MAC is still in reset */
	spin_lock_bh(&priv->reg_lock);
	reg = bcmgenet_umac_readl(priv, UMAC_CMD);
	if (reg & CMD_SW_RESET)
		reg &= ~CMD_SW_RESET;

	/* disable RX */
	reg &= ~CMD_RX_EN;
	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
	spin_unlock_bh(&priv->reg_lock);
	mdelay(10);

	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
		reg |= MPD_EN;
@@ -177,13 +164,8 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
	}

	hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
	if (priv->wolopts & WAKE_FILTER) {
		list_for_each_entry(rule, &priv->rxnfc_list, list)
			if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
				hfb_enable |= (1 << rule->fs.location);
		reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
	reg = hfb_ctrl_reg | RBUF_ACPI_EN;
	bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
	}

	/* Do not leave UniMAC in MPD mode only */
	retries = bcmgenet_poll_wol_status(priv);
@@ -198,15 +180,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
	netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
		  retries);

	clk_prepare_enable(priv->clk_wol);
	priv->wol_active = 1;
	/* Disable phy status updates while suspending */
	mutex_lock(&dev->phydev->lock);
	dev->phydev->state = PHY_READY;
	mutex_unlock(&dev->phydev->lock);

	if (hfb_enable) {
		bcmgenet_hfb_reg_writel(priv, hfb_enable,
					HFB_FLT_ENABLE_V3PLUS + 4);
		hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
		bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
	}
	clk_prepare_enable(priv->clk_wol);

	/* Enable CRC forward */
	spin_lock_bh(&priv->reg_lock);
@@ -214,13 +193,17 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
	priv->crc_fwd_en = 1;
	reg |= CMD_CRC_FWD;

	/* Can't suspend with WoL if MAC is still in reset */
	if (reg & CMD_SW_RESET)
		reg &= ~CMD_SW_RESET;

	/* Receiver must be enabled for WOL MP detection */
	reg |= CMD_RX_EN;
	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
	spin_unlock_bh(&priv->reg_lock);

	reg = UMAC_IRQ_MPD_R;
	if (hfb_enable)
	if (hfb_ctrl_reg & RBUF_HFB_EN)
		reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;

	bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
@@ -228,40 +211,42 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
	return 0;
}

void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
int bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
			      enum bcmgenet_power_mode mode)
{
	struct net_device *dev = priv->dev;
	u32 reg;

	if (mode != GENET_POWER_WOL_MAGIC) {
		netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
		return;
		return -EINVAL;
	}

	if (!priv->wol_active)
		return;	/* failed to suspend so skip the rest */

	priv->wol_active = 0;
	clk_disable_unprepare(priv->clk_wol);
	priv->crc_fwd_en = 0;

	bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT,
				 INTRL2_CPU_MASK_SET);
	if (bcmgenet_has_mdio_intr(priv))
		bcmgenet_intrl2_0_writel(priv,
					 UMAC_IRQ_MDIO_EVENT,
					 INTRL2_CPU_MASK_CLEAR);

	/* Disable Magic Packet Detection */
	if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
		reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
		if (!(reg & MPD_EN))
			return;	/* already reset so skip the rest */
			return -EPERM;	/* already reset so skip the rest */
		reg &= ~(MPD_EN | MPD_PW_EN);
		bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
	}

	/* Disable WAKE_FILTER Detection */
	if (priv->wolopts & WAKE_FILTER) {
	/* Disable ACPI mode */
	reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
	if (!(reg & RBUF_ACPI_EN))
			return;	/* already reset so skip the rest */
		reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
		return -EPERM;	/* already reset so skip the rest */
	reg &= ~RBUF_ACPI_EN;
	bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
	}

	/* Disable CRC Forward */
	spin_lock_bh(&priv->reg_lock);
@@ -269,4 +254,14 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
	reg &= ~CMD_CRC_FWD;
	bcmgenet_umac_writel(priv, reg, UMAC_CMD);
	spin_unlock_bh(&priv->reg_lock);

	/* Resume link status tracking */
	mutex_lock(&dev->phydev->lock);
	if (dev->phydev->link)
		dev->phydev->state = PHY_RUNNING;
	else
		dev->phydev->state = PHY_NOLINK;
	mutex_unlock(&dev->phydev->lock);

	return 0;
}
+3 −3
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/*
 * Broadcom GENET MDIO routines
 *
 * Copyright (c) 2014-2024 Broadcom
 * Copyright (c) 2014-2025 Broadcom
 */

#include <linux/acpi.h>
@@ -154,7 +154,7 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
	u32 reg = 0;

	/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
	if (GENET_IS_V4(priv) || priv->ephy_16nm) {
	if (GENET_IS_V4(priv) || bcmgenet_has_ephy_16nm(priv)) {
		reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
		if (enable) {
			reg &= ~EXT_CK25_DIS;
@@ -184,7 +184,7 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable)

static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
	if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
	if (bcmgenet_has_moca_link_det(priv))
		fixed_phy_set_link_update(priv->dev->phydev,
					  bcmgenet_fixed_phy_link_update);
}