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" 31*a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 326a1751b7SAlex Bligh #include "qemu/main-loop.h" 330b8fa32fSMarkus Armbruster #include "qemu/module.h" 340f3a4a01SFabien Chouteau 350f3a4a01SFabien Chouteau #include "trace.h" 360f3a4a01SFabien Chouteau 370f3a4a01SFabien Chouteau #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ 380f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ 390f3a4a01SFabien Chouteau 400f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8 410f3a4a01SFabien Chouteau 420f3a4a01SFabien Chouteau /* GPTimer Config register fields */ 430f3a4a01SFabien Chouteau #define GPTIMER_ENABLE (1 << 0) 440f3a4a01SFabien Chouteau #define GPTIMER_RESTART (1 << 1) 450f3a4a01SFabien Chouteau #define GPTIMER_LOAD (1 << 2) 460f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE (1 << 3) 470f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4) 480f3a4a01SFabien Chouteau #define GPTIMER_CHAIN (1 << 5) /* Not supported */ 490f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ 500f3a4a01SFabien Chouteau 510f3a4a01SFabien Chouteau /* Memory mapped register offsets */ 520f3a4a01SFabien Chouteau #define SCALER_OFFSET 0x00 530f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET 0x04 540f3a4a01SFabien Chouteau #define CONFIG_OFFSET 0x08 550f3a4a01SFabien Chouteau #define COUNTER_OFFSET 0x00 560f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04 570f3a4a01SFabien Chouteau #define TIMER_BASE 0x10 580f3a4a01SFabien Chouteau 59541ab55fSAndreas Färber #define GRLIB_GPTIMER(obj) \ 60541ab55fSAndreas Färber OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER) 61541ab55fSAndreas Färber 620f3a4a01SFabien Chouteau typedef struct GPTimer GPTimer; 630f3a4a01SFabien Chouteau typedef struct GPTimerUnit GPTimerUnit; 640f3a4a01SFabien Chouteau 650f3a4a01SFabien Chouteau struct GPTimer { 660f3a4a01SFabien Chouteau QEMUBH *bh; 670f3a4a01SFabien Chouteau struct ptimer_state *ptimer; 680f3a4a01SFabien Chouteau 690f3a4a01SFabien Chouteau qemu_irq irq; 700f3a4a01SFabien Chouteau int id; 710f3a4a01SFabien Chouteau GPTimerUnit *unit; 720f3a4a01SFabien Chouteau 730f3a4a01SFabien Chouteau /* registers */ 740f3a4a01SFabien Chouteau uint32_t counter; 750f3a4a01SFabien Chouteau uint32_t reload; 760f3a4a01SFabien Chouteau uint32_t config; 770f3a4a01SFabien Chouteau }; 780f3a4a01SFabien Chouteau 790f3a4a01SFabien Chouteau struct GPTimerUnit { 80541ab55fSAndreas Färber SysBusDevice parent_obj; 81541ab55fSAndreas Färber 82cde844faSAvi Kivity MemoryRegion iomem; 830f3a4a01SFabien Chouteau 840f3a4a01SFabien Chouteau uint32_t nr_timers; /* Number of timers available */ 850f3a4a01SFabien Chouteau uint32_t freq_hz; /* System frequency */ 860f3a4a01SFabien Chouteau uint32_t irq_line; /* Base irq line */ 870f3a4a01SFabien Chouteau 880f3a4a01SFabien Chouteau GPTimer *timers; 890f3a4a01SFabien Chouteau 900f3a4a01SFabien Chouteau /* registers */ 910f3a4a01SFabien Chouteau uint32_t scaler; 920f3a4a01SFabien Chouteau uint32_t reload; 930f3a4a01SFabien Chouteau uint32_t config; 940f3a4a01SFabien Chouteau }; 950f3a4a01SFabien Chouteau 960f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer) 970f3a4a01SFabien Chouteau { 980f3a4a01SFabien Chouteau assert(timer != NULL); 990f3a4a01SFabien Chouteau 1000f3a4a01SFabien Chouteau 1010f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 1020f3a4a01SFabien Chouteau 1030f3a4a01SFabien Chouteau if (!(timer->config & GPTIMER_ENABLE)) { 1040f3a4a01SFabien Chouteau /* Timer disabled */ 1050f3a4a01SFabien Chouteau trace_grlib_gptimer_disabled(timer->id, timer->config); 1060f3a4a01SFabien Chouteau return; 1070f3a4a01SFabien Chouteau } 1080f3a4a01SFabien Chouteau 1090f3a4a01SFabien Chouteau /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at 1100f3a4a01SFabien Chouteau underflow. Set count + 1 to simulate the GPTimer behavior. */ 1110f3a4a01SFabien Chouteau 1129d5614d5SSebastian Huber trace_grlib_gptimer_enable(timer->id, timer->counter); 1130f3a4a01SFabien Chouteau 1149d5614d5SSebastian Huber ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1); 1150f3a4a01SFabien Chouteau ptimer_run(timer->ptimer, 1); 1160f3a4a01SFabien Chouteau } 1170f3a4a01SFabien Chouteau 1180f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer) 1190f3a4a01SFabien Chouteau { 1200f3a4a01SFabien Chouteau assert(timer != NULL); 1210f3a4a01SFabien Chouteau 1220f3a4a01SFabien Chouteau trace_grlib_gptimer_restart(timer->id, timer->reload); 1230f3a4a01SFabien Chouteau 1240f3a4a01SFabien Chouteau timer->counter = timer->reload; 1250f3a4a01SFabien Chouteau grlib_gptimer_enable(timer); 1260f3a4a01SFabien Chouteau } 1270f3a4a01SFabien Chouteau 1280f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) 1290f3a4a01SFabien Chouteau { 1300f3a4a01SFabien Chouteau int i = 0; 1310f3a4a01SFabien Chouteau uint32_t value = 0; 1320f3a4a01SFabien Chouteau 1330f3a4a01SFabien Chouteau assert(unit != NULL); 1340f3a4a01SFabien Chouteau 1350f3a4a01SFabien Chouteau if (scaler > 0) { 1360f3a4a01SFabien Chouteau value = unit->freq_hz / (scaler + 1); 1370f3a4a01SFabien Chouteau } else { 1380f3a4a01SFabien Chouteau value = unit->freq_hz; 1390f3a4a01SFabien Chouteau } 1400f3a4a01SFabien Chouteau 1410f3a4a01SFabien Chouteau trace_grlib_gptimer_set_scaler(scaler, value); 1420f3a4a01SFabien Chouteau 1430f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 1440f3a4a01SFabien Chouteau ptimer_set_freq(unit->timers[i].ptimer, value); 1450f3a4a01SFabien Chouteau } 1460f3a4a01SFabien Chouteau } 1470f3a4a01SFabien Chouteau 1480f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque) 1490f3a4a01SFabien Chouteau { 1500f3a4a01SFabien Chouteau GPTimer *timer = opaque; 1510f3a4a01SFabien Chouteau assert(timer != NULL); 1520f3a4a01SFabien Chouteau 1530f3a4a01SFabien Chouteau trace_grlib_gptimer_hit(timer->id); 1540f3a4a01SFabien Chouteau 1550f3a4a01SFabien Chouteau /* Timer expired */ 1560f3a4a01SFabien Chouteau 1570f3a4a01SFabien Chouteau if (timer->config & GPTIMER_INT_ENABLE) { 1580f3a4a01SFabien Chouteau /* Set the pending bit (only unset by write in the config register) */ 1590f3a4a01SFabien Chouteau timer->config |= GPTIMER_INT_PENDING; 1600f3a4a01SFabien Chouteau qemu_irq_pulse(timer->irq); 1610f3a4a01SFabien Chouteau } 1620f3a4a01SFabien Chouteau 1630f3a4a01SFabien Chouteau if (timer->config & GPTIMER_RESTART) { 1640f3a4a01SFabien Chouteau grlib_gptimer_restart(timer); 1650f3a4a01SFabien Chouteau } 1660f3a4a01SFabien Chouteau } 1670f3a4a01SFabien Chouteau 168a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, 169cde844faSAvi Kivity unsigned size) 1700f3a4a01SFabien Chouteau { 1710f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 172a8170e5eSAvi Kivity hwaddr timer_addr; 1730f3a4a01SFabien Chouteau int id; 1740f3a4a01SFabien Chouteau uint32_t value = 0; 1750f3a4a01SFabien Chouteau 1760f3a4a01SFabien Chouteau addr &= 0xff; 1770f3a4a01SFabien Chouteau 1780f3a4a01SFabien Chouteau /* Unit registers */ 1790f3a4a01SFabien Chouteau switch (addr) { 1800f3a4a01SFabien Chouteau case SCALER_OFFSET: 181b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->scaler); 1820f3a4a01SFabien Chouteau return unit->scaler; 1830f3a4a01SFabien Chouteau 1840f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 185b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->reload); 1860f3a4a01SFabien Chouteau return unit->reload; 1870f3a4a01SFabien Chouteau 1880f3a4a01SFabien Chouteau case CONFIG_OFFSET: 189b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->config); 1900f3a4a01SFabien Chouteau return unit->config; 1910f3a4a01SFabien Chouteau 1920f3a4a01SFabien Chouteau default: 1930f3a4a01SFabien Chouteau break; 1940f3a4a01SFabien Chouteau } 1950f3a4a01SFabien Chouteau 1960f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 1970f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 1980f3a4a01SFabien Chouteau 1990f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2000f3a4a01SFabien Chouteau 2010f3a4a01SFabien Chouteau /* GPTimer registers */ 2020f3a4a01SFabien Chouteau switch (timer_addr) { 2030f3a4a01SFabien Chouteau case COUNTER_OFFSET: 2040f3a4a01SFabien Chouteau value = ptimer_get_count(unit->timers[id].ptimer); 205b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2060f3a4a01SFabien Chouteau return value; 2070f3a4a01SFabien Chouteau 2080f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 2090f3a4a01SFabien Chouteau value = unit->timers[id].reload; 210b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2110f3a4a01SFabien Chouteau return value; 2120f3a4a01SFabien Chouteau 2130f3a4a01SFabien Chouteau case CONFIG_OFFSET: 214b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); 2150f3a4a01SFabien Chouteau return unit->timers[id].config; 2160f3a4a01SFabien Chouteau 2170f3a4a01SFabien Chouteau default: 2180f3a4a01SFabien Chouteau break; 2190f3a4a01SFabien Chouteau } 2200f3a4a01SFabien Chouteau 2210f3a4a01SFabien Chouteau } 2220f3a4a01SFabien Chouteau 223b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, 0); 2240f3a4a01SFabien Chouteau return 0; 2250f3a4a01SFabien Chouteau } 2260f3a4a01SFabien Chouteau 227a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr, 228cde844faSAvi Kivity uint64_t value, unsigned size) 2290f3a4a01SFabien Chouteau { 2300f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 231a8170e5eSAvi Kivity hwaddr timer_addr; 2320f3a4a01SFabien Chouteau int id; 2330f3a4a01SFabien Chouteau 2340f3a4a01SFabien Chouteau addr &= 0xff; 2350f3a4a01SFabien Chouteau 2360f3a4a01SFabien Chouteau /* Unit registers */ 2370f3a4a01SFabien Chouteau switch (addr) { 2380f3a4a01SFabien Chouteau case SCALER_OFFSET: 2390f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2400f3a4a01SFabien Chouteau unit->scaler = value; 241b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->scaler); 2420f3a4a01SFabien Chouteau return; 2430f3a4a01SFabien Chouteau 2440f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 2450f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2460f3a4a01SFabien Chouteau unit->reload = value; 247b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->reload); 2480f3a4a01SFabien Chouteau grlib_gptimer_set_scaler(unit, value); 2490f3a4a01SFabien Chouteau return; 2500f3a4a01SFabien Chouteau 2510f3a4a01SFabien Chouteau case CONFIG_OFFSET: 2520f3a4a01SFabien Chouteau /* Read Only (disable timer freeze not supported) */ 253b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, 0); 2540f3a4a01SFabien Chouteau return; 2550f3a4a01SFabien Chouteau 2560f3a4a01SFabien Chouteau default: 2570f3a4a01SFabien Chouteau break; 2580f3a4a01SFabien Chouteau } 2590f3a4a01SFabien Chouteau 2600f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2610f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2620f3a4a01SFabien Chouteau 2630f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2640f3a4a01SFabien Chouteau 2650f3a4a01SFabien Chouteau /* GPTimer registers */ 2660f3a4a01SFabien Chouteau switch (timer_addr) { 2670f3a4a01SFabien Chouteau case COUNTER_OFFSET: 268b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2690f3a4a01SFabien Chouteau unit->timers[id].counter = value; 2700f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2710f3a4a01SFabien Chouteau return; 2720f3a4a01SFabien Chouteau 2730f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 274b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2750f3a4a01SFabien Chouteau unit->timers[id].reload = value; 2760f3a4a01SFabien Chouteau return; 2770f3a4a01SFabien Chouteau 2780f3a4a01SFabien Chouteau case CONFIG_OFFSET: 279b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2800f3a4a01SFabien Chouteau 2810f3a4a01SFabien Chouteau if (value & GPTIMER_INT_PENDING) { 2820f3a4a01SFabien Chouteau /* clear pending bit */ 2830f3a4a01SFabien Chouteau value &= ~GPTIMER_INT_PENDING; 2840f3a4a01SFabien Chouteau } else { 2850f3a4a01SFabien Chouteau /* keep pending bit */ 2860f3a4a01SFabien Chouteau value |= unit->timers[id].config & GPTIMER_INT_PENDING; 2870f3a4a01SFabien Chouteau } 2880f3a4a01SFabien Chouteau 2890f3a4a01SFabien Chouteau unit->timers[id].config = value; 2900f3a4a01SFabien Chouteau 2910f3a4a01SFabien Chouteau /* gptimer_restart calls gptimer_enable, so if "enable" and "load" 2920f3a4a01SFabien Chouteau bits are present, we just have to call restart. */ 2930f3a4a01SFabien Chouteau 2940f3a4a01SFabien Chouteau if (value & GPTIMER_LOAD) { 2950f3a4a01SFabien Chouteau grlib_gptimer_restart(&unit->timers[id]); 2960f3a4a01SFabien Chouteau } else if (value & GPTIMER_ENABLE) { 2970f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2980f3a4a01SFabien Chouteau } 2990f3a4a01SFabien Chouteau 3000f3a4a01SFabien Chouteau /* These fields must always be read as 0 */ 3010f3a4a01SFabien Chouteau value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); 3020f3a4a01SFabien Chouteau 3030f3a4a01SFabien Chouteau unit->timers[id].config = value; 3040f3a4a01SFabien Chouteau return; 3050f3a4a01SFabien Chouteau 3060f3a4a01SFabien Chouteau default: 3070f3a4a01SFabien Chouteau break; 3080f3a4a01SFabien Chouteau } 3090f3a4a01SFabien Chouteau 3100f3a4a01SFabien Chouteau } 3110f3a4a01SFabien Chouteau 312b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, value); 3130f3a4a01SFabien Chouteau } 3140f3a4a01SFabien Chouteau 315cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = { 316cde844faSAvi Kivity .read = grlib_gptimer_read, 317cde844faSAvi Kivity .write = grlib_gptimer_write, 318cde844faSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 319cde844faSAvi Kivity .valid = { 320cde844faSAvi Kivity .min_access_size = 4, 321cde844faSAvi Kivity .max_access_size = 4, 322cde844faSAvi Kivity }, 3230f3a4a01SFabien Chouteau }; 3240f3a4a01SFabien Chouteau 3250f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d) 3260f3a4a01SFabien Chouteau { 327541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(d); 3280f3a4a01SFabien Chouteau int i = 0; 3290f3a4a01SFabien Chouteau 3300f3a4a01SFabien Chouteau assert(unit != NULL); 3310f3a4a01SFabien Chouteau 3320f3a4a01SFabien Chouteau unit->scaler = 0; 3330f3a4a01SFabien Chouteau unit->reload = 0; 3340f3a4a01SFabien Chouteau 3350f3a4a01SFabien Chouteau unit->config = unit->nr_timers; 3360f3a4a01SFabien Chouteau unit->config |= unit->irq_line << 3; 3370f3a4a01SFabien Chouteau unit->config |= 1 << 8; /* separate interrupt */ 3380f3a4a01SFabien Chouteau unit->config |= 1 << 9; /* Disable timer freeze */ 3390f3a4a01SFabien Chouteau 3400f3a4a01SFabien Chouteau 3410f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3420f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3430f3a4a01SFabien Chouteau 3440f3a4a01SFabien Chouteau timer->counter = 0; 3450f3a4a01SFabien Chouteau timer->reload = 0; 3460f3a4a01SFabien Chouteau timer->config = 0; 3470f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 3480f3a4a01SFabien Chouteau ptimer_set_count(timer->ptimer, 0); 3490f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 3500f3a4a01SFabien Chouteau } 3510f3a4a01SFabien Chouteau } 3520f3a4a01SFabien Chouteau 35323251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp) 3540f3a4a01SFabien Chouteau { 355541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(dev); 3560f3a4a01SFabien Chouteau unsigned int i; 35723251fb8SMao Zhongyi SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 3580f3a4a01SFabien Chouteau 3590f3a4a01SFabien Chouteau assert(unit->nr_timers > 0); 3600f3a4a01SFabien Chouteau assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); 3610f3a4a01SFabien Chouteau 3627267c094SAnthony Liguori unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); 3630f3a4a01SFabien Chouteau 3640f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3650f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3660f3a4a01SFabien Chouteau 3670f3a4a01SFabien Chouteau timer->unit = unit; 3680f3a4a01SFabien Chouteau timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); 369e7ea81c3SDmitry Osipenko timer->ptimer = ptimer_init(timer->bh, PTIMER_POLICY_DEFAULT); 3700f3a4a01SFabien Chouteau timer->id = i; 3710f3a4a01SFabien Chouteau 3720f3a4a01SFabien Chouteau /* One IRQ line for each timer */ 37323251fb8SMao Zhongyi sysbus_init_irq(sbd, &timer->irq); 3740f3a4a01SFabien Chouteau 3750f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 3760f3a4a01SFabien Chouteau } 3770f3a4a01SFabien Chouteau 378853dca12SPaolo Bonzini memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, 379853dca12SPaolo Bonzini unit, "gptimer", 380cde844faSAvi Kivity UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); 3810f3a4a01SFabien Chouteau 38223251fb8SMao Zhongyi sysbus_init_mmio(sbd, &unit->iomem); 3830f3a4a01SFabien Chouteau } 3840f3a4a01SFabien Chouteau 385999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = { 3860f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), 3870f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), 3880f3a4a01SFabien Chouteau DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), 389999e12bbSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 390999e12bbSAnthony Liguori }; 391999e12bbSAnthony Liguori 392999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data) 393999e12bbSAnthony Liguori { 39439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 395999e12bbSAnthony Liguori 39623251fb8SMao Zhongyi dc->realize = grlib_gptimer_realize; 39739bffca2SAnthony Liguori dc->reset = grlib_gptimer_reset; 39839bffca2SAnthony Liguori dc->props = grlib_gptimer_properties; 3990f3a4a01SFabien Chouteau } 400999e12bbSAnthony Liguori 4018c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = { 402541ab55fSAndreas Färber .name = TYPE_GRLIB_GPTIMER, 40339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 40439bffca2SAnthony Liguori .instance_size = sizeof(GPTimerUnit), 405999e12bbSAnthony Liguori .class_init = grlib_gptimer_class_init, 4060f3a4a01SFabien Chouteau }; 4070f3a4a01SFabien Chouteau 40883f7d43aSAndreas Färber static void grlib_gptimer_register_types(void) 4090f3a4a01SFabien Chouteau { 41039bffca2SAnthony Liguori type_register_static(&grlib_gptimer_info); 4110f3a4a01SFabien Chouteau } 4120f3a4a01SFabien Chouteau 41383f7d43aSAndreas Färber type_init(grlib_gptimer_register_types) 414