1405df5b5SVincent Donnefort // SPDX-License-Identifier: GPL-2.0
2405df5b5SVincent Donnefort /*
3405df5b5SVincent Donnefort * Copyright (C) 2025 Google LLC
4405df5b5SVincent Donnefort * Author: Vincent Donnefort <vdonnefort@google.com>
5405df5b5SVincent Donnefort */
6405df5b5SVincent Donnefort
7405df5b5SVincent Donnefort #include <nvhe/clock.h>
8405df5b5SVincent Donnefort
9405df5b5SVincent Donnefort #include <asm/arch_timer.h>
10405df5b5SVincent Donnefort #include <asm/div64.h>
11405df5b5SVincent Donnefort
12405df5b5SVincent Donnefort static struct clock_data {
13405df5b5SVincent Donnefort struct {
14405df5b5SVincent Donnefort u32 mult;
15405df5b5SVincent Donnefort u32 shift;
16405df5b5SVincent Donnefort u64 epoch_ns;
17405df5b5SVincent Donnefort u64 epoch_cyc;
18405df5b5SVincent Donnefort u64 cyc_overflow64;
19405df5b5SVincent Donnefort } data[2];
20405df5b5SVincent Donnefort u64 cur;
21405df5b5SVincent Donnefort } trace_clock_data;
22405df5b5SVincent Donnefort
__clock_mult_uint128(u64 cyc,u32 mult,u32 shift)23405df5b5SVincent Donnefort static u64 __clock_mult_uint128(u64 cyc, u32 mult, u32 shift)
24405df5b5SVincent Donnefort {
25405df5b5SVincent Donnefort __uint128_t ns = (__uint128_t)cyc * mult;
26405df5b5SVincent Donnefort
27405df5b5SVincent Donnefort ns >>= shift;
28405df5b5SVincent Donnefort
29405df5b5SVincent Donnefort return (u64)ns;
30405df5b5SVincent Donnefort }
31405df5b5SVincent Donnefort
32405df5b5SVincent Donnefort /* Does not guarantee no reader on the modified bank. */
trace_clock_update(u32 mult,u32 shift,u64 epoch_ns,u64 epoch_cyc)33405df5b5SVincent Donnefort void trace_clock_update(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc)
34405df5b5SVincent Donnefort {
35405df5b5SVincent Donnefort struct clock_data *clock = &trace_clock_data;
36405df5b5SVincent Donnefort u64 bank = clock->cur ^ 1;
37405df5b5SVincent Donnefort
38*9a624ea3SMostafa Saleh if (!mult || shift >= 64)
39*9a624ea3SMostafa Saleh return;
40*9a624ea3SMostafa Saleh
41405df5b5SVincent Donnefort clock->data[bank].mult = mult;
42405df5b5SVincent Donnefort clock->data[bank].shift = shift;
43405df5b5SVincent Donnefort clock->data[bank].epoch_ns = epoch_ns;
44405df5b5SVincent Donnefort clock->data[bank].epoch_cyc = epoch_cyc;
45405df5b5SVincent Donnefort clock->data[bank].cyc_overflow64 = ULONG_MAX / mult;
46405df5b5SVincent Donnefort
47405df5b5SVincent Donnefort smp_store_release(&clock->cur, bank);
48405df5b5SVincent Donnefort }
49405df5b5SVincent Donnefort
50405df5b5SVincent Donnefort /* Use untrusted host data */
trace_clock(void)51405df5b5SVincent Donnefort u64 trace_clock(void)
52405df5b5SVincent Donnefort {
53405df5b5SVincent Donnefort struct clock_data *clock = &trace_clock_data;
54405df5b5SVincent Donnefort u64 bank = smp_load_acquire(&clock->cur);
55405df5b5SVincent Donnefort u64 cyc, ns;
56405df5b5SVincent Donnefort
57405df5b5SVincent Donnefort cyc = __arch_counter_get_cntvct() - clock->data[bank].epoch_cyc;
58405df5b5SVincent Donnefort
59405df5b5SVincent Donnefort if (likely(cyc < clock->data[bank].cyc_overflow64)) {
60405df5b5SVincent Donnefort ns = cyc * clock->data[bank].mult;
61405df5b5SVincent Donnefort ns >>= clock->data[bank].shift;
62405df5b5SVincent Donnefort } else {
63405df5b5SVincent Donnefort ns = __clock_mult_uint128(cyc, clock->data[bank].mult,
64405df5b5SVincent Donnefort clock->data[bank].shift);
65405df5b5SVincent Donnefort }
66405df5b5SVincent Donnefort
67405df5b5SVincent Donnefort return (u64)ns + clock->data[bank].epoch_ns;
68405df5b5SVincent Donnefort }
69