xref: /qemu/hw/timer/cadence_ttc.c (revision 64552b6b)
1 /*
2  * Xilinx Zynq cadence TTC model
3  *
4  * Copyright (c) 2011 Xilinx Inc.
5  * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
6  * Copyright (c) 2012 PetaLogix Pty Ltd.
7  * Written By Haibing Ma
8  *            M. Habib
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version
13  * 2 of the License, or (at your option) any later version.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "hw/irq.h"
21 #include "hw/sysbus.h"
22 #include "qemu/module.h"
23 #include "qemu/timer.h"
24 
25 #ifdef CADENCE_TTC_ERR_DEBUG
26 #define DB_PRINT(...) do { \
27     fprintf(stderr,  ": %s: ", __func__); \
28     fprintf(stderr, ## __VA_ARGS__); \
29     } while (0)
30 #else
31     #define DB_PRINT(...)
32 #endif
33 
34 #define COUNTER_INTR_IV     0x00000001
35 #define COUNTER_INTR_M1     0x00000002
36 #define COUNTER_INTR_M2     0x00000004
37 #define COUNTER_INTR_M3     0x00000008
38 #define COUNTER_INTR_OV     0x00000010
39 #define COUNTER_INTR_EV     0x00000020
40 
41 #define COUNTER_CTRL_DIS    0x00000001
42 #define COUNTER_CTRL_INT    0x00000002
43 #define COUNTER_CTRL_DEC    0x00000004
44 #define COUNTER_CTRL_MATCH  0x00000008
45 #define COUNTER_CTRL_RST    0x00000010
46 
47 #define CLOCK_CTRL_PS_EN    0x00000001
48 #define CLOCK_CTRL_PS_V     0x0000001e
49 
50 typedef struct {
51     QEMUTimer *timer;
52     int freq;
53 
54     uint32_t reg_clock;
55     uint32_t reg_count;
56     uint32_t reg_value;
57     uint16_t reg_interval;
58     uint16_t reg_match[3];
59     uint32_t reg_intr;
60     uint32_t reg_intr_en;
61     uint32_t reg_event_ctrl;
62     uint32_t reg_event;
63 
64     uint64_t cpu_time;
65     unsigned int cpu_time_valid;
66 
67     qemu_irq irq;
68 } CadenceTimerState;
69 
70 #define TYPE_CADENCE_TTC "cadence_ttc"
71 #define CADENCE_TTC(obj) \
72     OBJECT_CHECK(CadenceTTCState, (obj), TYPE_CADENCE_TTC)
73 
74 typedef struct CadenceTTCState {
75     SysBusDevice parent_obj;
76 
77     MemoryRegion iomem;
78     CadenceTimerState timer[3];
79 } CadenceTTCState;
80 
81 static void cadence_timer_update(CadenceTimerState *s)
82 {
83     qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
84 }
85 
86 static CadenceTimerState *cadence_timer_from_addr(void *opaque,
87                                         hwaddr offset)
88 {
89     unsigned int index;
90     CadenceTTCState *s = (CadenceTTCState *)opaque;
91 
92     index = (offset >> 2) % 3;
93 
94     return &s->timer[index];
95 }
96 
97 static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
98 {
99     /* timer_steps has max value of 0x100000000. double check it
100      * (or overflow can happen below) */
101     assert(timer_steps <= 1ULL << 32);
102 
103     uint64_t r = timer_steps * 1000000000ULL;
104     if (s->reg_clock & CLOCK_CTRL_PS_EN) {
105         r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
106     } else {
107         r >>= 16;
108     }
109     r /= (uint64_t)s->freq;
110     return r;
111 }
112 
113 static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
114 {
115     uint64_t to_divide = 1000000000ULL;
116 
117     uint64_t r = ns;
118      /* for very large intervals (> 8s) do some division first to stop
119       * overflow (costs some prescision) */
120     while (r >= 8ULL << 30 && to_divide > 1) {
121         r /= 1000;
122         to_divide /= 1000;
123     }
124     r <<= 16;
125     /* keep early-dividing as needed */
126     while (r >= 8ULL << 30 && to_divide > 1) {
127         r /= 1000;
128         to_divide /= 1000;
129     }
130     r *= (uint64_t)s->freq;
131     if (s->reg_clock & CLOCK_CTRL_PS_EN) {
132         r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
133     }
134 
135     r /= to_divide;
136     return r;
137 }
138 
139 /* determine if x is in between a and b, exclusive of a, inclusive of b */
140 
141 static inline int64_t is_between(int64_t x, int64_t a, int64_t b)
142 {
143     if (a < b) {
144         return x > a && x <= b;
145     }
146     return x < a && x >= b;
147 }
148 
149 static void cadence_timer_run(CadenceTimerState *s)
150 {
151     int i;
152     int64_t event_interval, next_value;
153 
154     assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
155 
156     if (s->reg_count & COUNTER_CTRL_DIS) {
157         s->cpu_time_valid = 0;
158         return;
159     }
160 
161     { /* figure out what's going to happen next (rollover or match) */
162         int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ?
163                 (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
164         next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
165         for (i = 0; i < 3; ++i) {
166             int64_t cand = (uint64_t)s->reg_match[i] << 16;
167             if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
168                 next_value = cand;
169             }
170         }
171     }
172     DB_PRINT("next timer event value: %09llx\n",
173             (unsigned long long)next_value);
174 
175     event_interval = next_value - (int64_t)s->reg_value;
176     event_interval = (event_interval < 0) ? -event_interval : event_interval;
177 
178     timer_mod(s->timer, s->cpu_time +
179                 cadence_timer_get_ns(s, event_interval));
180 }
181 
182 static void cadence_timer_sync(CadenceTimerState *s)
183 {
184     int i;
185     int64_t r, x;
186     int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ?
187             (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
188     uint64_t old_time = s->cpu_time;
189 
190     s->cpu_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
191     DB_PRINT("cpu time: %lld ns\n", (long long)old_time);
192 
193     if (!s->cpu_time_valid || old_time == s->cpu_time) {
194         s->cpu_time_valid = 1;
195         return;
196     }
197 
198     r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time);
199     x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
200 
201     for (i = 0; i < 3; ++i) {
202         int64_t m = (int64_t)s->reg_match[i] << 16;
203         if (m > interval) {
204             continue;
205         }
206         /* check to see if match event has occurred. check m +/- interval
207          * to account for match events in wrap around cases */
208         if (is_between(m, s->reg_value, x) ||
209             is_between(m + interval, s->reg_value, x) ||
210             is_between(m - interval, s->reg_value, x)) {
211             s->reg_intr |= (2 << i);
212         }
213     }
214     if ((x < 0) || (x >= interval)) {
215         s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
216             COUNTER_INTR_IV : COUNTER_INTR_OV;
217     }
218     while (x < 0) {
219         x += interval;
220     }
221     s->reg_value = (uint32_t)(x % interval);
222     cadence_timer_update(s);
223 }
224 
225 static void cadence_timer_tick(void *opaque)
226 {
227     CadenceTimerState *s = opaque;
228 
229     DB_PRINT("\n");
230     cadence_timer_sync(s);
231     cadence_timer_run(s);
232 }
233 
234 static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset)
235 {
236     CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
237     uint32_t value;
238 
239     cadence_timer_sync(s);
240     cadence_timer_run(s);
241 
242     switch (offset) {
243     case 0x00: /* clock control */
244     case 0x04:
245     case 0x08:
246         return s->reg_clock;
247 
248     case 0x0c: /* counter control */
249     case 0x10:
250     case 0x14:
251         return s->reg_count;
252 
253     case 0x18: /* counter value */
254     case 0x1c:
255     case 0x20:
256         return (uint16_t)(s->reg_value >> 16);
257 
258     case 0x24: /* reg_interval counter */
259     case 0x28:
260     case 0x2c:
261         return s->reg_interval;
262 
263     case 0x30: /* match 1 counter */
264     case 0x34:
265     case 0x38:
266         return s->reg_match[0];
267 
268     case 0x3c: /* match 2 counter */
269     case 0x40:
270     case 0x44:
271         return s->reg_match[1];
272 
273     case 0x48: /* match 3 counter */
274     case 0x4c:
275     case 0x50:
276         return s->reg_match[2];
277 
278     case 0x54: /* interrupt register */
279     case 0x58:
280     case 0x5c:
281         /* cleared after read */
282         value = s->reg_intr;
283         s->reg_intr = 0;
284         cadence_timer_update(s);
285         return value;
286 
287     case 0x60: /* interrupt enable */
288     case 0x64:
289     case 0x68:
290         return s->reg_intr_en;
291 
292     case 0x6c:
293     case 0x70:
294     case 0x74:
295         return s->reg_event_ctrl;
296 
297     case 0x78:
298     case 0x7c:
299     case 0x80:
300         return s->reg_event;
301 
302     default:
303         return 0;
304     }
305 }
306 
307 static uint64_t cadence_ttc_read(void *opaque, hwaddr offset,
308     unsigned size)
309 {
310     uint32_t ret = cadence_ttc_read_imp(opaque, offset);
311 
312     DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret);
313     return ret;
314 }
315 
316 static void cadence_ttc_write(void *opaque, hwaddr offset,
317         uint64_t value, unsigned size)
318 {
319     CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
320 
321     DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value);
322 
323     cadence_timer_sync(s);
324 
325     switch (offset) {
326     case 0x00: /* clock control */
327     case 0x04:
328     case 0x08:
329         s->reg_clock = value & 0x3F;
330         break;
331 
332     case 0x0c: /* counter control */
333     case 0x10:
334     case 0x14:
335         if (value & COUNTER_CTRL_RST) {
336             s->reg_value = 0;
337         }
338         s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
339         break;
340 
341     case 0x24: /* interval register */
342     case 0x28:
343     case 0x2c:
344         s->reg_interval = value & 0xffff;
345         break;
346 
347     case 0x30: /* match register */
348     case 0x34:
349     case 0x38:
350         s->reg_match[0] = value & 0xffff;
351         break;
352 
353     case 0x3c: /* match register */
354     case 0x40:
355     case 0x44:
356         s->reg_match[1] = value & 0xffff;
357         break;
358 
359     case 0x48: /* match register */
360     case 0x4c:
361     case 0x50:
362         s->reg_match[2] = value & 0xffff;
363         break;
364 
365     case 0x54: /* interrupt register */
366     case 0x58:
367     case 0x5c:
368         break;
369 
370     case 0x60: /* interrupt enable */
371     case 0x64:
372     case 0x68:
373         s->reg_intr_en = value & 0x3f;
374         break;
375 
376     case 0x6c: /* event control */
377     case 0x70:
378     case 0x74:
379         s->reg_event_ctrl = value & 0x07;
380         break;
381 
382     default:
383         return;
384     }
385 
386     cadence_timer_run(s);
387     cadence_timer_update(s);
388 }
389 
390 static const MemoryRegionOps cadence_ttc_ops = {
391     .read = cadence_ttc_read,
392     .write = cadence_ttc_write,
393     .endianness = DEVICE_NATIVE_ENDIAN,
394 };
395 
396 static void cadence_timer_reset(CadenceTimerState *s)
397 {
398    s->reg_count = 0x21;
399 }
400 
401 static void cadence_timer_init(uint32_t freq, CadenceTimerState *s)
402 {
403     memset(s, 0, sizeof(CadenceTimerState));
404     s->freq = freq;
405 
406     cadence_timer_reset(s);
407 
408     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cadence_timer_tick, s);
409 }
410 
411 static void cadence_ttc_init(Object *obj)
412 {
413     CadenceTTCState *s = CADENCE_TTC(obj);
414     int i;
415 
416     for (i = 0; i < 3; ++i) {
417         cadence_timer_init(133000000, &s->timer[i]);
418         sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->timer[i].irq);
419     }
420 
421     memory_region_init_io(&s->iomem, obj, &cadence_ttc_ops, s,
422                           "timer", 0x1000);
423     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
424 }
425 
426 static int cadence_timer_pre_save(void *opaque)
427 {
428     cadence_timer_sync((CadenceTimerState *)opaque);
429 
430     return 0;
431 }
432 
433 static int cadence_timer_post_load(void *opaque, int version_id)
434 {
435     CadenceTimerState *s = opaque;
436 
437     s->cpu_time_valid = 0;
438     cadence_timer_sync(s);
439     cadence_timer_run(s);
440     cadence_timer_update(s);
441     return 0;
442 }
443 
444 static const VMStateDescription vmstate_cadence_timer = {
445     .name = "cadence_timer",
446     .version_id = 1,
447     .minimum_version_id = 1,
448     .pre_save = cadence_timer_pre_save,
449     .post_load = cadence_timer_post_load,
450     .fields = (VMStateField[]) {
451         VMSTATE_UINT32(reg_clock, CadenceTimerState),
452         VMSTATE_UINT32(reg_count, CadenceTimerState),
453         VMSTATE_UINT32(reg_value, CadenceTimerState),
454         VMSTATE_UINT16(reg_interval, CadenceTimerState),
455         VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
456         VMSTATE_UINT32(reg_intr, CadenceTimerState),
457         VMSTATE_UINT32(reg_intr_en, CadenceTimerState),
458         VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState),
459         VMSTATE_UINT32(reg_event, CadenceTimerState),
460         VMSTATE_END_OF_LIST()
461     }
462 };
463 
464 static const VMStateDescription vmstate_cadence_ttc = {
465     .name = "cadence_TTC",
466     .version_id = 1,
467     .minimum_version_id = 1,
468     .fields = (VMStateField[]) {
469         VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0,
470                             vmstate_cadence_timer,
471                             CadenceTimerState),
472         VMSTATE_END_OF_LIST()
473     }
474 };
475 
476 static void cadence_ttc_class_init(ObjectClass *klass, void *data)
477 {
478     DeviceClass *dc = DEVICE_CLASS(klass);
479 
480     dc->vmsd = &vmstate_cadence_ttc;
481 }
482 
483 static const TypeInfo cadence_ttc_info = {
484     .name  = TYPE_CADENCE_TTC,
485     .parent = TYPE_SYS_BUS_DEVICE,
486     .instance_size  = sizeof(CadenceTTCState),
487     .instance_init = cadence_ttc_init,
488     .class_init = cadence_ttc_class_init,
489 };
490 
491 static void cadence_ttc_register_types(void)
492 {
493     type_register_static(&cadence_ttc_info);
494 }
495 
496 type_init(cadence_ttc_register_types)
497