10f3a4a01SFabien Chouteau /* 20f3a4a01SFabien Chouteau * QEMU GRLIB GPTimer Emulator 30f3a4a01SFabien Chouteau * 4f432962eSClément Chigot * SPDX-License-Identifier: MIT 5f432962eSClément Chigot * 6f432962eSClément Chigot * Copyright (c) 2010-2024 AdaCore 70f3a4a01SFabien Chouteau * 80f3a4a01SFabien Chouteau * Permission is hereby granted, free of charge, to any person obtaining a copy 90f3a4a01SFabien Chouteau * of this software and associated documentation files (the "Software"), to deal 100f3a4a01SFabien Chouteau * in the Software without restriction, including without limitation the rights 110f3a4a01SFabien Chouteau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 120f3a4a01SFabien Chouteau * copies of the Software, and to permit persons to whom the Software is 130f3a4a01SFabien Chouteau * furnished to do so, subject to the following conditions: 140f3a4a01SFabien Chouteau * 150f3a4a01SFabien Chouteau * The above copyright notice and this permission notice shall be included in 160f3a4a01SFabien Chouteau * all copies or substantial portions of the Software. 170f3a4a01SFabien Chouteau * 180f3a4a01SFabien Chouteau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 190f3a4a01SFabien Chouteau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 200f3a4a01SFabien Chouteau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 210f3a4a01SFabien Chouteau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 220f3a4a01SFabien Chouteau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 230f3a4a01SFabien Chouteau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 240f3a4a01SFabien Chouteau * THE SOFTWARE. 250f3a4a01SFabien Chouteau */ 260f3a4a01SFabien Chouteau 27db5ebe5fSPeter Maydell #include "qemu/osdep.h" 28f432962eSClément Chigot #include "hw/timer/grlib_gptimer.h" 2983c9f4caSPaolo Bonzini #include "hw/sysbus.h" 301de7afc9SPaolo Bonzini #include "qemu/timer.h" 3164552b6bSMarkus Armbruster #include "hw/irq.h" 3283c9f4caSPaolo Bonzini #include "hw/ptimer.h" 33a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 340b8fa32fSMarkus Armbruster #include "qemu/module.h" 350f3a4a01SFabien Chouteau 360f3a4a01SFabien Chouteau #include "trace.h" 37db1015e9SEduardo Habkost #include "qom/object.h" 380f3a4a01SFabien Chouteau 390f3a4a01SFabien Chouteau #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ 400f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ 410f3a4a01SFabien Chouteau 420f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8 430f3a4a01SFabien Chouteau 440f3a4a01SFabien Chouteau /* GPTimer Config register fields */ 450f3a4a01SFabien Chouteau #define GPTIMER_ENABLE (1 << 0) 460f3a4a01SFabien Chouteau #define GPTIMER_RESTART (1 << 1) 470f3a4a01SFabien Chouteau #define GPTIMER_LOAD (1 << 2) 480f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE (1 << 3) 490f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4) 500f3a4a01SFabien Chouteau #define GPTIMER_CHAIN (1 << 5) /* Not supported */ 510f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ 520f3a4a01SFabien Chouteau 530f3a4a01SFabien Chouteau /* Memory mapped register offsets */ 540f3a4a01SFabien Chouteau #define SCALER_OFFSET 0x00 550f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET 0x04 560f3a4a01SFabien Chouteau #define CONFIG_OFFSET 0x08 570f3a4a01SFabien Chouteau #define COUNTER_OFFSET 0x00 580f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04 590f3a4a01SFabien Chouteau #define TIMER_BASE 0x10 600f3a4a01SFabien Chouteau 618063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(GPTimerUnit, GRLIB_GPTIMER) 62541ab55fSAndreas Färber 630f3a4a01SFabien Chouteau typedef struct GPTimer GPTimer; 640f3a4a01SFabien Chouteau 650f3a4a01SFabien Chouteau struct GPTimer { 660f3a4a01SFabien Chouteau struct ptimer_state *ptimer; 670f3a4a01SFabien Chouteau 680f3a4a01SFabien Chouteau qemu_irq irq; 690f3a4a01SFabien Chouteau int id; 700f3a4a01SFabien Chouteau GPTimerUnit *unit; 710f3a4a01SFabien Chouteau 720f3a4a01SFabien Chouteau /* registers */ 730f3a4a01SFabien Chouteau uint32_t counter; 740f3a4a01SFabien Chouteau uint32_t reload; 750f3a4a01SFabien Chouteau uint32_t config; 760f3a4a01SFabien Chouteau }; 770f3a4a01SFabien Chouteau 780f3a4a01SFabien Chouteau struct GPTimerUnit { 79541ab55fSAndreas Färber SysBusDevice parent_obj; 80541ab55fSAndreas Färber 81cde844faSAvi Kivity MemoryRegion iomem; 820f3a4a01SFabien Chouteau 830f3a4a01SFabien Chouteau uint32_t nr_timers; /* Number of timers available */ 840f3a4a01SFabien Chouteau uint32_t freq_hz; /* System frequency */ 850f3a4a01SFabien Chouteau uint32_t irq_line; /* Base irq line */ 860f3a4a01SFabien Chouteau 870f3a4a01SFabien Chouteau GPTimer *timers; 880f3a4a01SFabien Chouteau 890f3a4a01SFabien Chouteau /* registers */ 900f3a4a01SFabien Chouteau uint32_t scaler; 910f3a4a01SFabien Chouteau uint32_t reload; 920f3a4a01SFabien Chouteau uint32_t config; 930f3a4a01SFabien Chouteau }; 940f3a4a01SFabien Chouteau 95663e475fSPeter Maydell static void grlib_gptimer_tx_begin(GPTimer *timer) 96663e475fSPeter Maydell { 97663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 98663e475fSPeter Maydell } 99663e475fSPeter Maydell 100663e475fSPeter Maydell static void grlib_gptimer_tx_commit(GPTimer *timer) 101663e475fSPeter Maydell { 102663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 103663e475fSPeter Maydell } 104663e475fSPeter Maydell 105663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */ 1060f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer) 1070f3a4a01SFabien Chouteau { 1080f3a4a01SFabien Chouteau assert(timer != NULL); 1090f3a4a01SFabien Chouteau 1100f3a4a01SFabien Chouteau 1110f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 1120f3a4a01SFabien Chouteau 1130f3a4a01SFabien Chouteau if (!(timer->config & GPTIMER_ENABLE)) { 1140f3a4a01SFabien Chouteau /* Timer disabled */ 1150f3a4a01SFabien Chouteau trace_grlib_gptimer_disabled(timer->id, timer->config); 1160f3a4a01SFabien Chouteau return; 1170f3a4a01SFabien Chouteau } 1180f3a4a01SFabien Chouteau 1190f3a4a01SFabien Chouteau /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at 1200f3a4a01SFabien Chouteau underflow. Set count + 1 to simulate the GPTimer behavior. */ 1210f3a4a01SFabien Chouteau 1229d5614d5SSebastian Huber trace_grlib_gptimer_enable(timer->id, timer->counter); 1230f3a4a01SFabien Chouteau 1249d5614d5SSebastian Huber ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1); 1250f3a4a01SFabien Chouteau ptimer_run(timer->ptimer, 1); 1260f3a4a01SFabien Chouteau } 1270f3a4a01SFabien Chouteau 128663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */ 1290f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer) 1300f3a4a01SFabien Chouteau { 1310f3a4a01SFabien Chouteau assert(timer != NULL); 1320f3a4a01SFabien Chouteau 1330f3a4a01SFabien Chouteau trace_grlib_gptimer_restart(timer->id, timer->reload); 1340f3a4a01SFabien Chouteau 1350f3a4a01SFabien Chouteau timer->counter = timer->reload; 1360f3a4a01SFabien Chouteau grlib_gptimer_enable(timer); 1370f3a4a01SFabien Chouteau } 1380f3a4a01SFabien Chouteau 1390f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) 1400f3a4a01SFabien Chouteau { 1410f3a4a01SFabien Chouteau int i = 0; 1420f3a4a01SFabien Chouteau uint32_t value = 0; 1430f3a4a01SFabien Chouteau 1440f3a4a01SFabien Chouteau assert(unit != NULL); 1450f3a4a01SFabien Chouteau 1460f3a4a01SFabien Chouteau if (scaler > 0) { 1470f3a4a01SFabien Chouteau value = unit->freq_hz / (scaler + 1); 1480f3a4a01SFabien Chouteau } else { 1490f3a4a01SFabien Chouteau value = unit->freq_hz; 1500f3a4a01SFabien Chouteau } 1510f3a4a01SFabien Chouteau 1520f3a4a01SFabien Chouteau trace_grlib_gptimer_set_scaler(scaler, value); 1530f3a4a01SFabien Chouteau 1540f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 155663e475fSPeter Maydell ptimer_transaction_begin(unit->timers[i].ptimer); 1560f3a4a01SFabien Chouteau ptimer_set_freq(unit->timers[i].ptimer, value); 157663e475fSPeter Maydell ptimer_transaction_commit(unit->timers[i].ptimer); 1580f3a4a01SFabien Chouteau } 1590f3a4a01SFabien Chouteau } 1600f3a4a01SFabien Chouteau 1610f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque) 1620f3a4a01SFabien Chouteau { 1630f3a4a01SFabien Chouteau GPTimer *timer = opaque; 1640f3a4a01SFabien Chouteau assert(timer != NULL); 1650f3a4a01SFabien Chouteau 1660f3a4a01SFabien Chouteau trace_grlib_gptimer_hit(timer->id); 1670f3a4a01SFabien Chouteau 1680f3a4a01SFabien Chouteau /* Timer expired */ 1690f3a4a01SFabien Chouteau 1700f3a4a01SFabien Chouteau if (timer->config & GPTIMER_INT_ENABLE) { 1710f3a4a01SFabien Chouteau /* Set the pending bit (only unset by write in the config register) */ 1720f3a4a01SFabien Chouteau timer->config |= GPTIMER_INT_PENDING; 1730f3a4a01SFabien Chouteau qemu_irq_pulse(timer->irq); 1740f3a4a01SFabien Chouteau } 1750f3a4a01SFabien Chouteau 1760f3a4a01SFabien Chouteau if (timer->config & GPTIMER_RESTART) { 1770f3a4a01SFabien Chouteau grlib_gptimer_restart(timer); 1780f3a4a01SFabien Chouteau } 1790f3a4a01SFabien Chouteau } 1800f3a4a01SFabien Chouteau 181a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, 182cde844faSAvi Kivity unsigned size) 1830f3a4a01SFabien Chouteau { 1840f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 185a8170e5eSAvi Kivity hwaddr timer_addr; 1860f3a4a01SFabien Chouteau int id; 1870f3a4a01SFabien Chouteau uint32_t value = 0; 1880f3a4a01SFabien Chouteau 1890f3a4a01SFabien Chouteau addr &= 0xff; 1900f3a4a01SFabien Chouteau 1910f3a4a01SFabien Chouteau /* Unit registers */ 1920f3a4a01SFabien Chouteau switch (addr) { 1930f3a4a01SFabien Chouteau case SCALER_OFFSET: 194b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->scaler); 1950f3a4a01SFabien Chouteau return unit->scaler; 1960f3a4a01SFabien Chouteau 1970f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 198b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->reload); 1990f3a4a01SFabien Chouteau return unit->reload; 2000f3a4a01SFabien Chouteau 2010f3a4a01SFabien Chouteau case CONFIG_OFFSET: 202b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->config); 2030f3a4a01SFabien Chouteau return unit->config; 2040f3a4a01SFabien Chouteau 2050f3a4a01SFabien Chouteau default: 2060f3a4a01SFabien Chouteau break; 2070f3a4a01SFabien Chouteau } 2080f3a4a01SFabien Chouteau 2090f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2100f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2110f3a4a01SFabien Chouteau 2120f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2130f3a4a01SFabien Chouteau 2140f3a4a01SFabien Chouteau /* GPTimer registers */ 2150f3a4a01SFabien Chouteau switch (timer_addr) { 2160f3a4a01SFabien Chouteau case COUNTER_OFFSET: 2170f3a4a01SFabien Chouteau value = ptimer_get_count(unit->timers[id].ptimer); 218b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2190f3a4a01SFabien Chouteau return value; 2200f3a4a01SFabien Chouteau 2210f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 2220f3a4a01SFabien Chouteau value = unit->timers[id].reload; 223b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2240f3a4a01SFabien Chouteau return value; 2250f3a4a01SFabien Chouteau 2260f3a4a01SFabien Chouteau case CONFIG_OFFSET: 227b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); 2280f3a4a01SFabien Chouteau return unit->timers[id].config; 2290f3a4a01SFabien Chouteau 2300f3a4a01SFabien Chouteau default: 2310f3a4a01SFabien Chouteau break; 2320f3a4a01SFabien Chouteau } 2330f3a4a01SFabien Chouteau 2340f3a4a01SFabien Chouteau } 2350f3a4a01SFabien Chouteau 236b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, 0); 2370f3a4a01SFabien Chouteau return 0; 2380f3a4a01SFabien Chouteau } 2390f3a4a01SFabien Chouteau 240a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr, 241cde844faSAvi Kivity uint64_t value, unsigned size) 2420f3a4a01SFabien Chouteau { 2430f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 244a8170e5eSAvi Kivity hwaddr timer_addr; 2450f3a4a01SFabien Chouteau int id; 2460f3a4a01SFabien Chouteau 2470f3a4a01SFabien Chouteau addr &= 0xff; 2480f3a4a01SFabien Chouteau 2490f3a4a01SFabien Chouteau /* Unit registers */ 2500f3a4a01SFabien Chouteau switch (addr) { 2510f3a4a01SFabien Chouteau case SCALER_OFFSET: 2520f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2530f3a4a01SFabien Chouteau unit->scaler = value; 254b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->scaler); 2550f3a4a01SFabien Chouteau return; 2560f3a4a01SFabien Chouteau 2570f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 2580f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2590f3a4a01SFabien Chouteau unit->reload = value; 260b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->reload); 2610f3a4a01SFabien Chouteau grlib_gptimer_set_scaler(unit, value); 2620f3a4a01SFabien Chouteau return; 2630f3a4a01SFabien Chouteau 2640f3a4a01SFabien Chouteau case CONFIG_OFFSET: 2650f3a4a01SFabien Chouteau /* Read Only (disable timer freeze not supported) */ 266b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, 0); 2670f3a4a01SFabien Chouteau return; 2680f3a4a01SFabien Chouteau 2690f3a4a01SFabien Chouteau default: 2700f3a4a01SFabien Chouteau break; 2710f3a4a01SFabien Chouteau } 2720f3a4a01SFabien Chouteau 2730f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2740f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2750f3a4a01SFabien Chouteau 2760f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2770f3a4a01SFabien Chouteau 2780f3a4a01SFabien Chouteau /* GPTimer registers */ 2790f3a4a01SFabien Chouteau switch (timer_addr) { 2800f3a4a01SFabien Chouteau case COUNTER_OFFSET: 281b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 282663e475fSPeter Maydell grlib_gptimer_tx_begin(&unit->timers[id]); 2830f3a4a01SFabien Chouteau unit->timers[id].counter = value; 2840f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 285663e475fSPeter Maydell grlib_gptimer_tx_commit(&unit->timers[id]); 2860f3a4a01SFabien Chouteau return; 2870f3a4a01SFabien Chouteau 2880f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 289b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2900f3a4a01SFabien Chouteau unit->timers[id].reload = value; 2910f3a4a01SFabien Chouteau return; 2920f3a4a01SFabien Chouteau 2930f3a4a01SFabien Chouteau case CONFIG_OFFSET: 294b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2950f3a4a01SFabien Chouteau 2960f3a4a01SFabien Chouteau if (value & GPTIMER_INT_PENDING) { 2970f3a4a01SFabien Chouteau /* clear pending bit */ 2980f3a4a01SFabien Chouteau value &= ~GPTIMER_INT_PENDING; 2990f3a4a01SFabien Chouteau } else { 3000f3a4a01SFabien Chouteau /* keep pending bit */ 3010f3a4a01SFabien Chouteau value |= unit->timers[id].config & GPTIMER_INT_PENDING; 3020f3a4a01SFabien Chouteau } 3030f3a4a01SFabien Chouteau 3040f3a4a01SFabien Chouteau unit->timers[id].config = value; 3050f3a4a01SFabien Chouteau 3060f3a4a01SFabien Chouteau /* gptimer_restart calls gptimer_enable, so if "enable" and "load" 3070f3a4a01SFabien Chouteau bits are present, we just have to call restart. */ 3080f3a4a01SFabien Chouteau 309663e475fSPeter Maydell grlib_gptimer_tx_begin(&unit->timers[id]); 3100f3a4a01SFabien Chouteau if (value & GPTIMER_LOAD) { 3110f3a4a01SFabien Chouteau grlib_gptimer_restart(&unit->timers[id]); 3120f3a4a01SFabien Chouteau } else if (value & GPTIMER_ENABLE) { 3130f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 3140f3a4a01SFabien Chouteau } 3150f3a4a01SFabien Chouteau 3160f3a4a01SFabien Chouteau /* These fields must always be read as 0 */ 3170f3a4a01SFabien Chouteau value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); 3180f3a4a01SFabien Chouteau 3190f3a4a01SFabien Chouteau unit->timers[id].config = value; 320663e475fSPeter Maydell grlib_gptimer_tx_commit(&unit->timers[id]); 3210f3a4a01SFabien Chouteau return; 3220f3a4a01SFabien Chouteau 3230f3a4a01SFabien Chouteau default: 3240f3a4a01SFabien Chouteau break; 3250f3a4a01SFabien Chouteau } 3260f3a4a01SFabien Chouteau 3270f3a4a01SFabien Chouteau } 3280f3a4a01SFabien Chouteau 329b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, value); 3300f3a4a01SFabien Chouteau } 3310f3a4a01SFabien Chouteau 332cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = { 333cde844faSAvi Kivity .read = grlib_gptimer_read, 334cde844faSAvi Kivity .write = grlib_gptimer_write, 335cde844faSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 336cde844faSAvi Kivity .valid = { 337cde844faSAvi Kivity .min_access_size = 4, 338cde844faSAvi Kivity .max_access_size = 4, 339cde844faSAvi Kivity }, 3400f3a4a01SFabien Chouteau }; 3410f3a4a01SFabien Chouteau 3420f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d) 3430f3a4a01SFabien Chouteau { 344541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(d); 3450f3a4a01SFabien Chouteau int i = 0; 3460f3a4a01SFabien Chouteau 3470f3a4a01SFabien Chouteau assert(unit != NULL); 3480f3a4a01SFabien Chouteau 3490f3a4a01SFabien Chouteau unit->scaler = 0; 3500f3a4a01SFabien Chouteau unit->reload = 0; 3510f3a4a01SFabien Chouteau 3520f3a4a01SFabien Chouteau unit->config = unit->nr_timers; 3530f3a4a01SFabien Chouteau unit->config |= unit->irq_line << 3; 3540f3a4a01SFabien Chouteau unit->config |= 1 << 8; /* separate interrupt */ 3550f3a4a01SFabien Chouteau unit->config |= 1 << 9; /* Disable timer freeze */ 3560f3a4a01SFabien Chouteau 3570f3a4a01SFabien Chouteau 3580f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3590f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3600f3a4a01SFabien Chouteau 3610f3a4a01SFabien Chouteau timer->counter = 0; 3620f3a4a01SFabien Chouteau timer->reload = 0; 3630f3a4a01SFabien Chouteau timer->config = 0; 364663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 3650f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 3660f3a4a01SFabien Chouteau ptimer_set_count(timer->ptimer, 0); 3670f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 368663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 3690f3a4a01SFabien Chouteau } 3700f3a4a01SFabien Chouteau } 3710f3a4a01SFabien Chouteau 37223251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp) 3730f3a4a01SFabien Chouteau { 374541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(dev); 3750f3a4a01SFabien Chouteau unsigned int i; 37623251fb8SMao Zhongyi SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 3770f3a4a01SFabien Chouteau 3780f3a4a01SFabien Chouteau assert(unit->nr_timers > 0); 3790f3a4a01SFabien Chouteau assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); 3800f3a4a01SFabien Chouteau 3817267c094SAnthony Liguori unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); 3820f3a4a01SFabien Chouteau 3830f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3840f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3850f3a4a01SFabien Chouteau 3860f3a4a01SFabien Chouteau timer->unit = unit; 387663e475fSPeter Maydell timer->ptimer = ptimer_init(grlib_gptimer_hit, timer, 3889598c1bbSPeter Maydell PTIMER_POLICY_LEGACY); 3890f3a4a01SFabien Chouteau timer->id = i; 3900f3a4a01SFabien Chouteau 3910f3a4a01SFabien Chouteau /* One IRQ line for each timer */ 39223251fb8SMao Zhongyi sysbus_init_irq(sbd, &timer->irq); 3930f3a4a01SFabien Chouteau 394663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 3950f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 396663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 3970f3a4a01SFabien Chouteau } 3980f3a4a01SFabien Chouteau 399853dca12SPaolo Bonzini memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, 400853dca12SPaolo Bonzini unit, "gptimer", 401cde844faSAvi Kivity UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); 4020f3a4a01SFabien Chouteau 40323251fb8SMao Zhongyi sysbus_init_mmio(sbd, &unit->iomem); 4040f3a4a01SFabien Chouteau } 4050f3a4a01SFabien Chouteau 406999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = { 4070f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), 4080f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), 4090f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), 410999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 411999e12bbSAnthony Liguori }; 412999e12bbSAnthony Liguori 413999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data) 414999e12bbSAnthony Liguori { 41539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 416999e12bbSAnthony Liguori 41723251fb8SMao Zhongyi dc->realize = grlib_gptimer_realize; 418*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, grlib_gptimer_reset); 4194f67d30bSMarc-André Lureau device_class_set_props(dc, grlib_gptimer_properties); 4200f3a4a01SFabien Chouteau } 421999e12bbSAnthony Liguori 4228c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = { 423541ab55fSAndreas Färber .name = TYPE_GRLIB_GPTIMER, 42439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 42539bffca2SAnthony Liguori .instance_size = sizeof(GPTimerUnit), 426999e12bbSAnthony Liguori .class_init = grlib_gptimer_class_init, 4270f3a4a01SFabien Chouteau }; 4280f3a4a01SFabien Chouteau 42983f7d43aSAndreas Färber static void grlib_gptimer_register_types(void) 4300f3a4a01SFabien Chouteau { 43139bffca2SAnthony Liguori type_register_static(&grlib_gptimer_info); 4320f3a4a01SFabien Chouteau } 4330f3a4a01SFabien Chouteau 43483f7d43aSAndreas Färber type_init(grlib_gptimer_register_types) 435