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; 3014cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 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; 3643888c2fSAtish Patra riscv_cpu_update_mip(cpu, 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 */ 4343888c2fSAtish Patra void riscv_timer_write_timecmp(RISCVCPU *cpu, 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 CPURISCVState *env = &cpu->env; 4943888c2fSAtish Patra RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; 5043888c2fSAtish Patra uint32_t timebase_freq = mtimer->timebase_freq; 5143888c2fSAtish Patra uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; 5243888c2fSAtish Patra 5343888c2fSAtish Patra if (timecmp <= rtc_r) { 5443888c2fSAtish Patra /* 5543888c2fSAtish Patra * If we're setting an stimecmp value in the "past", 5643888c2fSAtish Patra * immediately raise the timer interrupt 5743888c2fSAtish Patra */ 583ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) { 593ec0fe18SAtish Patra env->vstime_irq = 1; 6014cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(1)); 6114cb78bfSAnup Patel } else { 6214cb78bfSAnup Patel riscv_cpu_update_mip(cpu, MIP_STIP, BOOL_TO_MASK(1)); 633ec0fe18SAtish Patra } 6443888c2fSAtish Patra return; 6543888c2fSAtish Patra } 6643888c2fSAtish Patra 6714cb78bfSAnup Patel /* Clear the [VS|S]TIP bit in mip */ 683ec0fe18SAtish Patra if (timer_irq == MIP_VSTIP) { 693ec0fe18SAtish Patra env->vstime_irq = 0; 7014cb78bfSAnup Patel riscv_cpu_update_mip(cpu, 0, BOOL_TO_MASK(0)); 7114cb78bfSAnup Patel } else { 7243888c2fSAtish Patra riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); 7314cb78bfSAnup Patel } 7443888c2fSAtish Patra 75*ae0edf21SAnup Patel /* 76*ae0edf21SAnup Patel * Sstc specification says the following about timer interrupt: 77*ae0edf21SAnup Patel * "A supervisor timer interrupt becomes pending - as reflected in 78*ae0edf21SAnup Patel * the STIP bit in the mip and sip registers - whenever time contains 79*ae0edf21SAnup Patel * a value greater than or equal to stimecmp, treating the values 80*ae0edf21SAnup Patel * as unsigned integers. Writes to stimecmp are guaranteed to be 81*ae0edf21SAnup Patel * reflected in STIP eventually, but not necessarily immediately. 82*ae0edf21SAnup Patel * The interrupt remains posted until stimecmp becomes greater 83*ae0edf21SAnup Patel * than time - typically as a result of writing stimecmp." 84*ae0edf21SAnup Patel * 85*ae0edf21SAnup Patel * When timecmp = UINT64_MAX, the time CSR will eventually reach 86*ae0edf21SAnup Patel * timecmp value but on next timer tick the time CSR will wrap-around 87*ae0edf21SAnup Patel * and become zero which is less than UINT64_MAX. Now, the timer 88*ae0edf21SAnup Patel * interrupt behaves like a level triggered interrupt so it will 89*ae0edf21SAnup Patel * become 1 when time = timecmp = UINT64_MAX and next timer tick 90*ae0edf21SAnup Patel * it will become 0 again because time = 0 < timecmp = UINT64_MAX. 91*ae0edf21SAnup Patel * 92*ae0edf21SAnup Patel * Based on above, we don't re-start the QEMU timer when timecmp 93*ae0edf21SAnup Patel * equals UINT64_MAX. 94*ae0edf21SAnup Patel */ 95*ae0edf21SAnup Patel if (timecmp == UINT64_MAX) { 96*ae0edf21SAnup Patel return; 97*ae0edf21SAnup Patel } 98*ae0edf21SAnup 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 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