xref: /qemu/hw/timer/grlib_gptimer.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
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