xref: /openbmc/qemu/hw/timer/a9gtimer.c (revision 6a0acfff)
1 /*
2  * Global peripheral timer block for ARM A9MP
3  *
4  * (C) 2013 Xilinx Inc.
5  *
6  * Written by François LEGAL
7  * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version
12  * 2 of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "qemu/osdep.h"
24 #include "hw/irq.h"
25 #include "hw/timer/a9gtimer.h"
26 #include "qapi/error.h"
27 #include "qemu/timer.h"
28 #include "qemu/bitops.h"
29 #include "qemu/log.h"
30 #include "qemu/module.h"
31 #include "qom/cpu.h"
32 
33 #ifndef A9_GTIMER_ERR_DEBUG
34 #define A9_GTIMER_ERR_DEBUG 0
35 #endif
36 
37 #define DB_PRINT_L(level, ...) do { \
38     if (A9_GTIMER_ERR_DEBUG > (level)) { \
39         fprintf(stderr,  ": %s: ", __func__); \
40         fprintf(stderr, ## __VA_ARGS__); \
41     } \
42 } while (0)
43 
44 #define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__)
45 
46 static inline int a9_gtimer_get_current_cpu(A9GTimerState *s)
47 {
48     if (current_cpu->cpu_index >= s->num_cpu) {
49         hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n",
50                  s->num_cpu, current_cpu->cpu_index);
51     }
52     return current_cpu->cpu_index;
53 }
54 
55 static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s)
56 {
57     uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
58                                   R_CONTROL_PRESCALER_LEN);
59 
60     return (prescale + 1) * 10;
61 }
62 
63 static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
64 {
65     A9GTimerUpdate ret;
66 
67     ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
68     ret.new = s->ref_counter +
69               (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s);
70     return ret;
71 }
72 
73 static void a9_gtimer_update(A9GTimerState *s, bool sync)
74 {
75 
76     A9GTimerUpdate update = a9_gtimer_get_update(s);
77     int i;
78     int64_t next_cdiff = 0;
79 
80     for (i = 0; i < s->num_cpu; ++i) {
81         A9GTimerPerCPU *gtb = &s->per_cpu[i];
82         int64_t cdiff = 0;
83 
84         if ((s->control & R_CONTROL_TIMER_ENABLE) &&
85                 (gtb->control & R_CONTROL_COMP_ENABLE)) {
86             /* R2p0+, where the compare function is >= */
87             if (gtb->compare < update.new) {
88                 DB_PRINT("Compare event happened for CPU %d\n", i);
89                 gtb->status = 1;
90                 if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
91                     uint64_t inc =
92                         QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
93                     DB_PRINT("Auto incrementing timer compare by %"
94                                                         PRId64 "\n", inc);
95                     gtb->compare += inc;
96                 }
97             }
98             cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
99             if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) {
100                 next_cdiff = cdiff;
101             }
102         }
103 
104         qemu_set_irq(gtb->irq,
105                      gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE));
106     }
107 
108     timer_del(s->timer);
109     if (next_cdiff) {
110         DB_PRINT("scheduling qemu_timer to fire again in %"
111                  PRIx64 " cycles\n", next_cdiff);
112         timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s));
113     }
114 
115     if (s->control & R_CONTROL_TIMER_ENABLE) {
116         s->counter = update.new;
117     }
118 
119     if (sync) {
120         s->cpu_ref_time = update.now;
121         s->ref_counter = s->counter;
122     }
123 }
124 
125 static void a9_gtimer_update_no_sync(void *opaque)
126 {
127     A9GTimerState *s = A9_GTIMER(opaque);
128 
129     a9_gtimer_update(s, false);
130 }
131 
132 static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size)
133 {
134     A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
135     A9GTimerState *s = gtb->parent;
136     A9GTimerUpdate update;
137     uint64_t ret = 0;
138     int shift = 0;
139 
140     switch (addr) {
141     case R_COUNTER_HI:
142         shift = 32;
143         /* fallthrough */
144     case R_COUNTER_LO:
145         update = a9_gtimer_get_update(s);
146         ret = extract64(update.new, shift, 32);
147         break;
148     case R_CONTROL:
149         ret = s->control | gtb->control;
150         break;
151     case R_INTERRUPT_STATUS:
152         ret = gtb->status;
153         break;
154     case R_COMPARATOR_HI:
155         shift = 32;
156         /* fallthrough */
157     case R_COMPARATOR_LO:
158         ret = extract64(gtb->compare, shift, 32);
159         break;
160     case R_AUTO_INCREMENT:
161         ret =  gtb->inc;
162         break;
163     default:
164         qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n",
165                       (unsigned)addr);
166         return 0;
167     }
168 
169     DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret);
170     return ret;
171 }
172 
173 static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value,
174                             unsigned size)
175 {
176     A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
177     A9GTimerState *s = gtb->parent;
178     int shift = 0;
179 
180     DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value);
181 
182     switch (addr) {
183     case R_COUNTER_HI:
184         shift = 32;
185         /* fallthrough */
186     case R_COUNTER_LO:
187         /*
188          * Keep it simple - ARM docco explicitly says to disable timer before
189          * modding it, so don't bother trying to do all the difficult on the fly
190          * timer modifications - (if they even work in real hardware??).
191          */
192         if (s->control & R_CONTROL_TIMER_ENABLE) {
193             qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n");
194             return;
195         }
196         s->counter = deposit64(s->counter, shift, 32, value);
197         return;
198     case R_CONTROL:
199         a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC);
200         gtb->control = value & R_CONTROL_BANKED;
201         s->control = value & ~R_CONTROL_BANKED;
202         break;
203     case R_INTERRUPT_STATUS:
204         a9_gtimer_update(s, false);
205         gtb->status &= ~value;
206         break;
207     case R_COMPARATOR_HI:
208         shift = 32;
209         /* fallthrough */
210     case R_COMPARATOR_LO:
211         a9_gtimer_update(s, false);
212         gtb->compare = deposit64(gtb->compare, shift, 32, value);
213         break;
214     case R_AUTO_INCREMENT:
215         gtb->inc = value;
216         return;
217     default:
218         return;
219     }
220 
221     a9_gtimer_update(s, false);
222 }
223 
224 /* Wrapper functions to implement the "read global timer for
225  * the current CPU" memory regions.
226  */
227 static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr,
228                                     unsigned size)
229 {
230     A9GTimerState *s = A9_GTIMER(opaque);
231     int id = a9_gtimer_get_current_cpu(s);
232 
233     /* no \n so concatenates with message from read fn */
234     DB_PRINT("CPU:%d:", id);
235 
236     return a9_gtimer_read(&s->per_cpu[id], addr, size);
237 }
238 
239 static void a9_gtimer_this_write(void *opaque, hwaddr addr,
240                                  uint64_t value, unsigned size)
241 {
242     A9GTimerState *s = A9_GTIMER(opaque);
243     int id = a9_gtimer_get_current_cpu(s);
244 
245     /* no \n so concatenates with message from write fn */
246     DB_PRINT("CPU:%d:", id);
247 
248     a9_gtimer_write(&s->per_cpu[id], addr, value, size);
249 }
250 
251 static const MemoryRegionOps a9_gtimer_this_ops = {
252     .read = a9_gtimer_this_read,
253     .write = a9_gtimer_this_write,
254     .valid = {
255         .min_access_size = 4,
256         .max_access_size = 4,
257     },
258     .endianness = DEVICE_NATIVE_ENDIAN,
259 };
260 
261 static const MemoryRegionOps a9_gtimer_ops = {
262     .read = a9_gtimer_read,
263     .write = a9_gtimer_write,
264     .valid = {
265         .min_access_size = 4,
266         .max_access_size = 4,
267     },
268     .endianness = DEVICE_NATIVE_ENDIAN,
269 };
270 
271 static void a9_gtimer_reset(DeviceState *dev)
272 {
273     A9GTimerState *s = A9_GTIMER(dev);
274     int i;
275 
276     s->counter = 0;
277     s->control = 0;
278 
279     for (i = 0; i < s->num_cpu; i++) {
280         A9GTimerPerCPU *gtb = &s->per_cpu[i];
281 
282         gtb->control = 0;
283         gtb->status = 0;
284         gtb->compare = 0;
285         gtb->inc = 0;
286     }
287     a9_gtimer_update(s, false);
288 }
289 
290 static void a9_gtimer_realize(DeviceState *dev, Error **errp)
291 {
292     A9GTimerState *s = A9_GTIMER(dev);
293     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
294     int i;
295 
296     if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) {
297         error_setg(errp, "%s: num-cpu must be between 1 and %d",
298                    __func__, A9_GTIMER_MAX_CPUS);
299         return;
300     }
301 
302     memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s,
303                           "a9gtimer shared", 0x20);
304     sysbus_init_mmio(sbd, &s->iomem);
305     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s);
306 
307     for (i = 0; i < s->num_cpu; i++) {
308         A9GTimerPerCPU *gtb = &s->per_cpu[i];
309 
310         gtb->parent = s;
311         sysbus_init_irq(sbd, &gtb->irq);
312         memory_region_init_io(&gtb->iomem, OBJECT(dev), &a9_gtimer_ops, gtb,
313                               "a9gtimer per cpu", 0x20);
314         sysbus_init_mmio(sbd, &gtb->iomem);
315     }
316 }
317 
318 static const VMStateDescription vmstate_a9_gtimer_per_cpu = {
319     .name = "arm.cortex-a9-global-timer.percpu",
320     .version_id = 1,
321     .minimum_version_id = 1,
322     .fields = (VMStateField[]) {
323         VMSTATE_UINT32(control, A9GTimerPerCPU),
324         VMSTATE_UINT64(compare, A9GTimerPerCPU),
325         VMSTATE_UINT32(status, A9GTimerPerCPU),
326         VMSTATE_UINT32(inc, A9GTimerPerCPU),
327         VMSTATE_END_OF_LIST()
328     }
329 };
330 
331 static const VMStateDescription vmstate_a9_gtimer = {
332     .name = "arm.cortex-a9-global-timer",
333     .version_id = 1,
334     .minimum_version_id = 1,
335     .fields = (VMStateField[]) {
336         VMSTATE_TIMER_PTR(timer, A9GTimerState),
337         VMSTATE_UINT64(counter, A9GTimerState),
338         VMSTATE_UINT64(ref_counter, A9GTimerState),
339         VMSTATE_UINT64(cpu_ref_time, A9GTimerState),
340         VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu,
341                                      1, vmstate_a9_gtimer_per_cpu,
342                                      A9GTimerPerCPU),
343         VMSTATE_END_OF_LIST()
344     }
345 };
346 
347 static Property a9_gtimer_properties[] = {
348     DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0),
349     DEFINE_PROP_END_OF_LIST()
350 };
351 
352 static void a9_gtimer_class_init(ObjectClass *klass, void *data)
353 {
354     DeviceClass *dc = DEVICE_CLASS(klass);
355 
356     dc->realize = a9_gtimer_realize;
357     dc->vmsd = &vmstate_a9_gtimer;
358     dc->reset = a9_gtimer_reset;
359     dc->props = a9_gtimer_properties;
360 }
361 
362 static const TypeInfo a9_gtimer_info = {
363     .name          = TYPE_A9_GTIMER,
364     .parent        = TYPE_SYS_BUS_DEVICE,
365     .instance_size = sizeof(A9GTimerState),
366     .class_init    = a9_gtimer_class_init,
367 };
368 
369 static void a9_gtimer_register_types(void)
370 {
371     type_register_static(&a9_gtimer_info);
372 }
373 
374 type_init(a9_gtimer_register_types)
375