Statistics
| Branch: | Revision:

root / hw / timer / exynos4210_mct.c @ 3bd88451

History | View | Annotate | Download (41.7 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 83c9f4ca Paolo Bonzini
#include "hw/sysbus.h"
56 1de7afc9 Paolo Bonzini
#include "qemu/timer.h"
57 12c775db Evgeny Voevodin
#include "qemu-common.h"
58 83c9f4ca Paolo Bonzini
#include "hw/ptimer.h"
59 12c775db Evgeny Voevodin
60 0d09e41a Paolo Bonzini
#include "hw/arm/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
    count = s->count - count;
380 12c775db Evgeny Voevodin
    return s->reg.cnt + count;
381 12c775db Evgeny Voevodin
}
382 12c775db Evgeny Voevodin
383 12c775db Evgeny Voevodin
/*
384 12c775db Evgeny Voevodin
 * Stop global FRC timer
385 12c775db Evgeny Voevodin
 */
386 12c775db Evgeny Voevodin
static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
387 12c775db Evgeny Voevodin
{
388 12c775db Evgeny Voevodin
    DPRINTF("global timer frc stop\n");
389 12c775db Evgeny Voevodin
390 12c775db Evgeny Voevodin
    ptimer_stop(s->ptimer_frc);
391 12c775db Evgeny Voevodin
}
392 12c775db Evgeny Voevodin
393 12c775db Evgeny Voevodin
/*
394 12c775db Evgeny Voevodin
 * Start global FRC timer
395 12c775db Evgeny Voevodin
 */
396 12c775db Evgeny Voevodin
static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
397 12c775db Evgeny Voevodin
{
398 12c775db Evgeny Voevodin
    DPRINTF("global timer frc start\n");
399 12c775db Evgeny Voevodin
400 12c775db Evgeny Voevodin
    ptimer_run(s->ptimer_frc, 1);
401 12c775db Evgeny Voevodin
}
402 12c775db Evgeny Voevodin
403 12c775db Evgeny Voevodin
/*
404 12c775db Evgeny Voevodin
 * Find next nearest Comparator. If current Comparator value equals to other
405 12c775db Evgeny Voevodin
 * Comparator value, skip them both
406 12c775db Evgeny Voevodin
 */
407 12c775db Evgeny Voevodin
static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
408 12c775db Evgeny Voevodin
{
409 12c775db Evgeny Voevodin
    int res;
410 12c775db Evgeny Voevodin
    int i;
411 12c775db Evgeny Voevodin
    int enabled;
412 12c775db Evgeny Voevodin
    uint64_t min;
413 12c775db Evgeny Voevodin
    int min_comp_i;
414 12c775db Evgeny Voevodin
    uint64_t gfrc;
415 12c775db Evgeny Voevodin
    uint64_t distance;
416 12c775db Evgeny Voevodin
    uint64_t distance_min;
417 12c775db Evgeny Voevodin
    int comp_i;
418 12c775db Evgeny Voevodin
419 12c775db Evgeny Voevodin
    /* get gfrc count */
420 12c775db Evgeny Voevodin
    gfrc = exynos4210_gfrc_get_count(&s->g_timer);
421 12c775db Evgeny Voevodin
422 12c775db Evgeny Voevodin
    min = UINT64_MAX;
423 12c775db Evgeny Voevodin
    distance_min = UINT64_MAX;
424 12c775db Evgeny Voevodin
    comp_i = MCT_GT_CMP_NUM;
425 12c775db Evgeny Voevodin
    min_comp_i = MCT_GT_CMP_NUM;
426 12c775db Evgeny Voevodin
    enabled = 0;
427 12c775db Evgeny Voevodin
428 12c775db Evgeny Voevodin
    /* lookup for nearest comparator */
429 12c775db Evgeny Voevodin
    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
430 12c775db Evgeny Voevodin
431 12c775db Evgeny Voevodin
        if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
432 12c775db Evgeny Voevodin
433 12c775db Evgeny Voevodin
            enabled = 1;
434 12c775db Evgeny Voevodin
435 12c775db Evgeny Voevodin
            if (s->g_timer.reg.comp[i] > gfrc) {
436 12c775db Evgeny Voevodin
                /* Comparator is upper then FRC */
437 12c775db Evgeny Voevodin
                distance = s->g_timer.reg.comp[i] - gfrc;
438 12c775db Evgeny Voevodin
439 12c775db Evgeny Voevodin
                if (distance <= distance_min) {
440 12c775db Evgeny Voevodin
                    distance_min = distance;
441 12c775db Evgeny Voevodin
                    comp_i = i;
442 12c775db Evgeny Voevodin
                }
443 12c775db Evgeny Voevodin
            } else {
444 12c775db Evgeny Voevodin
                /* Comparator is below FRC, find the smallest */
445 12c775db Evgeny Voevodin
446 12c775db Evgeny Voevodin
                if (s->g_timer.reg.comp[i] <= min) {
447 12c775db Evgeny Voevodin
                    min = s->g_timer.reg.comp[i];
448 12c775db Evgeny Voevodin
                    min_comp_i = i;
449 12c775db Evgeny Voevodin
                }
450 12c775db Evgeny Voevodin
            }
451 12c775db Evgeny Voevodin
        }
452 12c775db Evgeny Voevodin
    }
453 12c775db Evgeny Voevodin
454 12c775db Evgeny Voevodin
    if (!enabled) {
455 12c775db Evgeny Voevodin
        /* All Comparators disabled */
456 12c775db Evgeny Voevodin
        res = -1;
457 12c775db Evgeny Voevodin
    } else if (comp_i < MCT_GT_CMP_NUM) {
458 12c775db Evgeny Voevodin
        /* Found upper Comparator */
459 12c775db Evgeny Voevodin
        res = comp_i;
460 12c775db Evgeny Voevodin
    } else {
461 12c775db Evgeny Voevodin
        /* All Comparators are below or equal to FRC  */
462 12c775db Evgeny Voevodin
        res = min_comp_i;
463 12c775db Evgeny Voevodin
    }
464 12c775db Evgeny Voevodin
465 12c775db Evgeny Voevodin
    DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
466 12c775db Evgeny Voevodin
            res,
467 12c775db Evgeny Voevodin
            s->g_timer.reg.comp[res],
468 12c775db Evgeny Voevodin
            distance_min,
469 12c775db Evgeny Voevodin
            gfrc);
470 12c775db Evgeny Voevodin
471 12c775db Evgeny Voevodin
    return res;
472 12c775db Evgeny Voevodin
}
473 12c775db Evgeny Voevodin
474 12c775db Evgeny Voevodin
/*
475 12c775db Evgeny Voevodin
 * Get distance to nearest Comparator
476 12c775db Evgeny Voevodin
 */
477 12c775db Evgeny Voevodin
static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
478 12c775db Evgeny Voevodin
{
479 12c775db Evgeny Voevodin
    if (id == -1) {
480 12c775db Evgeny Voevodin
        /* no enabled Comparators, choose max distance */
481 12c775db Evgeny Voevodin
        return MCT_GT_COUNTER_STEP;
482 12c775db Evgeny Voevodin
    }
483 12c775db Evgeny Voevodin
    if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
484 12c775db Evgeny Voevodin
        return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
485 12c775db Evgeny Voevodin
    } else {
486 12c775db Evgeny Voevodin
        return MCT_GT_COUNTER_STEP;
487 12c775db Evgeny Voevodin
    }
488 12c775db Evgeny Voevodin
}
489 12c775db Evgeny Voevodin
490 12c775db Evgeny Voevodin
/*
491 12c775db Evgeny Voevodin
 * Restart global FRC timer
492 12c775db Evgeny Voevodin
 */
493 12c775db Evgeny Voevodin
static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
494 12c775db Evgeny Voevodin
{
495 12c775db Evgeny Voevodin
    uint64_t distance;
496 12c775db Evgeny Voevodin
497 12c775db Evgeny Voevodin
    exynos4210_gfrc_stop(&s->g_timer);
498 12c775db Evgeny Voevodin
499 12c775db Evgeny Voevodin
    s->g_timer.curr_comp = exynos4210_gcomp_find(s);
500 12c775db Evgeny Voevodin
501 12c775db Evgeny Voevodin
    distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
502 12c775db Evgeny Voevodin
503 12c775db Evgeny Voevodin
    if (distance > MCT_GT_COUNTER_STEP || !distance) {
504 12c775db Evgeny Voevodin
        distance = MCT_GT_COUNTER_STEP;
505 12c775db Evgeny Voevodin
    }
506 12c775db Evgeny Voevodin
507 12c775db Evgeny Voevodin
    exynos4210_gfrc_set_count(&s->g_timer, distance);
508 12c775db Evgeny Voevodin
    exynos4210_gfrc_start(&s->g_timer);
509 12c775db Evgeny Voevodin
}
510 12c775db Evgeny Voevodin
511 12c775db Evgeny Voevodin
/*
512 12c775db Evgeny Voevodin
 * Raise global timer CMP IRQ
513 12c775db Evgeny Voevodin
 */
