diff options
-rw-r--r-- | arch/arm64/include/asm/arch_timer.h | 31 | ||||
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 42 |
2 files changed, 50 insertions, 23 deletions
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index 01917b4c65ca..6bd1a9a1573a 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/bug.h> | 25 | #include <linux/bug.h> |
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/jump_label.h> | 27 | #include <linux/jump_label.h> |
28 | #include <linux/smp.h> | ||
28 | #include <linux/types.h> | 29 | #include <linux/types.h> |
29 | 30 | ||
30 | #include <clocksource/arm_arch_timer.h> | 31 | #include <clocksource/arm_arch_timer.h> |
@@ -55,17 +56,25 @@ struct arch_timer_erratum_workaround { | |||
55 | int (*set_next_event_virt)(unsigned long, struct clock_event_device *); | 56 | int (*set_next_event_virt)(unsigned long, struct clock_event_device *); |
56 | }; | 57 | }; |
57 | 58 | ||
58 | extern const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround; | 59 | DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *, |
59 | 60 | timer_unstable_counter_workaround); | |
60 | #define arch_timer_reg_read_stable(reg) \ | 61 | |
61 | ({ \ | 62 | #define arch_timer_reg_read_stable(reg) \ |
62 | u64 _val; \ | 63 | ({ \ |
63 | if (needs_unstable_timer_counter_workaround() && \ | 64 | u64 _val; \ |
64 | timer_unstable_counter_workaround->read_##reg) \ | 65 | if (needs_unstable_timer_counter_workaround()) { \ |
65 | _val = timer_unstable_counter_workaround->read_##reg(); \ | 66 | const struct arch_timer_erratum_workaround *wa; \ |
66 | else \ | 67 | preempt_disable(); \ |
67 | _val = read_sysreg(reg); \ | 68 | wa = __this_cpu_read(timer_unstable_counter_workaround); \ |
68 | _val; \ | 69 | if (wa && wa->read_##reg) \ |
70 | _val = wa->read_##reg(); \ | ||
71 | else \ | ||
72 | _val = read_sysreg(reg); \ | ||
73 | preempt_enable(); \ | ||
74 | } else { \ | ||
75 | _val = read_sysreg(reg); \ | ||
76 | } \ | ||
77 | _val; \ | ||
69 | }) | 78 | }) |
70 | 79 | ||
71 | /* | 80 | /* |
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a0c9ee80147e..4551587bcb44 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
@@ -235,7 +235,8 @@ static u64 notrace hisi_161010101_read_cntvct_el0(void) | |||
235 | #endif | 235 | #endif |
236 | 236 | ||
237 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND | 237 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
238 | const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL; | 238 | DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, |
239 | timer_unstable_counter_workaround); | ||
239 | EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); | 240 | EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); |
240 | 241 | ||
241 | DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); | 242 | DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); |
@@ -338,9 +339,18 @@ arch_timer_iterate_errata(enum arch_timer_erratum_match_type type, | |||
338 | } | 339 | } |
339 | 340 | ||
340 | static | 341 | static |
341 | void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa) | 342 | void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa, |
343 | bool local) | ||
342 | { | 344 | { |
343 | timer_unstable_counter_workaround = wa; | 345 | int i; |
346 | |||
347 | if (local) { | ||
348 | __this_cpu_write(timer_unstable_counter_workaround, wa); | ||
349 | } else { | ||
350 | for_each_possible_cpu(i) | ||
351 | per_cpu(timer_unstable_counter_workaround, i) = wa; | ||
352 | } | ||
353 | |||
344 | static_branch_enable(&arch_timer_read_ool_enabled); | 354 | static_branch_enable(&arch_timer_read_ool_enabled); |
345 | } | 355 | } |
346 | 356 | ||
@@ -369,14 +379,17 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t | |||
369 | return; | 379 | return; |
370 | 380 | ||
371 | if (needs_unstable_timer_counter_workaround()) { | 381 | if (needs_unstable_timer_counter_workaround()) { |
372 | if (wa != timer_unstable_counter_workaround) | 382 | const struct arch_timer_erratum_workaround *__wa; |
383 | __wa = __this_cpu_read(timer_unstable_counter_workaround); | ||
384 | if (__wa && wa != __wa) | ||
373 | pr_warn("Can't enable workaround for %s (clashes with %s\n)", | 385 | pr_warn("Can't enable workaround for %s (clashes with %s\n)", |
374 | wa->desc, | 386 | wa->desc, __wa->desc); |
375 | timer_unstable_counter_workaround->desc); | 387 | |
376 | return; | 388 | if (__wa) |
389 | return; | ||
377 | } | 390 | } |
378 | 391 | ||
379 | arch_timer_enable_workaround(wa); | 392 | arch_timer_enable_workaround(wa, local); |
380 | pr_info("Enabling %s workaround for %s\n", | 393 | pr_info("Enabling %s workaround for %s\n", |
381 | local ? "local" : "global", wa->desc); | 394 | local ? "local" : "global", wa->desc); |
382 | } | 395 | } |
@@ -384,10 +397,15 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t | |||
384 | #define erratum_handler(fn, r, ...) \ | 397 | #define erratum_handler(fn, r, ...) \ |
385 | ({ \ | 398 | ({ \ |
386 | bool __val; \ | 399 | bool __val; \ |
387 | if (needs_unstable_timer_counter_workaround() && \ | 400 | if (needs_unstable_timer_counter_workaround()) { \ |
388 | timer_unstable_counter_workaround->fn) { \ | 401 | const struct arch_timer_erratum_workaround *__wa; \ |
389 | r = timer_unstable_counter_workaround->fn(__VA_ARGS__); \ | 402 | __wa = __this_cpu_read(timer_unstable_counter_workaround); \ |
390 | __val = true; \ | 403 | if (__wa && __wa->fn) { \ |
404 | r = __wa->fn(__VA_ARGS__); \ | ||
405 | __val = true; \ | ||
406 | } else { \ | ||
407 | __val = false; \ | ||
408 | } \ | ||
391 | } else { \ | 409 | } else { \ |
392 | __val = false; \ | 410 | __val = false; \ |
393 | } \ | 411 | } \ |