Re: [PATCH] LoongArch: detect and disable sc.q if erratic

From: Mingcong Bai

Date: Thu Apr 02 2026 - 02:19:32 EST


Hi Ruoyao,

在 2026/4/2 12:47, Xi Ruoyao 写道:
We've observed that, on some Loongson 2K3000/3B6000M systems with earlier
firmware revisions, the sc.q instruction may write incorrect data into
the upper half of the written 128-bit datum.

It seems upgrading the firmware (for example, the 202602 release from
Loongson [1]) will resolve the issue. But since not all systems may be
running the most up-to-date firmware, based on firmware update avail-
ability and the environment in which they are running in.

To help with system compatibility and ensure correct behavior, check if
sc.q behaves erratically and disable if so.

Link: https://github.com/loongson/Firmware/pull/156 [1]
Signed-off-by: Xi Ruoyao <xry111@xxxxxxxxxxx>
---
arch/loongarch/kernel/cpu-probe.c | 32 ++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)

This patch was tested on a Loongson XB6MXC0_V1.0 motherboard (Loongson 3B6000M) and an OrangePi Nova V1.1 (Loongson 2K3000), each against firmware versions stable202511 (without workaround) and stable202602 (with workaround). Both devices had sc.q disabled correctly with stable202511, and enabled with the latter, no instability or functionality issue was observed.

With that:

Tested-by: Mingcong Bai <jeffbai@xxxxxxx>

diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 657bbae6c1c7..943e5826c71b 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -132,6 +132,36 @@ static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
}
}
+/*
+ * Some LoongArch has broken sc.q which incorrectly handles the upper word
+ * when the lower word is zero. Newer firmware versions (such as the 202602
+ * release from Loongson) seem to contain a workaround for this issue.
+ *
+ * Disable sc.q if erratic to ensure reliability and compatibility.
+ */
+static bool sc_q_is_sane(void)
+{
+ struct {
+ long word[2];
+ } __aligned(16) mem;
+ register long tmp;
+
+ asm (
+ "1:ll.d\t$r0, %[mem]\n\t"
+ "move\t%[tmp], $r0\n\t"
+ "sc.q\t%[tmp], %[one], %[mem]\n\t"
+ "beqz\t%[tmp], 1b"
+ : [mem] "=ZB" (mem), [tmp] "=&r" (tmp)
+ : [one] "r" (1));
+
+ if (mem.word[1] != 1) {
+ pr_warn_once("Warning: sc.q is erratic on this platform, disabling for both kernel and HWCAP. Please try a firmware update.");
+ return false;
+ }
+
+ return true;
+}
+
static void cpu_probe_common(struct cpuinfo_loongarch *c)
{
unsigned int config;
@@ -177,7 +207,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
c->options |= LOONGARCH_CPU_LAM;
elf_hwcap |= HWCAP_LOONGARCH_LAM;
}
- if (config & CPUCFG2_SCQ) {
+ if ((config & CPUCFG2_SCQ) && sc_q_is_sane()) {
c->options |= LOONGARCH_CPU_SCQ;
elf_hwcap |= HWCAP_LOONGARCH_SCQ;
}