514 12c775db Evgeny Voevodin
static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
515 12c775db Evgeny Voevodin
{
516 12c775db Evgeny Voevodin
    Exynos4210MCTGT *s = opaque;
517 12c775db Evgeny Voevodin
518 12c775db Evgeny Voevodin
    /* If CSTAT is pending and IRQ is enabled */
519 12c775db Evgeny Voevodin
    if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
520 12c775db Evgeny Voevodin
            (s->reg.int_enb & G_INT_ENABLE(id))) {
521 12c775db Evgeny Voevodin
        DPRINTF("gcmp timer[%d] IRQ\n", id);
522 12c775db Evgeny Voevodin
        qemu_irq_raise(s->irq[id]);
523 12c775db Evgeny Voevodin
    }
524 12c775db Evgeny Voevodin
}
525 12c775db Evgeny Voevodin
526 12c775db Evgeny Voevodin
/*
527 12c775db Evgeny Voevodin
 * Lower global timer CMP IRQ
528 12c775db Evgeny Voevodin
 */
529 12c775db Evgeny Voevodin
static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
530 12c775db Evgeny Voevodin
{
531 12c775db Evgeny Voevodin
    Exynos4210MCTGT *s = opaque;
532 12c775db Evgeny Voevodin
    qemu_irq_lower(s->irq[id]);
533 12c775db Evgeny Voevodin
}
534 12c775db Evgeny Voevodin
535 12c775db Evgeny Voevodin
/*
536 12c775db Evgeny Voevodin
 * Global timer FRC event handler.
537 12c775db Evgeny Voevodin
 * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
538 12c775db Evgeny Voevodin
 * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
539 12c775db Evgeny Voevodin
 */
540 12c775db Evgeny Voevodin
static void exynos4210_gfrc_event(void *opaque)
541 12c775db Evgeny Voevodin
{
542 12c775db Evgeny Voevodin
    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
543 12c775db Evgeny Voevodin
    int i;
544 12c775db Evgeny Voevodin
    uint64_t distance;
545 12c775db Evgeny Voevodin
546 12c775db Evgeny Voevodin
    DPRINTF("\n");
547 12c775db Evgeny Voevodin
548 12c775db Evgeny Voevodin
    s->g_timer.reg.cnt += s->g_timer.count;
549 12c775db Evgeny Voevodin
550 12c775db Evgeny Voevodin
    /* Process all comparators */
551 12c775db Evgeny Voevodin
    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
552 12c775db Evgeny Voevodin
553 12c775db Evgeny Voevodin
        if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
554 12c775db Evgeny Voevodin
            /* reached nearest comparator */
555 12c775db Evgeny Voevodin
556 12c775db Evgeny Voevodin
            s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
557 12c775db Evgeny Voevodin
558 12c775db Evgeny Voevodin
            /* Auto increment */
559 12c775db Evgeny Voevodin
            if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
560 12c775db Evgeny Voevodin
                s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
561 12c775db Evgeny Voevodin
            }
562 12c775db Evgeny Voevodin
563 12c775db Evgeny Voevodin
            /* IRQ */
564 12c775db Evgeny Voevodin
            exynos4210_gcomp_raise_irq(&s->g_timer, i);
565 12c775db Evgeny Voevodin
        }
566 12c775db Evgeny Voevodin
    }
567 12c775db Evgeny Voevodin
568 12c775db Evgeny Voevodin
    /* Reload FRC to reach nearest comparator */
569 12c775db Evgeny Voevodin
    s->g_timer.curr_comp = exynos4210_gcomp_find(s);
570 12c775db Evgeny Voevodin
    distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
571 97331270 Jean-Christophe DUBOIS
    if (distance > MCT_GT_COUNTER_STEP || !distance) {
572 12c775db Evgeny Voevodin
        distance = MCT_GT_COUNTER_STEP;
573 12c775db Evgeny Voevodin
    }
574 12c775db Evgeny Voevodin
    exynos4210_gfrc_set_count(&s->g_timer, distance);
575 12c775db Evgeny Voevodin
576 12c775db Evgeny Voevodin
    exynos4210_gfrc_start(&s->g_timer);
577 12c775db Evgeny Voevodin
}
578 12c775db Evgeny Voevodin
579 12c775db Evgeny Voevodin
/*
580 12c775db Evgeny Voevodin
 * Get counter of FRC local timer.
581 12c775db Evgeny Voevodin
 */
582 12c775db Evgeny Voevodin
static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
583 12c775db Evgeny Voevodin
{
584 12c775db Evgeny Voevodin
    return ptimer_get_count(s->ptimer_frc);
585 12c775db Evgeny Voevodin
}
586 12c775db Evgeny Voevodin
587 12c775db Evgeny Voevodin
/*
588 12c775db Evgeny Voevodin
 * Set counter of FRC local timer.
589 12c775db Evgeny Voevodin
 */
590 12c775db Evgeny Voevodin
static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
591 12c775db Evgeny Voevodin
{
592 12c775db Evgeny Voevodin
    if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
593 12c775db Evgeny Voevodin
        ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
594 12c775db Evgeny Voevodin
    } else {
595 12c775db Evgeny Voevodin
        ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
596 12c775db Evgeny Voevodin
    }
597 12c775db Evgeny Voevodin
}
598 12c775db Evgeny Voevodin
599 12c775db Evgeny Voevodin
/*
600 12c775db Evgeny Voevodin
 * Start local FRC timer
601 12c775db Evgeny Voevodin
 */
602 12c775db Evgeny Voevodin
static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
603 12c775db Evgeny Voevodin
{
604 12c775db Evgeny Voevodin
    ptimer_run(s->ptimer_frc, 1);
605 12c775db Evgeny Voevodin
}
606 12c775db Evgeny Voevodin
607 12c775db Evgeny Voevodin
/*
608 12c775db Evgeny Voevodin
 * Stop local FRC timer
609 12c775db Evgeny Voevodin
 */
610 12c775db Evgeny Voevodin
static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
611 12c775db Evgeny Voevodin
{
612 12c775db Evgeny Voevodin
    ptimer_stop(s->ptimer_frc);
613 12c775db Evgeny Voevodin
}
614 12c775db Evgeny Voevodin
615 12c775db Evgeny Voevodin
/*
616 12c775db Evgeny Voevodin
 * Local timer free running counter tick handler
617 12c775db Evgeny Voevodin
 */
618 12c775db Evgeny Voevodin
static void exynos4210_lfrc_event(void *opaque)
619 12c775db Evgeny Voevodin
{
620 12c775db Evgeny Voevodin
    Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
621 12c775db Evgeny Voevodin
622 12c775db Evgeny Voevodin
    /* local frc expired */
623 12c775db Evgeny Voevodin
624 12c775db Evgeny Voevodin
    DPRINTF("\n");
625 12c775db Evgeny Voevodin
626 12c775db Evgeny Voevodin
    s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
627 12c775db Evgeny Voevodin
628 12c775db Evgeny Voevodin
    /* update frc counter */
629 12c775db Evgeny Voevodin
    exynos4210_lfrc_update_count(s);
630 12c775db Evgeny Voevodin
631 12c775db Evgeny Voevodin
    /* raise irq */
632 12c775db Evgeny Voevodin
    if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
633 12c775db Evgeny Voevodin
        qemu_irq_raise(s->irq);
634 12c775db Evgeny Voevodin
    }
635 12c775db Evgeny Voevodin
636 12c775db Evgeny Voevodin
    /*  we reached here, this means that timer is enabled */
637 12c775db Evgeny Voevodin
    exynos4210_lfrc_start(s);
638 12c775db Evgeny Voevodin
}
639 12c775db Evgeny Voevodin
640 12c775db Evgeny Voevodin
static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
641 12c775db Evgeny Voevodin
static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
642 12c775db Evgeny Voevodin
static void exynos4210_ltick_recalc_count(struct tick_timer *s);
643 12c775db Evgeny Voevodin
644 12c775db Evgeny Voevodin
/*
645 12c775db Evgeny Voevodin
 * Action on enabling local tick int timer
646 12c775db Evgeny Voevodin
 */
647 12c775db Evgeny Voevodin
static void exynos4210_ltick_int_start(struct tick_timer *s)
648 12c775db Evgeny Voevodin
{
649 12c775db Evgeny Voevodin
    if (!s->int_run) {
650 12c775db Evgeny Voevodin
        s->int_run = 1;
651 12c775db Evgeny Voevodin
    }
652 12c775db Evgeny Voevodin
}
653 12c775db Evgeny Voevodin
654 12c775db Evgeny Voevodin
/*
655 12c775db Evgeny Voevodin
 * Action on disabling local tick int timer
656 12c775db Evgeny Voevodin
 */
