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