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.
This commit is contained in:
Emmanuel Vadot 2018-06-14 17:18:15 +00:00
parent e4b58dfe33
commit 38d3befe9c

View file

@ -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;
}