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