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>
This commit is contained in:
Ding Tianhong 2017-02-06 16:47:41 +00:00 committed by Daniel Lezcano
parent 5444ea6a7f
commit 16d10ef29f
3 changed files with 79 additions and 53 deletions

View File

@ -29,41 +29,29 @@
#include <clocksource/arm_arch_timer.h> #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; 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) static_branch_unlikely(&arch_timer_read_ool_enabled)
#else #else
#define needs_fsl_a008585_workaround() false #define needs_unstable_timer_counter_workaround() false
#endif #endif
u32 __fsl_a008585_read_cntp_tval_el0(void);
u32 __fsl_a008585_read_cntv_tval_el0(void);
u64 __fsl_a008585_read_cntvct_el0(void);
/* struct arch_timer_erratum_workaround {
* The number of retries is an arbitrary value well beyond the highest number const char *id; /* Indicate the Erratum ID */
* of iterations the loop has been observed to take. u32 (*read_cntp_tval_el0)(void);
*/ u32 (*read_cntv_tval_el0)(void);
#define __fsl_a008585_read_reg(reg) ({ \ u64 (*read_cntvct_el0)(void);
u64 _old, _new; \ };
int _retries = 200; \
\ extern const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
do { \
_old = read_sysreg(reg); \
_new = read_sysreg(reg); \
_retries--; \
} while (unlikely(_old != _new) && _retries); \
\
WARN_ON_ONCE(!_retries); \
_new; \
})
#define arch_timer_reg_read_stable(reg) \ #define arch_timer_reg_read_stable(reg) \
({ \ ({ \
u64 _val; \ u64 _val; \
if (needs_fsl_a008585_workaround()) \ if (needs_unstable_timer_counter_workaround()) \
_val = __fsl_a008585_read_##reg(); \ _val = timer_unstable_counter_workaround->read_##reg();\
else \ else \
_val = read_sysreg(reg); \ _val = read_sysreg(reg); \
_val; \ _val; \

View File

@ -342,10 +342,14 @@ config ARM_ARCH_TIMER_EVTSTREAM
This must be disabled for hardware validation purposes to detect any This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events. hardware anomalies of missing events.
config ARM_ARCH_TIMER_OOL_WORKAROUND
bool
config FSL_ERRATUM_A008585 config FSL_ERRATUM_A008585
bool "Workaround for Freescale/NXP Erratum A-008585" bool "Workaround for Freescale/NXP Erratum A-008585"
default y default y
depends on ARM_ARCH_TIMER && ARM64 depends on ARM_ARCH_TIMER && ARM64
select ARM_ARCH_TIMER_OOL_WORKAROUND
help help
This option enables a workaround for Freescale/NXP Erratum This option enables a workaround for Freescale/NXP Erratum
A-008585 ("ARM generic timer may contain an erroneous A-008585 ("ARM generic timer may contain an erroneous

View File

@ -96,27 +96,58 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
*/ */
#ifdef CONFIG_FSL_ERRATUM_A008585 #ifdef CONFIG_FSL_ERRATUM_A008585
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled); /*
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled); * 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 int fsl_a008585_enable = -1; static u32 notrace fsl_a008585_read_cntp_tval_el0(void)
u32 __fsl_a008585_read_cntp_tval_el0(void)
{ {
return __fsl_a008585_read_reg(cntp_tval_el0); 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); 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); return __fsl_a008585_read_reg(cntvct_el0);
} }
EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0); #endif
#endif /* CONFIG_FSL_ERRATUM_A008585 */
#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 static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val, 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); arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
} }
#ifdef CONFIG_FSL_ERRATUM_A008585 #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
static __always_inline void fsl_a008585_set_next_event(const int access, static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk) unsigned long evt, struct clock_event_device *clk)
{ {
unsigned long ctrl; 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); 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) 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; 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) 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; 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, static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk) struct clock_event_device *clk)
@ -329,16 +360,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0; 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)) if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return; return;
if (arch_timer_uses_ppi == VIRT_PPI) 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 else
clk->set_next_event = fsl_a008585_set_next_event_phys; clk->set_next_event = erratum_set_next_event_phys;
#endif #endif
} }
@ -371,7 +402,7 @@ static void __arch_timer_setup(unsigned type,
BUG(); BUG();
} }
fsl_a008585_set_sne(clk); erratum_workaround_set_sne(clk);
} else { } else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ; clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer"; clk->name = "arch_mem_timer";
@ -591,7 +622,7 @@ static void __init arch_counter_register(unsigned type)
clocksource_counter.archdata.vdso_direct = true; 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 * Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor. * 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"); arch_timer_c3stop = !of_property_read_bool(np, "always-on");
#ifdef CONFIG_FSL_ERRATUM_A008585 #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
if (fsl_a008585_enable < 0) for (i = 0; i < ARRAY_SIZE(ool_workarounds); i++) {
fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585"); if (of_property_read_bool(np, ool_workarounds[i].id)) {
if (fsl_a008585_enable) { timer_unstable_counter_workaround = &ool_workarounds[i];
static_branch_enable(&arch_timer_read_ool_enabled); 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 #endif