diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2013-10-03 16:13:51 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2013-10-03 16:13:51 +0200 |
commit | 191124efb4d6e5e47fe073b4b97350873523e88c (patch) | |
tree | 8fb9dbbff739e19aa1750c58fe5ec12ee6547e49 /drivers/clocksource/arm_arch_timer.c | |
parent | 68e90740284c69292881cd38c7ece6f09a18a58f (diff) | |
parent | 346e7480f1d4740b3d798da60f83f087ea6488b4 (diff) | |
download | op-kernel-dev-191124efb4d6e5e47fe073b4b97350873523e88c.zip op-kernel-dev-191124efb4d6e5e47fe073b4b97350873523e88c.tar.gz |
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>
Diffstat (limited to 'drivers/clocksource/arm_arch_timer.c')
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index ce98d5e..b94b0d4 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -13,6 +13,7 @@ #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> @@ -294,6 +295,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 +321,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; } @@ -460,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; @@ -499,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); |