From 38d3befe9cd0ae763f3e2ffd23e6a48518689074 Mon Sep 17 00:00:00 2001 From: Emmanuel Vadot Date: Thu, 14 Jun 2018 17:18:15 +0000 Subject: [PATCH] arm timer: Add workaround for Allwinner A64 timer The timer present in allwinner A64 SoC is unstable, value can jump backward or forward. It was found that when bit 11 and upper roll over the low bits can sometimes being read as all as 1 or all as 0. Simply ignore the values for those cases. --- sys/arm/arm/generic_timer.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c index b13080d57bb..2739c5b6da8 100644 --- a/sys/arm/arm/generic_timer.c +++ b/sys/arm/arm/generic_timer.c @@ -92,6 +92,7 @@ __FBSDID("$FreeBSD$"); struct arm_tmr_softc { struct resource *res[4]; void *ihl[4]; + uint64_t (*get_cntxct)(bool); uint32_t clkfreq; struct eventtimer et; bool physical; @@ -141,6 +142,28 @@ get_freq(void) return (get_el0(cntfrq)); } +static uint64_t +get_cntxct_a64_unstable(bool physical) +{ + uint64_t val +; + isb(); + if (physical) { + do { + val = get_el0(cntpct); + } + while (((val + 1) & 0x7FF) <= 1); + } + else { + do { + val = get_el0(cntvct); + } + while (((val + 1) & 0x7FF) <= 1); + } + + return (val); +} + static uint64_t get_cntxct(bool physical) { @@ -226,7 +249,7 @@ static unsigned arm_tmr_get_timecount(struct timecounter *tc) { - return (get_cntxct(arm_tmr_sc->physical)); + return (arm_tmr_sc->get_cntxct(arm_tmr_sc->physical)); } static int @@ -379,6 +402,7 @@ arm_tmr_attach(device_t dev) if (arm_tmr_sc) return (ENXIO); + sc->get_cntxct = &get_cntxct_a64_unstable; #ifdef FDT /* Get the base clock frequency */ node = ofw_bus_get_node(dev); @@ -387,6 +411,13 @@ arm_tmr_attach(device_t dev) sizeof(clock)); if (error > 0) sc->clkfreq = clock; + + if (OF_hasprop(node, "allwinner,sun50i-a64-unstable-timer")) { + sc->get_cntxct = &get_cntxct_a64_unstable; + if (bootverbose) + device_printf(dev, + "Enabling allwinner unstable timer workaround\n"); + } } #endif @@ -518,10 +549,10 @@ arm_tmr_do_delay(int usec, void *arg) else counts = usec * counts_per_usec; - first = get_cntxct(sc->physical); + first = sc->get_cntxct(sc->physical); while (counts > 0) { - last = get_cntxct(sc->physical); + last = sc->get_cntxct(sc->physical); counts -= (int32_t)(last - first); first = last; }