Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[cascardo/linux.git] / drivers / clocksource / arm_arch_timer.c
index fbd9ccd..95fb944 100644 (file)
 #include <linux/device.h>
 #include <linux/smp.h>
 #include <linux/cpu.h>
+#include <linux/cpu_pm.h>
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/sched_clock.h>
 
 #include <asm/arch_timer.h>
 #include <asm/virt.h>
@@ -294,6 +296,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 +322,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;
 }
@@ -389,7 +406,7 @@ static struct clocksource clocksource_counter = {
        .rating = 400,
        .read   = arch_counter_read,
        .mask   = CLOCKSOURCE_MASK(56),
-       .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+       .flags  = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
 };
 
 static struct cyclecounter cyclecounter = {
@@ -419,6 +436,9 @@ static void __init arch_counter_register(unsigned type)
        cyclecounter.mult = clocksource_counter.mult;
        cyclecounter.shift = clocksource_counter.shift;
        timecounter_init(&timecounter, &cyclecounter, start_count);
+
+       /* 56 bits minimum, so we assume worst case rollover */
+       sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate);
 }
 
 static void arch_timer_stop(struct clock_event_device *clk)
@@ -460,6 +480,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;
@@ -499,11 +546,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);