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