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