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