Index: sys/arm/arm/generic_timer.c =================================================================== --- sys/arm/arm/generic_timer.c (revision 336112) +++ sys/arm/arm/generic_timer.c (working copy) @@ -145,8 +145,8 @@ get_freq(void) static uint64_t get_cntxct_a64_unstable(bool physical) { - uint64_t val -; + uint64_t val; + isb(); if (physical) { do { @@ -204,6 +204,16 @@ set_tval(uint32_t val, bool physical) return (0); } +static void +set_cval(uint64_t val, bool physical) +{ + + if (physical) + set_el0(cntp_cval, val); + else + set_el0(cntv_cval, val); +} + static int get_ctrl(bool physical) { @@ -275,6 +285,41 @@ arm_tmr_start(struct eventtimer *et, sbintime_t fi } +static int +arm_tmr_start_a64_unstable(struct eventtimer *et, sbintime_t first, + sbintime_t period __unused) +{ + struct arm_tmr_softc *sc; + uint32_t ctrl; + uint64_t cntreg, counts; + + sc = (struct arm_tmr_softc *)et->et_priv; + + if (first == 0) + return (EINVAL); + + /* + * The hardware apparently suffers internally from the same bug software + * sees when reading the count register: during a rollover the read + * returns an indeterminate value for the low 11 bits (all-zeroes or + * all-ones). A write to the tval register is handled internally by + * reading the count register, adding the tval, and storing the result + * to the compare register; the rollover glitch can result in the + * compare register being set almost 2^32 ticks too far into the future. + * The Allwinner workaround in their linux BSP is to set tval, then + * calculate what cval should be, read cval back, and see if the + * hardware calculated it wrong. If we're going to do the math + * ourselves, we might as well just set cval directly ourselves. + */ + ctrl = (get_ctrl(sc->physical) & ~GT_CTRL_INT_MASK) | GT_CTRL_ENABLE; + counts = ((uint32_t)et->et_frequency * first) >> 32; + cntreg = get_cntxct_a64_unstable(sc->physical); + set_cval(cntreg + counts, sc->physical); + set_ctrl(ctrl, sc->physical); + + return (0); +} + static void arm_tmr_disable(bool physical) { @@ -397,7 +442,9 @@ arm_tmr_attach(device_t dev) #endif int error; int i; + bool a64_unstable; + a64_unstable = false; sc = device_get_softc(dev); if (arm_tmr_sc) return (ENXIO); @@ -413,6 +460,7 @@ arm_tmr_attach(device_t dev) sc->clkfreq = clock; if (OF_hasprop(node, "allwinner,sun50i-a64-unstable-timer")) { + a64_unstable = true; sc->get_cntxct = &get_cntxct_a64_unstable; if (bootverbose) device_printf(dev, @@ -475,7 +523,7 @@ arm_tmr_attach(device_t dev) sc->et.et_frequency = sc->clkfreq; sc->et.et_min_period = (0x00000010LLU << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; - sc->et.et_start = arm_tmr_start; + sc->et.et_start = (a64_unstable) ? arm_tmr_start_a64_unstable : arm_tmr_start; sc->et.et_stop = arm_tmr_stop; sc->et.et_priv = sc; et_register(&sc->et);