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.  | Implementor    | Component       | Erratum ID      | Kconfig                     |  +----------------+-----------------+-----------------+-----------------------------+ +| Allwinner      | A64/R18         | UNKNOWN1        | SUN50I_ERRATUM_UNKNOWN1     | +|                |                 |                 |                             |  | ARM            | Cortex-A53      | #826319         | ARM64_ERRATUM_826319        |  | ARM            | Cortex-A53      | #827319         | ARM64_ERRATUM_827319        |  | 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  	  The workaround will be dynamically enabled when an affected  	  core is detected. +config SUN50I_ERRATUM_UNKNOWN1 +	bool "Workaround for Allwinner A64 erratum UNKNOWN1" +	default y +	depends on ARM_ARCH_TIMER && ARM64 && ARCH_SUNXI +	select ARM_ARCH_TIMER_OOL_WORKAROUND +	help +	  This option enables a workaround for instability in the timer on +	  the Allwinner A64 SoC. The workaround will only be active if the +	  allwinner,erratum-unknown1 property is found in the timer node. +  config ARM_GLOBAL_TIMER  	bool "Support for the ARM global timer" if COMPILE_TEST  	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)  }  #endif +#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 +/* + * The low bits of the counter registers are indeterminate while bit 10 or + * greater is rolling over. Since the counter value can jump both backward + * (7ff -> 000 -> 800) and forward (7ff -> fff -> 800), ignore register values + * with all ones or all zeros in the low bits. Bound the loop by the maximum + * number of CPU cycles in 3 consecutive 24 MHz counter periods. + */ +#define __sun50i_a64_read_reg(reg) ({					\ +	u64 _val;							\ +	int _retries = 150;						\ +									\ +	do {								\ +		_val = read_sysreg(reg);				\ +		_retries--;						\ +	} while (((_val + 1) & GENMASK(9, 0)) <= 1 && _retries);	\ +									\ +	WARN_ON_ONCE(!_retries);					\ +	_val;								\ +}) + +static u64 notrace sun50i_a64_read_cntpct_el0(void) +{ +	return __sun50i_a64_read_reg(cntpct_el0); +} + +static u64 notrace sun50i_a64_read_cntvct_el0(void) +{ +	return __sun50i_a64_read_reg(cntvct_el0); +} + +static u32 notrace sun50i_a64_read_cntp_tval_el0(void) +{ +	return read_sysreg(cntp_cval_el0) - sun50i_a64_read_cntpct_el0(); +} + +static u32 notrace sun50i_a64_read_cntv_tval_el0(void) +{ +	return read_sysreg(cntv_cval_el0) - sun50i_a64_read_cntvct_el0(); +} +#endif +  #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND  DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround);  EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); @@ -423,6 +465,19 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = {  		.read_cntvct_el0 = arm64_1188873_read_cntvct_el0,  	},  #endif +#ifdef CONFIG_SUN50I_ERRATUM_UNKNOWN1 +	{ +		.match_type = ate_match_dt, +		.id = "allwinner,erratum-unknown1", +		.desc = "Allwinner erratum UNKNOWN1", +		.read_cntp_tval_el0 = sun50i_a64_read_cntp_tval_el0, +		.read_cntv_tval_el0 = sun50i_a64_read_cntv_tval_el0, +		.read_cntpct_el0 = sun50i_a64_read_cntpct_el0, +		.read_cntvct_el0 = sun50i_a64_read_cntvct_el0, +		.set_next_event_phys = erratum_set_next_event_tval_phys, +		.set_next_event_virt = erratum_set_next_event_tval_virt, +	}, +#endif  };  typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *, | 
