diff options
| author | Marc Zyngier <marc.zyngier@arm.com> | 2019-04-08 11:49:04 -0400 |
|---|---|---|
| committer | Will Deacon <will.deacon@arm.com> | 2019-04-30 11:10:57 -0400 |
| commit | 5ef19a161cfa88a59508979e2f39d3d092c1d5c0 (patch) | |
| tree | 2772116c607252e543d024465bba3165caa99435 | |
| parent | dea86a80033f8b0fb25a805f46dde9f3b1a7c23a (diff) | |
clocksource/arm_arch_timer: Direcly assign set_next_event workaround
When a given timer is affected by an erratum and requires an
alternative implementation of set_next_event, we do a rather
complicated dance to detect and call the workaround on each
set_next_event call.
This is clearly idiotic, as we can perfectly detect whether
this CPU requires a workaround while setting up the clock event
device.
This only requires the CPU-specific detection to be done a bit
earlier, and we can then safely override the set_next_event pointer
if we have a workaround associated to that CPU.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by; Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
| -rw-r--r-- | arch/arm/include/asm/arch_timer.h | 4 | ||||
| -rw-r--r-- | arch/arm64/include/asm/arch_timer.h | 16 | ||||
| -rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 46 |
3 files changed, 28 insertions, 38 deletions
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 0a8d7bba2cb0..3f0a0191f763 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | #include <clocksource/arm_arch_timer.h> | 11 | #include <clocksource/arm_arch_timer.h> |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_ARM_ARCH_TIMER | 13 | #ifdef CONFIG_ARM_ARCH_TIMER |
| 14 | /* 32bit ARM doesn't know anything about timer errata... */ | ||
| 15 | #define has_erratum_handler(h) (false) | ||
| 16 | #define erratum_handler(h) (arch_timer_##h) | ||
| 17 | |||
| 14 | int arch_timer_arch_init(void); | 18 | int arch_timer_arch_init(void); |
| 15 | 19 | ||
| 16 | /* | 20 | /* |
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index f2a234d6516c..c3762ffcc933 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h | |||
| @@ -31,10 +31,26 @@ | |||
| 31 | #include <clocksource/arm_arch_timer.h> | 31 | #include <clocksource/arm_arch_timer.h> |
| 32 | 32 | ||
| 33 | #if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) | 33 | #if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) |
| 34 | #define has_erratum_handler(h) \ | ||
| 35 | ({ \ | ||
| 36 | const struct arch_timer_erratum_workaround *__wa; \ | ||
| 37 | __wa = __this_cpu_read(timer_unstable_counter_workaround); \ | ||
| 38 | (__wa && __wa->h); \ | ||
| 39 | }) | ||
| 40 | |||
| 41 | #define erratum_handler(h) \ | ||
| 42 | ({ \ | ||
| 43 | const struct arch_timer_erratum_workaround *__wa; \ | ||
| 44 | __wa = __this_cpu_read(timer_unstable_counter_workaround); \ | ||
| 45 | (__wa && __wa->h) ? __wa->h : arch_timer_##h; \ | ||
| 46 | }) | ||
| 47 | |||
| 34 | extern struct static_key_false arch_timer_read_ool_enabled; | 48 | extern struct static_key_false arch_timer_read_ool_enabled; |
| 35 | #define needs_unstable_timer_counter_workaround() \ | 49 | #define needs_unstable_timer_counter_workaround() \ |
| 36 | static_branch_unlikely(&arch_timer_read_ool_enabled) | 50 | static_branch_unlikely(&arch_timer_read_ool_enabled) |
| 37 | #else | 51 | #else |
| 52 | #define has_erratum_handler(h) false | ||
| 53 | #define erratum_handler(h) (arch_timer_##h) | ||
| 38 | #define needs_unstable_timer_counter_workaround() false | 54 | #define needs_unstable_timer_counter_workaround() false |
| 39 | #endif | 55 | #endif |
| 40 | 56 | ||
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index da11a9508b77..b2a88a64aab4 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
| @@ -598,36 +598,12 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t | |||
| 598 | local ? "local" : "global", wa->desc); | 598 | local ? "local" : "global", wa->desc); |
| 599 | } | 599 | } |
| 600 | 600 | ||
| 601 | #define erratum_handler(fn, r, ...) \ | ||
| 602 | ({ \ | ||
| 603 | bool __val; \ | ||
| 604 | if (needs_unstable_timer_counter_workaround()) { \ | ||
| 605 | const struct arch_timer_erratum_workaround *__wa; \ | ||
| 606 | __wa = __this_cpu_read(timer_unstable_counter_workaround); \ | ||
| 607 | if (__wa && __wa->fn) { \ | ||
| 608 | r = __wa->fn(__VA_ARGS__); \ | ||
| 609 | __val = true; \ | ||
| 610 | } else { \ | ||
| 611 | __val = false; \ | ||
| 612 | } \ | ||
| 613 | } else { \ | ||
| 614 | __val = false; \ | ||
| 615 | } \ | ||
| 616 | __val; \ | ||
| 617 | }) | ||
| 618 | |||
| 619 | static bool arch_timer_this_cpu_has_cntvct_wa(void) | 601 | static bool arch_timer_this_cpu_has_cntvct_wa(void) |
| 620 | { | 602 | { |
| 621 | const struct arch_timer_erratum_workaround *wa; | 603 | return has_erratum_handler(read_cntvct_el0); |
| 622 | |||
| 623 | wa = __this_cpu_read(timer_unstable_counter_workaround); | ||
| 624 | return wa && wa->read_cntvct_el0; | ||
| 625 | } | 604 | } |
| 626 | #else | 605 | #else |
| 627 | #define arch_timer_check_ool_workaround(t,a) do { } while(0) | 606 | #define arch_timer_check_ool_workaround(t,a) do { } while(0) |
| 628 | #define erratum_set_next_event_tval_virt(...) ({BUG(); 0;}) | ||
| 629 | #define erratum_set_next_event_tval_phys(...) ({BUG(); 0;}) | ||
| 630 | #define erratum_handler(fn, r, ...) ({false;}) | ||
| 631 | #define arch_timer_this_cpu_has_cntvct_wa() ({false;}) | 607 | #define arch_timer_this_cpu_has_cntvct_wa() ({false;}) |
| 632 | #endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */ | 608 | #endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */ |
| 633 | 609 | ||
| @@ -721,11 +697,6 @@ static __always_inline void set_next_event(const int access, unsigned long evt, | |||
| 721 | static int arch_timer_set_next_event_virt(unsigned long evt, | 697 | static int arch_timer_set_next_event_virt(unsigned long evt, |
| 722 | struct clock_event_device *clk) | 698 | struct clock_event_device *clk) |
| 723 | { | 699 | { |
| 724 | int ret; | ||
| 725 | |||
| 726 | if (erratum_handler(set_next_event_virt, ret, evt, clk)) | ||
| 727 | return ret; | ||
| 728 | |||
| 729 | set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk); | 700 | set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk); |
| 730 | return 0; | 701 | return 0; |
| 731 | } | 702 | } |
| @@ -733,11 +704,6 @@ static int arch_timer_set_next_event_virt(unsigned long evt, | |||
| 733 | static int arch_timer_set_next_event_phys(unsigned long evt, | 704 | static int arch_timer_set_next_event_phys(unsigned long evt, |
| 734 | struct clock_event_device *clk) | 705 | struct clock_event_device *clk) |
| 735 | { | 706 | { |
| 736 | int ret; | ||
| 737 | |||
| 738 | if (erratum_handler(set_next_event_phys, ret, evt, clk)) | ||
| 739 | return ret; | ||
| 740 | |||
| 741 | set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk); | 707 | set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk); |
| 742 | return 0; | 708 | return 0; |
| 743 | } | 709 | } |
| @@ -762,6 +728,10 @@ static void __arch_timer_setup(unsigned type, | |||
| 762 | clk->features = CLOCK_EVT_FEAT_ONESHOT; | 728 | clk->features = CLOCK_EVT_FEAT_ONESHOT; |
| 763 | 729 | ||
| 764 | if (type == ARCH_TIMER_TYPE_CP15) { | 730 | if (type == ARCH_TIMER_TYPE_CP15) { |
| 731 | typeof(clk->set_next_event) sne; | ||
| 732 | |||
| 733 | arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL); | ||
| 734 | |||
| 765 | if (arch_timer_c3stop) | 735 | if (arch_timer_c3stop) |
| 766 | clk->features |= CLOCK_EVT_FEAT_C3STOP; | 736 | clk->features |= CLOCK_EVT_FEAT_C3STOP; |
| 767 | clk->name = "arch_sys_timer"; | 737 | clk->name = "arch_sys_timer"; |
| @@ -772,20 +742,20 @@ static void __arch_timer_setup(unsigned type, | |||
| 772 | case ARCH_TIMER_VIRT_PPI: | 742 | case ARCH_TIMER_VIRT_PPI: |
| 773 | clk->set_state_shutdown = arch_timer_shutdown_virt; | 743 | clk->set_state_shutdown = arch_timer_shutdown_virt; |
| 774 | clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; | 744 | clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; |
| 775 | clk->set_next_event = arch_timer_set_next_event_virt; | 745 | sne = erratum_handler(set_next_event_virt); |
| 776 | break; | 746 | break; |
| 777 | case ARCH_TIMER_PHYS_SECURE_PPI: | 747 | case ARCH_TIMER_PHYS_SECURE_PPI: |
| 778 | case ARCH_TIMER_PHYS_NONSECURE_PPI: | 748 | case ARCH_TIMER_PHYS_NONSECURE_PPI: |
| 779 | case ARCH_TIMER_HYP_PPI: | 749 | case ARCH_TIMER_HYP_PPI: |
| 780 | clk->set_state_shutdown = arch_timer_shutdown_phys; | 750 | clk->set_state_shutdown = arch_timer_shutdown_phys; |
| 781 | clk->set_state_oneshot_stopped = arch_timer_shutdown_phys; | 751 | clk->set_state_oneshot_stopped = arch_timer_shutdown_phys; |
| 782 | clk->set_next_event = arch_timer_set_next_event_phys; | 752 | sne = erratum_handler(set_next_event_phys); |
| 783 | break; | 753 | break; |
| 784 | default: | 754 | default: |
| 785 | BUG(); | 755 | BUG(); |
| 786 | } | 756 | } |
| 787 | 757 | ||
| 788 | arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL); | 758 | clk->set_next_event = sne; |
| 789 | } else { | 759 | } else { |
| 790 | clk->features |= CLOCK_EVT_FEAT_DYNIRQ; | 760 | clk->features |= CLOCK_EVT_FEAT_DYNIRQ; |
| 791 | clk->name = "arch_mem_timer"; | 761 | clk->name = "arch_mem_timer"; |
