1*df41cbd6SAlistair Francis /* 2*df41cbd6SAlistair Francis * QEMU lowRISC Ibex Timer device 3*df41cbd6SAlistair Francis * 4*df41cbd6SAlistair Francis * Copyright (c) 2021 Western Digital 5*df41cbd6SAlistair Francis * 6*df41cbd6SAlistair Francis * For details check the documentation here: 7*df41cbd6SAlistair Francis * https://docs.opentitan.org/hw/ip/rv_timer/doc/ 8*df41cbd6SAlistair Francis * 9*df41cbd6SAlistair Francis * Permission is hereby granted, free of charge, to any person obtaining a copy 10*df41cbd6SAlistair Francis * of this software and associated documentation files (the "Software"), to deal 11*df41cbd6SAlistair Francis * in the Software without restriction, including without limitation the rights 12*df41cbd6SAlistair Francis * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13*df41cbd6SAlistair Francis * copies of the Software, and to permit persons to whom the Software is 14*df41cbd6SAlistair Francis * furnished to do so, subject to the following conditions: 15*df41cbd6SAlistair Francis * 16*df41cbd6SAlistair Francis * The above copyright notice and this permission notice shall be included in 17*df41cbd6SAlistair Francis * all copies or substantial portions of the Software. 18*df41cbd6SAlistair Francis * 19*df41cbd6SAlistair Francis * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20*df41cbd6SAlistair Francis * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21*df41cbd6SAlistair Francis * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22*df41cbd6SAlistair Francis * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23*df41cbd6SAlistair Francis * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24*df41cbd6SAlistair Francis * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25*df41cbd6SAlistair Francis * THE SOFTWARE. 26*df41cbd6SAlistair Francis */ 27*df41cbd6SAlistair Francis 28*df41cbd6SAlistair Francis #include "qemu/osdep.h" 29*df41cbd6SAlistair Francis #include "qemu/log.h" 30*df41cbd6SAlistair Francis #include "qemu/timer.h" 31*df41cbd6SAlistair Francis #include "hw/timer/ibex_timer.h" 32*df41cbd6SAlistair Francis #include "hw/irq.h" 33*df41cbd6SAlistair Francis #include "hw/qdev-properties.h" 34*df41cbd6SAlistair Francis #include "target/riscv/cpu.h" 35*df41cbd6SAlistair Francis #include "migration/vmstate.h" 36*df41cbd6SAlistair Francis 37*df41cbd6SAlistair Francis REG32(CTRL, 0x00) 38*df41cbd6SAlistair Francis FIELD(CTRL, ACTIVE, 0, 1) 39*df41cbd6SAlistair Francis REG32(CFG0, 0x100) 40*df41cbd6SAlistair Francis FIELD(CFG0, PRESCALE, 0, 12) 41*df41cbd6SAlistair Francis FIELD(CFG0, STEP, 16, 8) 42*df41cbd6SAlistair Francis REG32(LOWER0, 0x104) 43*df41cbd6SAlistair Francis REG32(UPPER0, 0x108) 44*df41cbd6SAlistair Francis REG32(COMPARE_LOWER0, 0x10C) 45*df41cbd6SAlistair Francis REG32(COMPARE_UPPER0, 0x110) 46*df41cbd6SAlistair Francis REG32(INTR_ENABLE, 0x114) 47*df41cbd6SAlistair Francis FIELD(INTR_ENABLE, IE_0, 0, 1) 48*df41cbd6SAlistair Francis REG32(INTR_STATE, 0x118) 49*df41cbd6SAlistair Francis FIELD(INTR_STATE, IS_0, 0, 1) 50*df41cbd6SAlistair Francis REG32(INTR_TEST, 0x11C) 51*df41cbd6SAlistair Francis FIELD(INTR_TEST, T_0, 0, 1) 52*df41cbd6SAlistair Francis 53*df41cbd6SAlistair Francis static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) 54*df41cbd6SAlistair Francis { 55*df41cbd6SAlistair Francis return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 56*df41cbd6SAlistair Francis timebase_freq, NANOSECONDS_PER_SECOND); 57*df41cbd6SAlistair Francis } 58*df41cbd6SAlistair Francis 59*df41cbd6SAlistair Francis static void ibex_timer_update_irqs(IbexTimerState *s) 60*df41cbd6SAlistair Francis { 61*df41cbd6SAlistair Francis CPUState *cs = qemu_get_cpu(0); 62*df41cbd6SAlistair Francis RISCVCPU *cpu = RISCV_CPU(cs); 63*df41cbd6SAlistair Francis uint64_t value = s->timer_compare_lower0 | 64*df41cbd6SAlistair Francis ((uint64_t)s->timer_compare_upper0 << 32); 65*df41cbd6SAlistair Francis uint64_t next, diff; 66*df41cbd6SAlistair Francis uint64_t now = cpu_riscv_read_rtc(s->timebase_freq); 67*df41cbd6SAlistair Francis 68*df41cbd6SAlistair Francis if (!(s->timer_ctrl & R_CTRL_ACTIVE_MASK)) { 69*df41cbd6SAlistair Francis /* Timer isn't active */ 70*df41cbd6SAlistair Francis return; 71*df41cbd6SAlistair Francis } 72*df41cbd6SAlistair Francis 73*df41cbd6SAlistair Francis /* Update the CPUs mtimecmp */ 74*df41cbd6SAlistair Francis cpu->env.timecmp = value; 75*df41cbd6SAlistair Francis 76*df41cbd6SAlistair Francis if (cpu->env.timecmp <= now) { 77*df41cbd6SAlistair Francis /* 78*df41cbd6SAlistair Francis * If the mtimecmp was in the past raise the interrupt now. 79*df41cbd6SAlistair Francis */ 80*df41cbd6SAlistair Francis riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); 81*df41cbd6SAlistair Francis if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) { 82*df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK; 83*df41cbd6SAlistair Francis qemu_set_irq(s->irq, true); 84*df41cbd6SAlistair Francis } 85*df41cbd6SAlistair Francis return; 86*df41cbd6SAlistair Francis } 87*df41cbd6SAlistair Francis 88*df41cbd6SAlistair Francis /* Setup a timer to trigger the interrupt in the future */ 89*df41cbd6SAlistair Francis riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0)); 90*df41cbd6SAlistair Francis qemu_set_irq(s->irq, false); 91*df41cbd6SAlistair Francis 92*df41cbd6SAlistair Francis diff = cpu->env.timecmp - now; 93*df41cbd6SAlistair Francis next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 94*df41cbd6SAlistair Francis muldiv64(diff, 95*df41cbd6SAlistair Francis NANOSECONDS_PER_SECOND, 96*df41cbd6SAlistair Francis s->timebase_freq); 97*df41cbd6SAlistair Francis 98*df41cbd6SAlistair Francis if (next < qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { 99*df41cbd6SAlistair Francis /* We overflowed the timer, just set it as large as we can */ 100*df41cbd6SAlistair Francis timer_mod(cpu->env.timer, 0x7FFFFFFFFFFFFFFF); 101*df41cbd6SAlistair Francis } else { 102*df41cbd6SAlistair Francis timer_mod(cpu->env.timer, next); 103*df41cbd6SAlistair Francis } 104*df41cbd6SAlistair Francis } 105*df41cbd6SAlistair Francis 106*df41cbd6SAlistair Francis static void ibex_timer_cb(void *opaque) 107*df41cbd6SAlistair Francis { 108*df41cbd6SAlistair Francis IbexTimerState *s = opaque; 109*df41cbd6SAlistair Francis CPUState *cs = qemu_get_cpu(0); 110*df41cbd6SAlistair Francis RISCVCPU *cpu = RISCV_CPU(cs); 111*df41cbd6SAlistair Francis 112*df41cbd6SAlistair Francis riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); 113*df41cbd6SAlistair Francis if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) { 114*df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK; 115*df41cbd6SAlistair Francis qemu_set_irq(s->irq, true); 116*df41cbd6SAlistair Francis } 117*df41cbd6SAlistair Francis } 118*df41cbd6SAlistair Francis 119*df41cbd6SAlistair Francis static void ibex_timer_reset(DeviceState *dev) 120*df41cbd6SAlistair Francis { 121*df41cbd6SAlistair Francis IbexTimerState *s = IBEX_TIMER(dev); 122*df41cbd6SAlistair Francis 123*df41cbd6SAlistair Francis CPUState *cpu = qemu_get_cpu(0); 124*df41cbd6SAlistair Francis CPURISCVState *env = cpu->env_ptr; 125*df41cbd6SAlistair Francis env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 126*df41cbd6SAlistair Francis &ibex_timer_cb, s); 127*df41cbd6SAlistair Francis env->timecmp = 0; 128*df41cbd6SAlistair Francis 129*df41cbd6SAlistair Francis s->timer_ctrl = 0x00000000; 130*df41cbd6SAlistair Francis s->timer_cfg0 = 0x00010000; 131*df41cbd6SAlistair Francis s->timer_compare_lower0 = 0xFFFFFFFF; 132*df41cbd6SAlistair Francis s->timer_compare_upper0 = 0xFFFFFFFF; 133*df41cbd6SAlistair Francis s->timer_intr_enable = 0x00000000; 134*df41cbd6SAlistair Francis s->timer_intr_state = 0x00000000; 135*df41cbd6SAlistair Francis s->timer_intr_test = 0x00000000; 136*df41cbd6SAlistair Francis 137*df41cbd6SAlistair Francis ibex_timer_update_irqs(s); 138*df41cbd6SAlistair Francis } 139*df41cbd6SAlistair Francis 140*df41cbd6SAlistair Francis static uint64_t ibex_timer_read(void *opaque, hwaddr addr, 141*df41cbd6SAlistair Francis unsigned int size) 142*df41cbd6SAlistair Francis { 143*df41cbd6SAlistair Francis IbexTimerState *s = opaque; 144*df41cbd6SAlistair Francis uint64_t now = cpu_riscv_read_rtc(s->timebase_freq); 145*df41cbd6SAlistair Francis uint64_t retvalue = 0; 146*df41cbd6SAlistair Francis 147*df41cbd6SAlistair Francis switch (addr >> 2) { 148*df41cbd6SAlistair Francis case R_CTRL: 149*df41cbd6SAlistair Francis retvalue = s->timer_ctrl; 150*df41cbd6SAlistair Francis break; 151*df41cbd6SAlistair Francis case R_CFG0: 152*df41cbd6SAlistair Francis retvalue = s->timer_cfg0; 153*df41cbd6SAlistair Francis break; 154*df41cbd6SAlistair Francis case R_LOWER0: 155*df41cbd6SAlistair Francis retvalue = now; 156*df41cbd6SAlistair Francis break; 157*df41cbd6SAlistair Francis case R_UPPER0: 158*df41cbd6SAlistair Francis retvalue = now >> 32; 159*df41cbd6SAlistair Francis break; 160*df41cbd6SAlistair Francis case R_COMPARE_LOWER0: 161*df41cbd6SAlistair Francis retvalue = s->timer_compare_lower0; 162*df41cbd6SAlistair Francis break; 163*df41cbd6SAlistair Francis case R_COMPARE_UPPER0: 164*df41cbd6SAlistair Francis retvalue = s->timer_compare_upper0; 165*df41cbd6SAlistair Francis break; 166*df41cbd6SAlistair Francis case R_INTR_ENABLE: 167*df41cbd6SAlistair Francis retvalue = s->timer_intr_enable; 168*df41cbd6SAlistair Francis break; 169*df41cbd6SAlistair Francis case R_INTR_STATE: 170*df41cbd6SAlistair Francis retvalue = s->timer_intr_state; 171*df41cbd6SAlistair Francis break; 172*df41cbd6SAlistair Francis case R_INTR_TEST: 173*df41cbd6SAlistair Francis retvalue = s->timer_intr_test; 174*df41cbd6SAlistair Francis break; 175*df41cbd6SAlistair Francis default: 176*df41cbd6SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 177*df41cbd6SAlistair Francis "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); 178*df41cbd6SAlistair Francis return 0; 179*df41cbd6SAlistair Francis } 180*df41cbd6SAlistair Francis 181*df41cbd6SAlistair Francis return retvalue; 182*df41cbd6SAlistair Francis } 183*df41cbd6SAlistair Francis 184*df41cbd6SAlistair Francis static void ibex_timer_write(void *opaque, hwaddr addr, 185*df41cbd6SAlistair Francis uint64_t val64, unsigned int size) 186*df41cbd6SAlistair Francis { 187*df41cbd6SAlistair Francis IbexTimerState *s = opaque; 188*df41cbd6SAlistair Francis uint32_t val = val64; 189*df41cbd6SAlistair Francis 190*df41cbd6SAlistair Francis switch (addr >> 2) { 191*df41cbd6SAlistair Francis case R_CTRL: 192*df41cbd6SAlistair Francis s->timer_ctrl = val; 193*df41cbd6SAlistair Francis break; 194*df41cbd6SAlistair Francis case R_CFG0: 195*df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing prescale or step not supported"); 196*df41cbd6SAlistair Francis s->timer_cfg0 = val; 197*df41cbd6SAlistair Francis break; 198*df41cbd6SAlistair Francis case R_LOWER0: 199*df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing timer value is not supported"); 200*df41cbd6SAlistair Francis break; 201*df41cbd6SAlistair Francis case R_UPPER0: 202*df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing timer value is not supported"); 203*df41cbd6SAlistair Francis break; 204*df41cbd6SAlistair Francis case R_COMPARE_LOWER0: 205*df41cbd6SAlistair Francis s->timer_compare_lower0 = val; 206*df41cbd6SAlistair Francis ibex_timer_update_irqs(s); 207*df41cbd6SAlistair Francis break; 208*df41cbd6SAlistair Francis case R_COMPARE_UPPER0: 209*df41cbd6SAlistair Francis s->timer_compare_upper0 = val; 210*df41cbd6SAlistair Francis ibex_timer_update_irqs(s); 211*df41cbd6SAlistair Francis break; 212*df41cbd6SAlistair Francis case R_INTR_ENABLE: 213*df41cbd6SAlistair Francis s->timer_intr_enable = val; 214*df41cbd6SAlistair Francis break; 215*df41cbd6SAlistair Francis case R_INTR_STATE: 216*df41cbd6SAlistair Francis /* Write 1 to clear */ 217*df41cbd6SAlistair Francis s->timer_intr_state &= ~val; 218*df41cbd6SAlistair Francis break; 219*df41cbd6SAlistair Francis case R_INTR_TEST: 220*df41cbd6SAlistair Francis s->timer_intr_test = val; 221*df41cbd6SAlistair Francis if (s->timer_intr_enable & 222*df41cbd6SAlistair Francis s->timer_intr_test & 223*df41cbd6SAlistair Francis R_INTR_ENABLE_IE_0_MASK) { 224*df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK; 225*df41cbd6SAlistair Francis qemu_set_irq(s->irq, true); 226*df41cbd6SAlistair Francis } 227*df41cbd6SAlistair Francis break; 228*df41cbd6SAlistair Francis default: 229*df41cbd6SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 230*df41cbd6SAlistair Francis "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); 231*df41cbd6SAlistair Francis } 232*df41cbd6SAlistair Francis } 233*df41cbd6SAlistair Francis 234*df41cbd6SAlistair Francis static const MemoryRegionOps ibex_timer_ops = { 235*df41cbd6SAlistair Francis .read = ibex_timer_read, 236*df41cbd6SAlistair Francis .write = ibex_timer_write, 237*df41cbd6SAlistair Francis .endianness = DEVICE_NATIVE_ENDIAN, 238*df41cbd6SAlistair Francis .impl.min_access_size = 4, 239*df41cbd6SAlistair Francis .impl.max_access_size = 4, 240*df41cbd6SAlistair Francis }; 241*df41cbd6SAlistair Francis 242*df41cbd6SAlistair Francis static int ibex_timer_post_load(void *opaque, int version_id) 243*df41cbd6SAlistair Francis { 244*df41cbd6SAlistair Francis IbexTimerState *s = opaque; 245*df41cbd6SAlistair Francis 246*df41cbd6SAlistair Francis ibex_timer_update_irqs(s); 247*df41cbd6SAlistair Francis return 0; 248*df41cbd6SAlistair Francis } 249*df41cbd6SAlistair Francis 250*df41cbd6SAlistair Francis static const VMStateDescription vmstate_ibex_timer = { 251*df41cbd6SAlistair Francis .name = TYPE_IBEX_TIMER, 252*df41cbd6SAlistair Francis .version_id = 1, 253*df41cbd6SAlistair Francis .minimum_version_id = 1, 254*df41cbd6SAlistair Francis .post_load = ibex_timer_post_load, 255*df41cbd6SAlistair Francis .fields = (VMStateField[]) { 256*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_ctrl, IbexTimerState), 257*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_cfg0, IbexTimerState), 258*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_compare_lower0, IbexTimerState), 259*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_compare_upper0, IbexTimerState), 260*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_intr_enable, IbexTimerState), 261*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_intr_state, IbexTimerState), 262*df41cbd6SAlistair Francis VMSTATE_UINT32(timer_intr_test, IbexTimerState), 263*df41cbd6SAlistair Francis VMSTATE_END_OF_LIST() 264*df41cbd6SAlistair Francis } 265*df41cbd6SAlistair Francis }; 266*df41cbd6SAlistair Francis 267*df41cbd6SAlistair Francis static Property ibex_timer_properties[] = { 268*df41cbd6SAlistair Francis DEFINE_PROP_UINT32("timebase-freq", IbexTimerState, timebase_freq, 10000), 269*df41cbd6SAlistair Francis DEFINE_PROP_END_OF_LIST(), 270*df41cbd6SAlistair Francis }; 271*df41cbd6SAlistair Francis 272*df41cbd6SAlistair Francis static void ibex_timer_init(Object *obj) 273*df41cbd6SAlistair Francis { 274*df41cbd6SAlistair Francis IbexTimerState *s = IBEX_TIMER(obj); 275*df41cbd6SAlistair Francis 276*df41cbd6SAlistair Francis sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); 277*df41cbd6SAlistair Francis 278*df41cbd6SAlistair Francis memory_region_init_io(&s->mmio, obj, &ibex_timer_ops, s, 279*df41cbd6SAlistair Francis TYPE_IBEX_TIMER, 0x400); 280*df41cbd6SAlistair Francis sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 281*df41cbd6SAlistair Francis } 282*df41cbd6SAlistair Francis 283*df41cbd6SAlistair Francis static void ibex_timer_class_init(ObjectClass *klass, void *data) 284*df41cbd6SAlistair Francis { 285*df41cbd6SAlistair Francis DeviceClass *dc = DEVICE_CLASS(klass); 286*df41cbd6SAlistair Francis 287*df41cbd6SAlistair Francis dc->reset = ibex_timer_reset; 288*df41cbd6SAlistair Francis dc->vmsd = &vmstate_ibex_timer; 289*df41cbd6SAlistair Francis device_class_set_props(dc, ibex_timer_properties); 290*df41cbd6SAlistair Francis } 291*df41cbd6SAlistair Francis 292*df41cbd6SAlistair Francis static const TypeInfo ibex_timer_info = { 293*df41cbd6SAlistair Francis .name = TYPE_IBEX_TIMER, 294*df41cbd6SAlistair Francis .parent = TYPE_SYS_BUS_DEVICE, 295*df41cbd6SAlistair Francis .instance_size = sizeof(IbexTimerState), 296*df41cbd6SAlistair Francis .instance_init = ibex_timer_init, 297*df41cbd6SAlistair Francis .class_init = ibex_timer_class_init, 298*df41cbd6SAlistair Francis }; 299*df41cbd6SAlistair Francis 300*df41cbd6SAlistair Francis static void ibex_timer_register_types(void) 301*df41cbd6SAlistair Francis { 302*df41cbd6SAlistair Francis type_register_static(&ibex_timer_info); 303*df41cbd6SAlistair Francis } 304*df41cbd6SAlistair Francis 305*df41cbd6SAlistair Francis type_init(ibex_timer_register_types) 306