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_vstimer_cb(void *opaque) 26 { 27 RISCVCPU *cpu = opaque; 28 CPURISCVState *env = &cpu->env; 29 env->vstime_irq = 1; 30 riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(1)); 31 } 32 33 static void riscv_stimer_cb(void *opaque) 34 { 35 RISCVCPU *cpu = opaque; 36 riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); 37 } 38 39 /* 40 * Called when timecmp is written to update the QEMU timer or immediately 41 * trigger timer interrupt if mtimecmp <= current timer value. 42 */ 43 void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, 44 uint64_t timecmp, uint64_t delta, 45 uint32_t timer_irq) 46 { 47 uint64_t diff, ns_diff, next; 48 CPURISCVState *env = &cpu->env; 49 RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; 50 uint32_t timebase_freq = mtimer->timebase_freq; 51 uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; 52 53 if (timecmp <= rtc_r) { 54 /* 55 * If we're setting an stimecmp value in the "past", 56 * immediately raise the timer interrupt 57 */ 58 if (timer_irq == MIP_VSTIP) { 59 env->vstime_irq = 1; 60 } 61 riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(1)); 62 return; 63 } 64 65 if (timer_irq == MIP_VSTIP) { 66 env->vstime_irq = 0; 67 } 68 /* Clear the [V]STIP bit in mip */ 69 riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); 70 71 /* otherwise, set up the future timer interrupt */ 72 diff = timecmp - rtc_r; 73 /* back to ns (note args switched in muldiv64) */ 74 ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 75 76 /* 77 * check if ns_diff overflowed and check if the addition would potentially 78 * overflow 79 */ 80 if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 81 ns_diff > INT64_MAX) { 82 next = INT64_MAX; 83 } else { 84 /* 85 * as it is very unlikely qemu_clock_get_ns will return a value 86 * greater than INT64_MAX, no additional check is needed for an 87 * unsigned integer overflow. 88 */ 89 next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 90 /* 91 * if ns_diff is INT64_MAX next may still be outside the range 92 * of a signed integer. 93 */ 94 next = MIN(next, INT64_MAX); 95 } 96 97 timer_mod(timer, next); 98 } 99 100 void riscv_timer_init(RISCVCPU *cpu) 101 { 102 CPURISCVState *env; 103 104 if (!cpu) { 105 return; 106 } 107 108 env = &cpu->env; 109 env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); 110 env->stimecmp = 0; 111 112 env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); 113 env->vstimecmp = 0; 114 } 115