10f3a4a01SFabien Chouteau /* 20f3a4a01SFabien Chouteau * QEMU GRLIB GPTimer Emulator 30f3a4a01SFabien Chouteau * 40f3a4a01SFabien Chouteau * Copyright (c) 2010-2011 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 2583c9f4caSPaolo Bonzini #include "hw/sysbus.h" 261de7afc9SPaolo Bonzini #include "qemu/timer.h" 2783c9f4caSPaolo Bonzini #include "hw/ptimer.h" 28*6a1751b7SAlex Bligh #include "qemu/timer.h" 29*6a1751b7SAlex Bligh #include "qemu/main-loop.h" 300f3a4a01SFabien Chouteau 310f3a4a01SFabien Chouteau #include "trace.h" 320f3a4a01SFabien Chouteau 330f3a4a01SFabien Chouteau #define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ 340f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ 350f3a4a01SFabien Chouteau 360f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8 370f3a4a01SFabien Chouteau 380f3a4a01SFabien Chouteau /* GPTimer Config register fields */ 390f3a4a01SFabien Chouteau #define GPTIMER_ENABLE (1 << 0) 400f3a4a01SFabien Chouteau #define GPTIMER_RESTART (1 << 1) 410f3a4a01SFabien Chouteau #define GPTIMER_LOAD (1 << 2) 420f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE (1 << 3) 430f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4) 440f3a4a01SFabien Chouteau #define GPTIMER_CHAIN (1 << 5) /* Not supported */ 450f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ 460f3a4a01SFabien Chouteau 470f3a4a01SFabien Chouteau /* Memory mapped register offsets */ 480f3a4a01SFabien Chouteau #define SCALER_OFFSET 0x00 490f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET 0x04 500f3a4a01SFabien Chouteau #define CONFIG_OFFSET 0x08 510f3a4a01SFabien Chouteau #define COUNTER_OFFSET 0x00 520f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04 530f3a4a01SFabien Chouteau #define TIMER_BASE 0x10 540f3a4a01SFabien Chouteau 55541ab55fSAndreas Färber #define TYPE_GRLIB_GPTIMER "grlib,gptimer" 56541ab55fSAndreas Färber #define GRLIB_GPTIMER(obj) \ 57541ab55fSAndreas Färber OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER) 58541ab55fSAndreas Färber 590f3a4a01SFabien Chouteau typedef struct GPTimer GPTimer; 600f3a4a01SFabien Chouteau typedef struct GPTimerUnit GPTimerUnit; 610f3a4a01SFabien Chouteau 620f3a4a01SFabien Chouteau struct GPTimer { 630f3a4a01SFabien Chouteau QEMUBH *bh; 640f3a4a01SFabien Chouteau struct ptimer_state *ptimer; 650f3a4a01SFabien Chouteau 660f3a4a01SFabien Chouteau qemu_irq irq; 670f3a4a01SFabien Chouteau int id; 680f3a4a01SFabien Chouteau GPTimerUnit *unit; 690f3a4a01SFabien Chouteau 700f3a4a01SFabien Chouteau /* registers */ 710f3a4a01SFabien Chouteau uint32_t counter; 720f3a4a01SFabien Chouteau uint32_t reload; 730f3a4a01SFabien Chouteau uint32_t config; 740f3a4a01SFabien Chouteau }; 750f3a4a01SFabien Chouteau 760f3a4a01SFabien Chouteau struct GPTimerUnit { 77541ab55fSAndreas Färber SysBusDevice parent_obj; 78541ab55fSAndreas Färber 79cde844faSAvi Kivity MemoryRegion iomem; 800f3a4a01SFabien Chouteau 810f3a4a01SFabien Chouteau uint32_t nr_timers; /* Number of timers available */ 820f3a4a01SFabien Chouteau uint32_t freq_hz; /* System frequency */ 830f3a4a01SFabien Chouteau uint32_t irq_line; /* Base irq line */ 840f3a4a01SFabien Chouteau 850f3a4a01SFabien Chouteau GPTimer *timers; 860f3a4a01SFabien Chouteau 870f3a4a01SFabien Chouteau /* registers */ 880f3a4a01SFabien Chouteau uint32_t scaler; 890f3a4a01SFabien Chouteau uint32_t reload; 900f3a4a01SFabien Chouteau uint32_t config; 910f3a4a01SFabien Chouteau }; 920f3a4a01SFabien Chouteau 930f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer) 940f3a4a01SFabien Chouteau { 950f3a4a01SFabien Chouteau assert(timer != NULL); 960f3a4a01SFabien Chouteau 970f3a4a01SFabien Chouteau 980f3a4a01SFabien Chouteau ptimer_stop(timer->ptimer); 990f3a4a01SFabien Chouteau 1000f3a4a01SFabien Chouteau if (!(timer->config & GPTIMER_ENABLE)) { 1010f3a4a01SFabien Chouteau /* Timer disabled */ 1020f3a4a01SFabien Chouteau trace_grlib_gptimer_disabled(timer->id, timer->config); 1030f3a4a01SFabien Chouteau return; 1040f3a4a01SFabien Chouteau } 1050f3a4a01SFabien Chouteau 1060f3a4a01SFabien Chouteau /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at 1070f3a4a01SFabien Chouteau underflow. Set count + 1 to simulate the GPTimer behavior. */ 1080f3a4a01SFabien Chouteau 1090f3a4a01SFabien Chouteau trace_grlib_gptimer_enable(timer->id, timer->counter + 1); 1100f3a4a01SFabien Chouteau 1110f3a4a01SFabien Chouteau ptimer_set_count(timer->ptimer, timer->counter + 1); 1120f3a4a01SFabien Chouteau ptimer_run(timer->ptimer, 1); 1130f3a4a01SFabien Chouteau } 1140f3a4a01SFabien Chouteau 1150f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer) 1160f3a4a01SFabien Chouteau { 1170f3a4a01SFabien Chouteau assert(timer != NULL); 1180f3a4a01SFabien Chouteau 1190f3a4a01SFabien Chouteau trace_grlib_gptimer_restart(timer->id, timer->reload); 1200f3a4a01SFabien Chouteau 1210f3a4a01SFabien Chouteau timer->counter = timer->reload; 1220f3a4a01SFabien Chouteau grlib_gptimer_enable(timer); 1230f3a4a01SFabien Chouteau } 1240f3a4a01SFabien Chouteau 1250f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) 1260f3a4a01SFabien Chouteau { 1270f3a4a01SFabien Chouteau int i = 0; 1280f3a4a01SFabien Chouteau uint32_t value = 0; 1290f3a4a01SFabien Chouteau 1300f3a4a01SFabien Chouteau assert(unit != NULL); 1310f3a4a01SFabien Chouteau 1320f3a4a01SFabien Chouteau if (scaler > 0) { 1330f3a4a01SFabien Chouteau value = unit->freq_hz / (scaler + 1); 1340f3a4a01SFabien Chouteau } else { 1350f3a4a01SFabien Chouteau value = unit->freq_hz; 1360f3a4a01SFabien Chouteau } 1370f3a4a01SFabien Chouteau 1380f3a4a01SFabien Chouteau trace_grlib_gptimer_set_scaler(scaler, value); 1390f3a4a01SFabien Chouteau 1400f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 1410f3a4a01SFabien Chouteau ptimer_set_freq(unit->timers[i].ptimer, value); 1420f3a4a01SFabien Chouteau } 1430f3a4a01SFabien Chouteau } 1440f3a4a01SFabien Chouteau 1450f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque) 1460f3a4a01SFabien Chouteau { 1470f3a4a01SFabien Chouteau GPTimer *timer = opaque; 1480f3a4a01SFabien Chouteau assert(timer != NULL); 1490f3a4a01SFabien Chouteau 1500f3a4a01SFabien Chouteau trace_grlib_gptimer_hit(timer->id); 1510f3a4a01SFabien Chouteau 1520f3a4a01SFabien Chouteau /* Timer expired */ 1530f3a4a01SFabien Chouteau 1540f3a4a01SFabien Chouteau if (timer->config & GPTIMER_INT_ENABLE) { 1550f3a4a01SFabien Chouteau /* Set the pending bit (only unset by write in the config register) */ 1560f3a4a01SFabien Chouteau timer->config |= GPTIMER_INT_PENDING; 1570f3a4a01SFabien Chouteau qemu_irq_pulse(timer->irq); 1580f3a4a01SFabien Chouteau } 1590f3a4a01SFabien Chouteau 1600f3a4a01SFabien Chouteau if (timer->config & GPTIMER_RESTART) { 1610f3a4a01SFabien Chouteau grlib_gptimer_restart(timer); 1620f3a4a01SFabien Chouteau } 1630f3a4a01SFabien Chouteau } 1640f3a4a01SFabien Chouteau 165a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, 166cde844faSAvi Kivity unsigned size) 1670f3a4a01SFabien Chouteau { 1680f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 169a8170e5eSAvi Kivity hwaddr timer_addr; 1700f3a4a01SFabien Chouteau int id; 1710f3a4a01SFabien Chouteau uint32_t value = 0; 1720f3a4a01SFabien Chouteau 1730f3a4a01SFabien Chouteau addr &= 0xff; 1740f3a4a01SFabien Chouteau 1750f3a4a01SFabien Chouteau /* Unit registers */ 1760f3a4a01SFabien Chouteau switch (addr) { 1770f3a4a01SFabien Chouteau case SCALER_OFFSET: 178b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->scaler); 1790f3a4a01SFabien Chouteau return unit->scaler; 1800f3a4a01SFabien Chouteau 1810f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 182b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->reload); 1830f3a4a01SFabien Chouteau return unit->reload; 1840f3a4a01SFabien Chouteau 1850f3a4a01SFabien Chouteau case CONFIG_OFFSET: 186b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, unit->config); 1870f3a4a01SFabien Chouteau return unit->config; 1880f3a4a01SFabien Chouteau 1890f3a4a01SFabien Chouteau default: 1900f3a4a01SFabien Chouteau break; 1910f3a4a01SFabien Chouteau } 1920f3a4a01SFabien Chouteau 1930f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 1940f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 1950f3a4a01SFabien Chouteau 1960f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 1970f3a4a01SFabien Chouteau 1980f3a4a01SFabien Chouteau /* GPTimer registers */ 1990f3a4a01SFabien Chouteau switch (timer_addr) { 2000f3a4a01SFabien Chouteau case COUNTER_OFFSET: 2010f3a4a01SFabien Chouteau value = ptimer_get_count(unit->timers[id].ptimer); 202b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2030f3a4a01SFabien Chouteau return value; 2040f3a4a01SFabien Chouteau 2050f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 2060f3a4a01SFabien Chouteau value = unit->timers[id].reload; 207b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, value); 2080f3a4a01SFabien Chouteau return value; 2090f3a4a01SFabien Chouteau 2100f3a4a01SFabien Chouteau case CONFIG_OFFSET: 211b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); 2120f3a4a01SFabien Chouteau return unit->timers[id].config; 2130f3a4a01SFabien Chouteau 2140f3a4a01SFabien Chouteau default: 2150f3a4a01SFabien Chouteau break; 2160f3a4a01SFabien Chouteau } 2170f3a4a01SFabien Chouteau 2180f3a4a01SFabien Chouteau } 2190f3a4a01SFabien Chouteau 220b4548fccSStefan Hajnoczi trace_grlib_gptimer_readl(-1, addr, 0); 2210f3a4a01SFabien Chouteau return 0; 2220f3a4a01SFabien Chouteau } 2230f3a4a01SFabien Chouteau 224a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr, 225cde844faSAvi Kivity uint64_t value, unsigned size) 2260f3a4a01SFabien Chouteau { 2270f3a4a01SFabien Chouteau GPTimerUnit *unit = opaque; 228a8170e5eSAvi Kivity hwaddr timer_addr; 2290f3a4a01SFabien Chouteau int id; 2300f3a4a01SFabien Chouteau 2310f3a4a01SFabien Chouteau addr &= 0xff; 2320f3a4a01SFabien Chouteau 2330f3a4a01SFabien Chouteau /* Unit registers */ 2340f3a4a01SFabien Chouteau switch (addr) { 2350f3a4a01SFabien Chouteau case SCALER_OFFSET: 2360f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2370f3a4a01SFabien Chouteau unit->scaler = value; 238b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->scaler); 2390f3a4a01SFabien Chouteau return; 2400f3a4a01SFabien Chouteau 2410f3a4a01SFabien Chouteau case SCALER_RELOAD_OFFSET: 2420f3a4a01SFabien Chouteau value &= 0xFFFF; /* clean up the value */ 2430f3a4a01SFabien Chouteau unit->reload = value; 244b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, unit->reload); 2450f3a4a01SFabien Chouteau grlib_gptimer_set_scaler(unit, value); 2460f3a4a01SFabien Chouteau return; 2470f3a4a01SFabien Chouteau 2480f3a4a01SFabien Chouteau case CONFIG_OFFSET: 2490f3a4a01SFabien Chouteau /* Read Only (disable timer freeze not supported) */ 250b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, 0); 2510f3a4a01SFabien Chouteau return; 2520f3a4a01SFabien Chouteau 2530f3a4a01SFabien Chouteau default: 2540f3a4a01SFabien Chouteau break; 2550f3a4a01SFabien Chouteau } 2560f3a4a01SFabien Chouteau 2570f3a4a01SFabien Chouteau timer_addr = (addr % TIMER_BASE); 2580f3a4a01SFabien Chouteau id = (addr - TIMER_BASE) / TIMER_BASE; 2590f3a4a01SFabien Chouteau 2600f3a4a01SFabien Chouteau if (id >= 0 && id < unit->nr_timers) { 2610f3a4a01SFabien Chouteau 2620f3a4a01SFabien Chouteau /* GPTimer registers */ 2630f3a4a01SFabien Chouteau switch (timer_addr) { 2640f3a4a01SFabien Chouteau case COUNTER_OFFSET: 265b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2660f3a4a01SFabien Chouteau unit->timers[id].counter = value; 2670f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2680f3a4a01SFabien Chouteau return; 2690f3a4a01SFabien Chouteau 2700f3a4a01SFabien Chouteau case COUNTER_RELOAD_OFFSET: 271b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2720f3a4a01SFabien Chouteau unit->timers[id].reload = value; 2730f3a4a01SFabien Chouteau return; 2740f3a4a01SFabien Chouteau 2750f3a4a01SFabien Chouteau case CONFIG_OFFSET: 276b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(id, addr, value); 2770f3a4a01SFabien Chouteau 2780f3a4a01SFabien Chouteau if (value & GPTIMER_INT_PENDING) { 2790f3a4a01SFabien Chouteau /* clear pending bit */ 2800f3a4a01SFabien Chouteau value &= ~GPTIMER_INT_PENDING; 2810f3a4a01SFabien Chouteau } else { 2820f3a4a01SFabien Chouteau /* keep pending bit */ 2830f3a4a01SFabien Chouteau value |= unit->timers[id].config & GPTIMER_INT_PENDING; 2840f3a4a01SFabien Chouteau } 2850f3a4a01SFabien Chouteau 2860f3a4a01SFabien Chouteau unit->timers[id].config = value; 2870f3a4a01SFabien Chouteau 2880f3a4a01SFabien Chouteau /* gptimer_restart calls gptimer_enable, so if "enable" and "load" 2890f3a4a01SFabien Chouteau bits are present, we just have to call restart. */ 2900f3a4a01SFabien Chouteau 2910f3a4a01SFabien Chouteau if (value & GPTIMER_LOAD) { 2920f3a4a01SFabien Chouteau grlib_gptimer_restart(&unit->timers[id]); 2930f3a4a01SFabien Chouteau } else if (value & GPTIMER_ENABLE) { 2940f3a4a01SFabien Chouteau grlib_gptimer_enable(&unit->timers[id]); 2950f3a4a01SFabien Chouteau } 2960f3a4a01SFabien Chouteau 2970f3a4a01SFabien Chouteau /* These fields must always be read as 0 */ 2980f3a4a01SFabien Chouteau value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); 2990f3a4a01SFabien Chouteau 3000f3a4a01SFabien Chouteau unit->timers[id].config = value; 3010f3a4a01SFabien Chouteau return; 3020f3a4a01SFabien Chouteau 3030f3a4a01SFabien Chouteau default: 3040f3a4a01SFabien Chouteau break; 3050f3a4a01SFabien Chouteau } 3060f3a4a01SFabien Chouteau 3070f3a4a01SFabien Chouteau } 3080f3a4a01SFabien Chouteau 309b4548fccSStefan Hajnoczi trace_grlib_gptimer_writel(-1, addr, value); 3100f3a4a01SFabien Chouteau } 3110f3a4a01SFabien Chouteau 312cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = { 313cde844faSAvi Kivity .read = grlib_gptimer_read, 314cde844faSAvi Kivity .write = grlib_gptimer_write, 315cde844faSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 316cde844faSAvi Kivity .valid = { 317cde844faSAvi Kivity .min_access_size = 4, 318cde844faSAvi Kivity .max_access_size = 4, 319cde844faSAvi Kivity }, 3200f3a4a01SFabien Chouteau }; 3210f3a4a01SFabien Chouteau 3220f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d) 3230f3a4a01SFabien Chouteau { 324541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(d); 3250f3a4a01SFabien Chouteau int i = 0; 3260f3a4a01SFabien Chouteau 3270f3a4a01SFabien Chouteau assert(unit != NULL); 3280f3a4a01SFabien Chouteau 3290f3a4a01SFabien Chouteau unit->scaler = 0; 3300f3a4a01SFabien Chouteau unit->reload = 0; 3310f3a4a01SFabien Chouteau unit->config = 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 3510f3a4a01SFabien Chouteau static int grlib_gptimer_init(SysBusDevice *dev) 3520f3a4a01SFabien Chouteau { 353541ab55fSAndreas Färber GPTimerUnit *unit = GRLIB_GPTIMER(dev); 3540f3a4a01SFabien Chouteau unsigned int i; 3550f3a4a01SFabien Chouteau 3560f3a4a01SFabien Chouteau assert(unit->nr_timers > 0); 3570f3a4a01SFabien Chouteau assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); 3580f3a4a01SFabien Chouteau 3597267c094SAnthony Liguori unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); 3600f3a4a01SFabien Chouteau 3610f3a4a01SFabien Chouteau for (i = 0; i < unit->nr_timers; i++) { 3620f3a4a01SFabien Chouteau GPTimer *timer = &unit->timers[i]; 3630f3a4a01SFabien Chouteau 3640f3a4a01SFabien Chouteau timer->unit = unit; 3650f3a4a01SFabien Chouteau timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); 3660f3a4a01SFabien Chouteau timer->ptimer = ptimer_init(timer->bh); 3670f3a4a01SFabien Chouteau timer->id = i; 3680f3a4a01SFabien Chouteau 3690f3a4a01SFabien Chouteau /* One IRQ line for each timer */ 3700f3a4a01SFabien Chouteau sysbus_init_irq(dev, &timer->irq); 3710f3a4a01SFabien Chouteau 3720f3a4a01SFabien Chouteau ptimer_set_freq(timer->ptimer, unit->freq_hz); 3730f3a4a01SFabien Chouteau } 3740f3a4a01SFabien Chouteau 375853dca12SPaolo Bonzini memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, 376853dca12SPaolo Bonzini unit, "gptimer", 377cde844faSAvi Kivity UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); 3780f3a4a01SFabien Chouteau 379750ecd44SAvi Kivity sysbus_init_mmio(dev, &unit->iomem); 3800f3a4a01SFabien Chouteau return 0; 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 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 394999e12bbSAnthony Liguori 395999e12bbSAnthony Liguori k->init = grlib_gptimer_init; 39639bffca2SAnthony Liguori dc->reset = grlib_gptimer_reset; 39739bffca2SAnthony Liguori dc->props = grlib_gptimer_properties; 3980f3a4a01SFabien Chouteau } 399999e12bbSAnthony Liguori 4008c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = { 401541ab55fSAndreas Färber .name = TYPE_GRLIB_GPTIMER, 40239bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 40339bffca2SAnthony Liguori .instance_size = sizeof(GPTimerUnit), 404999e12bbSAnthony Liguori .class_init = grlib_gptimer_class_init, 4050f3a4a01SFabien Chouteau }; 4060f3a4a01SFabien Chouteau 40783f7d43aSAndreas Färber static void grlib_gptimer_register_types(void) 4080f3a4a01SFabien Chouteau { 40939bffca2SAnthony Liguori type_register_static(&grlib_gptimer_info); 4100f3a4a01SFabien Chouteau } 4110f3a4a01SFabien Chouteau 41283f7d43aSAndreas Färber type_init(grlib_gptimer_register_types) 413