2 * RISC-V timer helper implementation.
4 * Copyright (c) 2022 Rivos Inc.
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.
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
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/>.
19 #include "qemu/osdep.h"
22 #include "time_helper.h"
23 #include "hw/intc/riscv_aclint.h"
25 static void riscv_vstimer_cb(void *opaque
)
27 RISCVCPU
*cpu
= opaque
;
28 CPURISCVState
*env
= &cpu
->env
;
30 riscv_cpu_update_mip(env
, 0, BOOL_TO_MASK(1));
33 static void riscv_stimer_cb(void *opaque
)
35 RISCVCPU
*cpu
= opaque
;
36 riscv_cpu_update_mip(&cpu
->env
, MIP_STIP
, BOOL_TO_MASK(1));
40 * Called when timecmp is written to update the QEMU timer or immediately
41 * trigger timer interrupt if mtimecmp <= current timer value.
43 void riscv_timer_write_timecmp(CPURISCVState
*env
, QEMUTimer
*timer
,
44 uint64_t timecmp
, uint64_t delta
,
47 uint64_t diff
, ns_diff
, next
;
48 RISCVAclintMTimerState
*mtimer
= env
->rdtime_fn_arg
;
49 uint32_t timebase_freq
= mtimer
->timebase_freq
;
50 uint64_t rtc_r
= env
->rdtime_fn(env
->rdtime_fn_arg
) + delta
;
52 if (timecmp
<= rtc_r
) {
54 * If we're setting an stimecmp value in the "past",
55 * immediately raise the timer interrupt
57 if (timer_irq
== MIP_VSTIP
) {
59 riscv_cpu_update_mip(env
, 0, BOOL_TO_MASK(1));
61 riscv_cpu_update_mip(env
, MIP_STIP
, BOOL_TO_MASK(1));
66 /* Clear the [VS|S]TIP bit in mip */
67 if (timer_irq
== MIP_VSTIP
) {
69 riscv_cpu_update_mip(env
, 0, BOOL_TO_MASK(0));
71 riscv_cpu_update_mip(env
, timer_irq
, BOOL_TO_MASK(0));
75 * Sstc specification says the following about timer interrupt:
76 * "A supervisor timer interrupt becomes pending - as reflected in
77 * the STIP bit in the mip and sip registers - whenever time contains
78 * a value greater than or equal to stimecmp, treating the values
79 * as unsigned integers. Writes to stimecmp are guaranteed to be
80 * reflected in STIP eventually, but not necessarily immediately.
81 * The interrupt remains posted until stimecmp becomes greater
82 * than time - typically as a result of writing stimecmp."
84 * When timecmp = UINT64_MAX, the time CSR will eventually reach
85 * timecmp value but on next timer tick the time CSR will wrap-around
86 * and become zero which is less than UINT64_MAX. Now, the timer
87 * interrupt behaves like a level triggered interrupt so it will
88 * become 1 when time = timecmp = UINT64_MAX and next timer tick
89 * it will become 0 again because time = 0 < timecmp = UINT64_MAX.
91 * Based on above, we don't re-start the QEMU timer when timecmp
94 if (timecmp
== UINT64_MAX
) {
98 /* otherwise, set up the future timer interrupt */
99 diff
= timecmp
- rtc_r
;
100 /* back to ns (note args switched in muldiv64) */
101 ns_diff
= muldiv64(diff
, NANOSECONDS_PER_SECOND
, timebase_freq
);
104 * check if ns_diff overflowed and check if the addition would potentially
107 if ((NANOSECONDS_PER_SECOND
> timebase_freq
&& ns_diff
< diff
) ||
108 ns_diff
> INT64_MAX
) {
112 * as it is very unlikely qemu_clock_get_ns will return a value
113 * greater than INT64_MAX, no additional check is needed for an
114 * unsigned integer overflow.
116 next
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) + ns_diff
;
118 * if ns_diff is INT64_MAX next may still be outside the range
119 * of a signed integer.
121 next
= MIN(next
, INT64_MAX
);
124 timer_mod(timer
, next
);
127 void riscv_timer_init(RISCVCPU
*cpu
)
136 env
->stimer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
, &riscv_stimer_cb
, cpu
);
139 env
->vstimer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
, &riscv_vstimer_cb
, cpu
);