xref: /qemu/hw/timer/mips_gictimer.c (revision 603476c2)
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2016 Imagination Technologies
7  */
8 
9 #include "qemu/osdep.h"
10 #include "hw/hw.h"
11 #include "hw/sysbus.h"
12 #include "qemu/timer.h"
13 #include "hw/timer/mips_gictimer.h"
14 
15 #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
16 
17 static void gic_vptimer_update(MIPSGICTimerState *gictimer,
18                                    uint32_t vp_index, uint64_t now)
19 {
20     uint64_t next;
21     uint32_t wait;
22 
23     wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo -
24            (uint32_t)(now / TIMER_PERIOD);
25     next = now + (uint64_t)wait * TIMER_PERIOD;
26 
27     timer_mod(gictimer->vptimers[vp_index].qtimer, next);
28 }
29 
30 static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index,
31                                uint64_t now)
32 {
33     if (gictimer->countstop) {
34         /* timer stopped */
35         return;
36     }
37     gictimer->cb(gictimer->opaque, vp_index);
38     gic_vptimer_update(gictimer, vp_index, now);
39 }
40 
41 static void gic_vptimer_cb(void *opaque)
42 {
43     MIPSGICTimerVPState *vptimer = opaque;
44     MIPSGICTimerState *gictimer = vptimer->gictimer;
45     gic_vptimer_expire(gictimer, vptimer->vp_index,
46                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
47 }
48 
49 uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer)
50 {
51     int i;
52     if (gictimer->countstop) {
53         return gictimer->sh_counterlo;
54     } else {
55         uint64_t now;
56         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
57         for (i = 0; i < gictimer->num_vps; i++) {
58             if (timer_pending(gictimer->vptimers[i].qtimer)
59                 && timer_expired(gictimer->vptimers[i].qtimer, now)) {
60                 /* The timer has already expired.  */
61                 gic_vptimer_expire(gictimer, i, now);
62             }
63         }
64         return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
65     }
66 }
67 
68 void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count)
69 {
70     int i;
71     uint64_t now;
72 
73     if (gictimer->countstop || !gictimer->vptimers[0].qtimer) {
74         gictimer->sh_counterlo = count;
75     } else {
76         /* Store new count register */
77         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
78         gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD);
79         /* Update timer timer */
80         for (i = 0; i < gictimer->num_vps; i++) {
81             gic_vptimer_update(gictimer, i, now);
82         }
83     }
84 }
85 
86 uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
87                                       uint32_t vp_index)
88 {
89     return gictimer->vptimers[vp_index].comparelo;
90 }
91 
92 void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer,
93                                     uint32_t vp_index, uint64_t compare)
94 {
95     gictimer->vptimers[vp_index].comparelo = (uint32_t) compare;
96     gic_vptimer_update(gictimer, vp_index,
97                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
98 }
99 
100 uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
101 {
102     return gictimer->countstop;
103 }
104 
105 void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
106 {
107     gictimer->countstop = 0;
108     mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
109 }
110 
111 void mips_gictimer_stop_count(MIPSGICTimerState *gictimer)
112 {
113     int i;
114 
115     gictimer->countstop = 1;
116     /* Store the current value */
117     gictimer->sh_counterlo +=
118         (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
119     for (i = 0; i < gictimer->num_vps; i++) {
120         timer_del(gictimer->vptimers[i].qtimer);
121     }
122 }
123 
124 MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps,
125                                       MIPSGICTimerCB *cb)
126 {
127     int i;
128     MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1);
129     gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps);
130     gictimer->countstop = 1;
131     gictimer->num_vps = nvps;
132     gictimer->opaque = opaque;
133     gictimer->cb = cb;
134     for (i = 0; i < nvps; i++) {
135         gictimer->vptimers[i].gictimer = gictimer;
136         gictimer->vptimers[i].vp_index = i;
137         gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
138                                             &gic_vptimer_cb,
139                                             &gictimer->vptimers[i]);
140     }
141     return gictimer;
142 }
143