[PATCH net-next 1/9] net: sparx5/lan969x: stop the FDMA channels properly

From: Daniel Machon

Date: Thu Nov 05 2026 - 09:46:01 EST


The FDMA channels are activated by setting their bit in
FDMA_CH_ACTIVATE, and are disabled through FDMA_CH_DISABLE, after which
a channel finishes the DCB it is fetching before it becomes inactive,
as shown by FDMA_CH_ACTIVE. The injection and extraction ports are
stopped through INJ_STOP and XTR_STOP.

sparx5_fdma_stop() tries to stop the channels by clearing their bit in
FDMA_CH_ACTIVATE, which has no effect: the register only acts on the
bits that are set, and its bits are cleared immediately by the
hardware. It also never stops the injection port, and waits for
XTR_BUF_IS_EMPTY to be 0, which means that the extraction buffer is not
empty, instead of 1, so the wait always runs until it times out.

Add the FDMA_CH_ACTIVE register, and make sparx5_fdma_stop() disable the
channels and wait for them to become inactive, stop the injection port,
and wait for the extraction buffer to be empty.

The stop sequence is used by the FDMA deinit, and by the FDMA buffer
resize added later in this series.

Signed-off-by: Daniel Machon <daniel.machon@xxxxxxxxxxxxx>
---
.../net/ethernet/microchip/sparx5/sparx5_fdma.c | 46 ++++++++++++++++------
.../ethernet/microchip/sparx5/sparx5_main_regs.h | 11 ++++++
2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
index 15ec9b053824f..bcae0bbd5e44e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
@@ -20,6 +20,8 @@

#define FDMA_XTR_BUFFER_SIZE 2048
#define FDMA_WEIGHT 4
+#define SPX5_FDMA_STOP_SLEEP_US 500
+#define SPX5_FDMA_STOP_TIMEOUT_US 10000

static int sparx5_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db,
u64 *dataptr)
@@ -86,10 +88,6 @@ static void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *r
{
struct fdma *fdma = &rx->fdma;

- /* Deactivate the RX channel */
- spx5_rmw(0, BIT(fdma->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
- sparx5, FDMA_CH_ACTIVATE);
-
/* Disable RX channel DB interrupt */
spx5_rmw(0, BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA,
sparx5, FDMA_INTR_DB_ENA);
@@ -125,9 +123,9 @@ static void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx)

static void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx)
{
- /* Disable the channel */
- spx5_rmw(0, BIT(tx->fdma.channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE,
- sparx5, FDMA_CH_ACTIVATE);
+ /* Stop TX fdma */
+ spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(1), FDMA_PORT_CTRL_INJ_STOP,
+ sparx5, FDMA_PORT_CTRL(0));
}

void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma)
@@ -447,6 +445,11 @@ static u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5)
return spx5_rd(sparx5, FDMA_PORT_CTRL(0));
}

+static u32 sparx5_fdma_ch_active(struct sparx5 *sparx5)
+{
+ return spx5_rd(sparx5, FDMA_CH_ACTIVE);
+}
+
int sparx5_fdma_start(struct sparx5 *sparx5)
{
const struct sparx5_ops *ops = sparx5->data->ops;
@@ -468,20 +471,39 @@ int sparx5_fdma_start(struct sparx5 *sparx5)

int sparx5_fdma_stop(struct sparx5 *sparx5)
{
+ u32 channels = BIT(sparx5->rx.fdma.channel_id) |
+ BIT(sparx5->tx.fdma.channel_id);
struct sparx5_rx *rx = &sparx5->rx;
struct sparx5_tx *tx = &sparx5->tx;
u32 val;

napi_disable(&rx->napi);

- /* Stop the fdma and channel interrupts */
+ /* Disable the channels, and wait for them to finish the current DCB */
+ spx5_wr(FDMA_CH_DISABLE_CH_DISABLE_SET(channels), sparx5,
+ FDMA_CH_DISABLE);
+
+ if (read_poll_timeout(sparx5_fdma_ch_active,
+ val,
+ !(FDMA_CH_ACTIVE_CH_ACTIVE_GET(val) & channels),
+ SPX5_FDMA_STOP_SLEEP_US,
+ SPX5_FDMA_STOP_TIMEOUT_US,
+ false,
+ sparx5))
+ dev_warn(sparx5->dev, "Failed to stop FDMA channels\n");
+
+ /* Stop the fdma and the RX channel interrupt */
sparx5_fdma_rx_deactivate(sparx5, rx);
sparx5_fdma_tx_deactivate(sparx5, tx);

- /* Wait for the RX channel to stop */
- read_poll_timeout(sparx5_fdma_port_ctrl, val,
- FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0,
- 500, 10000, 0, sparx5);
+ /* Wait for the extraction buffer to be empty */
+ read_poll_timeout(sparx5_fdma_port_ctrl,
+ val,
+ FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val),
+ SPX5_FDMA_STOP_SLEEP_US,
+ SPX5_FDMA_STOP_TIMEOUT_US,
+ false,
+ sparx5);

return 0;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
index 1464fd1a1d447..df438be0ec552 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -4713,6 +4713,17 @@ extern const struct sparx5_regs *regs;
__REG(TARGET_FDMA, 0, 1, 8, 0, 1, regs->gsize[GW_FDMA_FDMA], 148, r, 8,\
4)

+/* FDMA:FDMA:FDMA_CH_ACTIVE */
+#define FDMA_CH_ACTIVE \
+ __REG(TARGET_FDMA, 0, 1, 8, 0, 1, regs->gsize[GW_FDMA_FDMA], 180, 0, 1,\
+ 4)
+
+#define FDMA_CH_ACTIVE_CH_ACTIVE GENMASK(7, 0)
+#define FDMA_CH_ACTIVE_CH_ACTIVE_SET(x)\
+ FIELD_PREP(FDMA_CH_ACTIVE_CH_ACTIVE, x)
+#define FDMA_CH_ACTIVE_CH_ACTIVE_GET(x)\
+ FIELD_GET(FDMA_CH_ACTIVE_CH_ACTIVE, x)
+
/* FDMA:FDMA:FDMA_CH_CFG */
#define FDMA_CH_CFG(r) \
__REG(TARGET_FDMA, 0, 1, 8, 0, 1, regs->gsize[GW_FDMA_FDMA], 224, r, 8,\

--
2.34.1