Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Ciprian Costea <ciprianmarian.costea@oss.nxp.com>
To: Marc Kleine-Budde <mkl@pengutronix.de>,
	Vincent Mailhol <mailhol@kernel.org>,
	Nicolas Ferre <nicolas.ferre@microchip.com>,
	Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Claudiu Beznea <claudiu.beznea@tuxon.dev>,
	Haibo Chen <haibo.chen@nxp.com>
Cc: linux-can@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, NXP S32 Linux Team <s32@nxp.com>,
	imx@lists.linux.dev, Enric Balletbo <eballetb@redhat.com>,
	Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Subject: [PATCH v7 4/4] can: flexcan: use one rx-offload source per IRQ line
Date: Fri,  2 Oct 2026 09:12:03 +0200	[thread overview]
Message-ID: <20261002071203.1287650-5-ciprianmarian.costea@oss.nxp.com> (raw)
In-Reply-To: <20261002071203.1287650-1-ciprianmarian.costea@oss.nxp.com>

From: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>

Currently, flexcan_irq() is requested on every IRQ line of the
controller: the mailbox line, the bus off and error lines
(FLEXCAN_QUIRK_NR_IRQ_3) and a second mailbox line
(FLEXCAN_QUIRK_SECONDARY_MB_IRQ). On NXP S32G2, which has all four, the
handlers can run at the same time on different CPUs and corrupt the
rx-offload queue they share.

Add an rx-offload source for each extra line with
can_rx_offload_add_source(), pick the source in flexcan_irq() based on
the IRQ number and pass it to the functions that queue skbs.

flexcan_mailbox_read() is now called with any of the sources, so get the
private data with netdev_priv() instead of container_of().

This only fixes the queue corruption. All handlers still process the
whole mailbox range and the same ESR events. A dedicated handler per line
and splitting the mailbox range between the two mailbox lines will follow
in a separate series, which also removes the lookup added here.

Fixes: 8503a4b1a24d ("can: flexcan: add NXP S32G2/S32G3 SoC support")
Assisted-by: LLM
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
---
 drivers/net/can/flexcan/flexcan-core.c | 86 +++++++++++++++++++++-----
 drivers/net/can/flexcan/flexcan.h      |  3 +
 2 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index f5d22c61503f..2ef06ed0c744 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -828,7 +828,8 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
 	return NETDEV_TX_OK;
 }
 
-static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
+static void flexcan_irq_bus_err(struct net_device *dev,
+				struct can_rx_offload *offload, u32 reg_esr)
 {
 	struct flexcan_priv *priv = netdev_priv(dev);
 	struct flexcan_regs __iomem *regs = priv->regs;
@@ -885,12 +886,13 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
 	if (tx_errors)
 		dev->stats.tx_errors++;
 
-	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
+	err = can_rx_offload_queue_timestamp(offload, skb, timestamp);
 	if (err)
 		dev->stats.rx_fifo_errors++;
 }
 
-static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
+static void flexcan_irq_state(struct net_device *dev,
+			      struct can_rx_offload *offload, u32 reg_esr)
 {
 	struct flexcan_priv *priv = netdev_priv(dev);
 	struct flexcan_regs __iomem *regs = priv->regs;
@@ -932,7 +934,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
 	if (unlikely(new_state == CAN_STATE_BUS_OFF))
 		can_bus_off(dev);
 
-	err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp);
+	err = can_rx_offload_queue_timestamp(offload, skb, timestamp);
 	if (err)
 		dev->stats.rx_fifo_errors++;
 }
@@ -967,16 +969,12 @@ static inline u64 flexcan_read_reg_iflag_tx(struct flexcan_priv *priv)
 	return flexcan_read64_mask(priv, &priv->regs->iflag1, priv->tx_mask);
 }
 
