diff options
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 15 | ||||
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 49 |
2 files changed, 64 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 971d796e071d..5e940f839a2d 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -75,6 +75,21 @@ config ARM_ARCH_TIMER | |||
75 | bool | 75 | bool |
76 | select CLKSRC_OF if OF | 76 | select CLKSRC_OF if OF |
77 | 77 | ||
78 | config ARM_ARCH_TIMER_EVTSTREAM | ||
79 | bool "Support for ARM architected timer event stream generation" | ||
80 | default y if ARM_ARCH_TIMER | ||
81 | help | ||
82 | This option enables support for event stream generation based on | ||
83 | the ARM architected timer. It is used for waking up CPUs executing | ||
84 | the wfe instruction at a frequency represented as a power-of-2 | ||
85 | divisor of the clock rate. | ||
86 | The main use of the event stream is wfe-based timeouts of userspace | ||
87 | locking implementations. It might also be useful for imposing timeout | ||
88 | on wfe to safeguard against any programming errors in case an expected | ||
89 | event is not generated. | ||
90 | This must be disabled for hardware validation purposes to detect any | ||
91 | hardware anomalies of missing events. | ||
92 | |||
78 | config ARM_GLOBAL_TIMER | 93 | config ARM_GLOBAL_TIMER |
79 | bool | 94 | bool |
80 | select CLKSRC_OF if OF | 95 | select CLKSRC_OF if OF |
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index ce98d5e70927..b94b0d44c158 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/smp.h> | 14 | #include <linux/smp.h> |
15 | #include <linux/cpu.h> | 15 | #include <linux/cpu.h> |
16 | #include <linux/cpu_pm.h> | ||
16 | #include <linux/clockchips.h> | 17 | #include <linux/clockchips.h> |
17 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
18 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
@@ -294,6 +295,19 @@ static void __arch_timer_setup(unsigned type, | |||
294 | clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); | 295 | clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); |
295 | } | 296 | } |
296 | 297 | ||
298 | static void arch_timer_configure_evtstream(void) | ||
299 | { | ||
300 | int evt_stream_div, pos; | ||
301 | |||
302 | /* Find the closest power of two to the divisor */ | ||
303 | evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ; | ||
304 | pos = fls(evt_stream_div); | ||
305 | if (pos > 1 && !(evt_stream_div & (1 << (pos - 2)))) | ||
306 | pos--; | ||
307 | /* enable event stream */ | ||
308 | arch_timer_evtstrm_enable(min(pos, 15)); | ||
309 | } | ||
310 | |||
297 | static int arch_timer_setup(struct clock_event_device *clk) | 311 | static int arch_timer_setup(struct clock_event_device *clk) |
298 | { | 312 | { |
299 | __arch_timer_setup(ARCH_CP15_TIMER, clk); | 313 | __arch_timer_setup(ARCH_CP15_TIMER, clk); |
@@ -307,6 +321,8 @@ static int arch_timer_setup(struct clock_event_device *clk) | |||
307 | } | 321 | } |
308 | 322 | ||
309 | arch_counter_set_user_access(); | 323 | arch_counter_set_user_access(); |
324 | if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM)) | ||
325 | arch_timer_configure_evtstream(); | ||
310 | 326 | ||
311 | return 0; | 327 | return 0; |
312 | } | 328 | } |
@@ -460,6 +476,33 @@ static struct notifier_block arch_timer_cpu_nb = { | |||
460 | .notifier_call = arch_timer_cpu_notify, | 476 | .notifier_call = arch_timer_cpu_notify, |
461 | }; | 477 | }; |
462 | 478 | ||
479 | #ifdef CONFIG_CPU_PM | ||
480 | static unsigned int saved_cntkctl; | ||
481 | static int arch_timer_cpu_pm_notify(struct notifier_block *self, | ||
482 | unsigned long action, void *hcpu) | ||
483 | { | ||
484 | if (action == CPU_PM_ENTER) | ||
485 | saved_cntkctl = arch_timer_get_cntkctl(); | ||
486 | else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) | ||
487 | arch_timer_set_cntkctl(saved_cntkctl); | ||
488 | return NOTIFY_OK; | ||
489 | } | ||
490 | |||
491 | static struct notifier_block arch_timer_cpu_pm_notifier = { | ||
492 | .notifier_call = arch_timer_cpu_pm_notify, | ||
493 | }; | ||
494 | |||
495 | static int __init arch_timer_cpu_pm_init(void) | ||
496 | { | ||
497 | return cpu_pm_register_notifier(&arch_timer_cpu_pm_notifier); | ||
498 | } | ||
499 | #else | ||
500 | static int __init arch_timer_cpu_pm_init(void) | ||
501 | { | ||
502 | return 0; | ||
503 | } | ||
504 | #endif | ||
505 | |||
463 | static int __init arch_timer_register(void) | 506 | static int __init arch_timer_register(void) |
464 | { | 507 | { |
465 | int err; | 508 | int err; |
@@ -499,11 +542,17 @@ static int __init arch_timer_register(void) | |||
499 | if (err) | 542 | if (err) |
500 | goto out_free_irq; | 543 | goto out_free_irq; |
501 | 544 | ||
545 | err = arch_timer_cpu_pm_init(); | ||
546 | if (err) | ||
547 | goto out_unreg_notify; | ||
548 | |||
502 | /* Immediately configure the timer on the boot CPU */ | 549 | /* Immediately configure the timer on the boot CPU */ |
503 | arch_timer_setup(this_cpu_ptr(arch_timer_evt)); | 550 | arch_timer_setup(this_cpu_ptr(arch_timer_evt)); |
504 | 551 | ||
505 | return 0; | 552 | return 0; |
506 | 553 | ||
554 | out_unreg_notify: | ||
555 | unregister_cpu_notifier(&arch_timer_cpu_nb); | ||
507 | out_free_irq: | 556 | out_free_irq: |
508 | if (arch_timer_use_virtual) | 557 | if (arch_timer_use_virtual) |
509 | free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt); | 558 | free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt); |