xref: /openbmc/qemu/hw/timer/mips_gictimer.c (revision dc5bd18f)
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 uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
18 {
19     return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
20 }
21 
22 static void gic_vptimer_update(MIPSGICTimerState *gictimer,
23                                    uint32_t vp_index, uint64_t now)
24 {
25     uint64_t next;
26     uint32_t wait;
27 
28     wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo -
29            (uint32_t)(now / TIMER_PERIOD);
30     next = now + (uint64_t)wait * TIMER_PERIOD;
31 
32     timer_mod(gictimer->vptimers[vp_index].qtimer, next);
33 }
34 
35 static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index,
36                                uint64_t now)
37 {
38     if (gictimer->countstop) {
39         /* timer stopped */
40         return;
41     }
42     gictimer->cb(gictimer->opaque, vp_index);
43     gic_vptimer_update(gictimer, vp_index, now);
44 }
45 
46 static void gic_vptimer_cb(void *opaque)
47 {
48     MIPSGICTimerVPState *vptimer = opaque;
49     MIPSGICTimerState *gictimer = vptimer->gictimer;
50     gic_vptimer_expire(gictimer, vptimer->vp_index,
51                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
52 }
53 
54 uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer)
55 {
56     int i;
57     if (gictimer->countstop) {
58         return gictimer->sh_counterlo;
59     } else {
60         uint64_t now;
61         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
62         for (i = 0; i < gictimer->num_vps; i++) {
63             if (timer_pending(gictimer->vptimers[i].qtimer)
64                 && timer_expired(gictimer->vptimers[i].qtimer, now)) {
65                 /* The timer has already expired.  */
66                 gic_vptimer_expire(gictimer, i, now);
67             }
68         }
69         return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
70     }
71 }
72 
73 void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count)
74 {
75     int i;
76     uint64_t now;
77 
78     if (gictimer->countstop || !gictimer->vptimers[0].qtimer) {
79         gictimer->sh_counterlo = count;
80     } else {
81         /* Store new count register */
82         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
83         gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD);
84         /* Update timer timer */
85         for (i = 0; i < gictimer->num_vps; i++) {
86             gic_vptimer_update(gictimer, i, now);
87         }
88     }
89 }
90 
91 uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
92                                       uint32_t vp_index)
93 {
94     return gictimer->vptimers[vp_index].comparelo;
95 }
96 
97 void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer,
98                                     uint32_t vp_index, uint64_t compare)
99 {
100     gictimer->vptimers[vp_index].comparelo = (uint32_t) compare;
101     gic_vptimer_update(gictimer, vp_index,
102                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
103 }
104 
105 uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
106 {
107     return gictimer->countstop;
108 }
109 
110 void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
111 {
112     gictimer->countstop = 0;
113     mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
114 }
115 
116 void mips_gictimer_stop_count(MIPSGICTimerState *gictimer)
117 {
118     int i;
119 
120     gictimer->countstop = 1;
121     /* Store the current value */
122     gictimer->sh_counterlo +=
123         (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
124     for (i = 0; i < gictimer->num_vps; i++) {
125         timer_del(gictimer->vptimers[i].qtimer);
126     }
127 }
128 
129 MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps,
130                                       MIPSGICTimerCB *cb)
131 {
132     int i;
133     MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1);
134     gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps);
135     gictimer->countstop = 1;
136     gictimer->num_vps = nvps;
137     gictimer->opaque = opaque;
138     gictimer->cb = cb;
139     for (i = 0; i < nvps; i++) {
140         gictimer->vptimers[i].gictimer = gictimer;
141         gictimer->vptimers[i].vp_index = i;
142         gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
143                                             &gic_vptimer_cb,
144                                             &gictimer->vptimers[i]);
145     }
146     return gictimer;
147 }
148