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" 2983c9f4caSPaolo Bonzini #include "hw/ptimer.h" 306a1751b7SAlex Bligh #include "qemu/main-loop.h" 31*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 320f3a4a01SFabien Chouteau 330f3a4a01SFabien Chouteau #include "trace.h" 340f3a4a01SFabien Chouteau 350f3a4a01SFabien Chouteau #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ 360f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ 370f3a4a01SFabien Chouteau 380f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8 390f3a4a01SFabien Chouteau 400f3a4a01SFabien Chouteau /* GPTimer Config register fields */ 410f3a4a01SFabien Chouteau #define GPTIMER_ENABLE (1 << 0) 420f3a4a01SFabien Chouteau #define GPTIMER_RESTART (1 << 1) 430f3a4a01SFabien Chouteau #define GPTIMER_LOAD (1 << 2) 440f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE (1 << 3) 450f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4) 460f3a4a01SFabien Chouteau #define GPTIMER_CHAIN (1 << 5) /* Not supported */ 470f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ 480f3a4a01SFabien Chouteau 490f3a4a01SFabien Chouteau /* Memory mapped register offsets */ 500f3a4a01SFabien Chouteau #define SCALER_OFFSET 0x00 510f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET 0x04 520f3a4a01SFabien Chouteau #define CONFIG_OFFSET 0x08 530f3a4a01SFabien Chouteau #define COUNTER_OFFSET 0x00 540f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04 550f3a4a01SFabien Chouteau #define TIMER_BASE 0x10 560f3a4a01SFabien Chouteau 57541ab55fSAndreas Färber #define GRLIB_GPTIMER(obj) \ 58541ab55fSAndreas Färber OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER) 59541ab55fSAndreas Färber 600f3a4a01SFabien Chouteau typedef struct GPTimer GPTimer; 610f3a4a01SFabien Chouteau typedef struct GPTimerUnit GPTimerUnit; 620f3a4a01SFabien Chouteau 630f3a4a01SFabien Chouteau struct GPTimer { 640f3a4a01SFabien Chouteau QEMUBH *bh; 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 940f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer) 950f3a4a01SFabien Chouteau { 960f3a4a01SFabien Chouteau assert(timer != NULL); 970f3a4a01SFabien Chouteau 980f3a4a01SFabien Chouteau 990f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 1000f3a4a01SFabien Chouteau 1010f3a4a01SFabien Chouteau if (!(timer->config & GPTIMER_ENABLE)) { 1020f3a4a01SFabien Chouteau /* Timer disabled */ 1030f3a4a01SFabien Chouteau trace_grlib_gptimer_disabled(timer->id, timer->config); 1040f3a4a01SFabien Chouteau return; 1050f3a4a01SFabien Chouteau } 1060f3a4a01SFabien Chouteau 1070f3a4a01SFabien Chouteau /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at 1080f3a4a01SFabien Chouteau underflow. Set count + 1 to simulate the GPTimer behavior. */ 1090f3a4a01SFabien Chouteau 1109d5614d5SSebastian Huber trace_grlib_gptimer_enable(timer->id, timer->counter); 1110f3a4a01SFabien Chouteau 1129d5614d5SSebastian Huber ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1); 1130f3a4a01SFabien Chouteau ptimer_run(timer->ptimer, 1); 1140f3a4a01SFabien Chouteau } 1150f3a4a01SFabien Chouteau 1160f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer) 1170f3a4a01SFabien Chouteau { 1180f3a4a01SFabien Chouteau assert(timer != NULL); 1190f3a4a01SFabien Chouteau 1200f3a4a01SFabien Chouteau trace_grlib_gptimer_restart(timer->id, timer->reload); 1210f3a4a01SFabien Chouteau 1220f3a4a01SFabien Chouteau timer->counter = timer->reload; 1230f3a4a01SFabien Chouteau grlib_gptimer_enable(timer); 1240f3a4a01SFabien Chouteau } 1250f3a4a01SFabien Chouteau 1260f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) 1270f3a4a01SFabien Chouteau { 1280f3a4a01SFabien Chouteau int i = 0; 1290f3a4a01SFabien Chouteau uint32_t value = 0; 1300f3a4a01SFabien Chouteau 1310f3a4a01SFabien Chouteau assert(unit != NULL); 1320f3a4a01SFabien Chouteau 1330f3a4a01SFabien Chouteau if (scaler > 0) { 1340f3a4a01SFabien Chouteau value = unit->freq_hz / (scaler + 1); 1350f3a4a01SFabien Chouteau } else { 1360f3a4a01SFabien Chouteau value = unit->freq_hz; 1370f3a4a01SFabien Chouteau } 1380f3a4a01SFabien Chouteau 1390f3a4a01SFabien Chouteau trace_grlib_gptimer_set_scaler(scaler, value); 1400f3a4a01SFabien Chouteau 1410f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 1420f3a4a01SFabien Chouteau ptimer_set_freq(unit->timers[i].ptimer, value); 1430f3a4a01SFabien Chouteau } 1440f3a4a01SFabien Chouteau } 1450f3a4a01SFabien Chouteau 1460f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque) 1470f3a4a01SFabien Chouteau { 1480f3a4a01SFabien Chouteau GPTimer *timer = opaque; 1490f3a4a01SFabien Chouteau assert(timer != NULL); 1500f3a4a01SFabien Chouteau 1510f3a4a01SFabien Chouteau trace_grlib_gptimer_hit(timer->id); 1520f3a4a01SFabien Chouteau 1530f3a4a01SFabien Chouteau /* Timer expired */ 1540f3a4a01SFabien Chouteau 1550f3a4a01SFabien Chouteau if (timer->config & GPTIMER_INT_ENABLE) { 1560f3a4a01SFabien Chouteau /* Set the pending bit (only unset by write in the config register) */ 1570f3a4a01SFabien Chouteau timer->config |= GPTIMER_INT_PENDING; 1580f3a4a01SFabien Chouteau qemu_irq_pulse(timer->irq); 1590f3a4a01SFabien Chouteau } 1600f3a4a01SFabien Chouteau 1610f3a4a01SFabien Chouteau if (timer->config & GPTIMER_RESTART) { 1620f3a4a01SFabien Chouteau grlib_gptimer_restart(timer); 1630f3a4a01SFabien Chouteau } 1640f3a4a01SFabien Chouteau } 1650f3a4a01SFabien Chouteau 166a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, 167cde844faSAvi Kivity unsigned size) 1680f3a4a01SFabien Chouteau { 1690f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 170a8170e5eSAvi Kivity hwaddr timer_addr; 1710f3a4a01SFabien Chouteau int id; 1720f3a4a01SFabien Chouteau uint32_t value = 0; 1730f3a4a01SFabien Chouteau 1740f3a4a01SFabien Chouteau addr &= 0xff; 1750f3a4a01SFabien Chouteau 1760f3a4a01SFabien Chouteau /* Unit registers */ 1770f3a4a01SFabien Chouteau switch (addr) { 1780f3a4a01SFabien Chouteau case SCALER_OFFSET: 179b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->scaler); 1800f3a4a01SFabien Chouteau return unit->scaler; 1810f3a4a01SFabien Chouteau 1820f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 183b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->reload); 1840f3a4a01SFabien Chouteau return unit->reload; 1850f3a4a01SFabien Chouteau 1860f3a4a01SFabien Chouteau case CONFIG_OFFSET: 187b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->config); 1880f3a4a01SFabien Chouteau return unit->config; 1890f3a4a01SFabien Chouteau 1900f3a4a01SFabien Chouteau default: 1910f3a4a01SFabien Chouteau break; 1920f3a4a01SFabien Chouteau } 1930f3a4a01SFabien Chouteau 1940f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 1950f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 1960f3a4a01SFabien Chouteau 1970f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 1980f3a4a01SFabien Chouteau 1990f3a4a01SFabien Chouteau /* GPTimer registers */ 2000f3a4a01SFabien Chouteau switch (timer_addr) { 2010f3a4a01SFabien Chouteau case COUNTER_OFFSET: 2020f3a4a01SFabien Chouteau value = ptimer_get_count(unit->timers[id].ptimer); 203b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2040f3a4a01SFabien Chouteau return value; 2050f3a4a01SFabien Chouteau 2060f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 2070f3a4a01SFabien Chouteau value = unit->timers[id].reload; 208b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2090f3a4a01SFabien Chouteau return value; 2100f3a4a01SFabien Chouteau 2110f3a4a01SFabien Chouteau case CONFIG_OFFSET: 212b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); 2130f3a4a01SFabien Chouteau return unit->timers[id].config; 2140f3a4a01SFabien Chouteau 2150f3a4a01SFabien Chouteau default: 2160f3a4a01SFabien Chouteau break; 2170f3a4a01SFabien Chouteau } 2180f3a4a01SFabien Chouteau 2190f3a4a01SFabien Chouteau } 2200f3a4a01SFabien Chouteau 221b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, 0); 2220f3a4a01SFabien Chouteau return 0; 2230f3a4a01SFabien Chouteau } 2240f3a4a01SFabien Chouteau 225a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr, 226cde844faSAvi Kivity uint64_t value, unsigned size) 2270f3a4a01SFabien Chouteau { 2280f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 229a8170e5eSAvi Kivity hwaddr timer_addr; 2300f3a4a01SFabien Chouteau int id; 2310f3a4a01SFabien Chouteau 2320f3a4a01SFabien Chouteau addr &= 0xff; 2330f3a4a01SFabien Chouteau 2340f3a4a01SFabien Chouteau /* Unit registers */ 2350f3a4a01SFabien Chouteau switch (addr) { 2360f3a4a01SFabien Chouteau case SCALER_OFFSET: 2370f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2380f3a4a01SFabien Chouteau unit->scaler = value; 239b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->scaler); 2400f3a4a01SFabien Chouteau return; 2410f3a4a01SFabien Chouteau 2420f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 2430f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2440f3a4a01SFabien Chouteau unit->reload = value; 245b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->reload); 2460f3a4a01SFabien Chouteau grlib_gptimer_set_scaler(unit, value); 2470f3a4a01SFabien Chouteau return; 2480f3a4a01SFabien Chouteau 2490f3a4a01SFabien Chouteau case CONFIG_OFFSET: 2500f3a4a01SFabien Chouteau /* Read Only (disable timer freeze not supported) */ 251b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, 0); 2520f3a4a01SFabien Chouteau return; 2530f3a4a01SFabien Chouteau 2540f3a4a01SFabien Chouteau default: 2550f3a4a01SFabien Chouteau break; 2560f3a4a01SFabien Chouteau } 2570f3a4a01SFabien Chouteau 2580f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2590f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2600f3a4a01SFabien Chouteau 2610f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2620f3a4a01SFabien Chouteau 2630f3a4a01SFabien Chouteau /* GPTimer registers */ 2640f3a4a01SFabien Chouteau switch (timer_addr) { 2650f3a4a01SFabien Chouteau case COUNTER_OFFSET: 266b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2670f3a4a01SFabien Chouteau unit->timers[id].counter = value; 2680f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2690f3a4a01SFabien Chouteau return; 2700f3a4a01SFabien Chouteau 2710f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 272b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2730f3a4a01SFabien Chouteau unit->timers[id].reload = value; 2740f3a4a01SFabien Chouteau return; 2750f3a4a01SFabien Chouteau 2760f3a4a01SFabien Chouteau case CONFIG_OFFSET: 277b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2780f3a4a01SFabien Chouteau 2790f3a4a01SFabien Chouteau if (value & GPTIMER_INT_PENDING) { 2800f3a4a01SFabien Chouteau /* clear pending bit */ 2810f3a4a01SFabien Chouteau value &= ~GPTIMER_INT_PENDING; 2820f3a4a01SFabien Chouteau } else { 2830f3a4a01SFabien Chouteau /* keep pending bit */ 2840f3a4a01SFabien Chouteau value |= unit->timers[id].config & GPTIMER_INT_PENDING; 2850f3a4a01SFabien Chouteau } 2860f3a4a01SFabien Chouteau 2870f3a4a01SFabien Chouteau unit->timers[id].config = value; 2880f3a4a01SFabien Chouteau 2890f3a4a01SFabien Chouteau /* gptimer_restart calls gptimer_enable, so if "enable" and "load" 2900f3a4a01SFabien Chouteau bits are present, we just have to call restart. */ 2910f3a4a01SFabien Chouteau 2920f3a4a01SFabien Chouteau if (value & GPTIMER_LOAD) { 2930f3a4a01SFabien Chouteau grlib_gptimer_restart(&unit->timers[id]); 2940f3a4a01SFabien Chouteau } else if (value & GPTIMER_ENABLE) { 2950f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2960f3a4a01SFabien Chouteau } 2970f3a4a01SFabien Chouteau 2980f3a4a01SFabien Chouteau /* These fields must always be read as 0 */ 2990f3a4a01SFabien Chouteau value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); 3000f3a4a01SFabien Chouteau 3010f3a4a01SFabien Chouteau unit->timers[id].config = value; 3020f3a4a01SFabien Chouteau return; 3030f3a4a01SFabien Chouteau 3040f3a4a01SFabien Chouteau default: 3050f3a4a01SFabien Chouteau break; 3060f3a4a01SFabien Chouteau } 3070f3a4a01SFabien Chouteau 3080f3a4a01SFabien Chouteau } 3090f3a4a01SFabien Chouteau 310b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, value); 3110f3a4a01SFabien Chouteau } 3120f3a4a01SFabien Chouteau 313cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = { 314cde844faSAvi Kivity .read = grlib_gptimer_read, 315cde844faSAvi Kivity .write = grlib_gptimer_write, 316cde844faSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 317cde844faSAvi Kivity .valid = { 318cde844faSAvi Kivity .min_access_size = 4, 319cde844faSAvi Kivity .max_access_size = 4, 320cde844faSAvi Kivity }, 3210f3a4a01SFabien Chouteau }; 3220f3a4a01SFabien Chouteau 3230f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d) 3240f3a4a01SFabien Chouteau { 325541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(d); 3260f3a4a01SFabien Chouteau int i = 0; 3270f3a4a01SFabien Chouteau 3280f3a4a01SFabien Chouteau assert(unit != NULL); 3290f3a4a01SFabien Chouteau 3300f3a4a01SFabien Chouteau unit->scaler = 0; 3310f3a4a01SFabien Chouteau unit->reload = 0; 3320f3a4a01SFabien Chouteau 3330f3a4a01SFabien Chouteau unit->config = unit->nr_timers; 3340f3a4a01SFabien Chouteau unit->config |= unit->irq_line << 3; 3350f3a4a01SFabien Chouteau unit->config |= 1 << 8; /* separate interrupt */ 3360f3a4a01SFabien Chouteau unit->config |= 1 << 9; /* Disable timer freeze */ 3370f3a4a01SFabien Chouteau 3380f3a4a01SFabien Chouteau 3390f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3400f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3410f3a4a01SFabien Chouteau 3420f3a4a01SFabien Chouteau timer->counter = 0; 3430f3a4a01SFabien Chouteau timer->reload = 0; 3440f3a4a01SFabien Chouteau timer->config = 0; 3450f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 3460f3a4a01SFabien Chouteau ptimer_set_count(timer->ptimer, 0); 3470f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 3480f3a4a01SFabien Chouteau } 3490f3a4a01SFabien Chouteau } 3500f3a4a01SFabien Chouteau 35123251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp) 3520f3a4a01SFabien Chouteau { 353541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(dev); 3540f3a4a01SFabien Chouteau unsigned int i; 35523251fb8SMao Zhongyi SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 3560f3a4a01SFabien Chouteau 3570f3a4a01SFabien Chouteau assert(unit->nr_timers > 0); 3580f3a4a01SFabien Chouteau assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); 3590f3a4a01SFabien Chouteau 3607267c094SAnthony Liguori unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); 3610f3a4a01SFabien Chouteau 3620f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3630f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3640f3a4a01SFabien Chouteau 3650f3a4a01SFabien Chouteau timer->unit = unit; 3660f3a4a01SFabien Chouteau timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); 367e7ea81c3SDmitry Osipenko timer->ptimer = ptimer_init(timer->bh, PTIMER_POLICY_DEFAULT); 3680f3a4a01SFabien Chouteau timer->id = i; 3690f3a4a01SFabien Chouteau 3700f3a4a01SFabien Chouteau /* One IRQ line for each timer */ 37123251fb8SMao Zhongyi sysbus_init_irq(sbd, &timer->irq); 3720f3a4a01SFabien Chouteau 3730f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 3740f3a4a01SFabien Chouteau } 3750f3a4a01SFabien Chouteau 376853dca12SPaolo Bonzini memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, 377853dca12SPaolo Bonzini unit, "gptimer", 378cde844faSAvi Kivity UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); 3790f3a4a01SFabien Chouteau 38023251fb8SMao Zhongyi sysbus_init_mmio(sbd, &unit->iomem); 3810f3a4a01SFabien Chouteau } 3820f3a4a01SFabien Chouteau 383999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = { 3840f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), 3850f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), 3860f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), 387999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 388999e12bbSAnthony Liguori }; 389999e12bbSAnthony Liguori 390999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data) 391999e12bbSAnthony Liguori { 39239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 393999e12bbSAnthony Liguori 39423251fb8SMao Zhongyi dc->realize = grlib_gptimer_realize; 39539bffca2SAnthony Liguori dc->reset = grlib_gptimer_reset; 39639bffca2SAnthony Liguori dc->props = grlib_gptimer_properties; 3970f3a4a01SFabien Chouteau } 398999e12bbSAnthony Liguori 3998c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = { 400541ab55fSAndreas Färber .name = TYPE_GRLIB_GPTIMER, 40139bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 40239bffca2SAnthony Liguori .instance_size = sizeof(GPTimerUnit), 403999e12bbSAnthony Liguori .class_init = grlib_gptimer_class_init, 4040f3a4a01SFabien Chouteau }; 4050f3a4a01SFabien Chouteau 40683f7d43aSAndreas Färber static void grlib_gptimer_register_types(void) 4070f3a4a01SFabien Chouteau { 40839bffca2SAnthony Liguori type_register_static(&grlib_gptimer_info); 4090f3a4a01SFabien Chouteau } 4100f3a4a01SFabien Chouteau 41183f7d43aSAndreas Färber type_init(grlib_gptimer_register_types) 412