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