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
16uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
17{
18 return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
19}
20
21static 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
34static 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
45static 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
53uint32_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
72void 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
90uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
91 uint32_t vp_index)
92{
93 return gictimer->vptimers[vp_index].comparelo;
94}
95
96void 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
104uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
105{
106 return gictimer->countstop;
107}
108
109void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
110{
111 gictimer->countstop = 0;
112 mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
113}
114
115void 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
128MIPSGICTimerState *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