1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include "qemu/osdep.h"
24#include "hw/timer/a9gtimer.h"
25#include "qapi/error.h"
26#include "qemu/timer.h"
27#include "qemu/bitops.h"
28#include "qemu/log.h"
29#include "qom/cpu.h"
30
31#include "hw/fdt_generic_util.h"
32#include "hw/fdt_generic_devices.h"
33
34#ifndef A9_GTIMER_ERR_DEBUG
35#define A9_GTIMER_ERR_DEBUG 0
36#endif
37
38#define DB_PRINT_L(level, ...) do { \
39 if (A9_GTIMER_ERR_DEBUG > (level)) { \
40 fprintf(stderr, ": %s: ", __func__); \
41 fprintf(stderr, ## __VA_ARGS__); \
42 } \
43} while (0);
44
45#define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__)
46
47static inline int a9_gtimer_get_current_cpu(A9GTimerState *s)
48{
49 if (current_cpu->cpu_index >= s->num_cpu) {
50 hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n",
51 s->num_cpu, current_cpu->cpu_index);
52 }
53 return current_cpu->cpu_index;
54}
55
56static inline uint64_t a9_gtimer_get_conv_ps(A9GTimerState *s)
57{
58 uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
59 R_CONTROL_PRESCALER_LEN);
60
61 return (prescale + 1) * 1000000000000ull / s->freq_hz;
62}
63
64static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
65{
66 A9GTimerUpdate ret;
67
68 ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
69 ret.new = s->ref_counter +
70 1000ull * (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv_ps(s);
71 return ret;
72}
73
74static void a9_gtimer_update(A9GTimerState *s, bool sync)
75{
76
77 A9GTimerUpdate update = a9_gtimer_get_update(s);
78 int i;
79 int64_t next_cdiff = 0;
80
81 for (i = 0; i < s->num_cpu; ++i) {
82 A9GTimerPerCPU *gtb = &s->per_cpu[i];
83 int64_t cdiff = 0;
84
85 if ((s->control & R_CONTROL_TIMER_ENABLE) &&
86 (gtb->control & R_CONTROL_COMP_ENABLE)) {
87
88 if (gtb->compare < update.new) {
89 DB_PRINT("Compare event happened for CPU %d\n", i);
90 gtb->status = 1;
91 if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
92 uint64_t inc =
93 QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
94 DB_PRINT("Auto incrementing timer compare by %"
95 PRId64 "\n", inc);
96 gtb->compare += inc;
97 }
98 }
99 cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
100 if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) {
101 next_cdiff = cdiff;
102 }
103 }
104
105 qemu_set_irq(gtb->irq,
106 gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE));
107 }
108
109 timer_del(s->timer);
110 if (next_cdiff) {
111 DB_PRINT("scheduling qemu_timer to fire again in %"
112 PRIx64 " cycles\n", next_cdiff);
113 timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv_ps(s)
114 / 1000ull);
115 }
116
117 if (s->control & R_CONTROL_TIMER_ENABLE) {
118 s->counter = update.new;
119 }
120
121 if (sync) {
122 s->cpu_ref_time = update.now;
123 s->ref_counter = s->counter;
124 }
125}
126
127static void a9_gtimer_update_no_sync(void *opaque)
128{
129 A9GTimerState *s = A9_GTIMER(opaque);
130
131 a9_gtimer_update(s, false);
132}
133
134static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size)
135{
136 A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
137 A9GTimerState *s = gtb->parent;
138 A9GTimerUpdate update;
139 uint64_t ret = 0;
140 int shift = 0;
141
142 switch (addr) {
143 case R_COUNTER_HI:
144 shift = 32;
145
146 case R_COUNTER_LO:
147 update = a9_gtimer_get_update(s);
148 ret = extract64(update.new, shift, 32);
149 break;
150 case R_CONTROL:
151 ret = s->control | gtb->control;
152 break;
153 case R_INTERRUPT_STATUS:
154 ret = gtb->status;
155 break;
156 case R_COMPARATOR_HI:
157 shift = 32;
158
159 case R_COMPARATOR_LO:
160 ret = extract64(gtb->compare, shift, 32);
161 break;
162 case R_AUTO_INCREMENT:
163 ret = gtb->inc;
164 break;
165 default:
166 qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n",
167 (unsigned)addr);
168 return 0;
169 }
170
171 DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret);
172 return ret;
173}
174
175static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value,
176 unsigned size)
177{
178 A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
179 A9GTimerState *s = gtb->parent;
180 int shift = 0;
181
182 DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value);
183
184 switch (addr) {
185 case R_COUNTER_HI:
186 shift = 32;
187
188 case R_COUNTER_LO:
189
190
191
192
193
194 if (s->control & R_CONTROL_TIMER_ENABLE) {
195 qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n");
196 return;
197 }
198 s->counter = deposit64(s->counter, shift, 32, value);
199 return;
200 case R_CONTROL:
201 a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC);
202 gtb->control = value & R_CONTROL_BANKED;
203 s->control = value & ~R_CONTROL_BANKED;
204 break;
205 case R_INTERRUPT_STATUS:
206 a9_gtimer_update(s, false);
207 gtb->status &= ~value;
208 break;
209 case R_COMPARATOR_HI:
210 shift = 32;
211
212 case R_COMPARATOR_LO:
213 a9_gtimer_update(s, false);
214 gtb->compare = deposit64(gtb->compare, shift, 32, value);
215 break;
216 case R_AUTO_INCREMENT:
217 gtb->inc = value;
218 return;
219 default:
220 return;
221 }
222
223 a9_gtimer_update(s, false);
224}
225
226
227
228
229static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr,
230 unsigned size)
231{
232 A9GTimerState *s = A9_GTIMER(opaque);
233 int id = a9_gtimer_get_current_cpu(s);
234
235
236 DB_PRINT("CPU:%d:", id);
237
238 return a9_gtimer_read(&s->per_cpu[id], addr, size);
239}
240
241static void a9_gtimer_this_write(void *opaque, hwaddr addr,
242 uint64_t value, unsigned size)
243{
244 A9GTimerState *s = A9_GTIMER(opaque);
245 int id = a9_gtimer_get_current_cpu(s);
246
247
248 DB_PRINT("CPU:%d:", id);
249
250 a9_gtimer_write(&s->per_cpu[id], addr, value, size);
251}
252
253static const MemoryRegionOps a9_gtimer_this_ops = {
254 .read = a9_gtimer_this_read,
255 .write = a9_gtimer_this_write,
256 .valid = {
257 .min_access_size = 4,
258 .max_access_size = 4,
259 },
260 .endianness = DEVICE_NATIVE_ENDIAN,
261};
262
263static const MemoryRegionOps a9_gtimer_ops = {
264 .read = a9_gtimer_read,
265 .write = a9_gtimer_write,
266 .valid = {
267 .min_access_size = 4,
268 .max_access_size = 4,
269 },
270 .endianness = DEVICE_NATIVE_ENDIAN,
271};
272
273static void a9_gtimer_clock_handler(void *opaque, int n, int level)
274{
275 A9GTimerState *s = A9_GTIMER(opaque);
276
277 assert(n == 0);
278 s->freq_hz = level;
279}
280
281static void a9_gtimer_reset(DeviceState *dev)
282{
283 A9GTimerState *s = A9_GTIMER(dev);
284 int i;
285
286 s->counter = 0;
287 s->control = 0;
288
289 for (i = 0; i < s->num_cpu; i++) {
290 A9GTimerPerCPU *gtb = &s->per_cpu[i];
291
292 gtb->control = 0;
293 gtb->status = 0;
294 gtb->compare = 0;
295 gtb->inc = 0;
296 }
297 a9_gtimer_update(s, false);
298}
299
300static void a9_gtimer_realize(DeviceState *dev, Error **errp)
301{
302 A9GTimerState *s = A9_GTIMER(dev);
303 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
304 int i;
305
306 if (!s->num_cpu) {
307 s->num_cpu = fdt_generic_num_cpus;
308 }
309 if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) {
310 error_setg(errp, "%s: num-cpu must be between 1 and %d",
311 __func__, A9_GTIMER_MAX_CPUS);
312 return;
313 }
314
315 memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s,
316 "a9gtimer shared", 0x20);
317 sysbus_init_mmio(sbd, &s->iomem);
318 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s);
319
320 for (i = 0; i < s->num_cpu; i++) {
321 A9GTimerPerCPU *gtb = &s->per_cpu[i];
322
323 gtb->parent = s;
324 sysbus_init_irq(sbd, >b->irq);
325 memory_region_init_io(>b->iomem, OBJECT(dev), &a9_gtimer_ops, gtb,
326 "a9gtimer per cpu", 0x20);
327 sysbus_init_mmio(sbd, >b->iomem);
328 }
329
330 qdev_init_gpio_in_named(dev, a9_gtimer_clock_handler, "clock", 1);
331}
332
333static const VMStateDescription vmstate_a9_gtimer_per_cpu = {
334 .name = "arm.cortex-a9-global-timer.percpu",
335 .version_id = 1,
336 .minimum_version_id = 1,
337 .fields = (VMStateField[]) {
338 VMSTATE_UINT32(control, A9GTimerPerCPU),
339 VMSTATE_UINT64(compare, A9GTimerPerCPU),
340 VMSTATE_UINT32(status, A9GTimerPerCPU),
341 VMSTATE_UINT32(inc, A9GTimerPerCPU),
342 VMSTATE_END_OF_LIST()
343 }
344};
345
346static const VMStateDescription vmstate_a9_gtimer = {
347 .name = "arm.cortex-a9-global-timer",
348 .version_id = 1,
349 .minimum_version_id = 1,
350 .fields = (VMStateField[]) {
351 VMSTATE_TIMER_PTR(timer, A9GTimerState),
352 VMSTATE_UINT64(counter, A9GTimerState),
353 VMSTATE_UINT64(ref_counter, A9GTimerState),
354 VMSTATE_UINT64(cpu_ref_time, A9GTimerState),
355 VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu,
356 1, vmstate_a9_gtimer_per_cpu,
357 A9GTimerPerCPU),
358 VMSTATE_END_OF_LIST()
359 }
360};
361
362static const FDTGenericGPIOSet a9_gtimer_client_gpios [] = {
363 {
364 .names = &fdt_generic_gpio_name_set_clock,
365 .gpios = (FDTGenericGPIOConnection []) {
366 { . name = "clock", .fdt_index = 0 },
367 { },
368 },
369 },
370 { },
371};
372
373static Property a9_gtimer_properties[] = {
374 DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0),
375 DEFINE_PROP_UINT32("clock-frequency", A9GTimerState, freq_hz, 100000000),
376 DEFINE_PROP_END_OF_LIST()
377};
378
379static void a9_gtimer_class_init(ObjectClass *klass, void *data)
380{
381 DeviceClass *dc = DEVICE_CLASS(klass);
382 FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);
383
384 dc->realize = a9_gtimer_realize;
385 dc->vmsd = &vmstate_a9_gtimer;
386 dc->reset = a9_gtimer_reset;
387 dc->props = a9_gtimer_properties;
388 fggc->client_gpios = a9_gtimer_client_gpios;
389}
390
391static const TypeInfo a9_gtimer_info = {
392 .name = TYPE_A9_GTIMER,
393 .parent = TYPE_SYS_BUS_DEVICE,
394 .instance_size = sizeof(A9GTimerState),
395 .class_init = a9_gtimer_class_init,
396 .interfaces = (InterfaceInfo[]) {
397 { TYPE_FDT_GENERIC_GPIO },
398 { },
399 },
400};
401
402static void a9_gtimer_register_types(void)
403{
404 type_register_static(&a9_gtimer_info);
405}
406
407type_init(a9_gtimer_register_types)
408