From 2bc6b218717b9d08f466f88209251d54bc09b207 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Tue, 22 Sep 2026 19:14:30 +0100 Subject: arm64/boot: Disable trapping of PMZR_EL0 writes to EL2 __init_el2_fgt2() writes one mask to both HDFGRTR2_EL2 and HDFGWTR2_EL2. PMZR_EL0 is write-only, so its trap bit, nPMZR_EL0, exists only in HDFGWTR2_EL2 and is therefore never set: a PMZR_EL0 write from the host traps to EL2, where the nVHE hypervisor has no handler and BUG()s. The kernel never writes PMZR_EL0, but kernel.perf_user_access=1 has the PMU driver set PMUSERENR_EL0.UEN for a task with a user-read event, so a write from EL0 reaches the trap and takes the host down without a panic message. Accumulate the HDFGWTR2_EL2 bits separately, as __init_el2_fgt() already does for HDFGWTR_EL2, and set nPMZR_EL0 with the other FEAT_PMUv3p9 bits. Fixes: 858c7bfcb35e1 ("arm64/boot: Enable EL2 requirements for FEAT_PMUv3p9") Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba Reviewed-by: Anshuman Khandual Reviewed-by: Oliver Upton Signed-off-by: Will Deacon --- drivers/clocksource/timer-realtek.c | 150 ++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 drivers/clocksource/timer-realtek.c (limited to 'drivers/clocksource/timer-realtek.c') diff --git a/drivers/clocksource/timer-realtek.c b/drivers/clocksource/timer-realtek.c new file mode 100644 index 000000000..4f0439de9 --- /dev/null +++ b/drivers/clocksource/timer-realtek.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Realtek Semiconductor Corp. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "timer-of.h" + +#define ENBL 1 +#define DSBL 0 + +#define SYSTIMER_RATE 1000000 +#define SYSTIMER_MIN_DELTA 0x64 +#define SYSTIMER_MAX_DELTA ULONG_MAX + +/* SYSTIMER Register Offset (RTK Internal Use) */ +#define TS_LW_OFST 0x0 +#define TS_HW_OFST 0x4 +#define TS_CMP_VAL_LW_OFST 0x8 +#define TS_CMP_VAL_HW_OFST 0xC +#define TS_CMP_CTRL_OFST 0x10 +#define TS_CMP_STAT_OFST 0x14 + +/* SYSTIMER CMP CTRL REG Mask */ +#define TS_CMP_EN_MASK 0x1 +#define TS_WR_EN0_MASK 0x2 + +static void __iomem *systimer_base; + +static u64 rtk_ts64_read(void) +{ + u32 low, high; + u64 ts; + + /* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */ + low = readl(systimer_base + TS_LW_OFST); + high = readl(systimer_base + TS_HW_OFST); + ts = ((u64)high << 32) | low; + + return ts; +} + +static void rtk_cmp_value_write(u64 value) +{ + u32 high, low; + + low = value & 0xFFFFFFFF; + high = value >> 32; + + writel(high, systimer_base + TS_CMP_VAL_HW_OFST); + writel(low, systimer_base + TS_CMP_VAL_LW_OFST); +} + +static inline void rtk_cmp_en_write(bool cmp_en) +{ + u32 val; + + val = TS_WR_EN0_MASK; + if (cmp_en == ENBL) + val |= TS_CMP_EN_MASK; + + writel(val, systimer_base + TS_CMP_CTRL_OFST); +} + +static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt) +{ + u64 cmp_val; + + rtk_cmp_en_write(DSBL); + cmp_val = rtk_ts64_read(); + + /* Set CMP value to current timestamp plus delta_us */ + rtk_cmp_value_write(cmp_val + cycles); + rtk_cmp_en_write(ENBL); + return 0; +} + +static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id) +{ + struct clock_event_device *clkevt = dev_id; + void __iomem *reg_base; + u32 val; + + /* Disable TS CMP Match */ + rtk_cmp_en_write(DSBL); + + /* Clear TS CMP INTR */ + reg_base = systimer_base + TS_CMP_STAT_OFST; + val = readl(reg_base) & TS_CMP_EN_MASK; + writel(val | TS_CMP_EN_MASK, reg_base); + clkevt->event_handler(clkevt); + + return IRQ_HANDLED; +} + +static int rtk_syst_shutdown(struct clock_event_device *clkevt) +{ + void __iomem *reg_base; + u64 cmp_val = 0; + + /* Disable TS CMP Match */ + rtk_cmp_en_write(DSBL); + /* Set compare value to 0 */ + rtk_cmp_value_write(cmp_val); + + /* Clear TS CMP INTR */ + reg_base = systimer_base + TS_CMP_STAT_OFST; + writel(TS_CMP_EN_MASK, reg_base); + return 0; +} + +static struct timer_of rtk_timer_to = { + .flags = TIMER_OF_IRQ | TIMER_OF_BASE, + + .clkevt = { + .name = "rtk-clkevt", + .rating = 300, + .cpumask = cpu_possible_mask, + .features = CLOCK_EVT_FEAT_DYNIRQ | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = rtk_syst_clkevt_next_event, + .set_state_oneshot = rtk_syst_shutdown, + .set_state_shutdown = rtk_syst_shutdown, + }, + + .of_irq = { + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = rtk_ts_match_intr_handler, + }, +}; + +static int __init rtk_systimer_init(struct device_node *node) +{ + int ret; + + ret = timer_of_init(node, &rtk_timer_to); + if (ret) + return ret; + + systimer_base = timer_of_base(&rtk_timer_to); + clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE, + SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA); + + return 0; +} + +TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init); -- cgit v1.3.1