1
2
3
4
5
6
7
8
9
10
11
12#include "qemu/osdep.h"
13#include "hw/timer/armv7m_systick.h"
14#include "hw/sysbus.h"
15#include "qemu/timer.h"
16#include "qemu/log.h"
17#include "qemu/module.h"
18#include "trace.h"
19
20
21#define SYSTICK_SCALE 1000ULL
22
23#define SYSTICK_ENABLE (1 << 0)
24#define SYSTICK_TICKINT (1 << 1)
25#define SYSTICK_CLKSOURCE (1 << 2)
26#define SYSTICK_COUNTFLAG (1 << 16)
27
28int system_clock_scale;
29
30
31static inline int64_t systick_scale(SysTickState *s)
32{
33 if (s->control & SYSTICK_CLKSOURCE) {
34 return system_clock_scale;
35 } else {
36 return 1000;
37 }
38}
39
40static void systick_reload(SysTickState *s, int reset)
41{
42
43
44
45
46
47 trace_systick_reload();
48
49 if ((s->control & SYSTICK_ENABLE) == 0) {
50 return;
51 }
52
53 if (reset) {
54 s->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
55 }
56 s->tick += (s->reload + 1) * systick_scale(s);
57 timer_mod(s->timer, s->tick);
58}
59
60static void systick_timer_tick(void *opaque)
61{
62 SysTickState *s = (SysTickState *)opaque;
63
64 trace_systick_timer_tick();
65
66 s->control |= SYSTICK_COUNTFLAG;
67 if (s->control & SYSTICK_TICKINT) {
68
69 qemu_irq_pulse(s->irq);
70 }
71 if (s->reload == 0) {
72 s->control &= ~SYSTICK_ENABLE;
73 } else {
74 systick_reload(s, 0);
75 }
76}
77
78static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
79 unsigned size, MemTxAttrs attrs)
80{
81 SysTickState *s = opaque;
82 uint32_t val;
83
84 if (attrs.user) {
85
86 return MEMTX_ERROR;
87 }
88
89 switch (addr) {
90 case 0x0:
91 val = s->control;
92 s->control &= ~SYSTICK_COUNTFLAG;
93 break;
94 case 0x4:
95 val = s->reload;
96 break;
97 case 0x8:
98 {
99 int64_t t;
100
101 if ((s->control & SYSTICK_ENABLE) == 0) {
102 val = 0;
103 break;
104 }
105 t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
106 if (t >= s->tick) {
107 val = 0;
108 break;
109 }
110 val = ((s->tick - (t + 1)) / systick_scale(s)) + 1;
111
112
113
114 if (val > s->reload) {
115 val = 0;
116 }
117 break;
118 }
119 case 0xc:
120 val = 10000;
121 break;
122 default:
123 val = 0;
124 qemu_log_mask(LOG_GUEST_ERROR,
125 "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
126 break;
127 }
128
129 trace_systick_read(addr, val, size);
130 *data = val;
131 return MEMTX_OK;
132}
133
134static MemTxResult systick_write(void *opaque, hwaddr addr,
135 uint64_t value, unsigned size,
136 MemTxAttrs attrs)
137{
138 SysTickState *s = opaque;
139
140 if (attrs.user) {
141
142 return MEMTX_ERROR;
143 }
144
145 trace_systick_write(addr, value, size);
146
147 switch (addr) {
148 case 0x0:
149 {
150 uint32_t oldval = s->control;
151
152 s->control &= 0xfffffff8;
153 s->control |= value & 7;
154 if ((oldval ^ value) & SYSTICK_ENABLE) {
155 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
156 if (value & SYSTICK_ENABLE) {
157 if (s->tick) {
158 s->tick += now;
159 timer_mod(s->timer, s->tick);
160 } else {
161 systick_reload(s, 1);
162 }
163 } else {
164 timer_del(s->timer);
165 s->tick -= now;
166 if (s->tick < 0) {
167 s->tick = 0;
168 }
169 }
170 } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
171
172
173 systick_reload(s, 1);
174 }
175 break;
176 }
177 case 0x4:
178 s->reload = value;
179 break;
180 case 0x8:
181 systick_reload(s, 1);
182 s->control &= ~SYSTICK_COUNTFLAG;
183 break;
184 default:
185 qemu_log_mask(LOG_GUEST_ERROR,
186 "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
187 }
188 return MEMTX_OK;
189}
190
191static const MemoryRegionOps systick_ops = {
192 .read_with_attrs = systick_read,
193 .write_with_attrs = systick_write,
194 .endianness = DEVICE_NATIVE_ENDIAN,
195 .valid.min_access_size = 4,
196 .valid.max_access_size = 4,
197};
198
199static void systick_reset(DeviceState *dev)
200{
201 SysTickState *s = SYSTICK(dev);
202
203 s->control = 0;
204 s->reload = 0;
205 s->tick = 0;
206 timer_del(s->timer);
207}
208
209static void systick_instance_init(Object *obj)
210{
211 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
212 SysTickState *s = SYSTICK(obj);
213
214 memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
215 sysbus_init_mmio(sbd, &s->iomem);
216 sysbus_init_irq(sbd, &s->irq);
217 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s);
218}
219
220static const VMStateDescription vmstate_systick = {
221 .name = "armv7m_systick",
222 .version_id = 1,
223 .minimum_version_id = 1,
224 .fields = (VMStateField[]) {
225 VMSTATE_UINT32(control, SysTickState),
226 VMSTATE_UINT32(reload, SysTickState),
227 VMSTATE_INT64(tick, SysTickState),
228 VMSTATE_TIMER_PTR(timer, SysTickState),
229 VMSTATE_END_OF_LIST()
230 }
231};
232
233static void systick_class_init(ObjectClass *klass, void *data)
234{
235 DeviceClass *dc = DEVICE_CLASS(klass);
236
237 dc->vmsd = &vmstate_systick;
238 dc->reset = systick_reset;
239}
240
241static const TypeInfo armv7m_systick_info = {
242 .name = TYPE_SYSTICK,
243 .parent = TYPE_SYS_BUS_DEVICE,
244 .instance_init = systick_instance_init,
245 .instance_size = sizeof(SysTickState),
246 .class_init = systick_class_init,
247};
248
249static void armv7m_systick_register_types(void)
250{
251 type_register_static(&armv7m_systick_info);
252}
253
254type_init(armv7m_systick_register_types)
255