[PATCH net v4 14/15] net: stmmac: prepare device-local DMA interrupt quiescence

From: James Hilliard

Date: Sat Sep 26 2026 - 11:59:58 EST


Add DMA interrupt-mask accessors for the supported cores and a
per-channel gate protected by the channel lock. A handler invoked on a
shared IRQ can then mask newly enabled device sources without
acknowledging pending events or touching rings being replaced.

The retained-ring MTU transaction will save and restore these masks and
synchronize the registered handlers. No interrupt-controller line needs
to be disabled, so other devices sharing the IRQ remain serviceable.

Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 11 ++++++++
.../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 1 +
drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 1 +
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 2 ++
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 2 ++
drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 11 ++++++++
drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 2 ++
drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 10 ++++++++
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c | 11 ++++++++
drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 ++++
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 ++
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 30 ++++++++++++++++------
12 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 422d3df21ed9..31bebb2a9bab 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -374,6 +374,16 @@ static void sun8i_dwmac_disable_dma_irq(struct stmmac_priv *priv,
writel(value, ioaddr + EMAC_INT_EN);
}

+static u32 sun8i_dwmac_set_dma_irq_mask(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan, u32 mask)
+{
+ u32 old_mask = readl(ioaddr + EMAC_INT_EN);
+
+ writel(mask, ioaddr + EMAC_INT_EN);
+ readl(ioaddr + EMAC_INT_EN);
+ return old_mask;
+}
+
static void sun8i_dwmac_dma_start_tx(struct stmmac_priv *priv,
void __iomem *ioaddr, u32 chan)
{
@@ -579,6 +589,7 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = {
.enable_dma_transmission = sun8i_dwmac_enable_dma_transmission,
.enable_dma_irq = sun8i_dwmac_enable_dma_irq,
.disable_dma_irq = sun8i_dwmac_disable_dma_irq,
+ .set_irq_mask = sun8i_dwmac_set_dma_irq_mask,
.start_tx = sun8i_dwmac_dma_start_tx,
.stop_tx = sun8i_dwmac_dma_stop_tx,
.start_rx = sun8i_dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 4cb7e6c16bdd..2285eac69071 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -264,6 +264,7 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
.enable_dma_reception = dwmac_enable_dma_reception,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
+ .set_irq_mask = dwmac_set_dma_irq_mask,
.start_tx = dwmac_dma_start_tx,
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
index 5ffd3c1471c4..41579d10af3c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
@@ -118,6 +118,7 @@ const struct stmmac_dma_ops dwmac100_dma_ops = {
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
+ .set_irq_mask = dwmac_set_dma_irq_mask,
.start_tx = dwmac_dma_start_tx,
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index fb0a4e156a7b..86b23ce39d6b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -580,6 +580,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
.dma_tx_mode = dwmac4_dma_tx_chan_op_mode,
.enable_dma_irq = dwmac4_enable_dma_irq,
.disable_dma_irq = dwmac4_disable_dma_irq,
+ .set_irq_mask = dwmac4_set_dma_irq_mask,
.start_tx = dwmac4_dma_start_tx,
.stop_tx = dwmac4_dma_stop_tx,
.start_rx = dwmac4_dma_start_rx,
@@ -611,6 +612,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
.dma_tx_mode = dwmac4_dma_tx_chan_op_mode,
.enable_dma_irq = dwmac4_enable_dma_irq,
.disable_dma_irq = dwmac4_disable_dma_irq,
+ .set_irq_mask = dwmac4_set_dma_irq_mask,
.start_tx = dwmac4_dma_start_tx,
.stop_tx = dwmac4_dma_stop_tx,
.start_rx = dwmac4_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 9352107204eb..edccc09f0b03 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
@@ -184,6 +184,8 @@ void dwmac4_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
+u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan, u32 mask);
void dwmac4_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan);
void dwmac4_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index 9af0565a9bca..477bdb081c52 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -155,6 +155,17 @@ void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
}

+u32 dwmac4_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan, u32 mask)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+
+ writel(mask, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ return old_mask;
+}
+
int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_extra_stats *x, u32 chan, u32 dir)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index 970495bccfd2..4726801253f5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -150,6 +150,8 @@ void dwmac_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
+u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan, u32 mask);
void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan);
void dwmac_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index bb907db8fca1..88d904ed4685 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -84,6 +84,16 @@ void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan));
}

+u32 dwmac_set_dma_irq_mask(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan, u32 mask)
+{
+ u32 old_mask = readl(ioaddr + DMA_CHAN_INTR_ENA(chan));
+
+ writel(mask, ioaddr + DMA_CHAN_INTR_ENA(chan));
+ readl(ioaddr + DMA_CHAN_INTR_ENA(chan));
+ return old_mask;
+}
+
void dwmac_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index ff83858ebc1f..30915d3f5230 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -256,6 +256,16 @@ static void dwxgmac2_disable_dma_irq(struct stmmac_priv *priv,
writel(value, ioaddr + XGMAC_DMA_CH_INT_EN(chan));
}

