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