diff options
Diffstat (limited to 'drivers/clocksource/arm_arch_timer.c')
| -rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 55 | 
1 files changed, 55 insertions, 0 deletions
| 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 *, | 
