// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2024, James Raphael Tiovalen */ #include #include #include #include #include #include #include #include #include #include void timer_get_frequency(void) { const struct fdt_property *prop; u32 *data; int cpus, len; assert_msg(dt_available(), "ACPI not yet supported"); const void *fdt = dt_fdt(); cpus = fdt_path_offset(fdt, "/cpus"); assert(cpus >= 0); prop = fdt_get_property(fdt, cpus, "timebase-frequency", &len); assert(prop != NULL && len == 4); data = (u32 *)prop->data; timebase_frequency = fdt32_to_cpu(*data); } void timer_start(unsigned long duration_us) { uint64_t next = timer_get_cycles() + usec_to_cycles((uint64_t)duration_us); if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) { csr_write(CSR_STIMECMP, (unsigned long)next); if (__riscv_xlen == 32) csr_write(CSR_STIMECMPH, (unsigned long)(next >> 32)); } else if (sbi_probe(SBI_EXT_TIME)) { struct sbiret ret = sbi_set_timer(next); assert(ret.error == SBI_SUCCESS); assert(!(next >> 32)); } else { assert_msg(false, "No timer to start!"); } } void timer_stop(void) { if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) { /* * Subtract one from ULONG_MAX to workaround QEMU using that * exact number to decide *not* to update the timer. IOW, if * we used ULONG_MAX, then we wouldn't stop the timer at all, * but one less is still a big number ("infinity") and it gets * QEMU to do what we want. */ csr_write(CSR_STIMECMP, ULONG_MAX - 1); if (__riscv_xlen == 32) csr_write(CSR_STIMECMPH, ULONG_MAX - 1); } else if (sbi_probe(SBI_EXT_TIME)) { struct sbiret ret = sbi_set_timer(ULONG_MAX); assert(ret.error == SBI_SUCCESS); } else { assert_msg(false, "No timer to stop!"); } }