Commit 32cf6d0a authored by Daniel Lezcano's avatar Daniel Lezcano

Merge branch 'timers/drivers/armv8.6_arch_timer' into timers/drivers/next

The branch is a stable branch shared with ARM maintainers for the
first 13th patches of the series:

It is based on v5.14-rc3.

As stated by the changelog:

" [... ] enabling ARMv8.6 support for timer subsystem, and was prompted by a
discussion with Oliver around the fact that an ARMv8.6 implementation
must have a 1GHz counter, which leads to a number of things to break
in the timer code:

- the counter rollover can come pretty quickly as we only advertise a
  56bit counter,
- the maximum timer delta can be remarkably small, as we use the
  countdown interface which is limited to 32bit...

Thankfully, there is a way out: we can compute the minimal width of
the counter based on the guarantees that the architecture gives us,
and we can use the 64bit comparator interface instead of the countdown
to program the timer.

Finally, we start making use of the ARMv8.6 ECV features by switching
accesses to the counters to a self-synchronising register, removing
the need for an ISB. Hopefully, implementations will *not* just stick
an invisible ISB there...

A side effect of the switch to CVAL is that XGene-1 breaks. I have
added a workaround to keep it alive.

I have added Oliver's original patch[0] to the series and tweaked a
couple of things. Blame me if I broke anything.

The whole things has been tested on Juno (sysreg + MMIO timers),
XGene-1 (broken sysreg timers), FVP (FEAT_ECV, CNT*CTSS_EL0).
"

