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