[PATCH] LoongArch: Clear the pending timer interrupt when the clockevent stops

From: Wentao Guan

Date: Tue Sep 15 2026 - 06:03:56 EST


commit 8ef7f3132e40 ("LoongArch: Add cpuhotplug hooks to fix high cpu
usage of vCPU threads") clears a latched timer interrupt from a cpuhp
teardown callback, because a pending timer interrupt keeps waking an
offline CPU up again right after it entered the idle instruction.
Clearing a pending timer interrupt is part of stopping a clockevent
device on other architectures: lapic_timer_shutdown() masks the local
APIC timer and zeroes its counter, and riscv_clock_shutdown() calls
riscv_clock_event_stop(), which clears the timer interrupt.

Clear the pending timer interrupt in constant_set_state_shutdown()
instead. The clockevents core already stops the device before the CPU
is parked, through the generic tick teardown (tick_cpu_dying(),
registered on CPUHP_AP_TICK_DYING), so the cpuhp hooks have nothing
left to do: the startup callback only repeats what
loongson_init_secondary() does on every bring-up, and the teardown
callback only did the write_csr_tintclear() that now happens when the
device is stopped. CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING is not
needed any more and is removed.

Assisted-by: atomcode:deepseek-v4.1-flash
Signed-off-by: Wentao Guan <guanwentao@xxxxxxxxxxxxx>
---
arch/loongarch/kernel/time.c | 23 +++--------------------
include/linux/cpuhotplug.h | 1 -
2 files changed, 3 insertions(+), 21 deletions(-)

diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c
index dbaaabcaf6f09..64136a541b049 100644
--- a/arch/loongarch/kernel/time.c
+++ b/arch/loongarch/kernel/time.c
@@ -5,7 +5,6 @@
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#include <linux/clockchips.h>
-#include <linux/cpuhotplug.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
@@ -88,6 +87,9 @@ static int constant_set_state_shutdown(struct clock_event_device *evt)
timer_config &= ~CSR_TCFG_EN;
csr_write(timer_config, LOONGARCH_CSR_TCFG);

+ /* Clear Timer Interrupt */
+ write_csr_tintclear(CSR_TINTCLR_TI);
+
raw_spin_unlock(&state_lock);

return 0;
@@ -104,21 +106,6 @@ static int constant_timer_next_event(unsigned long delta, struct clock_event_dev
return 0;
}

-static int arch_timer_starting(unsigned int cpu)
-{
- set_csr_ecfg(ECFGF_TIMER);
-
- return 0;
-}
-
-static int arch_timer_dying(unsigned int cpu)
-{
- /* Clear Timer Interrupt */
- write_csr_tintclear(CSR_TINTCLR_TI);
-
- return 0;
-}
-
static unsigned long get_loops_per_jiffy(void)
{
u64 lpj = const_clock_freq;
@@ -189,10 +176,6 @@ int constant_clockevent_init(void)
lpj_fine = get_loops_per_jiffy();
pr_info("Constant clock event device register\n");

- cpuhp_setup_state(CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING,
- "clockevents/loongarch/timer:starting",
- arch_timer_starting, arch_timer_dying);
-
return 0;
}

diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 0fb3a2a62eb00..6e4bac0bf811a 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -167,7 +167,6 @@ enum cpuhp_state {
CPUHP_AP_QCOM_TIMER_STARTING,
CPUHP_AP_TEGRA_TIMER_STARTING,
CPUHP_AP_ARMADA_TIMER_STARTING,
- CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING,
CPUHP_AP_MIPS_GIC_TIMER_STARTING,
CPUHP_AP_ARC_TIMER_STARTING,
CPUHP_AP_REALTEK_TIMER_STARTING,
--
2.30.2