diff options
author | Ding Tianhong <dingtianhong@huawei.com> | 2017-02-06 11:47:41 -0500 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2017-02-07 18:14:03 -0500 |
commit | 16d10ef29f25aba923779234bb93a451b14d20e6 (patch) | |
tree | 3bec3cde9fcf0bd8e0d9a8940ef29ee9b49aeb18 /drivers/clocksource/arm_arch_timer.c | |
parent | 5444ea6a7f46276876e94ecf8d44615af1ef22f7 (diff) |
clocksource/drivers/arm_arch_timer: Introduce generic errata handling infrastructure
Currently we have code inline in the arch timer probe path to cater for
Freescale erratum A-008585, complete with ifdeffery. This is a little
ugly, and will get worse as we try to add more errata handling.
This patch refactors the handling of Freescale erratum A-008585. Now the
erratum is described in a generic arch_timer_erratum_workaround
structure, and the probe path can iterate over these to detect errata
and enable workarounds.
This will simplify the addition and maintenance of code handling
Hisilicon erratum 161010101.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
[Mark: split patch, correct Kconfig, reword commit message]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
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 | 92 |
1 files changed, 63 insertions, 29 deletions
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 6a9d031c3535..2af0739efd41 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
@@ -96,27 +96,58 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg); | |||
96 | */ | 96 | */ |
97 | 97 | ||
98 | #ifdef CONFIG_FSL_ERRATUM_A008585 | 98 | #ifdef CONFIG_FSL_ERRATUM_A008585 |
99 | DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); | 99 | /* |
100 | EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled); | 100 | * The number of retries is an arbitrary value well beyond the highest number |
101 | 101 | * of iterations the loop has been observed to take. | |
102 | static int fsl_a008585_enable = -1; | 102 | */ |
103 | 103 | #define __fsl_a008585_read_reg(reg) ({ \ | |
104 | u32 __fsl_a008585_read_cntp_tval_el0(void) | 104 | u64 _old, _new; \ |
105 | int _retries = 200; \ | ||
106 | \ | ||
107 | do { \ | ||
108 | _old = read_sysreg(reg); \ | ||
109 | _new = read_sysreg(reg); \ | ||
110 | _retries--; \ | ||
111 | } while (unlikely(_old != _new) && _retries); \ | ||
112 | \ | ||
113 | WARN_ON_ONCE(!_retries); \ | ||
114 | _new; \ | ||
115 | }) | ||
116 | |||
117 | static u32 notrace fsl_a008585_read_cntp_tval_el0(void) | ||
105 | { | 118 | { |
106 | return __fsl_a008585_read_reg(cntp_tval_el0); | 119 | return __fsl_a008585_read_reg(cntp_tval_el0); |
107 | } | 120 | } |
108 | 121 | ||
109 | u32 __fsl_a008585_read_cntv_tval_el0(void) | 122 | static u32 notrace fsl_a008585_read_cntv_tval_el0(void) |
110 | { | 123 | { |
111 | return __fsl_a008585_read_reg(cntv_tval_el0); | 124 | return __fsl_a008585_read_reg(cntv_tval_el0); |
112 | } | 125 | } |
113 | 126 | ||
114 | u64 __fsl_a008585_read_cntvct_el0(void) | 127 | static u64 notrace fsl_a008585_read_cntvct_el0(void) |
115 | { | 128 | { |
116 | return __fsl_a008585_read_reg(cntvct_el0); | 129 | return __fsl_a008585_read_reg(cntvct_el0); |
117 | } | 130 | } |
118 | EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0); | 131 | #endif |
119 | #endif /* CONFIG_FSL_ERRATUM_A008585 */ | 132 | |
133 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND | ||
134 | const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL; | ||
135 | EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); | ||
136 | |||
137 | DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); | ||
138 | EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled); | ||
139 | |||
140 | static const struct arch_timer_erratum_workaround ool_workarounds[] = { | ||
141 | #ifdef CONFIG_FSL_ERRATUM_A008585 | ||
142 | { | ||
143 | .id = "fsl,erratum-a008585", | ||
144 | .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0, | ||
145 | .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0, | ||
146 | .read_cntvct_el0 = fsl_a008585_read_cntvct_el0, | ||
147 | }, | ||
148 | #endif | ||
149 | }; | ||
150 | #endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */ | ||
120 | 151 | ||
121 | static __always_inline | 152 | static __always_inline |
122 | void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val, | 153 | void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val, |
@@ -267,8 +298,8 @@ static __always_inline void set_next_event(const int access, unsigned long evt, | |||
267 | arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); | 298 | arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); |
268 | } | 299 | } |
269 | 300 | ||
270 | #ifdef CONFIG_FSL_ERRATUM_A008585 | 301 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
271 | static __always_inline void fsl_a008585_set_next_event(const int access, | 302 | static __always_inline void erratum_set_next_event_generic(const int access, |
272 | unsigned long evt, struct clock_event_device *clk) | 303 | unsigned long evt, struct clock_event_device *clk) |
273 | { | 304 | { |
274 | unsigned long ctrl; | 305 | unsigned long ctrl; |
@@ -286,20 +317,20 @@ static __always_inline void fsl_a008585_set_next_event(const int access, | |||
286 | arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); | 317 | arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); |
287 | } | 318 | } |
288 | 319 | ||
289 | static int fsl_a008585_set_next_event_virt(unsigned long evt, | 320 | static int erratum_set_next_event_virt(unsigned long evt, |
290 | struct clock_event_device *clk) | 321 | struct clock_event_device *clk) |
291 | { | 322 | { |
292 | fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk); | 323 | erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk); |
293 | return 0; | 324 | return 0; |
294 | } | 325 | } |
295 | 326 | ||
296 | static int fsl_a008585_set_next_event_phys(unsigned long evt, | 327 | static int erratum_set_next_event_phys(unsigned long evt, |
297 | struct clock_event_device *clk) | 328 | struct clock_event_device *clk) |
298 | { | 329 | { |
299 | fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk); | 330 | erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk); |
300 | return 0; | 331 | return 0; |
301 | } | 332 | } |
302 | #endif /* CONFIG_FSL_ERRATUM_A008585 */ | 333 | #endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */ |
303 | 334 | ||
304 | static int arch_timer_set_next_event_virt(unsigned long evt, | 335 | static int arch_timer_set_next_event_virt(unsigned long evt, |
305 | struct clock_event_device *clk) | 336 | struct clock_event_device *clk) |
@@ -329,16 +360,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt, | |||
329 | return 0; | 360 | return 0; |
330 | } | 361 | } |
331 | 362 | ||
332 | static void fsl_a008585_set_sne(struct clock_event_device *clk) | 363 | static void erratum_workaround_set_sne(struct clock_event_device *clk) |
333 | { | 364 | { |
334 | #ifdef CONFIG_FSL_ERRATUM_A008585 | 365 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
335 | if (!static_branch_unlikely(&arch_timer_read_ool_enabled)) | 366 | if (!static_branch_unlikely(&arch_timer_read_ool_enabled)) |
336 | return; | 367 | return; |
337 | 368 | ||
338 | if (arch_timer_uses_ppi == VIRT_PPI) | 369 | if (arch_timer_uses_ppi == VIRT_PPI) |
339 | clk->set_next_event = fsl_a008585_set_next_event_virt; | 370 | clk->set_next_event = erratum_set_next_event_virt; |
340 | else | 371 | else |
341 | clk->set_next_event = fsl_a008585_set_next_event_phys; | 372 | clk->set_next_event = erratum_set_next_event_phys; |
342 | #endif | 373 | #endif |
343 | } | 374 | } |
344 | 375 | ||
@@ -371,7 +402,7 @@ static void __arch_timer_setup(unsigned type, | |||
371 | BUG(); | 402 | BUG(); |
372 | } | 403 | } |
373 | 404 | ||
374 | fsl_a008585_set_sne(clk); | 405 | erratum_workaround_set_sne(clk); |
375 | } else { | 406 | } else { |
376 | clk->features |= CLOCK_EVT_FEAT_DYNIRQ; | 407 | clk->features |= CLOCK_EVT_FEAT_DYNIRQ; |
377 | clk->name = "arch_mem_timer"; | 408 | clk->name = "arch_mem_timer"; |
@@ -591,7 +622,7 @@ static void __init arch_counter_register(unsigned type) | |||
591 | 622 | ||
592 | clocksource_counter.archdata.vdso_direct = true; | 623 | clocksource_counter.archdata.vdso_direct = true; |
593 | 624 | ||
594 | #ifdef CONFIG_FSL_ERRATUM_A008585 | 625 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
595 | /* | 626 | /* |
596 | * Don't use the vdso fastpath if errata require using | 627 | * Don't use the vdso fastpath if errata require using |
597 | * the out-of-line counter accessor. | 628 | * the out-of-line counter accessor. |
@@ -879,12 +910,15 @@ static int __init arch_timer_of_init(struct device_node *np) | |||
879 | 910 | ||
880 | arch_timer_c3stop = !of_property_read_bool(np, "always-on"); | 911 | arch_timer_c3stop = !of_property_read_bool(np, "always-on"); |
881 | 912 | ||
882 | #ifdef CONFIG_FSL_ERRATUM_A008585 | 913 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
883 | if (fsl_a008585_enable < 0) | 914 | for (i = 0; i < ARRAY_SIZE(ool_workarounds); i++) { |
884 | fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585"); | 915 | if (of_property_read_bool(np, ool_workarounds[i].id)) { |
885 | if (fsl_a008585_enable) { | 916 | timer_unstable_counter_workaround = &ool_workarounds[i]; |
886 | static_branch_enable(&arch_timer_read_ool_enabled); | 917 | static_branch_enable(&arch_timer_read_ool_enabled); |
887 | pr_info("Enabling workaround for FSL erratum A-008585\n"); | 918 | pr_info("arch_timer: Enabling workaround for %s\n", |
919 | timer_unstable_counter_workaround->id); | ||
920 | break; | ||
921 | } | ||
888 | } | 922 | } |
889 | #endif | 923 | #endif |
890 | 924 | ||