Link: https://lore.kernel.org/r/20211017124225.3018098-1-maz@kernel.orgSigned-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parents 58100c34 db26f8f2
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <asm/hwcap.h> #include <asm/hwcap.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/types.h> #include <linux/types.h>
#include <clocksource/arm_arch_timer.h> #include <clocksource/arm_arch_timer.h>
...@@ -24,29 +25,35 @@ int arch_timer_arch_init(void); ...@@ -24,29 +25,35 @@ int arch_timer_arch_init(void);
* the code. At least it does so with a recent GCC (4.6.3). * the code. At least it does so with a recent GCC (4.6.3).
*/ */
static __always_inline static __always_inline
void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u32 val) void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u64 val)
{ {
if (access == ARCH_TIMER_PHYS_ACCESS) { if (access == ARCH_TIMER_PHYS_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" ((u32)val));
isb();
break; break;
case ARCH_TIMER_REG_TVAL: case ARCH_TIMER_REG_CVAL:
asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val)); asm volatile("mcrr p15, 2, %Q0, %R0, c14" : : "r" (val));
break; break;
default:
BUILD_BUG();
} }
} else if (access == ARCH_TIMER_VIRT_ACCESS) { } else if (access == ARCH_TIMER_VIRT_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val)); asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" ((u32)val));
isb();
break; break;
case ARCH_TIMER_REG_TVAL: case ARCH_TIMER_REG_CVAL:
asm volatile("mcr p15, 0, %0, c14, c3, 0" : : "r" (val)); asm volatile("mcrr p15, 3, %Q0, %R0, c14" : : "r" (val));
break; break;
default:
BUILD_BUG();
} }
} else {
BUILD_BUG();
} }
isb();
} }
static __always_inline static __always_inline
...@@ -59,19 +66,19 @@ u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg) ...@@ -59,19 +66,19 @@ u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg)
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
break; break;
case ARCH_TIMER_REG_TVAL: default:
asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); BUILD_BUG();
break;
} }
} else if (access == ARCH_TIMER_VIRT_ACCESS) { } else if (access == ARCH_TIMER_VIRT_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val));
break; break;
case ARCH_TIMER_REG_TVAL: default:
asm volatile("mrc p15, 0, %0, c14, c3, 0" : "=r" (val)); BUILD_BUG();
break;
} }
} else {
BUILD_BUG();
} }
return val; return val;
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
({ \ ({ \
const struct arch_timer_erratum_workaround *__wa; \ const struct arch_timer_erratum_workaround *__wa; \
__wa = __this_cpu_read(timer_unstable_counter_workaround); \ __wa = __this_cpu_read(timer_unstable_counter_workaround); \
(__wa && __wa->h) ? __wa->h : arch_timer_##h; \ (__wa && __wa->h) ? ({ isb(); __wa->h;}) : arch_timer_##h; \
}) })
#else #else
...@@ -52,8 +52,6 @@ struct arch_timer_erratum_workaround { ...@@ -52,8 +52,6 @@ struct arch_timer_erratum_workaround {
enum arch_timer_erratum_match_type match_type; enum arch_timer_erratum_match_type match_type;
const void *id; const void *id;
const char *desc; const char *desc;
u32 (*read_cntp_tval_el0)(void);
u32 (*read_cntv_tval_el0)(void);
u64 (*read_cntpct_el0)(void); u64 (*read_cntpct_el0)(void);
u64 (*read_cntvct_el0)(void); u64 (*read_cntvct_el0)(void);
int (*set_next_event_phys)(unsigned long, struct clock_event_device *); int (*set_next_event_phys)(unsigned long, struct clock_event_device *);
...@@ -64,24 +62,15 @@ struct arch_timer_erratum_workaround { ...@@ -64,24 +62,15 @@ struct arch_timer_erratum_workaround {
DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *, DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *,
timer_unstable_counter_workaround); timer_unstable_counter_workaround);
/* inline sysreg accessors that make erratum_handler() work */
static inline notrace u32 arch_timer_read_cntp_tval_el0(void)
{
return read_sysreg(cntp_tval_el0);
}
static inline notrace u32 arch_timer_read_cntv_tval_el0(void)
{
return read_sysreg(cntv_tval_el0);
}
static inline notrace u64 arch_timer_read_cntpct_el0(void) static inline notrace u64 arch_timer_read_cntpct_el0(void)
{ {
isb();
return read_sysreg(cntpct_el0); return read_sysreg(cntpct_el0);
} }
static inline notrace u64 arch_timer_read_cntvct_el0(void) static inline notrace u64 arch_timer_read_cntvct_el0(void)
{ {
isb();
return read_sysreg(cntvct_el0); return read_sysreg(cntvct_el0);
} }
...@@ -102,51 +91,58 @@ static inline notrace u64 arch_timer_read_cntvct_el0(void) ...@@ -102,51 +91,58 @@ static inline notrace u64 arch_timer_read_cntvct_el0(void)
* the code. * the code.
*/ */
static __always_inline static __always_inline
void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u32 val) void arch_timer_reg_write_cp15(int access, enum arch_timer_reg reg, u64 val)
{ {
if (access == ARCH_TIMER_PHYS_ACCESS) { if (access == ARCH_TIMER_PHYS_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
write_sysreg(val, cntp_ctl_el0); write_sysreg(val, cntp_ctl_el0);
isb();
break; break;
case ARCH_TIMER_REG_TVAL: case ARCH_TIMER_REG_CVAL:
write_sysreg(val, cntp_tval_el0); write_sysreg(val, cntp_cval_el0);
break; break;
default:
BUILD_BUG();
} }
} else if (access == ARCH_TIMER_VIRT_ACCESS) { } else if (access == ARCH_TIMER_VIRT_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
write_sysreg(val, cntv_ctl_el0); write_sysreg(val, cntv_ctl_el0);
isb();
break; break;
case ARCH_TIMER_REG_TVAL: case ARCH_TIMER_REG_CVAL:
write_sysreg(val, cntv_tval_el0); write_sysreg(val, cntv_cval_el0);
break; break;
default:
BUILD_BUG();
} }
} else {
BUILD_BUG();
} }
isb();
} }
static __always_inline static __always_inline
u32 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg) u64 arch_timer_reg_read_cp15(int access, enum arch_timer_reg reg)
{ {
if (access == ARCH_TIMER_PHYS_ACCESS) { if (access == ARCH_TIMER_PHYS_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
return read_sysreg(cntp_ctl_el0); return read_sysreg(cntp_ctl_el0);
case ARCH_TIMER_REG_TVAL: default:
return arch_timer_reg_read_stable(cntp_tval_el0); BUILD_BUG();
} }
} else if (access == ARCH_TIMER_VIRT_ACCESS) { } else if (access == ARCH_TIMER_VIRT_ACCESS) {
switch (reg) { switch (reg) {
case ARCH_TIMER_REG_CTRL: case ARCH_TIMER_REG_CTRL:
return read_sysreg(cntv_ctl_el0); return read_sysreg(cntv_ctl_el0);
case ARCH_TIMER_REG_TVAL: default:
return arch_timer_reg_read_stable(cntv_tval_el0); BUILD_BUG();
} }
} }
BUG(); BUILD_BUG();
unreachable();
} }
static inline u32 arch_timer_get_cntfrq(void) static inline u32 arch_timer_get_cntfrq(void)
...@@ -169,7 +165,6 @@ static __always_inline u64 __arch_counter_get_cntpct_stable(void) ...@@ -169,7 +165,6 @@ static __always_inline u64 __arch_counter_get_cntpct_stable(void)
{ {
u64 cnt; u64 cnt;
isb();
cnt = arch_timer_reg_read_stable(cntpct_el0); cnt = arch_timer_reg_read_stable(cntpct_el0);
arch_counter_enforce_ordering(cnt); arch_counter_enforce_ordering(cnt);
return cnt; return cnt;
...@@ -189,7 +184,6 @@ static __always_inline u64 __arch_counter_get_cntvct_stable(void) ...@@ -189,7 +184,6 @@ static __always_inline u64 __arch_counter_get_cntvct_stable(void)
{ {
u64 cnt; u64 cnt;
isb();
cnt = arch_timer_reg_read_stable(cntvct_el0); cnt = arch_timer_reg_read_stable(cntvct_el0);
arch_counter_enforce_ordering(cnt); arch_counter_enforce_ordering(cnt);
return cnt; return cnt;
......
This diff is collapsed.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
enum arch_timer_reg { enum arch_timer_reg {
ARCH_TIMER_REG_CTRL, ARCH_TIMER_REG_CTRL,
ARCH_TIMER_REG_TVAL, ARCH_TIMER_REG_CVAL,
}; };
enum arch_timer_ppi_nr { enum arch_timer_ppi_nr {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment