]>
Commit | Line | Data |
---|---|---|
34d0831f PM |
1 | /* |
2 | * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors | |
3 | * | |
4 | * Copyright (c) 2007 Jocelyn Mayer | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "qemu/osdep.h" | |
26 | #include "include/hw/intc/ppc-uic.h" | |
27 | #include "hw/irq.h" | |
28 | #include "cpu.h" | |
29 | #include "hw/ppc/ppc.h" | |
30 | #include "hw/qdev-properties.h" | |
31 | #include "migration/vmstate.h" | |
32 | #include "qapi/error.h" | |
33 | ||
34 | enum { | |
35 | DCR_UICSR = 0x000, | |
36 | DCR_UICSRS = 0x001, | |
37 | DCR_UICER = 0x002, | |
38 | DCR_UICCR = 0x003, | |
39 | DCR_UICPR = 0x004, | |
40 | DCR_UICTR = 0x005, | |
41 | DCR_UICMSR = 0x006, | |
42 | DCR_UICVR = 0x007, | |
43 | DCR_UICVCR = 0x008, | |
44 | DCR_UICMAX = 0x009, | |
45 | }; | |
46 | ||
47 | /*#define DEBUG_UIC*/ | |
48 | ||
49 | #ifdef DEBUG_UIC | |
50 | # define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) | |
51 | #else | |
52 | # define LOG_UIC(...) do { } while (0) | |
53 | #endif | |
54 | ||
55 | static void ppcuic_trigger_irq(PPCUIC *uic) | |
56 | { | |
57 | uint32_t ir, cr; | |
58 | int start, end, inc, i; | |
59 | ||
60 | /* Trigger interrupt if any is pending */ | |
61 | ir = uic->uicsr & uic->uicer & (~uic->uiccr); | |
62 | cr = uic->uicsr & uic->uicer & uic->uiccr; | |
63 | LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 | |
64 | " uiccr %08" PRIx32 "\n" | |
65 | " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", | |
66 | __func__, uic->uicsr, uic->uicer, uic->uiccr, | |
67 | uic->uicsr & uic->uicer, ir, cr); | |
68 | if (ir != 0x0000000) { | |
69 | LOG_UIC("Raise UIC interrupt\n"); | |
70 | qemu_irq_raise(uic->output_int); | |
71 | } else { | |
72 | LOG_UIC("Lower UIC interrupt\n"); | |
73 | qemu_irq_lower(uic->output_int); | |
74 | } | |
75 | /* Trigger critical interrupt if any is pending and update vector */ | |
76 | if (cr != 0x0000000) { | |
77 | qemu_irq_raise(uic->output_cint); | |
78 | if (uic->use_vectors) { | |
79 | /* Compute critical IRQ vector */ | |
80 | if (uic->uicvcr & 1) { | |
81 | start = 31; | |
82 | end = 0; | |
83 | inc = -1; | |
84 | } else { | |
85 | start = 0; | |
86 | end = 31; | |
87 | inc = 1; | |
88 | } | |
89 | uic->uicvr = uic->uicvcr & 0xFFFFFFFC; | |
90 | for (i = start; i <= end; i += inc) { | |
91 | if (cr & (1 << i)) { | |
92 | uic->uicvr += (i - start) * 512 * inc; | |
93 | break; | |
94 | } | |
95 | } | |
96 | } | |
97 | LOG_UIC("Raise UIC critical interrupt - " | |
98 | "vector %08" PRIx32 "\n", uic->uicvr); | |
99 | } else { | |
100 | LOG_UIC("Lower UIC critical interrupt\n"); | |
101 | qemu_irq_lower(uic->output_cint); | |
102 | uic->uicvr = 0x00000000; | |
103 | } | |
104 | } | |
105 | ||
106 | static void ppcuic_set_irq(void *opaque, int irq_num, int level) | |
107 | { | |
108 | PPCUIC *uic; | |
109 | uint32_t mask, sr; | |
110 | ||
111 | uic = opaque; | |
112 | mask = 1U << (31 - irq_num); | |
113 | LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 | |
114 | " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", | |
115 | __func__, irq_num, level, | |
116 | uic->uicsr, mask, uic->uicsr & mask, level << irq_num); | |
117 | if (irq_num < 0 || irq_num > 31) { | |
118 | return; | |
119 | } | |
120 | sr = uic->uicsr; | |
121 | ||
122 | /* Update status register */ | |
123 | if (uic->uictr & mask) { | |
124 | /* Edge sensitive interrupt */ | |
125 | if (level == 1) { | |
126 | uic->uicsr |= mask; | |
127 | } | |
128 | } else { | |
129 | /* Level sensitive interrupt */ | |
130 | if (level == 1) { | |
131 | uic->uicsr |= mask; | |
132 | uic->level |= mask; | |
133 | } else { | |
134 | uic->uicsr &= ~mask; | |
135 | uic->level &= ~mask; | |
136 | } | |
137 | } | |
138 | LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " | |
139 | "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); | |
140 | if (sr != uic->uicsr) { | |
141 | ppcuic_trigger_irq(uic); | |
142 | } | |
143 | } | |
144 | ||
145 | static uint32_t dcr_read_uic(void *opaque, int dcrn) | |
146 | { | |
147 | PPCUIC *uic; | |
148 | uint32_t ret; | |
149 | ||
150 | uic = opaque; | |
151 | dcrn -= uic->dcr_base; | |
152 | switch (dcrn) { | |
153 | case DCR_UICSR: | |
154 | case DCR_UICSRS: | |
155 | ret = uic->uicsr; | |
156 | break; | |
157 | case DCR_UICER: | |
158 | ret = uic->uicer; | |
159 | break; | |
160 | case DCR_UICCR: | |
161 | ret = uic->uiccr; | |
162 | break; | |
163 | case DCR_UICPR: | |
164 | ret = uic->uicpr; | |
165 | break; | |
166 | case DCR_UICTR: | |
167 | ret = uic->uictr; | |
168 | break; | |
169 | case DCR_UICMSR: | |
170 | ret = uic->uicsr & uic->uicer; | |
171 | break; | |
172 | case DCR_UICVR: | |
173 | if (!uic->use_vectors) { | |
174 | goto no_read; | |
175 | } | |
176 | ret = uic->uicvr; | |
177 | break; | |
178 | case DCR_UICVCR: | |
179 | if (!uic->use_vectors) { | |
180 | goto no_read; | |
181 | } | |
182 | ret = uic->uicvcr; | |
183 | break; | |
184 | default: | |
185 | no_read: | |
186 | ret = 0x00000000; | |
187 | break; | |
188 | } | |
189 | ||
190 | return ret; | |
191 | } | |
192 | ||
193 | static void dcr_write_uic(void *opaque, int dcrn, uint32_t val) | |
194 | { | |
195 | PPCUIC *uic; | |
196 | ||
197 | uic = opaque; | |
198 | dcrn -= uic->dcr_base; | |
199 | LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); | |
200 | switch (dcrn) { | |
201 | case DCR_UICSR: | |
202 | uic->uicsr &= ~val; | |
203 | uic->uicsr |= uic->level; | |
204 | ppcuic_trigger_irq(uic); | |
205 | break; | |
206 | case DCR_UICSRS: | |
207 | uic->uicsr |= val; | |
208 | ppcuic_trigger_irq(uic); | |
209 | break; | |
210 | case DCR_UICER: | |
211 | uic->uicer = val; | |
212 | ppcuic_trigger_irq(uic); | |
213 | break; | |
214 | case DCR_UICCR: | |
215 | uic->uiccr = val; | |
216 | ppcuic_trigger_irq(uic); | |
217 | break; | |
218 | case DCR_UICPR: | |
219 | uic->uicpr = val; | |
220 | break; | |
221 | case DCR_UICTR: | |
222 | uic->uictr = val; | |
223 | ppcuic_trigger_irq(uic); | |
224 | break; | |
225 | case DCR_UICMSR: | |
226 | break; | |
227 | case DCR_UICVR: | |
228 | break; | |
229 | case DCR_UICVCR: | |
230 | uic->uicvcr = val & 0xFFFFFFFD; | |
231 | ppcuic_trigger_irq(uic); | |
232 | break; | |
233 | } | |
234 | } | |
235 | ||
236 | static void ppc_uic_reset(DeviceState *dev) | |
237 | { | |
238 | PPCUIC *uic = PPC_UIC(dev); | |
239 | ||
240 | uic->uiccr = 0x00000000; | |
241 | uic->uicer = 0x00000000; | |
242 | uic->uicpr = 0x00000000; | |
243 | uic->uicsr = 0x00000000; | |
244 | uic->uictr = 0x00000000; | |
245 | if (uic->use_vectors) { | |
246 | uic->uicvcr = 0x00000000; | |
247 | uic->uicvr = 0x0000000; | |
248 | } | |
249 | } | |
250 | ||
251 | static void ppc_uic_realize(DeviceState *dev, Error **errp) | |
252 | { | |
253 | PPCUIC *uic = PPC_UIC(dev); | |
254 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
255 | PowerPCCPU *cpu; | |
256 | int i; | |
257 | ||
258 | if (!uic->cpu) { | |
259 | /* This is a programming error in the code using this device */ | |
260 | error_setg(errp, "ppc-uic 'cpu' link property was not set"); | |
261 | return; | |
262 | } | |
263 | ||
264 | cpu = POWERPC_CPU(uic->cpu); | |
265 | for (i = 0; i < DCR_UICMAX; i++) { | |
266 | ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic, | |
267 | &dcr_read_uic, &dcr_write_uic); | |
268 | } | |
269 | ||
270 | sysbus_init_irq(sbd, &uic->output_int); | |
271 | sysbus_init_irq(sbd, &uic->output_cint); | |
272 | qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ); | |
273 | } | |
274 | ||
275 | static Property ppc_uic_properties[] = { | |
276 | DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *), | |
277 | DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0x30), | |
278 | DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), | |
279 | DEFINE_PROP_END_OF_LIST() | |
280 | }; | |
281 | ||
282 | static const VMStateDescription ppc_uic_vmstate = { | |
283 | .name = "ppc-uic", | |
284 | .version_id = 1, | |
285 | .minimum_version_id = 1, | |
286 | .fields = (VMStateField[]) { | |
287 | VMSTATE_UINT32(level, PPCUIC), | |
288 | VMSTATE_UINT32(uicsr, PPCUIC), | |
289 | VMSTATE_UINT32(uicer, PPCUIC), | |
290 | VMSTATE_UINT32(uiccr, PPCUIC), | |
291 | VMSTATE_UINT32(uicpr, PPCUIC), | |
292 | VMSTATE_UINT32(uictr, PPCUIC), | |
293 | VMSTATE_UINT32(uicvcr, PPCUIC), | |
294 | VMSTATE_UINT32(uicvr, PPCUIC), | |
295 | VMSTATE_END_OF_LIST() | |
296 | }, | |
297 | }; | |
298 | ||
299 | static void ppc_uic_class_init(ObjectClass *klass, void *data) | |
300 | { | |
301 | DeviceClass *dc = DEVICE_CLASS(klass); | |
302 | ||
303 | dc->reset = ppc_uic_reset; | |
304 | dc->realize = ppc_uic_realize; | |
305 | dc->vmsd = &ppc_uic_vmstate; | |
306 | device_class_set_props(dc, ppc_uic_properties); | |
307 | } | |
308 | ||
309 | static const TypeInfo ppc_uic_info = { | |
310 | .name = TYPE_PPC_UIC, | |
311 | .parent = TYPE_SYS_BUS_DEVICE, | |
312 | .instance_size = sizeof(PPCUIC), | |
313 | .class_init = ppc_uic_class_init, | |
314 | }; | |
315 | ||
316 | static void ppc_uic_register_types(void) | |
317 | { | |
318 | type_register_static(&ppc_uic_info); | |
319 | } | |
320 | ||
321 | type_init(ppc_uic_register_types); |