1 | /* |
2 | * Raspberry Pi emulation (c) 2012 Gregory Estrade |
3 | * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. |
4 | * This code is licensed under the GNU GPLv2 and later. |
5 | * Heavily based on pl190.c, copyright terms below: |
6 | * |
7 | * Arm PrimeCell PL190 Vector Interrupt Controller |
8 | * |
9 | * Copyright (c) 2006 CodeSourcery. |
10 | * Written by Paul Brook |
11 | * |
12 | * This code is licensed under the GPL. |
13 | */ |
14 | |
15 | #include "qemu/osdep.h" |
16 | #include "hw/intc/bcm2835_ic.h" |
17 | #include "hw/irq.h" |
18 | #include "migration/vmstate.h" |
19 | #include "qemu/log.h" |
20 | #include "qemu/module.h" |
21 | |
22 | #define GPU_IRQS 64 |
23 | #define ARM_IRQS 8 |
24 | |
25 | #define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */ |
26 | #define IRQ_PENDING_1 0x04 /* IRQ pending 1 */ |
27 | #define IRQ_PENDING_2 0x08 /* IRQ pending 2 */ |
28 | #define FIQ_CONTROL 0x0C /* FIQ register */ |
29 | #define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */ |
30 | #define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */ |
31 | #define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */ |
32 | #define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */ |
33 | #define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */ |
34 | #define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */ |
35 | |
36 | /* Update interrupts. */ |
37 | static void bcm2835_ic_update(BCM2835ICState *s) |
38 | { |
39 | bool set = false; |
40 | |
41 | if (s->fiq_enable) { |
42 | if (s->fiq_select >= GPU_IRQS) { |
43 | /* ARM IRQ */ |
44 | set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1); |
45 | } else { |
46 | set = extract64(s->gpu_irq_level, s->fiq_select, 1); |
47 | } |
48 | } |
49 | qemu_set_irq(s->fiq, set); |
50 | |
51 | set = (s->gpu_irq_level & s->gpu_irq_enable) |
52 | || (s->arm_irq_level & s->arm_irq_enable); |
53 | qemu_set_irq(s->irq, set); |
54 | |
55 | } |
56 | |
57 | static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level) |
58 | { |
59 | BCM2835ICState *s = opaque; |
60 | |
61 | assert(irq >= 0 && irq < 64); |
62 | s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0); |
63 | bcm2835_ic_update(s); |
64 | } |
65 | |
66 | static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level) |
67 | { |
68 | BCM2835ICState *s = opaque; |
69 | |
70 | assert(irq >= 0 && irq < 8); |
71 | s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0); |
72 | bcm2835_ic_update(s); |
73 | } |
74 | |
75 | static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 }; |
76 | |
77 | static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size) |
78 | { |
79 | BCM2835ICState *s = opaque; |
80 | uint32_t res = 0; |
81 | uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable; |
82 | int i; |
83 | |
84 | switch (offset) { |
85 | case IRQ_PENDING_BASIC: |
86 | /* bits 0-7: ARM irqs */ |
87 | res = s->arm_irq_level & s->arm_irq_enable; |
88 | |
89 | /* bits 8 & 9: pending registers 1 & 2 */ |
90 | res |= (((uint32_t)gpu_pending) != 0) << 8; |
91 | res |= ((gpu_pending >> 32) != 0) << 9; |
92 | |
93 | /* bits 10-20: selected GPU IRQs */ |
94 | for (i = 0; i < ARRAY_SIZE(irq_dups); i++) { |
95 | res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10); |
96 | } |
97 | break; |
98 | case IRQ_PENDING_1: |
99 | res = gpu_pending; |
100 | break; |
101 | case IRQ_PENDING_2: |
102 | res = gpu_pending >> 32; |
103 | break; |
104 | case FIQ_CONTROL: |
105 | res = (s->fiq_enable << 7) | s->fiq_select; |
106 | break; |
107 | case IRQ_ENABLE_1: |
108 | res = s->gpu_irq_enable; |
109 | break; |
110 | case IRQ_ENABLE_2: |
111 | res = s->gpu_irq_enable >> 32; |
112 | break; |
113 | case IRQ_ENABLE_BASIC: |
114 | res = s->arm_irq_enable; |
115 | break; |
116 | case IRQ_DISABLE_1: |
117 | res = ~s->gpu_irq_enable; |
118 | break; |
119 | case IRQ_DISABLE_2: |
120 | res = ~s->gpu_irq_enable >> 32; |
121 | break; |
122 | case IRQ_DISABLE_BASIC: |
123 | res = ~s->arm_irq_enable; |
124 | break; |
125 | default: |
126 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx"\n" , |
127 | __func__, offset); |
128 | return 0; |
129 | } |
130 | |
131 | return res; |
132 | } |
133 | |
134 | static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val, |
135 | unsigned size) |
136 | { |
137 | BCM2835ICState *s = opaque; |
138 | |
139 | switch (offset) { |
140 | case FIQ_CONTROL: |
141 | s->fiq_select = extract32(val, 0, 7); |
142 | s->fiq_enable = extract32(val, 7, 1); |
143 | break; |
144 | case IRQ_ENABLE_1: |
145 | s->gpu_irq_enable |= val; |
146 | break; |
147 | case IRQ_ENABLE_2: |
148 | s->gpu_irq_enable |= val << 32; |
149 | break; |
150 | case IRQ_ENABLE_BASIC: |
151 | s->arm_irq_enable |= val & 0xff; |
152 | break; |
153 | case IRQ_DISABLE_1: |
154 | s->gpu_irq_enable &= ~val; |
155 | break; |
156 | case IRQ_DISABLE_2: |
157 | s->gpu_irq_enable &= ~(val << 32); |
158 | break; |
159 | case IRQ_DISABLE_BASIC: |
160 | s->arm_irq_enable &= ~val & 0xff; |
161 | break; |
162 | default: |
163 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx"\n" , |
164 | __func__, offset); |
165 | return; |
166 | } |
167 | bcm2835_ic_update(s); |
168 | } |
169 | |
170 | static const MemoryRegionOps bcm2835_ic_ops = { |
171 | .read = bcm2835_ic_read, |
172 | .write = bcm2835_ic_write, |
173 | .endianness = DEVICE_NATIVE_ENDIAN, |
174 | .valid.min_access_size = 4, |
175 | .valid.max_access_size = 4, |
176 | }; |
177 | |
178 | static void bcm2835_ic_reset(DeviceState *d) |
179 | { |
180 | BCM2835ICState *s = BCM2835_IC(d); |
181 | |
182 | s->gpu_irq_enable = 0; |
183 | s->arm_irq_enable = 0; |
184 | s->fiq_enable = false; |
185 | s->fiq_select = 0; |
186 | } |
187 | |
188 | static void bcm2835_ic_init(Object *obj) |
189 | { |
190 | BCM2835ICState *s = BCM2835_IC(obj); |
191 | |
192 | memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC, |
193 | 0x200); |
194 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); |
195 | |
196 | qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq, |
197 | BCM2835_IC_GPU_IRQ, GPU_IRQS); |
198 | qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq, |
199 | BCM2835_IC_ARM_IRQ, ARM_IRQS); |
200 | |
201 | sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); |
202 | sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq); |
203 | } |
204 | |
205 | static const VMStateDescription vmstate_bcm2835_ic = { |
206 | .name = TYPE_BCM2835_IC, |
207 | .version_id = 1, |
208 | .minimum_version_id = 1, |
209 | .fields = (VMStateField[]) { |
210 | VMSTATE_UINT64(gpu_irq_level, BCM2835ICState), |
211 | VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState), |
212 | VMSTATE_UINT8(arm_irq_level, BCM2835ICState), |
213 | VMSTATE_UINT8(arm_irq_enable, BCM2835ICState), |
214 | VMSTATE_BOOL(fiq_enable, BCM2835ICState), |
215 | VMSTATE_UINT8(fiq_select, BCM2835ICState), |
216 | VMSTATE_END_OF_LIST() |
217 | } |
218 | }; |
219 | |
220 | static void bcm2835_ic_class_init(ObjectClass *klass, void *data) |
221 | { |
222 | DeviceClass *dc = DEVICE_CLASS(klass); |
223 | |
224 | dc->reset = bcm2835_ic_reset; |
225 | dc->vmsd = &vmstate_bcm2835_ic; |
226 | } |
227 | |
228 | static TypeInfo bcm2835_ic_info = { |
229 | .name = TYPE_BCM2835_IC, |
230 | .parent = TYPE_SYS_BUS_DEVICE, |
231 | .instance_size = sizeof(BCM2835ICState), |
232 | .class_init = bcm2835_ic_class_init, |
233 | .instance_init = bcm2835_ic_init, |
234 | }; |
235 | |
236 | static void bcm2835_ic_register_types(void) |
237 | { |
238 | type_register_static(&bcm2835_ic_info); |
239 | } |
240 | |
241 | type_init(bcm2835_ic_register_types) |
242 | |