From 28061758dc83df445a05af347b5ce55ccd968c03 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 13:43:26 +0100 Subject: [PATCH 1/5] ARM/ARM64: arch_timer: add macros for bits in control register Add macros to describe the bitfields in the ARM architected timer control register to make code easy to understand. Reviewed-by: Lorenzo Pieralisi Reviewed-by: Will Deacon Acked-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha --- arch/arm/include/asm/arch_timer.h | 9 +++++++-- arch/arm64/include/asm/arch_timer.h | 12 ++++++++---- include/clocksource/arm_arch_timer.h | 8 ++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 5665134bfa3e..d57e04de8e8c 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -93,8 +93,13 @@ static inline void arch_counter_set_user_access(void) asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); - /* disable user access to everything */ - cntkctl &= ~((3 << 8) | (7 << 0)); + /* 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); asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); } diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index c9f1d2816c2b..2b9722f42729 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -96,12 +96,16 @@ static inline void arch_counter_set_user_access(void) { u32 cntkctl; - /* Disable user access to the timers and the physical counter. */ asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl)); - cntkctl &= ~((3 << 8) | (1 << 0)); - /* Enable user access to the virtual counter and frequency. */ - cntkctl |= (1 << 1); + /* Disable user access to the timers and the physical counter */ + cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN + | ARCH_TIMER_USR_VT_ACCESS_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); + + /* Enable user access to the virtual counter */ + cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); } diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 93b7f96f9c59..8707dae4cee2 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -33,6 +33,14 @@ enum arch_timer_reg { #define ARCH_TIMER_MEM_PHYS_ACCESS 2 #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 */ + #ifdef CONFIG_ARM_ARCH_TIMER extern u32 arch_timer_get_rate(void); From e9faebc66ec74f1ab7f267d683b45e80faa69763 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 14:30:32 +0100 Subject: [PATCH 2/5] ARM: arch_timer: add support to configure and enable event stream This patch adds support for configuring the event stream frequency and enabling it. It also adds the hwcaps definitions to the user to detect this event stream feature. Cc: Russell King Cc: Lorenzo Pieralisi Acked-by: Catalin Marinas Acked-by: Will Deacon Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha --- arch/arm/include/asm/arch_timer.h | 29 +++++++++++++++++++++++++---- arch/arm/include/uapi/asm/hwcap.h | 1 + arch/arm/kernel/setup.c | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index d57e04de8e8c..0704e0cf5571 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -87,11 +87,21 @@ static inline u64 arch_counter_get_cntvct(void) return cval; } -static inline void arch_counter_set_user_access(void) +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)); +} + +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 */ @@ -100,9 +110,20 @@ static inline void arch_counter_set_user_access(void) | ARCH_TIMER_VIRT_EVT_EN | ARCH_TIMER_USR_VCT_ACCESS_EN | ARCH_TIMER_USR_PCT_ACCESS_EN); - - asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); + 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 diff --git a/arch/arm/include/uapi/asm/hwcap.h b/arch/arm/include/uapi/asm/hwcap.h index 6d34d080372a..7dcc10d67253 100644 --- a/arch/arm/include/uapi/asm/hwcap.h +++ b/arch/arm/include/uapi/asm/hwcap.h @@ -26,5 +26,6 @@ #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) #define HWCAP_LPAE (1 << 20) +#define HWCAP_EVTSTRM (1 << 21) #endif /* _UAPI__ASMARM_HWCAP_H */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 0e1e2b3afa45..5d65438685d8 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -975,6 +975,7 @@ static const char *hwcap_str[] = { "idivt", "vfpd32", "lpae", + "evtstrm", NULL }; From 46efe547aca8498d51b64460c02366ae4032ca32 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Tue, 13 Aug 2013 15:57:53 +0100 Subject: [PATCH 3/5] ARM64: arch_timer: add support to configure and enable event stream This patch adds support for configuring the event stream frequency and enabling it. It also adds the hwcaps as well as compat-specific definitions to the user to detect this event stream feature. Cc: Lorenzo Pieralisi Cc: Will Deacon Acked-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha --- arch/arm64/include/asm/arch_timer.h | 32 ++++++++++++++++++++++++++--- arch/arm64/include/asm/hwcap.h | 11 +++++----- arch/arm64/include/uapi/asm/hwcap.h | 1 + arch/arm64/kernel/setup.c | 11 ++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index 2b9722f42729..9400596a0f39 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -92,21 +92,47 @@ static inline u32 arch_timer_get_cntfrq(void) return val; } -static inline void arch_counter_set_user_access(void) +static inline u32 arch_timer_get_cntkctl(void) { u32 cntkctl; - asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl)); + return cntkctl; +} + +static inline void arch_timer_set_cntkctl(u32 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; - asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); + 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) diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index e2950b098e76..6cddbb0c9f54 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -30,6 +30,7 @@ #define COMPAT_HWCAP_IDIVA (1 << 17) #define COMPAT_HWCAP_IDIVT (1 << 18) #define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT) +#define COMPAT_HWCAP_EVTSTRM (1 << 21) #ifndef __ASSEMBLY__ /* @@ -37,11 +38,11 @@ * instruction set this cpu supports. */ #define ELF_HWCAP (elf_hwcap) -#define COMPAT_ELF_HWCAP (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) + +#ifdef CONFIG_COMPAT +#define COMPAT_ELF_HWCAP (compat_elf_hwcap) +extern unsigned int compat_elf_hwcap; +#endif extern unsigned long elf_hwcap; #endif diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index eea497578b87..9b12476e9c85 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -21,6 +21,7 @@ */ #define HWCAP_FP (1 << 0) #define HWCAP_ASIMD (1 << 1) +#define HWCAP_EVTSTRM (1 << 2) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 055cfb80e05c..d355b7b9710b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -60,6 +60,16 @@ EXPORT_SYMBOL(processor_id); unsigned long elf_hwcap __read_mostly; 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 *machine_name; phys_addr_t __fdt_pointer __initdata; @@ -304,6 +314,7 @@ subsys_initcall(topology_init); static const char *hwcap_str[] = { "fp", "asimd", + "evtstrm", NULL }; From 037f637767a82907efedda78d3ff405c34020075 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 23 Aug 2013 15:32:29 +0100 Subject: [PATCH 4/5] drivers: clocksource: add support for ARM architected timer event stream The ARM architected timer can generate events (used for waking up CPUs executing the wfe instruction) at a frequency represented as a power-of-2 divisor of the clock rate. An event stream might be used: - To implement wfe-based timeouts for userspace locking implementations. - To impose a timeout on a wfe for safeguarding against any programming error in case an expected event is not generated. This patch computes the event stream frequency aiming for a period of 100us between events. It uses ARM/ARM64 specific backends to configure and enable the event stream. Cc: Lorenzo Pieralisi Reviewed-by: Catalin Marinas Acked-by: Olof Johansson Signed-off-by: Will Deacon [sudeep: moving ARM/ARM64 changes into separate patches and adding Kconfig option] Signed-off-by: Sudeep KarkadaNagesha --- drivers/clocksource/Kconfig | 15 +++++++++++++++ drivers/clocksource/arm_arch_timer.c | 15 +++++++++++++++ include/clocksource/arm_arch_timer.h | 2 ++ 3 files changed, 32 insertions(+) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 41c69469ce20..559d80335446 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -74,6 +74,21 @@ config ARM_ARCH_TIMER bool 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 bool select CLKSRC_OF if OF diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fbd9ccd5e114..105f8ffa66a8 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -294,6 +294,19 @@ static void __arch_timer_setup(unsigned type, 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) { __arch_timer_setup(ARCH_CP15_TIMER, clk); @@ -307,6 +320,8 @@ static int arch_timer_setup(struct clock_event_device *clk) } arch_counter_set_user_access(); + if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM)) + arch_timer_configure_evtstream(); return 0; } diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 8707dae4cee2..6d26b40cbf5d 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -41,6 +41,8 @@ enum arch_timer_reg { #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 extern u32 arch_timer_get_rate(void); From 346e7480f1d4740b3d798da60f83f087ea6488b4 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Fri, 23 Aug 2013 15:53:15 +0100 Subject: [PATCH 5/5] drivers: clocksource: add CPU PM notifier for ARM architected timer Few control settings done in architected timer as part of initialisation can be lost when CPU enters deeper power states. They need to be restored when the CPU is (warm)reset again. This patch adds CPU PM notifiers to save the counter control register when entering low power modes and restore it when CPU exits low power. Reviewed-by: Catalin Marinas Reviewed-by: Lorenzo Pieralisi Reviewed-by: Will Deacon Acked-by: Olof Johansson Signed-off-by: Sudeep KarkadaNagesha --- drivers/clocksource/arm_arch_timer.c | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 105f8ffa66a8..9338f1ad6100 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -475,6 +476,33 @@ static struct notifier_block arch_timer_cpu_nb = { .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) { int err; @@ -514,11 +542,17 @@ static int __init arch_timer_register(void) if (err) goto out_free_irq; + err = arch_timer_cpu_pm_init(); + if (err) + goto out_unreg_notify; + /* Immediately configure the timer on the boot CPU */ arch_timer_setup(this_cpu_ptr(arch_timer_evt)); return 0; +out_unreg_notify: + unregister_cpu_notifier(&arch_timer_cpu_nb); out_free_irq: if (arch_timer_use_virtual) free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);