657 12c775db Evgeny Voevodin
static void exynos4210_ltick_int_stop(struct tick_timer *s)
658 12c775db Evgeny Voevodin
{
659 12c775db Evgeny Voevodin
    if (s->int_run) {
660 12c775db Evgeny Voevodin
        s->last_icnto = exynos4210_ltick_int_get_cnto(s);
661 12c775db Evgeny Voevodin
        s->int_run = 0;
662 12c775db Evgeny Voevodin
    }
663 12c775db Evgeny Voevodin
}
664 12c775db Evgeny Voevodin
665 12c775db Evgeny Voevodin
/*
666 12c775db Evgeny Voevodin
 * Get count for INT timer
667 12c775db Evgeny Voevodin
 */
668 12c775db Evgeny Voevodin
static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
669 12c775db Evgeny Voevodin
{
670 12c775db Evgeny Voevodin
    uint32_t icnto;
671 12c775db Evgeny Voevodin
    uint64_t remain;
672 12c775db Evgeny Voevodin
    uint64_t count;
673 12c775db Evgeny Voevodin
    uint64_t counted;
674 12c775db Evgeny Voevodin
    uint64_t cur_progress;
675 12c775db Evgeny Voevodin
676 12c775db Evgeny Voevodin
    count = ptimer_get_count(s->ptimer_tick);
677 12c775db Evgeny Voevodin
    if (count) {
678 12c775db Evgeny Voevodin
        /* timer is still counting, called not from event */
679 12c775db Evgeny Voevodin
        counted = s->count - ptimer_get_count(s->ptimer_tick);
680 12c775db Evgeny Voevodin
        cur_progress = s->progress + counted;
681 12c775db Evgeny Voevodin
    } else {
682 12c775db Evgeny Voevodin
        /* timer expired earlier */
683 12c775db Evgeny Voevodin
        cur_progress = s->progress;
684 12c775db Evgeny Voevodin
    }
685 12c775db Evgeny Voevodin
686 12c775db Evgeny Voevodin
    remain = s->distance - cur_progress;
687 12c775db Evgeny Voevodin
688 12c775db Evgeny Voevodin
    if (!s->int_run) {
689 12c775db Evgeny Voevodin
        /* INT is stopped. */
690 12c775db Evgeny Voevodin
        icnto = s->last_icnto;
691 12c775db Evgeny Voevodin
    } else {
692 12c775db Evgeny Voevodin
        /* Both are counting */
693 12c775db Evgeny Voevodin
        icnto = remain / s->tcntb;
694 12c775db Evgeny Voevodin
    }
695 12c775db Evgeny Voevodin
696 12c775db Evgeny Voevodin
    return icnto;
697 12c775db Evgeny Voevodin
}
698 12c775db Evgeny Voevodin
699 12c775db Evgeny Voevodin
/*
700 12c775db Evgeny Voevodin
 * Start local tick cnt timer.
701 12c775db Evgeny Voevodin
 */
702 12c775db Evgeny Voevodin
static void exynos4210_ltick_cnt_start(struct tick_timer *s)
703 12c775db Evgeny Voevodin
{
704 12c775db Evgeny Voevodin
    if (!s->cnt_run) {
705 12c775db Evgeny Voevodin
706 12c775db Evgeny Voevodin
        exynos4210_ltick_recalc_count(s);
707 12c775db Evgeny Voevodin
        ptimer_set_count(s->ptimer_tick, s->count);
708 12c775db Evgeny Voevodin
        ptimer_run(s->ptimer_tick, 1);
709 12c775db Evgeny Voevodin
710 12c775db Evgeny Voevodin
        s->cnt_run = 1;
711 12c775db Evgeny Voevodin
    }
712 12c775db Evgeny Voevodin
}
713 12c775db Evgeny Voevodin
714 12c775db Evgeny Voevodin
/*
715 12c775db Evgeny Voevodin
 * Stop local tick cnt timer.
716 12c775db Evgeny Voevodin
 */
717 12c775db Evgeny Voevodin
static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
718 12c775db Evgeny Voevodin
{
719 12c775db Evgeny Voevodin
    if (s->cnt_run) {
720 12c775db Evgeny Voevodin
721 12c775db Evgeny Voevodin
        s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
722 12c775db Evgeny Voevodin
723 12c775db Evgeny Voevodin
        if (s->int_run) {
724 12c775db Evgeny Voevodin
            exynos4210_ltick_int_stop(s);
725 12c775db Evgeny Voevodin
        }
726 12c775db Evgeny Voevodin
727 12c775db Evgeny Voevodin
        ptimer_stop(s->ptimer_tick);
728 12c775db Evgeny Voevodin
729 12c775db Evgeny Voevodin
        s->cnt_run = 0;
730 12c775db Evgeny Voevodin
    }
731 12c775db Evgeny Voevodin
}
732 12c775db Evgeny Voevodin
733 12c775db Evgeny Voevodin
/*
734 12c775db Evgeny Voevodin
 * Get counter for CNT timer
735 12c775db Evgeny Voevodin
 */
736 12c775db Evgeny Voevodin
static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
737 12c775db Evgeny Voevodin
{
738 12c775db Evgeny Voevodin
    uint32_t tcnto;
739 12c775db Evgeny Voevodin
    uint32_t icnto;
740 12c775db Evgeny Voevodin
    uint64_t remain;
741 12c775db Evgeny Voevodin
    uint64_t counted;
742 12c775db Evgeny Voevodin
    uint64_t count;
743 12c775db Evgeny Voevodin
    uint64_t cur_progress;
744 12c775db Evgeny Voevodin
745 12c775db Evgeny Voevodin
    count = ptimer_get_count(s->ptimer_tick);
746 12c775db Evgeny Voevodin
    if (count) {
747 12c775db Evgeny Voevodin
        /* timer is still counting, called not from event */
748 12c775db Evgeny Voevodin
        counted = s->count - ptimer_get_count(s->ptimer_tick);
749 12c775db Evgeny Voevodin
        cur_progress = s->progress + counted;
750 12c775db Evgeny Voevodin
    } else {
751 12c775db Evgeny Voevodin
        /* timer expired earlier */
752 12c775db Evgeny Voevodin
        cur_progress = s->progress;
753 12c775db Evgeny Voevodin
    }
754 12c775db Evgeny Voevodin
755 12c775db Evgeny Voevodin
    remain = s->distance - cur_progress;
756 12c775db Evgeny Voevodin
757 12c775db Evgeny Voevodin
    if (!s->cnt_run) {
758 12c775db Evgeny Voevodin
        /* Both are stopped. */
759 12c775db Evgeny Voevodin
        tcnto = s->last_tcnto;
760 12c775db Evgeny Voevodin
    } else if (!s->int_run) {
761 12c775db Evgeny Voevodin
        /* INT counter is stopped, progress is by CNT timer */
762 12c775db Evgeny Voevodin
        tcnto = remain % s->tcntb;
763 12c775db Evgeny Voevodin
    } else {
764 12c775db Evgeny Voevodin
        /* Both are counting */
765 12c775db Evgeny Voevodin
        icnto = remain / s->tcntb;
766 12c775db Evgeny Voevodin
        if (icnto) {
767 12c775db Evgeny Voevodin
            tcnto = remain % (icnto * s->tcntb);
768 12c775db Evgeny Voevodin
        } else {
769 12c775db Evgeny Voevodin
            tcnto = remain % s->tcntb;
770 12c775db Evgeny Voevodin
        }
771 12c775db Evgeny Voevodin
    }
772 12c775db Evgeny Voevodin
773 12c775db Evgeny Voevodin
    return tcnto;
774 12c775db Evgeny Voevodin
}
775 12c775db Evgeny Voevodin
776 12c775db Evgeny Voevodin
/*
777 12c775db Evgeny Voevodin
 * Set new values of counters for CNT and INT timers
778 12c775db Evgeny Voevodin
 */
779 12c775db Evgeny Voevodin
static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
780 12c775db Evgeny Voevodin
        uint32_t new_int)
781 12c775db Evgeny Voevodin
{
782 12c775db Evgeny Voevodin
    uint32_t cnt_stopped = 0;
783 12c775db Evgeny Voevodin
    uint32_t int_stopped = 0;
784 12c775db Evgeny Voevodin
785 12c775db Evgeny Voevodin
    if (s->cnt_run) {
786 12c775db Evgeny Voevodin
        exynos4210_ltick_cnt_stop(s);
787 12c775db Evgeny Voevodin
        cnt_stopped = 1;
788 12c775db Evgeny Voevodin
    }
789 12c775db Evgeny Voevodin
790 12c775db Evgeny Voevodin
    if (s->int_run) {
791 12c775db Evgeny Voevodin
        exynos4210_ltick_int_stop(s);
792 12c775db Evgeny Voevodin
        int_stopped = 1;
793 12c775db Evgeny Voevodin
    }
794 12c775db Evgeny Voevodin
795 12c775db Evgeny Voevodin
    s->tcntb = new_cnt + 1;
796 12c775db Evgeny Voevodin
    s->icntb = new_int + 1;
797 12c775db Evgeny Voevodin
798 12c775db Evgeny Voevodin
    if (cnt_stopped) {
799 12c775db Evgeny Voevodin
        exynos4210_ltick_cnt_start(s);
800 12c775db Evgeny Voevodin
    }
801 12c775db Evgeny Voevodin
    if (int_stopped) {
802 12c775db Evgeny Voevodin
        exynos4210_ltick_int_start(s);
803 12c775db Evgeny Voevodin
    }
804 12c775db Evgeny Voevodin
805 12c775db Evgeny Voevodin
}
806 12c775db Evgeny Voevodin
807 12c775db Evgeny Voevodin
/*
808 12c775db Evgeny Voevodin
 * Calculate new counter value for tick timer
809 12c775db Evgeny Voevodin
 */
