1 /*
2 * ARMv7M SysTick timer
3 *
4 * Copyright (c) 2006-2007 CodeSourcery.
5 * Written by Paul Brook
6 * Copyright (c) 2017 Linaro Ltd
7 * Written by Peter Maydell
8 *
9 * This code is licensed under the GPL (version 2 or later).
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
systick_set_period_from_clock(SysTickState * s)33 static void systick_set_period_from_clock(SysTickState *s)
34 {
35 /*
36 * Set the ptimer period from whichever clock is selected.
37 * Must be called from within a ptimer transaction block.
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
systick_timer_tick(void * opaque)46 static 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 /* Tell the NVIC to pend the SysTick exception */
55 qemu_irq_pulse(s->irq);
56 }
57 if (ptimer_get_limit(s->ptimer) == 0) {
58 /*
59 * Timer expiry with SYST_RVR zero disables the timer
60 * (but doesn't clear SYST_CSR.ENABLE)
61 */
62 ptimer_stop(s->ptimer);
63 }
64 }
65
systick_read(void * opaque,hwaddr addr,uint64_t * data,unsigned size,MemTxAttrs attrs)66 static 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 /* Generate BusFault for unprivileged accesses */
74 return MEMTX_ERROR;
75 }
76
77 switch (addr) {
78 case 0x0: /* SysTick Control and Status. */
79 val = s->control;
80 s->control &= ~SYSTICK_COUNTFLAG;
81 break;
82 case 0x4: /* SysTick Reload Value. */
83 val = ptimer_get_limit(s->ptimer);
84 break;
85 case 0x8: /* SysTick Current Value. */
86 val = ptimer_get_count(s->ptimer);
87 break;
88 case 0xc: /* SysTick Calibration Value. */
89 /*
90 * In real hardware it is possible to make this register report
91 * a different value from what the reference clock is actually
92 * running at. We don't model that (which usually happens due
93 * to integration errors in the real hardware) and instead always
94 * report the theoretical correct value as described in the
95 * knowledgebase article at
96 * https://developer.arm.com/documentation/ka001325/latest
97 * If necessary, we could implement an extra QOM property on this
98 * device to force the STCALIB value to something different from
99 * the "correct" value.
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 /* report that tick count does not yield exactly 10ms */
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
systick_write(void * opaque,hwaddr addr,uint64_t value,unsigned size,MemTxAttrs attrs)124 static 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 /* Generate BusFault for unprivileged accesses */
132 return MEMTX_ERROR;
133 }
134
135 trace_systick_write(addr, value, size);
136
137 switch (addr) {
138 case 0x0: /* SysTick Control and Status. */
139 {
140 uint32_t oldval;
141
142 if (!clock_has_source(s->refclk)) {
143 /* This bit is always 1 if there is no external refclk */
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: /* SysTick Reload Value. */
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: /* SysTick Current Value. */
172 /*
173 * Writing any value clears SYST_CVR to zero and clears
174 * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
175 * on the next clock edge unless SYST_RVR is zero.
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
192 static 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
systick_reset(DeviceState * dev)200 static 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 /* This bit is always 1 if there is no external refclk */
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
systick_cpuclk_update(void * opaque,ClockEvent event)217 static void systick_cpuclk_update(void *opaque, ClockEvent event)
218 {
219 SysTickState *s = SYSTICK(opaque);
220
221 if (!(s->control & SYSTICK_CLKSOURCE)) {
222 /* currently using refclk, we can ignore cpuclk changes */
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
systick_refclk_update(void * opaque,ClockEvent event)230 static void systick_refclk_update(void *opaque, ClockEvent event)
231 {
232 SysTickState *s = SYSTICK(opaque);
233
234 if (s->control & SYSTICK_CLKSOURCE) {
235 /* currently using cpuclk, we can ignore refclk changes */
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
systick_instance_init(Object * obj)243 static 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
systick_realize(DeviceState * dev,Error ** errp)258 static 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 /* It's OK not to connect the refclk */
272 }
273
274 static const VMStateDescription vmstate_systick = {
275 .name = "armv7m_systick",
276 .version_id = 3,
277 .minimum_version_id = 3,
278 .fields = (const 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
systick_class_init(ObjectClass * klass,void * data)288 static 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
297 static 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
armv7m_systick_register_types(void)305 static void armv7m_systick_register_types(void)
306 {
307 type_register_static(&armv7m_systick_info);
308 }
309
310 type_init(armv7m_systick_register_types)
311