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 "migration/vmstate.h"
15#include "hw/irq.h"
16#include "hw/sysbus.h"
17#include "hw/qdev-clock.h"
18#include "qemu/timer.h"
19#include "qemu/log.h"
20#include "qemu/module.h"
21#include "qapi/error.h"
22#include "trace.h"
23
24#define SYSTICK_ENABLE (1 << 0)
25#define SYSTICK_TICKINT (1 << 1)
26#define SYSTICK_CLKSOURCE (1 << 2)
27#define SYSTICK_COUNTFLAG (1 << 16)
28
29#define SYSCALIB_NOREF (1U << 31)
30#define SYSCALIB_SKEW (1U << 30)
31#define SYSCALIB_TENMS ((1U << 24) - 1)
32
33static void systick_set_period_from_clock(SysTickState *s)
34{
35
36
37
38
39 if (s->control & SYSTICK_CLKSOURCE) {
40 ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
41 } else {
42 ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
43 }
44}
45
46static void systick_timer_tick(void *opaque)
47{
48 SysTickState *s = (SysTickState *)opaque;
49
50 trace_systick_timer_tick();
51
52 s->control |= SYSTICK_COUNTFLAG;
53 if (s->control & SYSTICK_TICKINT) {
54
55 qemu_irq_pulse(s->irq);
56 }
57 if (ptimer_get_limit(s->ptimer) == 0) {
58
59
60
61
62 ptimer_stop(s->ptimer);
63 }
64}
65
66static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
67 unsigned size, MemTxAttrs attrs)
68{
69 SysTickState *s = opaque;
70 uint32_t val;
71
72 if (attrs.user) {
73
74 return MEMTX_ERROR;
75 }
76
77 switch (addr) {
78 case 0x0:
79 val = s->control;
80 s->control &= ~SYSTICK_COUNTFLAG;
81 break;
82 case 0x4:
83 val = ptimer_get_limit(s->ptimer);
84 break;
85 case 0x8:
86 val = ptimer_get_count(s->ptimer);
87 break;
88 case 0xc:
89
90
91
92
93
94
95
96
97
98
99
100
101 if (!clock_has_source(s->refclk)) {
102 val = SYSCALIB_NOREF;
103 break;
104 }
105 val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1;
106 val &= SYSCALIB_TENMS;
107 if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) {
108
109 val |= SYSCALIB_SKEW;
110 }
111 break;
112 default:
113 val = 0;
114 qemu_log_mask(LOG_GUEST_ERROR,
115 "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
116 break;
117 }
118
119 trace_systick_read(addr, val, size);
120 *data = val;
121 return MEMTX_OK;
122}
123
124static MemTxResult systick_write(void *opaque, hwaddr addr,
125 uint64_t value, unsigned size,
126 MemTxAttrs attrs)
127{
128 SysTickState *s = opaque;
129
130 if (attrs.user) {
131
132 return MEMTX_ERROR;
133 }
134
135 trace_systick_write(addr, value, size);
136
137 switch (addr) {
138 case 0x0:
139 {
140 uint32_t oldval;
141
142 if (!clock_has_source(s->refclk)) {
143
144 value |= SYSTICK_CLKSOURCE;
145 }
146
147 ptimer_transaction_begin(s->ptimer);
148 oldval = s->control;
149 s->control &= 0xfffffff8;
150 s->control |= value & 7;
151
152 if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
153 systick_set_period_from_clock(s);
154 }
155
156 if ((oldval ^ value) & SYSTICK_ENABLE) {
157 if (value & SYSTICK_ENABLE) {
158 ptimer_run(s->ptimer, 0);
159 } else {
160 ptimer_stop(s->ptimer);
161 }
162 }
163 ptimer_transaction_commit(s->ptimer);
164 break;
165 }
166 case 0x4:
167 ptimer_transaction_begin(s->ptimer);
168 ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
169 ptimer_transaction_commit(s->ptimer);
170 break;
171 case 0x8:
172
173
174
175
176
177 ptimer_transaction_begin(s->ptimer);
178 if (ptimer_get_limit(s->ptimer) == 0) {
179 ptimer_stop(s->ptimer);
180 }
181 ptimer_set_count(s->ptimer, 0);
182 s->control &= ~SYSTICK_COUNTFLAG;
183 ptimer_transaction_commit(s->ptimer);
184 break;
185 default:
186 qemu_log_mask(LOG_GUEST_ERROR,
187 "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
188 }
189 return MEMTX_OK;
190}
191
192static const MemoryRegionOps systick_ops = {
193 .read_with_attrs = systick_read,
194 .write_with_attrs = systick_write,
195 .endianness = DEVICE_NATIVE_ENDIAN,
196 .valid.min_access_size = 4,
197 .valid.max_access_size = 4,
198};
199
200static void systick_reset(DeviceState *dev)
201{
202 SysTickState *s = SYSTICK(dev);
203
204 ptimer_transaction_begin(s->ptimer);
205 s->control = 0;
206 if (!clock_has_source(s->refclk)) {
207
208 s->control |= SYSTICK_CLKSOURCE;
209 }
210 ptimer_stop(s->ptimer);
211 ptimer_set_count(s->ptimer, 0);
212 ptimer_set_limit(s->ptimer, 0, 0);
213 systick_set_period_from_clock(s);
214 ptimer_transaction_commit(s->ptimer);
215}
216
217static void systick_cpuclk_update(void *opaque, ClockEvent event)
218{
219 SysTickState *s = SYSTICK(opaque);
220
221 if (!(s->control & SYSTICK_CLKSOURCE)) {
222
223 }
224
225 ptimer_transaction_begin(s->ptimer);
226 ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
227 ptimer_transaction_commit(s->ptimer);
228}
229
230static void systick_refclk_update(void *opaque, ClockEvent event)
231{
232 SysTickState *s = SYSTICK(opaque);
233
234 if (s->control & SYSTICK_CLKSOURCE) {
235
236 }
237
238 ptimer_transaction_begin(s->ptimer);
239 ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
240 ptimer_transaction_commit(s->ptimer);
241}
242
243static void systick_instance_init(Object *obj)
244{
245 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
246 SysTickState *s = SYSTICK(obj);
247
248 memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
249 sysbus_init_mmio(sbd, &s->iomem);
250 sysbus_init_irq(sbd, &s->irq);
251
252 s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
253 systick_refclk_update, s, ClockUpdate);
254 s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk",
255 systick_cpuclk_update, s, ClockUpdate);
256}
257
258static void systick_realize(DeviceState *dev, Error **errp)
259{
260 SysTickState *s = SYSTICK(dev);
261 s->ptimer = ptimer_init(systick_timer_tick, s,
262 PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
263 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
264 PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
265 PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
266
267 if (!clock_has_source(s->cpuclk)) {
268 error_setg(errp, "systick: cpuclk must be connected");
269 return;
270 }
271
272}
273
274static const VMStateDescription vmstate_systick = {
275 .name = "armv7m_systick",
276 .version_id = 3,
277 .minimum_version_id = 3,
278 .fields = (VMStateField[]) {
279 VMSTATE_CLOCK(refclk, SysTickState),
280 VMSTATE_CLOCK(cpuclk, SysTickState),
281 VMSTATE_UINT32(control, SysTickState),
282 VMSTATE_INT64(tick, SysTickState),
283 VMSTATE_PTIMER(ptimer, SysTickState),
284 VMSTATE_END_OF_LIST()
285 }
286};
287
288static void systick_class_init(ObjectClass *klass, void *data)
289{
290 DeviceClass *dc = DEVICE_CLASS(klass);
291
292 dc->vmsd = &vmstate_systick;
293 dc->reset = systick_reset;
294 dc->realize = systick_realize;
295}
296
297static const TypeInfo armv7m_systick_info = {
298 .name = TYPE_SYSTICK,
299 .parent = TYPE_SYS_BUS_DEVICE,
300 .instance_init = systick_instance_init,
301 .instance_size = sizeof(SysTickState),
302 .class_init = systick_class_init,
303};
304
305static void armv7m_systick_register_types(void)
306{
307 type_register_static(&armv7m_systick_info);
308}
309
310type_init(armv7m_systick_register_types)
311