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