xref: /qemu/hw/intc/openpic.c (revision 457279cb)
17702e47cSPaolo Bonzini /*
27702e47cSPaolo Bonzini  * OpenPIC emulation
37702e47cSPaolo Bonzini  *
47702e47cSPaolo Bonzini  * Copyright (c) 2004 Jocelyn Mayer
57702e47cSPaolo Bonzini  *               2011 Alexander Graf
67702e47cSPaolo Bonzini  *
77702e47cSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
87702e47cSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
97702e47cSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
107702e47cSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
117702e47cSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
127702e47cSPaolo Bonzini  * furnished to do so, subject to the following conditions:
137702e47cSPaolo Bonzini  *
147702e47cSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
157702e47cSPaolo Bonzini  * all copies or substantial portions of the Software.
167702e47cSPaolo Bonzini  *
177702e47cSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187702e47cSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
197702e47cSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
207702e47cSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
217702e47cSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
227702e47cSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
237702e47cSPaolo Bonzini  * THE SOFTWARE.
247702e47cSPaolo Bonzini  */
257702e47cSPaolo Bonzini /*
267702e47cSPaolo Bonzini  *
277702e47cSPaolo Bonzini  * Based on OpenPic implementations:
287702e47cSPaolo Bonzini  * - Intel GW80314 I/O companion chip developer's manual
297702e47cSPaolo Bonzini  * - Motorola MPC8245 & MPC8540 user manuals.
307702e47cSPaolo Bonzini  * - Motorola MCP750 (aka Raven) programmer manual.
317702e47cSPaolo Bonzini  * - Motorola Harrier programmer manuel
327702e47cSPaolo Bonzini  *
337702e47cSPaolo Bonzini  * Serial interrupts, as implemented in Raven chipset are not supported yet.
347702e47cSPaolo Bonzini  *
357702e47cSPaolo Bonzini  */
360b8fa32fSMarkus Armbruster 
3790191d07SPeter Maydell #include "qemu/osdep.h"
3864552b6bSMarkus Armbruster #include "hw/irq.h"
397702e47cSPaolo Bonzini #include "hw/ppc/mac.h"
407702e47cSPaolo Bonzini #include "hw/pci/pci.h"
417702e47cSPaolo Bonzini #include "hw/ppc/openpic.h"
422b927571SAndreas Färber #include "hw/ppc/ppc_e500.h"
43a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
447702e47cSPaolo Bonzini #include "hw/sysbus.h"
45d6454270SMarkus Armbruster #include "migration/vmstate.h"
467702e47cSPaolo Bonzini #include "hw/pci/msi.h"
47da34e65cSMarkus Armbruster #include "qapi/error.h"
487702e47cSPaolo Bonzini #include "qemu/bitops.h"
4973d963c0SMichael Roth #include "qapi/qmp/qerror.h"
500b8fa32fSMarkus Armbruster #include "qemu/module.h"
51ddd5140bSAaron Larson #include "qemu/timer.h"
52df592270SMichael Davidsaver #include "qemu/error-report.h"
537702e47cSPaolo Bonzini 
547702e47cSPaolo Bonzini //#define DEBUG_OPENPIC
557702e47cSPaolo Bonzini 
567702e47cSPaolo Bonzini #ifdef DEBUG_OPENPIC
577702e47cSPaolo Bonzini static const int debug_openpic = 1;
587702e47cSPaolo Bonzini #else
597702e47cSPaolo Bonzini static const int debug_openpic = 0;
607702e47cSPaolo Bonzini #endif
617702e47cSPaolo Bonzini 
62ddd5140bSAaron Larson static int get_current_cpu(void);
637702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) do { \
647702e47cSPaolo Bonzini         if (debug_openpic) { \
65df592270SMichael Davidsaver             info_report("Core%d: " fmt, get_current_cpu(), ## __VA_ARGS__); \
667702e47cSPaolo Bonzini         } \
677702e47cSPaolo Bonzini     } while (0)
687702e47cSPaolo Bonzini 
697702e47cSPaolo Bonzini /* OpenPIC capability flags */
707702e47cSPaolo Bonzini #define OPENPIC_FLAG_IDR_CRIT     (1 << 0)
717702e47cSPaolo Bonzini #define OPENPIC_FLAG_ILR          (2 << 0)
727702e47cSPaolo Bonzini 
737702e47cSPaolo Bonzini /* OpenPIC address map */
747702e47cSPaolo Bonzini #define OPENPIC_GLB_REG_START        0x0
757702e47cSPaolo Bonzini #define OPENPIC_GLB_REG_SIZE         0x10F0
767702e47cSPaolo Bonzini #define OPENPIC_TMR_REG_START        0x10F0
777702e47cSPaolo Bonzini #define OPENPIC_TMR_REG_SIZE         0x220
787702e47cSPaolo Bonzini #define OPENPIC_MSI_REG_START        0x1600
797702e47cSPaolo Bonzini #define OPENPIC_MSI_REG_SIZE         0x200
807702e47cSPaolo Bonzini #define OPENPIC_SUMMARY_REG_START   0x3800
817702e47cSPaolo Bonzini #define OPENPIC_SUMMARY_REG_SIZE    0x800
827702e47cSPaolo Bonzini #define OPENPIC_SRC_REG_START        0x10000
838935a442SScott Wood #define OPENPIC_SRC_REG_SIZE         (OPENPIC_MAX_SRC * 0x20)
847702e47cSPaolo Bonzini #define OPENPIC_CPU_REG_START        0x20000
857702e47cSPaolo Bonzini #define OPENPIC_CPU_REG_SIZE         0x100 + ((MAX_CPU - 1) * 0x1000)
867702e47cSPaolo Bonzini 
877702e47cSPaolo Bonzini static FslMpicInfo fsl_mpic_20 = {
887702e47cSPaolo Bonzini     .max_ext = 12,
897702e47cSPaolo Bonzini };
907702e47cSPaolo Bonzini 
917702e47cSPaolo Bonzini static FslMpicInfo fsl_mpic_42 = {
927702e47cSPaolo Bonzini     .max_ext = 12,
937702e47cSPaolo Bonzini };
947702e47cSPaolo Bonzini 
957702e47cSPaolo Bonzini #define FRR_NIRQ_SHIFT    16
967702e47cSPaolo Bonzini #define FRR_NCPU_SHIFT     8
977702e47cSPaolo Bonzini #define FRR_VID_SHIFT      0
987702e47cSPaolo Bonzini 
997702e47cSPaolo Bonzini #define VID_REVISION_1_2   2
1007702e47cSPaolo Bonzini #define VID_REVISION_1_3   3
1017702e47cSPaolo Bonzini 
1027702e47cSPaolo Bonzini #define VIR_GENERIC      0x00000000 /* Generic Vendor ID */
10358b62835SBenjamin Herrenschmidt #define VIR_MPIC2A       0x00004614 /* IBM MPIC-2A */
1047702e47cSPaolo Bonzini 
1057702e47cSPaolo Bonzini #define GCR_RESET        0x80000000
1067702e47cSPaolo Bonzini #define GCR_MODE_PASS    0x00000000
1077702e47cSPaolo Bonzini #define GCR_MODE_MIXED   0x20000000
1087702e47cSPaolo Bonzini #define GCR_MODE_PROXY   0x60000000
1097702e47cSPaolo Bonzini 
1107702e47cSPaolo Bonzini #define TBCR_CI           0x80000000 /* count inhibit */
1117702e47cSPaolo Bonzini #define TCCR_TOG          0x80000000 /* toggles when decrement to zero */
1127702e47cSPaolo Bonzini 
1137702e47cSPaolo Bonzini #define IDR_EP_SHIFT      31
114def60298SPeter Maydell #define IDR_EP_MASK       (1U << IDR_EP_SHIFT)
1157702e47cSPaolo Bonzini #define IDR_CI0_SHIFT     30
1167702e47cSPaolo Bonzini #define IDR_CI1_SHIFT     29
1177702e47cSPaolo Bonzini #define IDR_P1_SHIFT      1
1187702e47cSPaolo Bonzini #define IDR_P0_SHIFT      0
1197702e47cSPaolo Bonzini 
1207702e47cSPaolo Bonzini #define ILR_INTTGT_MASK   0x000000ff
1217702e47cSPaolo Bonzini #define ILR_INTTGT_INT    0x00
1227702e47cSPaolo Bonzini #define ILR_INTTGT_CINT   0x01 /* critical */
1237702e47cSPaolo Bonzini #define ILR_INTTGT_MCP    0x02 /* machine check */
1247702e47cSPaolo Bonzini 
1257702e47cSPaolo Bonzini /* The currently supported INTTGT values happen to be the same as QEMU's
1267702e47cSPaolo Bonzini  * openpic output codes, but don't depend on this.  The output codes
1277702e47cSPaolo Bonzini  * could change (unlikely, but...) or support could be added for
1287702e47cSPaolo Bonzini  * more INTTGT values.
1297702e47cSPaolo Bonzini  */
1307702e47cSPaolo Bonzini static const int inttgt_output[][2] = {
1317702e47cSPaolo Bonzini     { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT },
1327702e47cSPaolo Bonzini     { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT },
1337702e47cSPaolo Bonzini     { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK },
1347702e47cSPaolo Bonzini };
1357702e47cSPaolo Bonzini 
1367702e47cSPaolo Bonzini static int inttgt_to_output(int inttgt)
1377702e47cSPaolo Bonzini {
1387702e47cSPaolo Bonzini     int i;
1397702e47cSPaolo Bonzini 
1407702e47cSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
1417702e47cSPaolo Bonzini         if (inttgt_output[i][0] == inttgt) {
1427702e47cSPaolo Bonzini             return inttgt_output[i][1];
1437702e47cSPaolo Bonzini         }
1447702e47cSPaolo Bonzini     }
1457702e47cSPaolo Bonzini 
146df592270SMichael Davidsaver     error_report("%s: unsupported inttgt %d", __func__, inttgt);
1477702e47cSPaolo Bonzini     return OPENPIC_OUTPUT_INT;
1487702e47cSPaolo Bonzini }
1497702e47cSPaolo Bonzini 
1507702e47cSPaolo Bonzini static int output_to_inttgt(int output)
1517702e47cSPaolo Bonzini {
1527702e47cSPaolo Bonzini     int i;
1537702e47cSPaolo Bonzini 
1547702e47cSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
1557702e47cSPaolo Bonzini         if (inttgt_output[i][1] == output) {
1567702e47cSPaolo Bonzini             return inttgt_output[i][0];
1577702e47cSPaolo Bonzini         }
1587702e47cSPaolo Bonzini     }
1597702e47cSPaolo Bonzini 
1607702e47cSPaolo Bonzini     abort();
1617702e47cSPaolo Bonzini }
1627702e47cSPaolo Bonzini 
1637702e47cSPaolo Bonzini #define MSIIR_OFFSET       0x140
1647702e47cSPaolo Bonzini #define MSIIR_SRS_SHIFT    29
1657702e47cSPaolo Bonzini #define MSIIR_SRS_MASK     (0x7 << MSIIR_SRS_SHIFT)
1667702e47cSPaolo Bonzini #define MSIIR_IBS_SHIFT    24
1677702e47cSPaolo Bonzini #define MSIIR_IBS_MASK     (0x1f << MSIIR_IBS_SHIFT)
1687702e47cSPaolo Bonzini 
1697702e47cSPaolo Bonzini static int get_current_cpu(void)
1707702e47cSPaolo Bonzini {
1714917cf44SAndreas Färber     if (!current_cpu) {
1727702e47cSPaolo Bonzini         return -1;
1737702e47cSPaolo Bonzini     }
1747702e47cSPaolo Bonzini 
1754917cf44SAndreas Färber     return current_cpu->cpu_index;
1767702e47cSPaolo Bonzini }
1777702e47cSPaolo Bonzini 
1787702e47cSPaolo Bonzini static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
1797702e47cSPaolo Bonzini                                           int idx);
1807702e47cSPaolo Bonzini static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
1817702e47cSPaolo Bonzini                                        uint32_t val, int idx);
1828ebe65f3SPaul Janzen static void openpic_reset(DeviceState *d);
1837702e47cSPaolo Bonzini 
184ddd5140bSAaron Larson /* Convert between openpic clock ticks and nanosecs.  In the hardware the clock
185ddd5140bSAaron Larson    frequency is driven by board inputs to the PIC which the PIC would then
186ddd5140bSAaron Larson    divide by 4 or 8.  For now hard code to 25MZ.
187ddd5140bSAaron Larson */
188ddd5140bSAaron Larson #define OPENPIC_TIMER_FREQ_MHZ 25
189ddd5140bSAaron Larson #define OPENPIC_TIMER_NS_PER_TICK (1000 / OPENPIC_TIMER_FREQ_MHZ)
190ddd5140bSAaron Larson static inline uint64_t ns_to_ticks(uint64_t ns)
191ddd5140bSAaron Larson {
192ddd5140bSAaron Larson     return ns    / OPENPIC_TIMER_NS_PER_TICK;
193ddd5140bSAaron Larson }
194ddd5140bSAaron Larson static inline uint64_t ticks_to_ns(uint64_t ticks)
195ddd5140bSAaron Larson {
196ddd5140bSAaron Larson     return ticks * OPENPIC_TIMER_NS_PER_TICK;
197ddd5140bSAaron Larson }
198ddd5140bSAaron Larson 
1997702e47cSPaolo Bonzini static inline void IRQ_setbit(IRQQueue *q, int n_IRQ)
2007702e47cSPaolo Bonzini {
2017702e47cSPaolo Bonzini     set_bit(n_IRQ, q->queue);
2027702e47cSPaolo Bonzini }
2037702e47cSPaolo Bonzini 
2047702e47cSPaolo Bonzini static inline void IRQ_resetbit(IRQQueue *q, int n_IRQ)
2057702e47cSPaolo Bonzini {
2067702e47cSPaolo Bonzini     clear_bit(n_IRQ, q->queue);
2077702e47cSPaolo Bonzini }
2087702e47cSPaolo Bonzini 
2097702e47cSPaolo Bonzini static void IRQ_check(OpenPICState *opp, IRQQueue *q)
2107702e47cSPaolo Bonzini {
2117702e47cSPaolo Bonzini     int irq = -1;
2127702e47cSPaolo Bonzini     int next = -1;
2137702e47cSPaolo Bonzini     int priority = -1;
2147702e47cSPaolo Bonzini 
2157702e47cSPaolo Bonzini     for (;;) {
2167702e47cSPaolo Bonzini         irq = find_next_bit(q->queue, opp->max_irq, irq + 1);
2177702e47cSPaolo Bonzini         if (irq == opp->max_irq) {
2187702e47cSPaolo Bonzini             break;
2197702e47cSPaolo Bonzini         }
2207702e47cSPaolo Bonzini 
221df592270SMichael Davidsaver         DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d",
2227702e47cSPaolo Bonzini                 irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority);
2237702e47cSPaolo Bonzini 
2247702e47cSPaolo Bonzini         if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) {
2257702e47cSPaolo Bonzini             next = irq;
2267702e47cSPaolo Bonzini             priority = IVPR_PRIORITY(opp->src[irq].ivpr);
2277702e47cSPaolo Bonzini         }
2287702e47cSPaolo Bonzini     }
2297702e47cSPaolo Bonzini 
2307702e47cSPaolo Bonzini     q->next = next;
2317702e47cSPaolo Bonzini     q->priority = priority;
2327702e47cSPaolo Bonzini }
2337702e47cSPaolo Bonzini 
2347702e47cSPaolo Bonzini static int IRQ_get_next(OpenPICState *opp, IRQQueue *q)
2357702e47cSPaolo Bonzini {
2367702e47cSPaolo Bonzini     /* XXX: optimize */
2377702e47cSPaolo Bonzini     IRQ_check(opp, q);
2387702e47cSPaolo Bonzini 
2397702e47cSPaolo Bonzini     return q->next;
2407702e47cSPaolo Bonzini }
2417702e47cSPaolo Bonzini 
2427702e47cSPaolo Bonzini static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ,
2437702e47cSPaolo Bonzini                            bool active, bool was_active)
2447702e47cSPaolo Bonzini {
2457702e47cSPaolo Bonzini     IRQDest *dst;
2467702e47cSPaolo Bonzini     IRQSource *src;
2477702e47cSPaolo Bonzini     int priority;
2487702e47cSPaolo Bonzini 
2497702e47cSPaolo Bonzini     dst = &opp->dst[n_CPU];
2507702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
2517702e47cSPaolo Bonzini 
252df592270SMichael Davidsaver     DPRINTF("%s: IRQ %d active %d was %d",
2537702e47cSPaolo Bonzini             __func__, n_IRQ, active, was_active);
2547702e47cSPaolo Bonzini 
2557702e47cSPaolo Bonzini     if (src->output != OPENPIC_OUTPUT_INT) {
256df592270SMichael Davidsaver         DPRINTF("%s: output %d irq %d active %d was %d count %d",
2577702e47cSPaolo Bonzini                 __func__, src->output, n_IRQ, active, was_active,
2587702e47cSPaolo Bonzini                 dst->outputs_active[src->output]);
2597702e47cSPaolo Bonzini 
2607702e47cSPaolo Bonzini         /* On Freescale MPIC, critical interrupts ignore priority,
2617702e47cSPaolo Bonzini          * IACK, EOI, etc.  Before MPIC v4.1 they also ignore
2627702e47cSPaolo Bonzini          * masking.
2637702e47cSPaolo Bonzini          */
2647702e47cSPaolo Bonzini         if (active) {
2657702e47cSPaolo Bonzini             if (!was_active && dst->outputs_active[src->output]++ == 0) {
266df592270SMichael Davidsaver                 DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d",
2677702e47cSPaolo Bonzini                         __func__, src->output, n_CPU, n_IRQ);
2687702e47cSPaolo Bonzini                 qemu_irq_raise(dst->irqs[src->output]);
2697702e47cSPaolo Bonzini             }
2707702e47cSPaolo Bonzini         } else {
2717702e47cSPaolo Bonzini             if (was_active && --dst->outputs_active[src->output] == 0) {
272df592270SMichael Davidsaver                 DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d",
2737702e47cSPaolo Bonzini                         __func__, src->output, n_CPU, n_IRQ);
2747702e47cSPaolo Bonzini                 qemu_irq_lower(dst->irqs[src->output]);
2757702e47cSPaolo Bonzini             }
2767702e47cSPaolo Bonzini         }
2777702e47cSPaolo Bonzini 
2787702e47cSPaolo Bonzini         return;
2797702e47cSPaolo Bonzini     }
2807702e47cSPaolo Bonzini 
2817702e47cSPaolo Bonzini     priority = IVPR_PRIORITY(src->ivpr);
2827702e47cSPaolo Bonzini 
2837702e47cSPaolo Bonzini     /* Even if the interrupt doesn't have enough priority,
2847702e47cSPaolo Bonzini      * it is still raised, in case ctpr is lowered later.
2857702e47cSPaolo Bonzini      */
2867702e47cSPaolo Bonzini     if (active) {
2877702e47cSPaolo Bonzini         IRQ_setbit(&dst->raised, n_IRQ);
2887702e47cSPaolo Bonzini     } else {
2897702e47cSPaolo Bonzini         IRQ_resetbit(&dst->raised, n_IRQ);
2907702e47cSPaolo Bonzini     }
2917702e47cSPaolo Bonzini 
2927702e47cSPaolo Bonzini     IRQ_check(opp, &dst->raised);
2937702e47cSPaolo Bonzini 
2947702e47cSPaolo Bonzini     if (active && priority <= dst->ctpr) {
295df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d",
2967702e47cSPaolo Bonzini                 __func__, n_IRQ, priority, dst->ctpr, n_CPU);
2977702e47cSPaolo Bonzini         active = 0;
2987702e47cSPaolo Bonzini     }
2997702e47cSPaolo Bonzini 
3007702e47cSPaolo Bonzini     if (active) {
3017702e47cSPaolo Bonzini         if (IRQ_get_next(opp, &dst->servicing) >= 0 &&
3027702e47cSPaolo Bonzini                 priority <= dst->servicing.priority) {
303df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d",
3047702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->servicing.next, n_CPU);
3057702e47cSPaolo Bonzini         } else {
306df592270SMichael Davidsaver             DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d",
3077702e47cSPaolo Bonzini                     __func__, n_CPU, n_IRQ, dst->raised.next);
3087702e47cSPaolo Bonzini             qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
3097702e47cSPaolo Bonzini         }
3107702e47cSPaolo Bonzini     } else {
3117702e47cSPaolo Bonzini         IRQ_get_next(opp, &dst->servicing);
3127702e47cSPaolo Bonzini         if (dst->raised.priority > dst->ctpr &&
3137702e47cSPaolo Bonzini                 dst->raised.priority > dst->servicing.priority) {
314df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d",
3157702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->raised.next, dst->raised.priority,
3167702e47cSPaolo Bonzini                     dst->ctpr, dst->servicing.priority, n_CPU);
3177702e47cSPaolo Bonzini             /* IRQ line stays asserted */
3187702e47cSPaolo Bonzini         } else {
319df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d",
3207702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU);
3217702e47cSPaolo Bonzini             qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
3227702e47cSPaolo Bonzini         }
3237702e47cSPaolo Bonzini     }
3247702e47cSPaolo Bonzini }
3257702e47cSPaolo Bonzini 
3267702e47cSPaolo Bonzini /* update pic state because registers for n_IRQ have changed value */
3277702e47cSPaolo Bonzini static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
3287702e47cSPaolo Bonzini {
3297702e47cSPaolo Bonzini     IRQSource *src;
3307702e47cSPaolo Bonzini     bool active, was_active;
3317702e47cSPaolo Bonzini     int i;
3327702e47cSPaolo Bonzini 
3337702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
3347702e47cSPaolo Bonzini     active = src->pending;
3357702e47cSPaolo Bonzini 
3367702e47cSPaolo Bonzini     if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) {
3377702e47cSPaolo Bonzini         /* Interrupt source is disabled */
338df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d is disabled", __func__, n_IRQ);
3397702e47cSPaolo Bonzini         active = false;
3407702e47cSPaolo Bonzini     }
3417702e47cSPaolo Bonzini 
3427702e47cSPaolo Bonzini     was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK);
3437702e47cSPaolo Bonzini 
3447702e47cSPaolo Bonzini     /*
3457702e47cSPaolo Bonzini      * We don't have a similar check for already-active because
3467702e47cSPaolo Bonzini      * ctpr may have changed and we need to withdraw the interrupt.
3477702e47cSPaolo Bonzini      */
3487702e47cSPaolo Bonzini     if (!active && !was_active) {
349df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d is already inactive", __func__, n_IRQ);
3507702e47cSPaolo Bonzini         return;
3517702e47cSPaolo Bonzini     }
3527702e47cSPaolo Bonzini 
3537702e47cSPaolo Bonzini     if (active) {
3547702e47cSPaolo Bonzini         src->ivpr |= IVPR_ACTIVITY_MASK;
3557702e47cSPaolo Bonzini     } else {
3567702e47cSPaolo Bonzini         src->ivpr &= ~IVPR_ACTIVITY_MASK;
3577702e47cSPaolo Bonzini     }
3587702e47cSPaolo Bonzini 
3597702e47cSPaolo Bonzini     if (src->destmask == 0) {
3607702e47cSPaolo Bonzini         /* No target */
361df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d has no target", __func__, n_IRQ);
3627702e47cSPaolo Bonzini         return;
3637702e47cSPaolo Bonzini     }
3647702e47cSPaolo Bonzini 
3657702e47cSPaolo Bonzini     if (src->destmask == (1 << src->last_cpu)) {
3667702e47cSPaolo Bonzini         /* Only one CPU is allowed to receive this IRQ */
3677702e47cSPaolo Bonzini         IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active);
3687702e47cSPaolo Bonzini     } else if (!(src->ivpr & IVPR_MODE_MASK)) {
3697702e47cSPaolo Bonzini         /* Directed delivery mode */
3707702e47cSPaolo Bonzini         for (i = 0; i < opp->nb_cpus; i++) {
3717702e47cSPaolo Bonzini             if (src->destmask & (1 << i)) {
3727702e47cSPaolo Bonzini                 IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
3737702e47cSPaolo Bonzini             }
3747702e47cSPaolo Bonzini         }
3757702e47cSPaolo Bonzini     } else {
3767702e47cSPaolo Bonzini         /* Distributed delivery mode */
3777702e47cSPaolo Bonzini         for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
3787702e47cSPaolo Bonzini             if (i == opp->nb_cpus) {
3797702e47cSPaolo Bonzini                 i = 0;
3807702e47cSPaolo Bonzini             }
3817702e47cSPaolo Bonzini             if (src->destmask & (1 << i)) {
3827702e47cSPaolo Bonzini                 IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
3837702e47cSPaolo Bonzini                 src->last_cpu = i;
3847702e47cSPaolo Bonzini                 break;
3857702e47cSPaolo Bonzini             }
3867702e47cSPaolo Bonzini         }
3877702e47cSPaolo Bonzini     }
3887702e47cSPaolo Bonzini }
3897702e47cSPaolo Bonzini 
3907702e47cSPaolo Bonzini static void openpic_set_irq(void *opaque, int n_IRQ, int level)
3917702e47cSPaolo Bonzini {
3927702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
3937702e47cSPaolo Bonzini     IRQSource *src;
3947702e47cSPaolo Bonzini 
3958935a442SScott Wood     if (n_IRQ >= OPENPIC_MAX_IRQ) {
396df592270SMichael Davidsaver         error_report("%s: IRQ %d out of range", __func__, n_IRQ);
3977702e47cSPaolo Bonzini         abort();
3987702e47cSPaolo Bonzini     }
3997702e47cSPaolo Bonzini 
4007702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
401df592270SMichael Davidsaver     DPRINTF("openpic: set irq %d = %d ivpr=0x%08x",
4027702e47cSPaolo Bonzini             n_IRQ, level, src->ivpr);
4037702e47cSPaolo Bonzini     if (src->level) {
4047702e47cSPaolo Bonzini         /* level-sensitive irq */
4057702e47cSPaolo Bonzini         src->pending = level;
4067702e47cSPaolo Bonzini         openpic_update_irq(opp, n_IRQ);
4077702e47cSPaolo Bonzini     } else {
4087702e47cSPaolo Bonzini         /* edge-sensitive irq */
4097702e47cSPaolo Bonzini         if (level) {
4107702e47cSPaolo Bonzini             src->pending = 1;
4117702e47cSPaolo Bonzini             openpic_update_irq(opp, n_IRQ);
4127702e47cSPaolo Bonzini         }
4137702e47cSPaolo Bonzini 
4147702e47cSPaolo Bonzini         if (src->output != OPENPIC_OUTPUT_INT) {
4157702e47cSPaolo Bonzini             /* Edge-triggered interrupts shouldn't be used
4167702e47cSPaolo Bonzini              * with non-INT delivery, but just in case,
4177702e47cSPaolo Bonzini              * try to make it do something sane rather than
4187702e47cSPaolo Bonzini              * cause an interrupt storm.  This is close to
4197702e47cSPaolo Bonzini              * what you'd probably see happen in real hardware.
4207702e47cSPaolo Bonzini              */
4217702e47cSPaolo Bonzini             src->pending = 0;
4227702e47cSPaolo Bonzini             openpic_update_irq(opp, n_IRQ);
4237702e47cSPaolo Bonzini         }
4247702e47cSPaolo Bonzini     }
4257702e47cSPaolo Bonzini }
4267702e47cSPaolo Bonzini 
4277702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ)
4287702e47cSPaolo Bonzini {
4297702e47cSPaolo Bonzini     return opp->src[n_IRQ].idr;
4307702e47cSPaolo Bonzini }
4317702e47cSPaolo Bonzini 
4327702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ)
4337702e47cSPaolo Bonzini {
4347702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_ILR) {
4357702e47cSPaolo Bonzini         return output_to_inttgt(opp->src[n_IRQ].output);
4367702e47cSPaolo Bonzini     }
4377702e47cSPaolo Bonzini 
4387702e47cSPaolo Bonzini     return 0xffffffff;
4397702e47cSPaolo Bonzini }
4407702e47cSPaolo Bonzini 
4417702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ)
4427702e47cSPaolo Bonzini {
4437702e47cSPaolo Bonzini     return opp->src[n_IRQ].ivpr;
4447702e47cSPaolo Bonzini }
4457702e47cSPaolo Bonzini 
4467702e47cSPaolo Bonzini static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val)
4477702e47cSPaolo Bonzini {
4487702e47cSPaolo Bonzini     IRQSource *src = &opp->src[n_IRQ];
4497702e47cSPaolo Bonzini     uint32_t normal_mask = (1UL << opp->nb_cpus) - 1;
4507702e47cSPaolo Bonzini     uint32_t crit_mask = 0;
4517702e47cSPaolo Bonzini     uint32_t mask = normal_mask;
4527702e47cSPaolo Bonzini     int crit_shift = IDR_EP_SHIFT - opp->nb_cpus;
4537702e47cSPaolo Bonzini     int i;
4547702e47cSPaolo Bonzini 
4557702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
4567702e47cSPaolo Bonzini         crit_mask = mask << crit_shift;
4577702e47cSPaolo Bonzini         mask |= crit_mask | IDR_EP;
4587702e47cSPaolo Bonzini     }
4597702e47cSPaolo Bonzini 
4607702e47cSPaolo Bonzini     src->idr = val & mask;
461df592270SMichael Davidsaver     DPRINTF("Set IDR %d to 0x%08x", n_IRQ, src->idr);
4627702e47cSPaolo Bonzini 
4637702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
4647702e47cSPaolo Bonzini         if (src->idr & crit_mask) {
4657702e47cSPaolo Bonzini             if (src->idr & normal_mask) {
4667702e47cSPaolo Bonzini                 DPRINTF("%s: IRQ configured for multiple output types, using "
467df592270SMichael Davidsaver                         "critical", __func__);
4687702e47cSPaolo Bonzini             }
4697702e47cSPaolo Bonzini 
4707702e47cSPaolo Bonzini             src->output = OPENPIC_OUTPUT_CINT;
4717702e47cSPaolo Bonzini             src->nomask = true;
4727702e47cSPaolo Bonzini             src->destmask = 0;
4737702e47cSPaolo Bonzini 
4747702e47cSPaolo Bonzini             for (i = 0; i < opp->nb_cpus; i++) {
4757702e47cSPaolo Bonzini                 int n_ci = IDR_CI0_SHIFT - i;
4767702e47cSPaolo Bonzini 
4777702e47cSPaolo Bonzini                 if (src->idr & (1UL << n_ci)) {
4787702e47cSPaolo Bonzini                     src->destmask |= 1UL << i;
4797702e47cSPaolo Bonzini                 }
4807702e47cSPaolo Bonzini             }
4817702e47cSPaolo Bonzini         } else {
4827702e47cSPaolo Bonzini             src->output = OPENPIC_OUTPUT_INT;
4837702e47cSPaolo Bonzini             src->nomask = false;
4847702e47cSPaolo Bonzini             src->destmask = src->idr & normal_mask;
4857702e47cSPaolo Bonzini         }
4867702e47cSPaolo Bonzini     } else {
4877702e47cSPaolo Bonzini         src->destmask = src->idr;
4887702e47cSPaolo Bonzini     }
4897702e47cSPaolo Bonzini }
4907702e47cSPaolo Bonzini 
4917702e47cSPaolo Bonzini static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val)
4927702e47cSPaolo Bonzini {
4937702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_ILR) {
4947702e47cSPaolo Bonzini         IRQSource *src = &opp->src[n_IRQ];
4957702e47cSPaolo Bonzini 
4967702e47cSPaolo Bonzini         src->output = inttgt_to_output(val & ILR_INTTGT_MASK);
497df592270SMichael Davidsaver         DPRINTF("Set ILR %d to 0x%08x, output %d", n_IRQ, src->idr,
4987702e47cSPaolo Bonzini                 src->output);
4997702e47cSPaolo Bonzini 
5007702e47cSPaolo Bonzini         /* TODO: on MPIC v4.0 only, set nomask for non-INT */
5017702e47cSPaolo Bonzini     }
5027702e47cSPaolo Bonzini }
5037702e47cSPaolo Bonzini 
5047702e47cSPaolo Bonzini static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val)
5057702e47cSPaolo Bonzini {
5067702e47cSPaolo Bonzini     uint32_t mask;
5077702e47cSPaolo Bonzini 
5087702e47cSPaolo Bonzini     /* NOTE when implementing newer FSL MPIC models: starting with v4.0,
5097702e47cSPaolo Bonzini      * the polarity bit is read-only on internal interrupts.
5107702e47cSPaolo Bonzini      */
5117702e47cSPaolo Bonzini     mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK |
5127702e47cSPaolo Bonzini            IVPR_POLARITY_MASK | opp->vector_mask;
5137702e47cSPaolo Bonzini 
5147702e47cSPaolo Bonzini     /* ACTIVITY bit is read-only */
5157702e47cSPaolo Bonzini     opp->src[n_IRQ].ivpr =
5167702e47cSPaolo Bonzini         (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask);
5177702e47cSPaolo Bonzini 
5187702e47cSPaolo Bonzini     /* For FSL internal interrupts, The sense bit is reserved and zero,
5197702e47cSPaolo Bonzini      * and the interrupt is always level-triggered.  Timers and IPIs
5207702e47cSPaolo Bonzini      * have no sense or polarity bits, and are edge-triggered.
5217702e47cSPaolo Bonzini      */
5227702e47cSPaolo Bonzini     switch (opp->src[n_IRQ].type) {
5237702e47cSPaolo Bonzini     case IRQ_TYPE_NORMAL:
5247702e47cSPaolo Bonzini         opp->src[n_IRQ].level = !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK);
5257702e47cSPaolo Bonzini         break;
5267702e47cSPaolo Bonzini 
5277702e47cSPaolo Bonzini     case IRQ_TYPE_FSLINT:
5287702e47cSPaolo Bonzini         opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK;
5297702e47cSPaolo Bonzini         break;
5307702e47cSPaolo Bonzini 
5317702e47cSPaolo Bonzini     case IRQ_TYPE_FSLSPECIAL:
5327702e47cSPaolo Bonzini         opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK);
5337702e47cSPaolo Bonzini         break;
5347702e47cSPaolo Bonzini     }
5357702e47cSPaolo Bonzini 
5367702e47cSPaolo Bonzini     openpic_update_irq(opp, n_IRQ);
537df592270SMichael Davidsaver     DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x", n_IRQ, val,
5387702e47cSPaolo Bonzini             opp->src[n_IRQ].ivpr);
5397702e47cSPaolo Bonzini }
5407702e47cSPaolo Bonzini 
5417702e47cSPaolo Bonzini static void openpic_gcr_write(OpenPICState *opp, uint64_t val)
5427702e47cSPaolo Bonzini {
5437702e47cSPaolo Bonzini     bool mpic_proxy = false;
5447702e47cSPaolo Bonzini 
5457702e47cSPaolo Bonzini     if (val & GCR_RESET) {
546e1766344SAndreas Färber         openpic_reset(DEVICE(opp));
5477702e47cSPaolo Bonzini         return;
5487702e47cSPaolo Bonzini     }
5497702e47cSPaolo Bonzini 
5507702e47cSPaolo Bonzini     opp->gcr &= ~opp->mpic_mode_mask;
5517702e47cSPaolo Bonzini     opp->gcr |= val & opp->mpic_mode_mask;
5527702e47cSPaolo Bonzini 
5537702e47cSPaolo Bonzini     /* Set external proxy mode */
5547702e47cSPaolo Bonzini     if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY) {
5557702e47cSPaolo Bonzini         mpic_proxy = true;
5567702e47cSPaolo Bonzini     }
5577702e47cSPaolo Bonzini 
5587702e47cSPaolo Bonzini     ppce500_set_mpic_proxy(mpic_proxy);
5597702e47cSPaolo Bonzini }
5607702e47cSPaolo Bonzini 
5617702e47cSPaolo Bonzini static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val,
5627702e47cSPaolo Bonzini                               unsigned len)
5637702e47cSPaolo Bonzini {
5647702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
5657702e47cSPaolo Bonzini     IRQDest *dst;
5667702e47cSPaolo Bonzini     int idx;
5677702e47cSPaolo Bonzini 
568df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
5697702e47cSPaolo Bonzini             __func__, addr, val);
5707702e47cSPaolo Bonzini     if (addr & 0xF) {
5717702e47cSPaolo Bonzini         return;
5727702e47cSPaolo Bonzini     }
5737702e47cSPaolo Bonzini     switch (addr) {
5747702e47cSPaolo Bonzini     case 0x00: /* Block Revision Register1 (BRR1) is Readonly */
5757702e47cSPaolo Bonzini         break;
5767702e47cSPaolo Bonzini     case 0x40:
5777702e47cSPaolo Bonzini     case 0x50:
5787702e47cSPaolo Bonzini     case 0x60:
5797702e47cSPaolo Bonzini     case 0x70:
5807702e47cSPaolo Bonzini     case 0x80:
5817702e47cSPaolo Bonzini     case 0x90:
5827702e47cSPaolo Bonzini     case 0xA0:
5837702e47cSPaolo Bonzini     case 0xB0:
5847702e47cSPaolo Bonzini         openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
5857702e47cSPaolo Bonzini         break;
5867702e47cSPaolo Bonzini     case 0x1000: /* FRR */
5877702e47cSPaolo Bonzini         break;
5887702e47cSPaolo Bonzini     case 0x1020: /* GCR */
5897702e47cSPaolo Bonzini         openpic_gcr_write(opp, val);
5907702e47cSPaolo Bonzini         break;
5917702e47cSPaolo Bonzini     case 0x1080: /* VIR */
5927702e47cSPaolo Bonzini         break;
5937702e47cSPaolo Bonzini     case 0x1090: /* PIR */
5947702e47cSPaolo Bonzini         for (idx = 0; idx < opp->nb_cpus; idx++) {
5957702e47cSPaolo Bonzini             if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) {
596df592270SMichael Davidsaver                 DPRINTF("Raise OpenPIC RESET output for CPU %d", idx);
5977702e47cSPaolo Bonzini                 dst = &opp->dst[idx];
5987702e47cSPaolo Bonzini                 qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]);
5997702e47cSPaolo Bonzini             } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) {
600df592270SMichael Davidsaver                 DPRINTF("Lower OpenPIC RESET output for CPU %d", idx);
6017702e47cSPaolo Bonzini                 dst = &opp->dst[idx];
6027702e47cSPaolo Bonzini                 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]);
6037702e47cSPaolo Bonzini             }
6047702e47cSPaolo Bonzini         }
6057702e47cSPaolo Bonzini         opp->pir = val;
6067702e47cSPaolo Bonzini         break;
6077702e47cSPaolo Bonzini     case 0x10A0: /* IPI_IVPR */
6087702e47cSPaolo Bonzini     case 0x10B0:
6097702e47cSPaolo Bonzini     case 0x10C0:
6107702e47cSPaolo Bonzini     case 0x10D0:
6117702e47cSPaolo Bonzini         {
6127702e47cSPaolo Bonzini             int idx;
6137702e47cSPaolo Bonzini             idx = (addr - 0x10A0) >> 4;
6147702e47cSPaolo Bonzini             write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val);
6157702e47cSPaolo Bonzini         }
6167702e47cSPaolo Bonzini         break;
6177702e47cSPaolo Bonzini     case 0x10E0: /* SPVE */
6187702e47cSPaolo Bonzini         opp->spve = val & opp->vector_mask;
6197702e47cSPaolo Bonzini         break;
6207702e47cSPaolo Bonzini     default:
6217702e47cSPaolo Bonzini         break;
6227702e47cSPaolo Bonzini     }
6237702e47cSPaolo Bonzini }
6247702e47cSPaolo Bonzini 
6257702e47cSPaolo Bonzini static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len)
6267702e47cSPaolo Bonzini {
6277702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
6287702e47cSPaolo Bonzini     uint32_t retval;
6297702e47cSPaolo Bonzini 
630df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
6317702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
6327702e47cSPaolo Bonzini     if (addr & 0xF) {
6337702e47cSPaolo Bonzini         return retval;
6347702e47cSPaolo Bonzini     }
6357702e47cSPaolo Bonzini     switch (addr) {
6367702e47cSPaolo Bonzini     case 0x1000: /* FRR */
6377702e47cSPaolo Bonzini         retval = opp->frr;
6387702e47cSPaolo Bonzini         break;
6397702e47cSPaolo Bonzini     case 0x1020: /* GCR */
6407702e47cSPaolo Bonzini         retval = opp->gcr;
6417702e47cSPaolo Bonzini         break;
6427702e47cSPaolo Bonzini     case 0x1080: /* VIR */
6437702e47cSPaolo Bonzini         retval = opp->vir;
6447702e47cSPaolo Bonzini         break;
6457702e47cSPaolo Bonzini     case 0x1090: /* PIR */
6467702e47cSPaolo Bonzini         retval = 0x00000000;
6477702e47cSPaolo Bonzini         break;
6487702e47cSPaolo Bonzini     case 0x00: /* Block Revision Register1 (BRR1) */
6497702e47cSPaolo Bonzini         retval = opp->brr1;
6507702e47cSPaolo Bonzini         break;
6517702e47cSPaolo Bonzini     case 0x40:
6527702e47cSPaolo Bonzini     case 0x50:
6537702e47cSPaolo Bonzini     case 0x60:
6547702e47cSPaolo Bonzini     case 0x70:
6557702e47cSPaolo Bonzini     case 0x80:
6567702e47cSPaolo Bonzini     case 0x90:
6577702e47cSPaolo Bonzini     case 0xA0:
6587702e47cSPaolo Bonzini     case 0xB0:
6597702e47cSPaolo Bonzini         retval = openpic_cpu_read_internal(opp, addr, get_current_cpu());
6607702e47cSPaolo Bonzini         break;
6617702e47cSPaolo Bonzini     case 0x10A0: /* IPI_IVPR */
6627702e47cSPaolo Bonzini     case 0x10B0:
6637702e47cSPaolo Bonzini     case 0x10C0:
6647702e47cSPaolo Bonzini     case 0x10D0:
6657702e47cSPaolo Bonzini         {
6667702e47cSPaolo Bonzini             int idx;
6677702e47cSPaolo Bonzini             idx = (addr - 0x10A0) >> 4;
6687702e47cSPaolo Bonzini             retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx);
6697702e47cSPaolo Bonzini         }
6707702e47cSPaolo Bonzini         break;
6717702e47cSPaolo Bonzini     case 0x10E0: /* SPVE */
6727702e47cSPaolo Bonzini         retval = opp->spve;
6737702e47cSPaolo Bonzini         break;
6747702e47cSPaolo Bonzini     default:
6757702e47cSPaolo Bonzini         break;
6767702e47cSPaolo Bonzini     }
677df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
6787702e47cSPaolo Bonzini 
6797702e47cSPaolo Bonzini     return retval;
6807702e47cSPaolo Bonzini }
6817702e47cSPaolo Bonzini 
682ddd5140bSAaron Larson static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled);
683ddd5140bSAaron Larson 
684ddd5140bSAaron Larson static void qemu_timer_cb(void *opaque)
685ddd5140bSAaron Larson {
686ddd5140bSAaron Larson     OpenPICTimer *tmr = opaque;
687ddd5140bSAaron Larson     OpenPICState *opp = tmr->opp;
688ddd5140bSAaron Larson     uint32_t    n_IRQ = tmr->n_IRQ;
689ddd5140bSAaron Larson     uint32_t val =   tmr->tbcr & ~TBCR_CI;
690ddd5140bSAaron Larson     uint32_t tog = ((tmr->tccr & TCCR_TOG) ^ TCCR_TOG);  /* invert toggle. */
691ddd5140bSAaron Larson 
692df592270SMichael Davidsaver     DPRINTF("%s n_IRQ=%d", __func__, n_IRQ);
693ddd5140bSAaron Larson     /* Reload current count from base count and setup timer. */
694ddd5140bSAaron Larson     tmr->tccr = val | tog;
695ddd5140bSAaron Larson     openpic_tmr_set_tmr(tmr, val, /*enabled=*/true);
696ddd5140bSAaron Larson     /* Raise the interrupt. */
697ddd5140bSAaron Larson     opp->src[n_IRQ].destmask = read_IRQreg_idr(opp, n_IRQ);
698ddd5140bSAaron Larson     openpic_set_irq(opp, n_IRQ, 1);
699ddd5140bSAaron Larson     openpic_set_irq(opp, n_IRQ, 0);
700ddd5140bSAaron Larson }
701ddd5140bSAaron Larson 
702ddd5140bSAaron Larson /* If enabled is true, arranges for an interrupt to be raised val clocks into
703ddd5140bSAaron Larson    the future, if enabled is false cancels the timer. */
704ddd5140bSAaron Larson static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled)
705ddd5140bSAaron Larson {
706ddd5140bSAaron Larson     uint64_t ns = ticks_to_ns(val & ~TCCR_TOG);
707ddd5140bSAaron Larson     /* A count of zero causes a timer to be set to expire immediately.  This
708ddd5140bSAaron Larson        effectively stops the simulation since the timer is constantly expiring
709ddd5140bSAaron Larson        which prevents guest code execution, so we don't honor that
710ddd5140bSAaron Larson        configuration.  On real hardware, this situation would generate an
711ddd5140bSAaron Larson        interrupt on every clock cycle if the interrupt was unmasked. */
712ddd5140bSAaron Larson     if ((ns == 0) || !enabled) {
713ddd5140bSAaron Larson         tmr->qemu_timer_active = false;
714ddd5140bSAaron Larson         tmr->tccr = tmr->tccr & TCCR_TOG;
715ddd5140bSAaron Larson         timer_del(tmr->qemu_timer); /* set timer to never expire. */
716ddd5140bSAaron Larson     } else {
717ddd5140bSAaron Larson         tmr->qemu_timer_active = true;
718ddd5140bSAaron Larson         uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
719ddd5140bSAaron Larson         tmr->origin_time = now;
720ddd5140bSAaron Larson         timer_mod(tmr->qemu_timer, now + ns);     /* set timer expiration. */
721ddd5140bSAaron Larson     }
722ddd5140bSAaron Larson }
723ddd5140bSAaron Larson 
724ddd5140bSAaron Larson /* Returns the currrent tccr value, i.e., timer value (in clocks) with
725ddd5140bSAaron Larson    appropriate TOG. */
726ddd5140bSAaron Larson static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr)
727ddd5140bSAaron Larson {
728ddd5140bSAaron Larson     uint64_t retval;
729ddd5140bSAaron Larson     if (!tmr->qemu_timer_active) {
730ddd5140bSAaron Larson         retval = tmr->tccr;
731ddd5140bSAaron Larson     } else {
732ddd5140bSAaron Larson         uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
733ddd5140bSAaron Larson         uint64_t used = now - tmr->origin_time;  /* nsecs */
734ddd5140bSAaron Larson         uint32_t used_ticks = (uint32_t)ns_to_ticks(used);
735ddd5140bSAaron Larson         uint32_t count = (tmr->tccr & ~TCCR_TOG) - used_ticks;
736ddd5140bSAaron Larson         retval = (uint32_t)((tmr->tccr & TCCR_TOG) | (count & ~TCCR_TOG));
737ddd5140bSAaron Larson     }
738ddd5140bSAaron Larson     return retval;
739ddd5140bSAaron Larson }
740ddd5140bSAaron Larson 
7417702e47cSPaolo Bonzini static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val,
7427702e47cSPaolo Bonzini                               unsigned len)
7437702e47cSPaolo Bonzini {
7447702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
7457702e47cSPaolo Bonzini     int idx;
7467702e47cSPaolo Bonzini 
747df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
748a09f7443SAaron Larson             __func__, (addr + 0x10f0), val);
7497702e47cSPaolo Bonzini     if (addr & 0xF) {
7507702e47cSPaolo Bonzini         return;
7517702e47cSPaolo Bonzini     }
7527702e47cSPaolo Bonzini 
753a09f7443SAaron Larson     if (addr == 0) {
7547702e47cSPaolo Bonzini         /* TFRR */
7557702e47cSPaolo Bonzini         opp->tfrr = val;
7567702e47cSPaolo Bonzini         return;
7577702e47cSPaolo Bonzini     }
758a09f7443SAaron Larson     addr -= 0x10;  /* correct for TFRR */
7597702e47cSPaolo Bonzini     idx = (addr >> 6) & 0x3;
7607702e47cSPaolo Bonzini 
7617702e47cSPaolo Bonzini     switch (addr & 0x30) {
7627702e47cSPaolo Bonzini     case 0x00: /* TCCR */
7637702e47cSPaolo Bonzini         break;
7647702e47cSPaolo Bonzini     case 0x10: /* TBCR */
765ddd5140bSAaron Larson         /* Did the enable status change? */
766ddd5140bSAaron Larson         if ((opp->timers[idx].tbcr & TBCR_CI) != (val & TBCR_CI)) {
767ddd5140bSAaron Larson             /* Did "Count Inhibit" transition from 1 to 0? */
768ddd5140bSAaron Larson             if ((val & TBCR_CI) == 0) {
769ddd5140bSAaron Larson                 opp->timers[idx].tccr = val & ~TCCR_TOG;
770ddd5140bSAaron Larson             }
771ddd5140bSAaron Larson             openpic_tmr_set_tmr(&opp->timers[idx],
772ddd5140bSAaron Larson                                 (val & ~TBCR_CI),
773ddd5140bSAaron Larson                                 /*enabled=*/((val & TBCR_CI) == 0));
7747702e47cSPaolo Bonzini         }
7757702e47cSPaolo Bonzini         opp->timers[idx].tbcr = val;
7767702e47cSPaolo Bonzini         break;
7777702e47cSPaolo Bonzini     case 0x20: /* TVPR */
7787702e47cSPaolo Bonzini         write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val);
7797702e47cSPaolo Bonzini         break;
7807702e47cSPaolo Bonzini     case 0x30: /* TDR */
7817702e47cSPaolo Bonzini         write_IRQreg_idr(opp, opp->irq_tim0 + idx, val);
7827702e47cSPaolo Bonzini         break;
7837702e47cSPaolo Bonzini     }
7847702e47cSPaolo Bonzini }
7857702e47cSPaolo Bonzini 
7867702e47cSPaolo Bonzini static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len)
7877702e47cSPaolo Bonzini {
7887702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
7897702e47cSPaolo Bonzini     uint32_t retval = -1;
7907702e47cSPaolo Bonzini     int idx;
7917702e47cSPaolo Bonzini 
792df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr + 0x10f0);
7937702e47cSPaolo Bonzini     if (addr & 0xF) {
7947702e47cSPaolo Bonzini         goto out;
7957702e47cSPaolo Bonzini     }
796a09f7443SAaron Larson     if (addr == 0) {
7977702e47cSPaolo Bonzini         /* TFRR */
7987702e47cSPaolo Bonzini         retval = opp->tfrr;
7997702e47cSPaolo Bonzini         goto out;
8007702e47cSPaolo Bonzini     }
801a09f7443SAaron Larson     addr -= 0x10;  /* correct for TFRR */
802a09f7443SAaron Larson     idx = (addr >> 6) & 0x3;
8037702e47cSPaolo Bonzini     switch (addr & 0x30) {
8047702e47cSPaolo Bonzini     case 0x00: /* TCCR */
805ddd5140bSAaron Larson         retval = openpic_tmr_get_timer(&opp->timers[idx]);
8067702e47cSPaolo Bonzini         break;
8077702e47cSPaolo Bonzini     case 0x10: /* TBCR */
8087702e47cSPaolo Bonzini         retval = opp->timers[idx].tbcr;
8097702e47cSPaolo Bonzini         break;
810a09f7443SAaron Larson     case 0x20: /* TVPR */
8117702e47cSPaolo Bonzini         retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx);
8127702e47cSPaolo Bonzini         break;
813a09f7443SAaron Larson     case 0x30: /* TDR */
8147702e47cSPaolo Bonzini         retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx);
8157702e47cSPaolo Bonzini         break;
8167702e47cSPaolo Bonzini     }
8177702e47cSPaolo Bonzini 
8187702e47cSPaolo Bonzini out:
819df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
8207702e47cSPaolo Bonzini 
8217702e47cSPaolo Bonzini     return retval;
8227702e47cSPaolo Bonzini }
8237702e47cSPaolo Bonzini 
8247702e47cSPaolo Bonzini static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val,
8257702e47cSPaolo Bonzini                               unsigned len)
8267702e47cSPaolo Bonzini {
8277702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8287702e47cSPaolo Bonzini     int idx;
8297702e47cSPaolo Bonzini 
830df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
8317702e47cSPaolo Bonzini             __func__, addr, val);
8327702e47cSPaolo Bonzini 
8337702e47cSPaolo Bonzini     addr = addr & 0xffff;
8347702e47cSPaolo Bonzini     idx = addr >> 5;
8357702e47cSPaolo Bonzini 
8367702e47cSPaolo Bonzini     switch (addr & 0x1f) {
8377702e47cSPaolo Bonzini     case 0x00:
8387702e47cSPaolo Bonzini         write_IRQreg_ivpr(opp, idx, val);
8397702e47cSPaolo Bonzini         break;
8407702e47cSPaolo Bonzini     case 0x10:
8417702e47cSPaolo Bonzini         write_IRQreg_idr(opp, idx, val);
8427702e47cSPaolo Bonzini         break;
8437702e47cSPaolo Bonzini     case 0x18:
8447702e47cSPaolo Bonzini         write_IRQreg_ilr(opp, idx, val);
8457702e47cSPaolo Bonzini         break;
8467702e47cSPaolo Bonzini     }
8477702e47cSPaolo Bonzini }
8487702e47cSPaolo Bonzini 
8497702e47cSPaolo Bonzini static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len)
8507702e47cSPaolo Bonzini {
8517702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8527702e47cSPaolo Bonzini     uint32_t retval;
8537702e47cSPaolo Bonzini     int idx;
8547702e47cSPaolo Bonzini 
855df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
8567702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
8577702e47cSPaolo Bonzini 
8587702e47cSPaolo Bonzini     addr = addr & 0xffff;
8597702e47cSPaolo Bonzini     idx = addr >> 5;
8607702e47cSPaolo Bonzini 
8617702e47cSPaolo Bonzini     switch (addr & 0x1f) {
8627702e47cSPaolo Bonzini     case 0x00:
8637702e47cSPaolo Bonzini         retval = read_IRQreg_ivpr(opp, idx);
8647702e47cSPaolo Bonzini         break;
8657702e47cSPaolo Bonzini     case 0x10:
8667702e47cSPaolo Bonzini         retval = read_IRQreg_idr(opp, idx);
8677702e47cSPaolo Bonzini         break;
8687702e47cSPaolo Bonzini     case 0x18:
8697702e47cSPaolo Bonzini         retval = read_IRQreg_ilr(opp, idx);
8707702e47cSPaolo Bonzini         break;
8717702e47cSPaolo Bonzini     }
8727702e47cSPaolo Bonzini 
873df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
8747702e47cSPaolo Bonzini     return retval;
8757702e47cSPaolo Bonzini }
8767702e47cSPaolo Bonzini 
8777702e47cSPaolo Bonzini static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val,
8787702e47cSPaolo Bonzini                               unsigned size)
8797702e47cSPaolo Bonzini {
8807702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8817702e47cSPaolo Bonzini     int idx = opp->irq_msi;
8827702e47cSPaolo Bonzini     int srs, ibs;
8837702e47cSPaolo Bonzini 
884df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64,
8857702e47cSPaolo Bonzini             __func__, addr, val);
8867702e47cSPaolo Bonzini     if (addr & 0xF) {
8877702e47cSPaolo Bonzini         return;
8887702e47cSPaolo Bonzini     }
8897702e47cSPaolo Bonzini 
8907702e47cSPaolo Bonzini     switch (addr) {
8917702e47cSPaolo Bonzini     case MSIIR_OFFSET:
8927702e47cSPaolo Bonzini         srs = val >> MSIIR_SRS_SHIFT;
8937702e47cSPaolo Bonzini         idx += srs;
8947702e47cSPaolo Bonzini         ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT;
8957702e47cSPaolo Bonzini         opp->msi[srs].msir |= 1 << ibs;
8967702e47cSPaolo Bonzini         openpic_set_irq(opp, idx, 1);
8977702e47cSPaolo Bonzini         break;
8987702e47cSPaolo Bonzini     default:
8997702e47cSPaolo Bonzini         /* most registers are read-only, thus ignored */
9007702e47cSPaolo Bonzini         break;
9017702e47cSPaolo Bonzini     }
9027702e47cSPaolo Bonzini }
9037702e47cSPaolo Bonzini 
9047702e47cSPaolo Bonzini static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size)
9057702e47cSPaolo Bonzini {
9067702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
9077702e47cSPaolo Bonzini     uint64_t r = 0;
9087702e47cSPaolo Bonzini     int i, srs;
9097702e47cSPaolo Bonzini 
910df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
9117702e47cSPaolo Bonzini     if (addr & 0xF) {
9127702e47cSPaolo Bonzini         return -1;
9137702e47cSPaolo Bonzini     }
9147702e47cSPaolo Bonzini 
9157702e47cSPaolo Bonzini     srs = addr >> 4;
9167702e47cSPaolo Bonzini 
9177702e47cSPaolo Bonzini     switch (addr) {
9187702e47cSPaolo Bonzini     case 0x00:
9197702e47cSPaolo Bonzini     case 0x10:
9207702e47cSPaolo Bonzini     case 0x20:
9217702e47cSPaolo Bonzini     case 0x30:
9227702e47cSPaolo Bonzini     case 0x40:
9237702e47cSPaolo Bonzini     case 0x50:
9247702e47cSPaolo Bonzini     case 0x60:
9257702e47cSPaolo Bonzini     case 0x70: /* MSIRs */
9267702e47cSPaolo Bonzini         r = opp->msi[srs].msir;
9277702e47cSPaolo Bonzini         /* Clear on read */
9287702e47cSPaolo Bonzini         opp->msi[srs].msir = 0;
9297702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_msi + srs, 0);
9307702e47cSPaolo Bonzini         break;
9317702e47cSPaolo Bonzini     case 0x120: /* MSISR */
9327702e47cSPaolo Bonzini         for (i = 0; i < MAX_MSI; i++) {
9337702e47cSPaolo Bonzini             r |= (opp->msi[i].msir ? 1 : 0) << i;
9347702e47cSPaolo Bonzini         }
9357702e47cSPaolo Bonzini         break;
9367702e47cSPaolo Bonzini     }
9377702e47cSPaolo Bonzini 
9387702e47cSPaolo Bonzini     return r;
9397702e47cSPaolo Bonzini }
9407702e47cSPaolo Bonzini 
9417702e47cSPaolo Bonzini static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size)
9427702e47cSPaolo Bonzini {
9437702e47cSPaolo Bonzini     uint64_t r = 0;
9447702e47cSPaolo Bonzini 
945df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
9467702e47cSPaolo Bonzini 
9477702e47cSPaolo Bonzini     /* TODO: EISR/EIMR */
9487702e47cSPaolo Bonzini 
9497702e47cSPaolo Bonzini     return r;
9507702e47cSPaolo Bonzini }
9517702e47cSPaolo Bonzini 
9527702e47cSPaolo Bonzini static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val,
9537702e47cSPaolo Bonzini                                   unsigned size)
9547702e47cSPaolo Bonzini {
955df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64,
9567702e47cSPaolo Bonzini             __func__, addr, val);
9577702e47cSPaolo Bonzini 
9587702e47cSPaolo Bonzini     /* TODO: EISR/EIMR */
9597702e47cSPaolo Bonzini }
9607702e47cSPaolo Bonzini 
9617702e47cSPaolo Bonzini static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
9627702e47cSPaolo Bonzini                                        uint32_t val, int idx)
9637702e47cSPaolo Bonzini {
9647702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
9657702e47cSPaolo Bonzini     IRQSource *src;
9667702e47cSPaolo Bonzini     IRQDest *dst;
9677702e47cSPaolo Bonzini     int s_IRQ, n_IRQ;
9687702e47cSPaolo Bonzini 
969df592270SMichael Davidsaver     DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x", __func__, idx,
9707702e47cSPaolo Bonzini             addr, val);
9717702e47cSPaolo Bonzini 
97204d2acbbSFabien Chouteau     if (idx < 0 || idx >= opp->nb_cpus) {
9737702e47cSPaolo Bonzini         return;
9747702e47cSPaolo Bonzini     }
9757702e47cSPaolo Bonzini 
9767702e47cSPaolo Bonzini     if (addr & 0xF) {
9777702e47cSPaolo Bonzini         return;
9787702e47cSPaolo Bonzini     }
9797702e47cSPaolo Bonzini     dst = &opp->dst[idx];
9807702e47cSPaolo Bonzini     addr &= 0xFF0;
9817702e47cSPaolo Bonzini     switch (addr) {
9827702e47cSPaolo Bonzini     case 0x40: /* IPIDR */
9837702e47cSPaolo Bonzini     case 0x50:
9847702e47cSPaolo Bonzini     case 0x60:
9857702e47cSPaolo Bonzini     case 0x70:
9867702e47cSPaolo Bonzini         idx = (addr - 0x40) >> 4;
9877702e47cSPaolo Bonzini         /* we use IDE as mask which CPUs to deliver the IPI to still. */
9887702e47cSPaolo Bonzini         opp->src[opp->irq_ipi0 + idx].destmask |= val;
9897702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
9907702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
9917702e47cSPaolo Bonzini         break;
9927702e47cSPaolo Bonzini     case 0x80: /* CTPR */
9937702e47cSPaolo Bonzini         dst->ctpr = val & 0x0000000F;
9947702e47cSPaolo Bonzini 
995df592270SMichael Davidsaver         DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d",
9967702e47cSPaolo Bonzini                 __func__, idx, dst->ctpr, dst->raised.priority,
9977702e47cSPaolo Bonzini                 dst->servicing.priority);
9987702e47cSPaolo Bonzini 
9997702e47cSPaolo Bonzini         if (dst->raised.priority <= dst->ctpr) {
1000df592270SMichael Davidsaver             DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr",
10017702e47cSPaolo Bonzini                     __func__, idx);
10027702e47cSPaolo Bonzini             qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
10037702e47cSPaolo Bonzini         } else if (dst->raised.priority > dst->servicing.priority) {
1004df592270SMichael Davidsaver             DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d",
10057702e47cSPaolo Bonzini                     __func__, idx, dst->raised.next);
10067702e47cSPaolo Bonzini             qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
10077702e47cSPaolo Bonzini         }
10087702e47cSPaolo Bonzini 
10097702e47cSPaolo Bonzini         break;
10107702e47cSPaolo Bonzini     case 0x90: /* WHOAMI */
10117702e47cSPaolo Bonzini         /* Read-only register */
10127702e47cSPaolo Bonzini         break;
10137702e47cSPaolo Bonzini     case 0xA0: /* IACK */
10147702e47cSPaolo Bonzini         /* Read-only register */
10157702e47cSPaolo Bonzini         break;
10167702e47cSPaolo Bonzini     case 0xB0: /* EOI */
1017df592270SMichael Davidsaver         DPRINTF("EOI");
10187702e47cSPaolo Bonzini         s_IRQ = IRQ_get_next(opp, &dst->servicing);
10197702e47cSPaolo Bonzini 
10207702e47cSPaolo Bonzini         if (s_IRQ < 0) {
1021df592270SMichael Davidsaver             DPRINTF("%s: EOI with no interrupt in service", __func__);
10227702e47cSPaolo Bonzini             break;
10237702e47cSPaolo Bonzini         }
10247702e47cSPaolo Bonzini 
10257702e47cSPaolo Bonzini         IRQ_resetbit(&dst->servicing, s_IRQ);
10267702e47cSPaolo Bonzini         /* Set up next servicing IRQ */
10277702e47cSPaolo Bonzini         s_IRQ = IRQ_get_next(opp, &dst->servicing);
10287702e47cSPaolo Bonzini         /* Check queued interrupts. */
10297702e47cSPaolo Bonzini         n_IRQ = IRQ_get_next(opp, &dst->raised);
10307702e47cSPaolo Bonzini         src = &opp->src[n_IRQ];
10317702e47cSPaolo Bonzini         if (n_IRQ != -1 &&
10327702e47cSPaolo Bonzini             (s_IRQ == -1 ||
10337702e47cSPaolo Bonzini              IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) {
1034df592270SMichael Davidsaver             DPRINTF("Raise OpenPIC INT output cpu %d irq %d",
10357702e47cSPaolo Bonzini                     idx, n_IRQ);
10367702e47cSPaolo Bonzini             qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]);
10377702e47cSPaolo Bonzini         }
10387702e47cSPaolo Bonzini         break;
10397702e47cSPaolo Bonzini     default:
10407702e47cSPaolo Bonzini         break;
10417702e47cSPaolo Bonzini     }
10427702e47cSPaolo Bonzini }
10437702e47cSPaolo Bonzini 
10447702e47cSPaolo Bonzini static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val,
10457702e47cSPaolo Bonzini                               unsigned len)
10467702e47cSPaolo Bonzini {
10477702e47cSPaolo Bonzini     openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
10487702e47cSPaolo Bonzini }
10497702e47cSPaolo Bonzini 
10507702e47cSPaolo Bonzini 
10517702e47cSPaolo Bonzini static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu)
10527702e47cSPaolo Bonzini {
10537702e47cSPaolo Bonzini     IRQSource *src;
10547702e47cSPaolo Bonzini     int retval, irq;
10557702e47cSPaolo Bonzini 
1056df592270SMichael Davidsaver     DPRINTF("Lower OpenPIC INT output");
10577702e47cSPaolo Bonzini     qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
10587702e47cSPaolo Bonzini 
10597702e47cSPaolo Bonzini     irq = IRQ_get_next(opp, &dst->raised);
1060df592270SMichael Davidsaver     DPRINTF("IACK: irq=%d", irq);
10617702e47cSPaolo Bonzini 
10627702e47cSPaolo Bonzini     if (irq == -1) {
10637702e47cSPaolo Bonzini         /* No more interrupt pending */
10647702e47cSPaolo Bonzini         return opp->spve;
10657702e47cSPaolo Bonzini     }
10667702e47cSPaolo Bonzini 
10677702e47cSPaolo Bonzini     src = &opp->src[irq];
10687702e47cSPaolo Bonzini     if (!(src->ivpr & IVPR_ACTIVITY_MASK) ||
10697702e47cSPaolo Bonzini             !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) {
1070df592270SMichael Davidsaver         error_report("%s: bad raised IRQ %d ctpr %d ivpr 0x%08x",
10717702e47cSPaolo Bonzini                 __func__, irq, dst->ctpr, src->ivpr);
10727702e47cSPaolo Bonzini         openpic_update_irq(opp, irq);
10737702e47cSPaolo Bonzini         retval = opp->spve;
10747702e47cSPaolo Bonzini     } else {
10757702e47cSPaolo Bonzini         /* IRQ enter servicing state */
10767702e47cSPaolo Bonzini         IRQ_setbit(&dst->servicing, irq);
10777702e47cSPaolo Bonzini         retval = IVPR_VECTOR(opp, src->ivpr);
10787702e47cSPaolo Bonzini     }
10797702e47cSPaolo Bonzini 
10807702e47cSPaolo Bonzini     if (!src->level) {
10817702e47cSPaolo Bonzini         /* edge-sensitive IRQ */
10827702e47cSPaolo Bonzini         src->ivpr &= ~IVPR_ACTIVITY_MASK;
10837702e47cSPaolo Bonzini         src->pending = 0;
10847702e47cSPaolo Bonzini         IRQ_resetbit(&dst->raised, irq);
10857702e47cSPaolo Bonzini     }
10867702e47cSPaolo Bonzini 
1087ddd5140bSAaron Larson     /* Timers and IPIs support multicast. */
1088ddd5140bSAaron Larson     if (((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) ||
1089ddd5140bSAaron Larson         ((irq >= opp->irq_tim0) && (irq < (opp->irq_tim0 + OPENPIC_MAX_TMR)))) {
1090df592270SMichael Davidsaver         DPRINTF("irq is IPI or TMR");
10917702e47cSPaolo Bonzini         src->destmask &= ~(1 << cpu);
10927702e47cSPaolo Bonzini         if (src->destmask && !src->level) {
10937702e47cSPaolo Bonzini             /* trigger on CPUs that didn't know about it yet */
10947702e47cSPaolo Bonzini             openpic_set_irq(opp, irq, 1);
10957702e47cSPaolo Bonzini             openpic_set_irq(opp, irq, 0);
10967702e47cSPaolo Bonzini             /* if all CPUs knew about it, set active bit again */
10977702e47cSPaolo Bonzini             src->ivpr |= IVPR_ACTIVITY_MASK;
10987702e47cSPaolo Bonzini         }
10997702e47cSPaolo Bonzini     }
11007702e47cSPaolo Bonzini 
11017702e47cSPaolo Bonzini     return retval;
11027702e47cSPaolo Bonzini }
11037702e47cSPaolo Bonzini 
11047702e47cSPaolo Bonzini static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
11057702e47cSPaolo Bonzini                                           int idx)
11067702e47cSPaolo Bonzini {
11077702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
11087702e47cSPaolo Bonzini     IRQDest *dst;
11097702e47cSPaolo Bonzini     uint32_t retval;
11107702e47cSPaolo Bonzini 
1111df592270SMichael Davidsaver     DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx, __func__, idx, addr);
11127702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
11137702e47cSPaolo Bonzini 
111404d2acbbSFabien Chouteau     if (idx < 0 || idx >= opp->nb_cpus) {
11157702e47cSPaolo Bonzini         return retval;
11167702e47cSPaolo Bonzini     }
11177702e47cSPaolo Bonzini 
11187702e47cSPaolo Bonzini     if (addr & 0xF) {
11197702e47cSPaolo Bonzini         return retval;
11207702e47cSPaolo Bonzini     }
11217702e47cSPaolo Bonzini     dst = &opp->dst[idx];
11227702e47cSPaolo Bonzini     addr &= 0xFF0;
11237702e47cSPaolo Bonzini     switch (addr) {
11247702e47cSPaolo Bonzini     case 0x80: /* CTPR */
11257702e47cSPaolo Bonzini         retval = dst->ctpr;
11267702e47cSPaolo Bonzini         break;
11277702e47cSPaolo Bonzini     case 0x90: /* WHOAMI */
11287702e47cSPaolo Bonzini         retval = idx;
11297702e47cSPaolo Bonzini         break;
11307702e47cSPaolo Bonzini     case 0xA0: /* IACK */
11317702e47cSPaolo Bonzini         retval = openpic_iack(opp, dst, idx);
11327702e47cSPaolo Bonzini         break;
11337702e47cSPaolo Bonzini     case 0xB0: /* EOI */
11347702e47cSPaolo Bonzini         retval = 0;
11357702e47cSPaolo Bonzini         break;
11367702e47cSPaolo Bonzini     default:
11377702e47cSPaolo Bonzini         break;
11387702e47cSPaolo Bonzini     }
1139df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
11407702e47cSPaolo Bonzini 
11417702e47cSPaolo Bonzini     return retval;
11427702e47cSPaolo Bonzini }
11437702e47cSPaolo Bonzini 
11447702e47cSPaolo Bonzini static uint64_t openpic_cpu_read(void *opaque, hwaddr addr, unsigned len)
11457702e47cSPaolo Bonzini {
11467702e47cSPaolo Bonzini     return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
11477702e47cSPaolo Bonzini }
11487702e47cSPaolo Bonzini 
11497702e47cSPaolo Bonzini static const MemoryRegionOps openpic_glb_ops_le = {
11507702e47cSPaolo Bonzini     .write = openpic_gbl_write,
11517702e47cSPaolo Bonzini     .read  = openpic_gbl_read,
11527702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11537702e47cSPaolo Bonzini     .impl = {
11547702e47cSPaolo Bonzini         .min_access_size = 4,
11557702e47cSPaolo Bonzini         .max_access_size = 4,
11567702e47cSPaolo Bonzini     },
11577702e47cSPaolo Bonzini };
11587702e47cSPaolo Bonzini 
11597702e47cSPaolo Bonzini static const MemoryRegionOps openpic_glb_ops_be = {
11607702e47cSPaolo Bonzini     .write = openpic_gbl_write,
11617702e47cSPaolo Bonzini     .read  = openpic_gbl_read,
11627702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
11637702e47cSPaolo Bonzini     .impl = {
11647702e47cSPaolo Bonzini         .min_access_size = 4,
11657702e47cSPaolo Bonzini         .max_access_size = 4,
11667702e47cSPaolo Bonzini     },
11677702e47cSPaolo Bonzini };
11687702e47cSPaolo Bonzini 
11697702e47cSPaolo Bonzini static const MemoryRegionOps openpic_tmr_ops_le = {
11707702e47cSPaolo Bonzini     .write = openpic_tmr_write,
11717702e47cSPaolo Bonzini     .read  = openpic_tmr_read,
11727702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11737702e47cSPaolo Bonzini     .impl = {
11747702e47cSPaolo Bonzini         .min_access_size = 4,
11757702e47cSPaolo Bonzini         .max_access_size = 4,
11767702e47cSPaolo Bonzini     },
11777702e47cSPaolo Bonzini };
11787702e47cSPaolo Bonzini 
11797702e47cSPaolo Bonzini static const MemoryRegionOps openpic_tmr_ops_be = {
11807702e47cSPaolo Bonzini     .write = openpic_tmr_write,
11817702e47cSPaolo Bonzini     .read  = openpic_tmr_read,
11827702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
11837702e47cSPaolo Bonzini     .impl = {
11847702e47cSPaolo Bonzini         .min_access_size = 4,
11857702e47cSPaolo Bonzini         .max_access_size = 4,
11867702e47cSPaolo Bonzini     },
11877702e47cSPaolo Bonzini };
11887702e47cSPaolo Bonzini 
11897702e47cSPaolo Bonzini static const MemoryRegionOps openpic_cpu_ops_le = {
11907702e47cSPaolo Bonzini     .write = openpic_cpu_write,
11917702e47cSPaolo Bonzini     .read  = openpic_cpu_read,
11927702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11937702e47cSPaolo Bonzini     .impl = {
11947702e47cSPaolo Bonzini         .min_access_size = 4,
11957702e47cSPaolo Bonzini         .max_access_size = 4,
11967702e47cSPaolo Bonzini     },
11977702e47cSPaolo Bonzini };
11987702e47cSPaolo Bonzini 
11997702e47cSPaolo Bonzini static const MemoryRegionOps openpic_cpu_ops_be = {
12007702e47cSPaolo Bonzini     .write = openpic_cpu_write,
12017702e47cSPaolo Bonzini     .read  = openpic_cpu_read,
12027702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12037702e47cSPaolo Bonzini     .impl = {
12047702e47cSPaolo Bonzini         .min_access_size = 4,
12057702e47cSPaolo Bonzini         .max_access_size = 4,
12067702e47cSPaolo Bonzini     },
12077702e47cSPaolo Bonzini };
12087702e47cSPaolo Bonzini 
12097702e47cSPaolo Bonzini static const MemoryRegionOps openpic_src_ops_le = {
12107702e47cSPaolo Bonzini     .write = openpic_src_write,
12117702e47cSPaolo Bonzini     .read  = openpic_src_read,
12127702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
12137702e47cSPaolo Bonzini     .impl = {
12147702e47cSPaolo Bonzini         .min_access_size = 4,
12157702e47cSPaolo Bonzini         .max_access_size = 4,
12167702e47cSPaolo Bonzini     },
12177702e47cSPaolo Bonzini };
12187702e47cSPaolo Bonzini 
12197702e47cSPaolo Bonzini static const MemoryRegionOps openpic_src_ops_be = {
12207702e47cSPaolo Bonzini     .write = openpic_src_write,
12217702e47cSPaolo Bonzini     .read  = openpic_src_read,
12227702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12237702e47cSPaolo Bonzini     .impl = {
12247702e47cSPaolo Bonzini         .min_access_size = 4,
12257702e47cSPaolo Bonzini         .max_access_size = 4,
12267702e47cSPaolo Bonzini     },
12277702e47cSPaolo Bonzini };
12287702e47cSPaolo Bonzini 
12297702e47cSPaolo Bonzini static const MemoryRegionOps openpic_msi_ops_be = {
12307702e47cSPaolo Bonzini     .read = openpic_msi_read,
12317702e47cSPaolo Bonzini     .write = openpic_msi_write,
12327702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12337702e47cSPaolo Bonzini     .impl = {
12347702e47cSPaolo Bonzini         .min_access_size = 4,
12357702e47cSPaolo Bonzini         .max_access_size = 4,
12367702e47cSPaolo Bonzini     },
12377702e47cSPaolo Bonzini };
12387702e47cSPaolo Bonzini 
12397702e47cSPaolo Bonzini static const MemoryRegionOps openpic_summary_ops_be = {
12407702e47cSPaolo Bonzini     .read = openpic_summary_read,
12417702e47cSPaolo Bonzini     .write = openpic_summary_write,
12427702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12437702e47cSPaolo Bonzini     .impl = {
12447702e47cSPaolo Bonzini         .min_access_size = 4,
12457702e47cSPaolo Bonzini         .max_access_size = 4,
12467702e47cSPaolo Bonzini     },
12477702e47cSPaolo Bonzini };
12487702e47cSPaolo Bonzini 
12498ebe65f3SPaul Janzen static void openpic_reset(DeviceState *d)
12508ebe65f3SPaul Janzen {
12518ebe65f3SPaul Janzen     OpenPICState *opp = OPENPIC(d);
12528ebe65f3SPaul Janzen     int i;
12538ebe65f3SPaul Janzen 
12548ebe65f3SPaul Janzen     opp->gcr = GCR_RESET;
12558ebe65f3SPaul Janzen     /* Initialise controller registers */
12568ebe65f3SPaul Janzen     opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) |
12578ebe65f3SPaul Janzen                ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) |
12588ebe65f3SPaul Janzen                (opp->vid << FRR_VID_SHIFT);
12598ebe65f3SPaul Janzen 
12608ebe65f3SPaul Janzen     opp->pir = 0;
12618ebe65f3SPaul Janzen     opp->spve = -1 & opp->vector_mask;
12628ebe65f3SPaul Janzen     opp->tfrr = opp->tfrr_reset;
12638ebe65f3SPaul Janzen     /* Initialise IRQ sources */
12648ebe65f3SPaul Janzen     for (i = 0; i < opp->max_irq; i++) {
12658ebe65f3SPaul Janzen         opp->src[i].ivpr = opp->ivpr_reset;
12668ebe65f3SPaul Janzen         switch (opp->src[i].type) {
12678ebe65f3SPaul Janzen         case IRQ_TYPE_NORMAL:
12688ebe65f3SPaul Janzen             opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK);
12698ebe65f3SPaul Janzen             break;
12708ebe65f3SPaul Janzen 
12718ebe65f3SPaul Janzen         case IRQ_TYPE_FSLINT:
12728ebe65f3SPaul Janzen             opp->src[i].ivpr |= IVPR_POLARITY_MASK;
12738ebe65f3SPaul Janzen             break;
12748ebe65f3SPaul Janzen 
12758ebe65f3SPaul Janzen         case IRQ_TYPE_FSLSPECIAL:
12768ebe65f3SPaul Janzen             break;
12778ebe65f3SPaul Janzen         }
1278ffd5e9feSPaul Janzen 
1279*457279cbSBin Meng         /* Mask all IPI interrupts for Freescale OpenPIC */
1280*457279cbSBin Meng         if ((opp->model == OPENPIC_MODEL_FSL_MPIC_20) ||
1281*457279cbSBin Meng             (opp->model == OPENPIC_MODEL_FSL_MPIC_42)) {
1282*457279cbSBin Meng             if (i >= opp->irq_ipi0 && i < opp->irq_tim0) {
1283*457279cbSBin Meng                 write_IRQreg_idr(opp, i, 0);
1284*457279cbSBin Meng                 continue;
1285*457279cbSBin Meng             }
1286*457279cbSBin Meng         }
1287*457279cbSBin Meng 
1288ffd5e9feSPaul Janzen         write_IRQreg_idr(opp, i, opp->idr_reset);
12898ebe65f3SPaul Janzen     }
12908ebe65f3SPaul Janzen     /* Initialise IRQ destinations */
12912ada66f9SMark Cave-Ayland     for (i = 0; i < opp->nb_cpus; i++) {
12928ebe65f3SPaul Janzen         opp->dst[i].ctpr      = 15;
12938ebe65f3SPaul Janzen         opp->dst[i].raised.next = -1;
12942ada66f9SMark Cave-Ayland         opp->dst[i].raised.priority = 0;
12952ada66f9SMark Cave-Ayland         bitmap_clear(opp->dst[i].raised.queue, 0, IRQQUEUE_SIZE_BITS);
12968ebe65f3SPaul Janzen         opp->dst[i].servicing.next = -1;
12972ada66f9SMark Cave-Ayland         opp->dst[i].servicing.priority = 0;
12982ada66f9SMark Cave-Ayland         bitmap_clear(opp->dst[i].servicing.queue, 0, IRQQUEUE_SIZE_BITS);
12998ebe65f3SPaul Janzen     }
13008ebe65f3SPaul Janzen     /* Initialise timers */
13018ebe65f3SPaul Janzen     for (i = 0; i < OPENPIC_MAX_TMR; i++) {
13028ebe65f3SPaul Janzen         opp->timers[i].tccr = 0;
13038ebe65f3SPaul Janzen         opp->timers[i].tbcr = TBCR_CI;
1304ddd5140bSAaron Larson         if (opp->timers[i].qemu_timer_active) {
1305ddd5140bSAaron Larson             timer_del(opp->timers[i].qemu_timer);  /* Inhibit timer */
1306ddd5140bSAaron Larson             opp->timers[i].qemu_timer_active = false;
1307ddd5140bSAaron Larson         }
13088ebe65f3SPaul Janzen     }
13098ebe65f3SPaul Janzen     /* Go out of RESET state */
13108ebe65f3SPaul Janzen     opp->gcr = 0;
13118ebe65f3SPaul Janzen }
13128ebe65f3SPaul Janzen 
13137702e47cSPaolo Bonzini typedef struct MemReg {
13147702e47cSPaolo Bonzini     const char             *name;
13157702e47cSPaolo Bonzini     MemoryRegionOps const  *ops;
13167702e47cSPaolo Bonzini     hwaddr      start_addr;
13177702e47cSPaolo Bonzini     ram_addr_t              size;
13187702e47cSPaolo Bonzini } MemReg;
13197702e47cSPaolo Bonzini 
13207702e47cSPaolo Bonzini static void fsl_common_init(OpenPICState *opp)
13217702e47cSPaolo Bonzini {
13227702e47cSPaolo Bonzini     int i;
13238935a442SScott Wood     int virq = OPENPIC_MAX_SRC;
13247702e47cSPaolo Bonzini 
13257702e47cSPaolo Bonzini     opp->vid = VID_REVISION_1_2;
13267702e47cSPaolo Bonzini     opp->vir = VIR_GENERIC;
13277702e47cSPaolo Bonzini     opp->vector_mask = 0xFFFF;
13287702e47cSPaolo Bonzini     opp->tfrr_reset = 0;
13297702e47cSPaolo Bonzini     opp->ivpr_reset = IVPR_MASK_MASK;
13307702e47cSPaolo Bonzini     opp->idr_reset = 1 << 0;
13318935a442SScott Wood     opp->max_irq = OPENPIC_MAX_IRQ;
13327702e47cSPaolo Bonzini 
13337702e47cSPaolo Bonzini     opp->irq_ipi0 = virq;
13348935a442SScott Wood     virq += OPENPIC_MAX_IPI;
13357702e47cSPaolo Bonzini     opp->irq_tim0 = virq;
13368935a442SScott Wood     virq += OPENPIC_MAX_TMR;
13377702e47cSPaolo Bonzini 
13388935a442SScott Wood     assert(virq <= OPENPIC_MAX_IRQ);
13397702e47cSPaolo Bonzini 
13407702e47cSPaolo Bonzini     opp->irq_msi = 224;
13417702e47cSPaolo Bonzini 
1342226419d6SMichael S. Tsirkin     msi_nonbroken = true;
13437702e47cSPaolo Bonzini     for (i = 0; i < opp->fsl->max_ext; i++) {
13447702e47cSPaolo Bonzini         opp->src[i].level = false;
13457702e47cSPaolo Bonzini     }
13467702e47cSPaolo Bonzini 
13477702e47cSPaolo Bonzini     /* Internal interrupts, including message and MSI */
13488935a442SScott Wood     for (i = 16; i < OPENPIC_MAX_SRC; i++) {
13497702e47cSPaolo Bonzini         opp->src[i].type = IRQ_TYPE_FSLINT;
13507702e47cSPaolo Bonzini         opp->src[i].level = true;
13517702e47cSPaolo Bonzini     }
13527702e47cSPaolo Bonzini 
13537702e47cSPaolo Bonzini     /* timers and IPIs */
13548935a442SScott Wood     for (i = OPENPIC_MAX_SRC; i < virq; i++) {
13557702e47cSPaolo Bonzini         opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
13567702e47cSPaolo Bonzini         opp->src[i].level = false;
13577702e47cSPaolo Bonzini     }
1358ddd5140bSAaron Larson 
1359ddd5140bSAaron Larson     for (i = 0; i < OPENPIC_MAX_TMR; i++) {
1360ddd5140bSAaron Larson         opp->timers[i].n_IRQ = opp->irq_tim0 + i;
1361ddd5140bSAaron Larson         opp->timers[i].qemu_timer_active = false;
1362ddd5140bSAaron Larson         opp->timers[i].qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
1363ddd5140bSAaron Larson                                                  &qemu_timer_cb,
1364ddd5140bSAaron Larson                                                  &opp->timers[i]);
1365ddd5140bSAaron Larson         opp->timers[i].opp = opp;
1366ddd5140bSAaron Larson     }
13677702e47cSPaolo Bonzini }
13687702e47cSPaolo Bonzini 
13697702e47cSPaolo Bonzini static void map_list(OpenPICState *opp, const MemReg *list, int *count)
13707702e47cSPaolo Bonzini {
13717702e47cSPaolo Bonzini     while (list->name) {
13727702e47cSPaolo Bonzini         assert(*count < ARRAY_SIZE(opp->sub_io_mem));
13737702e47cSPaolo Bonzini 
13741437c94bSPaolo Bonzini         memory_region_init_io(&opp->sub_io_mem[*count], OBJECT(opp), list->ops,
13751437c94bSPaolo Bonzini                               opp, list->name, list->size);
13767702e47cSPaolo Bonzini 
13777702e47cSPaolo Bonzini         memory_region_add_subregion(&opp->mem, list->start_addr,
13787702e47cSPaolo Bonzini                                     &opp->sub_io_mem[*count]);
13797702e47cSPaolo Bonzini 
13807702e47cSPaolo Bonzini         (*count)++;
13817702e47cSPaolo Bonzini         list++;
13827702e47cSPaolo Bonzini     }
13837702e47cSPaolo Bonzini }
13847702e47cSPaolo Bonzini 
1385e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irq_queue = {
1386e5f6e732SMark Cave-Ayland     .name = "openpic_irq_queue",
1387e5f6e732SMark Cave-Ayland     .version_id = 0,
1388e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1389e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1390e5f6e732SMark Cave-Ayland         VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size),
1391e5f6e732SMark Cave-Ayland         VMSTATE_INT32(next, IRQQueue),
1392e5f6e732SMark Cave-Ayland         VMSTATE_INT32(priority, IRQQueue),
1393e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1394e5f6e732SMark Cave-Ayland     }
1395e5f6e732SMark Cave-Ayland };
1396e5f6e732SMark Cave-Ayland 
1397e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irqdest = {
1398e5f6e732SMark Cave-Ayland     .name = "openpic_irqdest",
1399e5f6e732SMark Cave-Ayland     .version_id = 0,
1400e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1401e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1402e5f6e732SMark Cave-Ayland         VMSTATE_INT32(ctpr, IRQDest),
1403e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue,
1404e5f6e732SMark Cave-Ayland                        IRQQueue),
1405e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT(servicing, IRQDest, 0, vmstate_openpic_irq_queue,
1406e5f6e732SMark Cave-Ayland                        IRQQueue),
1407e5f6e732SMark Cave-Ayland         VMSTATE_UINT32_ARRAY(outputs_active, IRQDest, OPENPIC_OUTPUT_NB),
1408e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1409e5f6e732SMark Cave-Ayland     }
1410e5f6e732SMark Cave-Ayland };
1411e5f6e732SMark Cave-Ayland 
1412e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irqsource = {
1413e5f6e732SMark Cave-Ayland     .name = "openpic_irqsource",
1414e5f6e732SMark Cave-Ayland     .version_id = 0,
1415e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1416e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1417e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(ivpr, IRQSource),
1418e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(idr, IRQSource),
1419e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(destmask, IRQSource),
1420e5f6e732SMark Cave-Ayland         VMSTATE_INT32(last_cpu, IRQSource),
1421e5f6e732SMark Cave-Ayland         VMSTATE_INT32(pending, IRQSource),
1422e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1423e5f6e732SMark Cave-Ayland     }
1424e5f6e732SMark Cave-Ayland };
1425e5f6e732SMark Cave-Ayland 
1426e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_timer = {
1427e5f6e732SMark Cave-Ayland     .name = "openpic_timer",
1428e5f6e732SMark Cave-Ayland     .version_id = 0,
1429e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1430e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1431e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tccr, OpenPICTimer),
1432e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tbcr, OpenPICTimer),
1433e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1434e5f6e732SMark Cave-Ayland     }
1435e5f6e732SMark Cave-Ayland };
1436e5f6e732SMark Cave-Ayland 
1437e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_msi = {
1438e5f6e732SMark Cave-Ayland     .name = "openpic_msi",
1439e5f6e732SMark Cave-Ayland     .version_id = 0,
1440e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1441e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1442e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(msir, OpenPICMSI),
1443e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1444e5f6e732SMark Cave-Ayland     }
1445e5f6e732SMark Cave-Ayland };
1446e5f6e732SMark Cave-Ayland 
1447e5f6e732SMark Cave-Ayland static int openpic_post_load(void *opaque, int version_id)
1448e5f6e732SMark Cave-Ayland {
1449e5f6e732SMark Cave-Ayland     OpenPICState *opp = (OpenPICState *)opaque;
1450e5f6e732SMark Cave-Ayland     int i;
1451e5f6e732SMark Cave-Ayland 
1452e5f6e732SMark Cave-Ayland     /* Update internal ivpr and idr variables */
1453e5f6e732SMark Cave-Ayland     for (i = 0; i < opp->max_irq; i++) {
1454e5f6e732SMark Cave-Ayland         write_IRQreg_idr(opp, i, opp->src[i].idr);
1455e5f6e732SMark Cave-Ayland         write_IRQreg_ivpr(opp, i, opp->src[i].ivpr);
1456e5f6e732SMark Cave-Ayland     }
1457e5f6e732SMark Cave-Ayland 
1458e5f6e732SMark Cave-Ayland     return 0;
1459e5f6e732SMark Cave-Ayland }
1460e5f6e732SMark Cave-Ayland 
1461e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic = {
1462e5f6e732SMark Cave-Ayland     .name = "openpic",
1463e5f6e732SMark Cave-Ayland     .version_id = 3,
1464e5f6e732SMark Cave-Ayland     .minimum_version_id = 3,
1465e5f6e732SMark Cave-Ayland     .post_load = openpic_post_load,
1466e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1467e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(gcr, OpenPICState),
1468e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(vir, OpenPICState),
1469e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(pir, OpenPICState),
1470e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(spve, OpenPICState),
1471e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tfrr, OpenPICState),
1472e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(max_irq, OpenPICState),
1473e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0,
1474e5f6e732SMark Cave-Ayland                                      vmstate_openpic_irqsource, IRQSource),
1475d2164ad3SHalil Pasic         VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState, NULL),
1476e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0,
1477e5f6e732SMark Cave-Ayland                                      vmstate_openpic_irqdest, IRQDest),
1478e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0,
1479e5f6e732SMark Cave-Ayland                              vmstate_openpic_timer, OpenPICTimer),
1480e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_ARRAY(msi, OpenPICState, MAX_MSI, 0,
1481e5f6e732SMark Cave-Ayland                              vmstate_openpic_msi, OpenPICMSI),
1482e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_ipi0, OpenPICState),
1483e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_tim0, OpenPICState),
1484e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_msi, OpenPICState),
1485e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1486e5f6e732SMark Cave-Ayland     }
1487e5f6e732SMark Cave-Ayland };
1488e5f6e732SMark Cave-Ayland 
1489cbe72019SAndreas Färber static void openpic_init(Object *obj)
14907702e47cSPaolo Bonzini {
1491cbe72019SAndreas Färber     OpenPICState *opp = OPENPIC(obj);
1492cbe72019SAndreas Färber 
14931437c94bSPaolo Bonzini     memory_region_init(&opp->mem, obj, "openpic", 0x40000);
1494cbe72019SAndreas Färber }
1495cbe72019SAndreas Färber 
1496cbe72019SAndreas Färber static void openpic_realize(DeviceState *dev, Error **errp)
1497cbe72019SAndreas Färber {
1498cbe72019SAndreas Färber     SysBusDevice *d = SYS_BUS_DEVICE(dev);
1499e1766344SAndreas Färber     OpenPICState *opp = OPENPIC(dev);
15007702e47cSPaolo Bonzini     int i, j;
15017702e47cSPaolo Bonzini     int list_count = 0;
15027702e47cSPaolo Bonzini     static const MemReg list_le[] = {
15037702e47cSPaolo Bonzini         {"glb", &openpic_glb_ops_le,
15047702e47cSPaolo Bonzini                 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
15057702e47cSPaolo Bonzini         {"tmr", &openpic_tmr_ops_le,
15067702e47cSPaolo Bonzini                 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
15077702e47cSPaolo Bonzini         {"src", &openpic_src_ops_le,
15087702e47cSPaolo Bonzini                 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
15097702e47cSPaolo Bonzini         {"cpu", &openpic_cpu_ops_le,
15107702e47cSPaolo Bonzini                 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
15117702e47cSPaolo Bonzini         {NULL}
15127702e47cSPaolo Bonzini     };
15137702e47cSPaolo Bonzini     static const MemReg list_be[] = {
15147702e47cSPaolo Bonzini         {"glb", &openpic_glb_ops_be,
15157702e47cSPaolo Bonzini                 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
15167702e47cSPaolo Bonzini         {"tmr", &openpic_tmr_ops_be,
15177702e47cSPaolo Bonzini                 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
15187702e47cSPaolo Bonzini         {"src", &openpic_src_ops_be,
15197702e47cSPaolo Bonzini                 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
15207702e47cSPaolo Bonzini         {"cpu", &openpic_cpu_ops_be,
15217702e47cSPaolo Bonzini                 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
15227702e47cSPaolo Bonzini         {NULL}
15237702e47cSPaolo Bonzini     };
15247702e47cSPaolo Bonzini     static const MemReg list_fsl[] = {
15257702e47cSPaolo Bonzini         {"msi", &openpic_msi_ops_be,
15267702e47cSPaolo Bonzini                 OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
15277702e47cSPaolo Bonzini         {"summary", &openpic_summary_ops_be,
15287702e47cSPaolo Bonzini                 OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE},
15297702e47cSPaolo Bonzini         {NULL}
15307702e47cSPaolo Bonzini     };
15317702e47cSPaolo Bonzini 
153273d963c0SMichael Roth     if (opp->nb_cpus > MAX_CPU) {
1533c6bd8c70SMarkus Armbruster         error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
153473d963c0SMichael Roth                    TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus,
153573d963c0SMichael Roth                    (uint64_t)0, (uint64_t)MAX_CPU);
153673d963c0SMichael Roth         return;
153773d963c0SMichael Roth     }
153873d963c0SMichael Roth 
15397702e47cSPaolo Bonzini     switch (opp->model) {
15407702e47cSPaolo Bonzini     case OPENPIC_MODEL_FSL_MPIC_20:
15417702e47cSPaolo Bonzini     default:
15427702e47cSPaolo Bonzini         opp->fsl = &fsl_mpic_20;
15437702e47cSPaolo Bonzini         opp->brr1 = 0x00400200;
15447702e47cSPaolo Bonzini         opp->flags |= OPENPIC_FLAG_IDR_CRIT;
15457702e47cSPaolo Bonzini         opp->nb_irqs = 80;
15467702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_MIXED;
15477702e47cSPaolo Bonzini 
15487702e47cSPaolo Bonzini         fsl_common_init(opp);
15497702e47cSPaolo Bonzini         map_list(opp, list_be, &list_count);
15507702e47cSPaolo Bonzini         map_list(opp, list_fsl, &list_count);
15517702e47cSPaolo Bonzini 
15527702e47cSPaolo Bonzini         break;
15537702e47cSPaolo Bonzini 
15547702e47cSPaolo Bonzini     case OPENPIC_MODEL_FSL_MPIC_42:
15557702e47cSPaolo Bonzini         opp->fsl = &fsl_mpic_42;
15567702e47cSPaolo Bonzini         opp->brr1 = 0x00400402;
15577702e47cSPaolo Bonzini         opp->flags |= OPENPIC_FLAG_ILR;
15587702e47cSPaolo Bonzini         opp->nb_irqs = 196;
15597702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_PROXY;
15607702e47cSPaolo Bonzini 
15617702e47cSPaolo Bonzini         fsl_common_init(opp);
15627702e47cSPaolo Bonzini         map_list(opp, list_be, &list_count);
15637702e47cSPaolo Bonzini         map_list(opp, list_fsl, &list_count);
15647702e47cSPaolo Bonzini 
15657702e47cSPaolo Bonzini         break;
15667702e47cSPaolo Bonzini 
15677702e47cSPaolo Bonzini     case OPENPIC_MODEL_RAVEN:
15687702e47cSPaolo Bonzini         opp->nb_irqs = RAVEN_MAX_EXT;
15697702e47cSPaolo Bonzini         opp->vid = VID_REVISION_1_3;
15707702e47cSPaolo Bonzini         opp->vir = VIR_GENERIC;
15717702e47cSPaolo Bonzini         opp->vector_mask = 0xFF;
15727702e47cSPaolo Bonzini         opp->tfrr_reset = 4160000;
15737702e47cSPaolo Bonzini         opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK;
15747702e47cSPaolo Bonzini         opp->idr_reset = 0;
15757702e47cSPaolo Bonzini         opp->max_irq = RAVEN_MAX_IRQ;
15767702e47cSPaolo Bonzini         opp->irq_ipi0 = RAVEN_IPI_IRQ;
15777702e47cSPaolo Bonzini         opp->irq_tim0 = RAVEN_TMR_IRQ;
15787702e47cSPaolo Bonzini         opp->brr1 = -1;
15797702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_MIXED;
15807702e47cSPaolo Bonzini 
15817702e47cSPaolo Bonzini         if (opp->nb_cpus != 1) {
1582cbe72019SAndreas Färber             error_setg(errp, "Only UP supported today");
1583cbe72019SAndreas Färber             return;
15847702e47cSPaolo Bonzini         }
15857702e47cSPaolo Bonzini 
15867702e47cSPaolo Bonzini         map_list(opp, list_le, &list_count);
15877702e47cSPaolo Bonzini         break;
158858b62835SBenjamin Herrenschmidt 
158958b62835SBenjamin Herrenschmidt     case OPENPIC_MODEL_KEYLARGO:
159058b62835SBenjamin Herrenschmidt         opp->nb_irqs = KEYLARGO_MAX_EXT;
159158b62835SBenjamin Herrenschmidt         opp->vid = VID_REVISION_1_2;
159258b62835SBenjamin Herrenschmidt         opp->vir = VIR_GENERIC;
159358b62835SBenjamin Herrenschmidt         opp->vector_mask = 0xFF;
159458b62835SBenjamin Herrenschmidt         opp->tfrr_reset = 4160000;
159558b62835SBenjamin Herrenschmidt         opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK;
159658b62835SBenjamin Herrenschmidt         opp->idr_reset = 0;
159758b62835SBenjamin Herrenschmidt         opp->max_irq = KEYLARGO_MAX_IRQ;
159858b62835SBenjamin Herrenschmidt         opp->irq_ipi0 = KEYLARGO_IPI_IRQ;
159958b62835SBenjamin Herrenschmidt         opp->irq_tim0 = KEYLARGO_TMR_IRQ;
160058b62835SBenjamin Herrenschmidt         opp->brr1 = -1;
160158b62835SBenjamin Herrenschmidt         opp->mpic_mode_mask = GCR_MODE_MIXED;
160258b62835SBenjamin Herrenschmidt 
160358b62835SBenjamin Herrenschmidt         if (opp->nb_cpus != 1) {
160458b62835SBenjamin Herrenschmidt             error_setg(errp, "Only UP supported today");
160558b62835SBenjamin Herrenschmidt             return;
160658b62835SBenjamin Herrenschmidt         }
160758b62835SBenjamin Herrenschmidt 
160858b62835SBenjamin Herrenschmidt         map_list(opp, list_le, &list_count);
160958b62835SBenjamin Herrenschmidt         break;
16107702e47cSPaolo Bonzini     }
16117702e47cSPaolo Bonzini 
16127702e47cSPaolo Bonzini     for (i = 0; i < opp->nb_cpus; i++) {
1613aa2ac1daSPeter Crosthwaite         opp->dst[i].irqs = g_new0(qemu_irq, OPENPIC_OUTPUT_NB);
16147702e47cSPaolo Bonzini         for (j = 0; j < OPENPIC_OUTPUT_NB; j++) {
1615cbe72019SAndreas Färber             sysbus_init_irq(d, &opp->dst[i].irqs[j]);
16167702e47cSPaolo Bonzini         }
16172ada66f9SMark Cave-Ayland 
1618e5f6e732SMark Cave-Ayland         opp->dst[i].raised.queue_size = IRQQUEUE_SIZE_BITS;
16192ada66f9SMark Cave-Ayland         opp->dst[i].raised.queue = bitmap_new(IRQQUEUE_SIZE_BITS);
1620e5f6e732SMark Cave-Ayland         opp->dst[i].servicing.queue_size = IRQQUEUE_SIZE_BITS;
16212ada66f9SMark Cave-Ayland         opp->dst[i].servicing.queue = bitmap_new(IRQQUEUE_SIZE_BITS);
16227702e47cSPaolo Bonzini     }
16237702e47cSPaolo Bonzini 
1624cbe72019SAndreas Färber     sysbus_init_mmio(d, &opp->mem);
1625cbe72019SAndreas Färber     qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq);
16267702e47cSPaolo Bonzini }
16277702e47cSPaolo Bonzini 
16287702e47cSPaolo Bonzini static Property openpic_properties[] = {
16297702e47cSPaolo Bonzini     DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20),
16307702e47cSPaolo Bonzini     DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1),
16317702e47cSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
16327702e47cSPaolo Bonzini };
16337702e47cSPaolo Bonzini 
1634cbe72019SAndreas Färber static void openpic_class_init(ObjectClass *oc, void *data)
16357702e47cSPaolo Bonzini {
1636cbe72019SAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
16377702e47cSPaolo Bonzini 
1638cbe72019SAndreas Färber     dc->realize = openpic_realize;
16394f67d30bSMarc-André Lureau     device_class_set_props(dc, openpic_properties);
16407702e47cSPaolo Bonzini     dc->reset = openpic_reset;
1641e5f6e732SMark Cave-Ayland     dc->vmsd = &vmstate_openpic;
164229f8dd66SLaurent Vivier     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
16437702e47cSPaolo Bonzini }
16447702e47cSPaolo Bonzini 
16457702e47cSPaolo Bonzini static const TypeInfo openpic_info = {
1646e1766344SAndreas Färber     .name          = TYPE_OPENPIC,
16477702e47cSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
16487702e47cSPaolo Bonzini     .instance_size = sizeof(OpenPICState),
1649cbe72019SAndreas Färber     .instance_init = openpic_init,
16507702e47cSPaolo Bonzini     .class_init    = openpic_class_init,
16517702e47cSPaolo Bonzini };
16527702e47cSPaolo Bonzini 
16537702e47cSPaolo Bonzini static void openpic_register_types(void)
16547702e47cSPaolo Bonzini {
16557702e47cSPaolo Bonzini     type_register_static(&openpic_info);
16567702e47cSPaolo Bonzini }
16577702e47cSPaolo Bonzini 
16587702e47cSPaolo Bonzini type_init(openpic_register_types)
1659