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