Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clocksource/drivers/arm_arch_timer: Use event stream scaling when available

With FEAT_ECV and the 1GHz counter, it is pretty likely that the
event stream divider doesn't fit in the field that holds the
divider value (we only have 4 bits to describe counter bits [15:0]

Thankfully, FEAT_ECV also provides a scaling mechanism to switch
the field to cover counter bits [23:8] instead.

Enable this on arm64 when ECV is available (32bit doesn't have
any detection infrastructure and is unlikely to be run on an
ARMv8.6 system anyway).

Signed-off-by: Marc Zyngier <maz@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20220203170502.2694422-1-maz@kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>

authored by

Marc Zyngier and committed by
Daniel Lezcano
8c4b810a 0a3a4b9d

+12 -2
+11 -2
drivers/clocksource/arm_arch_timer.c
··· 880 880 clockevents_config_and_register(clk, arch_timer_rate, 0xf, max_delta); 881 881 } 882 882 883 - static void arch_timer_evtstrm_enable(int divider) 883 + static void arch_timer_evtstrm_enable(unsigned int divider) 884 884 { 885 885 u32 cntkctl = arch_timer_get_cntkctl(); 886 886 887 + #ifdef CONFIG_ARM64 888 + /* ECV is likely to require a large divider. Use the EVNTIS flag. */ 889 + if (cpus_have_const_cap(ARM64_HAS_ECV) && divider > 15) { 890 + cntkctl |= ARCH_TIMER_EVT_INTERVAL_SCALE; 891 + divider -= 8; 892 + } 893 + #endif 894 + 895 + divider = min(divider, 15U); 887 896 cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK; 888 897 /* Set the divider and enable virtual event stream */ 889 898 cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) ··· 921 912 lsb++; 922 913 923 914 /* enable event stream */ 924 - arch_timer_evtstrm_enable(max(0, min(lsb, 15))); 915 + arch_timer_evtstrm_enable(max(0, lsb)); 925 916 } 926 917 927 918 static void arch_counter_set_user_access(void)
+1
include/clocksource/arm_arch_timer.h
··· 56 56 #define ARCH_TIMER_EVT_TRIGGER_MASK (0xF << ARCH_TIMER_EVT_TRIGGER_SHIFT) 57 57 #define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */ 58 58 #define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */ 59 + #define ARCH_TIMER_EVT_INTERVAL_SCALE (1 << 17) /* EVNTIS in the ARMv8 ARM */ 59 60 60 61 #define ARCH_TIMER_EVT_STREAM_PERIOD_US 100 61 62 #define ARCH_TIMER_EVT_STREAM_FREQ \