+static u32 dwxgmac2_set_dma_irq_mask(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan, u32 mask)
+{
+ u32 old_mask = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan));
+
+ writel(mask, ioaddr + XGMAC_DMA_CH_INT_EN(chan));
+ readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan));
+ return old_mask;
+}
+
static void dwxgmac2_dma_start_tx(struct stmmac_priv *priv,
void __iomem *ioaddr, u32 chan)
{
@@ -604,6 +614,7 @@ const struct stmmac_dma_ops dwxgmac210_dma_ops = {
.dma_tx_mode = dwxgmac2_dma_tx_mode,
.enable_dma_irq = dwxgmac2_enable_dma_irq,
.disable_dma_irq = dwxgmac2_disable_dma_irq,
+ .set_irq_mask = dwxgmac2_set_dma_irq_mask,
.start_tx = dwxgmac2_dma_start_tx,
.stop_tx = dwxgmac2_dma_stop_tx,
.start_rx = dwxgmac2_dma_start_rx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 3bc4d955ef5f..e1630e84e91d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -197,6 +197,9 @@ struct stmmac_dma_ops {
u32 chan, bool rx, bool tx);
void (*disable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
+ /* Replace and flush the full interrupt enable mask; return the old mask. */
+ u32 (*set_irq_mask)(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan, u32 mask);
void (*start_tx)(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan);
void (*stop_tx)(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -263,6 +266,8 @@ struct stmmac_dma_ops {
stmmac_do_void_callback(__priv, dma, enable_dma_irq, __priv, __args)
#define stmmac_disable_dma_irq(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, disable_dma_irq, __priv, __args)
+#define stmmac_set_dma_irq_mask(__priv, __args...) \
+ stmmac_do_callback(__priv, dma, set_irq_mask, __priv, __args)
#define stmmac_start_tx(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, start_tx, __priv, __args)
#define stmmac_stop_tx(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index e15bab9accce..9e711acb81fb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -146,6 +146,8 @@ struct stmmac_channel {
struct stmmac_priv *priv_data;
spinlock_t lock;
u32 index;
+ /* Protected by lock; IRQ handlers must not access the DMA rings. */
+ bool irq_quiesced;
};

struct stmmac_fpe_cfg {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 4d70645c4a4f..08049c05ffc4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3267,14 +3267,30 @@ static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)

static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan, u32 dir)
{
- int status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
- &priv->xstats, chan, dir);
- struct stmmac_rx_queue *rx_q = &priv->dma_conf->rx_queue[chan];
- struct stmmac_tx_queue *tx_q = &priv->dma_conf->tx_queue[chan];
struct stmmac_channel *ch = &priv->channel[chan];
+ struct stmmac_rx_queue *rx_q;
+ struct stmmac_tx_queue *tx_q;
struct napi_struct *rx_napi;
struct napi_struct *tx_napi;
unsigned long flags;
+ int status;
+
+ spin_lock_irqsave(&ch->lock, flags);
+ if (unlikely(ch->irq_quiesced)) {
+ /* A shared IRQ may still invoke us, and DMA initialization can
+ * restore interrupt enables. Mask them again without acknowledging
+ * pending events or accessing the configuration being replaced.
+ */
+ stmmac_set_dma_irq_mask(priv, priv->ioaddr, chan, 0);
+ spin_unlock_irqrestore(&ch->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
+ &priv->xstats, chan, dir);
+ rx_q = &priv->dma_conf->rx_queue[chan];
+ tx_q = &priv->dma_conf->tx_queue[chan];

rx_napi = rx_q->xsk_pool ? &ch->rxtx_napi : &ch->rx_napi;
tx_napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
@@ -3995,8 +4011,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->tx_irq[j] > 0) {
irq_set_affinity_hint(msi->tx_irq[j], NULL);
- free_irq(msi->tx_irq[j],
- &priv->channel[j]);
+ free_irq(msi->tx_irq[j], &priv->channel[j]);
}
}
irq_idx = priv->plat->rx_queues_to_use;
@@ -4005,8 +4020,7 @@ static void stmmac_free_irq(struct net_device *dev,
for (j = irq_idx - 1; msi && j >= 0; j--) {
if (msi->rx_irq[j] > 0) {
irq_set_affinity_hint(msi->rx_irq[j], NULL);
- free_irq(msi->rx_irq[j],
- &priv->channel[j]);
+ free_irq(msi->rx_irq[j], &priv->channel[j]);
}
}


--
2.53.0