[PATCH 17/28] mfd: loongson-se: Reinitialise completions and wait uninterruptibly for commands

From: Lee Jones

Date: Wed Oct 21 2026 - 08:22:53 EST


loongson_se_send_controller_cmd() and loongson_se_send_engine_cmd() wait
on se->cmd_completion and engine->completion using
wait_for_completion_interruptible() without reinitialising the
completion before issuing a new command. Because the hardware has no
mechanism to abort an in-flight command once accepted by
loongson_se_poll(), returning early on a signal leaves the controller or
engine actively processing the shared command registers and DMA buffers
in the background, and its subsequent completion interrupt leaves a
stale completion token that causes the next command to return
prematurely.

Clear any stale pending interrupt status and reinitialise the target
completion inside loongson_se_poll() while holding se->dev_lock
immediately before notifying the controller, and wait uninterruptibly
via wait_for_completion() once loongson_se_poll() confirms the command
has been accepted by the hardware.

Fixes: e551fa3159e3 ("mfd: Add support for Loongson Security Engine chip controller")
Closes: https://sashiko.dev/#/bug/linux-4661fb0d-d936-4168-89cd-2f6dec31a7b5
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
drivers/mfd/loongson-se.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/mfd/loongson-se.c b/drivers/mfd/loongson-se.c
index 485ee4a6ca5e..9b531e62fb85 100644
--- a/drivers/mfd/loongson-se.c
+++ b/drivers/mfd/loongson-se.c
@@ -37,13 +37,17 @@ struct loongson_se_controller_cmd {
u32 info[7];
};

-static int loongson_se_poll(struct loongson_se *se, u32 int_bit)
+static int loongson_se_poll(struct loongson_se *se, u32 int_bit,
+ struct completion *completion)
{
u32 status;
int err;

spin_lock_irq(&se->dev_lock);

+ writel(int_bit, se->base + SE_S2LINT_CL);
+ reinit_completion(completion);
+
/* Notify the controller that the engine needs to be started */
writel(int_bit, se->base + SE_L2SINT_SET);

@@ -66,11 +70,13 @@ static int loongson_se_send_controller_cmd(struct loongson_se *se,
for (i = 0; i < SE_SEND_CMD_REG_LEN; i++)
writel(send_cmd[i], se->base + SE_SEND_CMD_REG + i * 4);

- err = loongson_se_poll(se, SE_INT_CONTROLLER);
+ err = loongson_se_poll(se, SE_INT_CONTROLLER, &se->cmd_completion);
if (err)
return err;

- return wait_for_completion_interruptible(&se->cmd_completion);
+ wait_for_completion(&se->cmd_completion);
+
+ return 0;
}

int loongson_se_send_engine_cmd(struct loongson_se_engine *engine)
@@ -80,12 +86,15 @@ int loongson_se_send_engine_cmd(struct loongson_se_engine *engine)
* where to obtain engine commands from. Now all we need to
* do is notify the controller that the engine needs to be started.
*/
- int err = loongson_se_poll(engine->se, BIT(engine->id));
+ int err = loongson_se_poll(engine->se, BIT(engine->id),
+ &engine->completion);

if (err)
return err;

- return wait_for_completion_interruptible(&engine->completion);
+ wait_for_completion(&engine->completion);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(loongson_se_send_engine_cmd);

--
2.56.0.218.g1ec904b173-goog