qapi: Improve specificity of type/member descriptions
[qemu/armbru.git] / hw / timer / armv7m_systick.c
blob5dfe39afe36d9ca9de4c6f5e0e260d0a56232956
1 /*
2 * ARMv7M SysTick timer
4 * Copyright (c) 2006-2007 CodeSourcery.
5 * Written by Paul Brook
6 * Copyright (c) 2017 Linaro Ltd
7 * Written by Peter Maydell
9 * This code is licensed under the GPL (version 2 or later).
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"
24 #define SYSTICK_ENABLE (1 << 0)
25 #define SYSTICK_TICKINT (1 << 1)
26 #define SYSTICK_CLKSOURCE (1 << 2)
27 #define SYSTICK_COUNTFLAG (1 << 16)
29 #define SYSCALIB_NOREF (1U << 31)
30 #define SYSCALIB_SKEW (1U << 30)
31 #define SYSCALIB_TENMS ((1U << 24) - 1)
33 static void systick_set_period_from_clock(SysTickState *s)
36 * Set the ptimer period from whichever clock is selected.
37 * Must be called from within a ptimer transaction block.
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);
46 static void systick_timer_tick(void *opaque)
48 SysTickState *s = (SysTickState *)opaque;
50 trace_systick_timer_tick();
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);
57 if (ptimer_get_limit(s->ptimer) == 0) {
59 * Timer expiry with SYST_RVR zero disables the timer
60 * (but doesn't clear SYST_CSR.ENABLE)
62 ptimer_stop(s->ptimer);
66 static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
67 unsigned size, MemTxAttrs attrs)
69 SysTickState *s = opaque;
70 uint32_t val;
72 if (attrs.user) {
73 /* Generate BusFault for unprivileged accesses */
74 return MEMTX_ERROR;
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. */
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.
101 if (!clock_has_source(s->refclk)) {
102 val = SYSCALIB_NOREF;
103 break;
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;
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;
119 trace_systick_read(addr, val, size);
120 *data = val;
121 return MEMTX_OK;
124 static MemTxResult systick_write(void *opaque, hwaddr addr,
125 uint64_t value, unsigned size,
126 MemTxAttrs attrs)
128 SysTickState *s = opaque;
130 if (attrs.user) {
131 /* Generate BusFault for unprivileged accesses */
132 return MEMTX_ERROR;
135 trace_systick_write(addr, value, size);
137 switch (addr) {
138 case 0x0: /* SysTick Control and Status. */
140 uint32_t oldval;
142 if (!clock_has_source(s->refclk)) {
143 /* This bit is always 1 if there is no external refclk */
144 value |= SYSTICK_CLKSOURCE;
147 ptimer_transaction_begin(s->ptimer);
148 oldval = s->control;
149 s->control &= 0xfffffff8;
150 s->control |= value & 7;
152 if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
153 systick_set_period_from_clock(s);
156 if ((oldval ^ value) & SYSTICK_ENABLE) {
157 if (value & SYSTICK_ENABLE) {
158 ptimer_run(s->ptimer, 0);
159 } else {
160 ptimer_stop(s->ptimer);
163 ptimer_transaction_commit(s->ptimer);
164 break;
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. */
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.
177 ptimer_transaction_begin(s->ptimer);
178 if (ptimer_get_limit(s->ptimer) == 0) {
179 ptimer_stop(s->ptimer);
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);
189 return MEMTX_OK;
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,
200 static void systick_reset(DeviceState *dev)
202 SysTickState *s = SYSTICK(dev);
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;
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);
217 static void systick_cpuclk_update(void *opaque, ClockEvent event)
219 SysTickState *s = SYSTICK(opaque);
221 if (!(s->control & SYSTICK_CLKSOURCE)) {
222 /* currently using refclk, we can ignore cpuclk changes */
225 ptimer_transaction_begin(s->ptimer);
226 ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
227 ptimer_transaction_commit(s->ptimer);
230 static void systick_refclk_update(void *opaque, ClockEvent event)
232 SysTickState *s = SYSTICK(opaque);
234 if (s->control & SYSTICK_CLKSOURCE) {
235 /* currently using cpuclk, we can ignore refclk changes */
238 ptimer_transaction_begin(s->ptimer);
239 ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
240 ptimer_transaction_commit(s->ptimer);
243 static void systick_instance_init(Object *obj)
245 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
246 SysTickState *s = SYSTICK(obj);
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);
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);
258 static void systick_realize(DeviceState *dev, Error **errp)
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);
267 if (!clock_has_source(s->cpuclk)) {
268 error_setg(errp, "systick: cpuclk must be connected");
269 return;
271 /* It's OK not to connect the refclk */
274 static 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()
288 static void systick_class_init(ObjectClass *klass, void *data)
290 DeviceClass *dc = DEVICE_CLASS(klass);
292 dc->vmsd = &vmstate_systick;
293 dc->reset = systick_reset;
294 dc->realize = systick_realize;
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,
305 static void armv7m_systick_register_types(void)
307 type_register_static(&armv7m_systick_info);
310 type_init(armv7m_systick_register_types)