]>
Commit | Line | Data |
---|---|---|
008ff9d7 JM |
1 | /* |
2 | * QEMU PowerPC 4xx embedded processors shared devices emulation | |
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 | */ | |
87ecb68b PB |
24 | #include "hw.h" |
25 | #include "ppc.h" | |
008ff9d7 | 26 | #include "ppc4xx.h" |
87ecb68b | 27 | #include "sysemu.h" |
008ff9d7 JM |
28 | |
29 | extern int loglevel; | |
30 | extern FILE *logfile; | |
31 | ||
32 | //#define DEBUG_MMIO | |
aae9366a | 33 | //#define DEBUG_UNASSIGNED |
008ff9d7 JM |
34 | #define DEBUG_UIC |
35 | ||
36 | /*****************************************************************************/ | |
37 | /* Generic PowerPC 4xx processor instanciation */ | |
38 | CPUState *ppc4xx_init (const unsigned char *cpu_model, | |
39 | clk_setup_t *cpu_clk, clk_setup_t *tb_clk, | |
40 | uint32_t sysclk) | |
41 | { | |
42 | CPUState *env; | |
008ff9d7 JM |
43 | |
44 | /* init CPUs */ | |
aaed909a FB |
45 | env = cpu_init(cpu_model); |
46 | if (!env) { | |
47 | fprintf(stderr, "Unable to find PowerPC %s CPU definition\n", | |
48 | cpu_model); | |
49 | exit(1); | |
008ff9d7 | 50 | } |
008ff9d7 JM |
51 | cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ |
52 | cpu_clk->opaque = env; | |
53 | /* Set time-base frequency to sysclk */ | |
54 | tb_clk->cb = ppc_emb_timers_init(env, sysclk); | |
55 | tb_clk->opaque = env; | |
56 | ppc_dcr_init(env, NULL, NULL); | |
57 | /* Register qemu callbacks */ | |
58 | qemu_register_reset(&cpu_ppc_reset, env); | |
59 | register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); | |
60 | ||
61 | return env; | |
62 | } | |
63 | ||
64 | /*****************************************************************************/ | |
65 | /* Fake device used to map multiple devices in a single memory page */ | |
66 | #define MMIO_AREA_BITS 8 | |
67 | #define MMIO_AREA_LEN (1 << MMIO_AREA_BITS) | |
68 | #define MMIO_AREA_NB (1 << (TARGET_PAGE_BITS - MMIO_AREA_BITS)) | |
69 | #define MMIO_IDX(addr) (((addr) >> MMIO_AREA_BITS) & (MMIO_AREA_NB - 1)) | |
70 | struct ppc4xx_mmio_t { | |
71 | target_phys_addr_t base; | |
72 | CPUReadMemoryFunc **mem_read[MMIO_AREA_NB]; | |
73 | CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB]; | |
74 | void *opaque[MMIO_AREA_NB]; | |
75 | }; | |
76 | ||
77 | static uint32_t unassigned_mmio_readb (void *opaque, target_phys_addr_t addr) | |
78 | { | |
79 | #ifdef DEBUG_UNASSIGNED | |
80 | ppc4xx_mmio_t *mmio; | |
81 | ||
82 | mmio = opaque; | |
83 | printf("Unassigned mmio read 0x" PADDRX " base " PADDRX "\n", | |
84 | addr, mmio->base); | |
85 | #endif | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | static void unassigned_mmio_writeb (void *opaque, | |
91 | target_phys_addr_t addr, uint32_t val) | |
92 | { | |
93 | #ifdef DEBUG_UNASSIGNED | |
94 | ppc4xx_mmio_t *mmio; | |
95 | ||
96 | mmio = opaque; | |
97 | printf("Unassigned mmio write 0x" PADDRX " = 0x%x base " PADDRX "\n", | |
98 | addr, val, mmio->base); | |
99 | #endif | |
100 | } | |
101 | ||
102 | static CPUReadMemoryFunc *unassigned_mmio_read[3] = { | |
103 | unassigned_mmio_readb, | |
104 | unassigned_mmio_readb, | |
105 | unassigned_mmio_readb, | |
106 | }; | |
107 | ||
108 | static CPUWriteMemoryFunc *unassigned_mmio_write[3] = { | |
109 | unassigned_mmio_writeb, | |
110 | unassigned_mmio_writeb, | |
111 | unassigned_mmio_writeb, | |
112 | }; | |
113 | ||
114 | static uint32_t mmio_readlen (ppc4xx_mmio_t *mmio, | |
115 | target_phys_addr_t addr, int len) | |
116 | { | |
117 | CPUReadMemoryFunc **mem_read; | |
118 | uint32_t ret; | |
119 | int idx; | |
120 | ||
121 | idx = MMIO_IDX(addr - mmio->base); | |
122 | #if defined(DEBUG_MMIO) | |
123 | printf("%s: mmio %p len %d addr " PADDRX " idx %d\n", __func__, | |
124 | mmio, len, addr, idx); | |
125 | #endif | |
126 | mem_read = mmio->mem_read[idx]; | |
127 | ret = (*mem_read[len])(mmio->opaque[idx], addr - mmio->base); | |
128 | ||
129 | return ret; | |
130 | } | |
131 | ||
132 | static void mmio_writelen (ppc4xx_mmio_t *mmio, | |
133 | target_phys_addr_t addr, uint32_t value, int len) | |
134 | { | |
135 | CPUWriteMemoryFunc **mem_write; | |
136 | int idx; | |
137 | ||
138 | idx = MMIO_IDX(addr - mmio->base); | |
139 | #if defined(DEBUG_MMIO) | |
aae9366a JM |
140 | printf("%s: mmio %p len %d addr " PADDRX " idx %d value %08" PRIx32 "\n", |
141 | __func__, mmio, len, addr, idx, value); | |
008ff9d7 JM |
142 | #endif |
143 | mem_write = mmio->mem_write[idx]; | |
144 | (*mem_write[len])(mmio->opaque[idx], addr - mmio->base, value); | |
145 | } | |
146 | ||
147 | static uint32_t mmio_readb (void *opaque, target_phys_addr_t addr) | |
148 | { | |
149 | #if defined(DEBUG_MMIO) | |
150 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
151 | #endif | |
152 | ||
153 | return mmio_readlen(opaque, addr, 0); | |
154 | } | |
155 | ||
156 | static void mmio_writeb (void *opaque, | |
157 | target_phys_addr_t addr, uint32_t value) | |
158 | { | |
159 | #if defined(DEBUG_MMIO) | |
aae9366a | 160 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
161 | #endif |
162 | mmio_writelen(opaque, addr, value, 0); | |
163 | } | |
164 | ||
165 | static uint32_t mmio_readw (void *opaque, target_phys_addr_t addr) | |
166 | { | |
167 | #if defined(DEBUG_MMIO) | |
168 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
169 | #endif | |
170 | ||
171 | return mmio_readlen(opaque, addr, 1); | |
172 | } | |
173 | ||
174 | static void mmio_writew (void *opaque, | |
175 | target_phys_addr_t addr, uint32_t value) | |
176 | { | |
177 | #if defined(DEBUG_MMIO) | |
aae9366a | 178 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
179 | #endif |
180 | mmio_writelen(opaque, addr, value, 1); | |
181 | } | |
182 | ||
183 | static uint32_t mmio_readl (void *opaque, target_phys_addr_t addr) | |
184 | { | |
185 | #if defined(DEBUG_MMIO) | |
186 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
187 | #endif | |
188 | ||
189 | return mmio_readlen(opaque, addr, 2); | |
190 | } | |
191 | ||
192 | static void mmio_writel (void *opaque, | |
193 | target_phys_addr_t addr, uint32_t value) | |
194 | { | |
195 | #if defined(DEBUG_MMIO) | |
aae9366a | 196 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
197 | #endif |
198 | mmio_writelen(opaque, addr, value, 2); | |
199 | } | |
200 | ||
201 | static CPUReadMemoryFunc *mmio_read[] = { | |
202 | &mmio_readb, | |
203 | &mmio_readw, | |
204 | &mmio_readl, | |
205 | }; | |
206 | ||
207 | static CPUWriteMemoryFunc *mmio_write[] = { | |
208 | &mmio_writeb, | |
209 | &mmio_writew, | |
210 | &mmio_writel, | |
211 | }; | |
212 | ||
213 | int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio, | |
214 | target_phys_addr_t offset, uint32_t len, | |
215 | CPUReadMemoryFunc **mem_read, | |
216 | CPUWriteMemoryFunc **mem_write, void *opaque) | |
217 | { | |
aae9366a | 218 | target_phys_addr_t end; |
008ff9d7 JM |
219 | int idx, eidx; |
220 | ||
221 | if ((offset + len) > TARGET_PAGE_SIZE) | |
222 | return -1; | |
223 | idx = MMIO_IDX(offset); | |
224 | end = offset + len - 1; | |
225 | eidx = MMIO_IDX(end); | |
226 | #if defined(DEBUG_MMIO) | |
aae9366a JM |
227 | printf("%s: offset " PADDRX " len %08" PRIx32 " " PADDRX " %d %d\n", |
228 | __func__, offset, len, end, idx, eidx); | |
008ff9d7 JM |
229 | #endif |
230 | for (; idx <= eidx; idx++) { | |
231 | mmio->mem_read[idx] = mem_read; | |
232 | mmio->mem_write[idx] = mem_write; | |
233 | mmio->opaque[idx] = opaque; | |
234 | } | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, target_phys_addr_t base) | |
240 | { | |
241 | ppc4xx_mmio_t *mmio; | |
242 | int mmio_memory; | |
243 | ||
244 | mmio = qemu_mallocz(sizeof(ppc4xx_mmio_t)); | |
245 | if (mmio != NULL) { | |
246 | mmio->base = base; | |
247 | mmio_memory = cpu_register_io_memory(0, mmio_read, mmio_write, mmio); | |
248 | #if defined(DEBUG_MMIO) | |
aae9366a JM |
249 | printf("%s: base " PADDRX " len %08x %d\n", __func__, |
250 | base, TARGET_PAGE_SIZE, mmio_memory); | |
008ff9d7 JM |
251 | #endif |
252 | cpu_register_physical_memory(base, TARGET_PAGE_SIZE, mmio_memory); | |
253 | ppc4xx_mmio_register(env, mmio, 0, TARGET_PAGE_SIZE, | |
254 | unassigned_mmio_read, unassigned_mmio_write, | |
255 | mmio); | |
256 | } | |
257 | ||
258 | return mmio; | |
259 | } | |
260 | ||
261 | /*****************************************************************************/ | |
262 | /* "Universal" Interrupt controller */ | |
263 | enum { | |
264 | DCR_UICSR = 0x000, | |
265 | DCR_UICSRS = 0x001, | |
266 | DCR_UICER = 0x002, | |
267 | DCR_UICCR = 0x003, | |
268 | DCR_UICPR = 0x004, | |
269 | DCR_UICTR = 0x005, | |
270 | DCR_UICMSR = 0x006, | |
271 | DCR_UICVR = 0x007, | |
272 | DCR_UICVCR = 0x008, | |
273 | DCR_UICMAX = 0x009, | |
274 | }; | |
275 | ||
276 | #define UIC_MAX_IRQ 32 | |
277 | typedef struct ppcuic_t ppcuic_t; | |
278 | struct ppcuic_t { | |
279 | uint32_t dcr_base; | |
280 | int use_vectors; | |
4c54e875 | 281 | uint32_t level; /* Remembers the state of level-triggered interrupts. */ |
008ff9d7 JM |
282 | uint32_t uicsr; /* Status register */ |
283 | uint32_t uicer; /* Enable register */ | |
284 | uint32_t uiccr; /* Critical register */ | |
285 | uint32_t uicpr; /* Polarity register */ | |
286 | uint32_t uictr; /* Triggering register */ | |
287 | uint32_t uicvcr; /* Vector configuration register */ | |
288 | uint32_t uicvr; | |
289 | qemu_irq *irqs; | |
290 | }; | |
291 | ||
292 | static void ppcuic_trigger_irq (ppcuic_t *uic) | |
293 | { | |
294 | uint32_t ir, cr; | |
295 | int start, end, inc, i; | |
296 | ||
297 | /* Trigger interrupt if any is pending */ | |
298 | ir = uic->uicsr & uic->uicer & (~uic->uiccr); | |
299 | cr = uic->uicsr & uic->uicer & uic->uiccr; | |
300 | #ifdef DEBUG_UIC | |
301 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
302 | fprintf(logfile, "%s: uicsr %08" PRIx32 " uicer %08" PRIx32 |
303 | " uiccr %08" PRIx32 "\n" | |
304 | " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", | |
305 | __func__, uic->uicsr, uic->uicer, uic->uiccr, | |
008ff9d7 JM |
306 | uic->uicsr & uic->uicer, ir, cr); |
307 | } | |
308 | #endif | |
309 | if (ir != 0x0000000) { | |
310 | #ifdef DEBUG_UIC | |
311 | if (loglevel & CPU_LOG_INT) { | |
312 | fprintf(logfile, "Raise UIC interrupt\n"); | |
313 | } | |
314 | #endif | |
315 | qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); | |
316 | } else { | |
317 | #ifdef DEBUG_UIC | |
318 | if (loglevel & CPU_LOG_INT) { | |
319 | fprintf(logfile, "Lower UIC interrupt\n"); | |
320 | } | |
321 | #endif | |
322 | qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); | |
323 | } | |
324 | /* Trigger critical interrupt if any is pending and update vector */ | |
325 | if (cr != 0x0000000) { | |
326 | qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); | |
327 | if (uic->use_vectors) { | |
328 | /* Compute critical IRQ vector */ | |
329 | if (uic->uicvcr & 1) { | |
330 | start = 31; | |
331 | end = 0; | |
332 | inc = -1; | |
333 | } else { | |
334 | start = 0; | |
335 | end = 31; | |
336 | inc = 1; | |
337 | } | |
338 | uic->uicvr = uic->uicvcr & 0xFFFFFFFC; | |
339 | for (i = start; i <= end; i += inc) { | |
340 | if (cr & (1 << i)) { | |
341 | uic->uicvr += (i - start) * 512 * inc; | |
342 | break; | |
343 | } | |
344 | } | |
345 | } | |
346 | #ifdef DEBUG_UIC | |
347 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
348 | fprintf(logfile, "Raise UIC critical interrupt - " |
349 | "vector %08" PRIx32 "\n", uic->uicvr); | |
008ff9d7 JM |
350 | } |
351 | #endif | |
352 | } else { | |
353 | #ifdef DEBUG_UIC | |
354 | if (loglevel & CPU_LOG_INT) { | |
355 | fprintf(logfile, "Lower UIC critical interrupt\n"); | |
356 | } | |
357 | #endif | |
358 | qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); | |
359 | uic->uicvr = 0x00000000; | |
360 | } | |
361 | } | |
362 | ||
363 | static void ppcuic_set_irq (void *opaque, int irq_num, int level) | |
364 | { | |
365 | ppcuic_t *uic; | |
366 | uint32_t mask, sr; | |
367 | ||
368 | uic = opaque; | |
923e5e33 | 369 | mask = 1 << (31-irq_num); |
008ff9d7 JM |
370 | #ifdef DEBUG_UIC |
371 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
372 | fprintf(logfile, "%s: irq %d level %d uicsr %08" PRIx32 |
373 | " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", | |
374 | __func__, irq_num, level, | |
008ff9d7 JM |
375 | uic->uicsr, mask, uic->uicsr & mask, level << irq_num); |
376 | } | |
377 | #endif | |
378 | if (irq_num < 0 || irq_num > 31) | |
379 | return; | |
380 | sr = uic->uicsr; | |
50bf72b3 | 381 | |
008ff9d7 JM |
382 | /* Update status register */ |
383 | if (uic->uictr & mask) { | |
384 | /* Edge sensitive interrupt */ | |
385 | if (level == 1) | |
386 | uic->uicsr |= mask; | |
387 | } else { | |
388 | /* Level sensitive interrupt */ | |
4c54e875 | 389 | if (level == 1) { |
008ff9d7 | 390 | uic->uicsr |= mask; |
4c54e875 AJ |
391 | uic->level |= mask; |
392 | } else { | |
008ff9d7 | 393 | uic->uicsr &= ~mask; |
4c54e875 AJ |
394 | uic->level &= ~mask; |
395 | } | |
008ff9d7 JM |
396 | } |
397 | #ifdef DEBUG_UIC | |
398 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
399 | fprintf(logfile, "%s: irq %d level %d sr %" PRIx32 " => " |
400 | "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); | |
008ff9d7 JM |
401 | } |
402 | #endif | |
403 | if (sr != uic->uicsr) | |
404 | ppcuic_trigger_irq(uic); | |
405 | } | |
406 | ||
407 | static target_ulong dcr_read_uic (void *opaque, int dcrn) | |
408 | { | |
409 | ppcuic_t *uic; | |
410 | target_ulong ret; | |
411 | ||
412 | uic = opaque; | |
413 | dcrn -= uic->dcr_base; | |
414 | switch (dcrn) { | |
415 | case DCR_UICSR: | |
416 | case DCR_UICSRS: | |
417 | ret = uic->uicsr; | |
418 | break; | |
419 | case DCR_UICER: | |
420 | ret = uic->uicer; | |
421 | break; | |
422 | case DCR_UICCR: | |
423 | ret = uic->uiccr; | |
424 | break; | |
425 | case DCR_UICPR: | |
426 | ret = uic->uicpr; | |
427 | break; | |
428 | case DCR_UICTR: | |
429 | ret = uic->uictr; | |
430 | break; | |
431 | case DCR_UICMSR: | |
432 | ret = uic->uicsr & uic->uicer; | |
433 | break; | |
434 | case DCR_UICVR: | |
435 | if (!uic->use_vectors) | |
436 | goto no_read; | |
437 | ret = uic->uicvr; | |
438 | break; | |
439 | case DCR_UICVCR: | |
440 | if (!uic->use_vectors) | |
441 | goto no_read; | |
442 | ret = uic->uicvcr; | |
443 | break; | |
444 | default: | |
445 | no_read: | |
446 | ret = 0x00000000; | |
447 | break; | |
448 | } | |
449 | ||
450 | return ret; | |
451 | } | |
452 | ||
453 | static void dcr_write_uic (void *opaque, int dcrn, target_ulong val) | |
454 | { | |
455 | ppcuic_t *uic; | |
456 | ||
457 | uic = opaque; | |
458 | dcrn -= uic->dcr_base; | |
459 | #ifdef DEBUG_UIC | |
460 | if (loglevel & CPU_LOG_INT) { | |
461 | fprintf(logfile, "%s: dcr %d val " ADDRX "\n", __func__, dcrn, val); | |
462 | } | |
463 | #endif | |
464 | switch (dcrn) { | |
465 | case DCR_UICSR: | |
466 | uic->uicsr &= ~val; | |
4c54e875 | 467 | uic->uicsr |= uic->level; |
008ff9d7 JM |
468 | ppcuic_trigger_irq(uic); |
469 | break; | |
470 | case DCR_UICSRS: | |
471 | uic->uicsr |= val; | |
472 | ppcuic_trigger_irq(uic); | |
473 | break; | |
474 | case DCR_UICER: | |
475 | uic->uicer = val; | |
476 | ppcuic_trigger_irq(uic); | |
477 | break; | |
478 | case DCR_UICCR: | |
479 | uic->uiccr = val; | |
480 | ppcuic_trigger_irq(uic); | |
481 | break; | |
482 | case DCR_UICPR: | |
483 | uic->uicpr = val; | |
008ff9d7 JM |
484 | break; |
485 | case DCR_UICTR: | |
486 | uic->uictr = val; | |
487 | ppcuic_trigger_irq(uic); | |
488 | break; | |
489 | case DCR_UICMSR: | |
490 | break; | |
491 | case DCR_UICVR: | |
492 | break; | |
493 | case DCR_UICVCR: | |
494 | uic->uicvcr = val & 0xFFFFFFFD; | |
495 | ppcuic_trigger_irq(uic); | |
496 | break; | |
497 | } | |
498 | } | |
499 | ||
500 | static void ppcuic_reset (void *opaque) | |
501 | { | |
502 | ppcuic_t *uic; | |
503 | ||
504 | uic = opaque; | |
505 | uic->uiccr = 0x00000000; | |
506 | uic->uicer = 0x00000000; | |
507 | uic->uicpr = 0x00000000; | |
508 | uic->uicsr = 0x00000000; | |
509 | uic->uictr = 0x00000000; | |
510 | if (uic->use_vectors) { | |
511 | uic->uicvcr = 0x00000000; | |
512 | uic->uicvr = 0x0000000; | |
513 | } | |
514 | } | |
515 | ||
516 | qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, | |
517 | uint32_t dcr_base, int has_ssr, int has_vr) | |
518 | { | |
519 | ppcuic_t *uic; | |
520 | int i; | |
521 | ||
522 | uic = qemu_mallocz(sizeof(ppcuic_t)); | |
523 | if (uic != NULL) { | |
524 | uic->dcr_base = dcr_base; | |
525 | uic->irqs = irqs; | |
526 | if (has_vr) | |
527 | uic->use_vectors = 1; | |
528 | for (i = 0; i < DCR_UICMAX; i++) { | |
529 | ppc_dcr_register(env, dcr_base + i, uic, | |
530 | &dcr_read_uic, &dcr_write_uic); | |
531 | } | |
532 | qemu_register_reset(ppcuic_reset, uic); | |
533 | ppcuic_reset(uic); | |
534 | } | |
535 | ||
536 | return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); | |
537 | } |