1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2024, James Raphael Tiovalen <jamestiotio@gmail.com>
4 */
5 #include <libcflat.h>
6 #include <devicetree.h>
7 #include <limits.h>
8 #include <asm/csr.h>
9 #include <asm/delay.h>
10 #include <asm/isa.h>
11 #include <asm/sbi.h>
12 #include <asm/setup.h>
13 #include <asm/smp.h>
14 #include <asm/timer.h>
15
timer_get_frequency(void)16 void timer_get_frequency(void)
17 {
18 const struct fdt_property *prop;
19 u32 *data;
20 int cpus, len;
21
22 assert_msg(dt_available(), "ACPI not yet supported");
23
24 const void *fdt = dt_fdt();
25
26 cpus = fdt_path_offset(fdt, "/cpus");
27 assert(cpus >= 0);
28
29 prop = fdt_get_property(fdt, cpus, "timebase-frequency", &len);
30 assert(prop != NULL && len == 4);
31
32 data = (u32 *)prop->data;
33 timebase_frequency = fdt32_to_cpu(*data);
34 }
35
timer_start(unsigned long duration_us)36 void timer_start(unsigned long duration_us)
37 {
38 uint64_t next = timer_get_cycles() + usec_to_cycles((uint64_t)duration_us);
39
40 if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
41 csr_write(CSR_STIMECMP, (unsigned long)next);
42 if (__riscv_xlen == 32)
43 csr_write(CSR_STIMECMPH, (unsigned long)(next >> 32));
44 } else if (sbi_probe(SBI_EXT_TIME)) {
45 struct sbiret ret = sbi_set_timer(next);
46 assert(ret.error == SBI_SUCCESS);
47 assert(!(next >> 32));
48 } else {
49 assert_msg(false, "No timer to start!");
50 }
51 }
52
timer_stop(void)53 void timer_stop(void)
54 {
55 if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
56 /*
57 * Subtract one from ULONG_MAX to workaround QEMU using that
58 * exact number to decide *not* to update the timer. IOW, if
59 * we used ULONG_MAX, then we wouldn't stop the timer at all,
60 * but one less is still a big number ("infinity") and it gets
61 * QEMU to do what we want.
62 */
63 csr_write(CSR_STIMECMP, ULONG_MAX - 1);
64 if (__riscv_xlen == 32)
65 csr_write(CSR_STIMECMPH, ULONG_MAX - 1);
66 } else if (sbi_probe(SBI_EXT_TIME)) {
67 struct sbiret ret = sbi_set_timer(ULONG_MAX);
68 assert(ret.error == SBI_SUCCESS);
69 } else {
70 assert_msg(false, "No timer to stop!");
71 }
72 }
73