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 15 uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic) 16 { 17 return NANOSECONDS_PER_SECOND / TIMER_PERIOD; 18 } 19 20 static 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 33 static 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 44 static 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 52 uint32_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 71 void 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 89 uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer, 90 uint32_t vp_index) 91 { 92 return gictimer->vptimers[vp_index].comparelo; 93 } 94 95 void 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 103 uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer) 104 { 105 return gictimer->countstop; 106 } 107 108 void mips_gictimer_start_count(MIPSGICTimerState *gictimer) 109 { 110 gictimer->countstop = 0; 111 mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo); 112 } 113 114 void 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 127 MIPSGICTimerState *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 } 146