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