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