810 12c775db Evgeny Voevodin
static void exynos4210_ltick_recalc_count(struct tick_timer *s)
811 12c775db Evgeny Voevodin
{
812 12c775db Evgeny Voevodin
    uint64_t to_count;
813 12c775db Evgeny Voevodin
814 12c775db Evgeny Voevodin
    if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
815 12c775db Evgeny Voevodin
        /*
816 12c775db Evgeny Voevodin
         * one or both timers run and not counted to the end;
817 12c775db Evgeny Voevodin
         * distance is not passed, recalculate with last_tcnto * last_icnto
818 12c775db Evgeny Voevodin
         */
819 12c775db Evgeny Voevodin
820 12c775db Evgeny Voevodin
        if (s->last_tcnto) {
821 12c775db Evgeny Voevodin
            to_count = s->last_tcnto * s->last_icnto;
822 12c775db Evgeny Voevodin
        } else {
823 12c775db Evgeny Voevodin
            to_count = s->last_icnto;
824 12c775db Evgeny Voevodin
        }
825 12c775db Evgeny Voevodin
    } else {
826 12c775db Evgeny Voevodin
        /* distance is passed, recalculate with tcnto * icnto */
827 12c775db Evgeny Voevodin
        if (s->icntb) {
828 12c775db Evgeny Voevodin
            s->distance = s->tcntb * s->icntb;
829 12c775db Evgeny Voevodin
        } else {
830 12c775db Evgeny Voevodin
            s->distance = s->tcntb;
831 12c775db Evgeny Voevodin
        }
832 12c775db Evgeny Voevodin
833 12c775db Evgeny Voevodin
        to_count = s->distance;
834 12c775db Evgeny Voevodin
        s->progress = 0;
835 12c775db Evgeny Voevodin
    }
836 12c775db Evgeny Voevodin
837 12c775db Evgeny Voevodin
    if (to_count > MCT_LT_COUNTER_STEP) {
838 12c775db Evgeny Voevodin
        /* count by step */
839 12c775db Evgeny Voevodin
        s->count = MCT_LT_COUNTER_STEP;
840 12c775db Evgeny Voevodin
    } else {
841 12c775db Evgeny Voevodin
        s->count = to_count;
842 12c775db Evgeny Voevodin
    }
843 12c775db Evgeny Voevodin
}
844 12c775db Evgeny Voevodin
845 12c775db Evgeny Voevodin
/*
846 12c775db Evgeny Voevodin
 * Initialize tick_timer
847 12c775db Evgeny Voevodin
 */
848 12c775db Evgeny Voevodin
static void exynos4210_ltick_timer_init(struct tick_timer *s)
849 12c775db Evgeny Voevodin
{
850 12c775db Evgeny Voevodin
    exynos4210_ltick_int_stop(s);
851 12c775db Evgeny Voevodin
    exynos4210_ltick_cnt_stop(s);
852 12c775db Evgeny Voevodin
853 12c775db Evgeny Voevodin
    s->count = 0;
854 12c775db Evgeny Voevodin
    s->distance = 0;
855 12c775db Evgeny Voevodin
    s->progress = 0;
856 12c775db Evgeny Voevodin
    s->icntb = 0;
857 12c775db Evgeny Voevodin
    s->tcntb = 0;
858 12c775db Evgeny Voevodin
}
859 12c775db Evgeny Voevodin
860 12c775db Evgeny Voevodin
/*
861 12c775db Evgeny Voevodin
 * tick_timer event.
862 12c775db Evgeny Voevodin
 * Raises when abstract tick_timer expires.
863 12c775db Evgeny Voevodin
 */
864 12c775db Evgeny Voevodin
static void exynos4210_ltick_timer_event(struct tick_timer *s)
865 12c775db Evgeny Voevodin
{
866 12c775db Evgeny Voevodin
    s->progress += s->count;
867 12c775db Evgeny Voevodin
}
868 12c775db Evgeny Voevodin
869 12c775db Evgeny Voevodin
/*
870 12c775db Evgeny Voevodin
 * Local timer tick counter handler.
871 12c775db Evgeny Voevodin
 * Don't use reloaded timers. If timer counter = zero
872 12c775db Evgeny Voevodin
 * then handler called but after handler finished no
873 12c775db Evgeny Voevodin
 * timer reload occurs.
874 12c775db Evgeny Voevodin
 */
875 12c775db Evgeny Voevodin
static void exynos4210_ltick_event(void *opaque)
876 12c775db Evgeny Voevodin
{
877 12c775db Evgeny Voevodin
    Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
878 12c775db Evgeny Voevodin
    uint32_t tcnto;
879 12c775db Evgeny Voevodin
    uint32_t icnto;
880 12c775db Evgeny Voevodin
#ifdef DEBUG_MCT
881 12c775db Evgeny Voevodin
    static uint64_t time1[2] = {0};
882 12c775db Evgeny Voevodin
    static uint64_t time2[2] = {0};
883 12c775db Evgeny Voevodin
#endif
884 12c775db Evgeny Voevodin
885 93148aa5 Stefan Weil
    /* Call tick_timer event handler, it will update its tcntb and icntb. */
886 12c775db Evgeny Voevodin
    exynos4210_ltick_timer_event(&s->tick_timer);
887 12c775db Evgeny Voevodin
888 12c775db Evgeny Voevodin
    /* get tick_timer cnt */
889 12c775db Evgeny Voevodin
    tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
890 12c775db Evgeny Voevodin
891 12c775db Evgeny Voevodin
    /* get tick_timer int */
892 12c775db Evgeny Voevodin
    icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
893 12c775db Evgeny Voevodin
894 12c775db Evgeny Voevodin
    /* raise IRQ if needed */
895 12c775db Evgeny Voevodin
    if (!icnto && s->reg.tcon & L_TCON_INT_START) {
896 12c775db Evgeny Voevodin
        /* INT counter enabled and expired */
897 12c775db Evgeny Voevodin
898 12c775db Evgeny Voevodin
        s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
899 12c775db Evgeny Voevodin
900 12c775db Evgeny Voevodin
        /* raise interrupt if enabled */
901 12c775db Evgeny Voevodin
        if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
902 12c775db Evgeny Voevodin
#ifdef DEBUG_MCT
903 12c775db Evgeny Voevodin
            time2[s->id] = qemu_get_clock_ns(vm_clock);
904 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
905 12c775db Evgeny Voevodin
                    time2[s->id] - time1[s->id]);
906 12c775db Evgeny Voevodin
            time1[s->id] = time2[s->id];
907 12c775db Evgeny Voevodin
#endif
908 12c775db Evgeny Voevodin
            qemu_irq_raise(s->irq);
909 12c775db Evgeny Voevodin
        }
910 12c775db Evgeny Voevodin
911 12c775db Evgeny Voevodin
        /* reload ICNTB */
912 12c775db Evgeny Voevodin
        if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
913 12c775db Evgeny Voevodin
            exynos4210_ltick_set_cntb(&s->tick_timer,
914 12c775db Evgeny Voevodin
                    s->reg.cnt[L_REG_CNT_TCNTB],
915 12c775db Evgeny Voevodin
                    s->reg.cnt[L_REG_CNT_ICNTB]);
916 12c775db Evgeny Voevodin
        }
917 12c775db Evgeny Voevodin
    } else {
918 12c775db Evgeny Voevodin
        /* reload TCNTB */
919 12c775db Evgeny Voevodin
        if (!tcnto) {
920 12c775db Evgeny Voevodin
            exynos4210_ltick_set_cntb(&s->tick_timer,
921 12c775db Evgeny Voevodin
                    s->reg.cnt[L_REG_CNT_TCNTB],
922 12c775db Evgeny Voevodin
                    icnto);
923 12c775db Evgeny Voevodin
        }
924 12c775db Evgeny Voevodin
    }
925 12c775db Evgeny Voevodin
926 12c775db Evgeny Voevodin
    /* start tick_timer cnt */
927 12c775db Evgeny Voevodin
    exynos4210_ltick_cnt_start(&s->tick_timer);
928 12c775db Evgeny Voevodin
929 12c775db Evgeny Voevodin
    /* start tick_timer int */
930 12c775db Evgeny Voevodin
    exynos4210_ltick_int_start(&s->tick_timer);
