diff options
author | Andre Przywara <andre.przywara@arm.com> | 2018-06-27 03:42:53 +0300 |
---|---|---|
committer | Jagan Teki <jagan@amarulasolutions.com> | 2018-07-03 19:30:00 +0300 |
commit | be0d217952222b2bd3ed071de9bb0c66d8cc80d9 (patch) | |
tree | 54129545162b45102f9c1a80bee4f96e51248667 /arch/arm/cpu/armv8/generic_timer.c | |
parent | 38651588d3d9a977ca457049d6357408ddad4a8b (diff) | |
download | u-boot-be0d217952222b2bd3ed071de9bb0c66d8cc80d9.tar.xz |
arm: timer: sunxi: add Allwinner timer erratum workaround
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 <andre.przywara@arm.com>
Reviewed-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Tested-by: Jagan Teki <jagan@amarulasolutions.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Tested-by: Guillaume Gardet <guillaume.gardet@free.fr>
Diffstat (limited to 'arch/arm/cpu/armv8/generic_timer.c')
-rw-r--r-- | arch/arm/cpu/armv8/generic_timer.c | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv8/generic_timer.c b/arch/arm/cpu/armv8/generic_timer.c index 3d04fde650..c1706dcec1 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). |