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