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
riscv_vstimer_cb(void * opaque)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;
30bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1));
313ec0fe18SAtish Patra }
323ec0fe18SAtish Patra
riscv_stimer_cb(void * opaque)3343888c2fSAtish Patra static void riscv_stimer_cb(void *opaque)
3443888c2fSAtish Patra {
3543888c2fSAtish Patra RISCVCPU *cpu = opaque;
36bbb9fc25SWeiwei Li riscv_cpu_update_mip(&cpu->env, 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 */
riscv_timer_write_timecmp(CPURISCVState * env,QEMUTimer * timer,uint64_t timecmp,uint64_t delta,uint32_t timer_irq)43bbb9fc25SWeiwei Li void riscv_timer_write_timecmp(CPURISCVState *env, 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 RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg;
4943888c2fSAtish Patra uint32_t timebase_freq = mtimer->timebase_freq;
5043888c2fSAtish Patra uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta;
5143888c2fSAtish Patra
5243888c2fSAtish Patra if (timecmp <= rtc_r) {
5343888c2fSAtish Patra /*
5443888c2fSAtish Patra * If we're setting an stimecmp value in the "past",
5543888c2fSAtish Patra * immediately raise the timer interrupt
5643888c2fSAtish Patra */
573ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) {
583ec0fe18SAtish Patra env->vstime_irq = 1;
59bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1));
6014cb78bfSAnup Patel } else {
61bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, MIP_STIP, BOOL_TO_MASK(1));
623ec0fe18SAtish Patra }
6343888c2fSAtish Patra return;
6443888c2fSAtish Patra }
6543888c2fSAtish Patra
6614cb78bfSAnup Patel /* Clear the [VS|S]TIP bit in mip */
673ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) {
683ec0fe18SAtish Patra env->vstime_irq = 0;
69bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0));
7014cb78bfSAnup Patel } else {
71bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0));
7214cb78bfSAnup Patel }
7343888c2fSAtish Patra
74ae0edf21SAnup Patel /*
75ae0edf21SAnup Patel * Sstc specification says the following about timer interrupt:
76ae0edf21SAnup Patel * "A supervisor timer interrupt becomes pending - as reflected in
77ae0edf21SAnup Patel * the STIP bit in the mip and sip registers - whenever time contains
78ae0edf21SAnup Patel * a value greater than or equal to stimecmp, treating the values
79ae0edf21SAnup Patel * as unsigned integers. Writes to stimecmp are guaranteed to be
80ae0edf21SAnup Patel * reflected in STIP eventually, but not necessarily immediately.
81ae0edf21SAnup Patel * The interrupt remains posted until stimecmp becomes greater
82ae0edf21SAnup Patel * than time - typically as a result of writing stimecmp."
83ae0edf21SAnup Patel *
84ae0edf21SAnup Patel * When timecmp = UINT64_MAX, the time CSR will eventually reach
85ae0edf21SAnup Patel * timecmp value but on next timer tick the time CSR will wrap-around
86ae0edf21SAnup Patel * and become zero which is less than UINT64_MAX. Now, the timer
87ae0edf21SAnup Patel * interrupt behaves like a level triggered interrupt so it will
88ae0edf21SAnup Patel * become 1 when time = timecmp = UINT64_MAX and next timer tick
89ae0edf21SAnup Patel * it will become 0 again because time = 0 < timecmp = UINT64_MAX.
90ae0edf21SAnup Patel *
91ae0edf21SAnup Patel * Based on above, we don't re-start the QEMU timer when timecmp
92ae0edf21SAnup Patel * equals UINT64_MAX.
93ae0edf21SAnup Patel */
94ae0edf21SAnup Patel if (timecmp == UINT64_MAX) {
95*2d2e3bdcSAndrew Jones timer_del(timer);
96ae0edf21SAnup Patel return;
97ae0edf21SAnup Patel }
98ae0edf21SAnup Patel
9943888c2fSAtish Patra /* otherwise, set up the future timer interrupt */
10043888c2fSAtish Patra diff = timecmp - rtc_r;
10143888c2fSAtish Patra /* back to ns (note args switched in muldiv64) */
10243888c2fSAtish Patra ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
10343888c2fSAtish Patra
10443888c2fSAtish Patra /*
10543888c2fSAtish Patra * check if ns_diff overflowed and check if the addition would potentially
10643888c2fSAtish Patra * overflow
10743888c2fSAtish Patra */
10843888c2fSAtish Patra if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
10943888c2fSAtish Patra ns_diff > INT64_MAX) {
11043888c2fSAtish Patra next = INT64_MAX;
11143888c2fSAtish Patra } else {
11243888c2fSAtish Patra /*
11343888c2fSAtish Patra * as it is very unlikely qemu_clock_get_ns will return a value
11443888c2fSAtish Patra * greater than INT64_MAX, no additional check is needed for an
11543888c2fSAtish Patra * unsigned integer overflow.
11643888c2fSAtish Patra */
11743888c2fSAtish Patra next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
11843888c2fSAtish Patra /*
11943888c2fSAtish Patra * if ns_diff is INT64_MAX next may still be outside the range
12043888c2fSAtish Patra * of a signed integer.
12143888c2fSAtish Patra */
12243888c2fSAtish Patra next = MIN(next, INT64_MAX);
12343888c2fSAtish Patra }
12443888c2fSAtish Patra
12543888c2fSAtish Patra timer_mod(timer, next);
12643888c2fSAtish Patra }
12743888c2fSAtish Patra
riscv_timer_init(RISCVCPU * cpu)12843888c2fSAtish Patra void riscv_timer_init(RISCVCPU *cpu)
12943888c2fSAtish Patra {
13043888c2fSAtish Patra CPURISCVState *env;
13143888c2fSAtish Patra
13243888c2fSAtish Patra if (!cpu) {
13343888c2fSAtish Patra return;
13443888c2fSAtish Patra }
13543888c2fSAtish Patra
13643888c2fSAtish Patra env = &cpu->env;
13743888c2fSAtish Patra env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu);
13843888c2fSAtish Patra env->stimecmp = 0;
13943888c2fSAtish Patra
1403ec0fe18SAtish Patra env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu);
1413ec0fe18SAtish Patra env->vstimecmp = 0;
14243888c2fSAtish Patra }
143