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 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; 30*bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1)); 313ec0fe18SAtish Patra } 323ec0fe18SAtish Patra 3343888c2fSAtish Patra static void riscv_stimer_cb(void *opaque) 3443888c2fSAtish Patra { 3543888c2fSAtish Patra RISCVCPU *cpu = opaque; 36*bbb9fc25SWeiwei 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 */ 43*bbb9fc25SWeiwei 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; 59*bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1)); 6014cb78bfSAnup Patel } else { 61*bbb9fc25SWeiwei 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; 69*bbb9fc25SWeiwei Li riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); 7014cb78bfSAnup Patel } else { 71*bbb9fc25SWeiwei 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) { 95ae0edf21SAnup Patel return; 96ae0edf21SAnup Patel } 97ae0edf21SAnup Patel 9843888c2fSAtish Patra /* otherwise, set up the future timer interrupt */ 9943888c2fSAtish Patra diff = timecmp - rtc_r; 10043888c2fSAtish Patra /* back to ns (note args switched in muldiv64) */ 10143888c2fSAtish Patra ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); 10243888c2fSAtish Patra 10343888c2fSAtish Patra /* 10443888c2fSAtish Patra * check if ns_diff overflowed and check if the addition would potentially 10543888c2fSAtish Patra * overflow 10643888c2fSAtish Patra */ 10743888c2fSAtish Patra if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || 10843888c2fSAtish Patra ns_diff > INT64_MAX) { 10943888c2fSAtish Patra next = INT64_MAX; 11043888c2fSAtish Patra } else { 11143888c2fSAtish Patra /* 11243888c2fSAtish Patra * as it is very unlikely qemu_clock_get_ns will return a value 11343888c2fSAtish Patra * greater than INT64_MAX, no additional check is needed for an 11443888c2fSAtish Patra * unsigned integer overflow. 11543888c2fSAtish Patra */ 11643888c2fSAtish Patra next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; 11743888c2fSAtish Patra /* 11843888c2fSAtish Patra * if ns_diff is INT64_MAX next may still be outside the range 11943888c2fSAtish Patra * of a signed integer. 12043888c2fSAtish Patra */ 12143888c2fSAtish Patra next = MIN(next, INT64_MAX); 12243888c2fSAtish Patra } 12343888c2fSAtish Patra 12443888c2fSAtish Patra timer_mod(timer, next); 12543888c2fSAtish Patra } 12643888c2fSAtish Patra 12743888c2fSAtish Patra void riscv_timer_init(RISCVCPU *cpu) 12843888c2fSAtish Patra { 12943888c2fSAtish Patra CPURISCVState *env; 13043888c2fSAtish Patra 13143888c2fSAtish Patra if (!cpu) { 13243888c2fSAtish Patra return; 13343888c2fSAtish Patra } 13443888c2fSAtish Patra 13543888c2fSAtish Patra env = &cpu->env; 13643888c2fSAtish Patra env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); 13743888c2fSAtish Patra env->stimecmp = 0; 13843888c2fSAtish Patra 1393ec0fe18SAtish Patra env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); 1403ec0fe18SAtish Patra env->vstimecmp = 0; 14143888c2fSAtish Patra } 142