Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16d10ef2 authored by Ding Tianhong's avatar Ding Tianhong Committed by Daniel Lezcano
Browse files

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: default avatarDing Tianhong <dingtianhong@huawei.com>
[Mark: split patch, correct Kconfig, reword commit message]
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parent 5444ea6a
Loading
Loading
Loading
Loading
+13 −25
Original line number Diff line number Diff line
@@ -29,41 +29,29 @@

#include <clocksource/arm_arch_timer.h>

#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND)
extern struct static_key_false arch_timer_read_ool_enabled;
#define needs_fsl_a008585_workaround() \
#define needs_unstable_timer_counter_workaround() \
	static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
#define needs_fsl_a008585_workaround()  false
#define needs_unstable_timer_counter_workaround()  false
#endif

u32 __fsl_a008585_read_cntp_tval_el0(void);
u32 __fsl_a008585_read_cntv_tval_el0(void);
u64 __fsl_a008585_read_cntvct_el0(void);

/*
 * The number of retries is an arbitrary value well beyond the highest number
 * of iterations the loop has been observed to take.
 */
#define __fsl_a008585_read_reg(reg) ({			\
	u64 _old, _new;					\
	int _retries = 200;				\
							\
	do {						\
		_old = read_sysreg(reg);		\
		_new = read_sysreg(reg);		\
		_retries--;				\
	} while (unlikely(_old != _new) && _retries);	\
							\
	WARN_ON_ONCE(!_retries);			\
	_new;						\
})
struct arch_timer_erratum_workaround {
	const char *id;		/* Indicate the Erratum ID */
	u32 (*read_cntp_tval_el0)(void);
	u32 (*read_cntv_tval_el0)(void);
	u64 (*read_cntvct_el0)(void);
};

extern const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;

#define arch_timer_reg_read_stable(reg) 		\
({							\
	u64 _val;					\
	if (needs_fsl_a008585_workaround())		\
		_val = __fsl_a008585_read_##reg();	\
	if (needs_unstable_timer_counter_workaround())		\
		_val = timer_unstable_counter_workaround->read_##reg();\
	else						\
		_val = read_sysreg(reg);		\
	_val;						\
+4 −0
Original line number Diff line number Diff line
@@ -342,10 +342,14 @@ config ARM_ARCH_TIMER_EVTSTREAM
	  This must be disabled for hardware validation purposes to detect any
	  hardware anomalies of missing events.

config ARM_ARCH_TIMER_OOL_WORKAROUND
	bool

config FSL_ERRATUM_A008585
	bool "Workaround for Freescale/NXP Erratum A-008585"
	default y
	depends on ARM_ARCH_TIMER && ARM64
	select ARM_ARCH_TIMER_OOL_WORKAROUND
	help
	  This option enables a workaround for Freescale/NXP Erratum
	  A-008585 ("ARM generic timer may contain an erroneous
+63 −29
Original line number Diff line number Diff line
@@ -96,27 +96,58 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
 */

#ifdef CONFIG_FSL_ERRATUM_A008585
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);

static int fsl_a008585_enable = -1;

u32 __fsl_a008585_read_cntp_tval_el0(void)
/*
 * The number of retries is an arbitrary value well beyond the highest number
 * of iterations the loop has been observed to take.
 */
#define __fsl_a008585_read_reg(reg) ({			\
	u64 _old, _new;					\
	int _retries = 200;				\
							\
	do {						\
		_old = read_sysreg(reg);		\
		_new = read_sysreg(reg);		\
		_retries--;				\
	} while (unlikely(_old != _new) && _retries);	\
							\
	WARN_ON_ONCE(!_retries);			\
	_new;						\
})

static u32 notrace fsl_a008585_read_cntp_tval_el0(void)
{
	return __fsl_a008585_read_reg(cntp_tval_el0);
}

u32 __fsl_a008585_read_cntv_tval_el0(void)
static u32 notrace fsl_a008585_read_cntv_tval_el0(void)
{
	return __fsl_a008585_read_reg(cntv_tval_el0);
}

u64 __fsl_a008585_read_cntvct_el0(void)
static u64 notrace fsl_a008585_read_cntvct_el0(void)
{
	return __fsl_a008585_read_reg(cntvct_el0);
}
EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
#endif /* CONFIG_FSL_ERRATUM_A008585 */
#endif

#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);

DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);

static const struct arch_timer_erratum_workaround ool_workarounds[] = {
#ifdef CONFIG_FSL_ERRATUM_A008585
	{
		.id = "fsl,erratum-a008585",
		.read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
		.read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
		.read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
	},
#endif
};
#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */

static __always_inline
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,
	arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}

#ifdef CONFIG_FSL_ERRATUM_A008585
static __always_inline void fsl_a008585_set_next_event(const int access,
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
static __always_inline void erratum_set_next_event_generic(const int access,
		unsigned long evt, struct clock_event_device *clk)
{
	unsigned long ctrl;
@@ -286,20 +317,20 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
	arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}

static int fsl_a008585_set_next_event_virt(unsigned long evt,
static int erratum_set_next_event_virt(unsigned long evt,
					   struct clock_event_device *clk)
{
	fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
	erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
	return 0;
}

static int fsl_a008585_set_next_event_phys(unsigned long evt,
static int erratum_set_next_event_phys(unsigned long evt,
					   struct clock_event_device *clk)
{
	fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
	erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
	return 0;
}
#endif /* CONFIG_FSL_ERRATUM_A008585 */
#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */

static int arch_timer_set_next_event_virt(unsigned long evt,
					  struct clock_event_device *clk)
@@ -329,16 +360,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
	return 0;
}

static void fsl_a008585_set_sne(struct clock_event_device *clk)
static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
#ifdef CONFIG_FSL_ERRATUM_A008585
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
	if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
		return;

	if (arch_timer_uses_ppi == VIRT_PPI)
		clk->set_next_event = fsl_a008585_set_next_event_virt;
		clk->set_next_event = erratum_set_next_event_virt;
	else
		clk->set_next_event = fsl_a008585_set_next_event_phys;
		clk->set_next_event = erratum_set_next_event_phys;
#endif
}

@@ -371,7 +402,7 @@ static void __arch_timer_setup(unsigned type,
			BUG();
		}

		fsl_a008585_set_sne(clk);
		erratum_workaround_set_sne(clk);
	} else {
		clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
		clk->name = "arch_mem_timer";
@@ -591,7 +622,7 @@ static void __init arch_counter_register(unsigned type)

		clocksource_counter.archdata.vdso_direct = true;

#ifdef CONFIG_FSL_ERRATUM_A008585
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
		/*
		 * Don't use the vdso fastpath if errata require using
		 * the out-of-line counter accessor.
@@ -879,12 +910,15 @@ static int __init arch_timer_of_init(struct device_node *np)

	arch_timer_c3stop = !of_property_read_bool(np, "always-on");

#ifdef CONFIG_FSL_ERRATUM_A008585
	if (fsl_a008585_enable < 0)
		fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
	if (fsl_a008585_enable) {
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
	for (i = 0; i < ARRAY_SIZE(ool_workarounds); i++) {
		if (of_property_read_bool(np, ool_workarounds[i].id)) {
			timer_unstable_counter_workaround = &ool_workarounds[i];
			static_branch_enable(&arch_timer_read_ool_enabled);
		pr_info("Enabling workaround for FSL erratum A-008585\n");
			pr_info("arch_timer: Enabling workaround for %s\n",
				timer_unstable_counter_workaround->id);
			break;
		}
	}
#endif