xref: /qemu/hw/timer/grlib_gptimer.c (revision 9598c1bb39b2d4f0d3a55072cc70251c452132cd)
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"
35db1015e9SEduardo Habkost #include "qom/object.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 
598063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(GPTimerUnit, GRLIB_GPTIMER)
60541ab55fSAndreas Färber 
610f3a4a01SFabien Chouteau typedef struct GPTimer     GPTimer;
620f3a4a01SFabien Chouteau 
630f3a4a01SFabien Chouteau struct GPTimer {
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 
93663e475fSPeter Maydell static void grlib_gptimer_tx_begin(GPTimer *timer)
94663e475fSPeter Maydell {
95663e475fSPeter Maydell     ptimer_transaction_begin(timer->ptimer);
96663e475fSPeter Maydell }
97663e475fSPeter Maydell 
98663e475fSPeter Maydell static void grlib_gptimer_tx_commit(GPTimer *timer)
99663e475fSPeter Maydell {
100663e475fSPeter Maydell     ptimer_transaction_commit(timer->ptimer);
101663e475fSPeter Maydell }
102663e475fSPeter Maydell 
103663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
1040f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer)
1050f3a4a01SFabien Chouteau {
1060f3a4a01SFabien Chouteau     assert(timer != NULL);
1070f3a4a01SFabien Chouteau 
1080f3a4a01SFabien Chouteau 
1090f3a4a01SFabien Chouteau     ptimer_stop(timer->ptimer);
1100f3a4a01SFabien Chouteau 
1110f3a4a01SFabien Chouteau     if (!(timer->config & GPTIMER_ENABLE)) {
1120f3a4a01SFabien Chouteau         /* Timer disabled */
1130f3a4a01SFabien Chouteau         trace_grlib_gptimer_disabled(timer->id, timer->config);
1140f3a4a01SFabien Chouteau         return;
1150f3a4a01SFabien Chouteau     }
1160f3a4a01SFabien Chouteau 
1170f3a4a01SFabien Chouteau     /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
1180f3a4a01SFabien Chouteau        underflow. Set count + 1 to simulate the GPTimer behavior. */
1190f3a4a01SFabien Chouteau 
1209d5614d5SSebastian Huber     trace_grlib_gptimer_enable(timer->id, timer->counter);
1210f3a4a01SFabien Chouteau 
1229d5614d5SSebastian Huber     ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1);
1230f3a4a01SFabien Chouteau     ptimer_run(timer->ptimer, 1);
1240f3a4a01SFabien Chouteau }
1250f3a4a01SFabien Chouteau 
126663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
1270f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer)
1280f3a4a01SFabien Chouteau {
1290f3a4a01SFabien Chouteau     assert(timer != NULL);
1300f3a4a01SFabien Chouteau 
1310f3a4a01SFabien Chouteau     trace_grlib_gptimer_restart(timer->id, timer->reload);
1320f3a4a01SFabien Chouteau 
1330f3a4a01SFabien Chouteau     timer->counter = timer->reload;
1340f3a4a01SFabien Chouteau     grlib_gptimer_enable(timer);
1350f3a4a01SFabien Chouteau }
1360f3a4a01SFabien Chouteau 
1370f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
1380f3a4a01SFabien Chouteau {
1390f3a4a01SFabien Chouteau     int i = 0;
1400f3a4a01SFabien Chouteau     uint32_t value = 0;
1410f3a4a01SFabien Chouteau 
1420f3a4a01SFabien Chouteau     assert(unit != NULL);
1430f3a4a01SFabien Chouteau 
1440f3a4a01SFabien Chouteau     if (scaler > 0) {
1450f3a4a01SFabien Chouteau         value = unit->freq_hz / (scaler + 1);
1460f3a4a01SFabien Chouteau     } else {
1470f3a4a01SFabien Chouteau         value = unit->freq_hz;
1480f3a4a01SFabien Chouteau     }
1490f3a4a01SFabien Chouteau 
1500f3a4a01SFabien Chouteau     trace_grlib_gptimer_set_scaler(scaler, value);
1510f3a4a01SFabien Chouteau 
1520f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
153663e475fSPeter Maydell         ptimer_transaction_begin(unit->timers[i].ptimer);
1540f3a4a01SFabien Chouteau         ptimer_set_freq(unit->timers[i].ptimer, value);
155663e475fSPeter Maydell         ptimer_transaction_commit(unit->timers[i].ptimer);
1560f3a4a01SFabien Chouteau     }
1570f3a4a01SFabien Chouteau }
1580f3a4a01SFabien Chouteau 
1590f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque)
1600f3a4a01SFabien Chouteau {
1610f3a4a01SFabien Chouteau     GPTimer *timer = opaque;
1620f3a4a01SFabien Chouteau     assert(timer != NULL);
1630f3a4a01SFabien Chouteau 
1640f3a4a01SFabien Chouteau     trace_grlib_gptimer_hit(timer->id);
1650f3a4a01SFabien Chouteau 
1660f3a4a01SFabien Chouteau     /* Timer expired */
1670f3a4a01SFabien Chouteau 
1680f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_INT_ENABLE) {
1690f3a4a01SFabien Chouteau         /* Set the pending bit (only unset by write in the config register) */
1700f3a4a01SFabien Chouteau         timer->config |= GPTIMER_INT_PENDING;
1710f3a4a01SFabien Chouteau         qemu_irq_pulse(timer->irq);
1720f3a4a01SFabien Chouteau     }
1730f3a4a01SFabien Chouteau 
1740f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_RESTART) {
1750f3a4a01SFabien Chouteau         grlib_gptimer_restart(timer);
1760f3a4a01SFabien Chouteau     }
1770f3a4a01SFabien Chouteau }
1780f3a4a01SFabien Chouteau 
179a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr,
180cde844faSAvi Kivity                                    unsigned size)
1810f3a4a01SFabien Chouteau {
1820f3a4a01SFabien Chouteau     GPTimerUnit        *unit  = opaque;
183a8170e5eSAvi Kivity     hwaddr  timer_addr;
1840f3a4a01SFabien Chouteau     int                 id;
1850f3a4a01SFabien Chouteau     uint32_t            value = 0;
1860f3a4a01SFabien Chouteau 
1870f3a4a01SFabien Chouteau     addr &= 0xff;
1880f3a4a01SFabien Chouteau 
1890f3a4a01SFabien Chouteau     /* Unit registers */
1900f3a4a01SFabien Chouteau     switch (addr) {
1910f3a4a01SFabien Chouteau     case SCALER_OFFSET:
192b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->scaler);
1930f3a4a01SFabien Chouteau         return unit->scaler;
1940f3a4a01SFabien Chouteau 
1950f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
196b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->reload);
1970f3a4a01SFabien Chouteau         return unit->reload;
1980f3a4a01SFabien Chouteau 
1990f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
200b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->config);
2010f3a4a01SFabien Chouteau         return unit->config;
2020f3a4a01SFabien Chouteau 
2030f3a4a01SFabien Chouteau     default:
2040f3a4a01SFabien Chouteau         break;
2050f3a4a01SFabien Chouteau     }
2060f3a4a01SFabien Chouteau 
2070f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2080f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2090f3a4a01SFabien Chouteau 
2100f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2110f3a4a01SFabien Chouteau 
2120f3a4a01SFabien Chouteau         /* GPTimer registers */
2130f3a4a01SFabien Chouteau         switch (timer_addr) {
2140f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
2150f3a4a01SFabien Chouteau             value = ptimer_get_count(unit->timers[id].ptimer);
216b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2170f3a4a01SFabien Chouteau             return value;
2180f3a4a01SFabien Chouteau 
2190f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
2200f3a4a01SFabien Chouteau             value = unit->timers[id].reload;
221b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2220f3a4a01SFabien Chouteau             return value;
2230f3a4a01SFabien Chouteau 
2240f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
225b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
2260f3a4a01SFabien Chouteau             return unit->timers[id].config;
2270f3a4a01SFabien Chouteau 
2280f3a4a01SFabien Chouteau         default:
2290f3a4a01SFabien Chouteau             break;
2300f3a4a01SFabien Chouteau         }
2310f3a4a01SFabien Chouteau 
2320f3a4a01SFabien Chouteau     }
2330f3a4a01SFabien Chouteau 
234b4548fccSStefan Hajnoczi     trace_grlib_gptimer_readl(-1, addr, 0);
2350f3a4a01SFabien Chouteau     return 0;
2360f3a4a01SFabien Chouteau }
2370f3a4a01SFabien Chouteau 
238a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr,
239cde844faSAvi Kivity                                 uint64_t value, unsigned size)
2400f3a4a01SFabien Chouteau {
2410f3a4a01SFabien Chouteau     GPTimerUnit        *unit = opaque;
242a8170e5eSAvi Kivity     hwaddr  timer_addr;
2430f3a4a01SFabien Chouteau     int                 id;
2440f3a4a01SFabien Chouteau 
2450f3a4a01SFabien Chouteau     addr &= 0xff;
2460f3a4a01SFabien Chouteau 
2470f3a4a01SFabien Chouteau     /* Unit registers */
2480f3a4a01SFabien Chouteau     switch (addr) {
2490f3a4a01SFabien Chouteau     case SCALER_OFFSET:
2500f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2510f3a4a01SFabien Chouteau         unit->scaler = value;
252b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->scaler);
2530f3a4a01SFabien Chouteau         return;
2540f3a4a01SFabien Chouteau 
2550f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
2560f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2570f3a4a01SFabien Chouteau         unit->reload = value;
258b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->reload);
2590f3a4a01SFabien Chouteau         grlib_gptimer_set_scaler(unit, value);
2600f3a4a01SFabien Chouteau         return;
2610f3a4a01SFabien Chouteau 
2620f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
2630f3a4a01SFabien Chouteau         /* Read Only (disable timer freeze not supported) */
264b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, 0);
2650f3a4a01SFabien Chouteau         return;
2660f3a4a01SFabien Chouteau 
2670f3a4a01SFabien Chouteau     default:
2680f3a4a01SFabien Chouteau         break;
2690f3a4a01SFabien Chouteau     }
2700f3a4a01SFabien Chouteau 
2710f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2720f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2730f3a4a01SFabien Chouteau 
2740f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2750f3a4a01SFabien Chouteau 
2760f3a4a01SFabien Chouteau         /* GPTimer registers */
2770f3a4a01SFabien Chouteau         switch (timer_addr) {
2780f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
279b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
280663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
2810f3a4a01SFabien Chouteau             unit->timers[id].counter = value;
2820f3a4a01SFabien Chouteau             grlib_gptimer_enable(&unit->timers[id]);
283663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
2840f3a4a01SFabien Chouteau             return;
2850f3a4a01SFabien Chouteau 
2860f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
287b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2880f3a4a01SFabien Chouteau             unit->timers[id].reload = value;
2890f3a4a01SFabien Chouteau             return;
2900f3a4a01SFabien Chouteau 
2910f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
292b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2930f3a4a01SFabien Chouteau 
2940f3a4a01SFabien Chouteau             if (value & GPTIMER_INT_PENDING) {
2950f3a4a01SFabien Chouteau                 /* clear pending bit */
2960f3a4a01SFabien Chouteau                 value &= ~GPTIMER_INT_PENDING;
2970f3a4a01SFabien Chouteau             } else {
2980f3a4a01SFabien Chouteau                 /* keep pending bit */
2990f3a4a01SFabien Chouteau                 value |= unit->timers[id].config & GPTIMER_INT_PENDING;
3000f3a4a01SFabien Chouteau             }
3010f3a4a01SFabien Chouteau 
3020f3a4a01SFabien Chouteau             unit->timers[id].config = value;
3030f3a4a01SFabien Chouteau 
3040f3a4a01SFabien Chouteau             /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
3050f3a4a01SFabien Chouteau                bits are present, we just have to call restart. */
3060f3a4a01SFabien Chouteau 
307663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
3080f3a4a01SFabien Chouteau             if (value & GPTIMER_LOAD) {
3090f3a4a01SFabien Chouteau                 grlib_gptimer_restart(&unit->timers[id]);
3100f3a4a01SFabien Chouteau             } else if (value & GPTIMER_ENABLE) {
3110f3a4a01SFabien Chouteau                 grlib_gptimer_enable(&unit->timers[id]);
3120f3a4a01SFabien Chouteau             }
3130f3a4a01SFabien Chouteau 
3140f3a4a01SFabien Chouteau             /* These fields must always be read as 0 */
3150f3a4a01SFabien Chouteau             value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
3160f3a4a01SFabien Chouteau 
3170f3a4a01SFabien Chouteau             unit->timers[id].config = value;
318663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
3190f3a4a01SFabien Chouteau             return;
3200f3a4a01SFabien Chouteau 
3210f3a4a01SFabien Chouteau         default:
3220f3a4a01SFabien Chouteau             break;
3230f3a4a01SFabien Chouteau         }
3240f3a4a01SFabien Chouteau 
3250f3a4a01SFabien Chouteau     }
3260f3a4a01SFabien Chouteau 
327b4548fccSStefan Hajnoczi     trace_grlib_gptimer_writel(-1, addr, value);
3280f3a4a01SFabien Chouteau }
3290f3a4a01SFabien Chouteau 
330cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = {
331cde844faSAvi Kivity     .read = grlib_gptimer_read,
332cde844faSAvi Kivity     .write = grlib_gptimer_write,
333cde844faSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
334cde844faSAvi Kivity     .valid = {
335cde844faSAvi Kivity         .min_access_size = 4,
336cde844faSAvi Kivity         .max_access_size = 4,
337cde844faSAvi Kivity     },
3380f3a4a01SFabien Chouteau };
3390f3a4a01SFabien Chouteau 
3400f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d)
3410f3a4a01SFabien Chouteau {
342541ab55fSAndreas Färber     GPTimerUnit *unit = GRLIB_GPTIMER(d);
3430f3a4a01SFabien Chouteau     int          i    = 0;
3440f3a4a01SFabien Chouteau 
3450f3a4a01SFabien Chouteau     assert(unit != NULL);
3460f3a4a01SFabien Chouteau 
3470f3a4a01SFabien Chouteau     unit->scaler = 0;
3480f3a4a01SFabien Chouteau     unit->reload = 0;
3490f3a4a01SFabien Chouteau 
3500f3a4a01SFabien Chouteau     unit->config  = unit->nr_timers;
3510f3a4a01SFabien Chouteau     unit->config |= unit->irq_line << 3;
3520f3a4a01SFabien Chouteau     unit->config |= 1 << 8;     /* separate interrupt */
3530f3a4a01SFabien Chouteau     unit->config |= 1 << 9;     /* Disable timer freeze */
3540f3a4a01SFabien Chouteau 
3550f3a4a01SFabien Chouteau 
3560f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3570f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3580f3a4a01SFabien Chouteau 
3590f3a4a01SFabien Chouteau         timer->counter = 0;
3600f3a4a01SFabien Chouteau         timer->reload = 0;
3610f3a4a01SFabien Chouteau         timer->config = 0;
362663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3630f3a4a01SFabien Chouteau         ptimer_stop(timer->ptimer);
3640f3a4a01SFabien Chouteau         ptimer_set_count(timer->ptimer, 0);
3650f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
366663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3670f3a4a01SFabien Chouteau     }
3680f3a4a01SFabien Chouteau }
3690f3a4a01SFabien Chouteau 
37023251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp)
3710f3a4a01SFabien Chouteau {
372541ab55fSAndreas Färber     GPTimerUnit  *unit = GRLIB_GPTIMER(dev);
3730f3a4a01SFabien Chouteau     unsigned int  i;
37423251fb8SMao Zhongyi     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3750f3a4a01SFabien Chouteau 
3760f3a4a01SFabien Chouteau     assert(unit->nr_timers > 0);
3770f3a4a01SFabien Chouteau     assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
3780f3a4a01SFabien Chouteau 
3797267c094SAnthony Liguori     unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
3800f3a4a01SFabien Chouteau 
3810f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3820f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3830f3a4a01SFabien Chouteau 
3840f3a4a01SFabien Chouteau         timer->unit   = unit;
385663e475fSPeter Maydell         timer->ptimer = ptimer_init(grlib_gptimer_hit, timer,
386*9598c1bbSPeter Maydell                                     PTIMER_POLICY_LEGACY);
3870f3a4a01SFabien Chouteau         timer->id     = i;
3880f3a4a01SFabien Chouteau 
3890f3a4a01SFabien Chouteau         /* One IRQ line for each timer */
39023251fb8SMao Zhongyi         sysbus_init_irq(sbd, &timer->irq);
3910f3a4a01SFabien Chouteau 
392663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3930f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
394663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3950f3a4a01SFabien Chouteau     }
3960f3a4a01SFabien Chouteau 
397853dca12SPaolo Bonzini     memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops,
398853dca12SPaolo Bonzini                           unit, "gptimer",
399cde844faSAvi Kivity                           UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
4000f3a4a01SFabien Chouteau 
40123251fb8SMao Zhongyi     sysbus_init_mmio(sbd, &unit->iomem);
4020f3a4a01SFabien Chouteau }
4030f3a4a01SFabien Chouteau 
404999e12bbSAnthony Liguori static Property grlib_gptimer_properties[] = {
4050f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz,   40000000),
4060f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("irq-line",  GPTimerUnit, irq_line,  8),
4070f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
408999e12bbSAnthony Liguori     DEFINE_PROP_END_OF_LIST(),
409999e12bbSAnthony Liguori };
410999e12bbSAnthony Liguori 
411999e12bbSAnthony Liguori static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
412999e12bbSAnthony Liguori {
41339bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
414999e12bbSAnthony Liguori 
41523251fb8SMao Zhongyi     dc->realize = grlib_gptimer_realize;
41639bffca2SAnthony Liguori     dc->reset = grlib_gptimer_reset;
4174f67d30bSMarc-André Lureau     device_class_set_props(dc, grlib_gptimer_properties);
4180f3a4a01SFabien Chouteau }
419999e12bbSAnthony Liguori 
4208c43a6f0SAndreas Färber static const TypeInfo grlib_gptimer_info = {
421541ab55fSAndreas Färber     .name          = TYPE_GRLIB_GPTIMER,
42239bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
42339bffca2SAnthony Liguori     .instance_size = sizeof(GPTimerUnit),
424999e12bbSAnthony Liguori     .class_init    = grlib_gptimer_class_init,
4250f3a4a01SFabien Chouteau };
4260f3a4a01SFabien Chouteau 
42783f7d43aSAndreas Färber static void grlib_gptimer_register_types(void)
4280f3a4a01SFabien Chouteau {
42939bffca2SAnthony Liguori     type_register_static(&grlib_gptimer_info);
4300f3a4a01SFabien Chouteau }
4310f3a4a01SFabien Chouteau 
43283f7d43aSAndreas Färber type_init(grlib_gptimer_register_types)
433