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