Merge branch 'net-enetc-restore-rx-ring-congestion-mode-after-ring-reconfiguration'

Wei Fang says:

====================
net: enetc: restore RX ring congestion mode after ring reconfiguration

The RX BD ring congestion mode (CM) enables the ENETC MAC to generate
PAUSE frames when ingress congestion occurs. It is configured only in
the phylink .mac_link_up() callback, which is invoked when the link
status changes.

However, enetc_reconfigure() tears down and re-creates the RX BD rings
at runtime without any link status change, for example when enabling or
disabling PTP RX hardware timestamping. enetc_setup_rxbdr() rebuilds the
RBMR register from zero, which clears the CM bit, and since the link
status does not change, .mac_link_up() is not called again to restore
it. As a result, flow control silently stops working after such a
reconfiguration.

To solve this issue, track the desired CM state in a software flag
ENETC_RXBDR_CM, which is maintained by the .mac_link_up() /
.mac_link_down() callbacks and consulted by enetc_setup_rxbdr() when the
RX BD rings are (re)configured. Both ENETC v1 and ENETC v4 are affected
and are fixed in the same way.
====================

Link: https://patch.msgid.link/20260821064140.1315611-1-wei.fang@oss.nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-24 11:56:39 -07:00
commit 01ba856c36
4 changed files with 72 additions and 39 deletions

View File

@ -81,6 +81,33 @@ void enetc_reset_mac_addr_filter(struct enetc_mac_filter *filter)
}
EXPORT_SYMBOL_GPL(enetc_reset_mac_addr_filter);
void enetc_set_congestion_mode(struct enetc_ndev_priv *priv, bool enable)
{
struct enetc_si *si = priv->si;
struct enetc_hw *hw = &si->hw;
spin_lock(&si->gen_lock);
if (enable)
set_bit(ENETC_RXBDR_CM, &priv->flags);
else
clear_bit(ENETC_RXBDR_CM, &priv->flags);
for (int i = 0; i < priv->num_rx_rings; i++) {
u32 old_rbmr = enetc_rxbdr_rd(hw, i, ENETC_RBMR);
u32 rbmr;
rbmr = u32_replace_bits(old_rbmr, enable, ENETC_RBMR_CM);
if (rbmr == old_rbmr)
continue;
enetc_rxbdr_wr(hw, i, ENETC_RBMR, rbmr);
}
spin_unlock(&si->gen_lock);
}
EXPORT_SYMBOL_GPL(enetc_set_congestion_mode);
static int enetc_num_stack_tx_queues(struct enetc_ndev_priv *priv)
{
int num_tx_rings = priv->num_tx_rings;
@ -2632,7 +2659,6 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring,
bool extended)
{
int idx = rx_ring->index;
u32 rbmr = 0;
enetc_rxbdr_wr(hw, idx, ENETC_RBBAR0,
lower_32_bits(rx_ring->bd_dma_base));
@ -2660,12 +2686,6 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring,
enetc_rxbdr_wr(hw, idx, ENETC_RBICR0, ENETC_RBICR0_ICEN | 0x1);
rx_ring->ext_en = extended;
if (rx_ring->ext_en)
rbmr |= ENETC_RBMR_BDS;
if (rx_ring->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
rbmr |= ENETC_RBMR_VTE;
rx_ring->rcir = hw->reg + ENETC_BDR(RX, idx, ENETC_RBCIR);
rx_ring->idr = hw->reg + ENETC_SIRXIDR;
@ -2676,8 +2696,6 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring,
enetc_lock_mdio();
enetc_refill_rx_ring(rx_ring, enetc_bd_unused(rx_ring));
enetc_unlock_mdio();
enetc_rxbdr_wr(hw, idx, ENETC_RBMR, rbmr);
}
static void enetc_setup_bdrs(struct enetc_ndev_priv *priv, bool extended)
@ -2704,21 +2722,34 @@ static void enetc_enable_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring)
static void enetc_enable_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring)
{
struct enetc_ndev_priv *priv = netdev_priv(rx_ring->ndev);
int idx = rx_ring->index;
u32 rbmr;
u32 rbmr = ENETC_RBMR_EN;
if (rx_ring->ext_en)
rbmr |= ENETC_RBMR_BDS;
if (rx_ring->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)
rbmr |= ENETC_RBMR_VTE;
if (test_bit(ENETC_RXBDR_CM, &priv->flags))
rbmr |= ENETC_RBMR_CM;
rbmr = enetc_rxbdr_rd(hw, idx, ENETC_RBMR);
rbmr |= ENETC_RBMR_EN;
enetc_rxbdr_wr(hw, idx, ENETC_RBMR, rbmr);
}
static void enetc_enable_rx_bdrs(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
struct enetc_hw *hw = &si->hw;
int i;
spin_lock(&si->gen_lock);
for (i = 0; i < priv->num_rx_rings; i++)
enetc_enable_rxbdr(hw, priv->rx_ring[i]);
spin_unlock(&si->gen_lock);
}
static void enetc_enable_tx_bdrs(struct enetc_ndev_priv *priv)
@ -2748,11 +2779,16 @@ static void enetc_disable_txbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring)
static void enetc_disable_rx_bdrs(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
struct enetc_hw *hw = &si->hw;
int i;
spin_lock(&si->gen_lock);
for (i = 0; i < priv->num_rx_rings; i++)
enetc_disable_rxbdr(hw, priv->rx_ring[i]);
spin_unlock(&si->gen_lock);
}
static void enetc_disable_tx_bdrs(struct enetc_ndev_priv *priv)
@ -3344,11 +3380,16 @@ EXPORT_SYMBOL_GPL(enetc_get_stats);
static void enetc_enable_rxvlan(struct net_device *ndev, bool en)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_hw *hw = &priv->si->hw;
struct enetc_si *si = priv->si;
struct enetc_hw *hw = &si->hw;
int i;
spin_lock(&si->gen_lock);
for (i = 0; i < priv->num_rx_rings; i++)
enetc_bdr_enable_rxvlan(hw, i, en);
spin_unlock(&si->gen_lock);
}
static void enetc_enable_txvlan(struct net_device *ndev, bool en)
@ -3679,6 +3720,7 @@ int enetc_pci_probe(struct pci_dev *pdev, const char *name, int sizeof_priv)
si = PTR_ALIGN(p, ENETC_SI_ALIGN);
si->pad = (char *)si - (char *)p;
spin_lock_init(&si->gen_lock);
pci_set_drvdata(pdev, si);
si->pdev = pdev;

