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