1 | /* |
2 | * Rasperry Pi 2 emulation ARM control logic module. |
3 | * Copyright (c) 2015, Microsoft |
4 | * Written by Andrew Baumann |
5 | * |
6 | * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade |
7 | * This code is licensed under the GNU GPLv2 and later. |
8 | * |
9 | * At present, only implements interrupt routing, and mailboxes (i.e., |
10 | * not PMU interrupt, or AXI counters). |
11 | * |
12 | * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti |
13 | * |
14 | * Ref: |
15 | * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf |
16 | */ |
17 | |
18 | #include "qemu/osdep.h" |
19 | #include "hw/intc/bcm2836_control.h" |
20 | #include "hw/irq.h" |
21 | #include "migration/vmstate.h" |
22 | #include "qemu/log.h" |
23 | #include "qemu/module.h" |
24 | |
25 | #define REG_GPU_ROUTE 0x0c |
26 | #define REG_LOCALTIMERROUTING 0x24 |
27 | #define REG_LOCALTIMERCONTROL 0x34 |
28 | #define REG_LOCALTIMERACK 0x38 |
29 | #define REG_TIMERCONTROL 0x40 |
30 | #define REG_MBOXCONTROL 0x50 |
31 | #define REG_IRQSRC 0x60 |
32 | #define REG_FIQSRC 0x70 |
33 | #define REG_MBOX0_WR 0x80 |
34 | #define REG_MBOX0_RDCLR 0xc0 |
35 | #define REG_LIMIT 0x100 |
36 | |
37 | #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0) |
38 | #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0) |
39 | |
40 | #define IRQ_CNTPSIRQ 0 |
41 | #define IRQ_CNTPNSIRQ 1 |
42 | #define IRQ_CNTHPIRQ 2 |
43 | #define IRQ_CNTVIRQ 3 |
44 | #define IRQ_MAILBOX0 4 |
45 | #define IRQ_MAILBOX1 5 |
46 | #define IRQ_MAILBOX2 6 |
47 | #define IRQ_MAILBOX3 7 |
48 | #define IRQ_GPU 8 |
49 | #define IRQ_PMU 9 |
50 | #define IRQ_AXI 10 |
51 | #define IRQ_TIMER 11 |
52 | #define IRQ_MAX IRQ_TIMER |
53 | |
54 | #define LOCALTIMER_FREQ 38400000 |
55 | #define LOCALTIMER_INTFLAG (1 << 31) |
56 | #define LOCALTIMER_RELOAD (1 << 30) |
57 | #define LOCALTIMER_INTENABLE (1 << 29) |
58 | #define LOCALTIMER_ENABLE (1 << 28) |
59 | #define LOCALTIMER_VALUE(x) ((x) & 0xfffffff) |
60 | |
61 | static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq, |
62 | uint32_t controlreg, uint8_t controlidx) |
63 | { |
64 | if (FIQ_BIT(controlreg, controlidx)) { |
65 | /* deliver a FIQ */ |
66 | s->fiqsrc[core] |= (uint32_t)1 << irq; |
67 | } else if (IRQ_BIT(controlreg, controlidx)) { |
68 | /* deliver an IRQ */ |
69 | s->irqsrc[core] |= (uint32_t)1 << irq; |
70 | } else { |
71 | /* the interrupt is masked */ |
72 | } |
73 | } |
74 | |
75 | /* Update interrupts. */ |
76 | static void bcm2836_control_update(BCM2836ControlState *s) |
77 | { |
78 | int i, j; |
79 | |
80 | /* reset pending IRQs/FIQs */ |
81 | for (i = 0; i < BCM2836_NCORES; i++) { |
82 | s->irqsrc[i] = s->fiqsrc[i] = 0; |
83 | } |
84 | |
85 | /* apply routing logic, update status regs */ |
86 | if (s->gpu_irq) { |
87 | assert(s->route_gpu_irq < BCM2836_NCORES); |
88 | s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU; |
89 | } |
90 | |
91 | if (s->gpu_fiq) { |
92 | assert(s->route_gpu_fiq < BCM2836_NCORES); |
93 | s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU; |
94 | } |
95 | |
96 | /* |
97 | * handle the control module 'local timer' interrupt for one of the |
98 | * cores' IRQ/FIQ; this is distinct from the per-CPU timer |
99 | * interrupts handled below. |
100 | */ |
101 | if ((s->local_timer_control & LOCALTIMER_INTENABLE) && |
102 | (s->local_timer_control & LOCALTIMER_INTFLAG)) { |
103 | if (s->route_localtimer & 4) { |
104 | s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER; |
105 | } else { |
106 | s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER; |
107 | } |
108 | } |
109 | |
110 | for (i = 0; i < BCM2836_NCORES; i++) { |
111 | /* handle local timer interrupts for this core */ |
112 | if (s->timerirqs[i]) { |
113 | assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */ |
114 | for (j = 0; j <= IRQ_CNTVIRQ; j++) { |
115 | if ((s->timerirqs[i] & (1 << j)) != 0) { |
116 | /* local interrupt j is set */ |
117 | deliver_local(s, i, j, s->timercontrol[i], j); |
118 | } |
119 | } |
120 | } |
121 | |
122 | /* handle mailboxes for this core */ |
123 | for (j = 0; j < BCM2836_MBPERCORE; j++) { |
124 | if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) { |
125 | /* mailbox j is set */ |
126 | deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j); |
127 | } |
128 | } |
129 | } |
130 | |
131 | /* call set_irq appropriately for each output */ |
132 | for (i = 0; i < BCM2836_NCORES; i++) { |
133 | qemu_set_irq(s->irq[i], s->irqsrc[i] != 0); |
134 | qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0); |
135 | } |
136 | } |
137 | |
138 | static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq, |
139 | int level) |
140 | { |
141 | BCM2836ControlState *s = opaque; |
142 | |
143 | assert(core >= 0 && core < BCM2836_NCORES); |
144 | assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ); |
145 | |
146 | s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level); |
147 | |
148 | bcm2836_control_update(s); |
149 | } |
150 | |
151 | /* XXX: the following wrapper functions are a kludgy workaround, |
152 | * needed because I can't seem to pass useful information in the "irq" |
153 | * parameter when using named interrupts. Feel free to clean this up! |
154 | */ |
155 | |
156 | static void bcm2836_control_set_local_irq0(void *opaque, int core, int level) |
157 | { |
158 | bcm2836_control_set_local_irq(opaque, core, 0, level); |
159 | } |
160 | |
161 | static void bcm2836_control_set_local_irq1(void *opaque, int core, int level) |
162 | { |
163 | bcm2836_control_set_local_irq(opaque, core, 1, level); |
164 | } |
165 | |
166 | static void bcm2836_control_set_local_irq2(void *opaque, int core, int level) |
167 | { |
168 | bcm2836_control_set_local_irq(opaque, core, 2, level); |
169 | } |
170 | |
171 | static void bcm2836_control_set_local_irq3(void *opaque, int core, int level) |
172 | { |
173 | bcm2836_control_set_local_irq(opaque, core, 3, level); |
174 | } |
175 | |
176 | static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level) |
177 | { |
178 | BCM2836ControlState *s = opaque; |
179 | |
180 | s->gpu_irq = level; |
181 | |
182 | bcm2836_control_update(s); |
183 | } |
184 | |
185 | static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level) |
186 | { |
187 | BCM2836ControlState *s = opaque; |
188 | |
189 | s->gpu_fiq = level; |
190 | |
191 | bcm2836_control_update(s); |
192 | } |
193 | |
194 | static void bcm2836_control_local_timer_set_next(void *opaque) |
195 | { |
196 | BCM2836ControlState *s = opaque; |
197 | uint64_t next_event; |
198 | |
199 | assert(LOCALTIMER_VALUE(s->local_timer_control) > 0); |
200 | |
201 | next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + |
202 | muldiv64(LOCALTIMER_VALUE(s->local_timer_control), |
203 | NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ); |
204 | timer_mod(&s->timer, next_event); |
205 | } |
206 | |
207 | static void bcm2836_control_local_timer_tick(void *opaque) |
208 | { |
209 | BCM2836ControlState *s = opaque; |
210 | |
211 | bcm2836_control_local_timer_set_next(s); |
212 | |
213 | s->local_timer_control |= LOCALTIMER_INTFLAG; |
214 | bcm2836_control_update(s); |
215 | } |
216 | |
217 | static void bcm2836_control_local_timer_control(void *opaque, uint32_t val) |
218 | { |
219 | BCM2836ControlState *s = opaque; |
220 | |
221 | s->local_timer_control = val; |
222 | if (val & LOCALTIMER_ENABLE) { |
223 | bcm2836_control_local_timer_set_next(s); |
224 | } else { |
225 | timer_del(&s->timer); |
226 | } |
227 | } |
228 | |
229 | static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val) |
230 | { |
231 | BCM2836ControlState *s = opaque; |
232 | |
233 | if (val & LOCALTIMER_INTFLAG) { |
234 | s->local_timer_control &= ~LOCALTIMER_INTFLAG; |
235 | } |
236 | if ((val & LOCALTIMER_RELOAD) && |
237 | (s->local_timer_control & LOCALTIMER_ENABLE)) { |
238 | bcm2836_control_local_timer_set_next(s); |
239 | } |
240 | } |
241 | |
242 | static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) |
243 | { |
244 | BCM2836ControlState *s = opaque; |
245 | |
246 | if (offset == REG_GPU_ROUTE) { |
247 | assert(s->route_gpu_fiq < BCM2836_NCORES |
248 | && s->route_gpu_irq < BCM2836_NCORES); |
249 | return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq; |
250 | } else if (offset == REG_LOCALTIMERROUTING) { |
251 | return s->route_localtimer; |
252 | } else if (offset == REG_LOCALTIMERCONTROL) { |
253 | return s->local_timer_control; |
254 | } else if (offset == REG_LOCALTIMERACK) { |
255 | return 0; |
256 | } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { |
257 | return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2]; |
258 | } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { |
259 | return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2]; |
260 | } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) { |
261 | return s->irqsrc[(offset - REG_IRQSRC) >> 2]; |
262 | } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) { |
263 | return s->fiqsrc[(offset - REG_FIQSRC) >> 2]; |
264 | } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { |
265 | return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2]; |
266 | } else { |
267 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx"\n" , |
268 | __func__, offset); |
269 | return 0; |
270 | } |
271 | } |
272 | |
273 | static void bcm2836_control_write(void *opaque, hwaddr offset, |
274 | uint64_t val, unsigned size) |
275 | { |
276 | BCM2836ControlState *s = opaque; |
277 | |
278 | if (offset == REG_GPU_ROUTE) { |
279 | s->route_gpu_irq = val & 0x3; |
280 | s->route_gpu_fiq = (val >> 2) & 0x3; |
281 | } else if (offset == REG_LOCALTIMERROUTING) { |
282 | s->route_localtimer = val & 7; |
283 | } else if (offset == REG_LOCALTIMERCONTROL) { |
284 | bcm2836_control_local_timer_control(s, val); |
285 | } else if (offset == REG_LOCALTIMERACK) { |
286 | bcm2836_control_local_timer_ack(s, val); |
287 | } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { |
288 | s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff; |
289 | } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { |
290 | s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff; |
291 | } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) { |
292 | s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val; |
293 | } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { |
294 | s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val; |
295 | } else { |
296 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx"\n" , |
297 | __func__, offset); |
298 | return; |
299 | } |
300 | |
301 | bcm2836_control_update(s); |
302 | } |
303 | |
304 | static const MemoryRegionOps bcm2836_control_ops = { |
305 | .read = bcm2836_control_read, |
306 | .write = bcm2836_control_write, |
307 | .endianness = DEVICE_NATIVE_ENDIAN, |
308 | .valid.min_access_size = 4, |
309 | .valid.max_access_size = 4, |
310 | }; |
311 | |
312 | static void bcm2836_control_reset(DeviceState *d) |
313 | { |
314 | BCM2836ControlState *s = BCM2836_CONTROL(d); |
315 | int i; |
316 | |
317 | s->route_gpu_irq = s->route_gpu_fiq = 0; |
318 | |
319 | timer_del(&s->timer); |
320 | s->route_localtimer = 0; |
321 | s->local_timer_control = 0; |
322 | |
323 | for (i = 0; i < BCM2836_NCORES; i++) { |
324 | s->timercontrol[i] = 0; |
325 | s->mailboxcontrol[i] = 0; |
326 | } |
327 | |
328 | for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) { |
329 | s->mailboxes[i] = 0; |
330 | } |
331 | } |
332 | |
333 | static void bcm2836_control_init(Object *obj) |
334 | { |
335 | BCM2836ControlState *s = BCM2836_CONTROL(obj); |
336 | DeviceState *dev = DEVICE(obj); |
337 | |
338 | memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s, |
339 | TYPE_BCM2836_CONTROL, REG_LIMIT); |
340 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); |
341 | |
342 | /* inputs from each CPU core */ |
343 | qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq" , |
344 | BCM2836_NCORES); |
345 | qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq" , |
346 | BCM2836_NCORES); |
347 | qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq" , |
348 | BCM2836_NCORES); |
349 | qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq" , |
350 | BCM2836_NCORES); |
351 | |
352 | /* IRQ and FIQ inputs from upstream bcm2835 controller */ |
353 | qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq" , 1); |
354 | qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq" , 1); |
355 | |
356 | /* outputs to CPU cores */ |
357 | qdev_init_gpio_out_named(dev, s->irq, "irq" , BCM2836_NCORES); |
358 | qdev_init_gpio_out_named(dev, s->fiq, "fiq" , BCM2836_NCORES); |
359 | |
360 | /* create a qemu virtual timer */ |
361 | timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, |
362 | bcm2836_control_local_timer_tick, s); |
363 | } |
364 | |
365 | static const VMStateDescription vmstate_bcm2836_control = { |
366 | .name = TYPE_BCM2836_CONTROL, |
367 | .version_id = 2, |
368 | .minimum_version_id = 1, |
369 | .fields = (VMStateField[]) { |
370 | VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState, |
371 | BCM2836_NCORES * BCM2836_MBPERCORE), |
372 | VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState), |
373 | VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState), |
374 | VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES), |
375 | VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState, |
376 | BCM2836_NCORES), |
377 | VMSTATE_TIMER_V(timer, BCM2836ControlState, 2), |
378 | VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2), |
379 | VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2), |
380 | VMSTATE_END_OF_LIST() |
381 | } |
382 | }; |
383 | |
384 | static void bcm2836_control_class_init(ObjectClass *klass, void *data) |
385 | { |
386 | DeviceClass *dc = DEVICE_CLASS(klass); |
387 | |
388 | dc->reset = bcm2836_control_reset; |
389 | dc->vmsd = &vmstate_bcm2836_control; |
390 | } |
391 | |
392 | static TypeInfo bcm2836_control_info = { |
393 | .name = TYPE_BCM2836_CONTROL, |
394 | .parent = TYPE_SYS_BUS_DEVICE, |
395 | .instance_size = sizeof(BCM2836ControlState), |
396 | .class_init = bcm2836_control_class_init, |
397 | .instance_init = bcm2836_control_init, |
398 | }; |
399 | |
400 | static void bcm2836_control_register_types(void) |
401 | { |
402 | type_register_static(&bcm2836_control_info); |
403 | } |
404 | |
405 | type_init(bcm2836_control_register_types) |
406 | |