]> git.proxmox.com Git - mirror_qemu.git/blame - hw/timer/exynos4210_mct.c
Include qemu/module.h where needed, drop it from qemu-common.h
[mirror_qemu.git] / hw / timer / exynos4210_mct.c
CommitLineData
12c775db
EV
1/*
2 * Samsung exynos4210 Multi Core timer
3 *
4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
5 * All rights reserved.
6 *
7 * Evgeny Voevodin <e.voevodin@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * Global Timer:
25 *
26 * Consists of two timers. First represents Free Running Counter and second
27 * is used to measure interval from FRC to nearest comparator.
28 *
29 * 0 UINT64_MAX
30 * | timer0 |
31 * | <-------------------------------------------------------------- |
32 * | --------------------------------------------frc---------------> |
33 * |______________________________________________|__________________|
34 * CMP0 CMP1 CMP2 | CMP3
35 * __| |_
36 * | timer1 |
37 * | -------------> |
38 * frc CMPx
39 *
40 * Problem: when implementing global timer as is, overflow arises.
41 * next_time = cur_time + period * count;
42 * period and count are 64 bits width.
43 * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT
44 * register during each event.
45 *
46 * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because
47 * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--.
48 * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0
49 * generates IRQs suffers from too frequently events. Better to have one
50 * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT,
51 * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values,
52 * there is no way to avoid frequently events).
53 */
54
8ef94f0b 55#include "qemu/osdep.h"
f2ad5140 56#include "qemu/log.h"
83c9f4ca 57#include "hw/sysbus.h"
1de7afc9 58#include "qemu/timer.h"
6a1751b7 59#include "qemu/main-loop.h"
0b8fa32f 60#include "qemu/module.h"
83c9f4ca 61#include "hw/ptimer.h"
12c775db 62
0d09e41a 63#include "hw/arm/exynos4210.h"
12c775db
EV
64
65//#define DEBUG_MCT
66
67#ifdef DEBUG_MCT
68#define DPRINTF(fmt, ...) \
69 do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \
70 ## __VA_ARGS__); } while (0)
71#else
72#define DPRINTF(fmt, ...) do {} while (0)
73#endif
74
75#define MCT_CFG 0x000
76#define G_CNT_L 0x100
77#define G_CNT_U 0x104
78#define G_CNT_WSTAT 0x110
79#define G_COMP0_L 0x200
80#define G_COMP0_U 0x204
81#define G_COMP0_ADD_INCR 0x208
82#define G_COMP1_L 0x210
83#define G_COMP1_U 0x214
84#define G_COMP1_ADD_INCR 0x218
85#define G_COMP2_L 0x220
86#define G_COMP2_U 0x224
87#define G_COMP2_ADD_INCR 0x228
88#define G_COMP3_L 0x230
89#define G_COMP3_U 0x234
90#define G_COMP3_ADD_INCR 0x238
91#define G_TCON 0x240
92#define G_INT_CSTAT 0x244
93#define G_INT_ENB 0x248
94#define G_WSTAT 0x24C
95#define L0_TCNTB 0x300
96#define L0_TCNTO 0x304
97#define L0_ICNTB 0x308
98#define L0_ICNTO 0x30C
99#define L0_FRCNTB 0x310
100#define L0_FRCNTO 0x314
101#define L0_TCON 0x320
102#define L0_INT_CSTAT 0x330
103#define L0_INT_ENB 0x334
104#define L0_WSTAT 0x340
105#define L1_TCNTB 0x400
106#define L1_TCNTO 0x404
107#define L1_ICNTB 0x408
108#define L1_ICNTO 0x40C
109#define L1_FRCNTB 0x410
110#define L1_FRCNTO 0x414
111#define L1_TCON 0x420
112#define L1_INT_CSTAT 0x430
113#define L1_INT_ENB 0x434
114#define L1_WSTAT 0x440
115
116#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF)
117#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7))
118
119#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10)
120#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10)
121
122#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10)
123#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10)
124
125#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10)
126
127/* MCT bits */
128#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x))
129#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1))
130#define G_TCON_TIMER_ENABLE (1 << 8)
131
132#define G_INT_ENABLE(x) (1 << (x))
133#define G_INT_CSTAT_COMP(x) (1 << (x))
134
135#define G_CNT_WSTAT_L 1
136#define G_CNT_WSTAT_U 2
137
138#define G_WSTAT_COMP_L(x) (1 << 4 * (x))
139#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1))
140#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2))
141#define G_WSTAT_TCON_WRITE (1 << 16)
142
143#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100)
144#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \
145 (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2)
146
147#define L_ICNTB_MANUAL_UPDATE (1 << 31)
148
149#define L_TCON_TICK_START (1)
150#define L_TCON_INT_START (1 << 1)
151#define L_TCON_INTERVAL_MODE (1 << 2)
152#define L_TCON_FRC_START (1 << 3)
153
154#define L_INT_CSTAT_INTCNT (1 << 0)
155#define L_INT_CSTAT_FRCCNT (1 << 1)
156
157#define L_INT_INTENB_ICNTEIE (1 << 0)
158#define L_INT_INTENB_FRCEIE (1 << 1)
159
160#define L_WSTAT_TCNTB_WRITE (1 << 0)
161#define L_WSTAT_ICNTB_WRITE (1 << 1)
162#define L_WSTAT_FRCCNTB_WRITE (1 << 2)
163#define L_WSTAT_TCON_WRITE (1 << 3)
164
165enum LocalTimerRegCntIndexes {
166 L_REG_CNT_TCNTB,
167 L_REG_CNT_TCNTO,
168 L_REG_CNT_ICNTB,
169 L_REG_CNT_ICNTO,
170 L_REG_CNT_FRCCNTB,
171 L_REG_CNT_FRCCNTO,
172
173 L_REG_CNT_AMOUNT
174};
175
12c775db
EV
176#define MCT_SFR_SIZE 0x444
177
178#define MCT_GT_CMP_NUM 4
179
12c775db
EV
180#define MCT_GT_COUNTER_STEP 0x100000000ULL
181#define MCT_LT_COUNTER_STEP 0x100000000ULL
182#define MCT_LT_CNT_LOW_LIMIT 0x100
183
184/* global timer */
185typedef struct {
186 qemu_irq irq[MCT_GT_CMP_NUM];
187
188 struct gregs {
189 uint64_t cnt;
190 uint32_t cnt_wstat;
191 uint32_t tcon;
192 uint32_t int_cstat;
193 uint32_t int_enb;
194 uint32_t wstat;
195 uint64_t comp[MCT_GT_CMP_NUM];
196 uint32_t comp_add_incr[MCT_GT_CMP_NUM];
197 } reg;
198
199 uint64_t count; /* Value FRC was armed with */
200 int32_t curr_comp; /* Current comparator FRC is running to */
201
202 ptimer_state *ptimer_frc; /* FRC timer */
203
204} Exynos4210MCTGT;
205
206/* local timer */
207typedef struct {
208 int id; /* timer id */
209 qemu_irq irq; /* local timer irq */
210
211 struct tick_timer {
212 uint32_t cnt_run; /* cnt timer is running */
213 uint32_t int_run; /* int timer is running */
214
215 uint32_t last_icnto;
216 uint32_t last_tcnto;
217 uint32_t tcntb; /* initial value for TCNTB */
218 uint32_t icntb; /* initial value for ICNTB */
219
220 /* for step mode */
221 uint64_t distance; /* distance to count to the next event */
222 uint64_t progress; /* progress when counting by steps */
223 uint64_t count; /* count to arm timer with */
224
225 ptimer_state *ptimer_tick; /* timer for tick counter */
226 } tick_timer;
227
228 /* use ptimer.c to represent count down timer */
229
230 ptimer_state *ptimer_frc; /* timer for free running counter */
231
232 /* registers */
233 struct lregs {
234 uint32_t cnt[L_REG_CNT_AMOUNT];
235 uint32_t tcon;
236 uint32_t int_cstat;
237 uint32_t int_enb;
238 uint32_t wstat;
239 } reg;
240
241} Exynos4210MCTLT;
242
81e1010d
AF
243#define TYPE_EXYNOS4210_MCT "exynos4210.mct"
244#define EXYNOS4210_MCT(obj) \
245 OBJECT_CHECK(Exynos4210MCTState, (obj), TYPE_EXYNOS4210_MCT)
246
12c775db 247typedef struct Exynos4210MCTState {
81e1010d
AF
248 SysBusDevice parent_obj;
249
12c775db
EV
250 MemoryRegion iomem;
251
252 /* Registers */
253 uint32_t reg_mct_cfg;
254
255 Exynos4210MCTLT l_timer[2];
256 Exynos4210MCTGT g_timer;
257
258 uint32_t freq; /* all timers tick frequency, TCLK */
259} Exynos4210MCTState;
260
261/*** VMState ***/
262static const VMStateDescription vmstate_tick_timer = {
263 .name = "exynos4210.mct.tick_timer",
264 .version_id = 1,
265 .minimum_version_id = 1,
12c775db
EV
266 .fields = (VMStateField[]) {
267 VMSTATE_UINT32(cnt_run, struct tick_timer),
268 VMSTATE_UINT32(int_run, struct tick_timer),
269 VMSTATE_UINT32(last_icnto, struct tick_timer),
270 VMSTATE_UINT32(last_tcnto, struct tick_timer),
271 VMSTATE_UINT32(tcntb, struct tick_timer),
272 VMSTATE_UINT32(icntb, struct tick_timer),
273 VMSTATE_UINT64(distance, struct tick_timer),
274 VMSTATE_UINT64(progress, struct tick_timer),
275 VMSTATE_UINT64(count, struct tick_timer),
276 VMSTATE_PTIMER(ptimer_tick, struct tick_timer),
277 VMSTATE_END_OF_LIST()
278 }
279};
280
281static const VMStateDescription vmstate_lregs = {
282 .name = "exynos4210.mct.lregs",
283 .version_id = 1,
284 .minimum_version_id = 1,
12c775db
EV
285 .fields = (VMStateField[]) {
286 VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT),
287 VMSTATE_UINT32(tcon, struct lregs),
288 VMSTATE_UINT32(int_cstat, struct lregs),
289 VMSTATE_UINT32(int_enb, struct lregs),
290 VMSTATE_UINT32(wstat, struct lregs),
291 VMSTATE_END_OF_LIST()
292 }
293};
294
295static const VMStateDescription vmstate_exynos4210_mct_lt = {
296 .name = "exynos4210.mct.lt",
297 .version_id = 1,
298 .minimum_version_id = 1,
12c775db
EV
299 .fields = (VMStateField[]) {
300 VMSTATE_INT32(id, Exynos4210MCTLT),
301 VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0,
302 vmstate_tick_timer,
303 struct tick_timer),
304 VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT),
305 VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0,
306 vmstate_lregs,
307 struct lregs),
308 VMSTATE_END_OF_LIST()
309 }
310};
311
312static const VMStateDescription vmstate_gregs = {
313 .name = "exynos4210.mct.lregs",
314 .version_id = 1,
315 .minimum_version_id = 1,
12c775db
EV
316 .fields = (VMStateField[]) {
317 VMSTATE_UINT64(cnt, struct gregs),
318 VMSTATE_UINT32(cnt_wstat, struct gregs),
319 VMSTATE_UINT32(tcon, struct gregs),
320 VMSTATE_UINT32(int_cstat, struct gregs),
321 VMSTATE_UINT32(int_enb, struct gregs),
322 VMSTATE_UINT32(wstat, struct gregs),
323 VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM),
324 VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs,
325 MCT_GT_CMP_NUM),
326 VMSTATE_END_OF_LIST()
327 }
328};
329
330static const VMStateDescription vmstate_exynos4210_mct_gt = {
331 .name = "exynos4210.mct.lt",
332 .version_id = 1,
333 .minimum_version_id = 1,
12c775db
EV
334 .fields = (VMStateField[]) {
335 VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs,
336 struct gregs),
337 VMSTATE_UINT64(count, Exynos4210MCTGT),
338 VMSTATE_INT32(curr_comp, Exynos4210MCTGT),
339 VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT),
340 VMSTATE_END_OF_LIST()
341 }
342};
343
344static const VMStateDescription vmstate_exynos4210_mct_state = {
345 .name = "exynos4210.mct",
346 .version_id = 1,
347 .minimum_version_id = 1,
12c775db
EV
348 .fields = (VMStateField[]) {
349 VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState),
350 VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0,
351 vmstate_exynos4210_mct_lt, Exynos4210MCTLT),
352 VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0,
353 vmstate_exynos4210_mct_gt, Exynos4210MCTGT),
354 VMSTATE_UINT32(freq, Exynos4210MCTState),
355 VMSTATE_END_OF_LIST()
356 }
357};
358
359static void exynos4210_mct_update_freq(Exynos4210MCTState *s);
360
361/*
362 * Set counter of FRC global timer.
363 */
364static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
365{
366 s->count = count;
367 DPRINTF("global timer frc set count 0x%llx\n", count);
368 ptimer_set_count(s->ptimer_frc, count);
369}
370
371/*
372 * Get counter of FRC global timer.
373 */
374static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
375{
376 uint64_t count = 0;
377 count = ptimer_get_count(s->ptimer_frc);
12c775db
EV
378 count = s->count - count;
379 return s->reg.cnt + count;
380}
381
382/*
383 * Stop global FRC timer
384 */
385static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
386{
387 DPRINTF("global timer frc stop\n");
388
389 ptimer_stop(s->ptimer_frc);
390}
391
392/*
393 * Start global FRC timer
394 */
395static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
396{
397 DPRINTF("global timer frc start\n");
398
399 ptimer_run(s->ptimer_frc, 1);
400}
401
402/*
403 * Find next nearest Comparator. If current Comparator value equals to other
404 * Comparator value, skip them both
405 */
406static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
407{
408 int res;
409 int i;
410 int enabled;
411 uint64_t min;
412 int min_comp_i;
413 uint64_t gfrc;
414 uint64_t distance;
415 uint64_t distance_min;
416 int comp_i;
417
418 /* get gfrc count */
419 gfrc = exynos4210_gfrc_get_count(&s->g_timer);
420
421 min = UINT64_MAX;
422 distance_min = UINT64_MAX;
423 comp_i = MCT_GT_CMP_NUM;
424 min_comp_i = MCT_GT_CMP_NUM;
425 enabled = 0;
426
427 /* lookup for nearest comparator */
428 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
429
430 if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
431
432 enabled = 1;
433
434 if (s->g_timer.reg.comp[i] > gfrc) {
435 /* Comparator is upper then FRC */
436 distance = s->g_timer.reg.comp[i] - gfrc;
437
438 if (distance <= distance_min) {
439 distance_min = distance;
440 comp_i = i;
441 }
442 } else {
443 /* Comparator is below FRC, find the smallest */
444
445 if (s->g_timer.reg.comp[i] <= min) {
446 min = s->g_timer.reg.comp[i];
447 min_comp_i = i;
448 }
449 }
450 }
451 }
452
453 if (!enabled) {
454 /* All Comparators disabled */
455 res = -1;
456 } else if (comp_i < MCT_GT_CMP_NUM) {
457 /* Found upper Comparator */
458 res = comp_i;
459 } else {
460 /* All Comparators are below or equal to FRC */
461 res = min_comp_i;
462 }
463
464 DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
465 res,
466 s->g_timer.reg.comp[res],
467 distance_min,
468 gfrc);
469
470 return res;
471}
472
473/*
474 * Get distance to nearest Comparator
475 */
476static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
477{
478 if (id == -1) {
479 /* no enabled Comparators, choose max distance */
480 return MCT_GT_COUNTER_STEP;
481 }
482 if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
483 return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
484 } else {
485 return MCT_GT_COUNTER_STEP;
486 }
487}
488
489/*
490 * Restart global FRC timer
491 */
492static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
493{
494 uint64_t distance;
495
496 exynos4210_gfrc_stop(&s->g_timer);
497
498 s->g_timer.curr_comp = exynos4210_gcomp_find(s);
499
500 distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
501
502 if (distance > MCT_GT_COUNTER_STEP || !distance) {
503 distance = MCT_GT_COUNTER_STEP;
504 }
505
506 exynos4210_gfrc_set_count(&s->g_timer, distance);
507 exynos4210_gfrc_start(&s->g_timer);
508}
509
510/*
511 * Raise global timer CMP IRQ
512 */
513static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
514{
515 Exynos4210MCTGT *s = opaque;
516
517 /* If CSTAT is pending and IRQ is enabled */
518 if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
519 (s->reg.int_enb & G_INT_ENABLE(id))) {
520 DPRINTF("gcmp timer[%d] IRQ\n", id);
521 qemu_irq_raise(s->irq[id]);
522 }
523}
524
525/*
526 * Lower global timer CMP IRQ
527 */
528static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
529{
530 Exynos4210MCTGT *s = opaque;
531 qemu_irq_lower(s->irq[id]);
532}
533
534/*
535 * Global timer FRC event handler.
536 * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
537 * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
538 */
539static void exynos4210_gfrc_event(void *opaque)
540{
541 Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
542 int i;
543 uint64_t distance;
544
545 DPRINTF("\n");
546
547 s->g_timer.reg.cnt += s->g_timer.count;
548
549 /* Process all comparators */
550 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
551
552 if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
553 /* reached nearest comparator */
554
555 s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
556
557 /* Auto increment */
558 if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
559 s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
560 }
561
562 /* IRQ */
563 exynos4210_gcomp_raise_irq(&s->g_timer, i);
564 }
565 }
566
567 /* Reload FRC to reach nearest comparator */
568 s->g_timer.curr_comp = exynos4210_gcomp_find(s);
569 distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
97331270 570 if (distance > MCT_GT_COUNTER_STEP || !distance) {
12c775db
EV
571 distance = MCT_GT_COUNTER_STEP;
572 }
573 exynos4210_gfrc_set_count(&s->g_timer, distance);
574
575 exynos4210_gfrc_start(&s->g_timer);
12c775db
EV
576}
577
578/*
579 * Get counter of FRC local timer.
580 */
581static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
582{
583 return ptimer_get_count(s->ptimer_frc);
584}
585
586/*
587 * Set counter of FRC local timer.
588 */
589static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
590{
591 if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
592 ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
593 } else {
594 ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
595 }
596}
597
598/*
599 * Start local FRC timer
600 */
601static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
602{
603 ptimer_run(s->ptimer_frc, 1);
604}
605
606/*
607 * Stop local FRC timer
608 */
609static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
610{
611 ptimer_stop(s->ptimer_frc);
612}
613
614/*
615 * Local timer free running counter tick handler
616 */
617static void exynos4210_lfrc_event(void *opaque)
618{
619 Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
620
621 /* local frc expired */
622
623 DPRINTF("\n");
624
625 s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
626
627 /* update frc counter */
628 exynos4210_lfrc_update_count(s);
629
630 /* raise irq */
631 if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
632 qemu_irq_raise(s->irq);
633 }
634
635 /* we reached here, this means that timer is enabled */
636 exynos4210_lfrc_start(s);
637}
638
639static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
640static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
641static void exynos4210_ltick_recalc_count(struct tick_timer *s);
642
643/*
644 * Action on enabling local tick int timer
645 */
646static void exynos4210_ltick_int_start(struct tick_timer *s)
647{
648 if (!s->int_run) {
649 s->int_run = 1;
650 }
651}
652
653/*
654 * Action on disabling local tick int timer
655 */
656static void exynos4210_ltick_int_stop(struct tick_timer *s)
657{
658 if (s->int_run) {
659 s->last_icnto = exynos4210_ltick_int_get_cnto(s);
660 s->int_run = 0;
661 }
662}
663
664/*
665 * Get count for INT timer
666 */
667static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
668{
669 uint32_t icnto;
670 uint64_t remain;
671 uint64_t count;
672 uint64_t counted;
673 uint64_t cur_progress;
674
675 count = ptimer_get_count(s->ptimer_tick);
676 if (count) {
677 /* timer is still counting, called not from event */
678 counted = s->count - ptimer_get_count(s->ptimer_tick);
679 cur_progress = s->progress + counted;
680 } else {
681 /* timer expired earlier */
682 cur_progress = s->progress;
683 }
684
685 remain = s->distance - cur_progress;
686
687 if (!s->int_run) {
688 /* INT is stopped. */
689 icnto = s->last_icnto;
690 } else {
691 /* Both are counting */
692 icnto = remain / s->tcntb;
693 }
694
695 return icnto;
696}
697
698/*
699 * Start local tick cnt timer.
700 */
701static void exynos4210_ltick_cnt_start(struct tick_timer *s)
702{
703 if (!s->cnt_run) {
704
705 exynos4210_ltick_recalc_count(s);
706 ptimer_set_count(s->ptimer_tick, s->count);
707 ptimer_run(s->ptimer_tick, 1);
708
709 s->cnt_run = 1;
710 }
711}
712
713/*
714 * Stop local tick cnt timer.
715 */
716static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
717{
718 if (s->cnt_run) {
719
720 s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
721
722 if (s->int_run) {
723 exynos4210_ltick_int_stop(s);
724 }
725
726 ptimer_stop(s->ptimer_tick);
727
728 s->cnt_run = 0;
729 }
730}
731
732/*
733 * Get counter for CNT timer
734 */
735static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
736{
737 uint32_t tcnto;
738 uint32_t icnto;
739 uint64_t remain;
740 uint64_t counted;
741 uint64_t count;
742 uint64_t cur_progress;
743
744 count = ptimer_get_count(s->ptimer_tick);
745 if (count) {
746 /* timer is still counting, called not from event */
747 counted = s->count - ptimer_get_count(s->ptimer_tick);
748 cur_progress = s->progress + counted;
749 } else {
750 /* timer expired earlier */
751 cur_progress = s->progress;
752 }
753
754 remain = s->distance - cur_progress;
755
756 if (!s->cnt_run) {
757 /* Both are stopped. */
758 tcnto = s->last_tcnto;
759 } else if (!s->int_run) {
760 /* INT counter is stopped, progress is by CNT timer */
761 tcnto = remain % s->tcntb;
762 } else {
763 /* Both are counting */
764 icnto = remain / s->tcntb;
765 if (icnto) {
766 tcnto = remain % (icnto * s->tcntb);
767 } else {
768 tcnto = remain % s->tcntb;
769 }
770 }
771
772 return tcnto;
773}
774
775/*
776 * Set new values of counters for CNT and INT timers
777 */
778static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
779 uint32_t new_int)
780{
781 uint32_t cnt_stopped = 0;
782 uint32_t int_stopped = 0;
783
784 if (s->cnt_run) {
785 exynos4210_ltick_cnt_stop(s);
786 cnt_stopped = 1;
787 }
788
789 if (s->int_run) {
790 exynos4210_ltick_int_stop(s);
791 int_stopped = 1;
792 }
793
794 s->tcntb = new_cnt + 1;
795 s->icntb = new_int + 1;
796
797 if (cnt_stopped) {
798 exynos4210_ltick_cnt_start(s);
799 }
800 if (int_stopped) {
801 exynos4210_ltick_int_start(s);
802 }
803
804}
805
806/*
807 * Calculate new counter value for tick timer
808 */
809static void exynos4210_ltick_recalc_count(struct tick_timer *s)
810{
811 uint64_t to_count;
812
813 if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
814 /*
815 * one or both timers run and not counted to the end;
816 * distance is not passed, recalculate with last_tcnto * last_icnto
817 */
818
819 if (s->last_tcnto) {
c3a699be 820 to_count = (uint64_t)s->last_tcnto * s->last_icnto;
12c775db
EV
821 } else {
822 to_count = s->last_icnto;
823 }
824 } else {
825 /* distance is passed, recalculate with tcnto * icnto */
826 if (s->icntb) {
c3a699be 827 s->distance = (uint64_t)s->tcntb * s->icntb;
12c775db
EV
828 } else {
829 s->distance = s->tcntb;
830 }
831
832 to_count = s->distance;
833 s->progress = 0;
834 }
835
836 if (to_count > MCT_LT_COUNTER_STEP) {
837 /* count by step */
838 s->count = MCT_LT_COUNTER_STEP;
839 } else {
840 s->count = to_count;
841 }
842}
843
844/*
845 * Initialize tick_timer
846 */
847static void exynos4210_ltick_timer_init(struct tick_timer *s)
848{
849 exynos4210_ltick_int_stop(s);
850 exynos4210_ltick_cnt_stop(s);
851
852 s->count = 0;
853 s->distance = 0;
854 s->progress = 0;
855 s->icntb = 0;
856 s->tcntb = 0;
857}
858
859/*
860 * tick_timer event.
861 * Raises when abstract tick_timer expires.
862 */
863static void exynos4210_ltick_timer_event(struct tick_timer *s)
864{
865 s->progress += s->count;
866}
867
868/*
869 * Local timer tick counter handler.
870 * Don't use reloaded timers. If timer counter = zero
871 * then handler called but after handler finished no
872 * timer reload occurs.
873 */
874static void exynos4210_ltick_event(void *opaque)
875{
876 Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
877 uint32_t tcnto;
878 uint32_t icnto;
879#ifdef DEBUG_MCT
880 static uint64_t time1[2] = {0};
881 static uint64_t time2[2] = {0};
882#endif
883
93148aa5 884 /* Call tick_timer event handler, it will update its tcntb and icntb. */
12c775db
EV
885 exynos4210_ltick_timer_event(&s->tick_timer);
886
887 /* get tick_timer cnt */
888 tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
889
890 /* get tick_timer int */
891 icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
892
893 /* raise IRQ if needed */
894 if (!icnto && s->reg.tcon & L_TCON_INT_START) {
895 /* INT counter enabled and expired */
896
897 s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
898
899 /* raise interrupt if enabled */
900 if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
901#ifdef DEBUG_MCT
bc72ad67 902 time2[s->id] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
12c775db
EV
903 DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
904 time2[s->id] - time1[s->id]);
905 time1[s->id] = time2[s->id];
906#endif
907 qemu_irq_raise(s->irq);
908 }
909
910 /* reload ICNTB */
911 if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
912 exynos4210_ltick_set_cntb(&s->tick_timer,
913 s->reg.cnt[L_REG_CNT_TCNTB],
914 s->reg.cnt[L_REG_CNT_ICNTB]);
915 }
916 } else {
917 /* reload TCNTB */
918 if (!tcnto) {
919 exynos4210_ltick_set_cntb(&s->tick_timer,
920 s->reg.cnt[L_REG_CNT_TCNTB],
921 icnto);
922 }
923 }
924
925 /* start tick_timer cnt */
926 exynos4210_ltick_cnt_start(&s->tick_timer);
927
928 /* start tick_timer int */
929 exynos4210_ltick_int_start(&s->tick_timer);
930}
931
932/* update timer frequency */
933static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
934{
935 uint32_t freq = s->freq;
936 s->freq = 24000000 /
92e5d7e2 937 ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg) + 1) *
12c775db
EV
938 MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
939
940 if (freq != s->freq) {
941 DPRINTF("freq=%dHz\n", s->freq);
942
943 /* global timer */
944 ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
945
946 /* local timer */
947 ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
948 ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
949 ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
950 ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
951 }
952}
953
954/* set defaul_timer values for all fields */
955static void exynos4210_mct_reset(DeviceState *d)
956{
81e1010d 957 Exynos4210MCTState *s = EXYNOS4210_MCT(d);
12c775db
EV
958 uint32_t i;
959
960 s->reg_mct_cfg = 0;
961
962 /* global timer */
963 memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
964 exynos4210_gfrc_stop(&s->g_timer);
965
966 /* local timer */
967 memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
968 memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
969 for (i = 0; i < 2; i++) {
970 s->l_timer[i].reg.int_cstat = 0;
971 s->l_timer[i].reg.int_enb = 0;
972 s->l_timer[i].reg.tcon = 0;
973 s->l_timer[i].reg.wstat = 0;
974 s->l_timer[i].tick_timer.count = 0;
975 s->l_timer[i].tick_timer.distance = 0;
976 s->l_timer[i].tick_timer.progress = 0;
977 ptimer_stop(s->l_timer[i].ptimer_frc);
978
979 exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
980 }
981
982 exynos4210_mct_update_freq(s);
983
984}
985
986/* Multi Core Timer read */
a8170e5e 987static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
12c775db
EV
988 unsigned size)
989{
990 Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
991 int index;
992 int shift;
993 uint64_t count;
994 uint32_t value;
995 int lt_i;
996
997 switch (offset) {
998
999 case MCT_CFG:
1000 value = s->reg_mct_cfg;
1001 break;
1002
1003 case G_CNT_L: case G_CNT_U:
1004 shift = 8 * (offset & 0x4);
1005 count = exynos4210_gfrc_get_count(&s->g_timer);
1006 value = UINT32_MAX & (count >> shift);
1007 DPRINTF("read FRC=0x%llx\n", count);
1008 break;
1009
1010 case G_CNT_WSTAT:
1011 value = s->g_timer.reg.cnt_wstat;
1012 break;
1013
1014 case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
1015 case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
54ab9927
KK
1016 index = GET_G_COMP_IDX(offset);
1017 shift = 8 * (offset & 0x4);
1018 value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
12c775db
EV
1019 break;
1020
1021 case G_TCON:
1022 value = s->g_timer.reg.tcon;
1023 break;
1024
1025 case G_INT_CSTAT:
1026 value = s->g_timer.reg.int_cstat;
1027 break;
1028
1029 case G_INT_ENB:
1030 value = s->g_timer.reg.int_enb;
1031 break;
12c775db
EV
1032 case G_WSTAT:
1033 value = s->g_timer.reg.wstat;
1034 break;
1035
1036 case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
1037 case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
1038 value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
1039 break;
1040
1041 /* Local timers */
1042 case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
1043 case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
1044 lt_i = GET_L_TIMER_IDX(offset);
1045 index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1046 value = s->l_timer[lt_i].reg.cnt[index];
1047 break;
1048
1049 case L0_TCNTO: case L1_TCNTO:
1050 lt_i = GET_L_TIMER_IDX(offset);
1051
1052 value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
1053 DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
1054 break;
1055
1056 case L0_ICNTO: case L1_ICNTO:
1057 lt_i = GET_L_TIMER_IDX(offset);
1058
1059 value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
1060 DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
1061 break;
1062
1063 case L0_FRCNTO: case L1_FRCNTO:
1064 lt_i = GET_L_TIMER_IDX(offset);
1065
1066 value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
12c775db
EV
1067 break;
1068
1069 case L0_TCON: case L1_TCON:
1070 lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1071 value = s->l_timer[lt_i].reg.tcon;
1072 break;
1073
1074 case L0_INT_CSTAT: case L1_INT_CSTAT:
1075 lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1076 value = s->l_timer[lt_i].reg.int_cstat;
1077 break;
1078
1079 case L0_INT_ENB: case L1_INT_ENB:
1080 lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1081 value = s->l_timer[lt_i].reg.int_enb;
1082 break;
1083
1084 case L0_WSTAT: case L1_WSTAT:
1085 lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1086 value = s->l_timer[lt_i].reg.wstat;
1087 break;
1088
1089 default:
1090 hw_error("exynos4210.mct: bad read offset "
1091 TARGET_FMT_plx "\n", offset);
1092 break;
1093 }
1094 return value;
1095}
1096
1097/* MCT write */
a8170e5e 1098static void exynos4210_mct_write(void *opaque, hwaddr offset,
12c775db
EV
1099 uint64_t value, unsigned size)
1100{
1101 Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
1102 int index; /* index in buffer which represents register set */
1103 int shift;
1104 int lt_i;
1105 uint64_t new_frc;
1106 uint32_t i;
1107 uint32_t old_val;
1108#ifdef DEBUG_MCT
1109 static uint32_t icntb_max[2] = {0};
1110 static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
1111 static uint32_t tcntb_max[2] = {0};
1112 static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
1113#endif
1114
1115 new_frc = s->g_timer.reg.cnt;
1116
1117 switch (offset) {
1118
1119 case MCT_CFG:
1120 s->reg_mct_cfg = value;
1121 exynos4210_mct_update_freq(s);
1122 break;
1123
1124 case G_CNT_L:
1125 case G_CNT_U:
1126 if (offset == G_CNT_L) {
1127
1128 DPRINTF("global timer write to reg.cntl %llx\n", value);
1129
1130 new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
1131 s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
1132 }
1133 if (offset == G_CNT_U) {
1134
1135 DPRINTF("global timer write to reg.cntu %llx\n", value);
1136
1137 new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
1138 ((uint64_t)value << 32);
1139 s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
1140 }
1141
1142 s->g_timer.reg.cnt = new_frc;
1143 exynos4210_gfrc_restart(s);
1144 break;
1145
1146 case G_CNT_WSTAT:
1147 s->g_timer.reg.cnt_wstat &= ~(value);
1148 break;
1149
1150 case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
1151 case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
54ab9927
KK
1152 index = GET_G_COMP_IDX(offset);
1153 shift = 8 * (offset & 0x4);
1154 s->g_timer.reg.comp[index] =
1155 (s->g_timer.reg.comp[index] &
1156 (((uint64_t)UINT32_MAX << 32) >> shift)) +
1157 (value << shift);
12c775db 1158
54ab9927 1159 DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
12c775db 1160
54ab9927
KK
1161 if (offset & 0x4) {
1162 s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
1163 } else {
1164 s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
1165 }
12c775db 1166
54ab9927
KK
1167 exynos4210_gfrc_restart(s);
1168 break;
12c775db
EV
1169
1170 case G_TCON:
1171 old_val = s->g_timer.reg.tcon;
1172 s->g_timer.reg.tcon = value;
1173 s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
1174
1175 DPRINTF("global timer write to reg.g_tcon %llx\n", value);
1176
1177 /* Start FRC if transition from disabled to enabled */
1178 if ((value & G_TCON_TIMER_ENABLE) > (old_val &
1179 G_TCON_TIMER_ENABLE)) {
1180 exynos4210_gfrc_start(&s->g_timer);
1181 }
1182 if ((value & G_TCON_TIMER_ENABLE) < (old_val &
1183 G_TCON_TIMER_ENABLE)) {
1184 exynos4210_gfrc_stop(&s->g_timer);
1185 }
1186
1187 /* Start CMP if transition from disabled to enabled */
1188 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1189 if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
1190 G_TCON_COMP_ENABLE(i))) {
1191 exynos4210_gfrc_restart(s);
1192 }
1193 }
1194 break;
1195
1196 case G_INT_CSTAT:
1197 s->g_timer.reg.int_cstat &= ~(value);
1198 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1199 if (value & G_INT_CSTAT_COMP(i)) {
1200 exynos4210_gcomp_lower_irq(&s->g_timer, i);
1201 }
1202 }
1203 break;
1204
1205 case G_INT_ENB:
12c775db
EV
1206 /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
1207 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1208 if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
1209 G_INT_ENABLE(i))) {
1210 if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
1211 exynos4210_gcomp_raise_irq(&s->g_timer, i);
1212 }
1213 }
1214
1215 if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
1216 G_INT_ENABLE(i))) {
1217 exynos4210_gcomp_lower_irq(&s->g_timer, i);
1218 }
1219 }
1220
1221 DPRINTF("global timer INT enable %llx\n", value);
1222 s->g_timer.reg.int_enb = value;
1223 break;
1224
1225 case G_WSTAT:
1226 s->g_timer.reg.wstat &= ~(value);
1227 break;
1228
1229 case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
1230 case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
1231 index = GET_G_COMP_ADD_INCR_IDX(offset);
1232 s->g_timer.reg.comp_add_incr[index] = value;
1233 s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
1234 break;
1235
1236 /* Local timers */
1237 case L0_TCON: case L1_TCON:
1238 lt_i = GET_L_TIMER_IDX(offset);
1239 old_val = s->l_timer[lt_i].reg.tcon;
1240
1241 s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
1242 s->l_timer[lt_i].reg.tcon = value;
1243
1244 /* Stop local CNT */
1245 if ((value & L_TCON_TICK_START) <
1246 (old_val & L_TCON_TICK_START)) {
1247 DPRINTF("local timer[%d] stop cnt\n", lt_i);
1248 exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
1249 }
1250
1251 /* Stop local INT */
1252 if ((value & L_TCON_INT_START) <
1253 (old_val & L_TCON_INT_START)) {
1254 DPRINTF("local timer[%d] stop int\n", lt_i);
1255 exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
1256 }
1257
1258 /* Start local CNT */
1259 if ((value & L_TCON_TICK_START) >
1260 (old_val & L_TCON_TICK_START)) {
1261 DPRINTF("local timer[%d] start cnt\n", lt_i);
1262 exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
1263 }
1264
1265 /* Start local INT */
1266 if ((value & L_TCON_INT_START) >
1267 (old_val & L_TCON_INT_START)) {
1268 DPRINTF("local timer[%d] start int\n", lt_i);
1269 exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
1270 }
1271
1272 /* Start or Stop local FRC if TCON changed */
1273 if ((value & L_TCON_FRC_START) >
1274 (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
1275 DPRINTF("local timer[%d] start frc\n", lt_i);
1276 exynos4210_lfrc_start(&s->l_timer[lt_i]);
1277 }
1278 if ((value & L_TCON_FRC_START) <
1279 (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
1280 DPRINTF("local timer[%d] stop frc\n", lt_i);
1281 exynos4210_lfrc_stop(&s->l_timer[lt_i]);
1282 }
1283 break;
1284
1285 case L0_TCNTB: case L1_TCNTB:
12c775db
EV
1286 lt_i = GET_L_TIMER_IDX(offset);
1287 index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1288
1289 /*
1290 * TCNTB is updated to internal register only after CNT expired.
1291 * Due to this we should reload timer to nearest moment when CNT is
1292 * expired and then in event handler update tcntb to new TCNTB value.
1293 */
1294 exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
1295 s->l_timer[lt_i].tick_timer.icntb);
1296
1297 s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
1298 s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
1299
1300#ifdef DEBUG_MCT
1301 if (tcntb_min[lt_i] > value) {
1302 tcntb_min[lt_i] = value;
1303 }
1304 if (tcntb_max[lt_i] < value) {
1305 tcntb_max[lt_i] = value;
1306 }
1307 DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
1308 lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
1309#endif
1310 break;
1311
1312 case L0_ICNTB: case L1_ICNTB:
12c775db
EV
1313 lt_i = GET_L_TIMER_IDX(offset);
1314 index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1315
1316 s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
1317 s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
1318 ~L_ICNTB_MANUAL_UPDATE;
1319
1320 /*
1321 * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
1322 * could raise too fast disallowing QEMU to execute target code.
1323 */
1324 if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
1325 s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
1326 if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
1327 s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
1328 MCT_LT_CNT_LOW_LIMIT;
1329 } else {
1330 s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
1331 MCT_LT_CNT_LOW_LIMIT /
1332 s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
1333 }
1334 }
1335
1336 if (value & L_ICNTB_MANUAL_UPDATE) {
1337 exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
1338 s->l_timer[lt_i].tick_timer.tcntb,
1339 s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
1340 }
1341
1342#ifdef DEBUG_MCT
1343 if (icntb_min[lt_i] > value) {
1344 icntb_min[lt_i] = value;
1345 }
1346 if (icntb_max[lt_i] < value) {
1347 icntb_max[lt_i] = value;
1348 }
54ab9927
KK
1349 DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
1350 lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
12c775db 1351#endif
54ab9927 1352 break;
12c775db
EV
1353
1354 case L0_FRCNTB: case L1_FRCNTB:
12c775db
EV
1355 lt_i = GET_L_TIMER_IDX(offset);
1356 index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1357
1358 DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
1359
1360 s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
1361 s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
1362
1363 break;
1364
1365 case L0_TCNTO: case L1_TCNTO:
1366 case L0_ICNTO: case L1_ICNTO:
1367 case L0_FRCNTO: case L1_FRCNTO:
f2ad5140
KK
1368 qemu_log_mask(LOG_GUEST_ERROR,
1369 "exynos4210.mct: write to RO register " TARGET_FMT_plx,
1370 offset);
12c775db
EV
1371 break;
1372
1373 case L0_INT_CSTAT: case L1_INT_CSTAT:
1374 lt_i = GET_L_TIMER_IDX(offset);
1375
1376 DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
1377
1378 s->l_timer[lt_i].reg.int_cstat &= ~value;
1379 if (!s->l_timer[lt_i].reg.int_cstat) {
1380 qemu_irq_lower(s->l_timer[lt_i].irq);
1381 }
1382 break;
1383
1384 case L0_INT_ENB: case L1_INT_ENB:
1385 lt_i = GET_L_TIMER_IDX(offset);
1386 old_val = s->l_timer[lt_i].reg.int_enb;
1387
1388 /* Raise Local timer IRQ if cstat is pending */
1389 if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
1390 if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
1391 qemu_irq_raise(s->l_timer[lt_i].irq);
1392 }
1393 }
1394
1395 s->l_timer[lt_i].reg.int_enb = value;
1396
1397 break;
1398
1399 case L0_WSTAT: case L1_WSTAT:
1400 lt_i = GET_L_TIMER_IDX(offset);
1401
1402 s->l_timer[lt_i].reg.wstat &= ~value;
1403 break;
1404
1405 default:
1406 hw_error("exynos4210.mct: bad write offset "
1407 TARGET_FMT_plx "\n", offset);
1408 break;
1409 }
1410}
1411
1412static const MemoryRegionOps exynos4210_mct_ops = {
1413 .read = exynos4210_mct_read,
1414 .write = exynos4210_mct_write,
1415 .endianness = DEVICE_NATIVE_ENDIAN,
1416};
1417
1418/* MCT init */
7a53a140 1419static void exynos4210_mct_init(Object *obj)
12c775db
EV
1420{
1421 int i;
7a53a140
XZ
1422 Exynos4210MCTState *s = EXYNOS4210_MCT(obj);
1423 SysBusDevice *dev = SYS_BUS_DEVICE(obj);
12c775db
EV
1424 QEMUBH *bh[2];
1425
1426 /* Global timer */
1427 bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
e7ea81c3 1428 s->g_timer.ptimer_frc = ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
12c775db
EV
1429 memset(&s->g_timer.reg, 0, sizeof(struct gregs));
1430
1431 /* Local timers */
1432 for (i = 0; i < 2; i++) {
1433 bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
1434 bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
e7ea81c3
DO
1435 s->l_timer[i].tick_timer.ptimer_tick =
1436 ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
1437 s->l_timer[i].ptimer_frc = ptimer_init(bh[1], PTIMER_POLICY_DEFAULT);
12c775db
EV
1438 s->l_timer[i].id = i;
1439 }
1440
1441 /* IRQs */
1442 for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1443 sysbus_init_irq(dev, &s->g_timer.irq[i]);
1444 }
1445 for (i = 0; i < 2; i++) {
1446 sysbus_init_irq(dev, &s->l_timer[i].irq);
1447 }
1448
7a53a140 1449 memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s,
853dca12 1450 "exynos4210-mct", MCT_SFR_SIZE);
12c775db 1451 sysbus_init_mmio(dev, &s->iomem);
12c775db
EV
1452}
1453
1454static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
1455{
1456 DeviceClass *dc = DEVICE_CLASS(klass);
12c775db 1457
12c775db
EV
1458 dc->reset = exynos4210_mct_reset;
1459 dc->vmsd = &vmstate_exynos4210_mct_state;
1460}
1461
8c43a6f0 1462static const TypeInfo exynos4210_mct_info = {
81e1010d 1463 .name = TYPE_EXYNOS4210_MCT,
12c775db
EV
1464 .parent = TYPE_SYS_BUS_DEVICE,
1465 .instance_size = sizeof(Exynos4210MCTState),
7a53a140 1466 .instance_init = exynos4210_mct_init,
12c775db
EV
1467 .class_init = exynos4210_mct_class_init,
1468};
1469
1470static void exynos4210_mct_register_types(void)
1471{
1472 type_register_static(&exynos4210_mct_info);
1473}
1474
1475type_init(exynos4210_mct_register_types)