]> git.proxmox.com Git - mirror_qemu.git/blame - hw/timer/mips_gictimer.c
Merge remote-tracking branch 'remotes/famz/tags/staging-pull-request' into staging
[mirror_qemu.git] / hw / timer / mips_gictimer.c
CommitLineData
40514051
YK
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
eb90ab94
PB
17uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
18{
19 return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
20}
21
40514051
YK
22static 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
35static 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
46static 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
54uint32_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
73void 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
91uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
92 uint32_t vp_index)
93{
94 return gictimer->vptimers[vp_index].comparelo;
95}
96
97void 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
105uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
106{
107 return gictimer->countstop;
108}
109
110void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
111{
112 gictimer->countstop = 0;
113 mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
114}
115
116void 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
129MIPSGICTimerState *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}