xref: /qemu/hw/timer/grlib_gptimer.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
10f3a4a01SFabien Chouteau /*
20f3a4a01SFabien Chouteau  * QEMU GRLIB GPTimer Emulator
30f3a4a01SFabien Chouteau  *
4f432962eSClément Chigot  * SPDX-License-Identifier: MIT
5f432962eSClément Chigot  *
6f432962eSClément Chigot  * Copyright (c) 2010-2024 AdaCore
70f3a4a01SFabien Chouteau  *
80f3a4a01SFabien Chouteau  * Permission is hereby granted, free of charge, to any person obtaining a copy
90f3a4a01SFabien Chouteau  * of this software and associated documentation files (the "Software"), to deal
100f3a4a01SFabien Chouteau  * in the Software without restriction, including without limitation the rights
110f3a4a01SFabien Chouteau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
120f3a4a01SFabien Chouteau  * copies of the Software, and to permit persons to whom the Software is
130f3a4a01SFabien Chouteau  * furnished to do so, subject to the following conditions:
140f3a4a01SFabien Chouteau  *
150f3a4a01SFabien Chouteau  * The above copyright notice and this permission notice shall be included in
160f3a4a01SFabien Chouteau  * all copies or substantial portions of the Software.
170f3a4a01SFabien Chouteau  *
180f3a4a01SFabien Chouteau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
190f3a4a01SFabien Chouteau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
200f3a4a01SFabien Chouteau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
210f3a4a01SFabien Chouteau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
220f3a4a01SFabien Chouteau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
230f3a4a01SFabien Chouteau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
240f3a4a01SFabien Chouteau  * THE SOFTWARE.
250f3a4a01SFabien Chouteau  */
260f3a4a01SFabien Chouteau 
27db5ebe5fSPeter Maydell #include "qemu/osdep.h"
28f432962eSClément Chigot #include "hw/timer/grlib_gptimer.h"
2983c9f4caSPaolo Bonzini #include "hw/sysbus.h"
301de7afc9SPaolo Bonzini #include "qemu/timer.h"
3164552b6bSMarkus Armbruster #include "hw/irq.h"
3283c9f4caSPaolo Bonzini #include "hw/ptimer.h"
33a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
340b8fa32fSMarkus Armbruster #include "qemu/module.h"
350f3a4a01SFabien Chouteau 
360f3a4a01SFabien Chouteau #include "trace.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
380f3a4a01SFabien Chouteau 
390f3a4a01SFabien Chouteau #define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
400f3a4a01SFabien Chouteau #define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
410f3a4a01SFabien Chouteau 
420f3a4a01SFabien Chouteau #define GPTIMER_MAX_TIMERS 8
430f3a4a01SFabien Chouteau 
440f3a4a01SFabien Chouteau /* GPTimer Config register fields */
450f3a4a01SFabien Chouteau #define GPTIMER_ENABLE      (1 << 0)
460f3a4a01SFabien Chouteau #define GPTIMER_RESTART     (1 << 1)
470f3a4a01SFabien Chouteau #define GPTIMER_LOAD        (1 << 2)
480f3a4a01SFabien Chouteau #define GPTIMER_INT_ENABLE  (1 << 3)
490f3a4a01SFabien Chouteau #define GPTIMER_INT_PENDING (1 << 4)
500f3a4a01SFabien Chouteau #define GPTIMER_CHAIN       (1 << 5) /* Not supported */
510f3a4a01SFabien Chouteau #define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
520f3a4a01SFabien Chouteau 
530f3a4a01SFabien Chouteau /* Memory mapped register offsets */
540f3a4a01SFabien Chouteau #define SCALER_OFFSET         0x00
550f3a4a01SFabien Chouteau #define SCALER_RELOAD_OFFSET  0x04
560f3a4a01SFabien Chouteau #define CONFIG_OFFSET         0x08
570f3a4a01SFabien Chouteau #define COUNTER_OFFSET        0x00
580f3a4a01SFabien Chouteau #define COUNTER_RELOAD_OFFSET 0x04
590f3a4a01SFabien Chouteau #define TIMER_BASE            0x10
600f3a4a01SFabien Chouteau 
618063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(GPTimerUnit, GRLIB_GPTIMER)
62541ab55fSAndreas Färber 
630f3a4a01SFabien Chouteau typedef struct GPTimer     GPTimer;
640f3a4a01SFabien Chouteau 
650f3a4a01SFabien Chouteau struct GPTimer {
660f3a4a01SFabien Chouteau     struct ptimer_state *ptimer;
670f3a4a01SFabien Chouteau 
680f3a4a01SFabien Chouteau     qemu_irq     irq;
690f3a4a01SFabien Chouteau     int          id;
700f3a4a01SFabien Chouteau     GPTimerUnit *unit;
710f3a4a01SFabien Chouteau 
720f3a4a01SFabien Chouteau     /* registers */
730f3a4a01SFabien Chouteau     uint32_t counter;
740f3a4a01SFabien Chouteau     uint32_t reload;
750f3a4a01SFabien Chouteau     uint32_t config;
760f3a4a01SFabien Chouteau };
770f3a4a01SFabien Chouteau 
780f3a4a01SFabien Chouteau struct GPTimerUnit {
79541ab55fSAndreas Färber     SysBusDevice  parent_obj;
80541ab55fSAndreas Färber 
81cde844faSAvi Kivity     MemoryRegion iomem;
820f3a4a01SFabien Chouteau 
830f3a4a01SFabien Chouteau     uint32_t nr_timers;         /* Number of timers available */
840f3a4a01SFabien Chouteau     uint32_t freq_hz;           /* System frequency */
850f3a4a01SFabien Chouteau     uint32_t irq_line;          /* Base irq line */
860f3a4a01SFabien Chouteau 
870f3a4a01SFabien Chouteau     GPTimer *timers;
880f3a4a01SFabien Chouteau 
890f3a4a01SFabien Chouteau     /* registers */
900f3a4a01SFabien Chouteau     uint32_t scaler;
910f3a4a01SFabien Chouteau     uint32_t reload;
920f3a4a01SFabien Chouteau     uint32_t config;
930f3a4a01SFabien Chouteau };
940f3a4a01SFabien Chouteau 
grlib_gptimer_tx_begin(GPTimer * timer)95663e475fSPeter Maydell static void grlib_gptimer_tx_begin(GPTimer *timer)
96663e475fSPeter Maydell {
97663e475fSPeter Maydell     ptimer_transaction_begin(timer->ptimer);
98663e475fSPeter Maydell }
99663e475fSPeter Maydell 
grlib_gptimer_tx_commit(GPTimer * timer)100663e475fSPeter Maydell static void grlib_gptimer_tx_commit(GPTimer *timer)
101663e475fSPeter Maydell {
102663e475fSPeter Maydell     ptimer_transaction_commit(timer->ptimer);
103663e475fSPeter Maydell }
104663e475fSPeter Maydell 
105663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
grlib_gptimer_enable(GPTimer * timer)1060f3a4a01SFabien Chouteau static void grlib_gptimer_enable(GPTimer *timer)
1070f3a4a01SFabien Chouteau {
1080f3a4a01SFabien Chouteau     assert(timer != NULL);
1090f3a4a01SFabien Chouteau 
1100f3a4a01SFabien Chouteau 
1110f3a4a01SFabien Chouteau     ptimer_stop(timer->ptimer);
1120f3a4a01SFabien Chouteau 
1130f3a4a01SFabien Chouteau     if (!(timer->config & GPTIMER_ENABLE)) {
1140f3a4a01SFabien Chouteau         /* Timer disabled */
1150f3a4a01SFabien Chouteau         trace_grlib_gptimer_disabled(timer->id, timer->config);
1160f3a4a01SFabien Chouteau         return;
1170f3a4a01SFabien Chouteau     }
1180f3a4a01SFabien Chouteau 
1190f3a4a01SFabien Chouteau     /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
1200f3a4a01SFabien Chouteau        underflow. Set count + 1 to simulate the GPTimer behavior. */
1210f3a4a01SFabien Chouteau 
1229d5614d5SSebastian Huber     trace_grlib_gptimer_enable(timer->id, timer->counter);
1230f3a4a01SFabien Chouteau 
1249d5614d5SSebastian Huber     ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1);
1250f3a4a01SFabien Chouteau     ptimer_run(timer->ptimer, 1);
1260f3a4a01SFabien Chouteau }
1270f3a4a01SFabien Chouteau 
128663e475fSPeter Maydell /* Must be called within grlib_gptimer_tx_begin/commit block */
grlib_gptimer_restart(GPTimer * timer)1290f3a4a01SFabien Chouteau static void grlib_gptimer_restart(GPTimer *timer)
1300f3a4a01SFabien Chouteau {
1310f3a4a01SFabien Chouteau     assert(timer != NULL);
1320f3a4a01SFabien Chouteau 
1330f3a4a01SFabien Chouteau     trace_grlib_gptimer_restart(timer->id, timer->reload);
1340f3a4a01SFabien Chouteau 
1350f3a4a01SFabien Chouteau     timer->counter = timer->reload;
1360f3a4a01SFabien Chouteau     grlib_gptimer_enable(timer);
1370f3a4a01SFabien Chouteau }
1380f3a4a01SFabien Chouteau 
grlib_gptimer_set_scaler(GPTimerUnit * unit,uint32_t scaler)1390f3a4a01SFabien Chouteau static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
1400f3a4a01SFabien Chouteau {
1410f3a4a01SFabien Chouteau     int i = 0;
1420f3a4a01SFabien Chouteau     uint32_t value = 0;
1430f3a4a01SFabien Chouteau 
1440f3a4a01SFabien Chouteau     assert(unit != NULL);
1450f3a4a01SFabien Chouteau 
1460f3a4a01SFabien Chouteau     if (scaler > 0) {
1470f3a4a01SFabien Chouteau         value = unit->freq_hz / (scaler + 1);
1480f3a4a01SFabien Chouteau     } else {
1490f3a4a01SFabien Chouteau         value = unit->freq_hz;
1500f3a4a01SFabien Chouteau     }
1510f3a4a01SFabien Chouteau 
1520f3a4a01SFabien Chouteau     trace_grlib_gptimer_set_scaler(scaler, value);
1530f3a4a01SFabien Chouteau 
1540f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
155663e475fSPeter Maydell         ptimer_transaction_begin(unit->timers[i].ptimer);
1560f3a4a01SFabien Chouteau         ptimer_set_freq(unit->timers[i].ptimer, value);
157663e475fSPeter Maydell         ptimer_transaction_commit(unit->timers[i].ptimer);
1580f3a4a01SFabien Chouteau     }
1590f3a4a01SFabien Chouteau }
1600f3a4a01SFabien Chouteau 
grlib_gptimer_hit(void * opaque)1610f3a4a01SFabien Chouteau static void grlib_gptimer_hit(void *opaque)
1620f3a4a01SFabien Chouteau {
1630f3a4a01SFabien Chouteau     GPTimer *timer = opaque;
1640f3a4a01SFabien Chouteau     assert(timer != NULL);
1650f3a4a01SFabien Chouteau 
1660f3a4a01SFabien Chouteau     trace_grlib_gptimer_hit(timer->id);
1670f3a4a01SFabien Chouteau 
1680f3a4a01SFabien Chouteau     /* Timer expired */
1690f3a4a01SFabien Chouteau 
1700f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_INT_ENABLE) {
1710f3a4a01SFabien Chouteau         /* Set the pending bit (only unset by write in the config register) */
1720f3a4a01SFabien Chouteau         timer->config |= GPTIMER_INT_PENDING;
1730f3a4a01SFabien Chouteau         qemu_irq_pulse(timer->irq);
1740f3a4a01SFabien Chouteau     }
1750f3a4a01SFabien Chouteau 
1760f3a4a01SFabien Chouteau     if (timer->config & GPTIMER_RESTART) {
1770f3a4a01SFabien Chouteau         grlib_gptimer_restart(timer);
1780f3a4a01SFabien Chouteau     }
1790f3a4a01SFabien Chouteau }
1800f3a4a01SFabien Chouteau 
grlib_gptimer_read(void * opaque,hwaddr addr,unsigned size)181a8170e5eSAvi Kivity static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr,
182cde844faSAvi Kivity                                    unsigned size)
1830f3a4a01SFabien Chouteau {
1840f3a4a01SFabien Chouteau     GPTimerUnit        *unit  = opaque;
185a8170e5eSAvi Kivity     hwaddr  timer_addr;
1860f3a4a01SFabien Chouteau     int                 id;
1870f3a4a01SFabien Chouteau     uint32_t            value = 0;
1880f3a4a01SFabien Chouteau 
1890f3a4a01SFabien Chouteau     addr &= 0xff;
1900f3a4a01SFabien Chouteau 
1910f3a4a01SFabien Chouteau     /* Unit registers */
1920f3a4a01SFabien Chouteau     switch (addr) {
1930f3a4a01SFabien Chouteau     case SCALER_OFFSET:
194b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->scaler);
1950f3a4a01SFabien Chouteau         return unit->scaler;
1960f3a4a01SFabien Chouteau 
1970f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
198b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->reload);
1990f3a4a01SFabien Chouteau         return unit->reload;
2000f3a4a01SFabien Chouteau 
2010f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
202b4548fccSStefan Hajnoczi         trace_grlib_gptimer_readl(-1, addr, unit->config);
2030f3a4a01SFabien Chouteau         return unit->config;
2040f3a4a01SFabien Chouteau 
2050f3a4a01SFabien Chouteau     default:
2060f3a4a01SFabien Chouteau         break;
2070f3a4a01SFabien Chouteau     }
2080f3a4a01SFabien Chouteau 
2090f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2100f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2110f3a4a01SFabien Chouteau 
2120f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2130f3a4a01SFabien Chouteau 
2140f3a4a01SFabien Chouteau         /* GPTimer registers */
2150f3a4a01SFabien Chouteau         switch (timer_addr) {
2160f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
2170f3a4a01SFabien Chouteau             value = ptimer_get_count(unit->timers[id].ptimer);
218b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2190f3a4a01SFabien Chouteau             return value;
2200f3a4a01SFabien Chouteau 
2210f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
2220f3a4a01SFabien Chouteau             value = unit->timers[id].reload;
223b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, value);
2240f3a4a01SFabien Chouteau             return value;
2250f3a4a01SFabien Chouteau 
2260f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
227b4548fccSStefan Hajnoczi             trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
2280f3a4a01SFabien Chouteau             return unit->timers[id].config;
2290f3a4a01SFabien Chouteau 
2300f3a4a01SFabien Chouteau         default:
2310f3a4a01SFabien Chouteau             break;
2320f3a4a01SFabien Chouteau         }
2330f3a4a01SFabien Chouteau 
2340f3a4a01SFabien Chouteau     }
2350f3a4a01SFabien Chouteau 
236b4548fccSStefan Hajnoczi     trace_grlib_gptimer_readl(-1, addr, 0);
2370f3a4a01SFabien Chouteau     return 0;
2380f3a4a01SFabien Chouteau }
2390f3a4a01SFabien Chouteau 
grlib_gptimer_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)240a8170e5eSAvi Kivity static void grlib_gptimer_write(void *opaque, hwaddr addr,
241cde844faSAvi Kivity                                 uint64_t value, unsigned size)
2420f3a4a01SFabien Chouteau {
2430f3a4a01SFabien Chouteau     GPTimerUnit        *unit = opaque;
244a8170e5eSAvi Kivity     hwaddr  timer_addr;
2450f3a4a01SFabien Chouteau     int                 id;
2460f3a4a01SFabien Chouteau 
2470f3a4a01SFabien Chouteau     addr &= 0xff;
2480f3a4a01SFabien Chouteau 
2490f3a4a01SFabien Chouteau     /* Unit registers */
2500f3a4a01SFabien Chouteau     switch (addr) {
2510f3a4a01SFabien Chouteau     case SCALER_OFFSET:
2520f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2530f3a4a01SFabien Chouteau         unit->scaler = value;
254b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->scaler);
2550f3a4a01SFabien Chouteau         return;
2560f3a4a01SFabien Chouteau 
2570f3a4a01SFabien Chouteau     case SCALER_RELOAD_OFFSET:
2580f3a4a01SFabien Chouteau         value &= 0xFFFF; /* clean up the value */
2590f3a4a01SFabien Chouteau         unit->reload = value;
260b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, unit->reload);
2610f3a4a01SFabien Chouteau         grlib_gptimer_set_scaler(unit, value);
2620f3a4a01SFabien Chouteau         return;
2630f3a4a01SFabien Chouteau 
2640f3a4a01SFabien Chouteau     case CONFIG_OFFSET:
2650f3a4a01SFabien Chouteau         /* Read Only (disable timer freeze not supported) */
266b4548fccSStefan Hajnoczi         trace_grlib_gptimer_writel(-1, addr, 0);
2670f3a4a01SFabien Chouteau         return;
2680f3a4a01SFabien Chouteau 
2690f3a4a01SFabien Chouteau     default:
2700f3a4a01SFabien Chouteau         break;
2710f3a4a01SFabien Chouteau     }
2720f3a4a01SFabien Chouteau 
2730f3a4a01SFabien Chouteau     timer_addr = (addr % TIMER_BASE);
2740f3a4a01SFabien Chouteau     id         = (addr - TIMER_BASE) / TIMER_BASE;
2750f3a4a01SFabien Chouteau 
2760f3a4a01SFabien Chouteau     if (id >= 0 && id < unit->nr_timers) {
2770f3a4a01SFabien Chouteau 
2780f3a4a01SFabien Chouteau         /* GPTimer registers */
2790f3a4a01SFabien Chouteau         switch (timer_addr) {
2800f3a4a01SFabien Chouteau         case COUNTER_OFFSET:
281b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
282663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
2830f3a4a01SFabien Chouteau             unit->timers[id].counter = value;
2840f3a4a01SFabien Chouteau             grlib_gptimer_enable(&unit->timers[id]);
285663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
2860f3a4a01SFabien Chouteau             return;
2870f3a4a01SFabien Chouteau 
2880f3a4a01SFabien Chouteau         case COUNTER_RELOAD_OFFSET:
289b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2900f3a4a01SFabien Chouteau             unit->timers[id].reload = value;
2910f3a4a01SFabien Chouteau             return;
2920f3a4a01SFabien Chouteau 
2930f3a4a01SFabien Chouteau         case CONFIG_OFFSET:
294b4548fccSStefan Hajnoczi             trace_grlib_gptimer_writel(id, addr, value);
2950f3a4a01SFabien Chouteau 
2960f3a4a01SFabien Chouteau             if (value & GPTIMER_INT_PENDING) {
2970f3a4a01SFabien Chouteau                 /* clear pending bit */
2980f3a4a01SFabien Chouteau                 value &= ~GPTIMER_INT_PENDING;
2990f3a4a01SFabien Chouteau             } else {
3000f3a4a01SFabien Chouteau                 /* keep pending bit */
3010f3a4a01SFabien Chouteau                 value |= unit->timers[id].config & GPTIMER_INT_PENDING;
3020f3a4a01SFabien Chouteau             }
3030f3a4a01SFabien Chouteau 
3040f3a4a01SFabien Chouteau             unit->timers[id].config = value;
3050f3a4a01SFabien Chouteau 
3060f3a4a01SFabien Chouteau             /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
3070f3a4a01SFabien Chouteau                bits are present, we just have to call restart. */
3080f3a4a01SFabien Chouteau 
309663e475fSPeter Maydell             grlib_gptimer_tx_begin(&unit->timers[id]);
3100f3a4a01SFabien Chouteau             if (value & GPTIMER_LOAD) {
3110f3a4a01SFabien Chouteau                 grlib_gptimer_restart(&unit->timers[id]);
3120f3a4a01SFabien Chouteau             } else if (value & GPTIMER_ENABLE) {
3130f3a4a01SFabien Chouteau                 grlib_gptimer_enable(&unit->timers[id]);
3140f3a4a01SFabien Chouteau             }
3150f3a4a01SFabien Chouteau 
3160f3a4a01SFabien Chouteau             /* These fields must always be read as 0 */
3170f3a4a01SFabien Chouteau             value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
3180f3a4a01SFabien Chouteau 
3190f3a4a01SFabien Chouteau             unit->timers[id].config = value;
320663e475fSPeter Maydell             grlib_gptimer_tx_commit(&unit->timers[id]);
3210f3a4a01SFabien Chouteau             return;
3220f3a4a01SFabien Chouteau 
3230f3a4a01SFabien Chouteau         default:
3240f3a4a01SFabien Chouteau             break;
3250f3a4a01SFabien Chouteau         }
3260f3a4a01SFabien Chouteau 
3270f3a4a01SFabien Chouteau     }
3280f3a4a01SFabien Chouteau 
329b4548fccSStefan Hajnoczi     trace_grlib_gptimer_writel(-1, addr, value);
3300f3a4a01SFabien Chouteau }
3310f3a4a01SFabien Chouteau 
332cde844faSAvi Kivity static const MemoryRegionOps grlib_gptimer_ops = {
333cde844faSAvi Kivity     .read = grlib_gptimer_read,
334cde844faSAvi Kivity     .write = grlib_gptimer_write,
335cde844faSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
336cde844faSAvi Kivity     .valid = {
337cde844faSAvi Kivity         .min_access_size = 4,
338cde844faSAvi Kivity         .max_access_size = 4,
339cde844faSAvi Kivity     },
3400f3a4a01SFabien Chouteau };
3410f3a4a01SFabien Chouteau 
grlib_gptimer_reset(DeviceState * d)3420f3a4a01SFabien Chouteau static void grlib_gptimer_reset(DeviceState *d)
3430f3a4a01SFabien Chouteau {
344541ab55fSAndreas Färber     GPTimerUnit *unit = GRLIB_GPTIMER(d);
3450f3a4a01SFabien Chouteau     int          i    = 0;
3460f3a4a01SFabien Chouteau 
3470f3a4a01SFabien Chouteau     assert(unit != NULL);
3480f3a4a01SFabien Chouteau 
3490f3a4a01SFabien Chouteau     unit->scaler = 0;
3500f3a4a01SFabien Chouteau     unit->reload = 0;
3510f3a4a01SFabien Chouteau 
3520f3a4a01SFabien Chouteau     unit->config  = unit->nr_timers;
3530f3a4a01SFabien Chouteau     unit->config |= unit->irq_line << 3;
3540f3a4a01SFabien Chouteau     unit->config |= 1 << 8;     /* separate interrupt */
3550f3a4a01SFabien Chouteau     unit->config |= 1 << 9;     /* Disable timer freeze */
3560f3a4a01SFabien Chouteau 
3570f3a4a01SFabien Chouteau 
3580f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3590f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3600f3a4a01SFabien Chouteau 
3610f3a4a01SFabien Chouteau         timer->counter = 0;
3620f3a4a01SFabien Chouteau         timer->reload = 0;
3630f3a4a01SFabien Chouteau         timer->config = 0;
364663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3650f3a4a01SFabien Chouteau         ptimer_stop(timer->ptimer);
3660f3a4a01SFabien Chouteau         ptimer_set_count(timer->ptimer, 0);
3670f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
368663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3690f3a4a01SFabien Chouteau     }
3700f3a4a01SFabien Chouteau }
3710f3a4a01SFabien Chouteau 
grlib_gptimer_realize(DeviceState * dev,Error ** errp)37223251fb8SMao Zhongyi static void grlib_gptimer_realize(DeviceState *dev, Error **errp)
3730f3a4a01SFabien Chouteau {
374541ab55fSAndreas Färber     GPTimerUnit  *unit = GRLIB_GPTIMER(dev);
3750f3a4a01SFabien Chouteau     unsigned int  i;
37623251fb8SMao Zhongyi     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3770f3a4a01SFabien Chouteau 
3780f3a4a01SFabien Chouteau     assert(unit->nr_timers > 0);
3790f3a4a01SFabien Chouteau     assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
3800f3a4a01SFabien Chouteau 
3817267c094SAnthony Liguori     unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
3820f3a4a01SFabien Chouteau 
3830f3a4a01SFabien Chouteau     for (i = 0; i < unit->nr_timers; i++) {
3840f3a4a01SFabien Chouteau         GPTimer *timer = &unit->timers[i];
3850f3a4a01SFabien Chouteau 
3860f3a4a01SFabien Chouteau         timer->unit   = unit;
387663e475fSPeter Maydell         timer->ptimer = ptimer_init(grlib_gptimer_hit, timer,
3889598c1bbSPeter Maydell                                     PTIMER_POLICY_LEGACY);
3890f3a4a01SFabien Chouteau         timer->id     = i;
3900f3a4a01SFabien Chouteau 
3910f3a4a01SFabien Chouteau         /* One IRQ line for each timer */
39223251fb8SMao Zhongyi         sysbus_init_irq(sbd, &timer->irq);
3930f3a4a01SFabien Chouteau 
394663e475fSPeter Maydell         ptimer_transaction_begin(timer->ptimer);
3950f3a4a01SFabien Chouteau         ptimer_set_freq(timer->ptimer, unit->freq_hz);
396663e475fSPeter Maydell         ptimer_transaction_commit(timer->ptimer);
3970f3a4a01SFabien Chouteau     }
3980f3a4a01SFabien Chouteau 
399853dca12SPaolo Bonzini     memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops,
400853dca12SPaolo Bonzini                           unit, "gptimer",
401cde844faSAvi Kivity                           UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
4020f3a4a01SFabien Chouteau 
40323251fb8SMao Zhongyi     sysbus_init_mmio(sbd, &unit->iomem);
4040f3a4a01SFabien Chouteau }
4050f3a4a01SFabien Chouteau 
40674734e2bSRichard Henderson static const Property grlib_gptimer_properties[] = {
4070f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz,   40000000),
4080f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("irq-line",  GPTimerUnit, irq_line,  8),
4090f3a4a01SFabien Chouteau     DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
410999e12bbSAnthony Liguori };
411999e12bbSAnthony Liguori 
grlib_gptimer_class_init(ObjectClass * klass,const void * data)412*12d1a768SPhilippe Mathieu-Daudé static void grlib_gptimer_class_init(ObjectClass *klass, const void *data)
413999e12bbSAnthony Liguori {
41439bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
415999e12bbSAnthony Liguori 
41623251fb8SMao Zhongyi     dc->realize = grlib_gptimer_realize;
417e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, grlib_gptimer_reset);
4184f67d30bSMarc-André Lureau     device_class_set_props(dc, 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 
grlib_gptimer_register_types(void)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