diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index 3f046a475abe..a72802232e17 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -28,6 +28,7 @@ static inline int __in_irqentry_text(unsigned long ptr) ptr < (unsigned long)&__irqentry_text_end; } +extern void get_timer_freq_hook_init(void); extern void get_timer_count_hook_init(void); extern void __init early_trap_init(void *); extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 284affee152b..02131086e0ae 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -748,6 +748,41 @@ void get_timer_count_hook_init(void) } EXPORT_SYMBOL(get_timer_count_hook_init); +static int get_freq_trap(struct pt_regs *regs, unsigned int instr) +{ + u32 fval; + unsigned int res; + int rd = (instr >> 12) & 0xF; + + res = arm_check_condition(instr, regs->ARM_cpsr); + if (res == ARM_OPCODE_CONDTEST_FAIL) { + regs->ARM_pc += 4; + return 0; + } + + if (rd == 15) + return 1; + + fval = arch_timer_get_cntfrq(); + regs->uregs[rd] = fval; + regs->ARM_pc += 4; + return 0; +} + +static struct undef_hook get_freq_hook = { + .instr_mask = 0x0fff0fff, + .instr_val = 0x0e1e0f10, + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = get_freq_trap, +}; + +void get_timer_freq_hook_init(void) +{ + register_undef_hook(&get_freq_hook); +} +EXPORT_SYMBOL(get_timer_freq_hook_init); + /* * A data abort trap was taken, but we did not handle the instruction. * Try to abort the user program, or panic if it was the kernel. diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index 7e6a0061a389..1bae296f8e28 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -111,4 +111,5 @@ static inline u32 arm64_ras_serror_get_severity(u32 esr) bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr); void __noreturn arm64_serror_panic(struct pt_regs *regs, u32 esr); static inline void get_timer_count_hook_init(void) {} +static inline void get_timer_freq_hook_init(void) {} #endif diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 1dd13a6b80c4..b65abd6590e1 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1521,6 +1521,7 @@ static int __init arch_timer_mem_of_init(struct device_node *np) if (!ret && !arch_timer_needs_of_probing()) ret = arch_timer_common_init(); get_timer_count_hook_init(); + get_timer_freq_hook_init(); out: kfree(timer_mem); return ret;