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