mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 10:14:23 +08:00
Merge branch 'timer_evtstrm' of git://linux-arm.org/linux-skn into clockevents/3.13
Adds support to configure the rate and enable the event stream for architected timer. The event streams can be used to impose a timeout on a wfe, to safeguard against any programming error in case an expected event is not generated or even to implement wfe-based timeouts for userspace locking implementations. This feature can be disabled(enabled by default). Since the timer control register is reset to zero on warm boot, CPU PM notifier is added to save and restore the value. Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
This commit is contained in:
commit
191124efb4
@ -87,17 +87,43 @@ static inline u64 arch_counter_get_cntvct(void)
|
|||||||
return cval;
|
return cval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void arch_counter_set_user_access(void)
|
static inline u32 arch_timer_get_cntkctl(void)
|
||||||
{
|
{
|
||||||
u32 cntkctl;
|
u32 cntkctl;
|
||||||
|
|
||||||
asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
|
asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
|
||||||
|
return cntkctl;
|
||||||
|
}
|
||||||
|
|
||||||
/* disable user access to everything */
|
static inline void arch_timer_set_cntkctl(u32 cntkctl)
|
||||||
cntkctl &= ~((3 << 8) | (7 << 0));
|
{
|
||||||
|
|
||||||
asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
|
asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void arch_counter_set_user_access(void)
|
||||||
|
{
|
||||||
|
u32 cntkctl = arch_timer_get_cntkctl();
|
||||||
|
|
||||||
|
/* Disable user access to both physical/virtual counters/timers */
|
||||||
|
/* Also disable virtual event stream */
|
||||||
|
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
|
||||||
|
| ARCH_TIMER_USR_VT_ACCESS_EN
|
||||||
|
| ARCH_TIMER_VIRT_EVT_EN
|
||||||
|
| ARCH_TIMER_USR_VCT_ACCESS_EN
|
||||||
|
| ARCH_TIMER_USR_PCT_ACCESS_EN);
|
||||||
|
arch_timer_set_cntkctl(cntkctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void arch_timer_evtstrm_enable(int divider)
|
||||||
|
{
|
||||||
|
u32 cntkctl = arch_timer_get_cntkctl();
|
||||||
|
cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
|
||||||
|
/* Set the divider and enable virtual event stream */
|
||||||
|
cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
|
||||||
|
| ARCH_TIMER_VIRT_EVT_EN;
|
||||||
|
arch_timer_set_cntkctl(cntkctl);
|
||||||
|
elf_hwcap |= HWCAP_EVTSTRM;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,5 +26,6 @@
|
|||||||
#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
|
#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
|
||||||
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
|
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
|
||||||
#define HWCAP_LPAE (1 << 20)
|
#define HWCAP_LPAE (1 << 20)
|
||||||
|
#define HWCAP_EVTSTRM (1 << 21)
|
||||||
|
|
||||||
#endif /* _UAPI__ASMARM_HWCAP_H */
|
#endif /* _UAPI__ASMARM_HWCAP_H */
|
||||||
|
@ -975,6 +975,7 @@ static const char *hwcap_str[] = {
|
|||||||
"idivt",
|
"idivt",
|
||||||
"vfpd32",
|
"vfpd32",
|
||||||
"lpae",
|
"lpae",
|
||||||
|
"evtstrm",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,19 +92,49 @@ static inline u32 arch_timer_get_cntfrq(void)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void arch_counter_set_user_access(void)
|
static inline u32 arch_timer_get_cntkctl(void)
|
||||||
{
|
{
|
||||||
u32 cntkctl;
|
u32 cntkctl;
|
||||||
|
|
||||||
/* Disable user access to the timers and the physical counter. */
|
|
||||||
asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl));
|
asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl));
|
||||||
cntkctl &= ~((3 << 8) | (1 << 0));
|
return cntkctl;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable user access to the virtual counter and frequency. */
|
static inline void arch_timer_set_cntkctl(u32 cntkctl)
|
||||||
cntkctl |= (1 << 1);
|
{
|
||||||
asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl));
|
asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void arch_counter_set_user_access(void)
|
||||||
|
{
|
||||||
|
u32 cntkctl = arch_timer_get_cntkctl();
|
||||||
|
|
||||||
|
/* Disable user access to the timers and the physical counter */
|
||||||
|
/* Also disable virtual event stream */
|
||||||
|
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
|
||||||
|
| ARCH_TIMER_USR_VT_ACCESS_EN
|
||||||
|
| ARCH_TIMER_VIRT_EVT_EN
|
||||||
|
| ARCH_TIMER_USR_PCT_ACCESS_EN);
|
||||||
|
|
||||||
|
/* Enable user access to the virtual counter */
|
||||||
|
cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
|
||||||
|
|
||||||
|
arch_timer_set_cntkctl(cntkctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void arch_timer_evtstrm_enable(int divider)
|
||||||
|
{
|
||||||
|
u32 cntkctl = arch_timer_get_cntkctl();
|
||||||
|
cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
|
||||||
|
/* Set the divider and enable virtual event stream */
|
||||||
|
cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
|
||||||
|
| ARCH_TIMER_VIRT_EVT_EN;
|
||||||
|
arch_timer_set_cntkctl(cntkctl);
|
||||||
|
elf_hwcap |= HWCAP_EVTSTRM;
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static inline u64 arch_counter_get_cntvct(void)
|
static inline u64 arch_counter_get_cntvct(void)
|
||||||
{
|
{
|
||||||
u64 cval;
|
u64 cval;
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#define COMPAT_HWCAP_IDIVA (1 << 17)
|
#define COMPAT_HWCAP_IDIVA (1 << 17)
|
||||||
#define COMPAT_HWCAP_IDIVT (1 << 18)
|
#define COMPAT_HWCAP_IDIVT (1 << 18)
|
||||||
#define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT)
|
#define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT)
|
||||||
|
#define COMPAT_HWCAP_EVTSTRM (1 << 21)
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
/*
|
/*
|
||||||
@ -37,11 +38,11 @@
|
|||||||
* instruction set this cpu supports.
|
* instruction set this cpu supports.
|
||||||
*/
|
*/
|
||||||
#define ELF_HWCAP (elf_hwcap)
|
#define ELF_HWCAP (elf_hwcap)
|
||||||
#define COMPAT_ELF_HWCAP (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\
|
|
||||||
COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
|
#ifdef CONFIG_COMPAT
|
||||||
COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
|
#define COMPAT_ELF_HWCAP (compat_elf_hwcap)
|
||||||
COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
|
extern unsigned int compat_elf_hwcap;
|
||||||
COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV)
|
#endif
|
||||||
|
|
||||||
extern unsigned long elf_hwcap;
|
extern unsigned long elf_hwcap;
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
#define HWCAP_FP (1 << 0)
|
#define HWCAP_FP (1 << 0)
|
||||||
#define HWCAP_ASIMD (1 << 1)
|
#define HWCAP_ASIMD (1 << 1)
|
||||||
|
#define HWCAP_EVTSTRM (1 << 2)
|
||||||
|
|
||||||
|
|
||||||
#endif /* _UAPI__ASM_HWCAP_H */
|
#endif /* _UAPI__ASM_HWCAP_H */
|
||||||
|
@ -60,6 +60,16 @@ EXPORT_SYMBOL(processor_id);
|
|||||||
unsigned long elf_hwcap __read_mostly;
|
unsigned long elf_hwcap __read_mostly;
|
||||||
EXPORT_SYMBOL_GPL(elf_hwcap);
|
EXPORT_SYMBOL_GPL(elf_hwcap);
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
#define COMPAT_ELF_HWCAP_DEFAULT \
|
||||||
|
(COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\
|
||||||
|
COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
|
||||||
|
COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
|
||||||
|
COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
|
||||||
|
COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV)
|
||||||
|
unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *cpu_name;
|
static const char *cpu_name;
|
||||||
static const char *machine_name;
|
static const char *machine_name;
|
||||||
phys_addr_t __fdt_pointer __initdata;
|
phys_addr_t __fdt_pointer __initdata;
|
||||||
@ -304,6 +314,7 @@ subsys_initcall(topology_init);
|
|||||||
static const char *hwcap_str[] = {
|
static const char *hwcap_str[] = {
|
||||||
"fp",
|
"fp",
|
||||||
"asimd",
|
"asimd",
|
||||||
|
"evtstrm",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,6 +75,21 @@ config ARM_ARCH_TIMER
|
|||||||
bool
|
bool
|
||||||
select CLKSRC_OF if OF
|
select CLKSRC_OF if OF
|
||||||
|
|
||||||
|
config ARM_ARCH_TIMER_EVTSTREAM
|
||||||
|
bool "Support for ARM architected timer event stream generation"
|
||||||
|
default y if ARM_ARCH_TIMER
|
||||||
|
help
|
||||||
|
This option enables support for event stream generation based on
|
||||||
|
the ARM architected timer. It is used for waking up CPUs executing
|
||||||
|
the wfe instruction at a frequency represented as a power-of-2
|
||||||
|
divisor of the clock rate.
|
||||||
|
The main use of the event stream is wfe-based timeouts of userspace
|
||||||
|
locking implementations. It might also be useful for imposing timeout
|
||||||
|
on wfe to safeguard against any programming errors in case an expected
|
||||||
|
event is not generated.
|
||||||
|
This must be disabled for hardware validation purposes to detect any
|
||||||
|
hardware anomalies of missing events.
|
||||||
|
|
||||||
config ARM_GLOBAL_TIMER
|
config ARM_GLOBAL_TIMER
|
||||||
bool
|
bool
|
||||||
select CLKSRC_OF if OF
|
select CLKSRC_OF if OF
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/cpu_pm.h>
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
@ -294,6 +295,19 @@ static void __arch_timer_setup(unsigned type,
|
|||||||
clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
|
clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void arch_timer_configure_evtstream(void)
|
||||||
|
{
|
||||||
|
int evt_stream_div, pos;
|
||||||
|
|
||||||
|
/* Find the closest power of two to the divisor */
|
||||||
|
evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ;
|
||||||
|
pos = fls(evt_stream_div);
|
||||||
|
if (pos > 1 && !(evt_stream_div & (1 << (pos - 2))))
|
||||||
|
pos--;
|
||||||
|
/* enable event stream */
|
||||||
|
arch_timer_evtstrm_enable(min(pos, 15));
|
||||||
|
}
|
||||||
|
|
||||||
static int arch_timer_setup(struct clock_event_device *clk)
|
static int arch_timer_setup(struct clock_event_device *clk)
|
||||||
{
|
{
|
||||||
__arch_timer_setup(ARCH_CP15_TIMER, clk);
|
__arch_timer_setup(ARCH_CP15_TIMER, clk);
|
||||||
@ -307,6 +321,8 @@ static int arch_timer_setup(struct clock_event_device *clk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
arch_counter_set_user_access();
|
arch_counter_set_user_access();
|
||||||
|
if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM))
|
||||||
|
arch_timer_configure_evtstream();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -460,6 +476,33 @@ static struct notifier_block arch_timer_cpu_nb = {
|
|||||||
.notifier_call = arch_timer_cpu_notify,
|
.notifier_call = arch_timer_cpu_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_CPU_PM
|
||||||
|
static unsigned int saved_cntkctl;
|
||||||
|
static int arch_timer_cpu_pm_notify(struct notifier_block *self,
|
||||||
|
unsigned long action, void *hcpu)
|
||||||
|
{
|
||||||
|
if (action == CPU_PM_ENTER)
|
||||||
|
saved_cntkctl = arch_timer_get_cntkctl();
|
||||||
|
else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT)
|
||||||
|
arch_timer_set_cntkctl(saved_cntkctl);
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block arch_timer_cpu_pm_notifier = {
|
||||||
|
.notifier_call = arch_timer_cpu_pm_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init arch_timer_cpu_pm_init(void)
|
||||||
|
{
|
||||||
|
return cpu_pm_register_notifier(&arch_timer_cpu_pm_notifier);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int __init arch_timer_cpu_pm_init(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __init arch_timer_register(void)
|
static int __init arch_timer_register(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -499,11 +542,17 @@ static int __init arch_timer_register(void)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_free_irq;
|
goto out_free_irq;
|
||||||
|
|
||||||
|
err = arch_timer_cpu_pm_init();
|
||||||
|
if (err)
|
||||||
|
goto out_unreg_notify;
|
||||||
|
|
||||||
/* Immediately configure the timer on the boot CPU */
|
/* Immediately configure the timer on the boot CPU */
|
||||||
arch_timer_setup(this_cpu_ptr(arch_timer_evt));
|
arch_timer_setup(this_cpu_ptr(arch_timer_evt));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_unreg_notify:
|
||||||
|
unregister_cpu_notifier(&arch_timer_cpu_nb);
|
||||||
out_free_irq:
|
out_free_irq:
|
||||||
if (arch_timer_use_virtual)
|
if (arch_timer_use_virtual)
|
||||||
free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);
|
free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);
|
||||||
|
@ -33,6 +33,16 @@ enum arch_timer_reg {
|
|||||||
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
|
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
|
||||||
#define ARCH_TIMER_MEM_VIRT_ACCESS 3
|
#define ARCH_TIMER_MEM_VIRT_ACCESS 3
|
||||||
|
|
||||||
|
#define ARCH_TIMER_USR_PCT_ACCESS_EN (1 << 0) /* physical counter */
|
||||||
|
#define ARCH_TIMER_USR_VCT_ACCESS_EN (1 << 1) /* virtual counter */
|
||||||
|
#define ARCH_TIMER_VIRT_EVT_EN (1 << 2)
|
||||||
|
#define ARCH_TIMER_EVT_TRIGGER_SHIFT (4)
|
||||||
|
#define ARCH_TIMER_EVT_TRIGGER_MASK (0xF << ARCH_TIMER_EVT_TRIGGER_SHIFT)
|
||||||
|
#define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */
|
||||||
|
#define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */
|
||||||
|
|
||||||
|
#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */
|
||||||
|
|
||||||
#ifdef CONFIG_ARM_ARCH_TIMER
|
#ifdef CONFIG_ARM_ARCH_TIMER
|
||||||
|
|
||||||
extern u32 arch_timer_get_rate(void);
|
extern u32 arch_timer_get_rate(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user