1 /* 2 * General purpose implementation of a simple periodic countdown timer. 3 * 4 * Copyright (c) 2007 CodeSourcery. 5 * 6 * This code is licensed under the GNU LGPL. 7 */ 8 #include "hw/hw.h" 9 #include "qemu/timer.h" 10 #include "hw/ptimer.h" 11 #include "qemu/host-utils.h" 12 #include "sysemu/replay.h" 13 14 struct ptimer_state 15 { 16 uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ 17 uint64_t limit; 18 uint64_t delta; 19 uint32_t period_frac; 20 int64_t period; 21 int64_t last_event; 22 int64_t next_event; 23 QEMUBH *bh; 24 QEMUTimer *timer; 25 }; 26 27 /* Use a bottom-half routine to avoid reentrancy issues. */ 28 static void ptimer_trigger(ptimer_state *s) 29 { 30 if (s->bh) { 31 replay_bh_schedule_event(s->bh); 32 } 33 } 34 35 static void ptimer_reload(ptimer_state *s) 36 { 37 if (s->delta == 0) { 38 ptimer_trigger(s); 39 s->delta = s->limit; 40 } 41 if (s->delta == 0 || s->period == 0) { 42 fprintf(stderr, "Timer with period zero, disabling\n"); 43 s->enabled = 0; 44 return; 45 } 46 47 s->last_event = s->next_event; 48 s->next_event = s->last_event + s->delta * s->period; 49 if (s->period_frac) { 50 s->next_event += ((int64_t)s->period_frac * s->delta) >> 32; 51 } 52 timer_mod(s->timer, s->next_event); 53 } 54 55 static void ptimer_tick(void *opaque) 56 { 57 ptimer_state *s = (ptimer_state *)opaque; 58 ptimer_trigger(s); 59 s->delta = 0; 60 if (s->enabled == 2) { 61 s->enabled = 0; 62 } else { 63 ptimer_reload(s); 64 } 65 } 66 67 uint64_t ptimer_get_count(ptimer_state *s) 68 { 69 int64_t now; 70 uint64_t counter; 71 72 if (s->enabled) { 73 now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 74 /* Figure out the current counter value. */ 75 if (now - s->next_event > 0 76 || s->period == 0) { 77 /* Prevent timer underflowing if it should already have 78 triggered. */ 79 counter = 0; 80 } else { 81 uint64_t rem; 82 uint64_t div; 83 int clz1, clz2; 84 int shift; 85 86 /* We need to divide time by period, where time is stored in 87 rem (64-bit integer) and period is stored in period/period_frac 88 (64.32 fixed point). 89 90 Doing full precision division is hard, so scale values and 91 do a 64-bit division. The result should be rounded down, 92 so that the rounding error never causes the timer to go 93 backwards. 94 */ 95 96 rem = s->next_event - now; 97 div = s->period; 98 99 clz1 = clz64(rem); 100 clz2 = clz64(div); 101 shift = clz1 < clz2 ? clz1 : clz2; 102 103 rem <<= shift; 104 div <<= shift; 105 if (shift >= 32) { 106 div |= ((uint64_t)s->period_frac << (shift - 32)); 107 } else { 108 if (shift != 0) 109 div |= (s->period_frac >> (32 - shift)); 110 /* Look at remaining bits of period_frac and round div up if 111 necessary. */ 112 if ((uint32_t)(s->period_frac << shift)) 113 div += 1; 114 } 115 counter = rem / div; 116 } 117 } else { 118 counter = s->delta; 119 } 120 return counter; 121 } 122 123 void ptimer_set_count(ptimer_state *s, uint64_t count) 124 { 125 s->delta = count; 126 if (s->enabled) { 127 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 128 ptimer_reload(s); 129 } 130 } 131 132 void ptimer_run(ptimer_state *s, int oneshot) 133 { 134 if (s->enabled) { 135 return; 136 } 137 if (s->period == 0) { 138 fprintf(stderr, "Timer with period zero, disabling\n"); 139 return; 140 } 141 s->enabled = oneshot ? 2 : 1; 142 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 143 ptimer_reload(s); 144 } 145 146 /* Pause a timer. Note that this may cause it to "lose" time, even if it 147 is immediately restarted. */ 148 void ptimer_stop(ptimer_state *s) 149 { 150 if (!s->enabled) 151 return; 152 153 s->delta = ptimer_get_count(s); 154 timer_del(s->timer); 155 s->enabled = 0; 156 } 157 158 /* Set counter increment interval in nanoseconds. */ 159 void ptimer_set_period(ptimer_state *s, int64_t period) 160 { 161 s->period = period; 162 s->period_frac = 0; 163 if (s->enabled) { 164 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 165 ptimer_reload(s); 166 } 167 } 168 169 /* Set counter frequency in Hz. */ 170 void ptimer_set_freq(ptimer_state *s, uint32_t freq) 171 { 172 s->period = 1000000000ll / freq; 173 s->period_frac = (1000000000ll << 32) / freq; 174 if (s->enabled) { 175 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 176 ptimer_reload(s); 177 } 178 } 179 180 /* Set the initial countdown value. If reload is nonzero then also set 181 count = limit. */ 182 void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) 183 { 184 /* 185 * Artificially limit timeout rate to something 186 * achievable under QEMU. Otherwise, QEMU spends all 187 * its time generating timer interrupts, and there 188 * is no forward progress. 189 * About ten microseconds is the fastest that really works 190 * on the current generation of host machines. 191 */ 192 193 if (!use_icount && limit * s->period < 10000 && s->period) { 194 limit = 10000 / s->period; 195 } 196 197 s->limit = limit; 198 if (reload) 199 s->delta = limit; 200 if (s->enabled && reload) { 201 s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 202 ptimer_reload(s); 203 } 204 } 205 206 const VMStateDescription vmstate_ptimer = { 207 .name = "ptimer", 208 .version_id = 1, 209 .minimum_version_id = 1, 210 .fields = (VMStateField[]) { 211 VMSTATE_UINT8(enabled, ptimer_state), 212 VMSTATE_UINT64(limit, ptimer_state), 213 VMSTATE_UINT64(delta, ptimer_state), 214 VMSTATE_UINT32(period_frac, ptimer_state), 215 VMSTATE_INT64(period, ptimer_state), 216 VMSTATE_INT64(last_event, ptimer_state), 217 VMSTATE_INT64(next_event, ptimer_state), 218 VMSTATE_TIMER_PTR(timer, ptimer_state), 219 VMSTATE_END_OF_LIST() 220 } 221 }; 222 223 ptimer_state *ptimer_init(QEMUBH *bh) 224 { 225 ptimer_state *s; 226 227 s = (ptimer_state *)g_malloc0(sizeof(ptimer_state)); 228 s->bh = bh; 229 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); 230 return s; 231 } 232