From be0d217952222b2bd3ed071de9bb0c66d8cc80d9 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 27 Jun 2018 01:42:53 +0100 Subject: [PATCH] arm: timer: sunxi: add Allwinner timer erratum workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Allwinner A64 SoCs suffers from an arch timer implementation erratum, where sometimes the lower 11 bits of the counter value erroneously become all 0's or all 1's [1]. This leads to sudden jumps, both forwards and backwards, with the latter one often showing weird behaviour. Port the workaround proposed for Linux to U-Boot and activate it for all A64 boards. This fixes crashes when accessing MMC devices (SD cards), caused by a recent change to actually use the counter value for timeout checks. Fixes: 5ff8e54888e4d26a352453564f7f599d29696dc9 ("sunxi: improve throughput in the sunxi_mmc driver") [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-May/576886.html Signed-off-by: Andre Przywara Reviewed-by: Philipp Tomsich Tested-by: Jagan Teki Tested-by: Andreas Färber Tested-by: Guillaume Gardet --- arch/arm/cpu/armv8/generic_timer.c | 24 ++++++++++++++++++++++++ arch/arm/mach-sunxi/Kconfig | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/arch/arm/cpu/armv8/generic_timer.c b/arch/arm/cpu/armv8/generic_timer.c index 3d04fde..c1706dc 100644 --- a/arch/arm/cpu/armv8/generic_timer.c +++ b/arch/arm/cpu/armv8/generic_timer.c @@ -46,6 +46,30 @@ unsigned long timer_read_counter(void) return cntpct; } +#elif CONFIG_SUNXI_A64_TIMER_ERRATUM +/* + * This erratum sometimes flips the lower 11 bits of the counter value + * to all 0's or all 1's, leading to jumps forwards or backwards. + * Backwards jumps might be interpreted all roll-overs and be treated as + * huge jumps forward. + * The workaround is to check whether the lower 11 bits of the counter are + * all 0 or all 1, then discard this value and read again. + * This occasionally discards valid values, but will catch all erroneous + * reads and fixes the problem reliably. Also this mostly requires only a + * single read, so does not have any significant overhead. + * The algorithm was conceived by Samuel Holland. + */ +unsigned long timer_read_counter(void) +{ + unsigned long cntpct; + + isb(); + do { + asm volatile("mrs %0, cntpct_el0" : "=r" (cntpct)); + } while (((cntpct + 1) & GENMASK(10, 0)) <= 1); + + return cntpct; +} #else /* * timer_read_counter() using the Arm Generic Timer (aka arch timer). diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index a3f7723..3624a03 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -84,6 +84,9 @@ config SUNXI_HIGH_SRAM Chips using the latter setup are supposed to select this option to adjust the addresses accordingly. +config SUNXI_A64_TIMER_ERRATUM + bool + # Note only one of these may be selected at a time! But hidden choices are # not supported by Kconfig config SUNXI_GEN_SUN4I @@ -270,6 +273,7 @@ config MACH_SUN50I select SUNXI_DRAM_DW_32BIT select FIT select SPL_LOAD_FIT + select SUNXI_A64_TIMER_ERRATUM config MACH_SUN50I_H5 bool "sun50i (Allwinner H5)"