View File

@ -309,6 +309,13 @@ struct enetc_si {
struct net_device *ndev; /* back ref. */
/* General-purpose lock serializing updates that must not race,
* e.g. read-modify-write of shared hardware registers and of
* selected priv->flags bits between the phylink link callbacks
* and the ring (re)configuration path.
*/
spinlock_t gen_lock;
union {
struct enetc_cbdr cbd_ring; /* Only ENETC 1.0 */
struct ntmp_user ntmp_user; /* ENETC 4.1 and later */
@ -417,6 +424,7 @@ enum enetc_active_offloads {
enum enetc_flags_bit {
ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS = 0,
ENETC_TX_DOWN,
ENETC_RXBDR_CM,
};
/* interrupt coalescing modes */
@ -505,6 +513,7 @@ int enetc_get_driver_data(struct enetc_si *si);
void enetc_add_mac_addr_ht_filter(struct enetc_mac_filter *filter,
const unsigned char *addr);
void enetc_reset_mac_addr_filter(struct enetc_mac_filter *filter);
void enetc_set_congestion_mode(struct enetc_ndev_priv *priv, bool enable);
int enetc_open(struct net_device *ndev);
int enetc_close(struct net_device *ndev);

View File

@ -718,22 +718,14 @@ static void enetc4_set_rx_pause(struct enetc_pf *pf, bool rx_pause)
enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val);
}
static void enetc4_set_tx_pause(struct enetc_pf *pf, int num_rxbdr, bool tx_pause)
static void enetc4_set_tx_pause(struct enetc_pf *pf, bool tx_pause)
{
struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
u32 pause_off_thresh = 0, pause_on_thresh = 0;
u32 init_quanta = 0, refresh_quanta = 0;
struct enetc_hw *hw = &pf->si->hw;
u32 rbmr, old_rbmr;
int i;
for (i = 0; i < num_rxbdr; i++) {
old_rbmr = enetc_rxbdr_rd(hw, i, ENETC_RBMR);
rbmr = u32_replace_bits(old_rbmr, tx_pause ? 1 : 0, ENETC_RBMR_CM);
if (rbmr == old_rbmr)
continue;
enetc_rxbdr_wr(hw, i, ENETC_RBMR, rbmr);
}
enetc_set_congestion_mode(priv, tx_pause);
if (tx_pause) {
/* When the port first enters congestion, send a PAUSE request
@ -898,7 +890,7 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config,
tx_pause = false;
}
enetc4_set_tx_pause(pf, priv->num_rx_rings, tx_pause);
enetc4_set_tx_pause(pf, tx_pause);
enetc4_set_rx_pause(pf, rx_pause);
enetc4_mac_tx_enable(pf);
enetc4_mac_rx_enable(pf);

View File

@ -556,8 +556,7 @@ static void enetc_pl_mac_link_up(struct phylink_config *config,
struct enetc_hw *hw = &pf->si->hw;
struct enetc_si *si = pf->si;
struct enetc_ndev_priv *priv;
u32 rbmr, cmd_cfg;
int idx;
u32 cmd_cfg;
priv = netdev_priv(pf->si->ndev);
@ -569,16 +568,7 @@ static void enetc_pl_mac_link_up(struct phylink_config *config,
enetc_force_rgmii_mac(si, speed, duplex);
/* Flow control */
for (idx = 0; idx < priv->num_rx_rings; idx++) {
rbmr = enetc_rxbdr_rd(hw, idx, ENETC_RBMR);
if (tx_pause)
rbmr |= ENETC_RBMR_CM;
else
rbmr &= ~ENETC_RBMR_CM;
enetc_rxbdr_wr(hw, idx, ENETC_RBMR, rbmr);
}
enetc_set_congestion_mode(priv, tx_pause);
if (tx_pause) {
/* When the port first enters congestion, send a PAUSE request