931 12c775db Evgeny Voevodin
}
932 12c775db Evgeny Voevodin
933 12c775db Evgeny Voevodin
/* update timer frequency */
934 12c775db Evgeny Voevodin
static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
935 12c775db Evgeny Voevodin
{
936 12c775db Evgeny Voevodin
    uint32_t freq = s->freq;
937 12c775db Evgeny Voevodin
    s->freq = 24000000 /
938 12c775db Evgeny Voevodin
            ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
939 12c775db Evgeny Voevodin
                    MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
940 12c775db Evgeny Voevodin
941 12c775db Evgeny Voevodin
    if (freq != s->freq) {
942 12c775db Evgeny Voevodin
        DPRINTF("freq=%dHz\n", s->freq);
943 12c775db Evgeny Voevodin
944 12c775db Evgeny Voevodin
        /* global timer */
945 12c775db Evgeny Voevodin
        ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
946 12c775db Evgeny Voevodin
947 12c775db Evgeny Voevodin
        /* local timer */
948 12c775db Evgeny Voevodin
        ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
949 12c775db Evgeny Voevodin
        ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
950 12c775db Evgeny Voevodin
        ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
951 12c775db Evgeny Voevodin
        ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
952 12c775db Evgeny Voevodin
    }
953 12c775db Evgeny Voevodin
}
954 12c775db Evgeny Voevodin
955 12c775db Evgeny Voevodin
/* set defaul_timer values for all fields */
956 12c775db Evgeny Voevodin
static void exynos4210_mct_reset(DeviceState *d)
957 12c775db Evgeny Voevodin
{
958 12c775db Evgeny Voevodin
    Exynos4210MCTState *s = (Exynos4210MCTState *)d;
959 12c775db Evgeny Voevodin
    uint32_t i;
960 12c775db Evgeny Voevodin
961 12c775db Evgeny Voevodin
    s->reg_mct_cfg = 0;
962 12c775db Evgeny Voevodin
963 12c775db Evgeny Voevodin
    /* global timer */
964 12c775db Evgeny Voevodin
    memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
965 12c775db Evgeny Voevodin
    exynos4210_gfrc_stop(&s->g_timer);
966 12c775db Evgeny Voevodin
967 12c775db Evgeny Voevodin
    /* local timer */
968 12c775db Evgeny Voevodin
    memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
969 12c775db Evgeny Voevodin
    memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
970 12c775db Evgeny Voevodin
    for (i = 0; i < 2; i++) {
971 12c775db Evgeny Voevodin
        s->l_timer[i].reg.int_cstat = 0;
972 12c775db Evgeny Voevodin
        s->l_timer[i].reg.int_enb = 0;
973 12c775db Evgeny Voevodin
        s->l_timer[i].reg.tcon = 0;
974 12c775db Evgeny Voevodin
        s->l_timer[i].reg.wstat = 0;
975 12c775db Evgeny Voevodin
        s->l_timer[i].tick_timer.count = 0;
976 12c775db Evgeny Voevodin
        s->l_timer[i].tick_timer.distance = 0;
977 12c775db Evgeny Voevodin
        s->l_timer[i].tick_timer.progress = 0;
978 12c775db Evgeny Voevodin
        ptimer_stop(s->l_timer[i].ptimer_frc);
979 12c775db Evgeny Voevodin
980 12c775db Evgeny Voevodin
        exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
981 12c775db Evgeny Voevodin
    }
982 12c775db Evgeny Voevodin
983 12c775db Evgeny Voevodin
    exynos4210_mct_update_freq(s);
984 12c775db Evgeny Voevodin
985 12c775db Evgeny Voevodin
}
986 12c775db Evgeny Voevodin
987 12c775db Evgeny Voevodin
/* Multi Core Timer read */
988 a8170e5e Avi Kivity
static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
989 12c775db Evgeny Voevodin
        unsigned size)
990 12c775db Evgeny Voevodin
{
991 12c775db Evgeny Voevodin
    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
992 12c775db Evgeny Voevodin
    int index;
993 12c775db Evgeny Voevodin
    int shift;
994 12c775db Evgeny Voevodin
    uint64_t count;
995 12c775db Evgeny Voevodin
    uint32_t value;
996 12c775db Evgeny Voevodin
    int lt_i;
997 12c775db Evgeny Voevodin
998 12c775db Evgeny Voevodin
    switch (offset) {
999 12c775db Evgeny Voevodin
1000 12c775db Evgeny Voevodin
    case MCT_CFG:
1001 12c775db Evgeny Voevodin
        value = s->reg_mct_cfg;
1002 12c775db Evgeny Voevodin
        break;
1003 12c775db Evgeny Voevodin
1004 12c775db Evgeny Voevodin
    case G_CNT_L: case G_CNT_U:
1005 12c775db Evgeny Voevodin
        shift = 8 * (offset & 0x4);
1006 12c775db Evgeny Voevodin
        count = exynos4210_gfrc_get_count(&s->g_timer);
1007 12c775db Evgeny Voevodin
        value = UINT32_MAX & (count >> shift);
1008 12c775db Evgeny Voevodin
        DPRINTF("read FRC=0x%llx\n", count);
1009 12c775db Evgeny Voevodin
        break;
1010 12c775db Evgeny Voevodin
1011 12c775db Evgeny Voevodin
    case G_CNT_WSTAT:
1012 12c775db Evgeny Voevodin
        value = s->g_timer.reg.cnt_wstat;
1013 12c775db Evgeny Voevodin
        break;
1014 12c775db Evgeny Voevodin
1015 12c775db Evgeny Voevodin
    case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
1016 12c775db Evgeny Voevodin
    case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
1017 12c775db Evgeny Voevodin
    index = GET_G_COMP_IDX(offset);
1018 12c775db Evgeny Voevodin
    shift = 8 * (offset & 0x4);
1019 12c775db Evgeny Voevodin
    value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
1020 12c775db Evgeny Voevodin
    break;
1021 12c775db Evgeny Voevodin
1022 12c775db Evgeny Voevodin
    case G_TCON:
1023 12c775db Evgeny Voevodin
        value = s->g_timer.reg.tcon;
1024 12c775db Evgeny Voevodin
        break;
1025 12c775db Evgeny Voevodin
1026 12c775db Evgeny Voevodin
    case G_INT_CSTAT:
1027 12c775db Evgeny Voevodin
        value = s->g_timer.reg.int_cstat;
1028 12c775db Evgeny Voevodin
        break;
1029 12c775db Evgeny Voevodin
1030 12c775db Evgeny Voevodin
    case G_INT_ENB:
1031 12c775db Evgeny Voevodin
        value = s->g_timer.reg.int_enb;
1032 12c775db Evgeny Voevodin
        break;
1033 12c775db Evgeny Voevodin
        break;
1034 12c775db Evgeny Voevodin
    case G_WSTAT:
1035 12c775db Evgeny Voevodin
        value = s->g_timer.reg.wstat;
1036 12c775db Evgeny Voevodin
        break;
1037 12c775db Evgeny Voevodin
1038 12c775db Evgeny Voevodin
    case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
1039 12c775db Evgeny Voevodin
    case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
1040 12c775db Evgeny Voevodin
        value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
1041 12c775db Evgeny Voevodin
        break;
1042 12c775db Evgeny Voevodin
1043 12c775db Evgeny Voevodin
        /* Local timers */
1044 12c775db Evgeny Voevodin
    case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
1045 12c775db Evgeny Voevodin
    case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
1046 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1047 12c775db Evgeny Voevodin
        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1048 12c775db Evgeny Voevodin
        value = s->l_timer[lt_i].reg.cnt[index];
1049 12c775db Evgeny Voevodin
        break;
1050 12c775db Evgeny Voevodin
1051 12c775db Evgeny Voevodin
    case L0_TCNTO: case L1_TCNTO:
1052 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1053 12c775db Evgeny Voevodin
1054 12c775db Evgeny Voevodin
        value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
1055 12c775db Evgeny Voevodin
        DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
1056 12c775db Evgeny Voevodin
        break;
1057 12c775db Evgeny Voevodin
1058 12c775db Evgeny Voevodin
    case L0_ICNTO: case L1_ICNTO:
1059 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1060 12c775db Evgeny Voevodin
1061 12c775db Evgeny Voevodin
        value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
1062 12c775db Evgeny Voevodin
        DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
1063 12c775db Evgeny Voevodin
        break;
1064 12c775db Evgeny Voevodin
1065 12c775db Evgeny Voevodin
    case L0_FRCNTO: case L1_FRCNTO:
1066 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1067 12c775db Evgeny Voevodin
1068 12c775db Evgeny Voevodin
        value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
1069 12c775db Evgeny Voevodin
1070 12c775db Evgeny Voevodin
        break;
1071 12c775db Evgeny Voevodin
1072 12c775db Evgeny Voevodin
    case L0_TCON: case L1_TCON:
1073 12c775db Evgeny Voevodin
        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1074 12c775db Evgeny Voevodin
        value = s->l_timer[lt_i].reg.tcon;
1075 12c775db Evgeny Voevodin
        break;
1076 12c775db Evgeny Voevodin
1077 12c775db Evgeny Voevodin
    case L0_INT_CSTAT: case L1_INT_CSTAT:
1078 12c775db Evgeny Voevodin
        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1079 12c775db Evgeny Voevodin
        value = s->l_timer[lt_i].reg.int_cstat;
1080 12c775db Evgeny Voevodin
        break;
1081 12c775db Evgeny Voevodin
1082 12c775db Evgeny Voevodin
    case L0_INT_ENB: case L1_INT_ENB:
1083 12c775db Evgeny Voevodin
        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1084 12c775db Evgeny Voevodin
        value = s->l_timer[lt_i].reg.int_enb;
1085 12c775db Evgeny Voevodin
        break;
1086 12c775db Evgeny Voevodin
1087 12c775db Evgeny Voevodin
    case L0_WSTAT: case L1_WSTAT:
1088 12c775db Evgeny Voevodin
        lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
1089 12c775db Evgeny Voevodin
        value = s->l_timer[lt_i].reg.wstat;
1090 12c775db Evgeny Voevodin
        break;
1091 12c775db Evgeny Voevodin
1092 12c775db Evgeny Voevodin
    default:
1093 12c775db Evgeny Voevodin
        hw_error("exynos4210.mct: bad read offset "
1094 12c775db Evgeny Voevodin
                TARGET_FMT_plx "\n", offset);
1095 12c775db Evgeny Voevodin
        break;
1096 12c775db Evgeny Voevodin
    }
1097 12c775db Evgeny Voevodin
    return value;
1098 12c775db Evgeny Voevodin
}
1099 12c775db Evgeny Voevodin
1100 12c775db Evgeny Voevodin
/* MCT write */
1101 a8170e5e Avi Kivity
static void exynos4210_mct_write(void *opaque, hwaddr offset,
1102 12c775db Evgeny Voevodin
        uint64_t value, unsigned size)
