mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

In order to cope better with high frequency counters, move the programming of the timers from the countdown timer (TVAL) over to the comparator (CVAL). The programming model is slightly different, as we now need to read the current counter value to have an absolute deadline instead of a relative one. There is a small overhead to this change, which we will address in the following patches. Reviewed-by: Oliver Upton <oupton@google.com> Reviewed-by: Mark Rutland <mark.rutland@arm.com> Tested-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20211017124225.3018098-5-maz@kernel.org Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
146 lines
3.0 KiB
C
146 lines
3.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef __ASMARM_ARCH_TIMER_H
|
|
#define __ASMARM_ARCH_TIMER_H
|
|
|
|
#include <asm/barrier.h>
|
|
#include <asm/errno.h>
|
|
#include <asm/hwcap.h>
|
|
#include <linux/clocksource.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
|
|
#ifdef CONFIG_ARM_ARCH_TIMER
|
|
/* 32bit ARM doesn't know anything about timer errata... */
|
|
#define has_erratum_handler(h) (false)
|
|
#define erratum_handler(h) (arch_timer_##h)
|
|
|
|
int arch_timer_arch_init(void);
|
|
|
|
/*
|
|
* These register accessors are marked inline so the compiler can
|
|
* nicely work out which register we want, and chuck away the rest of
|
|
* the code. At least it does so with a recent GCC (4.6.3).
|
|
*/
|
|
static __always_inline
|
|
void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u64 val)
|
|
{
|
|
if (access == ARCH_TIMER_PHYS_ACCESS) {
|
|
switch (reg) {
|
|
case ARCH_TIMER_REG_CTRL:
|
|
asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" ((u32)val));
|
|
break;
|
|
case ARCH_TIMER_REG_CVAL:
|
|
asm volatile("mcrr p15, 2, %Q0, %R0, c14" : : "r" (val));
|
|
break;
|
|
default:
|
|
BUILD_BUG();
|
|
}
|
|
} else if (access == ARCH_TIMER_VIRT_ACCESS) {
|
|
switch (reg) {
|
|
case ARCH_TIMER_REG_CTRL:
|
|
asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" ((u32)val));
|
|
break;
|
|
case ARCH_TIMER_REG_CVAL:
|
|
asm volatile("mcrr p15, 3, %Q0, %R0, c14" : : "r" (val));
|
|
break;
|
|
default:
|
|
BUILD_BUG();
|
|
}
|
|
} else {
|
|
BUILD_BUG();
|
|
}
|
|
|
|
isb();
|
|
}
|
|
|
|
static __always_inline
|
|
u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg)
|
|
{
|
|
u32 val = 0;
|
|
|
|
if (access == ARCH_TIMER_PHYS_ACCESS) {
|
|
switch (reg) {
|
|
case ARCH_TIMER_REG_CTRL:
|
|
asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
|
|
break;
|
|
default:
|
|
BUILD_BUG();
|
|
}
|
|
} else if (access == ARCH_TIMER_VIRT_ACCESS) {
|
|
switch (reg) {
|
|
case ARCH_TIMER_REG_CTRL:
|
|
asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val));
|
|
break;
|
|
default:
|
|
BUILD_BUG();
|
|
}
|
|
} else {
|
|
BUILD_BUG();
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static inline u32 arch_timer_get_cntfrq(void)
|
|
{
|
|
u32 val;
|
|
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
|
|
return val;
|
|
}
|
|
|
|
static inline u64 __arch_counter_get_cntpct(void)
|
|
{
|
|
u64 cval;
|
|
|
|
isb();
|
|
asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
|
|
return cval;
|
|
}
|
|
|
|
static inline u64 __arch_counter_get_cntpct_stable(void)
|
|
{
|
|
return __arch_counter_get_cntpct();
|
|
}
|
|
|
|
static inline u64 __arch_counter_get_cntvct(void)
|
|
{
|
|
u64 cval;
|
|
|
|
isb();
|
|
asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
|
|
return cval;
|
|
}
|
|
|
|
static inline u64 __arch_counter_get_cntvct_stable(void)
|
|
{
|
|
return __arch_counter_get_cntvct();
|
|
}
|
|
|
|
static inline u32 arch_timer_get_cntkctl(void)
|
|
{
|
|
u32 cntkctl;
|
|
asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
|
|
return cntkctl;
|
|
}
|
|
|
|
static inline void arch_timer_set_cntkctl(u32 cntkctl)
|
|
{
|
|
asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
|
|
isb();
|
|
}
|
|
|
|
static inline void arch_timer_set_evtstrm_feature(void)
|
|
{
|
|
elf_hwcap |= HWCAP_EVTSTRM;
|
|
}
|
|
|
|
static inline bool arch_timer_have_evtstrm_feature(void)
|
|
{
|
|
return elf_hwcap & HWCAP_EVTSTRM;
|
|
}
|
|
#endif
|
|
|
|
#endif
|