1*40514051SYongbok Kim /* 2*40514051SYongbok Kim * This file is subject to the terms and conditions of the GNU General Public 3*40514051SYongbok Kim * License. See the file "COPYING" in the main directory of this archive 4*40514051SYongbok Kim * for more details. 5*40514051SYongbok Kim * 6*40514051SYongbok Kim * Copyright (C) 2016 Imagination Technologies 7*40514051SYongbok Kim */ 8*40514051SYongbok Kim 9*40514051SYongbok Kim #include "qemu/osdep.h" 10*40514051SYongbok Kim #include "hw/hw.h" 11*40514051SYongbok Kim #include "hw/sysbus.h" 12*40514051SYongbok Kim #include "qemu/timer.h" 13*40514051SYongbok Kim #include "hw/timer/mips_gictimer.h" 14*40514051SYongbok Kim 15*40514051SYongbok Kim #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */ 16*40514051SYongbok Kim 17*40514051SYongbok Kim static void gic_vptimer_update(MIPSGICTimerState *gictimer, 18*40514051SYongbok Kim uint32_t vp_index, uint64_t now) 19*40514051SYongbok Kim { 20*40514051SYongbok Kim uint64_t next; 21*40514051SYongbok Kim uint32_t wait; 22*40514051SYongbok Kim 23*40514051SYongbok Kim wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo - 24*40514051SYongbok Kim (uint32_t)(now / TIMER_PERIOD); 25*40514051SYongbok Kim next = now + (uint64_t)wait * TIMER_PERIOD; 26*40514051SYongbok Kim 27*40514051SYongbok Kim timer_mod(gictimer->vptimers[vp_index].qtimer, next); 28*40514051SYongbok Kim } 29*40514051SYongbok Kim 30*40514051SYongbok Kim static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index, 31*40514051SYongbok Kim uint64_t now) 32*40514051SYongbok Kim { 33*40514051SYongbok Kim if (gictimer->countstop) { 34*40514051SYongbok Kim /* timer stopped */ 35*40514051SYongbok Kim return; 36*40514051SYongbok Kim } 37*40514051SYongbok Kim gictimer->cb(gictimer->opaque, vp_index); 38*40514051SYongbok Kim gic_vptimer_update(gictimer, vp_index, now); 39*40514051SYongbok Kim } 40*40514051SYongbok Kim 41*40514051SYongbok Kim static void gic_vptimer_cb(void *opaque) 42*40514051SYongbok Kim { 43*40514051SYongbok Kim MIPSGICTimerVPState *vptimer = opaque; 44*40514051SYongbok Kim MIPSGICTimerState *gictimer = vptimer->gictimer; 45*40514051SYongbok Kim gic_vptimer_expire(gictimer, vptimer->vp_index, 46*40514051SYongbok Kim qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 47*40514051SYongbok Kim } 48*40514051SYongbok Kim 49*40514051SYongbok Kim uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer) 50*40514051SYongbok Kim { 51*40514051SYongbok Kim int i; 52*40514051SYongbok Kim if (gictimer->countstop) { 53*40514051SYongbok Kim return gictimer->sh_counterlo; 54*40514051SYongbok Kim } else { 55*40514051SYongbok Kim uint64_t now; 56*40514051SYongbok Kim now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 57*40514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 58*40514051SYongbok Kim if (timer_pending(gictimer->vptimers[i].qtimer) 59*40514051SYongbok Kim && timer_expired(gictimer->vptimers[i].qtimer, now)) { 60*40514051SYongbok Kim /* The timer has already expired. */ 61*40514051SYongbok Kim gic_vptimer_expire(gictimer, i, now); 62*40514051SYongbok Kim } 63*40514051SYongbok Kim } 64*40514051SYongbok Kim return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD); 65*40514051SYongbok Kim } 66*40514051SYongbok Kim } 67*40514051SYongbok Kim 68*40514051SYongbok Kim void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count) 69*40514051SYongbok Kim { 70*40514051SYongbok Kim int i; 71*40514051SYongbok Kim uint64_t now; 72*40514051SYongbok Kim 73*40514051SYongbok Kim if (gictimer->countstop || !gictimer->vptimers[0].qtimer) { 74*40514051SYongbok Kim gictimer->sh_counterlo = count; 75*40514051SYongbok Kim } else { 76*40514051SYongbok Kim /* Store new count register */ 77*40514051SYongbok Kim now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 78*40514051SYongbok Kim gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD); 79*40514051SYongbok Kim /* Update timer timer */ 80*40514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 81*40514051SYongbok Kim gic_vptimer_update(gictimer, i, now); 82*40514051SYongbok Kim } 83*40514051SYongbok Kim } 84*40514051SYongbok Kim } 85*40514051SYongbok Kim 86*40514051SYongbok Kim uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer, 87*40514051SYongbok Kim uint32_t vp_index) 88*40514051SYongbok Kim { 89*40514051SYongbok Kim return gictimer->vptimers[vp_index].comparelo; 90*40514051SYongbok Kim } 91*40514051SYongbok Kim 92*40514051SYongbok Kim void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer, 93*40514051SYongbok Kim uint32_t vp_index, uint64_t compare) 94*40514051SYongbok Kim { 95*40514051SYongbok Kim gictimer->vptimers[vp_index].comparelo = (uint32_t) compare; 96*40514051SYongbok Kim gic_vptimer_update(gictimer, vp_index, 97*40514051SYongbok Kim qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 98*40514051SYongbok Kim } 99*40514051SYongbok Kim 100*40514051SYongbok Kim uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer) 101*40514051SYongbok Kim { 102*40514051SYongbok Kim return gictimer->countstop; 103*40514051SYongbok Kim } 104*40514051SYongbok Kim 105*40514051SYongbok Kim void mips_gictimer_start_count(MIPSGICTimerState *gictimer) 106*40514051SYongbok Kim { 107*40514051SYongbok Kim gictimer->countstop = 0; 108*40514051SYongbok Kim mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo); 109*40514051SYongbok Kim } 110*40514051SYongbok Kim 111*40514051SYongbok Kim void mips_gictimer_stop_count(MIPSGICTimerState *gictimer) 112*40514051SYongbok Kim { 113*40514051SYongbok Kim int i; 114*40514051SYongbok Kim 115*40514051SYongbok Kim gictimer->countstop = 1; 116*40514051SYongbok Kim /* Store the current value */ 117*40514051SYongbok Kim gictimer->sh_counterlo += 118*40514051SYongbok Kim (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD); 119*40514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 120*40514051SYongbok Kim timer_del(gictimer->vptimers[i].qtimer); 121*40514051SYongbok Kim } 122*40514051SYongbok Kim } 123*40514051SYongbok Kim 124*40514051SYongbok Kim MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps, 125*40514051SYongbok Kim MIPSGICTimerCB *cb) 126*40514051SYongbok Kim { 127*40514051SYongbok Kim int i; 128*40514051SYongbok Kim MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1); 129*40514051SYongbok Kim gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps); 130*40514051SYongbok Kim gictimer->countstop = 1; 131*40514051SYongbok Kim gictimer->num_vps = nvps; 132*40514051SYongbok Kim gictimer->opaque = opaque; 133*40514051SYongbok Kim gictimer->cb = cb; 134*40514051SYongbok Kim for (i = 0; i < nvps; i++) { 135*40514051SYongbok Kim gictimer->vptimers[i].gictimer = gictimer; 136*40514051SYongbok Kim gictimer->vptimers[i].vp_index = i; 137*40514051SYongbok Kim gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 138*40514051SYongbok Kim &gic_vptimer_cb, 139*40514051SYongbok Kim &gictimer->vptimers[i]); 140*40514051SYongbok Kim } 141*40514051SYongbok Kim return gictimer; 142*40514051SYongbok Kim } 143