xref: /openbmc/qemu/hw/timer/armv7m_systick.c (revision 990d2c18)
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 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 
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 
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 
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 
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 
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 
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 
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 
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 
288 static void systick_class_init(ObjectClass *klass, void *data)
289 {
290     DeviceClass *dc = DEVICE_CLASS(klass);
291 
292     dc->vmsd = &vmstate_systick;
293     device_class_set_legacy_reset(dc, 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 
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