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