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:
parent
5444ea6a7f
commit
16d10ef29f
|
@ -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; \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue