diff options
-rw-r--r-- | Documentation/arm64/silicon-errata.txt | 2 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 10 | ||||
-rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 55 |
3 files changed, 67 insertions, 0 deletions
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt index 1f09d043d086..ddb8ce5333ba 100644 --- a/Documentation/arm64/silicon-errata.txt +++ b/Documentation/arm64/silicon-errata.txt | |||
@@ -44,6 +44,8 @@ stable kernels. | |||
44 | 44 | ||
45 | | Implementor | Component | Erratum ID | Kconfig | | 45 | | Implementor | Component | Erratum ID | Kconfig | |
46 | +----------------+-----------------+-----------------+-----------------------------+ | 46 | +----------------+-----------------+-----------------+-----------------------------+ |
47 | | Allwinner | A64/R18 | UNKNOWN1 | SUN50I_ERRATUM_UNKNOWN1 | | ||
48 | | | | | | | ||
47 | | ARM | Cortex-A53 | #826319 | ARM64_ERRATUM_826319 | | 49 | | ARM | Cortex-A53 | #826319 | ARM64_ERRATUM_826319 | |
48 | | ARM | Cortex-A53 | #827319 | ARM64_ERRATUM_827319 | | 50 | | ARM | Cortex-A53 | #827319 | ARM64_ERRATUM_827319 | |
49 | | ARM | Cortex-A53 | #824069 | ARM64_ERRATUM_824069 | | 51 | | ARM | Cortex-A53 | #824069 | ARM64_ERRATUM_824069 | |
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index a9e26f6a81a1..8dfd3bc448d0 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -360,6 +360,16 @@ config ARM64_ERRATUM_858921 | |||
360 | The workaround will be dynamically enabled when an affected | 360 | The workaround will be dynamically enabled when an affected |
361 | core is detected. | 361 | core is detected. |
362 | 362 | ||
363 | config SUN50I_ERRATUM_UNKNOWN1 | ||
364 | bool "Workaround for Allwinner A64 erratum UNKNOWN1" | ||
365 | default y | ||
366 | depends on ARM_ARCH_TIMER && ARM64 && ARCH_SUNXI | ||
367 | select ARM_ARCH_TIMER_OOL_WORKAROUND | ||
368 | help | ||
369 | This option enables a workaround for instability in the timer on | ||
370 | the Allwinner A64 SoC. The workaround will only be active if the | ||
371 | allwinner,erratum-unknown1 property is found in the timer node. | ||
372 | |||
363 | config ARM_GLOBAL_TIMER | 373 | config ARM_GLOBAL_TIMER |
364 | bool "Support for the ARM global timer" if COMPILE_TEST | 374 | bool "Support for the ARM global timer" if COMPILE_TEST |
365 | select TIMER_OF if OF | 375 | select TIMER_OF if OF |
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 9a7d4dc00b6e..a8b20b65bd4b 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c | |||
@@ -326,6 +326,48 @@ static u64 notrace arm64_1188873_read_cntvct_el0(void) | |||
326 | } | 326 | } |
327 | #endif | 327 | #endif |
328 | 328 | ||
329 | #ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 | ||
330 | /* | ||
331 | * The low bits of the counter registers are indeterminate while bit 10 or | ||
332 | * greater is rolling over. Since the counter value can jump both backward | ||
333 | * (7ff -> 000 -> 800) and forward (7ff -> fff -> 800), ignore register values | ||
334 | * with all ones or all zeros in the low bits. Bound the loop by the maximum | ||
335 | * number of CPU cycles in 3 consecutive 24 MHz counter periods. | ||
336 | */ | ||
337 | #define __sun50i_a64_read_reg(reg) ({ \ | ||
338 | u64 _val; \ | ||
339 | int _retries = 150; \ | ||
340 | \ | ||
341 | do { \ | ||
342 | _val = read_sysreg(reg); \ | ||
343 | _retries--; \ | ||
344 | } while (((_val + 1) & GENMASK(9, 0)) <= 1 && _retries); \ | ||
345 | \ | ||
346 | WARN_ON_ONCE(!_retries); \ | ||
347 | _val; \ | ||
348 | }) | ||
349 | |||
350 | static u64 notrace sun50i_a64_read_cntpct_el0(void) | ||
351 | { | ||
352 | return __sun50i_a64_read_reg(cntpct_el0); | ||
353 | } | ||
354 | |||
355 | static u64 notrace sun50i_a64_read_cntvct_el0(void) | ||
356 | { | ||
357 | return __sun50i_a64_read_reg(cntvct_el0); | ||
358 | } | ||
359 | |||
360 | static u32 notrace sun50i_a64_read_cntp_tval_el0(void) | ||
361 | { | ||
362 | return read_sysreg(cntp_cval_el0) - sun50i_a64_read_cntpct_el0(); | ||
363 | } | ||
364 | |||
365 | static u32 notrace sun50i_a64_read_cntv_tval_el0(void) | ||
366 | { | ||
367 | return read_sysreg(cntv_cval_el0) - sun50i_a64_read_cntvct_el0(); | ||
368 | } | ||
369 | #endif | ||
370 | |||
329 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND | 371 | #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND |
330 | DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround); | 372 | DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround); |
331 | EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); | 373 | EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); |
@@ -423,6 +465,19 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = { | |||
423 | .read_cntvct_el0 = arm64_1188873_read_cntvct_el0, | 465 | .read_cntvct_el0 = arm64_1188873_read_cntvct_el0, |
424 | }, | 466 | }, |
425 | #endif | 467 | #endif |
468 | #ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 | ||
469 | { | ||
470 | .match_type = ate_match_dt, | ||
471 | .id = "allwinner,erratum-unknown1", | ||
472 | .desc = "Allwinner erratum UNKNOWN1", | ||
473 | .read_cntp_tval_el0 = sun50i_a64_read_cntp_tval_el0, | ||
474 | .read_cntv_tval_el0 = sun50i_a64_read_cntv_tval_el0, | ||
475 | .read_cntpct_el0 = sun50i_a64_read_cntpct_el0, | ||
476 | .read_cntvct_el0 = sun50i_a64_read_cntvct_el0, | ||
477 | .set_next_event_phys = erratum_set_next_event_tval_phys, | ||
478 | .set_next_event_virt = erratum_set_next_event_tval_virt, | ||
479 | }, | ||
480 | #endif | ||
426 | }; | 481 | }; |
427 | 482 | ||
428 | typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *, | 483 | typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *, |