1103 12c775db Evgeny Voevodin
{
1104 12c775db Evgeny Voevodin
    Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
1105 12c775db Evgeny Voevodin
    int index;  /* index in buffer which represents register set */
1106 12c775db Evgeny Voevodin
    int shift;
1107 12c775db Evgeny Voevodin
    int lt_i;
1108 12c775db Evgeny Voevodin
    uint64_t new_frc;
1109 12c775db Evgeny Voevodin
    uint32_t i;
1110 12c775db Evgeny Voevodin
    uint32_t old_val;
1111 12c775db Evgeny Voevodin
#ifdef DEBUG_MCT
1112 12c775db Evgeny Voevodin
    static uint32_t icntb_max[2] = {0};
1113 12c775db Evgeny Voevodin
    static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
1114 12c775db Evgeny Voevodin
    static uint32_t tcntb_max[2] = {0};
1115 12c775db Evgeny Voevodin
    static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
1116 12c775db Evgeny Voevodin
#endif
1117 12c775db Evgeny Voevodin
1118 12c775db Evgeny Voevodin
    new_frc = s->g_timer.reg.cnt;
1119 12c775db Evgeny Voevodin
1120 12c775db Evgeny Voevodin
    switch (offset) {
1121 12c775db Evgeny Voevodin
1122 12c775db Evgeny Voevodin
    case MCT_CFG:
1123 12c775db Evgeny Voevodin
        s->reg_mct_cfg = value;
1124 12c775db Evgeny Voevodin
        exynos4210_mct_update_freq(s);
1125 12c775db Evgeny Voevodin
        break;
1126 12c775db Evgeny Voevodin
1127 12c775db Evgeny Voevodin
    case G_CNT_L:
1128 12c775db Evgeny Voevodin
    case G_CNT_U:
1129 12c775db Evgeny Voevodin
        if (offset == G_CNT_L) {
1130 12c775db Evgeny Voevodin
1131 12c775db Evgeny Voevodin
            DPRINTF("global timer write to reg.cntl %llx\n", value);
1132 12c775db Evgeny Voevodin
1133 12c775db Evgeny Voevodin
            new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
1134 12c775db Evgeny Voevodin
            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
1135 12c775db Evgeny Voevodin
        }
1136 12c775db Evgeny Voevodin
        if (offset == G_CNT_U) {
1137 12c775db Evgeny Voevodin
1138 12c775db Evgeny Voevodin
            DPRINTF("global timer write to reg.cntu %llx\n", value);
1139 12c775db Evgeny Voevodin
1140 12c775db Evgeny Voevodin
            new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
1141 12c775db Evgeny Voevodin
                    ((uint64_t)value << 32);
1142 12c775db Evgeny Voevodin
            s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
1143 12c775db Evgeny Voevodin
        }
1144 12c775db Evgeny Voevodin
1145 12c775db Evgeny Voevodin
        s->g_timer.reg.cnt = new_frc;
1146 12c775db Evgeny Voevodin
        exynos4210_gfrc_restart(s);
1147 12c775db Evgeny Voevodin
        break;
1148 12c775db Evgeny Voevodin
1149 12c775db Evgeny Voevodin
    case G_CNT_WSTAT:
1150 12c775db Evgeny Voevodin
        s->g_timer.reg.cnt_wstat &= ~(value);
1151 12c775db Evgeny Voevodin
        break;
1152 12c775db Evgeny Voevodin
1153 12c775db Evgeny Voevodin
    case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
1154 12c775db Evgeny Voevodin
    case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
1155 12c775db Evgeny Voevodin
    index = GET_G_COMP_IDX(offset);
1156 12c775db Evgeny Voevodin
    shift = 8 * (offset & 0x4);
1157 12c775db Evgeny Voevodin
    s->g_timer.reg.comp[index] =
1158 12c775db Evgeny Voevodin
            (s->g_timer.reg.comp[index] &
1159 12c775db Evgeny Voevodin
            (((uint64_t)UINT32_MAX << 32) >> shift)) +
1160 12c775db Evgeny Voevodin
            (value << shift);
1161 12c775db Evgeny Voevodin
1162 12c775db Evgeny Voevodin
    DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
1163 12c775db Evgeny Voevodin
1164 12c775db Evgeny Voevodin
    if (offset&0x4) {
1165 12c775db Evgeny Voevodin
        s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
1166 12c775db Evgeny Voevodin
    } else {
1167 12c775db Evgeny Voevodin
        s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
1168 12c775db Evgeny Voevodin
    }
1169 12c775db Evgeny Voevodin
1170 12c775db Evgeny Voevodin
    exynos4210_gfrc_restart(s);
1171 12c775db Evgeny Voevodin
    break;
1172 12c775db Evgeny Voevodin
1173 12c775db Evgeny Voevodin
    case G_TCON:
1174 12c775db Evgeny Voevodin
        old_val = s->g_timer.reg.tcon;
1175 12c775db Evgeny Voevodin
        s->g_timer.reg.tcon = value;
1176 12c775db Evgeny Voevodin
        s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
1177 12c775db Evgeny Voevodin
1178 12c775db Evgeny Voevodin
        DPRINTF("global timer write to reg.g_tcon %llx\n", value);
1179 12c775db Evgeny Voevodin
1180 12c775db Evgeny Voevodin
        /* Start FRC if transition from disabled to enabled */
1181 12c775db Evgeny Voevodin
        if ((value & G_TCON_TIMER_ENABLE) > (old_val &
1182 12c775db Evgeny Voevodin
                G_TCON_TIMER_ENABLE)) {
1183 12c775db Evgeny Voevodin
            exynos4210_gfrc_start(&s->g_timer);
1184 12c775db Evgeny Voevodin
        }
1185 12c775db Evgeny Voevodin
        if ((value & G_TCON_TIMER_ENABLE) < (old_val &
1186 12c775db Evgeny Voevodin
                G_TCON_TIMER_ENABLE)) {
1187 12c775db Evgeny Voevodin
            exynos4210_gfrc_stop(&s->g_timer);
1188 12c775db Evgeny Voevodin
        }
1189 12c775db Evgeny Voevodin
1190 12c775db Evgeny Voevodin
        /* Start CMP if transition from disabled to enabled */
1191 12c775db Evgeny Voevodin
        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1192 12c775db Evgeny Voevodin
            if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
1193 12c775db Evgeny Voevodin
                    G_TCON_COMP_ENABLE(i))) {
1194 12c775db Evgeny Voevodin
                exynos4210_gfrc_restart(s);
1195 12c775db Evgeny Voevodin
            }
1196 12c775db Evgeny Voevodin
        }
1197 12c775db Evgeny Voevodin
        break;
1198 12c775db Evgeny Voevodin
1199 12c775db Evgeny Voevodin
    case G_INT_CSTAT:
1200 12c775db Evgeny Voevodin
        s->g_timer.reg.int_cstat &= ~(value);
1201 12c775db Evgeny Voevodin
        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1202 12c775db Evgeny Voevodin
            if (value & G_INT_CSTAT_COMP(i)) {
1203 12c775db Evgeny Voevodin
                exynos4210_gcomp_lower_irq(&s->g_timer, i);
1204 12c775db Evgeny Voevodin
            }
1205 12c775db Evgeny Voevodin
        }
1206 12c775db Evgeny Voevodin
        break;
1207 12c775db Evgeny Voevodin
1208 12c775db Evgeny Voevodin
    case G_INT_ENB:
