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