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