1209 12c775db Evgeny Voevodin
1210 12c775db Evgeny Voevodin
        /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
1211 12c775db Evgeny Voevodin
        for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1212 12c775db Evgeny Voevodin
            if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
1213 12c775db Evgeny Voevodin
                    G_INT_ENABLE(i))) {
1214 12c775db Evgeny Voevodin
                if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
1215 12c775db Evgeny Voevodin
                    exynos4210_gcomp_raise_irq(&s->g_timer, i);
1216 12c775db Evgeny Voevodin
                }
1217 12c775db Evgeny Voevodin
            }
1218 12c775db Evgeny Voevodin
1219 12c775db Evgeny Voevodin
            if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
1220 12c775db Evgeny Voevodin
                    G_INT_ENABLE(i))) {
1221 12c775db Evgeny Voevodin
                exynos4210_gcomp_lower_irq(&s->g_timer, i);
1222 12c775db Evgeny Voevodin
            }
1223 12c775db Evgeny Voevodin
        }
1224 12c775db Evgeny Voevodin
1225 12c775db Evgeny Voevodin
        DPRINTF("global timer INT enable %llx\n", value);
1226 12c775db Evgeny Voevodin
        s->g_timer.reg.int_enb = value;
1227 12c775db Evgeny Voevodin
        break;
1228 12c775db Evgeny Voevodin
1229 12c775db Evgeny Voevodin
    case G_WSTAT:
1230 12c775db Evgeny Voevodin
        s->g_timer.reg.wstat &= ~(value);
1231 12c775db Evgeny Voevodin
        break;
1232 12c775db Evgeny Voevodin
1233 12c775db Evgeny Voevodin
    case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
1234 12c775db Evgeny Voevodin
    case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
1235 12c775db Evgeny Voevodin
        index = GET_G_COMP_ADD_INCR_IDX(offset);
1236 12c775db Evgeny Voevodin
        s->g_timer.reg.comp_add_incr[index] = value;
1237 12c775db Evgeny Voevodin
        s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
1238 12c775db Evgeny Voevodin
        break;
1239 12c775db Evgeny Voevodin
1240 12c775db Evgeny Voevodin
        /* Local timers */
1241 12c775db Evgeny Voevodin
    case L0_TCON: case L1_TCON:
1242 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1243 12c775db Evgeny Voevodin
        old_val = s->l_timer[lt_i].reg.tcon;
1244 12c775db Evgeny Voevodin
1245 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
1246 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.tcon = value;
1247 12c775db Evgeny Voevodin
1248 12c775db Evgeny Voevodin
        /* Stop local CNT */
1249 12c775db Evgeny Voevodin
        if ((value & L_TCON_TICK_START) <
1250 12c775db Evgeny Voevodin
                (old_val & L_TCON_TICK_START)) {
1251 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] stop cnt\n", lt_i);
1252 12c775db Evgeny Voevodin
            exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
1253 12c775db Evgeny Voevodin
        }
1254 12c775db Evgeny Voevodin
1255 12c775db Evgeny Voevodin
        /* Stop local INT */
1256 12c775db Evgeny Voevodin
        if ((value & L_TCON_INT_START) <
1257 12c775db Evgeny Voevodin
                (old_val & L_TCON_INT_START)) {
1258 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] stop int\n", lt_i);
1259 12c775db Evgeny Voevodin
            exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
1260 12c775db Evgeny Voevodin
        }
1261 12c775db Evgeny Voevodin
1262 12c775db Evgeny Voevodin
        /* Start local CNT */
1263 12c775db Evgeny Voevodin
        if ((value & L_TCON_TICK_START) >
1264 12c775db Evgeny Voevodin
        (old_val & L_TCON_TICK_START)) {
1265 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] start cnt\n", lt_i);
1266 12c775db Evgeny Voevodin
            exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
1267 12c775db Evgeny Voevodin
        }
1268 12c775db Evgeny Voevodin
1269 12c775db Evgeny Voevodin
        /* Start local INT */
1270 12c775db Evgeny Voevodin
        if ((value & L_TCON_INT_START) >
1271 12c775db Evgeny Voevodin
        (old_val & L_TCON_INT_START)) {
1272 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] start int\n", lt_i);
1273 12c775db Evgeny Voevodin
            exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
1274 12c775db Evgeny Voevodin
        }
1275 12c775db Evgeny Voevodin
1276 12c775db Evgeny Voevodin
        /* Start or Stop local FRC if TCON changed */
1277 12c775db Evgeny Voevodin
        if ((value & L_TCON_FRC_START) >
1278 12c775db Evgeny Voevodin
        (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
1279 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] start frc\n", lt_i);
1280 12c775db Evgeny Voevodin
            exynos4210_lfrc_start(&s->l_timer[lt_i]);
1281 12c775db Evgeny Voevodin
        }
1282 12c775db Evgeny Voevodin
        if ((value & L_TCON_FRC_START) <
1283 12c775db Evgeny Voevodin
                (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
1284 12c775db Evgeny Voevodin
            DPRINTF("local timer[%d] stop frc\n", lt_i);
1285 12c775db Evgeny Voevodin
            exynos4210_lfrc_stop(&s->l_timer[lt_i]);
1286 12c775db Evgeny Voevodin
        }
1287 12c775db Evgeny Voevodin
        break;
1288 12c775db Evgeny Voevodin
1289 12c775db Evgeny Voevodin
    case L0_TCNTB: case L1_TCNTB:
1290 12c775db Evgeny Voevodin
1291 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1292 12c775db Evgeny Voevodin
        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1293 12c775db Evgeny Voevodin
1294 12c775db Evgeny Voevodin
        /*
1295 12c775db Evgeny Voevodin
         * TCNTB is updated to internal register only after CNT expired.
1296 12c775db Evgeny Voevodin
         * Due to this we should reload timer to nearest moment when CNT is
1297 12c775db Evgeny Voevodin
         * expired and then in event handler update tcntb to new TCNTB value.
1298 12c775db Evgeny Voevodin
         */
1299 12c775db Evgeny Voevodin
        exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
1300 12c775db Evgeny Voevodin
                s->l_timer[lt_i].tick_timer.icntb);
1301 12c775db Evgeny Voevodin
1302 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
1303 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
1304 12c775db Evgeny Voevodin
1305 12c775db Evgeny Voevodin
#ifdef DEBUG_MCT
1306 12c775db Evgeny Voevodin
        if (tcntb_min[lt_i] > value) {
1307 12c775db Evgeny Voevodin
            tcntb_min[lt_i] = value;
1308 12c775db Evgeny Voevodin
        }
1309 12c775db Evgeny Voevodin
        if (tcntb_max[lt_i] < value) {
1310 12c775db Evgeny Voevodin
            tcntb_max[lt_i] = value;
1311 12c775db Evgeny Voevodin
        }
1312 12c775db Evgeny Voevodin
        DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
1313 12c775db Evgeny Voevodin
                lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
1314 12c775db Evgeny Voevodin
#endif
1315 12c775db Evgeny Voevodin
        break;
1316 12c775db Evgeny Voevodin
1317 12c775db Evgeny Voevodin
    case L0_ICNTB: case L1_ICNTB:
1318 12c775db Evgeny Voevodin
1319 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1320 12c775db Evgeny Voevodin
        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1321 12c775db Evgeny Voevodin
1322 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
1323 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
1324 12c775db Evgeny Voevodin
                ~L_ICNTB_MANUAL_UPDATE;
1325 12c775db Evgeny Voevodin
1326 12c775db Evgeny Voevodin
        /*
1327 12c775db Evgeny Voevodin
         * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
1328 12c775db Evgeny Voevodin
         * could raise too fast disallowing QEMU to execute target code.
1329 12c775db Evgeny Voevodin
         */
1330 12c775db Evgeny Voevodin
        if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
1331 12c775db Evgeny Voevodin
            s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
1332 12c775db Evgeny Voevodin
            if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
1333 12c775db Evgeny Voevodin
                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
1334 12c775db Evgeny Voevodin
                        MCT_LT_CNT_LOW_LIMIT;
1335 12c775db Evgeny Voevodin
            } else {
1336 12c775db Evgeny Voevodin
                s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
1337 12c775db Evgeny Voevodin
                        MCT_LT_CNT_LOW_LIMIT /
1338 12c775db Evgeny Voevodin
                        s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
1339 12c775db Evgeny Voevodin
            }
1340 12c775db Evgeny Voevodin
        }
1341 12c775db Evgeny Voevodin
1342 12c775db Evgeny Voevodin
        if (value & L_ICNTB_MANUAL_UPDATE) {
1343 12c775db Evgeny Voevodin
            exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
1344 12c775db Evgeny Voevodin
                    s->l_timer[lt_i].tick_timer.tcntb,
1345 12c775db Evgeny Voevodin
                    s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
1346 12c775db Evgeny Voevodin
        }
