1*43888c2fSAtish Patra /* 2*43888c2fSAtish Patra * RISC-V timer helper implementation. 3*43888c2fSAtish Patra * 4*43888c2fSAtish Patra * Copyright (c) 2022 Rivos Inc. 5*43888c2fSAtish Patra * 6*43888c2fSAtish Patra * This program is free software; you can redistribute it and/or modify it 7*43888c2fSAtish Patra * under the terms and conditions of the GNU General Public License, 8*43888c2fSAtish Patra * version 2 or later, as published by the Free Software Foundation. 9*43888c2fSAtish Patra * 10*43888c2fSAtish Patra * This program is distributed in the hope it will be useful, but WITHOUT 11*43888c2fSAtish Patra * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12*43888c2fSAtish Patra * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13*43888c2fSAtish Patra * more details. 14*43888c2fSAtish Patra * 15*43888c2fSAtish Patra * You should have received a copy of the GNU General Public License along with 16*43888c2fSAtish Patra * this program. If not, see <http://www.gnu.org/licenses/>. 17*43888c2fSAtish Patra */ 18*43888c2fSAtish Patra 19*43888c2fSAtish Patra #include "qemu/osdep.h" 20*43888c2fSAtish Patra #include "qemu/log.h" 21*43888c2fSAtish Patra #include "cpu_bits.h" 22*43888c2fSAtish Patra #include "time_helper.h" 23*43888c2fSAtish Patra #include "hw/intc/riscv_aclint.h" 24*43888c2fSAtish Patra 25*43888c2fSAtish Patra static void riscv_stimer_cb(void *opaque) 26*43888c2fSAtish Patra { 27*43888c2fSAtish Patra RISCVCPU *cpu = opaque; 28*43888c2fSAtish Patra riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); 29*43888c2fSAtish Patra } 30*43888c2fSAtish Patra 31*43888c2fSAtish Patra /* 32*43888c2fSAtish Patra * Called when timecmp is written to update the QEMU timer or immediately 33*43888c2fSAtish Patra * trigger timer interrupt if mtimecmp <= current timer value. 34*43888c2fSAtish Patra */ 35*43888c2fSAtish Patra void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, 36*43888c2fSAtish Patra uint64_t timecmp, uint64_t delta, 37*43888c2fSAtish Patra uint32_t timer_irq) 38*43888c2fSAtish Patra { 39*43888c2fSAtish Patra uint64_t diff, ns_diff, next; 40*43888c2fSAtish Patra CPURISCVState *env = &cpu->env; 41*43888c2fSAtish Patra RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; 42*43888c2fSAtish Patra uint32_t timebase_freq = mtimer->timebase_freq; 43*43888c2fSAtish Patra uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; 44*43888c2fSAtish Patra 45*43888c2fSAtish Patra if (timecmp <= rtc_r) { 46*43888c2fSAtish Patra /* 47*43888c2fSAtish Patra * If we're setting an stimecmp value in the "past", 48*43888c2fSAtish Patra * immediately raise the timer interrupt 49*43888c2fSAtish Patra */ 50*43888c2fSAtish Patra riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1)); 51*43888c2fSAtish Patra return; 52*43888c2fSAtish Patra } 53*43888c2fSAtish Patra 54*43888c2fSAtish Patra /* Clear the [V]STIP bit in mip */ 55*43888c2fSAtish Patra riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); 56*43888c2fSAtish Patra 57*43888c2fSAtish Patra /* otherwise, set up the future timer interrupt */ 58*43888c2fSAtish Patra diff = timecmp - rtc_r; 59*43888c2fSAtish Patra /* back to ns (note args switched in muldiv64) */ 60*43888c2fSAtish Patra ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 61*43888c2fSAtish Patra 62*43888c2fSAtish Patra /* 63*43888c2fSAtish Patra * check if ns_diff overflowed and check if the addition would potentially 64*43888c2fSAtish Patra * overflow 65*43888c2fSAtish Patra */ 66*43888c2fSAtish Patra if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 67*43888c2fSAtish Patra ns_diff > INT64_MAX) { 68*43888c2fSAtish Patra next = INT64_MAX; 69*43888c2fSAtish Patra } else { 70*43888c2fSAtish Patra /* 71*43888c2fSAtish Patra * as it is very unlikely qemu_clock_get_ns will return a value 72*43888c2fSAtish Patra * greater than INT64_MAX, no additional check is needed for an 73*43888c2fSAtish Patra * unsigned integer overflow. 74*43888c2fSAtish Patra */ 75*43888c2fSAtish Patra next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 76*43888c2fSAtish Patra /* 77*43888c2fSAtish Patra * if ns_diff is INT64_MAX next may still be outside the range 78*43888c2fSAtish Patra * of a signed integer. 79*43888c2fSAtish Patra */ 80*43888c2fSAtish Patra next = MIN(next, INT64_MAX); 81*43888c2fSAtish Patra } 82*43888c2fSAtish Patra 83*43888c2fSAtish Patra timer_mod(timer, next); 84*43888c2fSAtish Patra } 85*43888c2fSAtish Patra 86*43888c2fSAtish Patra void riscv_timer_init(RISCVCPU *cpu) 87*43888c2fSAtish Patra { 88*43888c2fSAtish Patra CPURISCVState *env; 89*43888c2fSAtish Patra 90*43888c2fSAtish Patra if (!cpu) { 91*43888c2fSAtish Patra return; 92*43888c2fSAtish Patra } 93*43888c2fSAtish Patra 94*43888c2fSAtish Patra env = &cpu->env; 95*43888c2fSAtish Patra env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); 96*43888c2fSAtish Patra env->stimecmp = 0; 97*43888c2fSAtish Patra 98*43888c2fSAtish Patra } 99