xref: /qemu/hw/timer/grlib_gptimer.c (revision 663e475fbeecf3de9b4d1a84454bccfb67e87bc2)
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"
31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
320b8fa32fSMarkus Armbruster #include "qemu/module.h"
330f3a4a01SFabien Chouteau 
340f3a4a01SFabien Chouteau #include "trace.h"
350f3a4a01SFabien Chouteau 
360f3a4a01SFabien Chouteau #define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
370f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
380f3a4a01SFabien Chouteau 
390f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8
400f3a4a01SFabien Chouteau 
410f3a4a01SFabien Chouteau /* GPTimer Config register fields */
420f3a4a01SFabien Chouteau #define GPTIMER_ENABLE      (1 << 0)
430f3a4a01SFabien Chouteau #define GPTIMER_RESTART     (1 << 1)
440f3a4a01SFabien Chouteau #define GPTIMER_LOAD        (1 << 2)
450f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE  (1 << 3)
460f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4)
470f3a4a01SFabien Chouteau #define GPTIMER_CHAIN       (1 << 5) /* Not supported */
480f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
490f3a4a01SFabien Chouteau 
500f3a4a01SFabien Chouteau /* Memory mapped register offsets */
510f3a4a01SFabien Chouteau #define SCALER_OFFSET         0x00
520f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET  0x04
530f3a4a01SFabien Chouteau #define CONFIG_OFFSET         0x08
540f3a4a01SFabien Chouteau #define COUNTER_OFFSET        0x00
550f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04
560f3a4a01SFabien Chouteau #define TIMER_BASE            0x10
570f3a4a01SFabien Chouteau 
58541ab55fSAndreas Färber #define GRLIB_GPTIMER(obj) \
59541ab55fSAndreas Färber     OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER)
60541ab55fSAndreas Färber 
610f3a4a01SFabien Chouteau typedef struct GPTimer     GPTimer;
620f3a4a01SFabien Chouteau typedef struct GPTimerUnit GPTimerUnit;
630f3a4a01SFabien Chouteau 
640f3a4a01SFabien Chouteau struct GPTimer {
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 
94*663e475fSPeter Maydell static void grlib_gptimer_tx_begin(GPTimer *timer)
95*663e475fSPeter Maydell {
96*663e475fSPeter Maydell     ptimer_transaction_begin(timer->ptimer);
97*663e475fSPeter Maydell }
98*663e475fSPeter Maydell 
99*663e475fSPeter Maydell static void grlib_gptimer_tx_commit(GPTimer *timer)
100*663e475fSPeter Maydell {
101*663e475fSPeter Maydell     ptimer_transaction_commit(timer->ptimer);
102*663e475fSPeter Maydell }
103*663e475fSPeter Maydell 
104*663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
1050f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer)
1060f3a4a01SFabien Chouteau {
1070f3a4a01SFabien Chouteau     assert(timer != NULL);
1080f3a4a01SFabien Chouteau 
1090f3a4a01SFabien Chouteau 
1100f3a4a01SFabien Chouteau     ptimer_stop(timer->ptimer);
1110f3a4a01SFabien Chouteau 
1120f3a4a01SFabien Chouteau     if (!(timer->config & GPTIMER_ENABLE)) {
1130f3a4a01SFabien Chouteau         /* Timer disabled */
1140f3a4a01SFabien Chouteau         trace_grlib_gptimer_disabled(timer->id, timer->config);
1150f3a4a01SFabien Chouteau         return;
1160f3a4a01SFabien Chouteau     }
1170f3a4a01SFabien Chouteau 
1180f3a4a01SFabien Chouteau     /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
1190f3a4a01SFabien Chouteau        underflow. Set count + 1 to simulate the GPTimer behavior. */
1200f3a4a01SFabien Chouteau 
1219d5614d5SSebastian Huber     trace_grlib_gptimer_enable(timer->id, timer->counter);
1220f3a4a01SFabien Chouteau 
1239d5614d5SSebastian Huber     ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1);
1240f3a4a01SFabien Chouteau     ptimer_run(timer->ptimer, 1);
1250f3a4a01SFabien Chouteau }
1260f3a4a01SFabien Chouteau 
127*663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
1280f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer)
1290f3a4a01SFabien Chouteau {
1300f3a4a01SFabien Chouteau     assert(timer != NULL);
1310f3a4a01SFabien Chouteau 
1320f3a4a01SFabien Chouteau     trace_grlib_gptimer_restart(timer->id, timer->reload);
1330f3a4a01SFabien Chouteau 
1340f3a4a01SFabien Chouteau     timer->counter = timer->reload;
1350f3a4a01SFabien Chouteau     grlib_gptimer_enable(timer);
1360f3a4a01SFabien Chouteau }
1370f3a4a01SFabien Chouteau 
1380f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
1390f3a4a01SFabien Chouteau {
1400f3a4a01SFabien Chouteau     int i = 0;
1410f3a4a01SFabien Chouteau     uint32_t value = 0;
1420f3a4a01SFabien Chouteau 
1430f3a4a01SFabien Chouteau     assert(unit != NULL);
1440f3a4a01SFabien Chouteau 
1450f3a4a01SFabien Chouteau     if (scaler > 0) {
1460f3a4a01SFabien Chouteau         value = unit->freq_hz / (scaler + 1);
1470f3a4a01SFabien Chouteau     } else {
1480f3a4a01SFabien Chouteau         value = unit->freq_hz;
1490f3a4a01SFabien Chouteau     }
1500f3a4a01SFabien Chouteau 
1510f3a4a01SFabien Chouteau     trace_grlib_gptimer_set_scaler(scaler, value);
1520f3a4a01SFabien Chouteau 
1530f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
154*663e475fSPeter Maydell         ptimer_transaction_begin(unit->timers[i].ptimer);
1550f3a4a01SFabien Chouteau         ptimer_set_freq(unit->timers[i].ptimer, value);
156*663e475fSPeter Maydell         ptimer_transaction_commit(unit->timers[i].ptimer);
1570f3a4a01SFabien Chouteau     }
1580f3a4a01SFabien Chouteau }
1590f3a4a01SFabien Chouteau 
1600f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque)
1610f3a4a01SFabien Chouteau {
1620f3a4a01SFabien Chouteau     GPTimer *timer = opaque;
1630f3a4a01SFabien Chouteau     assert(timer != NULL);
1640f3a4a01SFabien Chouteau 
1650f3a4a01SFabien Chouteau     trace_grlib_gptimer_hit(timer->id);
1660f3a4a01SFabien Chouteau 
1670f3a4a01SFabien Chouteau     /* Timer expired */
1680f3a4a01SFabien Chouteau 
1690f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_INT_ENABLE) {
1700f3a4a01SFabien Chouteau         /* Set the pending bit (only unset by write in the config register) */
1710f3a4a01SFabien Chouteau         timer->config |= GPTIMER_INT_PENDING;
1720f3a4a01SFabien Chouteau         qemu_irq_pulse(timer->irq);
1730f3a4a01SFabien Chouteau     }
1740f3a4a01SFabien Chouteau 
1750f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_RESTART) {
1760f3a4a01SFabien Chouteau         grlib_gptimer_restart(timer);
1770f3a4a01SFabien Chouteau     }
1780f3a4a01SFabien Chouteau }
1790f3a4a01SFabien Chouteau 
180a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr,
181cde844faSAvi Kivity                                    unsigned size)
1820f3a4a01SFabien Chouteau {
1830f3a4a01SFabien Chouteau     GPTimerUnit        *unit  = opaque;
184a8170e5eSAvi Kivity     hwaddr  timer_addr;
1850f3a4a01SFabien Chouteau     int                 id;
1860f3a4a01SFabien Chouteau     uint32_t            value = 0;
1870f3a4a01SFabien Chouteau 
1880f3a4a01SFabien Chouteau     addr &= 0xff;
1890f3a4a01SFabien Chouteau 
1900f3a4a01SFabien Chouteau     /* Unit registers */
1910f3a4a01SFabien Chouteau     switch (addr) {
1920f3a4a01SFabien Chouteau     case SCALER_OFFSET:
193b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->scaler);
1940f3a4a01SFabien Chouteau         return unit->scaler;
1950f3a4a01SFabien Chouteau 
1960f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
197b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->reload);
1980f3a4a01SFabien Chouteau         return unit->reload;
1990f3a4a01SFabien Chouteau 
2000f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
201b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->config);
2020f3a4a01SFabien Chouteau         return unit->config;
2030f3a4a01SFabien Chouteau 
2040f3a4a01SFabien Chouteau     default:
2050f3a4a01SFabien Chouteau         break;
2060f3a4a01SFabien Chouteau     }
2070f3a4a01SFabien Chouteau 
2080f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2090f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2100f3a4a01SFabien Chouteau 
2110f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2120f3a4a01SFabien Chouteau 
2130f3a4a01SFabien Chouteau         /* GPTimer registers */
2140f3a4a01SFabien Chouteau         switch (timer_addr) {
2150f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
2160f3a4a01SFabien Chouteau             value = ptimer_get_count(unit->timers[id].ptimer);
217b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2180f3a4a01SFabien Chouteau             return value;
2190f3a4a01SFabien Chouteau 
2200f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
2210f3a4a01SFabien Chouteau             value = unit->timers[id].reload;
222b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2230f3a4a01SFabien Chouteau             return value;
2240f3a4a01SFabien Chouteau 
2250f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
226b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
2270f3a4a01SFabien Chouteau             return unit->timers[id].config;
2280f3a4a01SFabien Chouteau 
2290f3a4a01SFabien Chouteau         default:
2300f3a4a01SFabien Chouteau             break;
2310f3a4a01SFabien Chouteau         }
2320f3a4a01SFabien Chouteau 
2330f3a4a01SFabien Chouteau     }
2340f3a4a01SFabien Chouteau 
235b4548fccSStefan Hajnoczi     trace_grlib_gptimer_readl(-1, addr, 0);
2360f3a4a01SFabien Chouteau     return 0;
2370f3a4a01SFabien Chouteau }
2380f3a4a01SFabien Chouteau 
239a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr,
240cde844faSAvi Kivity                                 uint64_t value, unsigned size)
2410f3a4a01SFabien Chouteau {
2420f3a4a01SFabien Chouteau     GPTimerUnit        *unit = opaque;
243a8170e5eSAvi Kivity     hwaddr  timer_addr;
2440f3a4a01SFabien Chouteau     int                 id;
2450f3a4a01SFabien Chouteau 
2460f3a4a01SFabien Chouteau     addr &= 0xff;
2470f3a4a01SFabien Chouteau 
2480f3a4a01SFabien Chouteau     /* Unit registers */
2490f3a4a01SFabien Chouteau     switch (addr) {
2500f3a4a01SFabien Chouteau     case SCALER_OFFSET:
2510f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2520f3a4a01SFabien Chouteau         unit->scaler = value;
253b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->scaler);
2540f3a4a01SFabien Chouteau         return;
2550f3a4a01SFabien Chouteau 
2560f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
2570f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2580f3a4a01SFabien Chouteau         unit->reload = value;
259b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->reload);
2600f3a4a01SFabien Chouteau         grlib_gptimer_set_scaler(unit, value);
2610f3a4a01SFabien Chouteau         return;
2620f3a4a01SFabien Chouteau 
2630f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
2640f3a4a01SFabien Chouteau         /* Read Only (disable timer freeze not supported) */
265b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, 0);
2660f3a4a01SFabien Chouteau         return;
2670f3a4a01SFabien Chouteau 
2680f3a4a01SFabien Chouteau     default:
2690f3a4a01SFabien Chouteau         break;
2700f3a4a01SFabien Chouteau     }
2710f3a4a01SFabien Chouteau 
2720f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2730f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2740f3a4a01SFabien Chouteau 
2750f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2760f3a4a01SFabien Chouteau 
2770f3a4a01SFabien Chouteau         /* GPTimer registers */
2780f3a4a01SFabien Chouteau         switch (timer_addr) {
2790f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
280b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
281*663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
2820f3a4a01SFabien Chouteau             unit->timers[id].counter = value;
2830f3a4a01SFabien Chouteau             grlib_gptimer_enable(&unit->timers[id]);
284*663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
2850f3a4a01SFabien Chouteau             return;
2860f3a4a01SFabien Chouteau 
2870f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
288b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2890f3a4a01SFabien Chouteau             unit->timers[id].reload = value;
2900f3a4a01SFabien Chouteau             return;
2910f3a4a01SFabien Chouteau 
2920f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
293b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2940f3a4a01SFabien Chouteau 
2950f3a4a01SFabien Chouteau             if (value & GPTIMER_INT_PENDING) {
2960f3a4a01SFabien Chouteau                 /* clear pending bit */
2970f3a4a01SFabien Chouteau                 value &= ~GPTIMER_INT_PENDING;
2980f3a4a01SFabien Chouteau             } else {
2990f3a4a01SFabien Chouteau                 /* keep pending bit */
3000f3a4a01SFabien Chouteau                 value |= unit->timers[id].config & GPTIMER_INT_PENDING;
3010f3a4a01SFabien Chouteau             }
3020f3a4a01SFabien Chouteau 
3030f3a4a01SFabien Chouteau             unit->timers[id].config = value;
3040f3a4a01SFabien Chouteau 
3050f3a4a01SFabien Chouteau             /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
3060f3a4a01SFabien Chouteau                bits are present, we just have to call restart. */
3070f3a4a01SFabien Chouteau 
308*663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
3090f3a4a01SFabien Chouteau             if (value & GPTIMER_LOAD) {
3100f3a4a01SFabien Chouteau                 grlib_gptimer_restart(&unit->timers[id]);
3110f3a4a01SFabien Chouteau             } else if (value & GPTIMER_ENABLE) {
3120f3a4a01SFabien Chouteau                 grlib_gptimer_enable(&unit->timers[id]);
3130f3a4a01SFabien Chouteau             }
3140f3a4a01SFabien Chouteau 
3150f3a4a01SFabien Chouteau             /* These fields must always be read as 0 */
3160f3a4a01SFabien Chouteau             value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
3170f3a4a01SFabien Chouteau 
3180f3a4a01SFabien Chouteau             unit->timers[id].config = value;
319*663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
3200f3a4a01SFabien Chouteau             return;
3210f3a4a01SFabien Chouteau 
3220f3a4a01SFabien Chouteau         default:
3230f3a4a01SFabien Chouteau             break;
3240f3a4a01SFabien Chouteau         }
3250f3a4a01SFabien Chouteau 
3260f3a4a01SFabien Chouteau     }
3270f3a4a01SFabien Chouteau 
328b4548fccSStefan Hajnoczi     trace_grlib_gptimer_writel(-1, addr, value);
3290f3a4a01SFabien Chouteau }
3300f3a4a01SFabien Chouteau 
331cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = {
332cde844faSAvi Kivity     .read = grlib_gptimer_read,
333cde844faSAvi Kivity     .write = grlib_gptimer_write,
334cde844faSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
335cde844faSAvi Kivity     .valid = {
336cde844faSAvi Kivity         .min_access_size = 4,
337cde844faSAvi Kivity         .max_access_size = 4,
338cde844faSAvi Kivity     },
3390f3a4a01SFabien Chouteau };
3400f3a4a01SFabien Chouteau 
3410f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d)
3420f3a4a01SFabien Chouteau {
343541ab55fSAndreas Färber     GPTimerUnit *unit = GRLIB_GPTIMER(d);
3440f3a4a01SFabien Chouteau     int          i    = 0;
3450f3a4a01SFabien Chouteau 
3460f3a4a01SFabien Chouteau     assert(unit != NULL);
3470f3a4a01SFabien Chouteau 
3480f3a4a01SFabien Chouteau     unit->scaler = 0;
3490f3a4a01SFabien Chouteau     unit->reload = 0;
3500f3a4a01SFabien Chouteau 
3510f3a4a01SFabien Chouteau     unit->config  = unit->nr_timers;
3520f3a4a01SFabien Chouteau     unit->config |= unit->irq_line << 3;
3530f3a4a01SFabien Chouteau     unit->config |= 1 << 8;     /* separate interrupt */
3540f3a4a01SFabien Chouteau     unit->config |= 1 << 9;     /* Disable timer freeze */
3550f3a4a01SFabien Chouteau 
3560f3a4a01SFabien Chouteau 
3570f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3580f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3590f3a4a01SFabien Chouteau 
3600f3a4a01SFabien Chouteau         timer->counter = 0;
3610f3a4a01SFabien Chouteau         timer->reload = 0;
3620f3a4a01SFabien Chouteau         timer->config = 0;
363*663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3640f3a4a01SFabien Chouteau         ptimer_stop(timer->ptimer);
3650f3a4a01SFabien Chouteau         ptimer_set_count(timer->ptimer, 0);
3660f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
367*663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3680f3a4a01SFabien Chouteau     }
3690f3a4a01SFabien Chouteau }
3700f3a4a01SFabien Chouteau 
37123251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp)
3720f3a4a01SFabien Chouteau {
373541ab55fSAndreas Färber     GPTimerUnit  *unit = GRLIB_GPTIMER(dev);
3740f3a4a01SFabien Chouteau     unsigned int  i;
37523251fb8SMao Zhongyi     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3760f3a4a01SFabien Chouteau 
3770f3a4a01SFabien Chouteau     assert(unit->nr_timers > 0);
3780f3a4a01SFabien Chouteau     assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
3790f3a4a01SFabien Chouteau 
3807267c094SAnthony Liguori     unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
3810f3a4a01SFabien Chouteau 
3820f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3830f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3840f3a4a01SFabien Chouteau 
3850f3a4a01SFabien Chouteau         timer->unit   = unit;
386*663e475fSPeter Maydell         timer->ptimer = ptimer_init(grlib_gptimer_hit, timer,
387*663e475fSPeter Maydell                                     PTIMER_POLICY_DEFAULT);
3880f3a4a01SFabien Chouteau         timer->id     = i;
3890f3a4a01SFabien Chouteau 
3900f3a4a01SFabien Chouteau         /* One IRQ line for each timer */
39123251fb8SMao Zhongyi         sysbus_init_irq(sbd, &timer->irq);
3920f3a4a01SFabien Chouteau 
393*663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3940f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
395*663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3960f3a4a01SFabien Chouteau     }
3970f3a4a01SFabien Chouteau 
398853dca12SPaolo Bonzini     memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops,
399853dca12SPaolo Bonzini                           unit, "gptimer",
400cde844faSAvi Kivity                           UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
4010f3a4a01SFabien Chouteau 
40223251fb8SMao Zhongyi     sysbus_init_mmio(sbd, &unit->iomem);
4030f3a4a01SFabien Chouteau }
4040f3a4a01SFabien Chouteau 
405999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = {
4060f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz,   40000000),
4070f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("irq-line",  GPTimerUnit, irq_line,  8),
4080f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
409999e12bbSAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
410999e12bbSAnthony Liguori };
411999e12bbSAnthony Liguori 
412999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
413999e12bbSAnthony Liguori {
41439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
415999e12bbSAnthony Liguori 
41623251fb8SMao Zhongyi     dc->realize = grlib_gptimer_realize;
41739bffca2SAnthony Liguori     dc->reset = grlib_gptimer_reset;
41839bffca2SAnthony Liguori     dc->props = grlib_gptimer_properties;
4190f3a4a01SFabien Chouteau }
420999e12bbSAnthony Liguori 
4218c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = {
422541ab55fSAndreas Färber     .name          = TYPE_GRLIB_GPTIMER,
42339bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
42439bffca2SAnthony Liguori     .instance_size = sizeof(GPTimerUnit),
425999e12bbSAnthony Liguori     .class_init    = grlib_gptimer_class_init,
4260f3a4a01SFabien Chouteau };
4270f3a4a01SFabien Chouteau 
42883f7d43aSAndreas Färber static void grlib_gptimer_register_types(void)
4290f3a4a01SFabien Chouteau {
43039bffca2SAnthony Liguori     type_register_static(&grlib_gptimer_info);
4310f3a4a01SFabien Chouteau }
4320f3a4a01SFabien Chouteau 
43383f7d43aSAndreas Färber type_init(grlib_gptimer_register_types)
434