xref: /qemu/target/riscv/time_helper.c (revision 43888c2f1823212b1064a6a94d65d8acaf954478)
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