-static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
-{
-	return container_of(offload, struct flexcan_priv, offload);
-}
-
 static struct sk_buff *flexcan_mailbox_read(struct can_rx_offload *offload,
 					    unsigned int n, u32 *timestamp,
 					    bool drop)
 {
-	struct flexcan_priv *priv = rx_offload_to_priv(offload);
+	/* offload may be any of the sources, see can_rx_offload_add_source() */
+	struct flexcan_priv *priv = netdev_priv(offload->dev);
 	struct flexcan_regs __iomem *regs = priv->regs;
 	struct flexcan_mb __iomem *mb;
 	struct sk_buff *skb;
@@ -1070,11 +1068,34 @@ static struct sk_buff *flexcan_mailbox_read(struct can_rx_offload *offload,
 	return skb;
 }
 
+/* The same handler is requested on every IRQ line, the line it is called
+ * for selects the rx-offload source to queue into.
+ */
+static struct can_rx_offload *
+flexcan_get_offload(struct flexcan_priv *priv, int irq)
+{
+	const u32 quirks = priv->devtype_data.quirks;
+
+	if (quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ &&
+	    irq == priv->irq_secondary_mb)
+		return &priv->offload_secondary_mb;
+
+	if (quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+		if (irq == priv->irq_boff)
+			return &priv->offload_boff;
+		if (irq == priv->irq_err)
+			return &priv->offload_err;
+	}
+
+	return &priv->offload;
+}
+
 static irqreturn_t flexcan_irq(int irq, void *dev_id)
 {
 	struct net_device *dev = dev_id;
 	struct net_device_stats *stats = &dev->stats;
 	struct flexcan_priv *priv = netdev_priv(dev);
+	struct can_rx_offload *offload = flexcan_get_offload(priv, irq);
 	struct flexcan_regs __iomem *regs = priv->regs;
 	irqreturn_t handled = IRQ_NONE;
 	u64 reg_iflag_tx;
@@ -1088,7 +1109,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 
 		while ((reg_iflag_rx = flexcan_read_reg_iflag_rx(priv))) {
 			handled = IRQ_HANDLED;
-			ret = can_rx_offload_irq_offload_timestamp(&priv->offload,
+			ret = can_rx_offload_irq_offload_timestamp(offload,
 								   reg_iflag_rx);
 			if (!ret)
 				break;
@@ -1099,7 +1120,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 		reg_iflag1 = priv->read(&regs->iflag1);
 		if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
 			handled = IRQ_HANDLED;
-			can_rx_offload_irq_offload_fifo(&priv->offload);
+			can_rx_offload_irq_offload_fifo(offload);
 		}
 
 		/* FIFO overflow interrupt */
@@ -1120,7 +1141,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 
 		handled = IRQ_HANDLED;
 		stats->tx_bytes +=
-			can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload, 0,
+			can_rx_offload_get_echo_skb_queue_timestamp(offload, 0,
 								    reg_ctrl << 16, NULL);
 		stats->tx_packets++;
 
@@ -1143,12 +1164,12 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 	if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
 	    (priv->devtype_data.quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE |
 					   FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
-		flexcan_irq_state(dev, reg_esr);
+		flexcan_irq_state(dev, offload, reg_esr);
 
 	/* bus error IRQ - handle if bus error reporting is activated */
 	if ((reg_esr & FLEXCAN_ESR_ERR_BUS) &&
 	    (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
-		flexcan_irq_bus_err(dev, reg_esr);
+		flexcan_irq_bus_err(dev, offload, reg_esr);
 
 	/* availability of error interrupt among state transitions in case
 	 * bus error reporting is de-activated and
@@ -1189,7 +1210,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 	}
 
 	if (handled)
-		can_rx_offload_irq_finish(&priv->offload);
+		can_rx_offload_irq_finish(offload);
 
 	return handled;
 }
@@ -1381,6 +1402,15 @@ static void flexcan_ram_init(struct net_device *dev)
 	priv->write(reg_ctrl2, &regs->ctrl2);
 }
 
+static int flexcan_rx_offload_add_source(struct flexcan_priv *priv,
+					 struct can_rx_offload *source)
+{
+	source->mb_first = priv->offload.mb_first;
+	source->mb_last = priv->offload.mb_last;
+
+	return can_rx_offload_add_source(&priv->offload, source);
+}
+
 static int flexcan_rx_offload_setup(struct net_device *dev)
 {
 	struct flexcan_priv *priv = netdev_priv(dev);
@@ -1422,6 +1452,30 @@ static int flexcan_rx_offload_setup(struct net_device *dev)
 		err = can_rx_offload_add_fifo(dev, &priv->offload,
 					      FLEXCAN_NAPI_WEIGHT);
 	}
+	if (err)
+		return err;
+
+	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_SECONDARY_MB_IRQ) {
+		err = flexcan_rx_offload_add_source(priv,
+						    &priv->offload_secondary_mb);
+		if (err)
+			goto out_can_rx_offload_del;
+	}
+
+	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_NR_IRQ_3) {
+		err = flexcan_rx_offload_add_source(priv, &priv->offload_boff);
+		if (err)
+			goto out_can_rx_offload_del;
+
+		err = flexcan_rx_offload_add_source(priv, &priv->offload_err);
+		if (err)
+			goto out_can_rx_offload_del;
+	}
+
+	return 0;
+
+out_can_rx_offload_del:
+	can_rx_offload_del(&priv->offload);
 
 	return err;
 }
diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
index 16692a2502eb..0b09e94d0010 100644
--- a/drivers/net/can/flexcan/flexcan.h
+++ b/drivers/net/can/flexcan/flexcan.h
@@ -88,6 +88,9 @@ struct flexcan_stop_mode {
 struct flexcan_priv {
 	struct can_priv can;
 	struct can_rx_offload offload;
+	struct can_rx_offload offload_secondary_mb;
+	struct can_rx_offload offload_boff;
+	struct can_rx_offload offload_err;
 	struct device *dev;
 
 	struct flexcan_regs __iomem *regs;
-- 
2.43.0



  parent reply	other threads:[~2026-10-02  7:12 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02  7:11 [PATCH v7 0/4] can: rx-offload: allow more than one receive source Ciprian Costea
2026-10-02  7:12 ` [PATCH v7 1/4] can: at91_can: release the rx-offload on teardown Ciprian Costea
2026-10-02  7:12 ` [PATCH v7 2/4] can: rx-offload: move skb_queue and napi into struct can_rx_offload_queue Ciprian Costea
2026-10-02  7:12 ` [PATCH v7 3/4] can: rx-offload: allow more than one receive source Ciprian Costea
2026-10-03 21:36   ` netdev-bot+sashiko
2026-10-05  9:46     ` Ciprian Marian Costea
2026-10-05 10:48     ` Marc Kleine-Budde
2026-10-02  7:12 ` Ciprian Costea [this message]
2026-10-03 21:36   ` [PATCH v7 4/4] can: flexcan: use one rx-offload source per IRQ line netdev-bot+sashiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261002071203.1287650-5-ciprianmarian.costea@oss.nxp.com \
    --to=ciprianmarian.costea@oss.nxp.com \
    --cc=alexandre.belloni@bootlin.com \
    --cc=claudiu.beznea@tuxon.dev \
    --cc=eballetb@redhat.com \
    --cc=haibo.chen@nxp.com \
    --cc=imx@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-can@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mailhol@kernel.org \
    --cc=mkl@pengutronix.de \
    --cc=nicolas.ferre@microchip.com \
    --cc=s32@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox