.../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 24 +++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 ++++ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++ 3 files changed, 32 insertions(+)
Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU
interrupts"), suspending causes an interrupt storm from the RPS
interrupt.
Fix this by adding a deinit_chan() op to stmmac_dma_ops, which
masks all default dma channel interrupts. This is called from
stmmac_stop_all_dma(), so interrupts don't trigger while suspending.
Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
Suggested-by: Andrew Lunn <andrew@lunn.ch>
Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: Luis Lang <luis.la@mail.de>
---
.../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 24 +++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 ++++
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++
3 files changed, 32 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 829a23bdad01..23ffe1adcd0d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
}
+static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 value;
+
+ value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ value &= ~DMA_CHAN_INTR_DEFAULT_MASK;
+ writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg, u32 chan)
@@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
}
+static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 value;
+
+ value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10;
+ writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
static void dwmac4_dma_init(void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg)
{
@@ -548,6 +570,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
.reset = dwmac4_dma_reset,
.init = dwmac4_dma_init,
.init_chan = dwmac4_dma_init_channel,
+ .deinit_chan = dwmac4_dma_deinit_channel,
.init_rx_chan = dwmac4_dma_init_rx_chan,
.init_tx_chan = dwmac4_dma_init_tx_chan,
.axi = dwmac4_dma_axi,
@@ -577,6 +600,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
.reset = dwmac4_dma_reset,
.init = dwmac4_dma_init,
.init_chan = dwmac410_dma_init_channel,
+ .deinit_chan = dwmac410_dma_deinit_channel,
.init_rx_chan = dwmac4_dma_init_rx_chan,
.init_tx_chan = dwmac4_dma_init_tx_chan,
.axi = dwmac4_dma_axi,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index e6317b94fff7..04dafec021b4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -170,6 +170,8 @@ struct stmmac_dma_ops {
void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg);
void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg, u32 chan);
+ void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan);
void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg,
dma_addr_t phy, u32 chan);
@@ -235,6 +237,8 @@ struct stmmac_dma_ops {
stmmac_do_void_callback(__priv, dma, init, __args)
#define stmmac_init_chan(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args)
+#define stmmac_deinit_chan(__priv, __args...) \
+ stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args)
#define stmmac_init_rx_chan(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
#define stmmac_init_tx_chan(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 2a0d7eff88d3..af29a50ddb89 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2560,6 +2560,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
{
u8 rx_channels_count = priv->plat->rx_queues_to_use;
u8 tx_channels_count = priv->plat->tx_queues_to_use;
+ u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
u8 chan;
for (chan = 0; chan < rx_channels_count; chan++)
@@ -2567,6 +2568,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
for (chan = 0; chan < tx_channels_count; chan++)
stmmac_stop_tx_dma(priv, chan);
+
+ for (chan = 0; chan < dma_csr_ch; chan++)
+ stmmac_deinit_chan(priv, priv->ioaddr, chan);
}
/**
--
2.55.0
Hi Luis,
On 7/20/26 13:15, Luis Lang wrote:
> Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU
> interrupts"), suspending causes an interrupt storm from the RPS
> interrupt.
> Fix this by adding a deinit_chan() op to stmmac_dma_ops, which
> masks all default dma channel interrupts. This is called from
> stmmac_stop_all_dma(), so interrupts don't trigger while suspending.
>
> Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
> Suggested-by: Andrew Lunn <andrew@lunn.ch>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Signed-off-by: Luis Lang <luis.la@mail.de>
I wasn't able to reproduce the original issue on dwmac4, however
I could test that suspend/resume as well as WoL still works on a
dwmac4 device with this patch applied.
Thanks for the patch !
Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Maxime
> ---
> .../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 24 +++++++++++++++++++
> drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 ++++
> .../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++
> 3 files changed, 32 insertions(+)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> index 829a23bdad01..23ffe1adcd0d 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> @@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
> ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> }
>
> +static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv,
> + void __iomem *ioaddr, u32 chan)
> +{
> + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
> + u32 value;
> +
> + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> + value &= ~DMA_CHAN_INTR_DEFAULT_MASK;
> + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> +}
> +
> static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
> void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg, u32 chan)
> @@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
> ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> }
>
> +static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv,
> + void __iomem *ioaddr, u32 chan)
> +{
> + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
> + u32 value;
> +
> + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> + value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10;
> + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> +}
> +
> static void dwmac4_dma_init(void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg)
> {
> @@ -548,6 +570,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
> .reset = dwmac4_dma_reset,
> .init = dwmac4_dma_init,
> .init_chan = dwmac4_dma_init_channel,
> + .deinit_chan = dwmac4_dma_deinit_channel,
> .init_rx_chan = dwmac4_dma_init_rx_chan,
> .init_tx_chan = dwmac4_dma_init_tx_chan,
> .axi = dwmac4_dma_axi,
> @@ -577,6 +600,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
> .reset = dwmac4_dma_reset,
> .init = dwmac4_dma_init,
> .init_chan = dwmac410_dma_init_channel,
> + .deinit_chan = dwmac410_dma_deinit_channel,
> .init_rx_chan = dwmac4_dma_init_rx_chan,
> .init_tx_chan = dwmac4_dma_init_tx_chan,
> .axi = dwmac4_dma_axi,
> diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> index e6317b94fff7..04dafec021b4 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> @@ -170,6 +170,8 @@ struct stmmac_dma_ops {
> void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg);
> void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg, u32 chan);
> + void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> + u32 chan);
> void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg,
> dma_addr_t phy, u32 chan);
> @@ -235,6 +237,8 @@ struct stmmac_dma_ops {
> stmmac_do_void_callback(__priv, dma, init, __args)
> #define stmmac_init_chan(__priv, __args...) \
> stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args)
> +#define stmmac_deinit_chan(__priv, __args...) \
> + stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args)
> #define stmmac_init_rx_chan(__priv, __args...) \
> stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
> #define stmmac_init_tx_chan(__priv, __args...) \
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 2a0d7eff88d3..af29a50ddb89 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -2560,6 +2560,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
> {
> u8 rx_channels_count = priv->plat->rx_queues_to_use;
> u8 tx_channels_count = priv->plat->tx_queues_to_use;
> + u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
> u8 chan;
>
> for (chan = 0; chan < rx_channels_count; chan++)
> @@ -2567,6 +2568,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
>
> for (chan = 0; chan < tx_channels_count; chan++)
> stmmac_stop_tx_dma(priv, chan);
> +
> + for (chan = 0; chan < dma_csr_ch; chan++)
> + stmmac_deinit_chan(priv, priv->ioaddr, chan);
> }
>
> /**
On Mon, Jul 20, 2026 at 01:15:29PM +0200, Luis Lang wrote:
> Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU
> interrupts"), suspending causes an interrupt storm from the RPS
> interrupt.
> Fix this by adding a deinit_chan() op to stmmac_dma_ops, which
> masks all default dma channel interrupts. This is called from
> stmmac_stop_all_dma(), so interrupts don't trigger while suspending.
>
> Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
> Suggested-by: Andrew Lunn <andrew@lunn.ch>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Signed-off-by: Luis Lang <luis.la@mail.de>
Nice. Thanks for working on this.
These are the sort of patches i like to see. We see a lot of AI driven
patches fixing theoretical issues which nobody really cares about. The
submitter does not appear to understand what they are doing, maybe
compile tests it, but never boot it on real hardware.
You have built and booted lots of kernels, done git bisects, sent a
good bug report, done the work of actually fixing the problem with a
little help from us, and pretty much got the processes correct first
time. You have the basics for being a real kernel hacker. I would be
happy to receive more patches from you.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Thanks
Andrew
© 2016 - 2026 Red Hat, Inc.