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