]> git.proxmox.com Git - mirror_qemu.git/blame - hw/openpic.c
Unify IRQ handling.
[mirror_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;
7668a27f 162 CPUState *env;
dbda808a
FB
163} IRQ_dst_t;
164
d537cf6c 165typedef struct openpic_t {
dbda808a 166 PCIDevice pci_dev;
47103572 167 SetIRQFunc *set_irq;
91d848eb 168 int mem_index;
dbda808a
FB
169 /* Global registers */
170 uint32_t frep; /* Feature reporting register */
171 uint32_t glbc; /* Global configuration register */
172 uint32_t micr; /* MPIC interrupt configuration register */
173 uint32_t veni; /* Vendor identification register */
174 uint32_t spve; /* Spurious vector register */
175 uint32_t tifr; /* Timer frequency reporting register */
176 /* Source registers */
177 IRQ_src_t src[MAX_IRQ];
178 /* Local registers per output pin */
179 IRQ_dst_t dst[MAX_CPU];
180 int nb_cpus;
181 /* Timer registers */
182 struct {
183 uint32_t ticc; /* Global timer current count register */
184 uint32_t tibc; /* Global timer base count register */
185 } timers[MAX_TMR];
186#if MAX_DBL > 0
187 /* Doorbell registers */
188 uint32_t dar; /* Doorbell activate register */
189 struct {
190 uint32_t dmr; /* Doorbell messaging register */
191 } doorbells[MAX_DBL];
192#endif
193#if MAX_MBX > 0
194 /* Mailbox registers */
195 struct {
196 uint32_t mbr; /* Mailbox register */
197 } mailboxes[MAX_MAILBOXES];
198#endif
d537cf6c 199} openpic_t;
dbda808a
FB
200
201static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
202{
203 set_bit(q->queue, n_IRQ);
204}
205
206static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
207{
208 reset_bit(q->queue, n_IRQ);
209}
210
211static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
212{
213 return test_bit(q->queue, n_IRQ);
214}
215
216static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
217{
218 int next, i;
219 int priority;
220
221 next = -1;
222 priority = -1;
223 for (i = 0; i < MAX_IRQ; i++) {
224 if (IRQ_testbit(q, i)) {
611493d9
FB
225 DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n",
226 i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
dbda808a
FB
227 if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
228 next = i;
229 priority = IPVP_PRIORITY(opp->src[i].ipvp);
230 }
231 }
232 }
233 q->next = next;
234 q->priority = priority;
235}
236
237static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q)
238{
239 if (q->next == -1) {
611493d9 240 /* XXX: optimize */
dbda808a
FB
241 IRQ_check(opp, q);
242 }
243
244 return q->next;
245}
246
247static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
248{
249 IRQ_dst_t *dst;
250 IRQ_src_t *src;
251 int priority;
252
253 dst = &opp->dst[n_CPU];
254 src = &opp->src[n_IRQ];
255 priority = IPVP_PRIORITY(src->ipvp);
256 if (priority <= dst->pctp) {
257 /* Too low priority */
258 return;
259 }
260 if (IRQ_testbit(&dst->raised, n_IRQ)) {
261 /* Interrupt miss */
262 return;
263 }
264 set_bit(&src->ipvp, IPVP_ACTIVITY);
265 IRQ_setbit(&dst->raised, n_IRQ);
266 if (priority > dst->raised.priority) {
267 IRQ_get_next(opp, &dst->raised);
47103572
JM
268 DPRINTF("Raise CPU IRQ fn %p env %p\n", opp->set_irq, dst->env);
269 opp->set_irq(dst->env, OPENPIC_EVT_INT, 1);
dbda808a
FB
270 }
271}
272
611493d9
FB
273/* update pic state because registers for n_IRQ have changed value */
274static void openpic_update_irq(openpic_t *opp, int n_IRQ)
dbda808a
FB
275{
276 IRQ_src_t *src;
277 int i;
278
279 src = &opp->src[n_IRQ];
611493d9
FB
280
281 if (!src->pending) {
282 /* no irq pending */
283 return;
284 }
285 if (test_bit(&src->ipvp, IPVP_MASK)) {
dbda808a
FB
286 /* Interrupt source is disabled */
287 return;
288 }
289 if (IPVP_PRIORITY(src->ipvp) == 0) {
290 /* Priority set to zero */
291 return;
292 }
611493d9
FB
293 if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
294 /* IRQ already active */
295 return;
296 }
dbda808a
FB
297 if (src->ide == 0x00000000) {
298 /* No target */
299 return;
300 }
611493d9
FB
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 }
dbda808a 309 } else {
611493d9
FB
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
d537cf6c 324static void openpic_set_irq(void *opaque, int n_IRQ, int level)
611493d9 325{
54fa5af5 326 openpic_t *opp = opaque;
611493d9
FB
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;
dbda808a 341 }
611493d9 342 openpic_update_irq(opp, n_IRQ);
dbda808a
FB
343}
344
345static void openpic_reset (openpic_t *opp)
346{
347 int i;
348
349 opp->glbc = 0x80000000;
f8407028 350 /* Initialise controller registers */
dbda808a
FB
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
391static 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
407static 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:
611493d9
FB
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);
dbda808a
FB
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
435static 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
455static 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
473static 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
493static 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
511static 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");
47103572 536 // opp->set_irq(dst->env, OPENPIC_EVT_RESET, 1);
dbda808a
FB
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
562static 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
614static 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 &&
8adbc566 634 (val & 0x80000000) == 0 &&
dbda808a
FB
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
648static 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
684static 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
706static 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
733static 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);
611493d9
FB
758 openpic_set_irq(opp, IRQ_IPI0 + idx, 1);
759 openpic_set_irq(opp, IRQ_IPI0 + idx, 0);
dbda808a
FB
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");
47103572 785 opp->set_irq(dst->env, OPENPIC_EVT_INT, 1);
dbda808a
FB
786 }
787 }
788 break;
789 default:
790 break;
791 }
792}
793
794static uint32_t openpic_cpu_read (void *opaque, uint32_t addr)
795{
796 openpic_t *opp = opaque;
797 IRQ_src_t *src;
798 IRQ_dst_t *dst;
799 uint32_t retval;
800 int idx, n_IRQ;
801
802 DPRINTF("%s: addr %08x\n", __func__, addr);
803 retval = 0xFFFFFFFF;
804 if (addr & 0xF)
805 return retval;
806 addr &= 0x1FFF0;
807 idx = addr / 0x1000;
808 dst = &opp->dst[idx];
809 addr &= 0xFF0;
810 switch (addr) {
811 case 0x80: /* PCTP */
812 retval = dst->pctp;
813 break;
814 case 0x90: /* WHOAMI */
815 retval = idx;
816 break;
817 case 0xA0: /* PIAC */
818 n_IRQ = IRQ_get_next(opp, &dst->raised);
819 DPRINTF("PIAC: irq=%d\n", n_IRQ);
820 if (n_IRQ == -1) {
821 /* No more interrupt pending */
822 retval = opp->spve;
823 } else {
824 src = &opp->src[n_IRQ];
825 if (!test_bit(&src->ipvp, IPVP_ACTIVITY) ||
826 !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) {
827 /* - Spurious level-sensitive IRQ
828 * - Priorities has been changed
829 * and the pending IRQ isn't allowed anymore
830 */
831 reset_bit(&src->ipvp, IPVP_ACTIVITY);
832 retval = IPVP_VECTOR(opp->spve);
833 } else {
834 /* IRQ enter servicing state */
835 IRQ_setbit(&dst->servicing, n_IRQ);
836 retval = IPVP_VECTOR(src->ipvp);
837 }
838 IRQ_resetbit(&dst->raised, n_IRQ);
839 dst->raised.next = -1;
611493d9
FB
840 if (!test_bit(&src->ipvp, IPVP_SENSE)) {
841 /* edge-sensitive IRQ */
dbda808a 842 reset_bit(&src->ipvp, IPVP_ACTIVITY);
611493d9
FB
843 src->pending = 0;
844 }
dbda808a
FB
845 }
846 break;
847 case 0xB0: /* PEOI */
848 retval = 0;
849 break;
850#if MAX_IPI > 0
851 case 0x40: /* IDE */
852 case 0x50:
853 idx = (addr - 0x40) >> 4;
854 retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE);
855 break;
856#endif
857 default:
858 break;
859 }
860 DPRINTF("%s: => %08x\n", __func__, retval);
861#if defined OPENPIC_SWAP
862 retval= bswap32(retval);
863#endif
864
865 return retval;
866}
867
868static void openpic_buggy_write (void *opaque,
869 target_phys_addr_t addr, uint32_t val)
870{
871 printf("Invalid OPENPIC write access !\n");
872}
873
874static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr)
875{
876 printf("Invalid OPENPIC read access !\n");
877
878 return -1;
879}
880
881static void openpic_writel (void *opaque,
882 target_phys_addr_t addr, uint32_t val)
883{
884 openpic_t *opp = opaque;
885
886 addr &= 0x3FFFF;
611493d9 887 DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
dbda808a
FB
888 if (addr < 0x1100) {
889 /* Global registers */
890 openpic_gbl_write(opp, addr, val);
891 } else if (addr < 0x10000) {
892 /* Timers registers */
893 openpic_timer_write(opp, addr, val);
894 } else if (addr < 0x20000) {
895 /* Source registers */
896 openpic_src_write(opp, addr, val);
897 } else {
898 /* CPU registers */
899 openpic_cpu_write(opp, addr, val);
900 }
901}
902
903static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr)
904{
905 openpic_t *opp = opaque;
906 uint32_t retval;
907
908 addr &= 0x3FFFF;
611493d9 909 DPRINTF("%s: offset %08x\n", __func__, (int)addr);
dbda808a
FB
910 if (addr < 0x1100) {
911 /* Global registers */
912 retval = openpic_gbl_read(opp, addr);
913 } else if (addr < 0x10000) {
914 /* Timers registers */
915 retval = openpic_timer_read(opp, addr);
916 } else if (addr < 0x20000) {
917 /* Source registers */
918 retval = openpic_src_read(opp, addr);
919 } else {
920 /* CPU registers */
921 retval = openpic_cpu_read(opp, addr);
922 }
923
924 return retval;
925}
926
927static CPUWriteMemoryFunc *openpic_write[] = {
928 &openpic_buggy_write,
929 &openpic_buggy_write,
930 &openpic_writel,
931};
932
933static CPUReadMemoryFunc *openpic_read[] = {
934 &openpic_buggy_read,
935 &openpic_buggy_read,
936 &openpic_readl,
937};
938
939static void openpic_map(PCIDevice *pci_dev, int region_num,
940 uint32_t addr, uint32_t size, int type)
941{
942 openpic_t *opp;
dbda808a
FB
943
944 DPRINTF("Map OpenPIC\n");
945 opp = (openpic_t *)pci_dev;
946 /* Global registers */
947 DPRINTF("Register OPENPIC gbl %08x => %08x\n",
948 addr + 0x1000, addr + 0x1000 + 0x100);
949 /* Timer registers */
950 DPRINTF("Register OPENPIC timer %08x => %08x\n",
951 addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR);
952 /* Interrupt source registers */
953 DPRINTF("Register OPENPIC src %08x => %08x\n",
954 addr + 0x10000, addr + 0x10000 + 0x20 * (EXT_IRQ + 2));
955 /* Per CPU registers */
956 DPRINTF("Register OPENPIC dst %08x => %08x\n",
957 addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU);
91d848eb 958 cpu_register_physical_memory(addr, 0x40000, opp->mem_index);
dbda808a
FB
959#if 0 // Don't implement ISU for now
960 opp_io_memory = cpu_register_io_memory(0, openpic_src_read,
961 openpic_src_write);
962 cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2),
963 opp_io_memory);
964#endif
965}
966
d537cf6c 967qemu_irq *openpic_init (PCIBus *bus, SetIRQFunc *set_irq,
05a8096f 968 int *pmem_index, int nb_cpus, CPUState **envp)
dbda808a
FB
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;
91d848eb
FB
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 }
47103572 998 opp->set_irq = set_irq;
91d848eb
FB
999 opp->mem_index = cpu_register_io_memory(0, openpic_read,
1000 openpic_write, opp);
1001
1002 // isu_base &= 0xFFFC0000;
dbda808a
FB
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 }
7668a27f
FB
1022 for (i = 0; i < nb_cpus; i++)
1023 opp->dst[i].env = envp[i];
dbda808a 1024 openpic_reset(opp);
91d848eb
FB
1025 if (pmem_index)
1026 *pmem_index = opp->mem_index;
d537cf6c 1027 return qemu_allocate_irqs(openpic_set_irq, opp, MAX_IRQ);
dbda808a 1028}