10f3a4a01SFabien Chouteau /* 20f3a4a01SFabien Chouteau * QEMU GRLIB GPTimer Emulator 30f3a4a01SFabien Chouteau * 4948caec8SKONRAD Frederic * Copyright (c) 2010-2019 AdaCore 50f3a4a01SFabien Chouteau * 60f3a4a01SFabien Chouteau * Permission is hereby granted, free of charge, to any person obtaining a copy 70f3a4a01SFabien Chouteau * of this software and associated documentation files (the "Software"), to deal 80f3a4a01SFabien Chouteau * in the Software without restriction, including without limitation the rights 90f3a4a01SFabien Chouteau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 100f3a4a01SFabien Chouteau * copies of the Software, and to permit persons to whom the Software is 110f3a4a01SFabien Chouteau * furnished to do so, subject to the following conditions: 120f3a4a01SFabien Chouteau * 130f3a4a01SFabien Chouteau * The above copyright notice and this permission notice shall be included in 140f3a4a01SFabien Chouteau * all copies or substantial portions of the Software. 150f3a4a01SFabien Chouteau * 160f3a4a01SFabien Chouteau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 170f3a4a01SFabien Chouteau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 180f3a4a01SFabien Chouteau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 190f3a4a01SFabien Chouteau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 200f3a4a01SFabien Chouteau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 210f3a4a01SFabien Chouteau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 220f3a4a01SFabien Chouteau * THE SOFTWARE. 230f3a4a01SFabien Chouteau */ 240f3a4a01SFabien Chouteau 25db5ebe5fSPeter Maydell #include "qemu/osdep.h" 26948caec8SKONRAD Frederic #include "hw/sparc/grlib.h" 2783c9f4caSPaolo Bonzini #include "hw/sysbus.h" 281de7afc9SPaolo Bonzini #include "qemu/timer.h" 2964552b6bSMarkus Armbruster #include "hw/irq.h" 3083c9f4caSPaolo Bonzini #include "hw/ptimer.h" 31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 320b8fa32fSMarkus Armbruster #include "qemu/module.h" 330f3a4a01SFabien Chouteau 340f3a4a01SFabien Chouteau #include "trace.h" 350f3a4a01SFabien Chouteau 360f3a4a01SFabien Chouteau #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ 370f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ 380f3a4a01SFabien Chouteau 390f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8 400f3a4a01SFabien Chouteau 410f3a4a01SFabien Chouteau /* GPTimer Config register fields */ 420f3a4a01SFabien Chouteau #define GPTIMER_ENABLE (1 << 0) 430f3a4a01SFabien Chouteau #define GPTIMER_RESTART (1 << 1) 440f3a4a01SFabien Chouteau #define GPTIMER_LOAD (1 << 2) 450f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE (1 << 3) 460f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4) 470f3a4a01SFabien Chouteau #define GPTIMER_CHAIN (1 << 5) /* Not supported */ 480f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ 490f3a4a01SFabien Chouteau 500f3a4a01SFabien Chouteau /* Memory mapped register offsets */ 510f3a4a01SFabien Chouteau #define SCALER_OFFSET 0x00 520f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET 0x04 530f3a4a01SFabien Chouteau #define CONFIG_OFFSET 0x08 540f3a4a01SFabien Chouteau #define COUNTER_OFFSET 0x00 550f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04 560f3a4a01SFabien Chouteau #define TIMER_BASE 0x10 570f3a4a01SFabien Chouteau 58541ab55fSAndreas Färber #define GRLIB_GPTIMER(obj) \ 59541ab55fSAndreas Färber OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER) 60541ab55fSAndreas Färber 610f3a4a01SFabien Chouteau typedef struct GPTimer GPTimer; 620f3a4a01SFabien Chouteau typedef struct GPTimerUnit GPTimerUnit; 630f3a4a01SFabien Chouteau 640f3a4a01SFabien Chouteau struct GPTimer { 650f3a4a01SFabien Chouteau struct ptimer_state *ptimer; 660f3a4a01SFabien Chouteau 670f3a4a01SFabien Chouteau qemu_irq irq; 680f3a4a01SFabien Chouteau int id; 690f3a4a01SFabien Chouteau GPTimerUnit *unit; 700f3a4a01SFabien Chouteau 710f3a4a01SFabien Chouteau /* registers */ 720f3a4a01SFabien Chouteau uint32_t counter; 730f3a4a01SFabien Chouteau uint32_t reload; 740f3a4a01SFabien Chouteau uint32_t config; 750f3a4a01SFabien Chouteau }; 760f3a4a01SFabien Chouteau 770f3a4a01SFabien Chouteau struct GPTimerUnit { 78541ab55fSAndreas Färber SysBusDevice parent_obj; 79541ab55fSAndreas Färber 80cde844faSAvi Kivity MemoryRegion iomem; 810f3a4a01SFabien Chouteau 820f3a4a01SFabien Chouteau uint32_t nr_timers; /* Number of timers available */ 830f3a4a01SFabien Chouteau uint32_t freq_hz; /* System frequency */ 840f3a4a01SFabien Chouteau uint32_t irq_line; /* Base irq line */ 850f3a4a01SFabien Chouteau 860f3a4a01SFabien Chouteau GPTimer *timers; 870f3a4a01SFabien Chouteau 880f3a4a01SFabien Chouteau /* registers */ 890f3a4a01SFabien Chouteau uint32_t scaler; 900f3a4a01SFabien Chouteau uint32_t reload; 910f3a4a01SFabien Chouteau uint32_t config; 920f3a4a01SFabien Chouteau }; 930f3a4a01SFabien Chouteau 94*663e475fSPeter Maydell static void grlib_gptimer_tx_begin(GPTimer *timer) 95*663e475fSPeter Maydell { 96*663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 97*663e475fSPeter Maydell } 98*663e475fSPeter Maydell 99*663e475fSPeter Maydell static void grlib_gptimer_tx_commit(GPTimer *timer) 100*663e475fSPeter Maydell { 101*663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 102*663e475fSPeter Maydell } 103*663e475fSPeter Maydell 104*663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */ 1050f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer) 1060f3a4a01SFabien Chouteau { 1070f3a4a01SFabien Chouteau assert(timer != NULL); 1080f3a4a01SFabien Chouteau 1090f3a4a01SFabien Chouteau 1100f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 1110f3a4a01SFabien Chouteau 1120f3a4a01SFabien Chouteau if (!(timer->config & GPTIMER_ENABLE)) { 1130f3a4a01SFabien Chouteau /* Timer disabled */ 1140f3a4a01SFabien Chouteau trace_grlib_gptimer_disabled(timer->id, timer->config); 1150f3a4a01SFabien Chouteau return; 1160f3a4a01SFabien Chouteau } 1170f3a4a01SFabien Chouteau 1180f3a4a01SFabien Chouteau /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at 1190f3a4a01SFabien Chouteau underflow. Set count + 1 to simulate the GPTimer behavior. */ 1200f3a4a01SFabien Chouteau 1219d5614d5SSebastian Huber trace_grlib_gptimer_enable(timer->id, timer->counter); 1220f3a4a01SFabien Chouteau 1239d5614d5SSebastian Huber ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1); 1240f3a4a01SFabien Chouteau ptimer_run(timer->ptimer, 1); 1250f3a4a01SFabien Chouteau } 1260f3a4a01SFabien Chouteau 127*663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */ 1280f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer) 1290f3a4a01SFabien Chouteau { 1300f3a4a01SFabien Chouteau assert(timer != NULL); 1310f3a4a01SFabien Chouteau 1320f3a4a01SFabien Chouteau trace_grlib_gptimer_restart(timer->id, timer->reload); 1330f3a4a01SFabien Chouteau 1340f3a4a01SFabien Chouteau timer->counter = timer->reload; 1350f3a4a01SFabien Chouteau grlib_gptimer_enable(timer); 1360f3a4a01SFabien Chouteau } 1370f3a4a01SFabien Chouteau 1380f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) 1390f3a4a01SFabien Chouteau { 1400f3a4a01SFabien Chouteau int i = 0; 1410f3a4a01SFabien Chouteau uint32_t value = 0; 1420f3a4a01SFabien Chouteau 1430f3a4a01SFabien Chouteau assert(unit != NULL); 1440f3a4a01SFabien Chouteau 1450f3a4a01SFabien Chouteau if (scaler > 0) { 1460f3a4a01SFabien Chouteau value = unit->freq_hz / (scaler + 1); 1470f3a4a01SFabien Chouteau } else { 1480f3a4a01SFabien Chouteau value = unit->freq_hz; 1490f3a4a01SFabien Chouteau } 1500f3a4a01SFabien Chouteau 1510f3a4a01SFabien Chouteau trace_grlib_gptimer_set_scaler(scaler, value); 1520f3a4a01SFabien Chouteau 1530f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 154*663e475fSPeter Maydell ptimer_transaction_begin(unit->timers[i].ptimer); 1550f3a4a01SFabien Chouteau ptimer_set_freq(unit->timers[i].ptimer, value); 156*663e475fSPeter Maydell ptimer_transaction_commit(unit->timers[i].ptimer); 1570f3a4a01SFabien Chouteau } 1580f3a4a01SFabien Chouteau } 1590f3a4a01SFabien Chouteau 1600f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque) 1610f3a4a01SFabien Chouteau { 1620f3a4a01SFabien Chouteau GPTimer *timer = opaque; 1630f3a4a01SFabien Chouteau assert(timer != NULL); 1640f3a4a01SFabien Chouteau 1650f3a4a01SFabien Chouteau trace_grlib_gptimer_hit(timer->id); 1660f3a4a01SFabien Chouteau 1670f3a4a01SFabien Chouteau /* Timer expired */ 1680f3a4a01SFabien Chouteau 1690f3a4a01SFabien Chouteau if (timer->config & GPTIMER_INT_ENABLE) { 1700f3a4a01SFabien Chouteau /* Set the pending bit (only unset by write in the config register) */ 1710f3a4a01SFabien Chouteau timer->config |= GPTIMER_INT_PENDING; 1720f3a4a01SFabien Chouteau qemu_irq_pulse(timer->irq); 1730f3a4a01SFabien Chouteau } 1740f3a4a01SFabien Chouteau 1750f3a4a01SFabien Chouteau if (timer->config & GPTIMER_RESTART) { 1760f3a4a01SFabien Chouteau grlib_gptimer_restart(timer); 1770f3a4a01SFabien Chouteau } 1780f3a4a01SFabien Chouteau } 1790f3a4a01SFabien Chouteau 180a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, 181cde844faSAvi Kivity unsigned size) 1820f3a4a01SFabien Chouteau { 1830f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 184a8170e5eSAvi Kivity hwaddr timer_addr; 1850f3a4a01SFabien Chouteau int id; 1860f3a4a01SFabien Chouteau uint32_t value = 0; 1870f3a4a01SFabien Chouteau 1880f3a4a01SFabien Chouteau addr &= 0xff; 1890f3a4a01SFabien Chouteau 1900f3a4a01SFabien Chouteau /* Unit registers */ 1910f3a4a01SFabien Chouteau switch (addr) { 1920f3a4a01SFabien Chouteau case SCALER_OFFSET: 193b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->scaler); 1940f3a4a01SFabien Chouteau return unit->scaler; 1950f3a4a01SFabien Chouteau 1960f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 197b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->reload); 1980f3a4a01SFabien Chouteau return unit->reload; 1990f3a4a01SFabien Chouteau 2000f3a4a01SFabien Chouteau case CONFIG_OFFSET: 201b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->config); 2020f3a4a01SFabien Chouteau return unit->config; 2030f3a4a01SFabien Chouteau 2040f3a4a01SFabien Chouteau default: 2050f3a4a01SFabien Chouteau break; 2060f3a4a01SFabien Chouteau } 2070f3a4a01SFabien Chouteau 2080f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2090f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2100f3a4a01SFabien Chouteau 2110f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2120f3a4a01SFabien Chouteau 2130f3a4a01SFabien Chouteau /* GPTimer registers */ 2140f3a4a01SFabien Chouteau switch (timer_addr) { 2150f3a4a01SFabien Chouteau case COUNTER_OFFSET: 2160f3a4a01SFabien Chouteau value = ptimer_get_count(unit->timers[id].ptimer); 217b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2180f3a4a01SFabien Chouteau return value; 2190f3a4a01SFabien Chouteau 2200f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 2210f3a4a01SFabien Chouteau value = unit->timers[id].reload; 222b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2230f3a4a01SFabien Chouteau return value; 2240f3a4a01SFabien Chouteau 2250f3a4a01SFabien Chouteau case CONFIG_OFFSET: 226b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); 2270f3a4a01SFabien Chouteau return unit->timers[id].config; 2280f3a4a01SFabien Chouteau 2290f3a4a01SFabien Chouteau default: 2300f3a4a01SFabien Chouteau break; 2310f3a4a01SFabien Chouteau } 2320f3a4a01SFabien Chouteau 2330f3a4a01SFabien Chouteau } 2340f3a4a01SFabien Chouteau 235b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, 0); 2360f3a4a01SFabien Chouteau return 0; 2370f3a4a01SFabien Chouteau } 2380f3a4a01SFabien Chouteau 239a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr, 240cde844faSAvi Kivity uint64_t value, unsigned size) 2410f3a4a01SFabien Chouteau { 2420f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 243a8170e5eSAvi Kivity hwaddr timer_addr; 2440f3a4a01SFabien Chouteau int id; 2450f3a4a01SFabien Chouteau 2460f3a4a01SFabien Chouteau addr &= 0xff; 2470f3a4a01SFabien Chouteau 2480f3a4a01SFabien Chouteau /* Unit registers */ 2490f3a4a01SFabien Chouteau switch (addr) { 2500f3a4a01SFabien Chouteau case SCALER_OFFSET: 2510f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2520f3a4a01SFabien Chouteau unit->scaler = value; 253b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->scaler); 2540f3a4a01SFabien Chouteau return; 2550f3a4a01SFabien Chouteau 2560f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 2570f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2580f3a4a01SFabien Chouteau unit->reload = value; 259b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->reload); 2600f3a4a01SFabien Chouteau grlib_gptimer_set_scaler(unit, value); 2610f3a4a01SFabien Chouteau return; 2620f3a4a01SFabien Chouteau 2630f3a4a01SFabien Chouteau case CONFIG_OFFSET: 2640f3a4a01SFabien Chouteau /* Read Only (disable timer freeze not supported) */ 265b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, 0); 2660f3a4a01SFabien Chouteau return; 2670f3a4a01SFabien Chouteau 2680f3a4a01SFabien Chouteau default: 2690f3a4a01SFabien Chouteau break; 2700f3a4a01SFabien Chouteau } 2710f3a4a01SFabien Chouteau 2720f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2730f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2740f3a4a01SFabien Chouteau 2750f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2760f3a4a01SFabien Chouteau 2770f3a4a01SFabien Chouteau /* GPTimer registers */ 2780f3a4a01SFabien Chouteau switch (timer_addr) { 2790f3a4a01SFabien Chouteau case COUNTER_OFFSET: 280b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 281*663e475fSPeter Maydell grlib_gptimer_tx_begin(&unit->timers[id]); 2820f3a4a01SFabien Chouteau unit->timers[id].counter = value; 2830f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 284*663e475fSPeter Maydell grlib_gptimer_tx_commit(&unit->timers[id]); 2850f3a4a01SFabien Chouteau return; 2860f3a4a01SFabien Chouteau 2870f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 288b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2890f3a4a01SFabien Chouteau unit->timers[id].reload = value; 2900f3a4a01SFabien Chouteau return; 2910f3a4a01SFabien Chouteau 2920f3a4a01SFabien Chouteau case CONFIG_OFFSET: 293b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2940f3a4a01SFabien Chouteau 2950f3a4a01SFabien Chouteau if (value & GPTIMER_INT_PENDING) { 2960f3a4a01SFabien Chouteau /* clear pending bit */ 2970f3a4a01SFabien Chouteau value &= ~GPTIMER_INT_PENDING; 2980f3a4a01SFabien Chouteau } else { 2990f3a4a01SFabien Chouteau /* keep pending bit */ 3000f3a4a01SFabien Chouteau value |= unit->timers[id].config & GPTIMER_INT_PENDING; 3010f3a4a01SFabien Chouteau } 3020f3a4a01SFabien Chouteau 3030f3a4a01SFabien Chouteau unit->timers[id].config = value; 3040f3a4a01SFabien Chouteau 3050f3a4a01SFabien Chouteau /* gptimer_restart calls gptimer_enable, so if "enable" and "load" 3060f3a4a01SFabien Chouteau bits are present, we just have to call restart. */ 3070f3a4a01SFabien Chouteau 308*663e475fSPeter Maydell grlib_gptimer_tx_begin(&unit->timers[id]); 3090f3a4a01SFabien Chouteau if (value & GPTIMER_LOAD) { 3100f3a4a01SFabien Chouteau grlib_gptimer_restart(&unit->timers[id]); 3110f3a4a01SFabien Chouteau } else if (value & GPTIMER_ENABLE) { 3120f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 3130f3a4a01SFabien Chouteau } 3140f3a4a01SFabien Chouteau 3150f3a4a01SFabien Chouteau /* These fields must always be read as 0 */ 3160f3a4a01SFabien Chouteau value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); 3170f3a4a01SFabien Chouteau 3180f3a4a01SFabien Chouteau unit->timers[id].config = value; 319*663e475fSPeter Maydell grlib_gptimer_tx_commit(&unit->timers[id]); 3200f3a4a01SFabien Chouteau return; 3210f3a4a01SFabien Chouteau 3220f3a4a01SFabien Chouteau default: 3230f3a4a01SFabien Chouteau break; 3240f3a4a01SFabien Chouteau } 3250f3a4a01SFabien Chouteau 3260f3a4a01SFabien Chouteau } 3270f3a4a01SFabien Chouteau 328b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, value); 3290f3a4a01SFabien Chouteau } 3300f3a4a01SFabien Chouteau 331cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = { 332cde844faSAvi Kivity .read = grlib_gptimer_read, 333cde844faSAvi Kivity .write = grlib_gptimer_write, 334cde844faSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 335cde844faSAvi Kivity .valid = { 336cde844faSAvi Kivity .min_access_size = 4, 337cde844faSAvi Kivity .max_access_size = 4, 338cde844faSAvi Kivity }, 3390f3a4a01SFabien Chouteau }; 3400f3a4a01SFabien Chouteau 3410f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d) 3420f3a4a01SFabien Chouteau { 343541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(d); 3440f3a4a01SFabien Chouteau int i = 0; 3450f3a4a01SFabien Chouteau 3460f3a4a01SFabien Chouteau assert(unit != NULL); 3470f3a4a01SFabien Chouteau 3480f3a4a01SFabien Chouteau unit->scaler = 0; 3490f3a4a01SFabien Chouteau unit->reload = 0; 3500f3a4a01SFabien Chouteau 3510f3a4a01SFabien Chouteau unit->config = unit->nr_timers; 3520f3a4a01SFabien Chouteau unit->config |= unit->irq_line << 3; 3530f3a4a01SFabien Chouteau unit->config |= 1 << 8; /* separate interrupt */ 3540f3a4a01SFabien Chouteau unit->config |= 1 << 9; /* Disable timer freeze */ 3550f3a4a01SFabien Chouteau 3560f3a4a01SFabien Chouteau 3570f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3580f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3590f3a4a01SFabien Chouteau 3600f3a4a01SFabien Chouteau timer->counter = 0; 3610f3a4a01SFabien Chouteau timer->reload = 0; 3620f3a4a01SFabien Chouteau timer->config = 0; 363*663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 3640f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 3650f3a4a01SFabien Chouteau ptimer_set_count(timer->ptimer, 0); 3660f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 367*663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 3680f3a4a01SFabien Chouteau } 3690f3a4a01SFabien Chouteau } 3700f3a4a01SFabien Chouteau 37123251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp) 3720f3a4a01SFabien Chouteau { 373541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(dev); 3740f3a4a01SFabien Chouteau unsigned int i; 37523251fb8SMao Zhongyi SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 3760f3a4a01SFabien Chouteau 3770f3a4a01SFabien Chouteau assert(unit->nr_timers > 0); 3780f3a4a01SFabien Chouteau assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); 3790f3a4a01SFabien Chouteau 3807267c094SAnthony Liguori unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); 3810f3a4a01SFabien Chouteau 3820f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3830f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3840f3a4a01SFabien Chouteau 3850f3a4a01SFabien Chouteau timer->unit = unit; 386*663e475fSPeter Maydell timer->ptimer = ptimer_init(grlib_gptimer_hit, timer, 387*663e475fSPeter Maydell PTIMER_POLICY_DEFAULT); 3880f3a4a01SFabien Chouteau timer->id = i; 3890f3a4a01SFabien Chouteau 3900f3a4a01SFabien Chouteau /* One IRQ line for each timer */ 39123251fb8SMao Zhongyi sysbus_init_irq(sbd, &timer->irq); 3920f3a4a01SFabien Chouteau 393*663e475fSPeter Maydell ptimer_transaction_begin(timer->ptimer); 3940f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 395*663e475fSPeter Maydell ptimer_transaction_commit(timer->ptimer); 3960f3a4a01SFabien Chouteau } 3970f3a4a01SFabien Chouteau 398853dca12SPaolo Bonzini memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, 399853dca12SPaolo Bonzini unit, "gptimer", 400cde844faSAvi Kivity UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); 4010f3a4a01SFabien Chouteau 40223251fb8SMao Zhongyi sysbus_init_mmio(sbd, &unit->iomem); 4030f3a4a01SFabien Chouteau } 4040f3a4a01SFabien Chouteau 405999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = { 4060f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), 4070f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), 4080f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), 409999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 410999e12bbSAnthony Liguori }; 411999e12bbSAnthony Liguori 412999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data) 413999e12bbSAnthony Liguori { 41439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 415999e12bbSAnthony Liguori 41623251fb8SMao Zhongyi dc->realize = grlib_gptimer_realize; 41739bffca2SAnthony Liguori dc->reset = grlib_gptimer_reset; 41839bffca2SAnthony Liguori dc->props = grlib_gptimer_properties; 4190f3a4a01SFabien Chouteau } 420999e12bbSAnthony Liguori 4218c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = { 422541ab55fSAndreas Färber .name = TYPE_GRLIB_GPTIMER, 42339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 42439bffca2SAnthony Liguori .instance_size = sizeof(GPTimerUnit), 425999e12bbSAnthony Liguori .class_init = grlib_gptimer_class_init, 4260f3a4a01SFabien Chouteau }; 4270f3a4a01SFabien Chouteau 42883f7d43aSAndreas Färber static void grlib_gptimer_register_types(void) 4290f3a4a01SFabien Chouteau { 43039bffca2SAnthony Liguori type_register_static(&grlib_gptimer_info); 4310f3a4a01SFabien Chouteau } 4320f3a4a01SFabien Chouteau 43383f7d43aSAndreas Färber type_init(grlib_gptimer_register_types) 434