193bcbb09SJames Raphael Tiovalen // SPDX-License-Identifier: GPL-2.0-only
293bcbb09SJames Raphael Tiovalen /*
393bcbb09SJames Raphael Tiovalen * Copyright (C) 2024, James Raphael Tiovalen <jamestiotio@gmail.com>
493bcbb09SJames Raphael Tiovalen */
593bcbb09SJames Raphael Tiovalen #include <libcflat.h>
693bcbb09SJames Raphael Tiovalen #include <devicetree.h>
7ff2fceb1SAndrew Jones #include <limits.h>
8ff2fceb1SAndrew Jones #include <asm/csr.h>
9ff2fceb1SAndrew Jones #include <asm/delay.h>
10ff2fceb1SAndrew Jones #include <asm/isa.h>
11ff2fceb1SAndrew Jones #include <asm/sbi.h>
1293bcbb09SJames Raphael Tiovalen #include <asm/setup.h>
13ff2fceb1SAndrew Jones #include <asm/smp.h>
1493bcbb09SJames Raphael Tiovalen #include <asm/timer.h>
1593bcbb09SJames Raphael Tiovalen
timer_get_frequency(void)1693bcbb09SJames Raphael Tiovalen void timer_get_frequency(void)
1793bcbb09SJames Raphael Tiovalen {
1893bcbb09SJames Raphael Tiovalen const struct fdt_property *prop;
1993bcbb09SJames Raphael Tiovalen u32 *data;
2093bcbb09SJames Raphael Tiovalen int cpus, len;
2193bcbb09SJames Raphael Tiovalen
2293bcbb09SJames Raphael Tiovalen assert_msg(dt_available(), "ACPI not yet supported");
2393bcbb09SJames Raphael Tiovalen
2493bcbb09SJames Raphael Tiovalen const void *fdt = dt_fdt();
2593bcbb09SJames Raphael Tiovalen
2693bcbb09SJames Raphael Tiovalen cpus = fdt_path_offset(fdt, "/cpus");
2793bcbb09SJames Raphael Tiovalen assert(cpus >= 0);
2893bcbb09SJames Raphael Tiovalen
2993bcbb09SJames Raphael Tiovalen prop = fdt_get_property(fdt, cpus, "timebase-frequency", &len);
3093bcbb09SJames Raphael Tiovalen assert(prop != NULL && len == 4);
3193bcbb09SJames Raphael Tiovalen
3293bcbb09SJames Raphael Tiovalen data = (u32 *)prop->data;
3393bcbb09SJames Raphael Tiovalen timebase_frequency = fdt32_to_cpu(*data);
3493bcbb09SJames Raphael Tiovalen }
35ff2fceb1SAndrew Jones
timer_start(unsigned long duration_us)36ff2fceb1SAndrew Jones void timer_start(unsigned long duration_us)
37ff2fceb1SAndrew Jones {
38ff2fceb1SAndrew Jones uint64_t next = timer_get_cycles() + usec_to_cycles((uint64_t)duration_us);
39ff2fceb1SAndrew Jones
40ff2fceb1SAndrew Jones if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
41ff2fceb1SAndrew Jones csr_write(CSR_STIMECMP, (unsigned long)next);
42ff2fceb1SAndrew Jones if (__riscv_xlen == 32)
43ff2fceb1SAndrew Jones csr_write(CSR_STIMECMPH, (unsigned long)(next >> 32));
44ff2fceb1SAndrew Jones } else if (sbi_probe(SBI_EXT_TIME)) {
45ff2fceb1SAndrew Jones struct sbiret ret = sbi_set_timer(next);
46ff2fceb1SAndrew Jones assert(ret.error == SBI_SUCCESS);
47ff2fceb1SAndrew Jones assert(!(next >> 32));
48ff2fceb1SAndrew Jones } else {
49ff2fceb1SAndrew Jones assert_msg(false, "No timer to start!");
50ff2fceb1SAndrew Jones }
51ff2fceb1SAndrew Jones }
52ff2fceb1SAndrew Jones
timer_stop(void)53ff2fceb1SAndrew Jones void timer_stop(void)
54ff2fceb1SAndrew Jones {
55ff2fceb1SAndrew Jones if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
56*4c7293ccSAndrew Jones /*
57*4c7293ccSAndrew Jones * Subtract one from ULONG_MAX to workaround QEMU using that
58*4c7293ccSAndrew Jones * exact number to decide *not* to update the timer. IOW, if
59*4c7293ccSAndrew Jones * we used ULONG_MAX, then we wouldn't stop the timer at all,
60*4c7293ccSAndrew Jones * but one less is still a big number ("infinity") and it gets
61*4c7293ccSAndrew Jones * QEMU to do what we want.
62*4c7293ccSAndrew Jones */
63*4c7293ccSAndrew Jones csr_write(CSR_STIMECMP, ULONG_MAX - 1);
64ff2fceb1SAndrew Jones if (__riscv_xlen == 32)
65*4c7293ccSAndrew Jones csr_write(CSR_STIMECMPH, ULONG_MAX - 1);
66ff2fceb1SAndrew Jones } else if (sbi_probe(SBI_EXT_TIME)) {
67ff2fceb1SAndrew Jones struct sbiret ret = sbi_set_timer(ULONG_MAX);
68ff2fceb1SAndrew Jones assert(ret.error == SBI_SUCCESS);
69ff2fceb1SAndrew Jones } else {
70ff2fceb1SAndrew Jones assert_msg(false, "No timer to stop!");
71ff2fceb1SAndrew Jones }
72ff2fceb1SAndrew Jones }
73