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