arm_arch_timer: Expose event stream status
authorJulien Thierry <julien.thierry@arm.com>
Fri, 13 Oct 2017 13:32:55 +0000 (14:32 +0100)
committerWill Deacon <will.deacon@arm.com>
Fri, 13 Oct 2017 17:55:05 +0000 (18:55 +0100)
The arch timer configuration for a CPU might get reset after suspending
said CPU.

In order to reliably use the event stream in the kernel (e.g. for delays),
we keep track of the state where we can safely consider the event stream as
properly configured. After writing to cntkctl, we issue an ISB to ensure
that subsequent delay loops can rely on the event stream being enabled.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm/include/asm/arch_timer.h
arch/arm64/include/asm/arch_timer.h
drivers/clocksource/arm_arch_timer.c
include/clocksource/arm_arch_timer.h

index d4ebf5679f1fd90c9422589381423a2bcd70e5bc..0b6e1040f9e7285c5cfd8e6252f853e2c2277291 100644 (file)
@@ -106,6 +106,7 @@ static inline u32 arch_timer_get_cntkctl(void)
 static inline void arch_timer_set_cntkctl(u32 cntkctl)
 {
        asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
+       isb();
 }
 
 #endif
index a652ce0a5cb2c33178ac2e447fad1159efb0c6a9..bdedd8f748d17c42c36a033b80c26b86e48c3661 100644 (file)
@@ -144,6 +144,7 @@ static inline u32 arch_timer_get_cntkctl(void)
 static inline void arch_timer_set_cntkctl(u32 cntkctl)
 {
        write_sysreg(cntkctl, cntkctl_el1);
+       isb();
 }
 
 static inline u64 arch_counter_get_cntpct(void)
index fd4b7f684bd030151d388071e05a3c2cfcb5e2c4..13e6baa13a89b2cb072d167dccd79aa56fa10233 100644 (file)
@@ -77,6 +77,7 @@ static bool arch_timer_mem_use_virtual;
 static bool arch_counter_suspend_stop;
 static bool vdso_default = true;
 
+static cpumask_t evtstrm_available = CPU_MASK_NONE;
 static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM);
 
 static int __init early_evtstrm_cfg(char *buf)
@@ -740,6 +741,7 @@ static void arch_timer_evtstrm_enable(int divider)
 #ifdef CONFIG_COMPAT
        compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
 #endif
+       cpumask_set_cpu(smp_processor_id(), &evtstrm_available);
 }
 
 static void arch_timer_configure_evtstream(void)
@@ -864,6 +866,16 @@ u32 arch_timer_get_rate(void)
        return arch_timer_rate;
 }
 
+bool arch_timer_evtstrm_available(void)
+{
+       /*
+        * We might get called from a preemptible context. This is fine
+        * because availability of the event stream should be always the same
+        * for a preemptible context and context where we might resume a task.
+        */
+       return cpumask_test_cpu(raw_smp_processor_id(), &evtstrm_available);
+}
+
 static u64 arch_counter_get_cntvct_mem(void)
 {
        u32 vct_lo, vct_hi, tmp_hi;
@@ -929,6 +941,8 @@ static int arch_timer_dying_cpu(unsigned int cpu)
 {
        struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt);
 
+       cpumask_clear_cpu(smp_processor_id(), &evtstrm_available);
+
        arch_timer_stop(clk);
        return 0;
 }
@@ -938,10 +952,16 @@ static DEFINE_PER_CPU(unsigned long, saved_cntkctl);
 static int arch_timer_cpu_pm_notify(struct notifier_block *self,
                                    unsigned long action, void *hcpu)
 {
-       if (action == CPU_PM_ENTER)
+       if (action == CPU_PM_ENTER) {
                __this_cpu_write(saved_cntkctl, arch_timer_get_cntkctl());
-       else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT)
+
+               cpumask_clear_cpu(smp_processor_id(), &evtstrm_available);
+       } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) {
                arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl));
+
+               if (elf_hwcap & HWCAP_EVTSTRM)
+                       cpumask_set_cpu(smp_processor_id(), &evtstrm_available);
+       }
        return NOTIFY_OK;
 }
 
@@ -1017,7 +1037,6 @@ static int __init arch_timer_register(void)
        if (err)
                goto out_unreg_notify;
 
-
        /* Register and immediately configure the timer on the boot CPU */
        err = cpuhp_setup_state(CPUHP_AP_ARM_ARCH_TIMER_STARTING,
                                "clockevents/arm/arch_timer:starting",
index cc805b72994acd1442a50a4ec6e7d5593a833d76..4e28283e2ec6a75bf715ebbd913f12ea1f01690b 100644 (file)
@@ -93,6 +93,7 @@ struct arch_timer_mem {
 extern u32 arch_timer_get_rate(void);
 extern u64 (*arch_timer_read_counter)(void);
 extern struct arch_timer_kvm_info *arch_timer_get_kvm_info(void);
+extern bool arch_timer_evtstrm_available(void);
 
 #else
 
@@ -106,6 +107,11 @@ static inline u64 arch_timer_read_counter(void)
        return 0;
 }
 
+static inline bool arch_timer_evtstrm_available(void)
+{
+       return false;
+}
+
 #endif
 
 #endif