Re: [PATCH v2] mfd: loongson-se: Add multi-node support

From: Qunqin Zhao

Date: Mon Apr 27 2026 - 05:24:47 EST



在 2026/4/27 下午5:02, Huacai Chen 写道:
Hi, Qunqin,

On Mon, Apr 27, 2026 at 4:55 PM Qunqin Zhao <zhaoqunqin@xxxxxxxxxxx> wrote:
On the Loongson platform, each node is equipped with a security engine
device. However, due to a hardware flaw, only the device on node 0 can
trigger interrupts. Therefore, interrupts from other nodes are forwarded
by node 0. We need to check in the interrupt handler of node 0 whether
this interrupt is intended for other nodes.
Multi-node or multi-package? In my opinion SE has no relationship with
NUMA node, so maybe package?

Here is the output of lscpu from my machine:

[loongson@localhost ~]$ lscpu
Architecture:          loongarch64
  CPU op-mode(s):      32-bit, 64-bit
  Address sizes:       48 bits physical, 48 bits virtual
  Byte Order:          Little Endian
CPU(s):                128
  On-line CPU(s) list: 0-127
Model name:            Loongson-3C6000/D
  CPU family:          Loongson-64bit
  Model:               0x11
  Thread(s) per core:  2
  Core(s) per socket:  32
  Socket(s):           2
  BogoMIPS:            4200.00
  Flags:               cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips
Caches (sum of all):
  L1d:                 4 MiB (64 instances)
  L1i:                 4 MiB (64 instances)
  L2:                  16 MiB (64 instances)
  L3:                  128 MiB (4 instances)
NUMA:
  NUMA node(s):        4
  NUMA node0 CPU(s):   0-31
  NUMA node1 CPU(s):   32-63
  NUMA node2 CPU(s):   64-95
  NUMA node3 CPU(s):   96-127

There are four SE devices in my system, one for each NUMA node.

Qunqin,

Thanks


Huacai

Signed-off-by: Qunqin Zhao <zhaoqunqin@xxxxxxxxxxx>
---
Changes in v2:
-Resending due to no feedback for one month.
-Rebased on top of latest mainline (7.1-rc1) to ensure the patch
applies cleanly.
-No functional changes since the previous submission.

Link to v1:
https://lore.kernel.org/all/20260226102225.19516-1-zhaoqunqin@xxxxxxxxxxx/#t

drivers/mfd/loongson-se.c | 38 +++++++++++++++++++++++++++------
include/linux/mfd/loongson-se.h | 3 +++
2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/drivers/mfd/loongson-se.c b/drivers/mfd/loongson-se.c
index 3902ba377d6..40e18c21268 100644
--- a/drivers/mfd/loongson-se.c
+++ b/drivers/mfd/loongson-se.c
@@ -37,6 +37,9 @@ struct loongson_se_controller_cmd {
u32 info[7];
};

+static DECLARE_COMPLETION(node0);
+static struct loongson_se *se_node[SE_MAX_NODES];
+
static int loongson_se_poll(struct loongson_se *se, u32 int_bit)
{
u32 status;
@@ -133,8 +136,8 @@ EXPORT_SYMBOL_GPL(loongson_se_init_engine);
static irqreturn_t se_irq_handler(int irq, void *dev_id)
{
struct loongson_se *se = dev_id;
- u32 int_status;
- int id;
+ u32 int_status, node_irq = 0;
+ int id, node;

spin_lock(&se->dev_lock);

@@ -147,6 +150,11 @@ static irqreturn_t se_irq_handler(int irq, void *dev_id)
writel(SE_INT_CONTROLLER, se->base + SE_S2LINT_CL);
}

+ if (int_status & SE_INT_OTHER_NODE) {
+ int_status &= ~SE_INT_OTHER_NODE;
+ node_irq = 1;
+ }
+
/* For engines */
while (int_status) {
id = __ffs(int_status);
@@ -157,6 +165,14 @@ static irqreturn_t se_irq_handler(int irq, void *dev_id)

spin_unlock(&se->dev_lock);

+ if (node_irq) {
+ writel(SE_INT_OTHER_NODE, se->base + SE_S2LINT_CL);
+ for (node = 1; node < SE_MAX_NODES; node++) {
+ if (se_node[node])
+ se_irq_handler(irq, se_node[node]);
+ }
+ }
+
return IRQ_HANDLED;
}

@@ -189,6 +205,7 @@ static int loongson_se_probe(struct platform_device *pdev)
struct loongson_se *se;
int nr_irq, irq, err, i;
dma_addr_t paddr;
+ int node = dev_to_node(dev);

se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL);
if (!se)
@@ -213,9 +230,16 @@ static int loongson_se_probe(struct platform_device *pdev)

writel(SE_INT_ALL, se->base + SE_S2LINT_EN);

- nr_irq = platform_irq_count(pdev);
- if (nr_irq <= 0)
- return -ENODEV;
+ if (node == 0 || node == NUMA_NO_NODE) {
+ nr_irq = platform_irq_count(pdev);
+ if (nr_irq <= 0)
+ return -ENODEV;
+ } else {
+ /* Only the device on node 0 can trigger interrupts */
+ nr_irq = 0;
+ wait_for_completion_interruptible(&node0);
+ se_node[node] = se;
+ }

for (i = 0; i < nr_irq; i++) {
irq = platform_get_irq(pdev, i);
@@ -228,7 +252,9 @@ static int loongson_se_probe(struct platform_device *pdev)
if (err)
return err;

- return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, engines,
+ complete_all(&node0);
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, engines,
ARRAY_SIZE(engines), NULL, 0, NULL);
}

diff --git a/include/linux/mfd/loongson-se.h b/include/linux/mfd/loongson-se.h
index 07afa0c2524..a80e06eb017 100644
--- a/include/linux/mfd/loongson-se.h
+++ b/include/linux/mfd/loongson-se.h
@@ -20,6 +20,9 @@

#define SE_INT_ALL 0xffffffff
#define SE_INT_CONTROLLER BIT(0)
+#define SE_INT_OTHER_NODE BIT(31)
+
+#define SE_MAX_NODES 8

#define SE_ENGINE_MAX 16
#define SE_ENGINE_RNG 1

base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
--
2.47.2