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