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