xref: /openbmc/qemu/hw/timer/armv7m_systick.c (revision a40e10f1)
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 
33 int system_clock_scale;
34 
35 static void systick_set_period_from_clock(SysTickState *s)
36 {
37     /*
38      * Set the ptimer period from whichever clock is selected.
39      * Must be called from within a ptimer transaction block.
40      */
41     if (s->control & SYSTICK_CLKSOURCE) {
42         ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
43     } else {
44         ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
45     }
46 }
47 
48 static void systick_timer_tick(void *opaque)
49 {
50     SysTickState *s = (SysTickState *)opaque;
51 
52     trace_systick_timer_tick();
53 
54     s->control |= SYSTICK_COUNTFLAG;
55     if (s->control & SYSTICK_TICKINT) {
56         /* Tell the NVIC to pend the SysTick exception */
57         qemu_irq_pulse(s->irq);
58     }
59     if (ptimer_get_limit(s->ptimer) == 0) {
60         /*
61          * Timer expiry with SYST_RVR zero disables the timer
62          * (but doesn't clear SYST_CSR.ENABLE)
63          */
64         ptimer_stop(s->ptimer);
65     }
66 }
67 
68 static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
69                                 unsigned size, MemTxAttrs attrs)
70 {
71     SysTickState *s = opaque;
72     uint32_t val;
73 
74     if (attrs.user) {
75         /* Generate BusFault for unprivileged accesses */
76         return MEMTX_ERROR;
77     }
78 
79     switch (addr) {
80     case 0x0: /* SysTick Control and Status.  */
81         val = s->control;
82         s->control &= ~SYSTICK_COUNTFLAG;
83         break;
84     case 0x4: /* SysTick Reload Value.  */
85         val = ptimer_get_limit(s->ptimer);
86         break;
87     case 0x8: /* SysTick Current Value.  */
88         val = ptimer_get_count(s->ptimer);
89         break;
90     case 0xc: /* SysTick Calibration Value.  */
91         /*
92          * In real hardware it is possible to make this register report
93          * a different value from what the reference clock is actually
94          * running at. We don't model that (which usually happens due
95          * to integration errors in the real hardware) and instead always
96          * report the theoretical correct value as described in the
97          * knowledgebase article at
98          * https://developer.arm.com/documentation/ka001325/latest
99          * If necessary, we could implement an extra QOM property on this
100          * device to force the STCALIB value to something different from
101          * the "correct" value.
102          */
103         if (!clock_has_source(s->refclk)) {
104             val = SYSCALIB_NOREF;
105             break;
106         }
107         val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1;
108         val &= SYSCALIB_TENMS;
109         if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) {
110             /* report that tick count does not yield exactly 10ms */
111             val |= SYSCALIB_SKEW;
112         }
113         break;
114     default:
115         val = 0;
116         qemu_log_mask(LOG_GUEST_ERROR,
117                       "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
118         break;
119     }
120 
121     trace_systick_read(addr, val, size);
122     *data = val;
123     return MEMTX_OK;
124 }
125 
126 static MemTxResult systick_write(void *opaque, hwaddr addr,
127                                  uint64_t value, unsigned size,
128                                  MemTxAttrs attrs)
129 {
130     SysTickState *s = opaque;
131 
132     if (attrs.user) {
133         /* Generate BusFault for unprivileged accesses */
134         return MEMTX_ERROR;
135     }
136 
137     trace_systick_write(addr, value, size);
138 
139     switch (addr) {
140     case 0x0: /* SysTick Control and Status.  */
141     {
142         uint32_t oldval;
143 
144         if (!clock_has_source(s->refclk)) {
145             /* This bit is always 1 if there is no external refclk */
146             value |= SYSTICK_CLKSOURCE;
147         }
148 
149         ptimer_transaction_begin(s->ptimer);
150         oldval = s->control;
151         s->control &= 0xfffffff8;
152         s->control |= value & 7;
153 
154         if ((oldval ^ value) & SYSTICK_ENABLE) {
155             if (value & SYSTICK_ENABLE) {
156                 ptimer_run(s->ptimer, 0);
157             } else {
158                 ptimer_stop(s->ptimer);
159             }
160         }
161 
162         if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
163             systick_set_period_from_clock(s);
164         }
165         ptimer_transaction_commit(s->ptimer);
166         break;
167     }
168     case 0x4: /* SysTick Reload Value.  */
169         ptimer_transaction_begin(s->ptimer);
170         ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
171         ptimer_transaction_commit(s->ptimer);
172         break;
173     case 0x8: /* SysTick Current Value. */
174         /*
175          * Writing any value clears SYST_CVR to zero and clears
176          * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
177          * on the next clock edge unless SYST_RVR is zero.
178          */
179         ptimer_transaction_begin(s->ptimer);
180         if (ptimer_get_limit(s->ptimer) == 0) {
181             ptimer_stop(s->ptimer);
182         }
183         ptimer_set_count(s->ptimer, 0);
184         s->control &= ~SYSTICK_COUNTFLAG;
185         ptimer_transaction_commit(s->ptimer);
186         break;
187     default:
188         qemu_log_mask(LOG_GUEST_ERROR,
189                       "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
190     }
191     return MEMTX_OK;
192 }
193 
194 static const MemoryRegionOps systick_ops = {
195     .read_with_attrs = systick_read,
196     .write_with_attrs = systick_write,
197     .endianness = DEVICE_NATIVE_ENDIAN,
198     .valid.min_access_size = 4,
199     .valid.max_access_size = 4,
200 };
201 
202 static void systick_reset(DeviceState *dev)
203 {
204     SysTickState *s = SYSTICK(dev);
205 
206     ptimer_transaction_begin(s->ptimer);
207     s->control = 0;
208     if (!clock_has_source(s->refclk)) {
209         /* This bit is always 1 if there is no external refclk */
210         s->control |= SYSTICK_CLKSOURCE;
211     }
212     ptimer_stop(s->ptimer);
213     ptimer_set_count(s->ptimer, 0);
214     ptimer_set_limit(s->ptimer, 0, 0);
215     systick_set_period_from_clock(s);
216     ptimer_transaction_commit(s->ptimer);
217 }
218 
219 static void systick_cpuclk_update(void *opaque, ClockEvent event)
220 {
221     SysTickState *s = SYSTICK(opaque);
222 
223     if (!(s->control & SYSTICK_CLKSOURCE)) {
224         /* currently using refclk, we can ignore cpuclk changes */
225     }
226 
227     ptimer_transaction_begin(s->ptimer);
228     ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
229     ptimer_transaction_commit(s->ptimer);
230 }
231 
232 static void systick_refclk_update(void *opaque, ClockEvent event)
233 {
234     SysTickState *s = SYSTICK(opaque);
235 
236     if (s->control & SYSTICK_CLKSOURCE) {
237         /* currently using cpuclk, we can ignore refclk changes */
238     }
239 
240     ptimer_transaction_begin(s->ptimer);
241     ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
242     ptimer_transaction_commit(s->ptimer);
243 }
244 
245 static void systick_instance_init(Object *obj)
246 {
247     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
248     SysTickState *s = SYSTICK(obj);
249 
250     memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
251     sysbus_init_mmio(sbd, &s->iomem);
252     sysbus_init_irq(sbd, &s->irq);
253 
254     s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
255                                    systick_refclk_update, s, ClockUpdate);
256     s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk",
257                                    systick_cpuclk_update, s, ClockUpdate);
258 }
259 
260 static void systick_realize(DeviceState *dev, Error **errp)
261 {
262     SysTickState *s = SYSTICK(dev);
263     s->ptimer = ptimer_init(systick_timer_tick, s,
264                             PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
265                             PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
266                             PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
267                             PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
268 
269     if (!clock_has_source(s->cpuclk)) {
270         error_setg(errp, "systick: cpuclk must be connected");
271         return;
272     }
273     /* It's OK not to connect the refclk */
274 }
275 
276 static const VMStateDescription vmstate_systick = {
277     .name = "armv7m_systick",
278     .version_id = 3,
279     .minimum_version_id = 3,
280     .fields = (VMStateField[]) {
281         VMSTATE_CLOCK(refclk, SysTickState),
282         VMSTATE_CLOCK(cpuclk, SysTickState),
283         VMSTATE_UINT32(control, SysTickState),
284         VMSTATE_INT64(tick, SysTickState),
285         VMSTATE_PTIMER(ptimer, SysTickState),
286         VMSTATE_END_OF_LIST()
287     }
288 };
289 
290 static void systick_class_init(ObjectClass *klass, void *data)
291 {
292     DeviceClass *dc = DEVICE_CLASS(klass);
293 
294     dc->vmsd = &vmstate_systick;
295     dc->reset = systick_reset;
296     dc->realize = systick_realize;
297 }
298 
299 static const TypeInfo armv7m_systick_info = {
300     .name = TYPE_SYSTICK,
301     .parent = TYPE_SYS_BUS_DEVICE,
302     .instance_init = systick_instance_init,
303     .instance_size = sizeof(SysTickState),
304     .class_init = systick_class_init,
305 };
306 
307 static void armv7m_systick_register_types(void)
308 {
309     type_register_static(&armv7m_systick_info);
310 }
311 
312 type_init(armv7m_systick_register_types)
313