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