13bd88451SPaolo Bonzini /* 23bd88451SPaolo Bonzini * Samsung exynos4210 Multi Core timer 33bd88451SPaolo Bonzini * 43bd88451SPaolo Bonzini * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. 53bd88451SPaolo Bonzini * All rights reserved. 63bd88451SPaolo Bonzini * 73bd88451SPaolo Bonzini * Evgeny Voevodin <e.voevodin@samsung.com> 83bd88451SPaolo Bonzini * 93bd88451SPaolo Bonzini * This program is free software; you can redistribute it and/or modify it 103bd88451SPaolo Bonzini * under the terms of the GNU General Public License as published by the 113bd88451SPaolo Bonzini * Free Software Foundation; either version 2 of the License, or (at your 123bd88451SPaolo Bonzini * option) any later version. 133bd88451SPaolo Bonzini * 143bd88451SPaolo Bonzini * This program is distributed in the hope that it will be useful, 153bd88451SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 163bd88451SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 173bd88451SPaolo Bonzini * See the GNU General Public License for more details. 183bd88451SPaolo Bonzini * 193bd88451SPaolo Bonzini * You should have received a copy of the GNU General Public License along 203bd88451SPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>. 213bd88451SPaolo Bonzini */ 223bd88451SPaolo Bonzini 233bd88451SPaolo Bonzini /* 243bd88451SPaolo Bonzini * Global Timer: 253bd88451SPaolo Bonzini * 263bd88451SPaolo Bonzini * Consists of two timers. First represents Free Running Counter and second 273bd88451SPaolo Bonzini * is used to measure interval from FRC to nearest comparator. 283bd88451SPaolo Bonzini * 293bd88451SPaolo Bonzini * 0 UINT64_MAX 303bd88451SPaolo Bonzini * | timer0 | 313bd88451SPaolo Bonzini * | <-------------------------------------------------------------- | 323bd88451SPaolo Bonzini * | --------------------------------------------frc---------------> | 333bd88451SPaolo Bonzini * |______________________________________________|__________________| 343bd88451SPaolo Bonzini * CMP0 CMP1 CMP2 | CMP3 353bd88451SPaolo Bonzini * __| |_ 363bd88451SPaolo Bonzini * | timer1 | 373bd88451SPaolo Bonzini * | -------------> | 383bd88451SPaolo Bonzini * frc CMPx 393bd88451SPaolo Bonzini * 403bd88451SPaolo Bonzini * Problem: when implementing global timer as is, overflow arises. 413bd88451SPaolo Bonzini * next_time = cur_time + period * count; 423bd88451SPaolo Bonzini * period and count are 64 bits width. 433bd88451SPaolo Bonzini * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT 443bd88451SPaolo Bonzini * register during each event. 453bd88451SPaolo Bonzini * 463bd88451SPaolo Bonzini * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because 473bd88451SPaolo Bonzini * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--. 483bd88451SPaolo Bonzini * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0 493bd88451SPaolo Bonzini * generates IRQs suffers from too frequently events. Better to have one 503bd88451SPaolo Bonzini * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT, 513bd88451SPaolo Bonzini * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values, 523bd88451SPaolo Bonzini * there is no way to avoid frequently events). 533bd88451SPaolo Bonzini */ 543bd88451SPaolo Bonzini 558ef94f0bSPeter Maydell #include "qemu/osdep.h" 56f2ad5140SKrzysztof Kozlowski #include "qemu/log.h" 57a27bd6c7SMarkus Armbruster #include "hw/hw.h" 583bd88451SPaolo Bonzini #include "hw/sysbus.h" 59d6454270SMarkus Armbruster #include "migration/vmstate.h" 603bd88451SPaolo Bonzini #include "qemu/timer.h" 610b8fa32fSMarkus Armbruster #include "qemu/module.h" 623bd88451SPaolo Bonzini #include "hw/ptimer.h" 633bd88451SPaolo Bonzini 643bd88451SPaolo Bonzini #include "hw/arm/exynos4210.h" 65650d103dSMarkus Armbruster #include "hw/hw.h" 6664552b6bSMarkus Armbruster #include "hw/irq.h" 673bd88451SPaolo Bonzini 683bd88451SPaolo Bonzini //#define DEBUG_MCT 693bd88451SPaolo Bonzini 703bd88451SPaolo Bonzini #ifdef DEBUG_MCT 713bd88451SPaolo Bonzini #define DPRINTF(fmt, ...) \ 723bd88451SPaolo Bonzini do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \ 733bd88451SPaolo Bonzini ## __VA_ARGS__); } while (0) 743bd88451SPaolo Bonzini #else 753bd88451SPaolo Bonzini #define DPRINTF(fmt, ...) do {} while (0) 763bd88451SPaolo Bonzini #endif 773bd88451SPaolo Bonzini 783bd88451SPaolo Bonzini #define MCT_CFG 0x000 793bd88451SPaolo Bonzini #define G_CNT_L 0x100 803bd88451SPaolo Bonzini #define G_CNT_U 0x104 813bd88451SPaolo Bonzini #define G_CNT_WSTAT 0x110 823bd88451SPaolo Bonzini #define G_COMP0_L 0x200 833bd88451SPaolo Bonzini #define G_COMP0_U 0x204 843bd88451SPaolo Bonzini #define G_COMP0_ADD_INCR 0x208 853bd88451SPaolo Bonzini #define G_COMP1_L 0x210 863bd88451SPaolo Bonzini #define G_COMP1_U 0x214 873bd88451SPaolo Bonzini #define G_COMP1_ADD_INCR 0x218 883bd88451SPaolo Bonzini #define G_COMP2_L 0x220 893bd88451SPaolo Bonzini #define G_COMP2_U 0x224 903bd88451SPaolo Bonzini #define G_COMP2_ADD_INCR 0x228 913bd88451SPaolo Bonzini #define G_COMP3_L 0x230 923bd88451SPaolo Bonzini #define G_COMP3_U 0x234 933bd88451SPaolo Bonzini #define G_COMP3_ADD_INCR 0x238 943bd88451SPaolo Bonzini #define G_TCON 0x240 953bd88451SPaolo Bonzini #define G_INT_CSTAT 0x244 963bd88451SPaolo Bonzini #define G_INT_ENB 0x248 973bd88451SPaolo Bonzini #define G_WSTAT 0x24C 983bd88451SPaolo Bonzini #define L0_TCNTB 0x300 993bd88451SPaolo Bonzini #define L0_TCNTO 0x304 1003bd88451SPaolo Bonzini #define L0_ICNTB 0x308 1013bd88451SPaolo Bonzini #define L0_ICNTO 0x30C 1023bd88451SPaolo Bonzini #define L0_FRCNTB 0x310 1033bd88451SPaolo Bonzini #define L0_FRCNTO 0x314 1043bd88451SPaolo Bonzini #define L0_TCON 0x320 1053bd88451SPaolo Bonzini #define L0_INT_CSTAT 0x330 1063bd88451SPaolo Bonzini #define L0_INT_ENB 0x334 1073bd88451SPaolo Bonzini #define L0_WSTAT 0x340 1083bd88451SPaolo Bonzini #define L1_TCNTB 0x400 1093bd88451SPaolo Bonzini #define L1_TCNTO 0x404 1103bd88451SPaolo Bonzini #define L1_ICNTB 0x408 1113bd88451SPaolo Bonzini #define L1_ICNTO 0x40C 1123bd88451SPaolo Bonzini #define L1_FRCNTB 0x410 1133bd88451SPaolo Bonzini #define L1_FRCNTO 0x414 1143bd88451SPaolo Bonzini #define L1_TCON 0x420 1153bd88451SPaolo Bonzini #define L1_INT_CSTAT 0x430 1163bd88451SPaolo Bonzini #define L1_INT_ENB 0x434 1173bd88451SPaolo Bonzini #define L1_WSTAT 0x440 1183bd88451SPaolo Bonzini 1193bd88451SPaolo Bonzini #define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF) 1203bd88451SPaolo Bonzini #define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7)) 1213bd88451SPaolo Bonzini 1223bd88451SPaolo Bonzini #define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10) 1233bd88451SPaolo Bonzini #define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10) 1243bd88451SPaolo Bonzini 1253bd88451SPaolo Bonzini #define G_COMP_L(x) (G_COMP0_L + (x) * 0x10) 1263bd88451SPaolo Bonzini #define G_COMP_U(x) (G_COMP0_U + (x) * 0x10) 1273bd88451SPaolo Bonzini 1283bd88451SPaolo Bonzini #define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10) 1293bd88451SPaolo Bonzini 1303bd88451SPaolo Bonzini /* MCT bits */ 1313bd88451SPaolo Bonzini #define G_TCON_COMP_ENABLE(x) (1 << 2 * (x)) 1323bd88451SPaolo Bonzini #define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1)) 1333bd88451SPaolo Bonzini #define G_TCON_TIMER_ENABLE (1 << 8) 1343bd88451SPaolo Bonzini 1353bd88451SPaolo Bonzini #define G_INT_ENABLE(x) (1 << (x)) 1363bd88451SPaolo Bonzini #define G_INT_CSTAT_COMP(x) (1 << (x)) 1373bd88451SPaolo Bonzini 1383bd88451SPaolo Bonzini #define G_CNT_WSTAT_L 1 1393bd88451SPaolo Bonzini #define G_CNT_WSTAT_U 2 1403bd88451SPaolo Bonzini 1413bd88451SPaolo Bonzini #define G_WSTAT_COMP_L(x) (1 << 4 * (x)) 1423bd88451SPaolo Bonzini #define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1)) 1433bd88451SPaolo Bonzini #define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2)) 1443bd88451SPaolo Bonzini #define G_WSTAT_TCON_WRITE (1 << 16) 1453bd88451SPaolo Bonzini 1463bd88451SPaolo Bonzini #define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100) 1473bd88451SPaolo Bonzini #define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \ 1483bd88451SPaolo Bonzini (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2) 1493bd88451SPaolo Bonzini 1503bd88451SPaolo Bonzini #define L_ICNTB_MANUAL_UPDATE (1 << 31) 1513bd88451SPaolo Bonzini 1523bd88451SPaolo Bonzini #define L_TCON_TICK_START (1) 1533bd88451SPaolo Bonzini #define L_TCON_INT_START (1 << 1) 1543bd88451SPaolo Bonzini #define L_TCON_INTERVAL_MODE (1 << 2) 1553bd88451SPaolo Bonzini #define L_TCON_FRC_START (1 << 3) 1563bd88451SPaolo Bonzini 1573bd88451SPaolo Bonzini #define L_INT_CSTAT_INTCNT (1 << 0) 1583bd88451SPaolo Bonzini #define L_INT_CSTAT_FRCCNT (1 << 1) 1593bd88451SPaolo Bonzini 1603bd88451SPaolo Bonzini #define L_INT_INTENB_ICNTEIE (1 << 0) 1613bd88451SPaolo Bonzini #define L_INT_INTENB_FRCEIE (1 << 1) 1623bd88451SPaolo Bonzini 1633bd88451SPaolo Bonzini #define L_WSTAT_TCNTB_WRITE (1 << 0) 1643bd88451SPaolo Bonzini #define L_WSTAT_ICNTB_WRITE (1 << 1) 1653bd88451SPaolo Bonzini #define L_WSTAT_FRCCNTB_WRITE (1 << 2) 1663bd88451SPaolo Bonzini #define L_WSTAT_TCON_WRITE (1 << 3) 1673bd88451SPaolo Bonzini 1683bd88451SPaolo Bonzini enum LocalTimerRegCntIndexes { 1693bd88451SPaolo Bonzini L_REG_CNT_TCNTB, 1703bd88451SPaolo Bonzini L_REG_CNT_TCNTO, 1713bd88451SPaolo Bonzini L_REG_CNT_ICNTB, 1723bd88451SPaolo Bonzini L_REG_CNT_ICNTO, 1733bd88451SPaolo Bonzini L_REG_CNT_FRCCNTB, 1743bd88451SPaolo Bonzini L_REG_CNT_FRCCNTO, 1753bd88451SPaolo Bonzini 1763bd88451SPaolo Bonzini L_REG_CNT_AMOUNT 1773bd88451SPaolo Bonzini }; 1783bd88451SPaolo Bonzini 1793bd88451SPaolo Bonzini #define MCT_SFR_SIZE 0x444 1803bd88451SPaolo Bonzini 1813bd88451SPaolo Bonzini #define MCT_GT_CMP_NUM 4 1823bd88451SPaolo Bonzini 1833bd88451SPaolo Bonzini #define MCT_GT_COUNTER_STEP 0x100000000ULL 1843bd88451SPaolo Bonzini #define MCT_LT_COUNTER_STEP 0x100000000ULL 1853bd88451SPaolo Bonzini #define MCT_LT_CNT_LOW_LIMIT 0x100 1863bd88451SPaolo Bonzini 1873bd88451SPaolo Bonzini /* global timer */ 1883bd88451SPaolo Bonzini typedef struct { 1893bd88451SPaolo Bonzini qemu_irq irq[MCT_GT_CMP_NUM]; 1903bd88451SPaolo Bonzini 1913bd88451SPaolo Bonzini struct gregs { 1923bd88451SPaolo Bonzini uint64_t cnt; 1933bd88451SPaolo Bonzini uint32_t cnt_wstat; 1943bd88451SPaolo Bonzini uint32_t tcon; 1953bd88451SPaolo Bonzini uint32_t int_cstat; 1963bd88451SPaolo Bonzini uint32_t int_enb; 1973bd88451SPaolo Bonzini uint32_t wstat; 1983bd88451SPaolo Bonzini uint64_t comp[MCT_GT_CMP_NUM]; 1993bd88451SPaolo Bonzini uint32_t comp_add_incr[MCT_GT_CMP_NUM]; 2003bd88451SPaolo Bonzini } reg; 2013bd88451SPaolo Bonzini 2023bd88451SPaolo Bonzini uint64_t count; /* Value FRC was armed with */ 2033bd88451SPaolo Bonzini int32_t curr_comp; /* Current comparator FRC is running to */ 2043bd88451SPaolo Bonzini 2053bd88451SPaolo Bonzini ptimer_state *ptimer_frc; /* FRC timer */ 2063bd88451SPaolo Bonzini 2073bd88451SPaolo Bonzini } Exynos4210MCTGT; 2083bd88451SPaolo Bonzini 2093bd88451SPaolo Bonzini /* local timer */ 2103bd88451SPaolo Bonzini typedef struct { 2113bd88451SPaolo Bonzini int id; /* timer id */ 2123bd88451SPaolo Bonzini qemu_irq irq; /* local timer irq */ 2133bd88451SPaolo Bonzini 2143bd88451SPaolo Bonzini struct tick_timer { 2153bd88451SPaolo Bonzini uint32_t cnt_run; /* cnt timer is running */ 2163bd88451SPaolo Bonzini uint32_t int_run; /* int timer is running */ 2173bd88451SPaolo Bonzini 2183bd88451SPaolo Bonzini uint32_t last_icnto; 2193bd88451SPaolo Bonzini uint32_t last_tcnto; 2203bd88451SPaolo Bonzini uint32_t tcntb; /* initial value for TCNTB */ 2213bd88451SPaolo Bonzini uint32_t icntb; /* initial value for ICNTB */ 2223bd88451SPaolo Bonzini 2233bd88451SPaolo Bonzini /* for step mode */ 2243bd88451SPaolo Bonzini uint64_t distance; /* distance to count to the next event */ 2253bd88451SPaolo Bonzini uint64_t progress; /* progress when counting by steps */ 2263bd88451SPaolo Bonzini uint64_t count; /* count to arm timer with */ 2273bd88451SPaolo Bonzini 2283bd88451SPaolo Bonzini ptimer_state *ptimer_tick; /* timer for tick counter */ 2293bd88451SPaolo Bonzini } tick_timer; 2303bd88451SPaolo Bonzini 2313bd88451SPaolo Bonzini /* use ptimer.c to represent count down timer */ 2323bd88451SPaolo Bonzini 2333bd88451SPaolo Bonzini ptimer_state *ptimer_frc; /* timer for free running counter */ 2343bd88451SPaolo Bonzini 2353bd88451SPaolo Bonzini /* registers */ 2363bd88451SPaolo Bonzini struct lregs { 2373bd88451SPaolo Bonzini uint32_t cnt[L_REG_CNT_AMOUNT]; 2383bd88451SPaolo Bonzini uint32_t tcon; 2393bd88451SPaolo Bonzini uint32_t int_cstat; 2403bd88451SPaolo Bonzini uint32_t int_enb; 2413bd88451SPaolo Bonzini uint32_t wstat; 2423bd88451SPaolo Bonzini } reg; 2433bd88451SPaolo Bonzini 2443bd88451SPaolo Bonzini } Exynos4210MCTLT; 2453bd88451SPaolo Bonzini 24681e1010dSAndreas Färber #define TYPE_EXYNOS4210_MCT "exynos4210.mct" 24781e1010dSAndreas Färber #define EXYNOS4210_MCT(obj) \ 24881e1010dSAndreas Färber OBJECT_CHECK(Exynos4210MCTState, (obj), TYPE_EXYNOS4210_MCT) 24981e1010dSAndreas Färber 2503bd88451SPaolo Bonzini typedef struct Exynos4210MCTState { 25181e1010dSAndreas Färber SysBusDevice parent_obj; 25281e1010dSAndreas Färber 2533bd88451SPaolo Bonzini MemoryRegion iomem; 2543bd88451SPaolo Bonzini 2553bd88451SPaolo Bonzini /* Registers */ 2563bd88451SPaolo Bonzini uint32_t reg_mct_cfg; 2573bd88451SPaolo Bonzini 2583bd88451SPaolo Bonzini Exynos4210MCTLT l_timer[2]; 2593bd88451SPaolo Bonzini Exynos4210MCTGT g_timer; 2603bd88451SPaolo Bonzini 2613bd88451SPaolo Bonzini uint32_t freq; /* all timers tick frequency, TCLK */ 2623bd88451SPaolo Bonzini } Exynos4210MCTState; 2633bd88451SPaolo Bonzini 2643bd88451SPaolo Bonzini /*** VMState ***/ 2653bd88451SPaolo Bonzini static const VMStateDescription vmstate_tick_timer = { 2663bd88451SPaolo Bonzini .name = "exynos4210.mct.tick_timer", 2673bd88451SPaolo Bonzini .version_id = 1, 2683bd88451SPaolo Bonzini .minimum_version_id = 1, 2693bd88451SPaolo Bonzini .fields = (VMStateField[]) { 2703bd88451SPaolo Bonzini VMSTATE_UINT32(cnt_run, struct tick_timer), 2713bd88451SPaolo Bonzini VMSTATE_UINT32(int_run, struct tick_timer), 2723bd88451SPaolo Bonzini VMSTATE_UINT32(last_icnto, struct tick_timer), 2733bd88451SPaolo Bonzini VMSTATE_UINT32(last_tcnto, struct tick_timer), 2743bd88451SPaolo Bonzini VMSTATE_UINT32(tcntb, struct tick_timer), 2753bd88451SPaolo Bonzini VMSTATE_UINT32(icntb, struct tick_timer), 2763bd88451SPaolo Bonzini VMSTATE_UINT64(distance, struct tick_timer), 2773bd88451SPaolo Bonzini VMSTATE_UINT64(progress, struct tick_timer), 2783bd88451SPaolo Bonzini VMSTATE_UINT64(count, struct tick_timer), 2793bd88451SPaolo Bonzini VMSTATE_PTIMER(ptimer_tick, struct tick_timer), 2803bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 2813bd88451SPaolo Bonzini } 2823bd88451SPaolo Bonzini }; 2833bd88451SPaolo Bonzini 2843bd88451SPaolo Bonzini static const VMStateDescription vmstate_lregs = { 2853bd88451SPaolo Bonzini .name = "exynos4210.mct.lregs", 2863bd88451SPaolo Bonzini .version_id = 1, 2873bd88451SPaolo Bonzini .minimum_version_id = 1, 2883bd88451SPaolo Bonzini .fields = (VMStateField[]) { 2893bd88451SPaolo Bonzini VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), 2903bd88451SPaolo Bonzini VMSTATE_UINT32(tcon, struct lregs), 2913bd88451SPaolo Bonzini VMSTATE_UINT32(int_cstat, struct lregs), 2923bd88451SPaolo Bonzini VMSTATE_UINT32(int_enb, struct lregs), 2933bd88451SPaolo Bonzini VMSTATE_UINT32(wstat, struct lregs), 2943bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 2953bd88451SPaolo Bonzini } 2963bd88451SPaolo Bonzini }; 2973bd88451SPaolo Bonzini 2983bd88451SPaolo Bonzini static const VMStateDescription vmstate_exynos4210_mct_lt = { 2993bd88451SPaolo Bonzini .name = "exynos4210.mct.lt", 3003bd88451SPaolo Bonzini .version_id = 1, 3013bd88451SPaolo Bonzini .minimum_version_id = 1, 3023bd88451SPaolo Bonzini .fields = (VMStateField[]) { 3033bd88451SPaolo Bonzini VMSTATE_INT32(id, Exynos4210MCTLT), 3043bd88451SPaolo Bonzini VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, 3053bd88451SPaolo Bonzini vmstate_tick_timer, 3063bd88451SPaolo Bonzini struct tick_timer), 3073bd88451SPaolo Bonzini VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT), 3083bd88451SPaolo Bonzini VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0, 3093bd88451SPaolo Bonzini vmstate_lregs, 3103bd88451SPaolo Bonzini struct lregs), 3113bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 3123bd88451SPaolo Bonzini } 3133bd88451SPaolo Bonzini }; 3143bd88451SPaolo Bonzini 3153bd88451SPaolo Bonzini static const VMStateDescription vmstate_gregs = { 3163bd88451SPaolo Bonzini .name = "exynos4210.mct.lregs", 3173bd88451SPaolo Bonzini .version_id = 1, 3183bd88451SPaolo Bonzini .minimum_version_id = 1, 3193bd88451SPaolo Bonzini .fields = (VMStateField[]) { 3203bd88451SPaolo Bonzini VMSTATE_UINT64(cnt, struct gregs), 3213bd88451SPaolo Bonzini VMSTATE_UINT32(cnt_wstat, struct gregs), 3223bd88451SPaolo Bonzini VMSTATE_UINT32(tcon, struct gregs), 3233bd88451SPaolo Bonzini VMSTATE_UINT32(int_cstat, struct gregs), 3243bd88451SPaolo Bonzini VMSTATE_UINT32(int_enb, struct gregs), 3253bd88451SPaolo Bonzini VMSTATE_UINT32(wstat, struct gregs), 3263bd88451SPaolo Bonzini VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM), 3273bd88451SPaolo Bonzini VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs, 3283bd88451SPaolo Bonzini MCT_GT_CMP_NUM), 3293bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 3303bd88451SPaolo Bonzini } 3313bd88451SPaolo Bonzini }; 3323bd88451SPaolo Bonzini 3333bd88451SPaolo Bonzini static const VMStateDescription vmstate_exynos4210_mct_gt = { 3343bd88451SPaolo Bonzini .name = "exynos4210.mct.lt", 3353bd88451SPaolo Bonzini .version_id = 1, 3363bd88451SPaolo Bonzini .minimum_version_id = 1, 3373bd88451SPaolo Bonzini .fields = (VMStateField[]) { 3383bd88451SPaolo Bonzini VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, 3393bd88451SPaolo Bonzini struct gregs), 3403bd88451SPaolo Bonzini VMSTATE_UINT64(count, Exynos4210MCTGT), 3413bd88451SPaolo Bonzini VMSTATE_INT32(curr_comp, Exynos4210MCTGT), 3423bd88451SPaolo Bonzini VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT), 3433bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 3443bd88451SPaolo Bonzini } 3453bd88451SPaolo Bonzini }; 3463bd88451SPaolo Bonzini 3473bd88451SPaolo Bonzini static const VMStateDescription vmstate_exynos4210_mct_state = { 3483bd88451SPaolo Bonzini .name = "exynos4210.mct", 3493bd88451SPaolo Bonzini .version_id = 1, 3503bd88451SPaolo Bonzini .minimum_version_id = 1, 3513bd88451SPaolo Bonzini .fields = (VMStateField[]) { 3523bd88451SPaolo Bonzini VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), 3533bd88451SPaolo Bonzini VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, 3543bd88451SPaolo Bonzini vmstate_exynos4210_mct_lt, Exynos4210MCTLT), 3553bd88451SPaolo Bonzini VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0, 3563bd88451SPaolo Bonzini vmstate_exynos4210_mct_gt, Exynos4210MCTGT), 3573bd88451SPaolo Bonzini VMSTATE_UINT32(freq, Exynos4210MCTState), 3583bd88451SPaolo Bonzini VMSTATE_END_OF_LIST() 3593bd88451SPaolo Bonzini } 3603bd88451SPaolo Bonzini }; 3613bd88451SPaolo Bonzini 3623bd88451SPaolo Bonzini static void exynos4210_mct_update_freq(Exynos4210MCTState *s); 3633bd88451SPaolo Bonzini 3643bd88451SPaolo Bonzini /* 3653bd88451SPaolo Bonzini * Set counter of FRC global timer. 3669ede4ec0SPeter Maydell * Must be called within exynos4210_gfrc_tx_begin/commit block. 3673bd88451SPaolo Bonzini */ 3683bd88451SPaolo Bonzini static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) 3693bd88451SPaolo Bonzini { 3703bd88451SPaolo Bonzini s->count = count; 3713bd88451SPaolo Bonzini DPRINTF("global timer frc set count 0x%llx\n", count); 3723bd88451SPaolo Bonzini ptimer_set_count(s->ptimer_frc, count); 3733bd88451SPaolo Bonzini } 3743bd88451SPaolo Bonzini 3753bd88451SPaolo Bonzini /* 3763bd88451SPaolo Bonzini * Get counter of FRC global timer. 3773bd88451SPaolo Bonzini */ 3783bd88451SPaolo Bonzini static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) 3793bd88451SPaolo Bonzini { 3803bd88451SPaolo Bonzini uint64_t count = 0; 3813bd88451SPaolo Bonzini count = ptimer_get_count(s->ptimer_frc); 3823bd88451SPaolo Bonzini count = s->count - count; 3833bd88451SPaolo Bonzini return s->reg.cnt + count; 3843bd88451SPaolo Bonzini } 3853bd88451SPaolo Bonzini 3863bd88451SPaolo Bonzini /* 3873bd88451SPaolo Bonzini * Stop global FRC timer 3889ede4ec0SPeter Maydell * Must be called within exynos4210_gfrc_tx_begin/commit block. 3893bd88451SPaolo Bonzini */ 3903bd88451SPaolo Bonzini static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) 3913bd88451SPaolo Bonzini { 3923bd88451SPaolo Bonzini DPRINTF("global timer frc stop\n"); 3933bd88451SPaolo Bonzini 3943bd88451SPaolo Bonzini ptimer_stop(s->ptimer_frc); 3953bd88451SPaolo Bonzini } 3963bd88451SPaolo Bonzini 3973bd88451SPaolo Bonzini /* 3983bd88451SPaolo Bonzini * Start global FRC timer 3999ede4ec0SPeter Maydell * Must be called within exynos4210_gfrc_tx_begin/commit block. 4003bd88451SPaolo Bonzini */ 4013bd88451SPaolo Bonzini static void exynos4210_gfrc_start(Exynos4210MCTGT *s) 4023bd88451SPaolo Bonzini { 4033bd88451SPaolo Bonzini DPRINTF("global timer frc start\n"); 4043bd88451SPaolo Bonzini 4053bd88451SPaolo Bonzini ptimer_run(s->ptimer_frc, 1); 4063bd88451SPaolo Bonzini } 4073bd88451SPaolo Bonzini 4083bd88451SPaolo Bonzini /* 4099ede4ec0SPeter Maydell * Start ptimer transaction for global FRC timer; this is just for 4109ede4ec0SPeter Maydell * consistency with the way we wrap operations like stop and run. 4119ede4ec0SPeter Maydell */ 4129ede4ec0SPeter Maydell static void exynos4210_gfrc_tx_begin(Exynos4210MCTGT *s) 4139ede4ec0SPeter Maydell { 4149ede4ec0SPeter Maydell ptimer_transaction_begin(s->ptimer_frc); 4159ede4ec0SPeter Maydell } 4169ede4ec0SPeter Maydell 4179ede4ec0SPeter Maydell /* Commit ptimer transaction for global FRC timer. */ 4189ede4ec0SPeter Maydell static void exynos4210_gfrc_tx_commit(Exynos4210MCTGT *s) 4199ede4ec0SPeter Maydell { 4209ede4ec0SPeter Maydell ptimer_transaction_commit(s->ptimer_frc); 4219ede4ec0SPeter Maydell } 4229ede4ec0SPeter Maydell 4239ede4ec0SPeter Maydell /* 4243bd88451SPaolo Bonzini * Find next nearest Comparator. If current Comparator value equals to other 4253bd88451SPaolo Bonzini * Comparator value, skip them both 4263bd88451SPaolo Bonzini */ 4273bd88451SPaolo Bonzini static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) 4283bd88451SPaolo Bonzini { 4293bd88451SPaolo Bonzini int res; 4303bd88451SPaolo Bonzini int i; 4313bd88451SPaolo Bonzini int enabled; 4323bd88451SPaolo Bonzini uint64_t min; 4333bd88451SPaolo Bonzini int min_comp_i; 4343bd88451SPaolo Bonzini uint64_t gfrc; 4353bd88451SPaolo Bonzini uint64_t distance; 4363bd88451SPaolo Bonzini uint64_t distance_min; 4373bd88451SPaolo Bonzini int comp_i; 4383bd88451SPaolo Bonzini 4393bd88451SPaolo Bonzini /* get gfrc count */ 4403bd88451SPaolo Bonzini gfrc = exynos4210_gfrc_get_count(&s->g_timer); 4413bd88451SPaolo Bonzini 4423bd88451SPaolo Bonzini min = UINT64_MAX; 4433bd88451SPaolo Bonzini distance_min = UINT64_MAX; 4443bd88451SPaolo Bonzini comp_i = MCT_GT_CMP_NUM; 4453bd88451SPaolo Bonzini min_comp_i = MCT_GT_CMP_NUM; 4463bd88451SPaolo Bonzini enabled = 0; 4473bd88451SPaolo Bonzini 4483bd88451SPaolo Bonzini /* lookup for nearest comparator */ 4493bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 4503bd88451SPaolo Bonzini 4513bd88451SPaolo Bonzini if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) { 4523bd88451SPaolo Bonzini 4533bd88451SPaolo Bonzini enabled = 1; 4543bd88451SPaolo Bonzini 4553bd88451SPaolo Bonzini if (s->g_timer.reg.comp[i] > gfrc) { 4563bd88451SPaolo Bonzini /* Comparator is upper then FRC */ 4573bd88451SPaolo Bonzini distance = s->g_timer.reg.comp[i] - gfrc; 4583bd88451SPaolo Bonzini 4593bd88451SPaolo Bonzini if (distance <= distance_min) { 4603bd88451SPaolo Bonzini distance_min = distance; 4613bd88451SPaolo Bonzini comp_i = i; 4623bd88451SPaolo Bonzini } 4633bd88451SPaolo Bonzini } else { 4643bd88451SPaolo Bonzini /* Comparator is below FRC, find the smallest */ 4653bd88451SPaolo Bonzini 4663bd88451SPaolo Bonzini if (s->g_timer.reg.comp[i] <= min) { 4673bd88451SPaolo Bonzini min = s->g_timer.reg.comp[i]; 4683bd88451SPaolo Bonzini min_comp_i = i; 4693bd88451SPaolo Bonzini } 4703bd88451SPaolo Bonzini } 4713bd88451SPaolo Bonzini } 4723bd88451SPaolo Bonzini } 4733bd88451SPaolo Bonzini 4743bd88451SPaolo Bonzini if (!enabled) { 4753bd88451SPaolo Bonzini /* All Comparators disabled */ 4763bd88451SPaolo Bonzini res = -1; 4773bd88451SPaolo Bonzini } else if (comp_i < MCT_GT_CMP_NUM) { 4783bd88451SPaolo Bonzini /* Found upper Comparator */ 4793bd88451SPaolo Bonzini res = comp_i; 4803bd88451SPaolo Bonzini } else { 4813bd88451SPaolo Bonzini /* All Comparators are below or equal to FRC */ 4823bd88451SPaolo Bonzini res = min_comp_i; 4833bd88451SPaolo Bonzini } 4843bd88451SPaolo Bonzini 4853bd88451SPaolo Bonzini DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", 4863bd88451SPaolo Bonzini res, 4873bd88451SPaolo Bonzini s->g_timer.reg.comp[res], 4883bd88451SPaolo Bonzini distance_min, 4893bd88451SPaolo Bonzini gfrc); 4903bd88451SPaolo Bonzini 4913bd88451SPaolo Bonzini return res; 4923bd88451SPaolo Bonzini } 4933bd88451SPaolo Bonzini 4943bd88451SPaolo Bonzini /* 4953bd88451SPaolo Bonzini * Get distance to nearest Comparator 4963bd88451SPaolo Bonzini */ 4973bd88451SPaolo Bonzini static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) 4983bd88451SPaolo Bonzini { 4993bd88451SPaolo Bonzini if (id == -1) { 5003bd88451SPaolo Bonzini /* no enabled Comparators, choose max distance */ 5013bd88451SPaolo Bonzini return MCT_GT_COUNTER_STEP; 5023bd88451SPaolo Bonzini } 5033bd88451SPaolo Bonzini if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) { 5043bd88451SPaolo Bonzini return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt; 5053bd88451SPaolo Bonzini } else { 5063bd88451SPaolo Bonzini return MCT_GT_COUNTER_STEP; 5073bd88451SPaolo Bonzini } 5083bd88451SPaolo Bonzini } 5093bd88451SPaolo Bonzini 5103bd88451SPaolo Bonzini /* 5113bd88451SPaolo Bonzini * Restart global FRC timer 5129ede4ec0SPeter Maydell * Must be called within exynos4210_gfrc_tx_begin/commit block. 5133bd88451SPaolo Bonzini */ 5143bd88451SPaolo Bonzini static void exynos4210_gfrc_restart(Exynos4210MCTState *s) 5153bd88451SPaolo Bonzini { 5163bd88451SPaolo Bonzini uint64_t distance; 5173bd88451SPaolo Bonzini 5183bd88451SPaolo Bonzini exynos4210_gfrc_stop(&s->g_timer); 5193bd88451SPaolo Bonzini 5203bd88451SPaolo Bonzini s->g_timer.curr_comp = exynos4210_gcomp_find(s); 5213bd88451SPaolo Bonzini 5223bd88451SPaolo Bonzini distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); 5233bd88451SPaolo Bonzini 5243bd88451SPaolo Bonzini if (distance > MCT_GT_COUNTER_STEP || !distance) { 5253bd88451SPaolo Bonzini distance = MCT_GT_COUNTER_STEP; 5263bd88451SPaolo Bonzini } 5273bd88451SPaolo Bonzini 5283bd88451SPaolo Bonzini exynos4210_gfrc_set_count(&s->g_timer, distance); 5293bd88451SPaolo Bonzini exynos4210_gfrc_start(&s->g_timer); 5303bd88451SPaolo Bonzini } 5313bd88451SPaolo Bonzini 5323bd88451SPaolo Bonzini /* 5333bd88451SPaolo Bonzini * Raise global timer CMP IRQ 5343bd88451SPaolo Bonzini */ 5353bd88451SPaolo Bonzini static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id) 5363bd88451SPaolo Bonzini { 5373bd88451SPaolo Bonzini Exynos4210MCTGT *s = opaque; 5383bd88451SPaolo Bonzini 5393bd88451SPaolo Bonzini /* If CSTAT is pending and IRQ is enabled */ 5403bd88451SPaolo Bonzini if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) && 5413bd88451SPaolo Bonzini (s->reg.int_enb & G_INT_ENABLE(id))) { 5423bd88451SPaolo Bonzini DPRINTF("gcmp timer[%d] IRQ\n", id); 5433bd88451SPaolo Bonzini qemu_irq_raise(s->irq[id]); 5443bd88451SPaolo Bonzini } 5453bd88451SPaolo Bonzini } 5463bd88451SPaolo Bonzini 5473bd88451SPaolo Bonzini /* 5483bd88451SPaolo Bonzini * Lower global timer CMP IRQ 5493bd88451SPaolo Bonzini */ 5503bd88451SPaolo Bonzini static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id) 5513bd88451SPaolo Bonzini { 5523bd88451SPaolo Bonzini Exynos4210MCTGT *s = opaque; 5533bd88451SPaolo Bonzini qemu_irq_lower(s->irq[id]); 5543bd88451SPaolo Bonzini } 5553bd88451SPaolo Bonzini 5563bd88451SPaolo Bonzini /* 5573bd88451SPaolo Bonzini * Global timer FRC event handler. 5583bd88451SPaolo Bonzini * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP 5593bd88451SPaolo Bonzini * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value 5603bd88451SPaolo Bonzini */ 5613bd88451SPaolo Bonzini static void exynos4210_gfrc_event(void *opaque) 5623bd88451SPaolo Bonzini { 5633bd88451SPaolo Bonzini Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; 5643bd88451SPaolo Bonzini int i; 5653bd88451SPaolo Bonzini uint64_t distance; 5663bd88451SPaolo Bonzini 5673bd88451SPaolo Bonzini DPRINTF("\n"); 5683bd88451SPaolo Bonzini 5693bd88451SPaolo Bonzini s->g_timer.reg.cnt += s->g_timer.count; 5703bd88451SPaolo Bonzini 5713bd88451SPaolo Bonzini /* Process all comparators */ 5723bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 5733bd88451SPaolo Bonzini 5743bd88451SPaolo Bonzini if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) { 5753bd88451SPaolo Bonzini /* reached nearest comparator */ 5763bd88451SPaolo Bonzini 5773bd88451SPaolo Bonzini s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i); 5783bd88451SPaolo Bonzini 5793bd88451SPaolo Bonzini /* Auto increment */ 5803bd88451SPaolo Bonzini if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) { 5813bd88451SPaolo Bonzini s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i]; 5823bd88451SPaolo Bonzini } 5833bd88451SPaolo Bonzini 5843bd88451SPaolo Bonzini /* IRQ */ 5853bd88451SPaolo Bonzini exynos4210_gcomp_raise_irq(&s->g_timer, i); 5863bd88451SPaolo Bonzini } 5873bd88451SPaolo Bonzini } 5883bd88451SPaolo Bonzini 5893bd88451SPaolo Bonzini /* Reload FRC to reach nearest comparator */ 5903bd88451SPaolo Bonzini s->g_timer.curr_comp = exynos4210_gcomp_find(s); 5913bd88451SPaolo Bonzini distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); 5923bd88451SPaolo Bonzini if (distance > MCT_GT_COUNTER_STEP || !distance) { 5933bd88451SPaolo Bonzini distance = MCT_GT_COUNTER_STEP; 5943bd88451SPaolo Bonzini } 5953bd88451SPaolo Bonzini exynos4210_gfrc_set_count(&s->g_timer, distance); 5963bd88451SPaolo Bonzini 5973bd88451SPaolo Bonzini exynos4210_gfrc_start(&s->g_timer); 5983bd88451SPaolo Bonzini } 5993bd88451SPaolo Bonzini 6003bd88451SPaolo Bonzini /* 6013bd88451SPaolo Bonzini * Get counter of FRC local timer. 6023bd88451SPaolo Bonzini */ 6033bd88451SPaolo Bonzini static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) 6043bd88451SPaolo Bonzini { 6053bd88451SPaolo Bonzini return ptimer_get_count(s->ptimer_frc); 6063bd88451SPaolo Bonzini } 6073bd88451SPaolo Bonzini 6083bd88451SPaolo Bonzini /* 6093bd88451SPaolo Bonzini * Set counter of FRC local timer. 61050f07d76SPeter Maydell * Must be called from within exynos4210_lfrc_tx_begin/commit block. 6113bd88451SPaolo Bonzini */ 6123bd88451SPaolo Bonzini static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) 6133bd88451SPaolo Bonzini { 6143bd88451SPaolo Bonzini if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) { 6153bd88451SPaolo Bonzini ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP); 6163bd88451SPaolo Bonzini } else { 6173bd88451SPaolo Bonzini ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]); 6183bd88451SPaolo Bonzini } 6193bd88451SPaolo Bonzini } 6203bd88451SPaolo Bonzini 6213bd88451SPaolo Bonzini /* 6223bd88451SPaolo Bonzini * Start local FRC timer 62350f07d76SPeter Maydell * Must be called from within exynos4210_lfrc_tx_begin/commit block. 6243bd88451SPaolo Bonzini */ 6253bd88451SPaolo Bonzini static void exynos4210_lfrc_start(Exynos4210MCTLT *s) 6263bd88451SPaolo Bonzini { 6273bd88451SPaolo Bonzini ptimer_run(s->ptimer_frc, 1); 6283bd88451SPaolo Bonzini } 6293bd88451SPaolo Bonzini 6303bd88451SPaolo Bonzini /* 6313bd88451SPaolo Bonzini * Stop local FRC timer 63250f07d76SPeter Maydell * Must be called from within exynos4210_lfrc_tx_begin/commit block. 6333bd88451SPaolo Bonzini */ 6343bd88451SPaolo Bonzini static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) 6353bd88451SPaolo Bonzini { 6363bd88451SPaolo Bonzini ptimer_stop(s->ptimer_frc); 6373bd88451SPaolo Bonzini } 6383bd88451SPaolo Bonzini 63950f07d76SPeter Maydell /* Start ptimer transaction for local FRC timer */ 64050f07d76SPeter Maydell static void exynos4210_lfrc_tx_begin(Exynos4210MCTLT *s) 64150f07d76SPeter Maydell { 64250f07d76SPeter Maydell ptimer_transaction_begin(s->ptimer_frc); 64350f07d76SPeter Maydell } 64450f07d76SPeter Maydell 64550f07d76SPeter Maydell /* Commit ptimer transaction for local FRC timer */ 64650f07d76SPeter Maydell static void exynos4210_lfrc_tx_commit(Exynos4210MCTLT *s) 64750f07d76SPeter Maydell { 64850f07d76SPeter Maydell ptimer_transaction_commit(s->ptimer_frc); 64950f07d76SPeter Maydell } 65050f07d76SPeter Maydell 6513bd88451SPaolo Bonzini /* 6523bd88451SPaolo Bonzini * Local timer free running counter tick handler 6533bd88451SPaolo Bonzini */ 6543bd88451SPaolo Bonzini static void exynos4210_lfrc_event(void *opaque) 6553bd88451SPaolo Bonzini { 6563bd88451SPaolo Bonzini Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; 6573bd88451SPaolo Bonzini 6583bd88451SPaolo Bonzini /* local frc expired */ 6593bd88451SPaolo Bonzini 6603bd88451SPaolo Bonzini DPRINTF("\n"); 6613bd88451SPaolo Bonzini 6623bd88451SPaolo Bonzini s->reg.int_cstat |= L_INT_CSTAT_FRCCNT; 6633bd88451SPaolo Bonzini 6643bd88451SPaolo Bonzini /* update frc counter */ 6653bd88451SPaolo Bonzini exynos4210_lfrc_update_count(s); 6663bd88451SPaolo Bonzini 6673bd88451SPaolo Bonzini /* raise irq */ 6683bd88451SPaolo Bonzini if (s->reg.int_enb & L_INT_INTENB_FRCEIE) { 6693bd88451SPaolo Bonzini qemu_irq_raise(s->irq); 6703bd88451SPaolo Bonzini } 6713bd88451SPaolo Bonzini 6723bd88451SPaolo Bonzini /* we reached here, this means that timer is enabled */ 6733bd88451SPaolo Bonzini exynos4210_lfrc_start(s); 6743bd88451SPaolo Bonzini } 6753bd88451SPaolo Bonzini 6763bd88451SPaolo Bonzini static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s); 6773bd88451SPaolo Bonzini static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s); 6783bd88451SPaolo Bonzini static void exynos4210_ltick_recalc_count(struct tick_timer *s); 6793bd88451SPaolo Bonzini 6803bd88451SPaolo Bonzini /* 6813bd88451SPaolo Bonzini * Action on enabling local tick int timer 6823bd88451SPaolo Bonzini */ 6833bd88451SPaolo Bonzini static void exynos4210_ltick_int_start(struct tick_timer *s) 6843bd88451SPaolo Bonzini { 6853bd88451SPaolo Bonzini if (!s->int_run) { 6863bd88451SPaolo Bonzini s->int_run = 1; 6873bd88451SPaolo Bonzini } 6883bd88451SPaolo Bonzini } 6893bd88451SPaolo Bonzini 6903bd88451SPaolo Bonzini /* 6913bd88451SPaolo Bonzini * Action on disabling local tick int timer 6923bd88451SPaolo Bonzini */ 6933bd88451SPaolo Bonzini static void exynos4210_ltick_int_stop(struct tick_timer *s) 6943bd88451SPaolo Bonzini { 6953bd88451SPaolo Bonzini if (s->int_run) { 6963bd88451SPaolo Bonzini s->last_icnto = exynos4210_ltick_int_get_cnto(s); 6973bd88451SPaolo Bonzini s->int_run = 0; 6983bd88451SPaolo Bonzini } 6993bd88451SPaolo Bonzini } 7003bd88451SPaolo Bonzini 7013bd88451SPaolo Bonzini /* 7023bd88451SPaolo Bonzini * Get count for INT timer 7033bd88451SPaolo Bonzini */ 7043bd88451SPaolo Bonzini static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) 7053bd88451SPaolo Bonzini { 7063bd88451SPaolo Bonzini uint32_t icnto; 7073bd88451SPaolo Bonzini uint64_t remain; 7083bd88451SPaolo Bonzini uint64_t count; 7093bd88451SPaolo Bonzini uint64_t counted; 7103bd88451SPaolo Bonzini uint64_t cur_progress; 7113bd88451SPaolo Bonzini 7123bd88451SPaolo Bonzini count = ptimer_get_count(s->ptimer_tick); 7133bd88451SPaolo Bonzini if (count) { 7143bd88451SPaolo Bonzini /* timer is still counting, called not from event */ 7153bd88451SPaolo Bonzini counted = s->count - ptimer_get_count(s->ptimer_tick); 7163bd88451SPaolo Bonzini cur_progress = s->progress + counted; 7173bd88451SPaolo Bonzini } else { 7183bd88451SPaolo Bonzini /* timer expired earlier */ 7193bd88451SPaolo Bonzini cur_progress = s->progress; 7203bd88451SPaolo Bonzini } 7213bd88451SPaolo Bonzini 7223bd88451SPaolo Bonzini remain = s->distance - cur_progress; 7233bd88451SPaolo Bonzini 7243bd88451SPaolo Bonzini if (!s->int_run) { 7253bd88451SPaolo Bonzini /* INT is stopped. */ 7263bd88451SPaolo Bonzini icnto = s->last_icnto; 7273bd88451SPaolo Bonzini } else { 7283bd88451SPaolo Bonzini /* Both are counting */ 7293bd88451SPaolo Bonzini icnto = remain / s->tcntb; 7303bd88451SPaolo Bonzini } 7313bd88451SPaolo Bonzini 7323bd88451SPaolo Bonzini return icnto; 7333bd88451SPaolo Bonzini } 7343bd88451SPaolo Bonzini 7353bd88451SPaolo Bonzini /* 7363bd88451SPaolo Bonzini * Start local tick cnt timer. 7376c27ee94SPeter Maydell * Must be called within exynos4210_ltick_tx_begin/commit block. 7383bd88451SPaolo Bonzini */ 7393bd88451SPaolo Bonzini static void exynos4210_ltick_cnt_start(struct tick_timer *s) 7403bd88451SPaolo Bonzini { 7413bd88451SPaolo Bonzini if (!s->cnt_run) { 7423bd88451SPaolo Bonzini 7433bd88451SPaolo Bonzini exynos4210_ltick_recalc_count(s); 7443bd88451SPaolo Bonzini ptimer_set_count(s->ptimer_tick, s->count); 7453bd88451SPaolo Bonzini ptimer_run(s->ptimer_tick, 1); 7463bd88451SPaolo Bonzini 7473bd88451SPaolo Bonzini s->cnt_run = 1; 7483bd88451SPaolo Bonzini } 7493bd88451SPaolo Bonzini } 7503bd88451SPaolo Bonzini 7513bd88451SPaolo Bonzini /* 7523bd88451SPaolo Bonzini * Stop local tick cnt timer. 7536c27ee94SPeter Maydell * Must be called within exynos4210_ltick_tx_begin/commit block. 7543bd88451SPaolo Bonzini */ 7553bd88451SPaolo Bonzini static void exynos4210_ltick_cnt_stop(struct tick_timer *s) 7563bd88451SPaolo Bonzini { 7573bd88451SPaolo Bonzini if (s->cnt_run) { 7583bd88451SPaolo Bonzini 7593bd88451SPaolo Bonzini s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s); 7603bd88451SPaolo Bonzini 7613bd88451SPaolo Bonzini if (s->int_run) { 7623bd88451SPaolo Bonzini exynos4210_ltick_int_stop(s); 7633bd88451SPaolo Bonzini } 7643bd88451SPaolo Bonzini 7653bd88451SPaolo Bonzini ptimer_stop(s->ptimer_tick); 7663bd88451SPaolo Bonzini 7673bd88451SPaolo Bonzini s->cnt_run = 0; 7683bd88451SPaolo Bonzini } 7693bd88451SPaolo Bonzini } 7703bd88451SPaolo Bonzini 7716c27ee94SPeter Maydell /* Start ptimer transaction for local tick timer */ 7726c27ee94SPeter Maydell static void exynos4210_ltick_tx_begin(struct tick_timer *s) 7736c27ee94SPeter Maydell { 7746c27ee94SPeter Maydell ptimer_transaction_begin(s->ptimer_tick); 7756c27ee94SPeter Maydell } 7766c27ee94SPeter Maydell 7776c27ee94SPeter Maydell /* Commit ptimer transaction for local tick timer */ 7786c27ee94SPeter Maydell static void exynos4210_ltick_tx_commit(struct tick_timer *s) 7796c27ee94SPeter Maydell { 7806c27ee94SPeter Maydell ptimer_transaction_commit(s->ptimer_tick); 7816c27ee94SPeter Maydell } 7826c27ee94SPeter Maydell 7833bd88451SPaolo Bonzini /* 7843bd88451SPaolo Bonzini * Get counter for CNT timer 7853bd88451SPaolo Bonzini */ 7863bd88451SPaolo Bonzini static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) 7873bd88451SPaolo Bonzini { 7883bd88451SPaolo Bonzini uint32_t tcnto; 7893bd88451SPaolo Bonzini uint32_t icnto; 7903bd88451SPaolo Bonzini uint64_t remain; 7913bd88451SPaolo Bonzini uint64_t counted; 7923bd88451SPaolo Bonzini uint64_t count; 7933bd88451SPaolo Bonzini uint64_t cur_progress; 7943bd88451SPaolo Bonzini 7953bd88451SPaolo Bonzini count = ptimer_get_count(s->ptimer_tick); 7963bd88451SPaolo Bonzini if (count) { 7973bd88451SPaolo Bonzini /* timer is still counting, called not from event */ 7983bd88451SPaolo Bonzini counted = s->count - ptimer_get_count(s->ptimer_tick); 7993bd88451SPaolo Bonzini cur_progress = s->progress + counted; 8003bd88451SPaolo Bonzini } else { 8013bd88451SPaolo Bonzini /* timer expired earlier */ 8023bd88451SPaolo Bonzini cur_progress = s->progress; 8033bd88451SPaolo Bonzini } 8043bd88451SPaolo Bonzini 8053bd88451SPaolo Bonzini remain = s->distance - cur_progress; 8063bd88451SPaolo Bonzini 8073bd88451SPaolo Bonzini if (!s->cnt_run) { 8083bd88451SPaolo Bonzini /* Both are stopped. */ 8093bd88451SPaolo Bonzini tcnto = s->last_tcnto; 8103bd88451SPaolo Bonzini } else if (!s->int_run) { 8113bd88451SPaolo Bonzini /* INT counter is stopped, progress is by CNT timer */ 8123bd88451SPaolo Bonzini tcnto = remain % s->tcntb; 8133bd88451SPaolo Bonzini } else { 8143bd88451SPaolo Bonzini /* Both are counting */ 8153bd88451SPaolo Bonzini icnto = remain / s->tcntb; 8163bd88451SPaolo Bonzini if (icnto) { 8173bd88451SPaolo Bonzini tcnto = remain % (icnto * s->tcntb); 8183bd88451SPaolo Bonzini } else { 8193bd88451SPaolo Bonzini tcnto = remain % s->tcntb; 8203bd88451SPaolo Bonzini } 8213bd88451SPaolo Bonzini } 8223bd88451SPaolo Bonzini 8233bd88451SPaolo Bonzini return tcnto; 8243bd88451SPaolo Bonzini } 8253bd88451SPaolo Bonzini 8263bd88451SPaolo Bonzini /* 8273bd88451SPaolo Bonzini * Set new values of counters for CNT and INT timers 8286c27ee94SPeter Maydell * Must be called within exynos4210_ltick_tx_begin/commit block. 8293bd88451SPaolo Bonzini */ 8303bd88451SPaolo Bonzini static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, 8313bd88451SPaolo Bonzini uint32_t new_int) 8323bd88451SPaolo Bonzini { 8333bd88451SPaolo Bonzini uint32_t cnt_stopped = 0; 8343bd88451SPaolo Bonzini uint32_t int_stopped = 0; 8353bd88451SPaolo Bonzini 8363bd88451SPaolo Bonzini if (s->cnt_run) { 8373bd88451SPaolo Bonzini exynos4210_ltick_cnt_stop(s); 8383bd88451SPaolo Bonzini cnt_stopped = 1; 8393bd88451SPaolo Bonzini } 8403bd88451SPaolo Bonzini 8413bd88451SPaolo Bonzini if (s->int_run) { 8423bd88451SPaolo Bonzini exynos4210_ltick_int_stop(s); 8433bd88451SPaolo Bonzini int_stopped = 1; 8443bd88451SPaolo Bonzini } 8453bd88451SPaolo Bonzini 8463bd88451SPaolo Bonzini s->tcntb = new_cnt + 1; 8473bd88451SPaolo Bonzini s->icntb = new_int + 1; 8483bd88451SPaolo Bonzini 8493bd88451SPaolo Bonzini if (cnt_stopped) { 8503bd88451SPaolo Bonzini exynos4210_ltick_cnt_start(s); 8513bd88451SPaolo Bonzini } 8523bd88451SPaolo Bonzini if (int_stopped) { 8533bd88451SPaolo Bonzini exynos4210_ltick_int_start(s); 8543bd88451SPaolo Bonzini } 8553bd88451SPaolo Bonzini 8563bd88451SPaolo Bonzini } 8573bd88451SPaolo Bonzini 8583bd88451SPaolo Bonzini /* 8593bd88451SPaolo Bonzini * Calculate new counter value for tick timer 8603bd88451SPaolo Bonzini */ 8613bd88451SPaolo Bonzini static void exynos4210_ltick_recalc_count(struct tick_timer *s) 8623bd88451SPaolo Bonzini { 8633bd88451SPaolo Bonzini uint64_t to_count; 8643bd88451SPaolo Bonzini 8653bd88451SPaolo Bonzini if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) { 8663bd88451SPaolo Bonzini /* 8673bd88451SPaolo Bonzini * one or both timers run and not counted to the end; 8683bd88451SPaolo Bonzini * distance is not passed, recalculate with last_tcnto * last_icnto 8693bd88451SPaolo Bonzini */ 8703bd88451SPaolo Bonzini 8713bd88451SPaolo Bonzini if (s->last_tcnto) { 872c3a699beSPeter Maydell to_count = (uint64_t)s->last_tcnto * s->last_icnto; 8733bd88451SPaolo Bonzini } else { 8743bd88451SPaolo Bonzini to_count = s->last_icnto; 8753bd88451SPaolo Bonzini } 8763bd88451SPaolo Bonzini } else { 8773bd88451SPaolo Bonzini /* distance is passed, recalculate with tcnto * icnto */ 8783bd88451SPaolo Bonzini if (s->icntb) { 879c3a699beSPeter Maydell s->distance = (uint64_t)s->tcntb * s->icntb; 8803bd88451SPaolo Bonzini } else { 8813bd88451SPaolo Bonzini s->distance = s->tcntb; 8823bd88451SPaolo Bonzini } 8833bd88451SPaolo Bonzini 8843bd88451SPaolo Bonzini to_count = s->distance; 8853bd88451SPaolo Bonzini s->progress = 0; 8863bd88451SPaolo Bonzini } 8873bd88451SPaolo Bonzini 8883bd88451SPaolo Bonzini if (to_count > MCT_LT_COUNTER_STEP) { 8893bd88451SPaolo Bonzini /* count by step */ 8903bd88451SPaolo Bonzini s->count = MCT_LT_COUNTER_STEP; 8913bd88451SPaolo Bonzini } else { 8923bd88451SPaolo Bonzini s->count = to_count; 8933bd88451SPaolo Bonzini } 8943bd88451SPaolo Bonzini } 8953bd88451SPaolo Bonzini 8963bd88451SPaolo Bonzini /* 8973bd88451SPaolo Bonzini * Initialize tick_timer 8983bd88451SPaolo Bonzini */ 8993bd88451SPaolo Bonzini static void exynos4210_ltick_timer_init(struct tick_timer *s) 9003bd88451SPaolo Bonzini { 9013bd88451SPaolo Bonzini exynos4210_ltick_int_stop(s); 9026c27ee94SPeter Maydell exynos4210_ltick_tx_begin(s); 9033bd88451SPaolo Bonzini exynos4210_ltick_cnt_stop(s); 9046c27ee94SPeter Maydell exynos4210_ltick_tx_commit(s); 9053bd88451SPaolo Bonzini 9063bd88451SPaolo Bonzini s->count = 0; 9073bd88451SPaolo Bonzini s->distance = 0; 9083bd88451SPaolo Bonzini s->progress = 0; 9093bd88451SPaolo Bonzini s->icntb = 0; 9103bd88451SPaolo Bonzini s->tcntb = 0; 9113bd88451SPaolo Bonzini } 9123bd88451SPaolo Bonzini 9133bd88451SPaolo Bonzini /* 9143bd88451SPaolo Bonzini * tick_timer event. 9153bd88451SPaolo Bonzini * Raises when abstract tick_timer expires. 9163bd88451SPaolo Bonzini */ 9173bd88451SPaolo Bonzini static void exynos4210_ltick_timer_event(struct tick_timer *s) 9183bd88451SPaolo Bonzini { 9193bd88451SPaolo Bonzini s->progress += s->count; 9203bd88451SPaolo Bonzini } 9213bd88451SPaolo Bonzini 9223bd88451SPaolo Bonzini /* 9233bd88451SPaolo Bonzini * Local timer tick counter handler. 9243bd88451SPaolo Bonzini * Don't use reloaded timers. If timer counter = zero 9253bd88451SPaolo Bonzini * then handler called but after handler finished no 9263bd88451SPaolo Bonzini * timer reload occurs. 9273bd88451SPaolo Bonzini */ 9283bd88451SPaolo Bonzini static void exynos4210_ltick_event(void *opaque) 9293bd88451SPaolo Bonzini { 9303bd88451SPaolo Bonzini Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; 9313bd88451SPaolo Bonzini uint32_t tcnto; 9323bd88451SPaolo Bonzini uint32_t icnto; 9333bd88451SPaolo Bonzini #ifdef DEBUG_MCT 9343bd88451SPaolo Bonzini static uint64_t time1[2] = {0}; 9353bd88451SPaolo Bonzini static uint64_t time2[2] = {0}; 9363bd88451SPaolo Bonzini #endif 9373bd88451SPaolo Bonzini 9383bd88451SPaolo Bonzini /* Call tick_timer event handler, it will update its tcntb and icntb. */ 9393bd88451SPaolo Bonzini exynos4210_ltick_timer_event(&s->tick_timer); 9403bd88451SPaolo Bonzini 9413bd88451SPaolo Bonzini /* get tick_timer cnt */ 9423bd88451SPaolo Bonzini tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer); 9433bd88451SPaolo Bonzini 9443bd88451SPaolo Bonzini /* get tick_timer int */ 9453bd88451SPaolo Bonzini icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer); 9463bd88451SPaolo Bonzini 9473bd88451SPaolo Bonzini /* raise IRQ if needed */ 9483bd88451SPaolo Bonzini if (!icnto && s->reg.tcon & L_TCON_INT_START) { 9493bd88451SPaolo Bonzini /* INT counter enabled and expired */ 9503bd88451SPaolo Bonzini 9513bd88451SPaolo Bonzini s->reg.int_cstat |= L_INT_CSTAT_INTCNT; 9523bd88451SPaolo Bonzini 9533bd88451SPaolo Bonzini /* raise interrupt if enabled */ 9543bd88451SPaolo Bonzini if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) { 9553bd88451SPaolo Bonzini #ifdef DEBUG_MCT 956bc72ad67SAlex Bligh time2[s->id] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 9573bd88451SPaolo Bonzini DPRINTF("local timer[%d] IRQ: %llx\n", s->id, 9583bd88451SPaolo Bonzini time2[s->id] - time1[s->id]); 9593bd88451SPaolo Bonzini time1[s->id] = time2[s->id]; 9603bd88451SPaolo Bonzini #endif 9613bd88451SPaolo Bonzini qemu_irq_raise(s->irq); 9623bd88451SPaolo Bonzini } 9633bd88451SPaolo Bonzini 9643bd88451SPaolo Bonzini /* reload ICNTB */ 9653bd88451SPaolo Bonzini if (s->reg.tcon & L_TCON_INTERVAL_MODE) { 9663bd88451SPaolo Bonzini exynos4210_ltick_set_cntb(&s->tick_timer, 9673bd88451SPaolo Bonzini s->reg.cnt[L_REG_CNT_TCNTB], 9683bd88451SPaolo Bonzini s->reg.cnt[L_REG_CNT_ICNTB]); 9693bd88451SPaolo Bonzini } 9703bd88451SPaolo Bonzini } else { 9713bd88451SPaolo Bonzini /* reload TCNTB */ 9723bd88451SPaolo Bonzini if (!tcnto) { 9733bd88451SPaolo Bonzini exynos4210_ltick_set_cntb(&s->tick_timer, 9743bd88451SPaolo Bonzini s->reg.cnt[L_REG_CNT_TCNTB], 9753bd88451SPaolo Bonzini icnto); 9763bd88451SPaolo Bonzini } 9773bd88451SPaolo Bonzini } 9783bd88451SPaolo Bonzini 9793bd88451SPaolo Bonzini /* start tick_timer cnt */ 9803bd88451SPaolo Bonzini exynos4210_ltick_cnt_start(&s->tick_timer); 9813bd88451SPaolo Bonzini 9823bd88451SPaolo Bonzini /* start tick_timer int */ 9833bd88451SPaolo Bonzini exynos4210_ltick_int_start(&s->tick_timer); 9843bd88451SPaolo Bonzini } 9853bd88451SPaolo Bonzini 9869ede4ec0SPeter Maydell static void tx_ptimer_set_freq(ptimer_state *s, uint32_t freq) 9879ede4ec0SPeter Maydell { 9889ede4ec0SPeter Maydell /* 9899ede4ec0SPeter Maydell * callers of exynos4210_mct_update_freq() never do anything 9909ede4ec0SPeter Maydell * else that needs to be in the same ptimer transaction, so 9919ede4ec0SPeter Maydell * to avoid a lot of repetition we have a convenience function 9929ede4ec0SPeter Maydell * for begin/set_freq/commit. 9939ede4ec0SPeter Maydell */ 9949ede4ec0SPeter Maydell ptimer_transaction_begin(s); 9959ede4ec0SPeter Maydell ptimer_set_freq(s, freq); 9969ede4ec0SPeter Maydell ptimer_transaction_commit(s); 9979ede4ec0SPeter Maydell } 9989ede4ec0SPeter Maydell 9993bd88451SPaolo Bonzini /* update timer frequency */ 10003bd88451SPaolo Bonzini static void exynos4210_mct_update_freq(Exynos4210MCTState *s) 10013bd88451SPaolo Bonzini { 10023bd88451SPaolo Bonzini uint32_t freq = s->freq; 10033bd88451SPaolo Bonzini s->freq = 24000000 / 10043bd88451SPaolo Bonzini ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg) + 1) * 10053bd88451SPaolo Bonzini MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); 10063bd88451SPaolo Bonzini 10073bd88451SPaolo Bonzini if (freq != s->freq) { 10083bd88451SPaolo Bonzini DPRINTF("freq=%dHz\n", s->freq); 10093bd88451SPaolo Bonzini 10103bd88451SPaolo Bonzini /* global timer */ 10119ede4ec0SPeter Maydell tx_ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); 10123bd88451SPaolo Bonzini 10133bd88451SPaolo Bonzini /* local timer */ 10146c27ee94SPeter Maydell tx_ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); 101550f07d76SPeter Maydell tx_ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); 10166c27ee94SPeter Maydell tx_ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); 101750f07d76SPeter Maydell tx_ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); 10183bd88451SPaolo Bonzini } 10193bd88451SPaolo Bonzini } 10203bd88451SPaolo Bonzini 10213bd88451SPaolo Bonzini /* set defaul_timer values for all fields */ 10223bd88451SPaolo Bonzini static void exynos4210_mct_reset(DeviceState *d) 10233bd88451SPaolo Bonzini { 102481e1010dSAndreas Färber Exynos4210MCTState *s = EXYNOS4210_MCT(d); 10253bd88451SPaolo Bonzini uint32_t i; 10263bd88451SPaolo Bonzini 10273bd88451SPaolo Bonzini s->reg_mct_cfg = 0; 10283bd88451SPaolo Bonzini 10293bd88451SPaolo Bonzini /* global timer */ 10303bd88451SPaolo Bonzini memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); 10319ede4ec0SPeter Maydell exynos4210_gfrc_tx_begin(&s->g_timer); 10323bd88451SPaolo Bonzini exynos4210_gfrc_stop(&s->g_timer); 10339ede4ec0SPeter Maydell exynos4210_gfrc_tx_commit(&s->g_timer); 10343bd88451SPaolo Bonzini 10353bd88451SPaolo Bonzini /* local timer */ 10363bd88451SPaolo Bonzini memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); 10373bd88451SPaolo Bonzini memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt)); 10383bd88451SPaolo Bonzini for (i = 0; i < 2; i++) { 10393bd88451SPaolo Bonzini s->l_timer[i].reg.int_cstat = 0; 10403bd88451SPaolo Bonzini s->l_timer[i].reg.int_enb = 0; 10413bd88451SPaolo Bonzini s->l_timer[i].reg.tcon = 0; 10423bd88451SPaolo Bonzini s->l_timer[i].reg.wstat = 0; 10433bd88451SPaolo Bonzini s->l_timer[i].tick_timer.count = 0; 10443bd88451SPaolo Bonzini s->l_timer[i].tick_timer.distance = 0; 10453bd88451SPaolo Bonzini s->l_timer[i].tick_timer.progress = 0; 104650f07d76SPeter Maydell exynos4210_lfrc_tx_begin(&s->l_timer[i]); 10473bd88451SPaolo Bonzini ptimer_stop(s->l_timer[i].ptimer_frc); 104850f07d76SPeter Maydell exynos4210_lfrc_tx_commit(&s->l_timer[i]); 10493bd88451SPaolo Bonzini 10503bd88451SPaolo Bonzini exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); 10513bd88451SPaolo Bonzini } 10523bd88451SPaolo Bonzini 10533bd88451SPaolo Bonzini exynos4210_mct_update_freq(s); 10543bd88451SPaolo Bonzini 10553bd88451SPaolo Bonzini } 10563bd88451SPaolo Bonzini 10573bd88451SPaolo Bonzini /* Multi Core Timer read */ 10583bd88451SPaolo Bonzini static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, 10593bd88451SPaolo Bonzini unsigned size) 10603bd88451SPaolo Bonzini { 10613bd88451SPaolo Bonzini Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; 10623bd88451SPaolo Bonzini int index; 10633bd88451SPaolo Bonzini int shift; 10643bd88451SPaolo Bonzini uint64_t count; 10653bd88451SPaolo Bonzini uint32_t value; 10663bd88451SPaolo Bonzini int lt_i; 10673bd88451SPaolo Bonzini 10683bd88451SPaolo Bonzini switch (offset) { 10693bd88451SPaolo Bonzini 10703bd88451SPaolo Bonzini case MCT_CFG: 10713bd88451SPaolo Bonzini value = s->reg_mct_cfg; 10723bd88451SPaolo Bonzini break; 10733bd88451SPaolo Bonzini 10743bd88451SPaolo Bonzini case G_CNT_L: case G_CNT_U: 10753bd88451SPaolo Bonzini shift = 8 * (offset & 0x4); 10763bd88451SPaolo Bonzini count = exynos4210_gfrc_get_count(&s->g_timer); 10773bd88451SPaolo Bonzini value = UINT32_MAX & (count >> shift); 10783bd88451SPaolo Bonzini DPRINTF("read FRC=0x%llx\n", count); 10793bd88451SPaolo Bonzini break; 10803bd88451SPaolo Bonzini 10813bd88451SPaolo Bonzini case G_CNT_WSTAT: 10823bd88451SPaolo Bonzini value = s->g_timer.reg.cnt_wstat; 10833bd88451SPaolo Bonzini break; 10843bd88451SPaolo Bonzini 10853bd88451SPaolo Bonzini case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): 10863bd88451SPaolo Bonzini case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): 10873bd88451SPaolo Bonzini index = GET_G_COMP_IDX(offset); 10883bd88451SPaolo Bonzini shift = 8 * (offset & 0x4); 10893bd88451SPaolo Bonzini value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); 10903bd88451SPaolo Bonzini break; 10913bd88451SPaolo Bonzini 10923bd88451SPaolo Bonzini case G_TCON: 10933bd88451SPaolo Bonzini value = s->g_timer.reg.tcon; 10943bd88451SPaolo Bonzini break; 10953bd88451SPaolo Bonzini 10963bd88451SPaolo Bonzini case G_INT_CSTAT: 10973bd88451SPaolo Bonzini value = s->g_timer.reg.int_cstat; 10983bd88451SPaolo Bonzini break; 10993bd88451SPaolo Bonzini 11003bd88451SPaolo Bonzini case G_INT_ENB: 11013bd88451SPaolo Bonzini value = s->g_timer.reg.int_enb; 11023bd88451SPaolo Bonzini break; 11033bd88451SPaolo Bonzini case G_WSTAT: 11043bd88451SPaolo Bonzini value = s->g_timer.reg.wstat; 11053bd88451SPaolo Bonzini break; 11063bd88451SPaolo Bonzini 11073bd88451SPaolo Bonzini case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: 11083bd88451SPaolo Bonzini case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: 11093bd88451SPaolo Bonzini value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)]; 11103bd88451SPaolo Bonzini break; 11113bd88451SPaolo Bonzini 11123bd88451SPaolo Bonzini /* Local timers */ 11133bd88451SPaolo Bonzini case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB: 11143bd88451SPaolo Bonzini case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB: 11153bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 11163bd88451SPaolo Bonzini index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); 11173bd88451SPaolo Bonzini value = s->l_timer[lt_i].reg.cnt[index]; 11183bd88451SPaolo Bonzini break; 11193bd88451SPaolo Bonzini 11203bd88451SPaolo Bonzini case L0_TCNTO: case L1_TCNTO: 11213bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 11223bd88451SPaolo Bonzini 11233bd88451SPaolo Bonzini value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer); 11243bd88451SPaolo Bonzini DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value); 11253bd88451SPaolo Bonzini break; 11263bd88451SPaolo Bonzini 11273bd88451SPaolo Bonzini case L0_ICNTO: case L1_ICNTO: 11283bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 11293bd88451SPaolo Bonzini 11303bd88451SPaolo Bonzini value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer); 11313bd88451SPaolo Bonzini DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value); 11323bd88451SPaolo Bonzini break; 11333bd88451SPaolo Bonzini 11343bd88451SPaolo Bonzini case L0_FRCNTO: case L1_FRCNTO: 11353bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 11363bd88451SPaolo Bonzini 11373bd88451SPaolo Bonzini value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); 11383bd88451SPaolo Bonzini break; 11393bd88451SPaolo Bonzini 11403bd88451SPaolo Bonzini case L0_TCON: case L1_TCON: 11413bd88451SPaolo Bonzini lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; 11423bd88451SPaolo Bonzini value = s->l_timer[lt_i].reg.tcon; 11433bd88451SPaolo Bonzini break; 11443bd88451SPaolo Bonzini 11453bd88451SPaolo Bonzini case L0_INT_CSTAT: case L1_INT_CSTAT: 11463bd88451SPaolo Bonzini lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; 11473bd88451SPaolo Bonzini value = s->l_timer[lt_i].reg.int_cstat; 11483bd88451SPaolo Bonzini break; 11493bd88451SPaolo Bonzini 11503bd88451SPaolo Bonzini case L0_INT_ENB: case L1_INT_ENB: 11513bd88451SPaolo Bonzini lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; 11523bd88451SPaolo Bonzini value = s->l_timer[lt_i].reg.int_enb; 11533bd88451SPaolo Bonzini break; 11543bd88451SPaolo Bonzini 11553bd88451SPaolo Bonzini case L0_WSTAT: case L1_WSTAT: 11563bd88451SPaolo Bonzini lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; 11573bd88451SPaolo Bonzini value = s->l_timer[lt_i].reg.wstat; 11583bd88451SPaolo Bonzini break; 11593bd88451SPaolo Bonzini 11603bd88451SPaolo Bonzini default: 11613bd88451SPaolo Bonzini hw_error("exynos4210.mct: bad read offset " 11623bd88451SPaolo Bonzini TARGET_FMT_plx "\n", offset); 11633bd88451SPaolo Bonzini break; 11643bd88451SPaolo Bonzini } 11653bd88451SPaolo Bonzini return value; 11663bd88451SPaolo Bonzini } 11673bd88451SPaolo Bonzini 11683bd88451SPaolo Bonzini /* MCT write */ 11693bd88451SPaolo Bonzini static void exynos4210_mct_write(void *opaque, hwaddr offset, 11703bd88451SPaolo Bonzini uint64_t value, unsigned size) 11713bd88451SPaolo Bonzini { 11723bd88451SPaolo Bonzini Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; 11733bd88451SPaolo Bonzini int index; /* index in buffer which represents register set */ 11743bd88451SPaolo Bonzini int shift; 11753bd88451SPaolo Bonzini int lt_i; 11763bd88451SPaolo Bonzini uint64_t new_frc; 11773bd88451SPaolo Bonzini uint32_t i; 11783bd88451SPaolo Bonzini uint32_t old_val; 11793bd88451SPaolo Bonzini #ifdef DEBUG_MCT 11803bd88451SPaolo Bonzini static uint32_t icntb_max[2] = {0}; 11813bd88451SPaolo Bonzini static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX}; 11823bd88451SPaolo Bonzini static uint32_t tcntb_max[2] = {0}; 11833bd88451SPaolo Bonzini static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX}; 11843bd88451SPaolo Bonzini #endif 11853bd88451SPaolo Bonzini 11863bd88451SPaolo Bonzini new_frc = s->g_timer.reg.cnt; 11873bd88451SPaolo Bonzini 11883bd88451SPaolo Bonzini switch (offset) { 11893bd88451SPaolo Bonzini 11903bd88451SPaolo Bonzini case MCT_CFG: 11913bd88451SPaolo Bonzini s->reg_mct_cfg = value; 11923bd88451SPaolo Bonzini exynos4210_mct_update_freq(s); 11933bd88451SPaolo Bonzini break; 11943bd88451SPaolo Bonzini 11953bd88451SPaolo Bonzini case G_CNT_L: 11963bd88451SPaolo Bonzini case G_CNT_U: 11973bd88451SPaolo Bonzini if (offset == G_CNT_L) { 11983bd88451SPaolo Bonzini 11993bd88451SPaolo Bonzini DPRINTF("global timer write to reg.cntl %llx\n", value); 12003bd88451SPaolo Bonzini 12013bd88451SPaolo Bonzini new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value; 12023bd88451SPaolo Bonzini s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L; 12033bd88451SPaolo Bonzini } 12043bd88451SPaolo Bonzini if (offset == G_CNT_U) { 12053bd88451SPaolo Bonzini 12063bd88451SPaolo Bonzini DPRINTF("global timer write to reg.cntu %llx\n", value); 12073bd88451SPaolo Bonzini 12083bd88451SPaolo Bonzini new_frc = (s->g_timer.reg.cnt & UINT32_MAX) + 12093bd88451SPaolo Bonzini ((uint64_t)value << 32); 12103bd88451SPaolo Bonzini s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U; 12113bd88451SPaolo Bonzini } 12123bd88451SPaolo Bonzini 12133bd88451SPaolo Bonzini s->g_timer.reg.cnt = new_frc; 12149ede4ec0SPeter Maydell exynos4210_gfrc_tx_begin(&s->g_timer); 12153bd88451SPaolo Bonzini exynos4210_gfrc_restart(s); 12169ede4ec0SPeter Maydell exynos4210_gfrc_tx_commit(&s->g_timer); 12173bd88451SPaolo Bonzini break; 12183bd88451SPaolo Bonzini 12193bd88451SPaolo Bonzini case G_CNT_WSTAT: 12203bd88451SPaolo Bonzini s->g_timer.reg.cnt_wstat &= ~(value); 12213bd88451SPaolo Bonzini break; 12223bd88451SPaolo Bonzini 12233bd88451SPaolo Bonzini case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): 12243bd88451SPaolo Bonzini case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): 12253bd88451SPaolo Bonzini index = GET_G_COMP_IDX(offset); 12263bd88451SPaolo Bonzini shift = 8 * (offset & 0x4); 12273bd88451SPaolo Bonzini s->g_timer.reg.comp[index] = 12283bd88451SPaolo Bonzini (s->g_timer.reg.comp[index] & 12293bd88451SPaolo Bonzini (((uint64_t)UINT32_MAX << 32) >> shift)) + 12303bd88451SPaolo Bonzini (value << shift); 12313bd88451SPaolo Bonzini 12323bd88451SPaolo Bonzini DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); 12333bd88451SPaolo Bonzini 12343bd88451SPaolo Bonzini if (offset & 0x4) { 12353bd88451SPaolo Bonzini s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); 12363bd88451SPaolo Bonzini } else { 12373bd88451SPaolo Bonzini s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); 12383bd88451SPaolo Bonzini } 12393bd88451SPaolo Bonzini 12409ede4ec0SPeter Maydell exynos4210_gfrc_tx_begin(&s->g_timer); 12413bd88451SPaolo Bonzini exynos4210_gfrc_restart(s); 12429ede4ec0SPeter Maydell exynos4210_gfrc_tx_commit(&s->g_timer); 12433bd88451SPaolo Bonzini break; 12443bd88451SPaolo Bonzini 12453bd88451SPaolo Bonzini case G_TCON: 12463bd88451SPaolo Bonzini old_val = s->g_timer.reg.tcon; 12473bd88451SPaolo Bonzini s->g_timer.reg.tcon = value; 12483bd88451SPaolo Bonzini s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE; 12493bd88451SPaolo Bonzini 12503bd88451SPaolo Bonzini DPRINTF("global timer write to reg.g_tcon %llx\n", value); 12513bd88451SPaolo Bonzini 12529ede4ec0SPeter Maydell exynos4210_gfrc_tx_begin(&s->g_timer); 12539ede4ec0SPeter Maydell 12543bd88451SPaolo Bonzini /* Start FRC if transition from disabled to enabled */ 12553bd88451SPaolo Bonzini if ((value & G_TCON_TIMER_ENABLE) > (old_val & 12563bd88451SPaolo Bonzini G_TCON_TIMER_ENABLE)) { 1257*1a391e20SGuenter Roeck exynos4210_gfrc_restart(s); 12583bd88451SPaolo Bonzini } 12593bd88451SPaolo Bonzini if ((value & G_TCON_TIMER_ENABLE) < (old_val & 12603bd88451SPaolo Bonzini G_TCON_TIMER_ENABLE)) { 12613bd88451SPaolo Bonzini exynos4210_gfrc_stop(&s->g_timer); 12623bd88451SPaolo Bonzini } 12633bd88451SPaolo Bonzini 12643bd88451SPaolo Bonzini /* Start CMP if transition from disabled to enabled */ 12653bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 12663bd88451SPaolo Bonzini if ((value & G_TCON_COMP_ENABLE(i)) != (old_val & 12673bd88451SPaolo Bonzini G_TCON_COMP_ENABLE(i))) { 12683bd88451SPaolo Bonzini exynos4210_gfrc_restart(s); 12693bd88451SPaolo Bonzini } 12703bd88451SPaolo Bonzini } 12719ede4ec0SPeter Maydell 12729ede4ec0SPeter Maydell exynos4210_gfrc_tx_commit(&s->g_timer); 12733bd88451SPaolo Bonzini break; 12743bd88451SPaolo Bonzini 12753bd88451SPaolo Bonzini case G_INT_CSTAT: 12763bd88451SPaolo Bonzini s->g_timer.reg.int_cstat &= ~(value); 12773bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 12783bd88451SPaolo Bonzini if (value & G_INT_CSTAT_COMP(i)) { 12793bd88451SPaolo Bonzini exynos4210_gcomp_lower_irq(&s->g_timer, i); 12803bd88451SPaolo Bonzini } 12813bd88451SPaolo Bonzini } 12823bd88451SPaolo Bonzini break; 12833bd88451SPaolo Bonzini 12843bd88451SPaolo Bonzini case G_INT_ENB: 12853bd88451SPaolo Bonzini /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ 12863bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 12873bd88451SPaolo Bonzini if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & 12883bd88451SPaolo Bonzini G_INT_ENABLE(i))) { 12893bd88451SPaolo Bonzini if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) { 12903bd88451SPaolo Bonzini exynos4210_gcomp_raise_irq(&s->g_timer, i); 12913bd88451SPaolo Bonzini } 12923bd88451SPaolo Bonzini } 12933bd88451SPaolo Bonzini 12943bd88451SPaolo Bonzini if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon & 12953bd88451SPaolo Bonzini G_INT_ENABLE(i))) { 12963bd88451SPaolo Bonzini exynos4210_gcomp_lower_irq(&s->g_timer, i); 12973bd88451SPaolo Bonzini } 12983bd88451SPaolo Bonzini } 12993bd88451SPaolo Bonzini 13003bd88451SPaolo Bonzini DPRINTF("global timer INT enable %llx\n", value); 13013bd88451SPaolo Bonzini s->g_timer.reg.int_enb = value; 13023bd88451SPaolo Bonzini break; 13033bd88451SPaolo Bonzini 13043bd88451SPaolo Bonzini case G_WSTAT: 13053bd88451SPaolo Bonzini s->g_timer.reg.wstat &= ~(value); 13063bd88451SPaolo Bonzini break; 13073bd88451SPaolo Bonzini 13083bd88451SPaolo Bonzini case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: 13093bd88451SPaolo Bonzini case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: 13103bd88451SPaolo Bonzini index = GET_G_COMP_ADD_INCR_IDX(offset); 13113bd88451SPaolo Bonzini s->g_timer.reg.comp_add_incr[index] = value; 13123bd88451SPaolo Bonzini s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index); 13133bd88451SPaolo Bonzini break; 13143bd88451SPaolo Bonzini 13153bd88451SPaolo Bonzini /* Local timers */ 13163bd88451SPaolo Bonzini case L0_TCON: case L1_TCON: 13173bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 13183bd88451SPaolo Bonzini old_val = s->l_timer[lt_i].reg.tcon; 13193bd88451SPaolo Bonzini 13203bd88451SPaolo Bonzini s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; 13213bd88451SPaolo Bonzini s->l_timer[lt_i].reg.tcon = value; 13223bd88451SPaolo Bonzini 13236c27ee94SPeter Maydell exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer); 13243bd88451SPaolo Bonzini /* Stop local CNT */ 13253bd88451SPaolo Bonzini if ((value & L_TCON_TICK_START) < 13263bd88451SPaolo Bonzini (old_val & L_TCON_TICK_START)) { 13273bd88451SPaolo Bonzini DPRINTF("local timer[%d] stop cnt\n", lt_i); 13283bd88451SPaolo Bonzini exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer); 13293bd88451SPaolo Bonzini } 13303bd88451SPaolo Bonzini 13313bd88451SPaolo Bonzini /* Stop local INT */ 13323bd88451SPaolo Bonzini if ((value & L_TCON_INT_START) < 13333bd88451SPaolo Bonzini (old_val & L_TCON_INT_START)) { 13343bd88451SPaolo Bonzini DPRINTF("local timer[%d] stop int\n", lt_i); 13353bd88451SPaolo Bonzini exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer); 13363bd88451SPaolo Bonzini } 13373bd88451SPaolo Bonzini 13383bd88451SPaolo Bonzini /* Start local CNT */ 13393bd88451SPaolo Bonzini if ((value & L_TCON_TICK_START) > 13403bd88451SPaolo Bonzini (old_val & L_TCON_TICK_START)) { 13413bd88451SPaolo Bonzini DPRINTF("local timer[%d] start cnt\n", lt_i); 13423bd88451SPaolo Bonzini exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer); 13433bd88451SPaolo Bonzini } 13443bd88451SPaolo Bonzini 13453bd88451SPaolo Bonzini /* Start local INT */ 13463bd88451SPaolo Bonzini if ((value & L_TCON_INT_START) > 13473bd88451SPaolo Bonzini (old_val & L_TCON_INT_START)) { 13483bd88451SPaolo Bonzini DPRINTF("local timer[%d] start int\n", lt_i); 13493bd88451SPaolo Bonzini exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); 13503bd88451SPaolo Bonzini } 13516c27ee94SPeter Maydell exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer); 13523bd88451SPaolo Bonzini 13533bd88451SPaolo Bonzini /* Start or Stop local FRC if TCON changed */ 135450f07d76SPeter Maydell exynos4210_lfrc_tx_begin(&s->l_timer[lt_i]); 13553bd88451SPaolo Bonzini if ((value & L_TCON_FRC_START) > 13563bd88451SPaolo Bonzini (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { 13573bd88451SPaolo Bonzini DPRINTF("local timer[%d] start frc\n", lt_i); 13583bd88451SPaolo Bonzini exynos4210_lfrc_start(&s->l_timer[lt_i]); 13593bd88451SPaolo Bonzini } 13603bd88451SPaolo Bonzini if ((value & L_TCON_FRC_START) < 13613bd88451SPaolo Bonzini (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { 13623bd88451SPaolo Bonzini DPRINTF("local timer[%d] stop frc\n", lt_i); 13633bd88451SPaolo Bonzini exynos4210_lfrc_stop(&s->l_timer[lt_i]); 13643bd88451SPaolo Bonzini } 136550f07d76SPeter Maydell exynos4210_lfrc_tx_commit(&s->l_timer[lt_i]); 13663bd88451SPaolo Bonzini break; 13673bd88451SPaolo Bonzini 13683bd88451SPaolo Bonzini case L0_TCNTB: case L1_TCNTB: 13693bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 13703bd88451SPaolo Bonzini index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); 13713bd88451SPaolo Bonzini 13723bd88451SPaolo Bonzini /* 13733bd88451SPaolo Bonzini * TCNTB is updated to internal register only after CNT expired. 13743bd88451SPaolo Bonzini * Due to this we should reload timer to nearest moment when CNT is 13753bd88451SPaolo Bonzini * expired and then in event handler update tcntb to new TCNTB value. 13763bd88451SPaolo Bonzini */ 13776c27ee94SPeter Maydell exynos4210_ltick_tx_begin(&s->l_timer[lt_i].tick_timer); 13783bd88451SPaolo Bonzini exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, 13793bd88451SPaolo Bonzini s->l_timer[lt_i].tick_timer.icntb); 13806c27ee94SPeter Maydell exynos4210_ltick_tx_commit(&s->l_timer[lt_i].tick_timer); 13813bd88451SPaolo Bonzini 13823bd88451SPaolo Bonzini s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; 13833bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; 13843bd88451SPaolo Bonzini 13853bd88451SPaolo Bonzini #ifdef DEBUG_MCT 13863bd88451SPaolo Bonzini if (tcntb_min[lt_i] > value) { 13873bd88451SPaolo Bonzini tcntb_min[lt_i] = value; 13883bd88451SPaolo Bonzini } 13893bd88451SPaolo Bonzini if (tcntb_max[lt_i] < value) { 13903bd88451SPaolo Bonzini tcntb_max[lt_i] = value; 13913bd88451SPaolo Bonzini } 13923bd88451SPaolo Bonzini DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n", 13933bd88451SPaolo Bonzini lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]); 13943bd88451SPaolo Bonzini #endif 13953bd88451SPaolo Bonzini break; 13963bd88451SPaolo Bonzini 13973bd88451SPaolo Bonzini case L0_ICNTB: case L1_ICNTB: 13983bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 13993bd88451SPaolo Bonzini index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); 14003bd88451SPaolo Bonzini 14013bd88451SPaolo Bonzini s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE; 14023bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value & 14033bd88451SPaolo Bonzini ~L_ICNTB_MANUAL_UPDATE; 14043bd88451SPaolo Bonzini 14053bd88451SPaolo Bonzini /* 14063bd88451SPaolo Bonzini * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event 14073bd88451SPaolo Bonzini * could raise too fast disallowing QEMU to execute target code. 14083bd88451SPaolo Bonzini */ 14093bd88451SPaolo Bonzini if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] * 14103bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) { 14113bd88451SPaolo Bonzini if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) { 14123bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = 14133bd88451SPaolo Bonzini MCT_LT_CNT_LOW_LIMIT; 14143bd88451SPaolo Bonzini } else { 14153bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = 14163bd88451SPaolo Bonzini MCT_LT_CNT_LOW_LIMIT / 14173bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]; 14183bd88451SPaolo Bonzini } 14193bd88451SPaolo Bonzini } 14203bd88451SPaolo Bonzini 14213bd88451SPaolo Bonzini if (value & L_ICNTB_MANUAL_UPDATE) { 14223bd88451SPaolo Bonzini exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, 14233bd88451SPaolo Bonzini s->l_timer[lt_i].tick_timer.tcntb, 14243bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]); 14253bd88451SPaolo Bonzini } 14263bd88451SPaolo Bonzini 14273bd88451SPaolo Bonzini #ifdef DEBUG_MCT 14283bd88451SPaolo Bonzini if (icntb_min[lt_i] > value) { 14293bd88451SPaolo Bonzini icntb_min[lt_i] = value; 14303bd88451SPaolo Bonzini } 14313bd88451SPaolo Bonzini if (icntb_max[lt_i] < value) { 14323bd88451SPaolo Bonzini icntb_max[lt_i] = value; 14333bd88451SPaolo Bonzini } 14343bd88451SPaolo Bonzini DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", 14353bd88451SPaolo Bonzini lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); 14363bd88451SPaolo Bonzini #endif 14373bd88451SPaolo Bonzini break; 14383bd88451SPaolo Bonzini 14393bd88451SPaolo Bonzini case L0_FRCNTB: case L1_FRCNTB: 14403bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 14413bd88451SPaolo Bonzini index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); 14423bd88451SPaolo Bonzini 14433bd88451SPaolo Bonzini DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value); 14443bd88451SPaolo Bonzini 14453bd88451SPaolo Bonzini s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE; 14463bd88451SPaolo Bonzini s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value; 14473bd88451SPaolo Bonzini 14483bd88451SPaolo Bonzini break; 14493bd88451SPaolo Bonzini 14503bd88451SPaolo Bonzini case L0_TCNTO: case L1_TCNTO: 14513bd88451SPaolo Bonzini case L0_ICNTO: case L1_ICNTO: 14523bd88451SPaolo Bonzini case L0_FRCNTO: case L1_FRCNTO: 1453f2ad5140SKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR, 1454f2ad5140SKrzysztof Kozlowski "exynos4210.mct: write to RO register " TARGET_FMT_plx, 1455f2ad5140SKrzysztof Kozlowski offset); 14563bd88451SPaolo Bonzini break; 14573bd88451SPaolo Bonzini 14583bd88451SPaolo Bonzini case L0_INT_CSTAT: case L1_INT_CSTAT: 14593bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 14603bd88451SPaolo Bonzini 14613bd88451SPaolo Bonzini DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value); 14623bd88451SPaolo Bonzini 14633bd88451SPaolo Bonzini s->l_timer[lt_i].reg.int_cstat &= ~value; 14643bd88451SPaolo Bonzini if (!s->l_timer[lt_i].reg.int_cstat) { 14653bd88451SPaolo Bonzini qemu_irq_lower(s->l_timer[lt_i].irq); 14663bd88451SPaolo Bonzini } 14673bd88451SPaolo Bonzini break; 14683bd88451SPaolo Bonzini 14693bd88451SPaolo Bonzini case L0_INT_ENB: case L1_INT_ENB: 14703bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 14713bd88451SPaolo Bonzini old_val = s->l_timer[lt_i].reg.int_enb; 14723bd88451SPaolo Bonzini 14733bd88451SPaolo Bonzini /* Raise Local timer IRQ if cstat is pending */ 14743bd88451SPaolo Bonzini if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) { 14753bd88451SPaolo Bonzini if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) { 14763bd88451SPaolo Bonzini qemu_irq_raise(s->l_timer[lt_i].irq); 14773bd88451SPaolo Bonzini } 14783bd88451SPaolo Bonzini } 14793bd88451SPaolo Bonzini 14803bd88451SPaolo Bonzini s->l_timer[lt_i].reg.int_enb = value; 14813bd88451SPaolo Bonzini 14823bd88451SPaolo Bonzini break; 14833bd88451SPaolo Bonzini 14843bd88451SPaolo Bonzini case L0_WSTAT: case L1_WSTAT: 14853bd88451SPaolo Bonzini lt_i = GET_L_TIMER_IDX(offset); 14863bd88451SPaolo Bonzini 14873bd88451SPaolo Bonzini s->l_timer[lt_i].reg.wstat &= ~value; 14883bd88451SPaolo Bonzini break; 14893bd88451SPaolo Bonzini 14903bd88451SPaolo Bonzini default: 14913bd88451SPaolo Bonzini hw_error("exynos4210.mct: bad write offset " 14923bd88451SPaolo Bonzini TARGET_FMT_plx "\n", offset); 14933bd88451SPaolo Bonzini break; 14943bd88451SPaolo Bonzini } 14953bd88451SPaolo Bonzini } 14963bd88451SPaolo Bonzini 14973bd88451SPaolo Bonzini static const MemoryRegionOps exynos4210_mct_ops = { 14983bd88451SPaolo Bonzini .read = exynos4210_mct_read, 14993bd88451SPaolo Bonzini .write = exynos4210_mct_write, 15003bd88451SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 15013bd88451SPaolo Bonzini }; 15023bd88451SPaolo Bonzini 15033bd88451SPaolo Bonzini /* MCT init */ 15047a53a140Sxiaoqiang.zhao static void exynos4210_mct_init(Object *obj) 15053bd88451SPaolo Bonzini { 15063bd88451SPaolo Bonzini int i; 15077a53a140Sxiaoqiang.zhao Exynos4210MCTState *s = EXYNOS4210_MCT(obj); 15087a53a140Sxiaoqiang.zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj); 15093bd88451SPaolo Bonzini 15103bd88451SPaolo Bonzini /* Global timer */ 15119ede4ec0SPeter Maydell s->g_timer.ptimer_frc = ptimer_init(exynos4210_gfrc_event, s, 15129ede4ec0SPeter Maydell PTIMER_POLICY_DEFAULT); 15133bd88451SPaolo Bonzini memset(&s->g_timer.reg, 0, sizeof(struct gregs)); 15143bd88451SPaolo Bonzini 15153bd88451SPaolo Bonzini /* Local timers */ 15163bd88451SPaolo Bonzini for (i = 0; i < 2; i++) { 1517e7ea81c3SDmitry Osipenko s->l_timer[i].tick_timer.ptimer_tick = 15186c27ee94SPeter Maydell ptimer_init(exynos4210_ltick_event, &s->l_timer[i], 15196c27ee94SPeter Maydell PTIMER_POLICY_DEFAULT); 1520b0142262SPeter Maydell s->l_timer[i].ptimer_frc = 152150f07d76SPeter Maydell ptimer_init(exynos4210_lfrc_event, &s->l_timer[i], 152250f07d76SPeter Maydell PTIMER_POLICY_DEFAULT); 15233bd88451SPaolo Bonzini s->l_timer[i].id = i; 15243bd88451SPaolo Bonzini } 15253bd88451SPaolo Bonzini 15263bd88451SPaolo Bonzini /* IRQs */ 15273bd88451SPaolo Bonzini for (i = 0; i < MCT_GT_CMP_NUM; i++) { 15283bd88451SPaolo Bonzini sysbus_init_irq(dev, &s->g_timer.irq[i]); 15293bd88451SPaolo Bonzini } 15303bd88451SPaolo Bonzini for (i = 0; i < 2; i++) { 15313bd88451SPaolo Bonzini sysbus_init_irq(dev, &s->l_timer[i].irq); 15323bd88451SPaolo Bonzini } 15333bd88451SPaolo Bonzini 15347a53a140Sxiaoqiang.zhao memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s, 1535853dca12SPaolo Bonzini "exynos4210-mct", MCT_SFR_SIZE); 15363bd88451SPaolo Bonzini sysbus_init_mmio(dev, &s->iomem); 15373bd88451SPaolo Bonzini } 15383bd88451SPaolo Bonzini 15393bd88451SPaolo Bonzini static void exynos4210_mct_class_init(ObjectClass *klass, void *data) 15403bd88451SPaolo Bonzini { 15413bd88451SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 15423bd88451SPaolo Bonzini 15433bd88451SPaolo Bonzini dc->reset = exynos4210_mct_reset; 15443bd88451SPaolo Bonzini dc->vmsd = &vmstate_exynos4210_mct_state; 15453bd88451SPaolo Bonzini } 15463bd88451SPaolo Bonzini 15473bd88451SPaolo Bonzini static const TypeInfo exynos4210_mct_info = { 154881e1010dSAndreas Färber .name = TYPE_EXYNOS4210_MCT, 15493bd88451SPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 15503bd88451SPaolo Bonzini .instance_size = sizeof(Exynos4210MCTState), 15517a53a140Sxiaoqiang.zhao .instance_init = exynos4210_mct_init, 15523bd88451SPaolo Bonzini .class_init = exynos4210_mct_class_init, 15533bd88451SPaolo Bonzini }; 15543bd88451SPaolo Bonzini 15553bd88451SPaolo Bonzini static void exynos4210_mct_register_types(void) 15563bd88451SPaolo Bonzini { 15573bd88451SPaolo Bonzini type_register_static(&exynos4210_mct_info); 15583bd88451SPaolo Bonzini } 15593bd88451SPaolo Bonzini 15603bd88451SPaolo Bonzini type_init(exynos4210_mct_register_types) 1561