xref: /openbmc/qemu/hw/timer/armv7m_systick.c (revision 284d697c)
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 "qemu/timer.h"
18 #include "qemu/log.h"
19 #include "qemu/module.h"
20 #include "trace.h"
21 
22 /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
23 #define SYSTICK_SCALE 1000ULL
24 
25 #define SYSTICK_ENABLE    (1 << 0)
26 #define SYSTICK_TICKINT   (1 << 1)
27 #define SYSTICK_CLKSOURCE (1 << 2)
28 #define SYSTICK_COUNTFLAG (1 << 16)
29 
30 int system_clock_scale;
31 
32 /* Conversion factor from qemu timer to SysTick frequencies.  */
33 static inline int64_t systick_scale(SysTickState *s)
34 {
35     if (s->control & SYSTICK_CLKSOURCE) {
36         return system_clock_scale;
37     } else {
38         return 1000;
39     }
40 }
41 
42 static void systick_timer_tick(void *opaque)
43 {
44     SysTickState *s = (SysTickState *)opaque;
45 
46     trace_systick_timer_tick();
47 
48     s->control |= SYSTICK_COUNTFLAG;
49     if (s->control & SYSTICK_TICKINT) {
50         /* Tell the NVIC to pend the SysTick exception */
51         qemu_irq_pulse(s->irq);
52     }
53     if (ptimer_get_limit(s->ptimer) == 0) {
54         /*
55          * Timer expiry with SYST_RVR zero disables the timer
56          * (but doesn't clear SYST_CSR.ENABLE)
57          */
58         ptimer_stop(s->ptimer);
59     }
60 }
61 
62 static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
63                                 unsigned size, MemTxAttrs attrs)
64 {
65     SysTickState *s = opaque;
66     uint32_t val;
67 
68     if (attrs.user) {
69         /* Generate BusFault for unprivileged accesses */
70         return MEMTX_ERROR;
71     }
72 
73     switch (addr) {
74     case 0x0: /* SysTick Control and Status.  */
75         val = s->control;
76         s->control &= ~SYSTICK_COUNTFLAG;
77         break;
78     case 0x4: /* SysTick Reload Value.  */
79         val = ptimer_get_limit(s->ptimer);
80         break;
81     case 0x8: /* SysTick Current Value.  */
82         val = ptimer_get_count(s->ptimer);
83         break;
84     case 0xc: /* SysTick Calibration Value.  */
85         val = 10000;
86         break;
87     default:
88         val = 0;
89         qemu_log_mask(LOG_GUEST_ERROR,
90                       "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
91         break;
92     }
93 
94     trace_systick_read(addr, val, size);
95     *data = val;
96     return MEMTX_OK;
97 }
98 
99 static MemTxResult systick_write(void *opaque, hwaddr addr,
100                                  uint64_t value, unsigned size,
101                                  MemTxAttrs attrs)
102 {
103     SysTickState *s = opaque;
104 
105     if (attrs.user) {
106         /* Generate BusFault for unprivileged accesses */
107         return MEMTX_ERROR;
108     }
109 
110     trace_systick_write(addr, value, size);
111 
112     switch (addr) {
113     case 0x0: /* SysTick Control and Status.  */
114     {
115         uint32_t oldval;
116 
117         ptimer_transaction_begin(s->ptimer);
118         oldval = s->control;
119         s->control &= 0xfffffff8;
120         s->control |= value & 7;
121 
122         if ((oldval ^ value) & SYSTICK_ENABLE) {
123             if (value & SYSTICK_ENABLE) {
124                 /*
125                  * Always reload the period in case board code has
126                  * changed system_clock_scale. If we ever replace that
127                  * global with a more sensible API then we might be able
128                  * to set the period only when it actually changes.
129                  */
130                 ptimer_set_period(s->ptimer, systick_scale(s));
131                 ptimer_run(s->ptimer, 0);
132             } else {
133                 ptimer_stop(s->ptimer);
134             }
135         } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
136             ptimer_set_period(s->ptimer, systick_scale(s));
137         }
138         ptimer_transaction_commit(s->ptimer);
139         break;
140     }
141     case 0x4: /* SysTick Reload Value.  */
142         ptimer_transaction_begin(s->ptimer);
143         ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
144         ptimer_transaction_commit(s->ptimer);
145         break;
146     case 0x8: /* SysTick Current Value. */
147         /*
148          * Writing any value clears SYST_CVR to zero and clears
149          * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
150          * on the next clock edge unless SYST_RVR is zero.
151          */
152         ptimer_transaction_begin(s->ptimer);
153         if (ptimer_get_limit(s->ptimer) == 0) {
154             ptimer_stop(s->ptimer);
155         }
156         ptimer_set_count(s->ptimer, 0);
157         s->control &= ~SYSTICK_COUNTFLAG;
158         ptimer_transaction_commit(s->ptimer);
159         break;
160     default:
161         qemu_log_mask(LOG_GUEST_ERROR,
162                       "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
163     }
164     return MEMTX_OK;
165 }
166 
167 static const MemoryRegionOps systick_ops = {
168     .read_with_attrs = systick_read,
169     .write_with_attrs = systick_write,
170     .endianness = DEVICE_NATIVE_ENDIAN,
171     .valid.min_access_size = 4,
172     .valid.max_access_size = 4,
173 };
174 
175 static void systick_reset(DeviceState *dev)
176 {
177     SysTickState *s = SYSTICK(dev);
178 
179     /*
180      * Forgetting to set system_clock_scale is always a board code
181      * bug. We can't check this earlier because for some boards
182      * (like stellaris) it is not yet configured at the point where
183      * the systick device is realized.
184      */
185     assert(system_clock_scale != 0);
186 
187     ptimer_transaction_begin(s->ptimer);
188     s->control = 0;
189     ptimer_stop(s->ptimer);
190     ptimer_set_count(s->ptimer, 0);
191     ptimer_set_limit(s->ptimer, 0, 0);
192     ptimer_set_period(s->ptimer, systick_scale(s));
193     ptimer_transaction_commit(s->ptimer);
194 }
195 
196 static void systick_instance_init(Object *obj)
197 {
198     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
199     SysTickState *s = SYSTICK(obj);
200 
201     memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
202     sysbus_init_mmio(sbd, &s->iomem);
203     sysbus_init_irq(sbd, &s->irq);
204 }
205 
206 static void systick_realize(DeviceState *dev, Error **errp)
207 {
208     SysTickState *s = SYSTICK(dev);
209     s->ptimer = ptimer_init(systick_timer_tick, s,
210                             PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
211                             PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
212                             PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
213                             PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
214 }
215 
216 static const VMStateDescription vmstate_systick = {
217     .name = "armv7m_systick",
218     .version_id = 2,
219     .minimum_version_id = 2,
220     .fields = (VMStateField[]) {
221         VMSTATE_UINT32(control, SysTickState),
222         VMSTATE_INT64(tick, SysTickState),
223         VMSTATE_PTIMER(ptimer, SysTickState),
224         VMSTATE_END_OF_LIST()
225     }
226 };
227 
228 static void systick_class_init(ObjectClass *klass, void *data)
229 {
230     DeviceClass *dc = DEVICE_CLASS(klass);
231 
232     dc->vmsd = &vmstate_systick;
233     dc->reset = systick_reset;
234     dc->realize = systick_realize;
235 }
236 
237 static const TypeInfo armv7m_systick_info = {
238     .name = TYPE_SYSTICK,
239     .parent = TYPE_SYS_BUS_DEVICE,
240     .instance_init = systick_instance_init,
241     .instance_size = sizeof(SysTickState),
242     .class_init = systick_class_init,
243 };
244 
245 static void armv7m_systick_register_types(void)
246 {
247     type_register_static(&armv7m_systick_info);
248 }
249 
250 type_init(armv7m_systick_register_types)
251