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