140514051SYongbok Kim /* 240514051SYongbok Kim * This file is subject to the terms and conditions of the GNU General Public 340514051SYongbok Kim * License. See the file "COPYING" in the main directory of this archive 440514051SYongbok Kim * for more details. 540514051SYongbok Kim * 640514051SYongbok Kim * Copyright (C) 2016 Imagination Technologies 740514051SYongbok Kim */ 840514051SYongbok Kim 940514051SYongbok Kim #include "qemu/osdep.h" 1040514051SYongbok Kim #include "hw/hw.h" 1140514051SYongbok Kim #include "hw/sysbus.h" 1240514051SYongbok Kim #include "qemu/timer.h" 1340514051SYongbok Kim #include "hw/timer/mips_gictimer.h" 1440514051SYongbok Kim 1540514051SYongbok Kim #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */ 1640514051SYongbok Kim 17*eb90ab94SPaul Burton uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic) 18*eb90ab94SPaul Burton { 19*eb90ab94SPaul Burton return NANOSECONDS_PER_SECOND / TIMER_PERIOD; 20*eb90ab94SPaul Burton } 21*eb90ab94SPaul Burton 2240514051SYongbok Kim static void gic_vptimer_update(MIPSGICTimerState *gictimer, 2340514051SYongbok Kim uint32_t vp_index, uint64_t now) 2440514051SYongbok Kim { 2540514051SYongbok Kim uint64_t next; 2640514051SYongbok Kim uint32_t wait; 2740514051SYongbok Kim 2840514051SYongbok Kim wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo - 2940514051SYongbok Kim (uint32_t)(now / TIMER_PERIOD); 3040514051SYongbok Kim next = now + (uint64_t)wait * TIMER_PERIOD; 3140514051SYongbok Kim 3240514051SYongbok Kim timer_mod(gictimer->vptimers[vp_index].qtimer, next); 3340514051SYongbok Kim } 3440514051SYongbok Kim 3540514051SYongbok Kim static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index, 3640514051SYongbok Kim uint64_t now) 3740514051SYongbok Kim { 3840514051SYongbok Kim if (gictimer->countstop) { 3940514051SYongbok Kim /* timer stopped */ 4040514051SYongbok Kim return; 4140514051SYongbok Kim } 4240514051SYongbok Kim gictimer->cb(gictimer->opaque, vp_index); 4340514051SYongbok Kim gic_vptimer_update(gictimer, vp_index, now); 4440514051SYongbok Kim } 4540514051SYongbok Kim 4640514051SYongbok Kim static void gic_vptimer_cb(void *opaque) 4740514051SYongbok Kim { 4840514051SYongbok Kim MIPSGICTimerVPState *vptimer = opaque; 4940514051SYongbok Kim MIPSGICTimerState *gictimer = vptimer->gictimer; 5040514051SYongbok Kim gic_vptimer_expire(gictimer, vptimer->vp_index, 5140514051SYongbok Kim qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 5240514051SYongbok Kim } 5340514051SYongbok Kim 5440514051SYongbok Kim uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer) 5540514051SYongbok Kim { 5640514051SYongbok Kim int i; 5740514051SYongbok Kim if (gictimer->countstop) { 5840514051SYongbok Kim return gictimer->sh_counterlo; 5940514051SYongbok Kim } else { 6040514051SYongbok Kim uint64_t now; 6140514051SYongbok Kim now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 6240514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 6340514051SYongbok Kim if (timer_pending(gictimer->vptimers[i].qtimer) 6440514051SYongbok Kim && timer_expired(gictimer->vptimers[i].qtimer, now)) { 6540514051SYongbok Kim /* The timer has already expired. */ 6640514051SYongbok Kim gic_vptimer_expire(gictimer, i, now); 6740514051SYongbok Kim } 6840514051SYongbok Kim } 6940514051SYongbok Kim return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD); 7040514051SYongbok Kim } 7140514051SYongbok Kim } 7240514051SYongbok Kim 7340514051SYongbok Kim void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count) 7440514051SYongbok Kim { 7540514051SYongbok Kim int i; 7640514051SYongbok Kim uint64_t now; 7740514051SYongbok Kim 7840514051SYongbok Kim if (gictimer->countstop || !gictimer->vptimers[0].qtimer) { 7940514051SYongbok Kim gictimer->sh_counterlo = count; 8040514051SYongbok Kim } else { 8140514051SYongbok Kim /* Store new count register */ 8240514051SYongbok Kim now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 8340514051SYongbok Kim gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD); 8440514051SYongbok Kim /* Update timer timer */ 8540514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 8640514051SYongbok Kim gic_vptimer_update(gictimer, i, now); 8740514051SYongbok Kim } 8840514051SYongbok Kim } 8940514051SYongbok Kim } 9040514051SYongbok Kim 9140514051SYongbok Kim uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer, 9240514051SYongbok Kim uint32_t vp_index) 9340514051SYongbok Kim { 9440514051SYongbok Kim return gictimer->vptimers[vp_index].comparelo; 9540514051SYongbok Kim } 9640514051SYongbok Kim 9740514051SYongbok Kim void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer, 9840514051SYongbok Kim uint32_t vp_index, uint64_t compare) 9940514051SYongbok Kim { 10040514051SYongbok Kim gictimer->vptimers[vp_index].comparelo = (uint32_t) compare; 10140514051SYongbok Kim gic_vptimer_update(gictimer, vp_index, 10240514051SYongbok Kim qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 10340514051SYongbok Kim } 10440514051SYongbok Kim 10540514051SYongbok Kim uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer) 10640514051SYongbok Kim { 10740514051SYongbok Kim return gictimer->countstop; 10840514051SYongbok Kim } 10940514051SYongbok Kim 11040514051SYongbok Kim void mips_gictimer_start_count(MIPSGICTimerState *gictimer) 11140514051SYongbok Kim { 11240514051SYongbok Kim gictimer->countstop = 0; 11340514051SYongbok Kim mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo); 11440514051SYongbok Kim } 11540514051SYongbok Kim 11640514051SYongbok Kim void mips_gictimer_stop_count(MIPSGICTimerState *gictimer) 11740514051SYongbok Kim { 11840514051SYongbok Kim int i; 11940514051SYongbok Kim 12040514051SYongbok Kim gictimer->countstop = 1; 12140514051SYongbok Kim /* Store the current value */ 12240514051SYongbok Kim gictimer->sh_counterlo += 12340514051SYongbok Kim (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD); 12440514051SYongbok Kim for (i = 0; i < gictimer->num_vps; i++) { 12540514051SYongbok Kim timer_del(gictimer->vptimers[i].qtimer); 12640514051SYongbok Kim } 12740514051SYongbok Kim } 12840514051SYongbok Kim 12940514051SYongbok Kim MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps, 13040514051SYongbok Kim MIPSGICTimerCB *cb) 13140514051SYongbok Kim { 13240514051SYongbok Kim int i; 13340514051SYongbok Kim MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1); 13440514051SYongbok Kim gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps); 13540514051SYongbok Kim gictimer->countstop = 1; 13640514051SYongbok Kim gictimer->num_vps = nvps; 13740514051SYongbok Kim gictimer->opaque = opaque; 13840514051SYongbok Kim gictimer->cb = cb; 13940514051SYongbok Kim for (i = 0; i < nvps; i++) { 14040514051SYongbok Kim gictimer->vptimers[i].gictimer = gictimer; 14140514051SYongbok Kim gictimer->vptimers[i].vp_index = i; 14240514051SYongbok Kim gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 14340514051SYongbok Kim &gic_vptimer_cb, 14440514051SYongbok Kim &gictimer->vptimers[i]); 14540514051SYongbok Kim } 14640514051SYongbok Kim return gictimer; 14740514051SYongbok Kim } 148