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