]> git.proxmox.com Git - qemu.git/blob - hw/openpic.c
8068e7e5901dbc3f7db2d2d2adc56193d54070d3
[qemu.git] / hw / openpic.c
1 /*
2 * OpenPIC emulation
3 *
4 * Copyright (c) 2004 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 *
26 * Based on OpenPic implementations:
27 * - Intel GW80314 I/O compagnion chip developper's manual
28 * - Motorola MPC8245 & MPC8540 user manuals.
29 * - Motorola MCP750 (aka Raven) programmer manual.
30 * - Motorola Harrier programmer manuel
31 *
32 * Serial interrupts, as implemented in Raven chipset are not supported yet.
33 *
34 */
35 #include "vl.h"
36
37 //#define DEBUG_OPENPIC
38
39 #ifdef DEBUG_OPENPIC
40 #define DPRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
41 #else
42 #define DPRINTF(fmt, args...) do { } while (0)
43 #endif
44 #define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0)
45
46 #define USE_MPCxxx /* Intel model is broken, for now */
47
48 #if defined (USE_INTEL_GW80314)
49 /* Intel GW80314 I/O Companion chip */
50
51 #define MAX_CPU 4
52 #define MAX_IRQ 32
53 #define MAX_DBL 4
54 #define MAX_MBX 4
55 #define MAX_TMR 4
56 #define VECTOR_BITS 8
57 #define MAX_IPI 0
58
59 #define VID (0x00000000)
60
61 #define OPENPIC_LITTLE_ENDIAN 1
62 #define OPENPIC_BIG_ENDIAN 0
63
64 #elif defined(USE_MPCxxx)
65
66 #define MAX_CPU 2
67 #define MAX_IRQ 64
68 #define EXT_IRQ 48
69 #define MAX_DBL 0
70 #define MAX_MBX 0
71 #define MAX_TMR 4
72 #define VECTOR_BITS 8
73 #define MAX_IPI 4
74 #define VID 0x03 /* MPIC version ID */
75 #define VENI 0x00000000 /* Vendor ID */
76
77 enum {
78 IRQ_IPVP = 0,
79 IRQ_IDE,
80 };
81
82 #define OPENPIC_LITTLE_ENDIAN 1
83 #define OPENPIC_BIG_ENDIAN 0
84
85 #else
86 #error "Please select which OpenPic implementation is to be emulated"
87 #endif
88
89 #if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \
90 (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN)
91 #define OPENPIC_SWAP
92 #endif
93
94 /* Interrupt definitions */
95 #define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */
96 #define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */
97 #define IRQ_TIM0 (EXT_IRQ + 2) /* First timer IRQ */
98 #if MAX_IPI > 0
99 #define IRQ_IPI0 (IRQ_TIM0 + MAX_TMR) /* First IPI IRQ */
100 #define IRQ_DBL0 (IRQ_IPI0 + (MAX_CPU * MAX_IPI)) /* First doorbell IRQ */
101 #else
102 #define IRQ_DBL0 (IRQ_TIM0 + MAX_TMR) /* First doorbell IRQ */
103 #define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */
104 #endif
105
106 #define BF_WIDTH(_bits_) \
107 (((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
108
109 static inline void set_bit (uint32_t *field, int bit)
110 {
111 field[bit >> 5] |= 1 << (bit & 0x1F);
112 }
113
114 static inline void reset_bit (uint32_t *field, int bit)
115 {
116 field[bit >> 5] &= ~(1 << (bit & 0x1F));
117 }
118
119 static inline int test_bit (uint32_t *field, int bit)
120 {
121 return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
122 }
123
124 enum {
125 IRQ_EXTERNAL = 0x01,
126 IRQ_INTERNAL = 0x02,
127 IRQ_TIMER = 0x04,
128 IRQ_SPECIAL = 0x08,
129 } IRQ_src_type;
130
131 typedef struct IRQ_queue_t {
132 uint32_t queue[BF_WIDTH(MAX_IRQ)];
133 int next;
134 int priority;
135 } IRQ_queue_t;
136
137 typedef struct IRQ_src_t {
138 uint32_t ipvp; /* IRQ vector/priority register */
139 uint32_t ide; /* IRQ destination register */
140 int type;
141 int last_cpu;
142 int pending; /* TRUE if IRQ is pending */
143 } IRQ_src_t;
144
145 enum IPVP_bits {
146 IPVP_MASK = 31,
147 IPVP_ACTIVITY = 30,
148 IPVP_MODE = 29,
149 IPVP_POLARITY = 23,
150 IPVP_SENSE = 22,
151 };
152 #define IPVP_PRIORITY_MASK (0x1F << 16)
153 #define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16))
154 #define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1)
155 #define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
156
157 typedef struct IRQ_dst_t {
158 uint32_t pctp; /* CPU current task priority */
159 uint32_t pcsr; /* CPU sensitivity register */
160 IRQ_queue_t raised;
161 IRQ_queue_t servicing;
162 CPUState *env; /* Needed if we did SMP */
163 } IRQ_dst_t;
164
165 struct openpic_t {
166 PCIDevice pci_dev;
167 int mem_index;
168 /* Global registers */
169 uint32_t frep; /* Feature reporting register */
170 uint32_t glbc; /* Global configuration register */
171 uint32_t micr; /* MPIC interrupt configuration register */
172 uint32_t veni; /* Vendor identification register */
173 uint32_t spve; /* Spurious vector register */
174 uint32_t tifr; /* Timer frequency reporting register */
175 /* Source registers */
176 IRQ_src_t src[MAX_IRQ];
177 /* Local registers per output pin */
178 IRQ_dst_t dst[MAX_CPU];
179 int nb_cpus;
180 /* Timer registers */
181 struct {
182 uint32_t ticc; /* Global timer current count register */
183 uint32_t tibc; /* Global timer base count register */
184 } timers[MAX_TMR];
185 #if MAX_DBL > 0
186 /* Doorbell registers */
187 uint32_t dar; /* Doorbell activate register */
188 struct {
189 uint32_t dmr; /* Doorbell messaging register */
190 } doorbells[MAX_DBL];
191 #endif
192 #if MAX_MBX > 0
193 /* Mailbox registers */
194 struct {
195 uint32_t mbr; /* Mailbox register */
196 } mailboxes[MAX_MAILBOXES];
197 #endif
198 };
199
200 static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
201 {
202 set_bit(q->queue, n_IRQ);
203 }
204
205 static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
206 {
207 reset_bit(q->queue, n_IRQ);
208 }
209
210 static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
211 {
212 return test_bit(q->queue, n_IRQ);
213 }
214
215 static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
216 {
217 int next, i;
218 int priority;
219
220 next = -1;
221 priority = -1;
222 for (i = 0; i < MAX_IRQ; i++) {
223 if (IRQ_testbit(q, i)) {
224 DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n",
225 i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
226 if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
227 next = i;
228 priority = IPVP_PRIORITY(opp->src[i].ipvp);
229 }
230 }
231 }
232 q->next = next;
233 q->priority = priority;
234 }
235
236 static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q)
237 {
238 if (q->next == -1) {
239 /* XXX: optimize */
240 IRQ_check(opp, q);
241 }
242
243 return q->next;
244 }
245
246 static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
247 {
248 IRQ_dst_t *dst;
249 IRQ_src_t *src;
250 int priority;
251
252 dst = &opp->dst[n_CPU];
253 src = &opp->src[n_IRQ];
254 priority = IPVP_PRIORITY(src->ipvp);
255 if (priority <= dst->pctp) {
256 /* Too low priority */
257 return;
258 }
259 if (IRQ_testbit(&dst->raised, n_IRQ)) {
260 /* Interrupt miss */
261 return;
262 }
263 set_bit(&src->ipvp, IPVP_ACTIVITY);
264 IRQ_setbit(&dst->raised, n_IRQ);
265 if (priority > dst->raised.priority) {
266 IRQ_get_next(opp, &dst->raised);
267 DPRINTF("Raise CPU IRQ\n");
268 /* XXX: choose the correct cpu */
269 cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
270 }
271 }
272
273 /* update pic state because registers for n_IRQ have changed value */
274 static void openpic_update_irq(openpic_t *opp, int n_IRQ)
275 {
276 IRQ_src_t *src;
277 int i;
278
279 src = &opp->src[n_IRQ];
280
281 if (!src->pending) {
282 /* no irq pending */
283 return;
284 }
285 if (test_bit(&src->ipvp, IPVP_MASK)) {
286 /* Interrupt source is disabled */
287 return;
288 }
289 if (IPVP_PRIORITY(src->ipvp) == 0) {
290 /* Priority set to zero */
291 return;
292 }
293 if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
294 /* IRQ already active */
295 return;
296 }
297 if (src->ide == 0x00000000) {
298 /* No target */
299 return;
300 }
301
302 if (!test_bit(&src->ipvp, IPVP_MODE) ||
303 src->ide == (1 << src->last_cpu)) {
304 /* Directed delivery mode */
305 for (i = 0; i < opp->nb_cpus; i++) {
306 if (test_bit(&src->ide, i))
307 IRQ_local_pipe(opp, i, n_IRQ);
308 }
309 } else {
310 /* Distributed delivery mode */
311 /* XXX: incorrect code */
312 for (i = src->last_cpu; i < src->last_cpu; i++) {
313 if (i == MAX_IRQ)
314 i = 0;
315 if (test_bit(&src->ide, i)) {
316 IRQ_local_pipe(opp, i, n_IRQ);
317 src->last_cpu = i;
318 break;
319 }
320 }
321 }
322 }
323
324 void openpic_set_irq(void *opaque, int n_IRQ, int level)
325 {
326 openpic_t *opp = opaque;
327 IRQ_src_t *src;
328
329 src = &opp->src[n_IRQ];
330 DPRINTF("openpic: set irq %d = %d ipvp=%08x\n",
331 n_IRQ, level, src->ipvp);
332 if (test_bit(&src->ipvp, IPVP_SENSE)) {
333 /* level-sensitive irq */
334 src->pending = level;
335 if (!level)
336 reset_bit(&src->ipvp, IPVP_ACTIVITY);
337 } else {
338 /* edge-sensitive irq */
339 if (level)
340 src->pending = 1;
341 }
342 openpic_update_irq(opp, n_IRQ);
343 }
344
345 static void openpic_reset (openpic_t *opp)
346 {
347 int i;
348
349 opp->glbc = 0x80000000;
350 /* Initialise controller registers */
351 opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID;
352 opp->veni = VENI;
353 opp->spve = 0x000000FF;
354 opp->tifr = 0x003F7A00;
355 /* ? */
356 opp->micr = 0x00000000;
357 /* Initialise IRQ sources */
358 for (i = 0; i < MAX_IRQ; i++) {
359 opp->src[i].ipvp = 0xA0000000;
360 opp->src[i].ide = 0x00000000;
361 }
362 /* Initialise IRQ destinations */
363 for (i = 0; i < opp->nb_cpus; i++) {
364 opp->dst[i].pctp = 0x0000000F;
365 opp->dst[i].pcsr = 0x00000000;
366 memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t));
367 memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
368 }
369 /* Initialise timers */
370 for (i = 0; i < MAX_TMR; i++) {
371 opp->timers[i].ticc = 0x00000000;
372 opp->timers[i].tibc = 0x80000000;
373 }
374 /* Initialise doorbells */
375 #if MAX_DBL > 0
376 opp->dar = 0x00000000;
377 for (i = 0; i < MAX_DBL; i++) {
378 opp->doorbells[i].dmr = 0x00000000;
379 }
380 #endif
381 /* Initialise mailboxes */
382 #if MAX_MBX > 0
383 for (i = 0; i < MAX_MBX; i++) { /* ? */
384 opp->mailboxes[i].mbr = 0x00000000;
385 }
386 #endif
387 /* Go out of RESET state */
388 opp->glbc = 0x00000000;
389 }
390
391 static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg)
392 {
393 uint32_t retval;
394
395 switch (reg) {
396 case IRQ_IPVP:
397 retval = opp->src[n_IRQ].ipvp;
398 break;
399 case IRQ_IDE:
400 retval = opp->src[n_IRQ].ide;
401 break;
402 }
403
404 return retval;
405 }
406
407 static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
408 uint32_t reg, uint32_t val)
409 {
410 uint32_t tmp;
411
412 switch (reg) {
413 case IRQ_IPVP:
414 /* NOTE: not fully accurate for special IRQs, but simple and
415 sufficient */
416 /* ACTIVITY bit is read-only */
417 opp->src[n_IRQ].ipvp =
418 (opp->src[n_IRQ].ipvp & 0x40000000) |
419 (val & 0x800F00FF);
420 openpic_update_irq(opp, n_IRQ);
421 DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n",
422 n_IRQ, val, opp->src[n_IRQ].ipvp);
423 break;
424 case IRQ_IDE:
425 tmp = val & 0xC0000000;
426 tmp |= val & ((1 << MAX_CPU) - 1);
427 opp->src[n_IRQ].ide = tmp;
428 DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
429 break;
430 }
431 }
432
433 #if 0 // Code provision for Intel model
434 #if MAX_DBL > 0
435 static uint32_t read_doorbell_register (openpic_t *opp,
436 int n_dbl, uint32_t offset)
437 {
438 uint32_t retval;
439
440 switch (offset) {
441 case DBL_IPVP_OFFSET:
442 retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP);
443 break;
444 case DBL_IDE_OFFSET:
445 retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE);
446 break;
447 case DBL_DMR_OFFSET:
448 retval = opp->doorbells[n_dbl].dmr;
449 break;
450 }
451
452 return retval;
453 }
454
455 static void write_doorbell_register (penpic_t *opp, int n_dbl,
456 uint32_t offset, uint32_t value)
457 {
458 switch (offset) {
459 case DBL_IVPR_OFFSET:
460 write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value);
461 break;
462 case DBL_IDE_OFFSET:
463 write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value);
464 break;
465 case DBL_DMR_OFFSET:
466 opp->doorbells[n_dbl].dmr = value;
467 break;
468 }
469 }
470 #endif
471
472 #if MAX_MBX > 0
473 static uint32_t read_mailbox_register (openpic_t *opp,
474 int n_mbx, uint32_t offset)
475 {
476 uint32_t retval;
477
478 switch (offset) {
479 case MBX_MBR_OFFSET:
480 retval = opp->mailboxes[n_mbx].mbr;
481 break;
482 case MBX_IVPR_OFFSET:
483 retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP);
484 break;
485 case MBX_DMR_OFFSET:
486 retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE);
487 break;
488 }
489
490 return retval;
491 }
492
493 static void write_mailbox_register (openpic_t *opp, int n_mbx,
494 uint32_t address, uint32_t value)
495 {
496 switch (offset) {
497 case MBX_MBR_OFFSET:
498 opp->mailboxes[n_mbx].mbr = value;
499 break;
500 case MBX_IVPR_OFFSET:
501 write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value);
502 break;
503 case MBX_DMR_OFFSET:
504 write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value);
505 break;
506 }
507 }
508 #endif
509 #endif /* 0 : Code provision for Intel model */
510
511 static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val)
512 {
513 openpic_t *opp = opaque;
514
515 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
516 if (addr & 0xF)
517 return;
518 #if defined OPENPIC_SWAP
519 val = bswap32(val);
520 #endif
521 addr &= 0xFF;
522 switch (addr) {
523 case 0x00: /* FREP */
524 break;
525 case 0x20: /* GLBC */
526 if (val & 0x80000000)
527 openpic_reset(opp);
528 opp->glbc = val & ~0x80000000;
529 break;
530 case 0x80: /* VENI */
531 break;
532 case 0x90: /* PINT */
533 /* XXX: Should be able to reset any CPU */
534 if (val & 1) {
535 DPRINTF("Reset CPU IRQ\n");
536 // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET);
537 }
538 break;
539 #if MAX_IPI > 0
540 case 0xA0: /* IPI_IPVP */
541 case 0xB0:
542 case 0xC0:
543 case 0xD0:
544 {
545 int idx;
546 idx = (addr - 0xA0) >> 4;
547 write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP, val);
548 }
549 break;
550 #endif
551 case 0xE0: /* SPVE */
552 opp->spve = val & 0x000000FF;
553 break;
554 case 0xF0: /* TIFR */
555 opp->tifr = val;
556 break;
557 default:
558 break;
559 }
560 }
561
562 static uint32_t openpic_gbl_read (void *opaque, uint32_t addr)
563 {
564 openpic_t *opp = opaque;
565 uint32_t retval;
566
567 DPRINTF("%s: addr %08x\n", __func__, addr);
568 retval = 0xFFFFFFFF;
569 if (addr & 0xF)
570 return retval;
571 addr &= 0xFF;
572 switch (addr) {
573 case 0x00: /* FREP */
574 retval = opp->frep;
575 break;
576 case 0x20: /* GLBC */
577 retval = opp->glbc;
578 break;
579 case 0x80: /* VENI */
580 retval = opp->veni;
581 break;
582 case 0x90: /* PINT */
583 retval = 0x00000000;
584 break;
585 #if MAX_IPI > 0
586 case 0xA0: /* IPI_IPVP */
587 case 0xB0:
588 case 0xC0:
589 case 0xD0:
590 {
591 int idx;
592 idx = (addr - 0xA0) >> 4;
593 retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP);
594 }
595 break;
596 #endif
597 case 0xE0: /* SPVE */
598 retval = opp->spve;
599 break;
600 case 0xF0: /* TIFR */
601 retval = opp->tifr;
602 break;
603 default:
604 break;
605 }
606 DPRINTF("%s: => %08x\n", __func__, retval);
607 #if defined OPENPIC_SWAP
608 retval = bswap32(retval);
609 #endif
610
611 return retval;
612 }
613
614 static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
615 {
616 openpic_t *opp = opaque;
617 int idx;
618
619 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
620 if (addr & 0xF)
621 return;
622 #if defined OPENPIC_SWAP
623 val = bswap32(val);
624 #endif
625 addr -= 0x1100;
626 addr &= 0xFFFF;
627 idx = (addr & 0xFFF0) >> 6;
628 addr = addr & 0x30;
629 switch (addr) {
630 case 0x00: /* TICC */
631 break;
632 case 0x10: /* TIBC */
633 if ((opp->timers[idx].ticc & 0x80000000) != 0 &&
634 (val & 0x80000000) == 0 &&
635 (opp->timers[idx].tibc & 0x80000000) != 0)
636 opp->timers[idx].ticc &= ~0x80000000;
637 opp->timers[idx].tibc = val;
638 break;
639 case 0x20: /* TIVP */
640 write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP, val);
641 break;
642 case 0x30: /* TIDE */
643 write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE, val);
644 break;
645 }
646 }
647
648 static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
649 {
650 openpic_t *opp = opaque;
651 uint32_t retval;
652 int idx;
653
654 DPRINTF("%s: addr %08x\n", __func__, addr);
655 retval = 0xFFFFFFFF;
656 if (addr & 0xF)
657 return retval;
658 addr -= 0x1100;
659 addr &= 0xFFFF;
660 idx = (addr & 0xFFF0) >> 6;
661 addr = addr & 0x30;
662 switch (addr) {
663 case 0x00: /* TICC */
664 retval = opp->timers[idx].ticc;
665 break;
666 case 0x10: /* TIBC */
667 retval = opp->timers[idx].tibc;
668 break;
669 case 0x20: /* TIPV */
670 retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP);
671 break;
672 case 0x30: /* TIDE */
673 retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE);
674 break;
675 }
676 DPRINTF("%s: => %08x\n", __func__, retval);
677 #if defined OPENPIC_SWAP
678 retval = bswap32(retval);
679 #endif
680
681 return retval;
682 }
683
684 static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val)
685 {
686 openpic_t *opp = opaque;
687 int idx;
688
689 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
690 if (addr & 0xF)
691 return;
692 #if defined OPENPIC_SWAP
693 val = tswap32(val);
694 #endif
695 addr = addr & 0xFFF0;
696 idx = addr >> 5;
697 if (addr & 0x10) {
698 /* EXDE / IFEDE / IEEDE */
699 write_IRQreg(opp, idx, IRQ_IDE, val);
700 } else {
701 /* EXVP / IFEVP / IEEVP */
702 write_IRQreg(opp, idx, IRQ_IPVP, val);
703 }
704 }
705
706 static uint32_t openpic_src_read (void *opaque, uint32_t addr)
707 {
708 openpic_t *opp = opaque;
709 uint32_t retval;
710 int idx;
711
712 DPRINTF("%s: addr %08x\n", __func__, addr);
713 retval = 0xFFFFFFFF;
714 if (addr & 0xF)
715 return retval;
716 addr = addr & 0xFFF0;
717 idx = addr >> 5;
718 if (addr & 0x10) {
719 /* EXDE / IFEDE / IEEDE */
720 retval = read_IRQreg(opp, idx, IRQ_IDE);
721 } else {
722 /* EXVP / IFEVP / IEEVP */
723 retval = read_IRQreg(opp, idx, IRQ_IPVP);
724 }
725 DPRINTF("%s: => %08x\n", __func__, retval);
726 #if defined OPENPIC_SWAP
727 retval = tswap32(retval);
728 #endif
729
730 return retval;
731 }
732
733 static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
734 {
735 openpic_t *opp = opaque;
736 IRQ_src_t *src;
737 IRQ_dst_t *dst;
738 int idx, n_IRQ;
739
740 DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
741 if (addr & 0xF)
742 return;
743 #if defined OPENPIC_SWAP
744 val = bswap32(val);
745 #endif
746 addr &= 0x1FFF0;
747 idx = addr / 0x1000;
748 dst = &opp->dst[idx];
749 addr &= 0xFF0;
750 switch (addr) {
751 #if MAX_IPI > 0
752 case 0x40: /* PIPD */
753 case 0x50:
754 case 0x60:
755 case 0x70:
756 idx = (addr - 0x40) >> 4;
757 write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE, val);
758 openpic_set_irq(opp, IRQ_IPI0 + idx, 1);
759 openpic_set_irq(opp, IRQ_IPI0 + idx, 0);
760 break;
761 #endif
762 case 0x80: /* PCTP */
763 dst->pctp = val & 0x0000000F;
764 break;
765 case 0x90: /* WHOAMI */
766 /* Read-only register */
767 break;
768 case 0xA0: /* PIAC */
769 /* Read-only register */
770 break;
771 case 0xB0: /* PEOI */
772 DPRINTF("PEOI\n");
773 n_IRQ = IRQ_get_next(opp, &dst->servicing);
774 IRQ_resetbit(&dst->servicing, n_IRQ);
775 dst->servicing.next = -1;
776 src = &opp->src[n_IRQ];
777 /* Set up next servicing IRQ */
778 IRQ_get_next(opp, &dst->servicing);
779 /* Check queued interrupts. */
780 n_IRQ = IRQ_get_next(opp, &dst->raised);
781 if (n_IRQ != -1) {
782 src = &opp->src[n_IRQ];
783 if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) {
784 DPRINTF("Raise CPU IRQ\n");
785 /* XXX: choose cpu */
786 cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
787 }
788 }
789 break;
790 default:
791 break;
792 }
793 }
794
795 static uint32_t openpic_cpu_read (void *opaque, uint32_t addr)
796 {
797 openpic_t *opp = opaque;
798 IRQ_src_t *src;
799 IRQ_dst_t *dst;
800 uint32_t retval;
801 int idx, n_IRQ;
802
803 DPRINTF("%s: addr %08x\n", __func__, addr);
804 retval = 0xFFFFFFFF;
805 if (addr & 0xF)
806 return retval;
807 addr &= 0x1FFF0;
808 idx = addr / 0x1000;
809 dst = &opp->dst[idx];
810 addr &= 0xFF0;
811 switch (addr) {
812 case 0x80: /* PCTP */
813 retval = dst->pctp;
814 break;
815 case 0x90: /* WHOAMI */
816 retval = idx;
817 break;
818 case 0xA0: /* PIAC */
819 n_IRQ = IRQ_get_next(opp, &dst->raised);
820 DPRINTF("PIAC: irq=%d\n", n_IRQ);
821 if (n_IRQ == -1) {
822 /* No more interrupt pending */
823 retval = opp->spve;
824 } else {
825 src = &opp->src[n_IRQ];
826 if (!test_bit(&src->ipvp, IPVP_ACTIVITY) ||
827 !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) {
828 /* - Spurious level-sensitive IRQ
829 * - Priorities has been changed
830 * and the pending IRQ isn't allowed anymore
831 */
832 reset_bit(&src->ipvp, IPVP_ACTIVITY);
833 retval = IPVP_VECTOR(opp->spve);
834 } else {
835 /* IRQ enter servicing state */
836 IRQ_setbit(&dst->servicing, n_IRQ);
837 retval = IPVP_VECTOR(src->ipvp);
838 }
839 IRQ_resetbit(&dst->raised, n_IRQ);
840 dst->raised.next = -1;
841 if (!test_bit(&src->ipvp, IPVP_SENSE)) {
842 /* edge-sensitive IRQ */
843 reset_bit(&src->ipvp, IPVP_ACTIVITY);
844 src->pending = 0;
845 }
846 }
847 break;
848 case 0xB0: /* PEOI */
849 retval = 0;
850 break;
851 #if MAX_IPI > 0
852 case 0x40: /* IDE */
853 case 0x50:
854 idx = (addr - 0x40) >> 4;
855 retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE);
856 break;
857 #endif
858 default:
859 break;
860 }
861 DPRINTF("%s: => %08x\n", __func__, retval);
862 #if defined OPENPIC_SWAP
863 retval= bswap32(retval);
864 #endif
865
866 return retval;
867 }
868
869 static void openpic_buggy_write (void *opaque,
870 target_phys_addr_t addr, uint32_t val)
871 {
872 printf("Invalid OPENPIC write access !\n");
873 }
874
875 static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr)
876 {
877 printf("Invalid OPENPIC read access !\n");
878
879 return -1;
880 }
881
882 static void openpic_writel (void *opaque,
883 target_phys_addr_t addr, uint32_t val)
884 {
885 openpic_t *opp = opaque;
886
887 addr &= 0x3FFFF;
888 DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
889 if (addr < 0x1100) {
890 /* Global registers */
891 openpic_gbl_write(opp, addr, val);
892 } else if (addr < 0x10000) {
893 /* Timers registers */
894 openpic_timer_write(opp, addr, val);
895 } else if (addr < 0x20000) {
896 /* Source registers */
897 openpic_src_write(opp, addr, val);
898 } else {
899 /* CPU registers */
900 openpic_cpu_write(opp, addr, val);
901 }
902 }
903
904 static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr)
905 {
906 openpic_t *opp = opaque;
907 uint32_t retval;
908
909 addr &= 0x3FFFF;
910 DPRINTF("%s: offset %08x\n", __func__, (int)addr);
911 if (addr < 0x1100) {
912 /* Global registers */
913 retval = openpic_gbl_read(opp, addr);
914 } else if (addr < 0x10000) {
915 /* Timers registers */
916 retval = openpic_timer_read(opp, addr);
917 } else if (addr < 0x20000) {
918 /* Source registers */
919 retval = openpic_src_read(opp, addr);
920 } else {
921 /* CPU registers */
922 retval = openpic_cpu_read(opp, addr);
923 }
924
925 return retval;
926 }
927
928 static CPUWriteMemoryFunc *openpic_write[] = {
929 &openpic_buggy_write,
930 &openpic_buggy_write,
931 &openpic_writel,
932 };
933
934 static CPUReadMemoryFunc *openpic_read[] = {
935 &openpic_buggy_read,
936 &openpic_buggy_read,
937 &openpic_readl,
938 };
939
940 static void openpic_map(PCIDevice *pci_dev, int region_num,
941 uint32_t addr, uint32_t size, int type)
942 {
943 openpic_t *opp;
944
945 DPRINTF("Map OpenPIC\n");
946 opp = (openpic_t *)pci_dev;
947 /* Global registers */
948 DPRINTF("Register OPENPIC gbl %08x => %08x\n",
949 addr + 0x1000, addr + 0x1000 + 0x100);
950 /* Timer registers */
951 DPRINTF("Register OPENPIC timer %08x => %08x\n",
952 addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR);
953 /* Interrupt source registers */
954 DPRINTF("Register OPENPIC src %08x => %08x\n",
955 addr + 0x10000, addr + 0x10000 + 0x20 * (EXT_IRQ + 2));
956 /* Per CPU registers */
957 DPRINTF("Register OPENPIC dst %08x => %08x\n",
958 addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU);
959 cpu_register_physical_memory(addr, 0x40000, opp->mem_index);
960 #if 0 // Don't implement ISU for now
961 opp_io_memory = cpu_register_io_memory(0, openpic_src_read,
962 openpic_src_write);
963 cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2),
964 opp_io_memory);
965 #endif
966 }
967
968 openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus)
969 {
970 openpic_t *opp;
971 uint8_t *pci_conf;
972 int i, m;
973
974 /* XXX: for now, only one CPU is supported */
975 if (nb_cpus != 1)
976 return NULL;
977 if (bus) {
978 opp = (openpic_t *)pci_register_device(bus, "OpenPIC", sizeof(openpic_t),
979 -1, NULL, NULL);
980 if (opp == NULL)
981 return NULL;
982 pci_conf = opp->pci_dev.config;
983 pci_conf[0x00] = 0x14; // IBM MPIC2
984 pci_conf[0x01] = 0x10;
985 pci_conf[0x02] = 0xFF;
986 pci_conf[0x03] = 0xFF;
987 pci_conf[0x0a] = 0x80; // PIC
988 pci_conf[0x0b] = 0x08;
989 pci_conf[0x0e] = 0x00; // header_type
990 pci_conf[0x3d] = 0x00; // no interrupt pin
991
992 /* Register I/O spaces */
993 pci_register_io_region((PCIDevice *)opp, 0, 0x40000,
994 PCI_ADDRESS_SPACE_MEM, &openpic_map);
995 } else {
996 opp = qemu_mallocz(sizeof(openpic_t));
997 }
998
999 opp->mem_index = cpu_register_io_memory(0, openpic_read,
1000 openpic_write, opp);
1001
1002 // isu_base &= 0xFFFC0000;
1003 opp->nb_cpus = nb_cpus;
1004 /* Set IRQ types */
1005 for (i = 0; i < EXT_IRQ; i++) {
1006 opp->src[i].type = IRQ_EXTERNAL;
1007 }
1008 for (; i < IRQ_TIM0; i++) {
1009 opp->src[i].type = IRQ_SPECIAL;
1010 }
1011 #if MAX_IPI > 0
1012 m = IRQ_IPI0;
1013 #else
1014 m = IRQ_DBL0;
1015 #endif
1016 for (; i < m; i++) {
1017 opp->src[i].type = IRQ_TIMER;
1018 }
1019 for (; i < MAX_IRQ; i++) {
1020 opp->src[i].type = IRQ_INTERNAL;
1021 }
1022 openpic_reset(opp);
1023 if (pmem_index)
1024 *pmem_index = opp->mem_index;
1025 return opp;
1026 }