143888c2fSAtish Patra /* 243888c2fSAtish Patra * RISC-V timer helper implementation. 343888c2fSAtish Patra * 443888c2fSAtish Patra * Copyright (c) 2022 Rivos Inc. 543888c2fSAtish Patra * 643888c2fSAtish Patra * This program is free software; you can redistribute it and/or modify it 743888c2fSAtish Patra * under the terms and conditions of the GNU General Public License, 843888c2fSAtish Patra * version 2 or later, as published by the Free Software Foundation. 943888c2fSAtish Patra * 1043888c2fSAtish Patra * This program is distributed in the hope it will be useful, but WITHOUT 1143888c2fSAtish Patra * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1243888c2fSAtish Patra * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1343888c2fSAtish Patra * more details. 1443888c2fSAtish Patra * 1543888c2fSAtish Patra * You should have received a copy of the GNU General Public License along with 1643888c2fSAtish Patra * this program. If not, see <http://www.gnu.org/licenses/>. 1743888c2fSAtish Patra */ 1843888c2fSAtish Patra 1943888c2fSAtish Patra #include "qemu/osdep.h" 2043888c2fSAtish Patra #include "qemu/log.h" 2143888c2fSAtish Patra #include "cpu_bits.h" 2243888c2fSAtish Patra #include "time_helper.h" 2343888c2fSAtish Patra #include "hw/intc/riscv_aclint.h" 2443888c2fSAtish Patra 253ec0fe18SAtish Patra static void riscv_vstimer_cb(void *opaque) 263ec0fe18SAtish Patra { 273ec0fe18SAtish Patra RISCVCPU *cpu = opaque; 283ec0fe18SAtish Patra CPURISCVState *env = &cpu->env; 293ec0fe18SAtish Patra env->vstime_irq = 1; 30*14cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(1)); 313ec0fe18SAtish Patra } 323ec0fe18SAtish Patra 3343888c2fSAtish Patra static void riscv_stimer_cb(void *opaque) 3443888c2fSAtish Patra { 3543888c2fSAtish Patra RISCVCPU *cpu = opaque; 3643888c2fSAtish Patra riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); 3743888c2fSAtish Patra } 3843888c2fSAtish Patra 3943888c2fSAtish Patra /* 4043888c2fSAtish Patra * Called when timecmp is written to update the QEMU timer or immediately 4143888c2fSAtish Patra * trigger timer interrupt if mtimecmp <= current timer value. 4243888c2fSAtish Patra */ 4343888c2fSAtish Patra void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, 4443888c2fSAtish Patra uint64_t timecmp, uint64_t delta, 4543888c2fSAtish Patra uint32_t timer_irq) 4643888c2fSAtish Patra { 4743888c2fSAtish Patra uint64_t diff, ns_diff, next; 4843888c2fSAtish Patra CPURISCVState *env = &cpu->env; 4943888c2fSAtish Patra RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; 5043888c2fSAtish Patra uint32_t timebase_freq = mtimer->timebase_freq; 5143888c2fSAtish Patra uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; 5243888c2fSAtish Patra 5343888c2fSAtish Patra if (timecmp <= rtc_r) { 5443888c2fSAtish Patra /* 5543888c2fSAtish Patra * If we're setting an stimecmp value in the "past", 5643888c2fSAtish Patra * immediately raise the timer interrupt 5743888c2fSAtish Patra */ 583ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) { 593ec0fe18SAtish Patra env->vstime_irq = 1; 60*14cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(1)); 61*14cb78bfSAnup Patel } else { 62*14cb78bfSAnup Patel riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); 633ec0fe18SAtish Patra } 6443888c2fSAtish Patra return; 6543888c2fSAtish Patra } 6643888c2fSAtish Patra 67*14cb78bfSAnup Patel /* Clear the [VS|S]TIP bit in mip */ 683ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) { 693ec0fe18SAtish Patra env->vstime_irq = 0; 70*14cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(0)); 71*14cb78bfSAnup Patel } else { 7243888c2fSAtish Patra riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); 73*14cb78bfSAnup Patel } 7443888c2fSAtish Patra 7543888c2fSAtish Patra /* otherwise, set up the future timer interrupt */ 7643888c2fSAtish Patra diff = timecmp - rtc_r; 7743888c2fSAtish Patra /* back to ns (note args switched in muldiv64) */ 7843888c2fSAtish Patra ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 7943888c2fSAtish Patra 8043888c2fSAtish Patra /* 8143888c2fSAtish Patra * check if ns_diff overflowed and check if the addition would potentially 8243888c2fSAtish Patra * overflow 8343888c2fSAtish Patra */ 8443888c2fSAtish Patra if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 8543888c2fSAtish Patra ns_diff > INT64_MAX) { 8643888c2fSAtish Patra next = INT64_MAX; 8743888c2fSAtish Patra } else { 8843888c2fSAtish Patra /* 8943888c2fSAtish Patra * as it is very unlikely qemu_clock_get_ns will return a value 9043888c2fSAtish Patra * greater than INT64_MAX, no additional check is needed for an 9143888c2fSAtish Patra * unsigned integer overflow. 9243888c2fSAtish Patra */ 9343888c2fSAtish Patra next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 9443888c2fSAtish Patra /* 9543888c2fSAtish Patra * if ns_diff is INT64_MAX next may still be outside the range 9643888c2fSAtish Patra * of a signed integer. 9743888c2fSAtish Patra */ 9843888c2fSAtish Patra next = MIN(next, INT64_MAX); 9943888c2fSAtish Patra } 10043888c2fSAtish Patra 10143888c2fSAtish Patra timer_mod(timer, next); 10243888c2fSAtish Patra } 10343888c2fSAtish Patra 10443888c2fSAtish Patra void riscv_timer_init(RISCVCPU *cpu) 10543888c2fSAtish Patra { 10643888c2fSAtish Patra CPURISCVState *env; 10743888c2fSAtish Patra 10843888c2fSAtish Patra if (!cpu) { 10943888c2fSAtish Patra return; 11043888c2fSAtish Patra } 11143888c2fSAtish Patra 11243888c2fSAtish Patra env = &cpu->env; 11343888c2fSAtish Patra env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); 11443888c2fSAtish Patra env->stimecmp = 0; 11543888c2fSAtish Patra 1163ec0fe18SAtish Patra env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); 1173ec0fe18SAtish Patra env->vstimecmp = 0; 11843888c2fSAtish Patra } 119