1347 12c775db Evgeny Voevodin
1348 12c775db Evgeny Voevodin
#ifdef DEBUG_MCT
1349 12c775db Evgeny Voevodin
        if (icntb_min[lt_i] > value) {
1350 12c775db Evgeny Voevodin
            icntb_min[lt_i] = value;
1351 12c775db Evgeny Voevodin
        }
1352 12c775db Evgeny Voevodin
        if (icntb_max[lt_i] < value) {
1353 12c775db Evgeny Voevodin
            icntb_max[lt_i] = value;
1354 12c775db Evgeny Voevodin
        }
1355 12c775db Evgeny Voevodin
DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
1356 12c775db Evgeny Voevodin
        lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
1357 12c775db Evgeny Voevodin
#endif
1358 12c775db Evgeny Voevodin
break;
1359 12c775db Evgeny Voevodin
1360 12c775db Evgeny Voevodin
    case L0_FRCNTB: case L1_FRCNTB:
1361 12c775db Evgeny Voevodin
1362 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1363 12c775db Evgeny Voevodin
        index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
1364 12c775db Evgeny Voevodin
1365 12c775db Evgeny Voevodin
        DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
1366 12c775db Evgeny Voevodin
1367 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
1368 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
1369 12c775db Evgeny Voevodin
1370 12c775db Evgeny Voevodin
        break;
1371 12c775db Evgeny Voevodin
1372 12c775db Evgeny Voevodin
    case L0_TCNTO: case L1_TCNTO:
1373 12c775db Evgeny Voevodin
    case L0_ICNTO: case L1_ICNTO:
1374 12c775db Evgeny Voevodin
    case L0_FRCNTO: case L1_FRCNTO:
1375 12c775db Evgeny Voevodin
        fprintf(stderr, "\n[exynos4210.mct: write to RO register "
1376 12c775db Evgeny Voevodin
                TARGET_FMT_plx "]\n\n", offset);
1377 12c775db Evgeny Voevodin
        break;
1378 12c775db Evgeny Voevodin
1379 12c775db Evgeny Voevodin
    case L0_INT_CSTAT: case L1_INT_CSTAT:
1380 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1381 12c775db Evgeny Voevodin
1382 12c775db Evgeny Voevodin
        DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
1383 12c775db Evgeny Voevodin
1384 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.int_cstat &= ~value;
1385 12c775db Evgeny Voevodin
        if (!s->l_timer[lt_i].reg.int_cstat) {
1386 12c775db Evgeny Voevodin
            qemu_irq_lower(s->l_timer[lt_i].irq);
1387 12c775db Evgeny Voevodin
        }
1388 12c775db Evgeny Voevodin
        break;
1389 12c775db Evgeny Voevodin
1390 12c775db Evgeny Voevodin
    case L0_INT_ENB: case L1_INT_ENB:
1391 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1392 12c775db Evgeny Voevodin
        old_val = s->l_timer[lt_i].reg.int_enb;
1393 12c775db Evgeny Voevodin
1394 12c775db Evgeny Voevodin
        /* Raise Local timer IRQ if cstat is pending */
1395 12c775db Evgeny Voevodin
        if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
1396 12c775db Evgeny Voevodin
            if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
1397 12c775db Evgeny Voevodin
                qemu_irq_raise(s->l_timer[lt_i].irq);
1398 12c775db Evgeny Voevodin
            }
1399 12c775db Evgeny Voevodin
        }
1400 12c775db Evgeny Voevodin
1401 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.int_enb = value;
1402 12c775db Evgeny Voevodin
1403 12c775db Evgeny Voevodin
        break;
1404 12c775db Evgeny Voevodin
1405 12c775db Evgeny Voevodin
    case L0_WSTAT: case L1_WSTAT:
1406 12c775db Evgeny Voevodin
        lt_i = GET_L_TIMER_IDX(offset);
1407 12c775db Evgeny Voevodin
1408 12c775db Evgeny Voevodin
        s->l_timer[lt_i].reg.wstat &= ~value;
1409 12c775db Evgeny Voevodin
        break;
1410 12c775db Evgeny Voevodin
1411 12c775db Evgeny Voevodin
    default:
1412 12c775db Evgeny Voevodin
        hw_error("exynos4210.mct: bad write offset "
1413 12c775db Evgeny Voevodin
                TARGET_FMT_plx "\n", offset);
1414 12c775db Evgeny Voevodin
        break;
1415 12c775db Evgeny Voevodin
    }
1416 12c775db Evgeny Voevodin
}
1417 12c775db Evgeny Voevodin
1418 12c775db Evgeny Voevodin
static const MemoryRegionOps exynos4210_mct_ops = {
1419 12c775db Evgeny Voevodin
    .read = exynos4210_mct_read,
1420 12c775db Evgeny Voevodin
    .write = exynos4210_mct_write,
1421 12c775db Evgeny Voevodin
    .endianness = DEVICE_NATIVE_ENDIAN,
1422 12c775db Evgeny Voevodin
};
1423 12c775db Evgeny Voevodin
1424 12c775db Evgeny Voevodin
/* MCT init */
1425 12c775db Evgeny Voevodin
static int exynos4210_mct_init(SysBusDevice *dev)
1426 12c775db Evgeny Voevodin
{
1427 12c775db Evgeny Voevodin
    int i;
1428 12c775db Evgeny Voevodin
    Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev);
1429 12c775db Evgeny Voevodin
    QEMUBH *bh[2];
1430 12c775db Evgeny Voevodin
1431 12c775db Evgeny Voevodin
    /* Global timer */
1432 12c775db Evgeny Voevodin
    bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
1433 12c775db Evgeny Voevodin
    s->g_timer.ptimer_frc = ptimer_init(bh[0]);
1434 12c775db Evgeny Voevodin
    memset(&s->g_timer.reg, 0, sizeof(struct gregs));
1435 12c775db Evgeny Voevodin
1436 12c775db Evgeny Voevodin
    /* Local timers */
1437 12c775db Evgeny Voevodin
    for (i = 0; i < 2; i++) {
1438 12c775db Evgeny Voevodin
        bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
1439 12c775db Evgeny Voevodin
        bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
1440 12c775db Evgeny Voevodin
        s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
1441 12c775db Evgeny Voevodin
        s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
1442 12c775db Evgeny Voevodin
        s->l_timer[i].id = i;
1443 12c775db Evgeny Voevodin
    }
1444 12c775db Evgeny Voevodin
1445 12c775db Evgeny Voevodin
    /* IRQs */
1446 12c775db Evgeny Voevodin
    for (i = 0; i < MCT_GT_CMP_NUM; i++) {
1447 12c775db Evgeny Voevodin
        sysbus_init_irq(dev, &s->g_timer.irq[i]);
1448 12c775db Evgeny Voevodin
    }
1449 12c775db Evgeny Voevodin
    for (i = 0; i < 2; i++) {
1450 12c775db Evgeny Voevodin
        sysbus_init_irq(dev, &s->l_timer[i].irq);
1451 12c775db Evgeny Voevodin
    }
1452 12c775db Evgeny Voevodin
1453 12c775db Evgeny Voevodin
    memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct",
1454 12c775db Evgeny Voevodin
            MCT_SFR_SIZE);
1455 12c775db Evgeny Voevodin
    sysbus_init_mmio(dev, &s->iomem);
1456 12c775db Evgeny Voevodin
1457 12c775db Evgeny Voevodin
    return 0;
1458 12c775db Evgeny Voevodin
}
1459 12c775db Evgeny Voevodin
1460 12c775db Evgeny Voevodin
static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
1461 12c775db Evgeny Voevodin
{
1462 12c775db Evgeny Voevodin
    DeviceClass *dc = DEVICE_CLASS(klass);
1463 12c775db Evgeny Voevodin
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
1464 12c775db Evgeny Voevodin
1465 12c775db Evgeny Voevodin
    k->init = exynos4210_mct_init;
1466 12c775db Evgeny Voevodin
    dc->reset = exynos4210_mct_reset;
1467 12c775db Evgeny Voevodin
    dc->vmsd = &vmstate_exynos4210_mct_state;
1468 12c775db Evgeny Voevodin
}
1469 12c775db Evgeny Voevodin
1470 8c43a6f0 Andreas Färber
static const TypeInfo exynos4210_mct_info = {
1471 12c775db Evgeny Voevodin
    .name          = "exynos4210.mct",
1472 12c775db Evgeny Voevodin
    .parent        = TYPE_SYS_BUS_DEVICE,
1473 12c775db Evgeny Voevodin
    .instance_size = sizeof(Exynos4210MCTState),
1474 12c775db Evgeny Voevodin
    .class_init    = exynos4210_mct_class_init,
1475 12c775db Evgeny Voevodin
};
1476 12c775db Evgeny Voevodin
1477 12c775db Evgeny Voevodin
static void exynos4210_mct_register_types(void)
1478 12c775db Evgeny Voevodin
{
1479 12c775db Evgeny Voevodin
    type_register_static(&exynos4210_mct_info);
1480 12c775db Evgeny Voevodin
}
1481 12c775db Evgeny Voevodin
1482 12c775db Evgeny Voevodin
type_init(exynos4210_mct_register_types)