Implement usertc corresponding to the Allwinner A64 agtimer(4) errata.
authorkettenis <kettenis@openbsd.org>
Sun, 5 Feb 2023 13:37:51 +0000 (13:37 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 5 Feb 2023 13:37:51 +0000 (13:37 +0000)
ok cheloha@, semarie@

lib/libc/arch/aarch64/gen/usertc.c

index d1dd51c..b7efb79 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: usertc.c,v 1.2 2020/07/15 22:58:33 kettenis Exp $     */
+/*     $OpenBSD: usertc.c,v 1.3 2023/02/05 13:37:51 kettenis Exp $     */
 /*
  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
  *
 #include <sys/types.h>
 #include <sys/timetc.h>
 
+static inline uint64_t
+agtimer_readcnt64_sun50i(void)
+{
+       uint64_t val;
+       int retry;
+
+       __asm volatile("isb" ::: "memory");
+       for (retry = 0; retry < 150; retry++) {
+               __asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val));
+
+               if (((val + 1) & 0x1ff) > 1)
+                       break;
+       }
+
+       return val;
+}
+
 static inline u_int
-agtimer_get_timecount(struct timecounter *tc)
+agtimer_get_timecount_default(struct timecounter *tc)
 {
        uint64_t val;
 
@@ -32,12 +49,21 @@ agtimer_get_timecount(struct timecounter *tc)
        return (val & 0xffffffff);
 }
 
+static inline u_int
+agtimer_get_timecount_sun50i(struct timecounter *tc)
+{
+       return agtimer_readcnt64_sun50i();
+}
+
 static int
 tc_get_timecount(struct timekeep *tk, u_int *tc)
 {
        switch (tk->tk_user) {
        case TC_AGTIMER:
-               *tc = agtimer_get_timecount(NULL);
+               *tc = agtimer_get_timecount_default(NULL);
+               return 0;
+       case TC_AGTIMER_SUN50I:
+               *tc = agtimer_get_timecount_sun50i(NULL);
                return 0;
        }