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 <[email protected]>
This commit is contained in:
Daniel Lezcano 2013-10-03 16:13:51 +02:00
commit 191124efb4
10 changed files with 161 additions and 16 deletions

View file

@ -75,6 +75,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

View file

@ -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);