Statistics
| Branch: | Revision:

root / hw / exynos4210_mct.c @ 93148aa5

History | View | Annotate | Download (41.8 kB)

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