xref: /qemu/hw/intc/openpic.c (revision 0b8fa32f)
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  */
36*0b8fa32fSMarkus Armbruster 
3790191d07SPeter Maydell #include "qemu/osdep.h"
387702e47cSPaolo Bonzini #include "hw/hw.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"
437702e47cSPaolo Bonzini #include "hw/sysbus.h"
447702e47cSPaolo Bonzini #include "hw/pci/msi.h"
45da34e65cSMarkus Armbruster #include "qapi/error.h"
467702e47cSPaolo Bonzini #include "qemu/bitops.h"
4773d963c0SMichael Roth #include "qapi/qmp/qerror.h"
4803dd024fSPaolo Bonzini #include "qemu/log.h"
49*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
50ddd5140bSAaron Larson #include "qemu/timer.h"
51df592270SMichael Davidsaver #include "qemu/error-report.h"
527702e47cSPaolo Bonzini 
537702e47cSPaolo Bonzini //#define DEBUG_OPENPIC
547702e47cSPaolo Bonzini 
557702e47cSPaolo Bonzini #ifdef DEBUG_OPENPIC
567702e47cSPaolo Bonzini static const int debug_openpic = 1;
577702e47cSPaolo Bonzini #else
587702e47cSPaolo Bonzini static const int debug_openpic = 0;
597702e47cSPaolo Bonzini #endif
607702e47cSPaolo Bonzini 
61ddd5140bSAaron Larson static int get_current_cpu(void);
627702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) do { \
637702e47cSPaolo Bonzini         if (debug_openpic) { \
64df592270SMichael Davidsaver             info_report("Core%d: " fmt, get_current_cpu(), ## __VA_ARGS__); \
657702e47cSPaolo Bonzini         } \
667702e47cSPaolo Bonzini     } while (0)
677702e47cSPaolo Bonzini 
687702e47cSPaolo Bonzini /* OpenPIC capability flags */
697702e47cSPaolo Bonzini #define OPENPIC_FLAG_IDR_CRIT     (1 << 0)
707702e47cSPaolo Bonzini #define OPENPIC_FLAG_ILR          (2 << 0)
717702e47cSPaolo Bonzini 
727702e47cSPaolo Bonzini /* OpenPIC address map */
737702e47cSPaolo Bonzini #define OPENPIC_GLB_REG_START        0x0
747702e47cSPaolo Bonzini #define OPENPIC_GLB_REG_SIZE         0x10F0
757702e47cSPaolo Bonzini #define OPENPIC_TMR_REG_START        0x10F0
767702e47cSPaolo Bonzini #define OPENPIC_TMR_REG_SIZE         0x220
777702e47cSPaolo Bonzini #define OPENPIC_MSI_REG_START        0x1600
787702e47cSPaolo Bonzini #define OPENPIC_MSI_REG_SIZE         0x200
797702e47cSPaolo Bonzini #define OPENPIC_SUMMARY_REG_START   0x3800
807702e47cSPaolo Bonzini #define OPENPIC_SUMMARY_REG_SIZE    0x800
817702e47cSPaolo Bonzini #define OPENPIC_SRC_REG_START        0x10000
828935a442SScott Wood #define OPENPIC_SRC_REG_SIZE         (OPENPIC_MAX_SRC * 0x20)
837702e47cSPaolo Bonzini #define OPENPIC_CPU_REG_START        0x20000
847702e47cSPaolo Bonzini #define OPENPIC_CPU_REG_SIZE         0x100 + ((MAX_CPU - 1) * 0x1000)
857702e47cSPaolo Bonzini 
867702e47cSPaolo Bonzini static FslMpicInfo fsl_mpic_20 = {
877702e47cSPaolo Bonzini     .max_ext = 12,
887702e47cSPaolo Bonzini };
897702e47cSPaolo Bonzini 
907702e47cSPaolo Bonzini static FslMpicInfo fsl_mpic_42 = {
917702e47cSPaolo Bonzini     .max_ext = 12,
927702e47cSPaolo Bonzini };
937702e47cSPaolo Bonzini 
947702e47cSPaolo Bonzini #define FRR_NIRQ_SHIFT    16
957702e47cSPaolo Bonzini #define FRR_NCPU_SHIFT     8
967702e47cSPaolo Bonzini #define FRR_VID_SHIFT      0
977702e47cSPaolo Bonzini 
987702e47cSPaolo Bonzini #define VID_REVISION_1_2   2
997702e47cSPaolo Bonzini #define VID_REVISION_1_3   3
1007702e47cSPaolo Bonzini 
1017702e47cSPaolo Bonzini #define VIR_GENERIC      0x00000000 /* Generic Vendor ID */
10258b62835SBenjamin Herrenschmidt #define VIR_MPIC2A       0x00004614 /* IBM MPIC-2A */
1037702e47cSPaolo Bonzini 
1047702e47cSPaolo Bonzini #define GCR_RESET        0x80000000
1057702e47cSPaolo Bonzini #define GCR_MODE_PASS    0x00000000
1067702e47cSPaolo Bonzini #define GCR_MODE_MIXED   0x20000000
1077702e47cSPaolo Bonzini #define GCR_MODE_PROXY   0x60000000
1087702e47cSPaolo Bonzini 
1097702e47cSPaolo Bonzini #define TBCR_CI           0x80000000 /* count inhibit */
1107702e47cSPaolo Bonzini #define TCCR_TOG          0x80000000 /* toggles when decrement to zero */
1117702e47cSPaolo Bonzini 
1127702e47cSPaolo Bonzini #define IDR_EP_SHIFT      31
113def60298SPeter Maydell #define IDR_EP_MASK       (1U << IDR_EP_SHIFT)
1147702e47cSPaolo Bonzini #define IDR_CI0_SHIFT     30
1157702e47cSPaolo Bonzini #define IDR_CI1_SHIFT     29
1167702e47cSPaolo Bonzini #define IDR_P1_SHIFT      1
1177702e47cSPaolo Bonzini #define IDR_P0_SHIFT      0
1187702e47cSPaolo Bonzini 
1197702e47cSPaolo Bonzini #define ILR_INTTGT_MASK   0x000000ff
1207702e47cSPaolo Bonzini #define ILR_INTTGT_INT    0x00
1217702e47cSPaolo Bonzini #define ILR_INTTGT_CINT   0x01 /* critical */
1227702e47cSPaolo Bonzini #define ILR_INTTGT_MCP    0x02 /* machine check */
1237702e47cSPaolo Bonzini 
1247702e47cSPaolo Bonzini /* The currently supported INTTGT values happen to be the same as QEMU's
1257702e47cSPaolo Bonzini  * openpic output codes, but don't depend on this.  The output codes
1267702e47cSPaolo Bonzini  * could change (unlikely, but...) or support could be added for
1277702e47cSPaolo Bonzini  * more INTTGT values.
1287702e47cSPaolo Bonzini  */
1297702e47cSPaolo Bonzini static const int inttgt_output[][2] = {
1307702e47cSPaolo Bonzini     { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT },
1317702e47cSPaolo Bonzini     { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT },
1327702e47cSPaolo Bonzini     { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK },
1337702e47cSPaolo Bonzini };
1347702e47cSPaolo Bonzini 
1357702e47cSPaolo Bonzini static int inttgt_to_output(int inttgt)
1367702e47cSPaolo Bonzini {
1377702e47cSPaolo Bonzini     int i;
1387702e47cSPaolo Bonzini 
1397702e47cSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
1407702e47cSPaolo Bonzini         if (inttgt_output[i][0] == inttgt) {
1417702e47cSPaolo Bonzini             return inttgt_output[i][1];
1427702e47cSPaolo Bonzini         }
1437702e47cSPaolo Bonzini     }
1447702e47cSPaolo Bonzini 
145df592270SMichael Davidsaver     error_report("%s: unsupported inttgt %d", __func__, inttgt);
1467702e47cSPaolo Bonzini     return OPENPIC_OUTPUT_INT;
1477702e47cSPaolo Bonzini }
1487702e47cSPaolo Bonzini 
1497702e47cSPaolo Bonzini static int output_to_inttgt(int output)
1507702e47cSPaolo Bonzini {
1517702e47cSPaolo Bonzini     int i;
1527702e47cSPaolo Bonzini 
1537702e47cSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
1547702e47cSPaolo Bonzini         if (inttgt_output[i][1] == output) {
1557702e47cSPaolo Bonzini             return inttgt_output[i][0];
1567702e47cSPaolo Bonzini         }
1577702e47cSPaolo Bonzini     }
1587702e47cSPaolo Bonzini 
1597702e47cSPaolo Bonzini     abort();
1607702e47cSPaolo Bonzini }
1617702e47cSPaolo Bonzini 
1627702e47cSPaolo Bonzini #define MSIIR_OFFSET       0x140
1637702e47cSPaolo Bonzini #define MSIIR_SRS_SHIFT    29
1647702e47cSPaolo Bonzini #define MSIIR_SRS_MASK     (0x7 << MSIIR_SRS_SHIFT)
1657702e47cSPaolo Bonzini #define MSIIR_IBS_SHIFT    24
1667702e47cSPaolo Bonzini #define MSIIR_IBS_MASK     (0x1f << MSIIR_IBS_SHIFT)
1677702e47cSPaolo Bonzini 
1687702e47cSPaolo Bonzini static int get_current_cpu(void)
1697702e47cSPaolo Bonzini {
1704917cf44SAndreas Färber     if (!current_cpu) {
1717702e47cSPaolo Bonzini         return -1;
1727702e47cSPaolo Bonzini     }
1737702e47cSPaolo Bonzini 
1744917cf44SAndreas Färber     return current_cpu->cpu_index;
1757702e47cSPaolo Bonzini }
1767702e47cSPaolo Bonzini 
1777702e47cSPaolo Bonzini static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
1787702e47cSPaolo Bonzini                                           int idx);
1797702e47cSPaolo Bonzini static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
1807702e47cSPaolo Bonzini                                        uint32_t val, int idx);
1818ebe65f3SPaul Janzen static void openpic_reset(DeviceState *d);
1827702e47cSPaolo Bonzini 
183ddd5140bSAaron Larson /* Convert between openpic clock ticks and nanosecs.  In the hardware the clock
184ddd5140bSAaron Larson    frequency is driven by board inputs to the PIC which the PIC would then
185ddd5140bSAaron Larson    divide by 4 or 8.  For now hard code to 25MZ.
186ddd5140bSAaron Larson */
187ddd5140bSAaron Larson #define OPENPIC_TIMER_FREQ_MHZ 25
188ddd5140bSAaron Larson #define OPENPIC_TIMER_NS_PER_TICK (1000 / OPENPIC_TIMER_FREQ_MHZ)
189ddd5140bSAaron Larson static inline uint64_t ns_to_ticks(uint64_t ns)
190ddd5140bSAaron Larson {
191ddd5140bSAaron Larson     return ns    / OPENPIC_TIMER_NS_PER_TICK;
192ddd5140bSAaron Larson }
193ddd5140bSAaron Larson static inline uint64_t ticks_to_ns(uint64_t ticks)
194ddd5140bSAaron Larson {
195ddd5140bSAaron Larson     return ticks * OPENPIC_TIMER_NS_PER_TICK;
196ddd5140bSAaron Larson }
197ddd5140bSAaron Larson 
1987702e47cSPaolo Bonzini static inline void IRQ_setbit(IRQQueue *q, int n_IRQ)
1997702e47cSPaolo Bonzini {
2007702e47cSPaolo Bonzini     set_bit(n_IRQ, q->queue);
2017702e47cSPaolo Bonzini }
2027702e47cSPaolo Bonzini 
2037702e47cSPaolo Bonzini static inline void IRQ_resetbit(IRQQueue *q, int n_IRQ)
2047702e47cSPaolo Bonzini {
2057702e47cSPaolo Bonzini     clear_bit(n_IRQ, q->queue);
2067702e47cSPaolo Bonzini }
2077702e47cSPaolo Bonzini 
2087702e47cSPaolo Bonzini static void IRQ_check(OpenPICState *opp, IRQQueue *q)
2097702e47cSPaolo Bonzini {
2107702e47cSPaolo Bonzini     int irq = -1;
2117702e47cSPaolo Bonzini     int next = -1;
2127702e47cSPaolo Bonzini     int priority = -1;
2137702e47cSPaolo Bonzini 
2147702e47cSPaolo Bonzini     for (;;) {
2157702e47cSPaolo Bonzini         irq = find_next_bit(q->queue, opp->max_irq, irq + 1);
2167702e47cSPaolo Bonzini         if (irq == opp->max_irq) {
2177702e47cSPaolo Bonzini             break;
2187702e47cSPaolo Bonzini         }
2197702e47cSPaolo Bonzini 
220df592270SMichael Davidsaver         DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d",
2217702e47cSPaolo Bonzini                 irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority);
2227702e47cSPaolo Bonzini 
2237702e47cSPaolo Bonzini         if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) {
2247702e47cSPaolo Bonzini             next = irq;
2257702e47cSPaolo Bonzini             priority = IVPR_PRIORITY(opp->src[irq].ivpr);
2267702e47cSPaolo Bonzini         }
2277702e47cSPaolo Bonzini     }
2287702e47cSPaolo Bonzini 
2297702e47cSPaolo Bonzini     q->next = next;
2307702e47cSPaolo Bonzini     q->priority = priority;
2317702e47cSPaolo Bonzini }
2327702e47cSPaolo Bonzini 
2337702e47cSPaolo Bonzini static int IRQ_get_next(OpenPICState *opp, IRQQueue *q)
2347702e47cSPaolo Bonzini {
2357702e47cSPaolo Bonzini     /* XXX: optimize */
2367702e47cSPaolo Bonzini     IRQ_check(opp, q);
2377702e47cSPaolo Bonzini 
2387702e47cSPaolo Bonzini     return q->next;
2397702e47cSPaolo Bonzini }
2407702e47cSPaolo Bonzini 
2417702e47cSPaolo Bonzini static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ,
2427702e47cSPaolo Bonzini                            bool active, bool was_active)
2437702e47cSPaolo Bonzini {
2447702e47cSPaolo Bonzini     IRQDest *dst;
2457702e47cSPaolo Bonzini     IRQSource *src;
2467702e47cSPaolo Bonzini     int priority;
2477702e47cSPaolo Bonzini 
2487702e47cSPaolo Bonzini     dst = &opp->dst[n_CPU];
2497702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
2507702e47cSPaolo Bonzini 
251df592270SMichael Davidsaver     DPRINTF("%s: IRQ %d active %d was %d",
2527702e47cSPaolo Bonzini             __func__, n_IRQ, active, was_active);
2537702e47cSPaolo Bonzini 
2547702e47cSPaolo Bonzini     if (src->output != OPENPIC_OUTPUT_INT) {
255df592270SMichael Davidsaver         DPRINTF("%s: output %d irq %d active %d was %d count %d",
2567702e47cSPaolo Bonzini                 __func__, src->output, n_IRQ, active, was_active,
2577702e47cSPaolo Bonzini                 dst->outputs_active[src->output]);
2587702e47cSPaolo Bonzini 
2597702e47cSPaolo Bonzini         /* On Freescale MPIC, critical interrupts ignore priority,
2607702e47cSPaolo Bonzini          * IACK, EOI, etc.  Before MPIC v4.1 they also ignore
2617702e47cSPaolo Bonzini          * masking.
2627702e47cSPaolo Bonzini          */
2637702e47cSPaolo Bonzini         if (active) {
2647702e47cSPaolo Bonzini             if (!was_active && dst->outputs_active[src->output]++ == 0) {
265df592270SMichael Davidsaver                 DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d",
2667702e47cSPaolo Bonzini                         __func__, src->output, n_CPU, n_IRQ);
2677702e47cSPaolo Bonzini                 qemu_irq_raise(dst->irqs[src->output]);
2687702e47cSPaolo Bonzini             }
2697702e47cSPaolo Bonzini         } else {
2707702e47cSPaolo Bonzini             if (was_active && --dst->outputs_active[src->output] == 0) {
271df592270SMichael Davidsaver                 DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d",
2727702e47cSPaolo Bonzini                         __func__, src->output, n_CPU, n_IRQ);
2737702e47cSPaolo Bonzini                 qemu_irq_lower(dst->irqs[src->output]);
2747702e47cSPaolo Bonzini             }
2757702e47cSPaolo Bonzini         }
2767702e47cSPaolo Bonzini 
2777702e47cSPaolo Bonzini         return;
2787702e47cSPaolo Bonzini     }
2797702e47cSPaolo Bonzini 
2807702e47cSPaolo Bonzini     priority = IVPR_PRIORITY(src->ivpr);
2817702e47cSPaolo Bonzini 
2827702e47cSPaolo Bonzini     /* Even if the interrupt doesn't have enough priority,
2837702e47cSPaolo Bonzini      * it is still raised, in case ctpr is lowered later.
2847702e47cSPaolo Bonzini      */
2857702e47cSPaolo Bonzini     if (active) {
2867702e47cSPaolo Bonzini         IRQ_setbit(&dst->raised, n_IRQ);
2877702e47cSPaolo Bonzini     } else {
2887702e47cSPaolo Bonzini         IRQ_resetbit(&dst->raised, n_IRQ);
2897702e47cSPaolo Bonzini     }
2907702e47cSPaolo Bonzini 
2917702e47cSPaolo Bonzini     IRQ_check(opp, &dst->raised);
2927702e47cSPaolo Bonzini 
2937702e47cSPaolo Bonzini     if (active && priority <= dst->ctpr) {
294df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d",
2957702e47cSPaolo Bonzini                 __func__, n_IRQ, priority, dst->ctpr, n_CPU);
2967702e47cSPaolo Bonzini         active = 0;
2977702e47cSPaolo Bonzini     }
2987702e47cSPaolo Bonzini 
2997702e47cSPaolo Bonzini     if (active) {
3007702e47cSPaolo Bonzini         if (IRQ_get_next(opp, &dst->servicing) >= 0 &&
3017702e47cSPaolo Bonzini                 priority <= dst->servicing.priority) {
302df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d",
3037702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->servicing.next, n_CPU);
3047702e47cSPaolo Bonzini         } else {
305df592270SMichael Davidsaver             DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d",
3067702e47cSPaolo Bonzini                     __func__, n_CPU, n_IRQ, dst->raised.next);
3077702e47cSPaolo Bonzini             qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
3087702e47cSPaolo Bonzini         }
3097702e47cSPaolo Bonzini     } else {
3107702e47cSPaolo Bonzini         IRQ_get_next(opp, &dst->servicing);
3117702e47cSPaolo Bonzini         if (dst->raised.priority > dst->ctpr &&
3127702e47cSPaolo Bonzini                 dst->raised.priority > dst->servicing.priority) {
313df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d",
3147702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->raised.next, dst->raised.priority,
3157702e47cSPaolo Bonzini                     dst->ctpr, dst->servicing.priority, n_CPU);
3167702e47cSPaolo Bonzini             /* IRQ line stays asserted */
3177702e47cSPaolo Bonzini         } else {
318df592270SMichael Davidsaver             DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d",
3197702e47cSPaolo Bonzini                     __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU);
3207702e47cSPaolo Bonzini             qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
3217702e47cSPaolo Bonzini         }
3227702e47cSPaolo Bonzini     }
3237702e47cSPaolo Bonzini }
3247702e47cSPaolo Bonzini 
3257702e47cSPaolo Bonzini /* update pic state because registers for n_IRQ have changed value */
3267702e47cSPaolo Bonzini static void openpic_update_irq(OpenPICState *opp, int n_IRQ)
3277702e47cSPaolo Bonzini {
3287702e47cSPaolo Bonzini     IRQSource *src;
3297702e47cSPaolo Bonzini     bool active, was_active;
3307702e47cSPaolo Bonzini     int i;
3317702e47cSPaolo Bonzini 
3327702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
3337702e47cSPaolo Bonzini     active = src->pending;
3347702e47cSPaolo Bonzini 
3357702e47cSPaolo Bonzini     if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) {
3367702e47cSPaolo Bonzini         /* Interrupt source is disabled */
337df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d is disabled", __func__, n_IRQ);
3387702e47cSPaolo Bonzini         active = false;
3397702e47cSPaolo Bonzini     }
3407702e47cSPaolo Bonzini 
3417702e47cSPaolo Bonzini     was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK);
3427702e47cSPaolo Bonzini 
3437702e47cSPaolo Bonzini     /*
3447702e47cSPaolo Bonzini      * We don't have a similar check for already-active because
3457702e47cSPaolo Bonzini      * ctpr may have changed and we need to withdraw the interrupt.
3467702e47cSPaolo Bonzini      */
3477702e47cSPaolo Bonzini     if (!active && !was_active) {
348df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d is already inactive", __func__, n_IRQ);
3497702e47cSPaolo Bonzini         return;
3507702e47cSPaolo Bonzini     }
3517702e47cSPaolo Bonzini 
3527702e47cSPaolo Bonzini     if (active) {
3537702e47cSPaolo Bonzini         src->ivpr |= IVPR_ACTIVITY_MASK;
3547702e47cSPaolo Bonzini     } else {
3557702e47cSPaolo Bonzini         src->ivpr &= ~IVPR_ACTIVITY_MASK;
3567702e47cSPaolo Bonzini     }
3577702e47cSPaolo Bonzini 
3587702e47cSPaolo Bonzini     if (src->destmask == 0) {
3597702e47cSPaolo Bonzini         /* No target */
360df592270SMichael Davidsaver         DPRINTF("%s: IRQ %d has no target", __func__, n_IRQ);
3617702e47cSPaolo Bonzini         return;
3627702e47cSPaolo Bonzini     }
3637702e47cSPaolo Bonzini 
3647702e47cSPaolo Bonzini     if (src->destmask == (1 << src->last_cpu)) {
3657702e47cSPaolo Bonzini         /* Only one CPU is allowed to receive this IRQ */
3667702e47cSPaolo Bonzini         IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active);
3677702e47cSPaolo Bonzini     } else if (!(src->ivpr & IVPR_MODE_MASK)) {
3687702e47cSPaolo Bonzini         /* Directed delivery mode */
3697702e47cSPaolo Bonzini         for (i = 0; i < opp->nb_cpus; i++) {
3707702e47cSPaolo Bonzini             if (src->destmask & (1 << i)) {
3717702e47cSPaolo Bonzini                 IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
3727702e47cSPaolo Bonzini             }
3737702e47cSPaolo Bonzini         }
3747702e47cSPaolo Bonzini     } else {
3757702e47cSPaolo Bonzini         /* Distributed delivery mode */
3767702e47cSPaolo Bonzini         for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
3777702e47cSPaolo Bonzini             if (i == opp->nb_cpus) {
3787702e47cSPaolo Bonzini                 i = 0;
3797702e47cSPaolo Bonzini             }
3807702e47cSPaolo Bonzini             if (src->destmask & (1 << i)) {
3817702e47cSPaolo Bonzini                 IRQ_local_pipe(opp, i, n_IRQ, active, was_active);
3827702e47cSPaolo Bonzini                 src->last_cpu = i;
3837702e47cSPaolo Bonzini                 break;
3847702e47cSPaolo Bonzini             }
3857702e47cSPaolo Bonzini         }
3867702e47cSPaolo Bonzini     }
3877702e47cSPaolo Bonzini }
3887702e47cSPaolo Bonzini 
3897702e47cSPaolo Bonzini static void openpic_set_irq(void *opaque, int n_IRQ, int level)
3907702e47cSPaolo Bonzini {
3917702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
3927702e47cSPaolo Bonzini     IRQSource *src;
3937702e47cSPaolo Bonzini 
3948935a442SScott Wood     if (n_IRQ >= OPENPIC_MAX_IRQ) {
395df592270SMichael Davidsaver         error_report("%s: IRQ %d out of range", __func__, n_IRQ);
3967702e47cSPaolo Bonzini         abort();
3977702e47cSPaolo Bonzini     }
3987702e47cSPaolo Bonzini 
3997702e47cSPaolo Bonzini     src = &opp->src[n_IRQ];
400df592270SMichael Davidsaver     DPRINTF("openpic: set irq %d = %d ivpr=0x%08x",
4017702e47cSPaolo Bonzini             n_IRQ, level, src->ivpr);
4027702e47cSPaolo Bonzini     if (src->level) {
4037702e47cSPaolo Bonzini         /* level-sensitive irq */
4047702e47cSPaolo Bonzini         src->pending = level;
4057702e47cSPaolo Bonzini         openpic_update_irq(opp, n_IRQ);
4067702e47cSPaolo Bonzini     } else {
4077702e47cSPaolo Bonzini         /* edge-sensitive irq */
4087702e47cSPaolo Bonzini         if (level) {
4097702e47cSPaolo Bonzini             src->pending = 1;
4107702e47cSPaolo Bonzini             openpic_update_irq(opp, n_IRQ);
4117702e47cSPaolo Bonzini         }
4127702e47cSPaolo Bonzini 
4137702e47cSPaolo Bonzini         if (src->output != OPENPIC_OUTPUT_INT) {
4147702e47cSPaolo Bonzini             /* Edge-triggered interrupts shouldn't be used
4157702e47cSPaolo Bonzini              * with non-INT delivery, but just in case,
4167702e47cSPaolo Bonzini              * try to make it do something sane rather than
4177702e47cSPaolo Bonzini              * cause an interrupt storm.  This is close to
4187702e47cSPaolo Bonzini              * what you'd probably see happen in real hardware.
4197702e47cSPaolo Bonzini              */
4207702e47cSPaolo Bonzini             src->pending = 0;
4217702e47cSPaolo Bonzini             openpic_update_irq(opp, n_IRQ);
4227702e47cSPaolo Bonzini         }
4237702e47cSPaolo Bonzini     }
4247702e47cSPaolo Bonzini }
4257702e47cSPaolo Bonzini 
4267702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ)
4277702e47cSPaolo Bonzini {
4287702e47cSPaolo Bonzini     return opp->src[n_IRQ].idr;
4297702e47cSPaolo Bonzini }
4307702e47cSPaolo Bonzini 
4317702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ)
4327702e47cSPaolo Bonzini {
4337702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_ILR) {
4347702e47cSPaolo Bonzini         return output_to_inttgt(opp->src[n_IRQ].output);
4357702e47cSPaolo Bonzini     }
4367702e47cSPaolo Bonzini 
4377702e47cSPaolo Bonzini     return 0xffffffff;
4387702e47cSPaolo Bonzini }
4397702e47cSPaolo Bonzini 
4407702e47cSPaolo Bonzini static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ)
4417702e47cSPaolo Bonzini {
4427702e47cSPaolo Bonzini     return opp->src[n_IRQ].ivpr;
4437702e47cSPaolo Bonzini }
4447702e47cSPaolo Bonzini 
4457702e47cSPaolo Bonzini static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val)
4467702e47cSPaolo Bonzini {
4477702e47cSPaolo Bonzini     IRQSource *src = &opp->src[n_IRQ];
4487702e47cSPaolo Bonzini     uint32_t normal_mask = (1UL << opp->nb_cpus) - 1;
4497702e47cSPaolo Bonzini     uint32_t crit_mask = 0;
4507702e47cSPaolo Bonzini     uint32_t mask = normal_mask;
4517702e47cSPaolo Bonzini     int crit_shift = IDR_EP_SHIFT - opp->nb_cpus;
4527702e47cSPaolo Bonzini     int i;
4537702e47cSPaolo Bonzini 
4547702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
4557702e47cSPaolo Bonzini         crit_mask = mask << crit_shift;
4567702e47cSPaolo Bonzini         mask |= crit_mask | IDR_EP;
4577702e47cSPaolo Bonzini     }
4587702e47cSPaolo Bonzini 
4597702e47cSPaolo Bonzini     src->idr = val & mask;
460df592270SMichael Davidsaver     DPRINTF("Set IDR %d to 0x%08x", n_IRQ, src->idr);
4617702e47cSPaolo Bonzini 
4627702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_IDR_CRIT) {
4637702e47cSPaolo Bonzini         if (src->idr & crit_mask) {
4647702e47cSPaolo Bonzini             if (src->idr & normal_mask) {
4657702e47cSPaolo Bonzini                 DPRINTF("%s: IRQ configured for multiple output types, using "
466df592270SMichael Davidsaver                         "critical", __func__);
4677702e47cSPaolo Bonzini             }
4687702e47cSPaolo Bonzini 
4697702e47cSPaolo Bonzini             src->output = OPENPIC_OUTPUT_CINT;
4707702e47cSPaolo Bonzini             src->nomask = true;
4717702e47cSPaolo Bonzini             src->destmask = 0;
4727702e47cSPaolo Bonzini 
4737702e47cSPaolo Bonzini             for (i = 0; i < opp->nb_cpus; i++) {
4747702e47cSPaolo Bonzini                 int n_ci = IDR_CI0_SHIFT - i;
4757702e47cSPaolo Bonzini 
4767702e47cSPaolo Bonzini                 if (src->idr & (1UL << n_ci)) {
4777702e47cSPaolo Bonzini                     src->destmask |= 1UL << i;
4787702e47cSPaolo Bonzini                 }
4797702e47cSPaolo Bonzini             }
4807702e47cSPaolo Bonzini         } else {
4817702e47cSPaolo Bonzini             src->output = OPENPIC_OUTPUT_INT;
4827702e47cSPaolo Bonzini             src->nomask = false;
4837702e47cSPaolo Bonzini             src->destmask = src->idr & normal_mask;
4847702e47cSPaolo Bonzini         }
4857702e47cSPaolo Bonzini     } else {
4867702e47cSPaolo Bonzini         src->destmask = src->idr;
4877702e47cSPaolo Bonzini     }
4887702e47cSPaolo Bonzini }
4897702e47cSPaolo Bonzini 
4907702e47cSPaolo Bonzini static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val)
4917702e47cSPaolo Bonzini {
4927702e47cSPaolo Bonzini     if (opp->flags & OPENPIC_FLAG_ILR) {
4937702e47cSPaolo Bonzini         IRQSource *src = &opp->src[n_IRQ];
4947702e47cSPaolo Bonzini 
4957702e47cSPaolo Bonzini         src->output = inttgt_to_output(val & ILR_INTTGT_MASK);
496df592270SMichael Davidsaver         DPRINTF("Set ILR %d to 0x%08x, output %d", n_IRQ, src->idr,
4977702e47cSPaolo Bonzini                 src->output);
4987702e47cSPaolo Bonzini 
4997702e47cSPaolo Bonzini         /* TODO: on MPIC v4.0 only, set nomask for non-INT */
5007702e47cSPaolo Bonzini     }
5017702e47cSPaolo Bonzini }
5027702e47cSPaolo Bonzini 
5037702e47cSPaolo Bonzini static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val)
5047702e47cSPaolo Bonzini {
5057702e47cSPaolo Bonzini     uint32_t mask;
5067702e47cSPaolo Bonzini 
5077702e47cSPaolo Bonzini     /* NOTE when implementing newer FSL MPIC models: starting with v4.0,
5087702e47cSPaolo Bonzini      * the polarity bit is read-only on internal interrupts.
5097702e47cSPaolo Bonzini      */
5107702e47cSPaolo Bonzini     mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK |
5117702e47cSPaolo Bonzini            IVPR_POLARITY_MASK | opp->vector_mask;
5127702e47cSPaolo Bonzini 
5137702e47cSPaolo Bonzini     /* ACTIVITY bit is read-only */
5147702e47cSPaolo Bonzini     opp->src[n_IRQ].ivpr =
5157702e47cSPaolo Bonzini         (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask);
5167702e47cSPaolo Bonzini 
5177702e47cSPaolo Bonzini     /* For FSL internal interrupts, The sense bit is reserved and zero,
5187702e47cSPaolo Bonzini      * and the interrupt is always level-triggered.  Timers and IPIs
5197702e47cSPaolo Bonzini      * have no sense or polarity bits, and are edge-triggered.
5207702e47cSPaolo Bonzini      */
5217702e47cSPaolo Bonzini     switch (opp->src[n_IRQ].type) {
5227702e47cSPaolo Bonzini     case IRQ_TYPE_NORMAL:
5237702e47cSPaolo Bonzini         opp->src[n_IRQ].level = !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK);
5247702e47cSPaolo Bonzini         break;
5257702e47cSPaolo Bonzini 
5267702e47cSPaolo Bonzini     case IRQ_TYPE_FSLINT:
5277702e47cSPaolo Bonzini         opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK;
5287702e47cSPaolo Bonzini         break;
5297702e47cSPaolo Bonzini 
5307702e47cSPaolo Bonzini     case IRQ_TYPE_FSLSPECIAL:
5317702e47cSPaolo Bonzini         opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK);
5327702e47cSPaolo Bonzini         break;
5337702e47cSPaolo Bonzini     }
5347702e47cSPaolo Bonzini 
5357702e47cSPaolo Bonzini     openpic_update_irq(opp, n_IRQ);
536df592270SMichael Davidsaver     DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x", n_IRQ, val,
5377702e47cSPaolo Bonzini             opp->src[n_IRQ].ivpr);
5387702e47cSPaolo Bonzini }
5397702e47cSPaolo Bonzini 
5407702e47cSPaolo Bonzini static void openpic_gcr_write(OpenPICState *opp, uint64_t val)
5417702e47cSPaolo Bonzini {
5427702e47cSPaolo Bonzini     bool mpic_proxy = false;
5437702e47cSPaolo Bonzini 
5447702e47cSPaolo Bonzini     if (val & GCR_RESET) {
545e1766344SAndreas Färber         openpic_reset(DEVICE(opp));
5467702e47cSPaolo Bonzini         return;
5477702e47cSPaolo Bonzini     }
5487702e47cSPaolo Bonzini 
5497702e47cSPaolo Bonzini     opp->gcr &= ~opp->mpic_mode_mask;
5507702e47cSPaolo Bonzini     opp->gcr |= val & opp->mpic_mode_mask;
5517702e47cSPaolo Bonzini 
5527702e47cSPaolo Bonzini     /* Set external proxy mode */
5537702e47cSPaolo Bonzini     if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY) {
5547702e47cSPaolo Bonzini         mpic_proxy = true;
5557702e47cSPaolo Bonzini     }
5567702e47cSPaolo Bonzini 
5577702e47cSPaolo Bonzini     ppce500_set_mpic_proxy(mpic_proxy);
5587702e47cSPaolo Bonzini }
5597702e47cSPaolo Bonzini 
5607702e47cSPaolo Bonzini static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val,
5617702e47cSPaolo Bonzini                               unsigned len)
5627702e47cSPaolo Bonzini {
5637702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
5647702e47cSPaolo Bonzini     IRQDest *dst;
5657702e47cSPaolo Bonzini     int idx;
5667702e47cSPaolo Bonzini 
567df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
5687702e47cSPaolo Bonzini             __func__, addr, val);
5697702e47cSPaolo Bonzini     if (addr & 0xF) {
5707702e47cSPaolo Bonzini         return;
5717702e47cSPaolo Bonzini     }
5727702e47cSPaolo Bonzini     switch (addr) {
5737702e47cSPaolo Bonzini     case 0x00: /* Block Revision Register1 (BRR1) is Readonly */
5747702e47cSPaolo Bonzini         break;
5757702e47cSPaolo Bonzini     case 0x40:
5767702e47cSPaolo Bonzini     case 0x50:
5777702e47cSPaolo Bonzini     case 0x60:
5787702e47cSPaolo Bonzini     case 0x70:
5797702e47cSPaolo Bonzini     case 0x80:
5807702e47cSPaolo Bonzini     case 0x90:
5817702e47cSPaolo Bonzini     case 0xA0:
5827702e47cSPaolo Bonzini     case 0xB0:
5837702e47cSPaolo Bonzini         openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
5847702e47cSPaolo Bonzini         break;
5857702e47cSPaolo Bonzini     case 0x1000: /* FRR */
5867702e47cSPaolo Bonzini         break;
5877702e47cSPaolo Bonzini     case 0x1020: /* GCR */
5887702e47cSPaolo Bonzini         openpic_gcr_write(opp, val);
5897702e47cSPaolo Bonzini         break;
5907702e47cSPaolo Bonzini     case 0x1080: /* VIR */
5917702e47cSPaolo Bonzini         break;
5927702e47cSPaolo Bonzini     case 0x1090: /* PIR */
5937702e47cSPaolo Bonzini         for (idx = 0; idx < opp->nb_cpus; idx++) {
5947702e47cSPaolo Bonzini             if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) {
595df592270SMichael Davidsaver                 DPRINTF("Raise OpenPIC RESET output for CPU %d", idx);
5967702e47cSPaolo Bonzini                 dst = &opp->dst[idx];
5977702e47cSPaolo Bonzini                 qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]);
5987702e47cSPaolo Bonzini             } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) {
599df592270SMichael Davidsaver                 DPRINTF("Lower OpenPIC RESET output for CPU %d", idx);
6007702e47cSPaolo Bonzini                 dst = &opp->dst[idx];
6017702e47cSPaolo Bonzini                 qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]);
6027702e47cSPaolo Bonzini             }
6037702e47cSPaolo Bonzini         }
6047702e47cSPaolo Bonzini         opp->pir = val;
6057702e47cSPaolo Bonzini         break;
6067702e47cSPaolo Bonzini     case 0x10A0: /* IPI_IVPR */
6077702e47cSPaolo Bonzini     case 0x10B0:
6087702e47cSPaolo Bonzini     case 0x10C0:
6097702e47cSPaolo Bonzini     case 0x10D0:
6107702e47cSPaolo Bonzini         {
6117702e47cSPaolo Bonzini             int idx;
6127702e47cSPaolo Bonzini             idx = (addr - 0x10A0) >> 4;
6137702e47cSPaolo Bonzini             write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val);
6147702e47cSPaolo Bonzini         }
6157702e47cSPaolo Bonzini         break;
6167702e47cSPaolo Bonzini     case 0x10E0: /* SPVE */
6177702e47cSPaolo Bonzini         opp->spve = val & opp->vector_mask;
6187702e47cSPaolo Bonzini         break;
6197702e47cSPaolo Bonzini     default:
6207702e47cSPaolo Bonzini         break;
6217702e47cSPaolo Bonzini     }
6227702e47cSPaolo Bonzini }
6237702e47cSPaolo Bonzini 
6247702e47cSPaolo Bonzini static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len)
6257702e47cSPaolo Bonzini {
6267702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
6277702e47cSPaolo Bonzini     uint32_t retval;
6287702e47cSPaolo Bonzini 
629df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
6307702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
6317702e47cSPaolo Bonzini     if (addr & 0xF) {
6327702e47cSPaolo Bonzini         return retval;
6337702e47cSPaolo Bonzini     }
6347702e47cSPaolo Bonzini     switch (addr) {
6357702e47cSPaolo Bonzini     case 0x1000: /* FRR */
6367702e47cSPaolo Bonzini         retval = opp->frr;
6377702e47cSPaolo Bonzini         break;
6387702e47cSPaolo Bonzini     case 0x1020: /* GCR */
6397702e47cSPaolo Bonzini         retval = opp->gcr;
6407702e47cSPaolo Bonzini         break;
6417702e47cSPaolo Bonzini     case 0x1080: /* VIR */
6427702e47cSPaolo Bonzini         retval = opp->vir;
6437702e47cSPaolo Bonzini         break;
6447702e47cSPaolo Bonzini     case 0x1090: /* PIR */
6457702e47cSPaolo Bonzini         retval = 0x00000000;
6467702e47cSPaolo Bonzini         break;
6477702e47cSPaolo Bonzini     case 0x00: /* Block Revision Register1 (BRR1) */
6487702e47cSPaolo Bonzini         retval = opp->brr1;
6497702e47cSPaolo Bonzini         break;
6507702e47cSPaolo Bonzini     case 0x40:
6517702e47cSPaolo Bonzini     case 0x50:
6527702e47cSPaolo Bonzini     case 0x60:
6537702e47cSPaolo Bonzini     case 0x70:
6547702e47cSPaolo Bonzini     case 0x80:
6557702e47cSPaolo Bonzini     case 0x90:
6567702e47cSPaolo Bonzini     case 0xA0:
6577702e47cSPaolo Bonzini     case 0xB0:
6587702e47cSPaolo Bonzini         retval = openpic_cpu_read_internal(opp, addr, get_current_cpu());
6597702e47cSPaolo Bonzini         break;
6607702e47cSPaolo Bonzini     case 0x10A0: /* IPI_IVPR */
6617702e47cSPaolo Bonzini     case 0x10B0:
6627702e47cSPaolo Bonzini     case 0x10C0:
6637702e47cSPaolo Bonzini     case 0x10D0:
6647702e47cSPaolo Bonzini         {
6657702e47cSPaolo Bonzini             int idx;
6667702e47cSPaolo Bonzini             idx = (addr - 0x10A0) >> 4;
6677702e47cSPaolo Bonzini             retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx);
6687702e47cSPaolo Bonzini         }
6697702e47cSPaolo Bonzini         break;
6707702e47cSPaolo Bonzini     case 0x10E0: /* SPVE */
6717702e47cSPaolo Bonzini         retval = opp->spve;
6727702e47cSPaolo Bonzini         break;
6737702e47cSPaolo Bonzini     default:
6747702e47cSPaolo Bonzini         break;
6757702e47cSPaolo Bonzini     }
676df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
6777702e47cSPaolo Bonzini 
6787702e47cSPaolo Bonzini     return retval;
6797702e47cSPaolo Bonzini }
6807702e47cSPaolo Bonzini 
681ddd5140bSAaron Larson static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled);
682ddd5140bSAaron Larson 
683ddd5140bSAaron Larson static void qemu_timer_cb(void *opaque)
684ddd5140bSAaron Larson {
685ddd5140bSAaron Larson     OpenPICTimer *tmr = opaque;
686ddd5140bSAaron Larson     OpenPICState *opp = tmr->opp;
687ddd5140bSAaron Larson     uint32_t    n_IRQ = tmr->n_IRQ;
688ddd5140bSAaron Larson     uint32_t val =   tmr->tbcr & ~TBCR_CI;
689ddd5140bSAaron Larson     uint32_t tog = ((tmr->tccr & TCCR_TOG) ^ TCCR_TOG);  /* invert toggle. */
690ddd5140bSAaron Larson 
691df592270SMichael Davidsaver     DPRINTF("%s n_IRQ=%d", __func__, n_IRQ);
692ddd5140bSAaron Larson     /* Reload current count from base count and setup timer. */
693ddd5140bSAaron Larson     tmr->tccr = val | tog;
694ddd5140bSAaron Larson     openpic_tmr_set_tmr(tmr, val, /*enabled=*/true);
695ddd5140bSAaron Larson     /* Raise the interrupt. */
696ddd5140bSAaron Larson     opp->src[n_IRQ].destmask = read_IRQreg_idr(opp, n_IRQ);
697ddd5140bSAaron Larson     openpic_set_irq(opp, n_IRQ, 1);
698ddd5140bSAaron Larson     openpic_set_irq(opp, n_IRQ, 0);
699ddd5140bSAaron Larson }
700ddd5140bSAaron Larson 
701ddd5140bSAaron Larson /* If enabled is true, arranges for an interrupt to be raised val clocks into
702ddd5140bSAaron Larson    the future, if enabled is false cancels the timer. */
703ddd5140bSAaron Larson static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled)
704ddd5140bSAaron Larson {
705ddd5140bSAaron Larson     uint64_t ns = ticks_to_ns(val & ~TCCR_TOG);
706ddd5140bSAaron Larson     /* A count of zero causes a timer to be set to expire immediately.  This
707ddd5140bSAaron Larson        effectively stops the simulation since the timer is constantly expiring
708ddd5140bSAaron Larson        which prevents guest code execution, so we don't honor that
709ddd5140bSAaron Larson        configuration.  On real hardware, this situation would generate an
710ddd5140bSAaron Larson        interrupt on every clock cycle if the interrupt was unmasked. */
711ddd5140bSAaron Larson     if ((ns == 0) || !enabled) {
712ddd5140bSAaron Larson         tmr->qemu_timer_active = false;
713ddd5140bSAaron Larson         tmr->tccr = tmr->tccr & TCCR_TOG;
714ddd5140bSAaron Larson         timer_del(tmr->qemu_timer); /* set timer to never expire. */
715ddd5140bSAaron Larson     } else {
716ddd5140bSAaron Larson         tmr->qemu_timer_active = true;
717ddd5140bSAaron Larson         uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
718ddd5140bSAaron Larson         tmr->origin_time = now;
719ddd5140bSAaron Larson         timer_mod(tmr->qemu_timer, now + ns);     /* set timer expiration. */
720ddd5140bSAaron Larson     }
721ddd5140bSAaron Larson }
722ddd5140bSAaron Larson 
723ddd5140bSAaron Larson /* Returns the currrent tccr value, i.e., timer value (in clocks) with
724ddd5140bSAaron Larson    appropriate TOG. */
725ddd5140bSAaron Larson static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr)
726ddd5140bSAaron Larson {
727ddd5140bSAaron Larson     uint64_t retval;
728ddd5140bSAaron Larson     if (!tmr->qemu_timer_active) {
729ddd5140bSAaron Larson         retval = tmr->tccr;
730ddd5140bSAaron Larson     } else {
731ddd5140bSAaron Larson         uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
732ddd5140bSAaron Larson         uint64_t used = now - tmr->origin_time;  /* nsecs */
733ddd5140bSAaron Larson         uint32_t used_ticks = (uint32_t)ns_to_ticks(used);
734ddd5140bSAaron Larson         uint32_t count = (tmr->tccr & ~TCCR_TOG) - used_ticks;
735ddd5140bSAaron Larson         retval = (uint32_t)((tmr->tccr & TCCR_TOG) | (count & ~TCCR_TOG));
736ddd5140bSAaron Larson     }
737ddd5140bSAaron Larson     return retval;
738ddd5140bSAaron Larson }
739ddd5140bSAaron Larson 
7407702e47cSPaolo Bonzini static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val,
7417702e47cSPaolo Bonzini                               unsigned len)
7427702e47cSPaolo Bonzini {
7437702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
7447702e47cSPaolo Bonzini     int idx;
7457702e47cSPaolo Bonzini 
746df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
747a09f7443SAaron Larson             __func__, (addr + 0x10f0), val);
7487702e47cSPaolo Bonzini     if (addr & 0xF) {
7497702e47cSPaolo Bonzini         return;
7507702e47cSPaolo Bonzini     }
7517702e47cSPaolo Bonzini 
752a09f7443SAaron Larson     if (addr == 0) {
7537702e47cSPaolo Bonzini         /* TFRR */
7547702e47cSPaolo Bonzini         opp->tfrr = val;
7557702e47cSPaolo Bonzini         return;
7567702e47cSPaolo Bonzini     }
757a09f7443SAaron Larson     addr -= 0x10;  /* correct for TFRR */
7587702e47cSPaolo Bonzini     idx = (addr >> 6) & 0x3;
7597702e47cSPaolo Bonzini 
7607702e47cSPaolo Bonzini     switch (addr & 0x30) {
7617702e47cSPaolo Bonzini     case 0x00: /* TCCR */
7627702e47cSPaolo Bonzini         break;
7637702e47cSPaolo Bonzini     case 0x10: /* TBCR */
764ddd5140bSAaron Larson         /* Did the enable status change? */
765ddd5140bSAaron Larson         if ((opp->timers[idx].tbcr & TBCR_CI) != (val & TBCR_CI)) {
766ddd5140bSAaron Larson             /* Did "Count Inhibit" transition from 1 to 0? */
767ddd5140bSAaron Larson             if ((val & TBCR_CI) == 0) {
768ddd5140bSAaron Larson                 opp->timers[idx].tccr = val & ~TCCR_TOG;
769ddd5140bSAaron Larson             }
770ddd5140bSAaron Larson             openpic_tmr_set_tmr(&opp->timers[idx],
771ddd5140bSAaron Larson                                 (val & ~TBCR_CI),
772ddd5140bSAaron Larson                                 /*enabled=*/((val & TBCR_CI) == 0));
7737702e47cSPaolo Bonzini         }
7747702e47cSPaolo Bonzini         opp->timers[idx].tbcr = val;
7757702e47cSPaolo Bonzini         break;
7767702e47cSPaolo Bonzini     case 0x20: /* TVPR */
7777702e47cSPaolo Bonzini         write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val);
7787702e47cSPaolo Bonzini         break;
7797702e47cSPaolo Bonzini     case 0x30: /* TDR */
7807702e47cSPaolo Bonzini         write_IRQreg_idr(opp, opp->irq_tim0 + idx, val);
7817702e47cSPaolo Bonzini         break;
7827702e47cSPaolo Bonzini     }
7837702e47cSPaolo Bonzini }
7847702e47cSPaolo Bonzini 
7857702e47cSPaolo Bonzini static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len)
7867702e47cSPaolo Bonzini {
7877702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
7887702e47cSPaolo Bonzini     uint32_t retval = -1;
7897702e47cSPaolo Bonzini     int idx;
7907702e47cSPaolo Bonzini 
791df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr + 0x10f0);
7927702e47cSPaolo Bonzini     if (addr & 0xF) {
7937702e47cSPaolo Bonzini         goto out;
7947702e47cSPaolo Bonzini     }
795a09f7443SAaron Larson     if (addr == 0) {
7967702e47cSPaolo Bonzini         /* TFRR */
7977702e47cSPaolo Bonzini         retval = opp->tfrr;
7987702e47cSPaolo Bonzini         goto out;
7997702e47cSPaolo Bonzini     }
800a09f7443SAaron Larson     addr -= 0x10;  /* correct for TFRR */
801a09f7443SAaron Larson     idx = (addr >> 6) & 0x3;
8027702e47cSPaolo Bonzini     switch (addr & 0x30) {
8037702e47cSPaolo Bonzini     case 0x00: /* TCCR */
804ddd5140bSAaron Larson         retval = openpic_tmr_get_timer(&opp->timers[idx]);
8057702e47cSPaolo Bonzini         break;
8067702e47cSPaolo Bonzini     case 0x10: /* TBCR */
8077702e47cSPaolo Bonzini         retval = opp->timers[idx].tbcr;
8087702e47cSPaolo Bonzini         break;
809a09f7443SAaron Larson     case 0x20: /* TVPR */
8107702e47cSPaolo Bonzini         retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx);
8117702e47cSPaolo Bonzini         break;
812a09f7443SAaron Larson     case 0x30: /* TDR */
8137702e47cSPaolo Bonzini         retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx);
8147702e47cSPaolo Bonzini         break;
8157702e47cSPaolo Bonzini     }
8167702e47cSPaolo Bonzini 
8177702e47cSPaolo Bonzini out:
818df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
8197702e47cSPaolo Bonzini 
8207702e47cSPaolo Bonzini     return retval;
8217702e47cSPaolo Bonzini }
8227702e47cSPaolo Bonzini 
8237702e47cSPaolo Bonzini static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val,
8247702e47cSPaolo Bonzini                               unsigned len)
8257702e47cSPaolo Bonzini {
8267702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8277702e47cSPaolo Bonzini     int idx;
8287702e47cSPaolo Bonzini 
829df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64,
8307702e47cSPaolo Bonzini             __func__, addr, val);
8317702e47cSPaolo Bonzini 
8327702e47cSPaolo Bonzini     addr = addr & 0xffff;
8337702e47cSPaolo Bonzini     idx = addr >> 5;
8347702e47cSPaolo Bonzini 
8357702e47cSPaolo Bonzini     switch (addr & 0x1f) {
8367702e47cSPaolo Bonzini     case 0x00:
8377702e47cSPaolo Bonzini         write_IRQreg_ivpr(opp, idx, val);
8387702e47cSPaolo Bonzini         break;
8397702e47cSPaolo Bonzini     case 0x10:
8407702e47cSPaolo Bonzini         write_IRQreg_idr(opp, idx, val);
8417702e47cSPaolo Bonzini         break;
8427702e47cSPaolo Bonzini     case 0x18:
8437702e47cSPaolo Bonzini         write_IRQreg_ilr(opp, idx, val);
8447702e47cSPaolo Bonzini         break;
8457702e47cSPaolo Bonzini     }
8467702e47cSPaolo Bonzini }
8477702e47cSPaolo Bonzini 
8487702e47cSPaolo Bonzini static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len)
8497702e47cSPaolo Bonzini {
8507702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8517702e47cSPaolo Bonzini     uint32_t retval;
8527702e47cSPaolo Bonzini     int idx;
8537702e47cSPaolo Bonzini 
854df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
8557702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
8567702e47cSPaolo Bonzini 
8577702e47cSPaolo Bonzini     addr = addr & 0xffff;
8587702e47cSPaolo Bonzini     idx = addr >> 5;
8597702e47cSPaolo Bonzini 
8607702e47cSPaolo Bonzini     switch (addr & 0x1f) {
8617702e47cSPaolo Bonzini     case 0x00:
8627702e47cSPaolo Bonzini         retval = read_IRQreg_ivpr(opp, idx);
8637702e47cSPaolo Bonzini         break;
8647702e47cSPaolo Bonzini     case 0x10:
8657702e47cSPaolo Bonzini         retval = read_IRQreg_idr(opp, idx);
8667702e47cSPaolo Bonzini         break;
8677702e47cSPaolo Bonzini     case 0x18:
8687702e47cSPaolo Bonzini         retval = read_IRQreg_ilr(opp, idx);
8697702e47cSPaolo Bonzini         break;
8707702e47cSPaolo Bonzini     }
8717702e47cSPaolo Bonzini 
872df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
8737702e47cSPaolo Bonzini     return retval;
8747702e47cSPaolo Bonzini }
8757702e47cSPaolo Bonzini 
8767702e47cSPaolo Bonzini static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val,
8777702e47cSPaolo Bonzini                               unsigned size)
8787702e47cSPaolo Bonzini {
8797702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
8807702e47cSPaolo Bonzini     int idx = opp->irq_msi;
8817702e47cSPaolo Bonzini     int srs, ibs;
8827702e47cSPaolo Bonzini 
883df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64,
8847702e47cSPaolo Bonzini             __func__, addr, val);
8857702e47cSPaolo Bonzini     if (addr & 0xF) {
8867702e47cSPaolo Bonzini         return;
8877702e47cSPaolo Bonzini     }
8887702e47cSPaolo Bonzini 
8897702e47cSPaolo Bonzini     switch (addr) {
8907702e47cSPaolo Bonzini     case MSIIR_OFFSET:
8917702e47cSPaolo Bonzini         srs = val >> MSIIR_SRS_SHIFT;
8927702e47cSPaolo Bonzini         idx += srs;
8937702e47cSPaolo Bonzini         ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT;
8947702e47cSPaolo Bonzini         opp->msi[srs].msir |= 1 << ibs;
8957702e47cSPaolo Bonzini         openpic_set_irq(opp, idx, 1);
8967702e47cSPaolo Bonzini         break;
8977702e47cSPaolo Bonzini     default:
8987702e47cSPaolo Bonzini         /* most registers are read-only, thus ignored */
8997702e47cSPaolo Bonzini         break;
9007702e47cSPaolo Bonzini     }
9017702e47cSPaolo Bonzini }
9027702e47cSPaolo Bonzini 
9037702e47cSPaolo Bonzini static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size)
9047702e47cSPaolo Bonzini {
9057702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
9067702e47cSPaolo Bonzini     uint64_t r = 0;
9077702e47cSPaolo Bonzini     int i, srs;
9087702e47cSPaolo Bonzini 
909df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
9107702e47cSPaolo Bonzini     if (addr & 0xF) {
9117702e47cSPaolo Bonzini         return -1;
9127702e47cSPaolo Bonzini     }
9137702e47cSPaolo Bonzini 
9147702e47cSPaolo Bonzini     srs = addr >> 4;
9157702e47cSPaolo Bonzini 
9167702e47cSPaolo Bonzini     switch (addr) {
9177702e47cSPaolo Bonzini     case 0x00:
9187702e47cSPaolo Bonzini     case 0x10:
9197702e47cSPaolo Bonzini     case 0x20:
9207702e47cSPaolo Bonzini     case 0x30:
9217702e47cSPaolo Bonzini     case 0x40:
9227702e47cSPaolo Bonzini     case 0x50:
9237702e47cSPaolo Bonzini     case 0x60:
9247702e47cSPaolo Bonzini     case 0x70: /* MSIRs */
9257702e47cSPaolo Bonzini         r = opp->msi[srs].msir;
9267702e47cSPaolo Bonzini         /* Clear on read */
9277702e47cSPaolo Bonzini         opp->msi[srs].msir = 0;
9287702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_msi + srs, 0);
9297702e47cSPaolo Bonzini         break;
9307702e47cSPaolo Bonzini     case 0x120: /* MSISR */
9317702e47cSPaolo Bonzini         for (i = 0; i < MAX_MSI; i++) {
9327702e47cSPaolo Bonzini             r |= (opp->msi[i].msir ? 1 : 0) << i;
9337702e47cSPaolo Bonzini         }
9347702e47cSPaolo Bonzini         break;
9357702e47cSPaolo Bonzini     }
9367702e47cSPaolo Bonzini 
9377702e47cSPaolo Bonzini     return r;
9387702e47cSPaolo Bonzini }
9397702e47cSPaolo Bonzini 
9407702e47cSPaolo Bonzini static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size)
9417702e47cSPaolo Bonzini {
9427702e47cSPaolo Bonzini     uint64_t r = 0;
9437702e47cSPaolo Bonzini 
944df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx, __func__, addr);
9457702e47cSPaolo Bonzini 
9467702e47cSPaolo Bonzini     /* TODO: EISR/EIMR */
9477702e47cSPaolo Bonzini 
9487702e47cSPaolo Bonzini     return r;
9497702e47cSPaolo Bonzini }
9507702e47cSPaolo Bonzini 
9517702e47cSPaolo Bonzini static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val,
9527702e47cSPaolo Bonzini                                   unsigned size)
9537702e47cSPaolo Bonzini {
954df592270SMichael Davidsaver     DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64,
9557702e47cSPaolo Bonzini             __func__, addr, val);
9567702e47cSPaolo Bonzini 
9577702e47cSPaolo Bonzini     /* TODO: EISR/EIMR */
9587702e47cSPaolo Bonzini }
9597702e47cSPaolo Bonzini 
9607702e47cSPaolo Bonzini static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
9617702e47cSPaolo Bonzini                                        uint32_t val, int idx)
9627702e47cSPaolo Bonzini {
9637702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
9647702e47cSPaolo Bonzini     IRQSource *src;
9657702e47cSPaolo Bonzini     IRQDest *dst;
9667702e47cSPaolo Bonzini     int s_IRQ, n_IRQ;
9677702e47cSPaolo Bonzini 
968df592270SMichael Davidsaver     DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x", __func__, idx,
9697702e47cSPaolo Bonzini             addr, val);
9707702e47cSPaolo Bonzini 
97104d2acbbSFabien Chouteau     if (idx < 0 || idx >= opp->nb_cpus) {
9727702e47cSPaolo Bonzini         return;
9737702e47cSPaolo Bonzini     }
9747702e47cSPaolo Bonzini 
9757702e47cSPaolo Bonzini     if (addr & 0xF) {
9767702e47cSPaolo Bonzini         return;
9777702e47cSPaolo Bonzini     }
9787702e47cSPaolo Bonzini     dst = &opp->dst[idx];
9797702e47cSPaolo Bonzini     addr &= 0xFF0;
9807702e47cSPaolo Bonzini     switch (addr) {
9817702e47cSPaolo Bonzini     case 0x40: /* IPIDR */
9827702e47cSPaolo Bonzini     case 0x50:
9837702e47cSPaolo Bonzini     case 0x60:
9847702e47cSPaolo Bonzini     case 0x70:
9857702e47cSPaolo Bonzini         idx = (addr - 0x40) >> 4;
9867702e47cSPaolo Bonzini         /* we use IDE as mask which CPUs to deliver the IPI to still. */
9877702e47cSPaolo Bonzini         opp->src[opp->irq_ipi0 + idx].destmask |= val;
9887702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
9897702e47cSPaolo Bonzini         openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
9907702e47cSPaolo Bonzini         break;
9917702e47cSPaolo Bonzini     case 0x80: /* CTPR */
9927702e47cSPaolo Bonzini         dst->ctpr = val & 0x0000000F;
9937702e47cSPaolo Bonzini 
994df592270SMichael Davidsaver         DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d",
9957702e47cSPaolo Bonzini                 __func__, idx, dst->ctpr, dst->raised.priority,
9967702e47cSPaolo Bonzini                 dst->servicing.priority);
9977702e47cSPaolo Bonzini 
9987702e47cSPaolo Bonzini         if (dst->raised.priority <= dst->ctpr) {
999df592270SMichael Davidsaver             DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr",
10007702e47cSPaolo Bonzini                     __func__, idx);
10017702e47cSPaolo Bonzini             qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
10027702e47cSPaolo Bonzini         } else if (dst->raised.priority > dst->servicing.priority) {
1003df592270SMichael Davidsaver             DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d",
10047702e47cSPaolo Bonzini                     __func__, idx, dst->raised.next);
10057702e47cSPaolo Bonzini             qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]);
10067702e47cSPaolo Bonzini         }
10077702e47cSPaolo Bonzini 
10087702e47cSPaolo Bonzini         break;
10097702e47cSPaolo Bonzini     case 0x90: /* WHOAMI */
10107702e47cSPaolo Bonzini         /* Read-only register */
10117702e47cSPaolo Bonzini         break;
10127702e47cSPaolo Bonzini     case 0xA0: /* IACK */
10137702e47cSPaolo Bonzini         /* Read-only register */
10147702e47cSPaolo Bonzini         break;
10157702e47cSPaolo Bonzini     case 0xB0: /* EOI */
1016df592270SMichael Davidsaver         DPRINTF("EOI");
10177702e47cSPaolo Bonzini         s_IRQ = IRQ_get_next(opp, &dst->servicing);
10187702e47cSPaolo Bonzini 
10197702e47cSPaolo Bonzini         if (s_IRQ < 0) {
1020df592270SMichael Davidsaver             DPRINTF("%s: EOI with no interrupt in service", __func__);
10217702e47cSPaolo Bonzini             break;
10227702e47cSPaolo Bonzini         }
10237702e47cSPaolo Bonzini 
10247702e47cSPaolo Bonzini         IRQ_resetbit(&dst->servicing, s_IRQ);
10257702e47cSPaolo Bonzini         /* Set up next servicing IRQ */
10267702e47cSPaolo Bonzini         s_IRQ = IRQ_get_next(opp, &dst->servicing);
10277702e47cSPaolo Bonzini         /* Check queued interrupts. */
10287702e47cSPaolo Bonzini         n_IRQ = IRQ_get_next(opp, &dst->raised);
10297702e47cSPaolo Bonzini         src = &opp->src[n_IRQ];
10307702e47cSPaolo Bonzini         if (n_IRQ != -1 &&
10317702e47cSPaolo Bonzini             (s_IRQ == -1 ||
10327702e47cSPaolo Bonzini              IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) {
1033df592270SMichael Davidsaver             DPRINTF("Raise OpenPIC INT output cpu %d irq %d",
10347702e47cSPaolo Bonzini                     idx, n_IRQ);
10357702e47cSPaolo Bonzini             qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]);
10367702e47cSPaolo Bonzini         }
10377702e47cSPaolo Bonzini         break;
10387702e47cSPaolo Bonzini     default:
10397702e47cSPaolo Bonzini         break;
10407702e47cSPaolo Bonzini     }
10417702e47cSPaolo Bonzini }
10427702e47cSPaolo Bonzini 
10437702e47cSPaolo Bonzini static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val,
10447702e47cSPaolo Bonzini                               unsigned len)
10457702e47cSPaolo Bonzini {
10467702e47cSPaolo Bonzini     openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
10477702e47cSPaolo Bonzini }
10487702e47cSPaolo Bonzini 
10497702e47cSPaolo Bonzini 
10507702e47cSPaolo Bonzini static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu)
10517702e47cSPaolo Bonzini {
10527702e47cSPaolo Bonzini     IRQSource *src;
10537702e47cSPaolo Bonzini     int retval, irq;
10547702e47cSPaolo Bonzini 
1055df592270SMichael Davidsaver     DPRINTF("Lower OpenPIC INT output");
10567702e47cSPaolo Bonzini     qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
10577702e47cSPaolo Bonzini 
10587702e47cSPaolo Bonzini     irq = IRQ_get_next(opp, &dst->raised);
1059df592270SMichael Davidsaver     DPRINTF("IACK: irq=%d", irq);
10607702e47cSPaolo Bonzini 
10617702e47cSPaolo Bonzini     if (irq == -1) {
10627702e47cSPaolo Bonzini         /* No more interrupt pending */
10637702e47cSPaolo Bonzini         return opp->spve;
10647702e47cSPaolo Bonzini     }
10657702e47cSPaolo Bonzini 
10667702e47cSPaolo Bonzini     src = &opp->src[irq];
10677702e47cSPaolo Bonzini     if (!(src->ivpr & IVPR_ACTIVITY_MASK) ||
10687702e47cSPaolo Bonzini             !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) {
1069df592270SMichael Davidsaver         error_report("%s: bad raised IRQ %d ctpr %d ivpr 0x%08x",
10707702e47cSPaolo Bonzini                 __func__, irq, dst->ctpr, src->ivpr);
10717702e47cSPaolo Bonzini         openpic_update_irq(opp, irq);
10727702e47cSPaolo Bonzini         retval = opp->spve;
10737702e47cSPaolo Bonzini     } else {
10747702e47cSPaolo Bonzini         /* IRQ enter servicing state */
10757702e47cSPaolo Bonzini         IRQ_setbit(&dst->servicing, irq);
10767702e47cSPaolo Bonzini         retval = IVPR_VECTOR(opp, src->ivpr);
10777702e47cSPaolo Bonzini     }
10787702e47cSPaolo Bonzini 
10797702e47cSPaolo Bonzini     if (!src->level) {
10807702e47cSPaolo Bonzini         /* edge-sensitive IRQ */
10817702e47cSPaolo Bonzini         src->ivpr &= ~IVPR_ACTIVITY_MASK;
10827702e47cSPaolo Bonzini         src->pending = 0;
10837702e47cSPaolo Bonzini         IRQ_resetbit(&dst->raised, irq);
10847702e47cSPaolo Bonzini     }
10857702e47cSPaolo Bonzini 
1086ddd5140bSAaron Larson     /* Timers and IPIs support multicast. */
1087ddd5140bSAaron Larson     if (((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) ||
1088ddd5140bSAaron Larson         ((irq >= opp->irq_tim0) && (irq < (opp->irq_tim0 + OPENPIC_MAX_TMR)))) {
1089df592270SMichael Davidsaver         DPRINTF("irq is IPI or TMR");
10907702e47cSPaolo Bonzini         src->destmask &= ~(1 << cpu);
10917702e47cSPaolo Bonzini         if (src->destmask && !src->level) {
10927702e47cSPaolo Bonzini             /* trigger on CPUs that didn't know about it yet */
10937702e47cSPaolo Bonzini             openpic_set_irq(opp, irq, 1);
10947702e47cSPaolo Bonzini             openpic_set_irq(opp, irq, 0);
10957702e47cSPaolo Bonzini             /* if all CPUs knew about it, set active bit again */
10967702e47cSPaolo Bonzini             src->ivpr |= IVPR_ACTIVITY_MASK;
10977702e47cSPaolo Bonzini         }
10987702e47cSPaolo Bonzini     }
10997702e47cSPaolo Bonzini 
11007702e47cSPaolo Bonzini     return retval;
11017702e47cSPaolo Bonzini }
11027702e47cSPaolo Bonzini 
11037702e47cSPaolo Bonzini static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr,
11047702e47cSPaolo Bonzini                                           int idx)
11057702e47cSPaolo Bonzini {
11067702e47cSPaolo Bonzini     OpenPICState *opp = opaque;
11077702e47cSPaolo Bonzini     IRQDest *dst;
11087702e47cSPaolo Bonzini     uint32_t retval;
11097702e47cSPaolo Bonzini 
1110df592270SMichael Davidsaver     DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx, __func__, idx, addr);
11117702e47cSPaolo Bonzini     retval = 0xFFFFFFFF;
11127702e47cSPaolo Bonzini 
111304d2acbbSFabien Chouteau     if (idx < 0 || idx >= opp->nb_cpus) {
11147702e47cSPaolo Bonzini         return retval;
11157702e47cSPaolo Bonzini     }
11167702e47cSPaolo Bonzini 
11177702e47cSPaolo Bonzini     if (addr & 0xF) {
11187702e47cSPaolo Bonzini         return retval;
11197702e47cSPaolo Bonzini     }
11207702e47cSPaolo Bonzini     dst = &opp->dst[idx];
11217702e47cSPaolo Bonzini     addr &= 0xFF0;
11227702e47cSPaolo Bonzini     switch (addr) {
11237702e47cSPaolo Bonzini     case 0x80: /* CTPR */
11247702e47cSPaolo Bonzini         retval = dst->ctpr;
11257702e47cSPaolo Bonzini         break;
11267702e47cSPaolo Bonzini     case 0x90: /* WHOAMI */
11277702e47cSPaolo Bonzini         retval = idx;
11287702e47cSPaolo Bonzini         break;
11297702e47cSPaolo Bonzini     case 0xA0: /* IACK */
11307702e47cSPaolo Bonzini         retval = openpic_iack(opp, dst, idx);
11317702e47cSPaolo Bonzini         break;
11327702e47cSPaolo Bonzini     case 0xB0: /* EOI */
11337702e47cSPaolo Bonzini         retval = 0;
11347702e47cSPaolo Bonzini         break;
11357702e47cSPaolo Bonzini     default:
11367702e47cSPaolo Bonzini         break;
11377702e47cSPaolo Bonzini     }
1138df592270SMichael Davidsaver     DPRINTF("%s: => 0x%08x", __func__, retval);
11397702e47cSPaolo Bonzini 
11407702e47cSPaolo Bonzini     return retval;
11417702e47cSPaolo Bonzini }
11427702e47cSPaolo Bonzini 
11437702e47cSPaolo Bonzini static uint64_t openpic_cpu_read(void *opaque, hwaddr addr, unsigned len)
11447702e47cSPaolo Bonzini {
11457702e47cSPaolo Bonzini     return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
11467702e47cSPaolo Bonzini }
11477702e47cSPaolo Bonzini 
11487702e47cSPaolo Bonzini static const MemoryRegionOps openpic_glb_ops_le = {
11497702e47cSPaolo Bonzini     .write = openpic_gbl_write,
11507702e47cSPaolo Bonzini     .read  = openpic_gbl_read,
11517702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11527702e47cSPaolo Bonzini     .impl = {
11537702e47cSPaolo Bonzini         .min_access_size = 4,
11547702e47cSPaolo Bonzini         .max_access_size = 4,
11557702e47cSPaolo Bonzini     },
11567702e47cSPaolo Bonzini };
11577702e47cSPaolo Bonzini 
11587702e47cSPaolo Bonzini static const MemoryRegionOps openpic_glb_ops_be = {
11597702e47cSPaolo Bonzini     .write = openpic_gbl_write,
11607702e47cSPaolo Bonzini     .read  = openpic_gbl_read,
11617702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
11627702e47cSPaolo Bonzini     .impl = {
11637702e47cSPaolo Bonzini         .min_access_size = 4,
11647702e47cSPaolo Bonzini         .max_access_size = 4,
11657702e47cSPaolo Bonzini     },
11667702e47cSPaolo Bonzini };
11677702e47cSPaolo Bonzini 
11687702e47cSPaolo Bonzini static const MemoryRegionOps openpic_tmr_ops_le = {
11697702e47cSPaolo Bonzini     .write = openpic_tmr_write,
11707702e47cSPaolo Bonzini     .read  = openpic_tmr_read,
11717702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11727702e47cSPaolo Bonzini     .impl = {
11737702e47cSPaolo Bonzini         .min_access_size = 4,
11747702e47cSPaolo Bonzini         .max_access_size = 4,
11757702e47cSPaolo Bonzini     },
11767702e47cSPaolo Bonzini };
11777702e47cSPaolo Bonzini 
11787702e47cSPaolo Bonzini static const MemoryRegionOps openpic_tmr_ops_be = {
11797702e47cSPaolo Bonzini     .write = openpic_tmr_write,
11807702e47cSPaolo Bonzini     .read  = openpic_tmr_read,
11817702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
11827702e47cSPaolo Bonzini     .impl = {
11837702e47cSPaolo Bonzini         .min_access_size = 4,
11847702e47cSPaolo Bonzini         .max_access_size = 4,
11857702e47cSPaolo Bonzini     },
11867702e47cSPaolo Bonzini };
11877702e47cSPaolo Bonzini 
11887702e47cSPaolo Bonzini static const MemoryRegionOps openpic_cpu_ops_le = {
11897702e47cSPaolo Bonzini     .write = openpic_cpu_write,
11907702e47cSPaolo Bonzini     .read  = openpic_cpu_read,
11917702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
11927702e47cSPaolo Bonzini     .impl = {
11937702e47cSPaolo Bonzini         .min_access_size = 4,
11947702e47cSPaolo Bonzini         .max_access_size = 4,
11957702e47cSPaolo Bonzini     },
11967702e47cSPaolo Bonzini };
11977702e47cSPaolo Bonzini 
11987702e47cSPaolo Bonzini static const MemoryRegionOps openpic_cpu_ops_be = {
11997702e47cSPaolo Bonzini     .write = openpic_cpu_write,
12007702e47cSPaolo Bonzini     .read  = openpic_cpu_read,
12017702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12027702e47cSPaolo Bonzini     .impl = {
12037702e47cSPaolo Bonzini         .min_access_size = 4,
12047702e47cSPaolo Bonzini         .max_access_size = 4,
12057702e47cSPaolo Bonzini     },
12067702e47cSPaolo Bonzini };
12077702e47cSPaolo Bonzini 
12087702e47cSPaolo Bonzini static const MemoryRegionOps openpic_src_ops_le = {
12097702e47cSPaolo Bonzini     .write = openpic_src_write,
12107702e47cSPaolo Bonzini     .read  = openpic_src_read,
12117702e47cSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
12127702e47cSPaolo Bonzini     .impl = {
12137702e47cSPaolo Bonzini         .min_access_size = 4,
12147702e47cSPaolo Bonzini         .max_access_size = 4,
12157702e47cSPaolo Bonzini     },
12167702e47cSPaolo Bonzini };
12177702e47cSPaolo Bonzini 
12187702e47cSPaolo Bonzini static const MemoryRegionOps openpic_src_ops_be = {
12197702e47cSPaolo Bonzini     .write = openpic_src_write,
12207702e47cSPaolo Bonzini     .read  = openpic_src_read,
12217702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12227702e47cSPaolo Bonzini     .impl = {
12237702e47cSPaolo Bonzini         .min_access_size = 4,
12247702e47cSPaolo Bonzini         .max_access_size = 4,
12257702e47cSPaolo Bonzini     },
12267702e47cSPaolo Bonzini };
12277702e47cSPaolo Bonzini 
12287702e47cSPaolo Bonzini static const MemoryRegionOps openpic_msi_ops_be = {
12297702e47cSPaolo Bonzini     .read = openpic_msi_read,
12307702e47cSPaolo Bonzini     .write = openpic_msi_write,
12317702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12327702e47cSPaolo Bonzini     .impl = {
12337702e47cSPaolo Bonzini         .min_access_size = 4,
12347702e47cSPaolo Bonzini         .max_access_size = 4,
12357702e47cSPaolo Bonzini     },
12367702e47cSPaolo Bonzini };
12377702e47cSPaolo Bonzini 
12387702e47cSPaolo Bonzini static const MemoryRegionOps openpic_summary_ops_be = {
12397702e47cSPaolo Bonzini     .read = openpic_summary_read,
12407702e47cSPaolo Bonzini     .write = openpic_summary_write,
12417702e47cSPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
12427702e47cSPaolo Bonzini     .impl = {
12437702e47cSPaolo Bonzini         .min_access_size = 4,
12447702e47cSPaolo Bonzini         .max_access_size = 4,
12457702e47cSPaolo Bonzini     },
12467702e47cSPaolo Bonzini };
12477702e47cSPaolo Bonzini 
12488ebe65f3SPaul Janzen static void openpic_reset(DeviceState *d)
12498ebe65f3SPaul Janzen {
12508ebe65f3SPaul Janzen     OpenPICState *opp = OPENPIC(d);
12518ebe65f3SPaul Janzen     int i;
12528ebe65f3SPaul Janzen 
12538ebe65f3SPaul Janzen     opp->gcr = GCR_RESET;
12548ebe65f3SPaul Janzen     /* Initialise controller registers */
12558ebe65f3SPaul Janzen     opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) |
12568ebe65f3SPaul Janzen                ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) |
12578ebe65f3SPaul Janzen                (opp->vid << FRR_VID_SHIFT);
12588ebe65f3SPaul Janzen 
12598ebe65f3SPaul Janzen     opp->pir = 0;
12608ebe65f3SPaul Janzen     opp->spve = -1 & opp->vector_mask;
12618ebe65f3SPaul Janzen     opp->tfrr = opp->tfrr_reset;
12628ebe65f3SPaul Janzen     /* Initialise IRQ sources */
12638ebe65f3SPaul Janzen     for (i = 0; i < opp->max_irq; i++) {
12648ebe65f3SPaul Janzen         opp->src[i].ivpr = opp->ivpr_reset;
12658ebe65f3SPaul Janzen         switch (opp->src[i].type) {
12668ebe65f3SPaul Janzen         case IRQ_TYPE_NORMAL:
12678ebe65f3SPaul Janzen             opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK);
12688ebe65f3SPaul Janzen             break;
12698ebe65f3SPaul Janzen 
12708ebe65f3SPaul Janzen         case IRQ_TYPE_FSLINT:
12718ebe65f3SPaul Janzen             opp->src[i].ivpr |= IVPR_POLARITY_MASK;
12728ebe65f3SPaul Janzen             break;
12738ebe65f3SPaul Janzen 
12748ebe65f3SPaul Janzen         case IRQ_TYPE_FSLSPECIAL:
12758ebe65f3SPaul Janzen             break;
12768ebe65f3SPaul Janzen         }
1277ffd5e9feSPaul Janzen 
1278ffd5e9feSPaul Janzen         write_IRQreg_idr(opp, i, opp->idr_reset);
12798ebe65f3SPaul Janzen     }
12808ebe65f3SPaul Janzen     /* Initialise IRQ destinations */
12812ada66f9SMark Cave-Ayland     for (i = 0; i < opp->nb_cpus; i++) {
12828ebe65f3SPaul Janzen         opp->dst[i].ctpr      = 15;
12838ebe65f3SPaul Janzen         opp->dst[i].raised.next = -1;
12842ada66f9SMark Cave-Ayland         opp->dst[i].raised.priority = 0;
12852ada66f9SMark Cave-Ayland         bitmap_clear(opp->dst[i].raised.queue, 0, IRQQUEUE_SIZE_BITS);
12868ebe65f3SPaul Janzen         opp->dst[i].servicing.next = -1;
12872ada66f9SMark Cave-Ayland         opp->dst[i].servicing.priority = 0;
12882ada66f9SMark Cave-Ayland         bitmap_clear(opp->dst[i].servicing.queue, 0, IRQQUEUE_SIZE_BITS);
12898ebe65f3SPaul Janzen     }
12908ebe65f3SPaul Janzen     /* Initialise timers */
12918ebe65f3SPaul Janzen     for (i = 0; i < OPENPIC_MAX_TMR; i++) {
12928ebe65f3SPaul Janzen         opp->timers[i].tccr = 0;
12938ebe65f3SPaul Janzen         opp->timers[i].tbcr = TBCR_CI;
1294ddd5140bSAaron Larson         if (opp->timers[i].qemu_timer_active) {
1295ddd5140bSAaron Larson             timer_del(opp->timers[i].qemu_timer);  /* Inhibit timer */
1296ddd5140bSAaron Larson             opp->timers[i].qemu_timer_active = false;
1297ddd5140bSAaron Larson         }
12988ebe65f3SPaul Janzen     }
12998ebe65f3SPaul Janzen     /* Go out of RESET state */
13008ebe65f3SPaul Janzen     opp->gcr = 0;
13018ebe65f3SPaul Janzen }
13028ebe65f3SPaul Janzen 
13037702e47cSPaolo Bonzini typedef struct MemReg {
13047702e47cSPaolo Bonzini     const char             *name;
13057702e47cSPaolo Bonzini     MemoryRegionOps const  *ops;
13067702e47cSPaolo Bonzini     hwaddr      start_addr;
13077702e47cSPaolo Bonzini     ram_addr_t              size;
13087702e47cSPaolo Bonzini } MemReg;
13097702e47cSPaolo Bonzini 
13107702e47cSPaolo Bonzini static void fsl_common_init(OpenPICState *opp)
13117702e47cSPaolo Bonzini {
13127702e47cSPaolo Bonzini     int i;
13138935a442SScott Wood     int virq = OPENPIC_MAX_SRC;
13147702e47cSPaolo Bonzini 
13157702e47cSPaolo Bonzini     opp->vid = VID_REVISION_1_2;
13167702e47cSPaolo Bonzini     opp->vir = VIR_GENERIC;
13177702e47cSPaolo Bonzini     opp->vector_mask = 0xFFFF;
13187702e47cSPaolo Bonzini     opp->tfrr_reset = 0;
13197702e47cSPaolo Bonzini     opp->ivpr_reset = IVPR_MASK_MASK;
13207702e47cSPaolo Bonzini     opp->idr_reset = 1 << 0;
13218935a442SScott Wood     opp->max_irq = OPENPIC_MAX_IRQ;
13227702e47cSPaolo Bonzini 
13237702e47cSPaolo Bonzini     opp->irq_ipi0 = virq;
13248935a442SScott Wood     virq += OPENPIC_MAX_IPI;
13257702e47cSPaolo Bonzini     opp->irq_tim0 = virq;
13268935a442SScott Wood     virq += OPENPIC_MAX_TMR;
13277702e47cSPaolo Bonzini 
13288935a442SScott Wood     assert(virq <= OPENPIC_MAX_IRQ);
13297702e47cSPaolo Bonzini 
13307702e47cSPaolo Bonzini     opp->irq_msi = 224;
13317702e47cSPaolo Bonzini 
1332226419d6SMichael S. Tsirkin     msi_nonbroken = true;
13337702e47cSPaolo Bonzini     for (i = 0; i < opp->fsl->max_ext; i++) {
13347702e47cSPaolo Bonzini         opp->src[i].level = false;
13357702e47cSPaolo Bonzini     }
13367702e47cSPaolo Bonzini 
13377702e47cSPaolo Bonzini     /* Internal interrupts, including message and MSI */
13388935a442SScott Wood     for (i = 16; i < OPENPIC_MAX_SRC; i++) {
13397702e47cSPaolo Bonzini         opp->src[i].type = IRQ_TYPE_FSLINT;
13407702e47cSPaolo Bonzini         opp->src[i].level = true;
13417702e47cSPaolo Bonzini     }
13427702e47cSPaolo Bonzini 
13437702e47cSPaolo Bonzini     /* timers and IPIs */
13448935a442SScott Wood     for (i = OPENPIC_MAX_SRC; i < virq; i++) {
13457702e47cSPaolo Bonzini         opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
13467702e47cSPaolo Bonzini         opp->src[i].level = false;
13477702e47cSPaolo Bonzini     }
1348ddd5140bSAaron Larson 
1349ddd5140bSAaron Larson     for (i = 0; i < OPENPIC_MAX_TMR; i++) {
1350ddd5140bSAaron Larson         opp->timers[i].n_IRQ = opp->irq_tim0 + i;
1351ddd5140bSAaron Larson         opp->timers[i].qemu_timer_active = false;
1352ddd5140bSAaron Larson         opp->timers[i].qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
1353ddd5140bSAaron Larson                                                  &qemu_timer_cb,
1354ddd5140bSAaron Larson                                                  &opp->timers[i]);
1355ddd5140bSAaron Larson         opp->timers[i].opp = opp;
1356ddd5140bSAaron Larson     }
13577702e47cSPaolo Bonzini }
13587702e47cSPaolo Bonzini 
13597702e47cSPaolo Bonzini static void map_list(OpenPICState *opp, const MemReg *list, int *count)
13607702e47cSPaolo Bonzini {
13617702e47cSPaolo Bonzini     while (list->name) {
13627702e47cSPaolo Bonzini         assert(*count < ARRAY_SIZE(opp->sub_io_mem));
13637702e47cSPaolo Bonzini 
13641437c94bSPaolo Bonzini         memory_region_init_io(&opp->sub_io_mem[*count], OBJECT(opp), list->ops,
13651437c94bSPaolo Bonzini                               opp, list->name, list->size);
13667702e47cSPaolo Bonzini 
13677702e47cSPaolo Bonzini         memory_region_add_subregion(&opp->mem, list->start_addr,
13687702e47cSPaolo Bonzini                                     &opp->sub_io_mem[*count]);
13697702e47cSPaolo Bonzini 
13707702e47cSPaolo Bonzini         (*count)++;
13717702e47cSPaolo Bonzini         list++;
13727702e47cSPaolo Bonzini     }
13737702e47cSPaolo Bonzini }
13747702e47cSPaolo Bonzini 
1375e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irq_queue = {
1376e5f6e732SMark Cave-Ayland     .name = "openpic_irq_queue",
1377e5f6e732SMark Cave-Ayland     .version_id = 0,
1378e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1379e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1380e5f6e732SMark Cave-Ayland         VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size),
1381e5f6e732SMark Cave-Ayland         VMSTATE_INT32(next, IRQQueue),
1382e5f6e732SMark Cave-Ayland         VMSTATE_INT32(priority, IRQQueue),
1383e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1384e5f6e732SMark Cave-Ayland     }
1385e5f6e732SMark Cave-Ayland };
1386e5f6e732SMark Cave-Ayland 
1387e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irqdest = {
1388e5f6e732SMark Cave-Ayland     .name = "openpic_irqdest",
1389e5f6e732SMark Cave-Ayland     .version_id = 0,
1390e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1391e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1392e5f6e732SMark Cave-Ayland         VMSTATE_INT32(ctpr, IRQDest),
1393e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue,
1394e5f6e732SMark Cave-Ayland                        IRQQueue),
1395e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT(servicing, IRQDest, 0, vmstate_openpic_irq_queue,
1396e5f6e732SMark Cave-Ayland                        IRQQueue),
1397e5f6e732SMark Cave-Ayland         VMSTATE_UINT32_ARRAY(outputs_active, IRQDest, OPENPIC_OUTPUT_NB),
1398e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1399e5f6e732SMark Cave-Ayland     }
1400e5f6e732SMark Cave-Ayland };
1401e5f6e732SMark Cave-Ayland 
1402e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_irqsource = {
1403e5f6e732SMark Cave-Ayland     .name = "openpic_irqsource",
1404e5f6e732SMark Cave-Ayland     .version_id = 0,
1405e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1406e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1407e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(ivpr, IRQSource),
1408e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(idr, IRQSource),
1409e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(destmask, IRQSource),
1410e5f6e732SMark Cave-Ayland         VMSTATE_INT32(last_cpu, IRQSource),
1411e5f6e732SMark Cave-Ayland         VMSTATE_INT32(pending, IRQSource),
1412e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1413e5f6e732SMark Cave-Ayland     }
1414e5f6e732SMark Cave-Ayland };
1415e5f6e732SMark Cave-Ayland 
1416e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_timer = {
1417e5f6e732SMark Cave-Ayland     .name = "openpic_timer",
1418e5f6e732SMark Cave-Ayland     .version_id = 0,
1419e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1420e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1421e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tccr, OpenPICTimer),
1422e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tbcr, OpenPICTimer),
1423e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1424e5f6e732SMark Cave-Ayland     }
1425e5f6e732SMark Cave-Ayland };
1426e5f6e732SMark Cave-Ayland 
1427e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic_msi = {
1428e5f6e732SMark Cave-Ayland     .name = "openpic_msi",
1429e5f6e732SMark Cave-Ayland     .version_id = 0,
1430e5f6e732SMark Cave-Ayland     .minimum_version_id = 0,
1431e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1432e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(msir, OpenPICMSI),
1433e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1434e5f6e732SMark Cave-Ayland     }
1435e5f6e732SMark Cave-Ayland };
1436e5f6e732SMark Cave-Ayland 
1437e5f6e732SMark Cave-Ayland static int openpic_post_load(void *opaque, int version_id)
1438e5f6e732SMark Cave-Ayland {
1439e5f6e732SMark Cave-Ayland     OpenPICState *opp = (OpenPICState *)opaque;
1440e5f6e732SMark Cave-Ayland     int i;
1441e5f6e732SMark Cave-Ayland 
1442e5f6e732SMark Cave-Ayland     /* Update internal ivpr and idr variables */
1443e5f6e732SMark Cave-Ayland     for (i = 0; i < opp->max_irq; i++) {
1444e5f6e732SMark Cave-Ayland         write_IRQreg_idr(opp, i, opp->src[i].idr);
1445e5f6e732SMark Cave-Ayland         write_IRQreg_ivpr(opp, i, opp->src[i].ivpr);
1446e5f6e732SMark Cave-Ayland     }
1447e5f6e732SMark Cave-Ayland 
1448e5f6e732SMark Cave-Ayland     return 0;
1449e5f6e732SMark Cave-Ayland }
1450e5f6e732SMark Cave-Ayland 
1451e5f6e732SMark Cave-Ayland static const VMStateDescription vmstate_openpic = {
1452e5f6e732SMark Cave-Ayland     .name = "openpic",
1453e5f6e732SMark Cave-Ayland     .version_id = 3,
1454e5f6e732SMark Cave-Ayland     .minimum_version_id = 3,
1455e5f6e732SMark Cave-Ayland     .post_load = openpic_post_load,
1456e5f6e732SMark Cave-Ayland     .fields = (VMStateField[]) {
1457e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(gcr, OpenPICState),
1458e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(vir, OpenPICState),
1459e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(pir, OpenPICState),
1460e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(spve, OpenPICState),
1461e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(tfrr, OpenPICState),
1462e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(max_irq, OpenPICState),
1463e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0,
1464e5f6e732SMark Cave-Ayland                                      vmstate_openpic_irqsource, IRQSource),
1465d2164ad3SHalil Pasic         VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState, NULL),
1466e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0,
1467e5f6e732SMark Cave-Ayland                                      vmstate_openpic_irqdest, IRQDest),
1468e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0,
1469e5f6e732SMark Cave-Ayland                              vmstate_openpic_timer, OpenPICTimer),
1470e5f6e732SMark Cave-Ayland         VMSTATE_STRUCT_ARRAY(msi, OpenPICState, MAX_MSI, 0,
1471e5f6e732SMark Cave-Ayland                              vmstate_openpic_msi, OpenPICMSI),
1472e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_ipi0, OpenPICState),
1473e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_tim0, OpenPICState),
1474e5f6e732SMark Cave-Ayland         VMSTATE_UINT32(irq_msi, OpenPICState),
1475e5f6e732SMark Cave-Ayland         VMSTATE_END_OF_LIST()
1476e5f6e732SMark Cave-Ayland     }
1477e5f6e732SMark Cave-Ayland };
1478e5f6e732SMark Cave-Ayland 
1479cbe72019SAndreas Färber static void openpic_init(Object *obj)
14807702e47cSPaolo Bonzini {
1481cbe72019SAndreas Färber     OpenPICState *opp = OPENPIC(obj);
1482cbe72019SAndreas Färber 
14831437c94bSPaolo Bonzini     memory_region_init(&opp->mem, obj, "openpic", 0x40000);
1484cbe72019SAndreas Färber }
1485cbe72019SAndreas Färber 
1486cbe72019SAndreas Färber static void openpic_realize(DeviceState *dev, Error **errp)
1487cbe72019SAndreas Färber {
1488cbe72019SAndreas Färber     SysBusDevice *d = SYS_BUS_DEVICE(dev);
1489e1766344SAndreas Färber     OpenPICState *opp = OPENPIC(dev);
14907702e47cSPaolo Bonzini     int i, j;
14917702e47cSPaolo Bonzini     int list_count = 0;
14927702e47cSPaolo Bonzini     static const MemReg list_le[] = {
14937702e47cSPaolo Bonzini         {"glb", &openpic_glb_ops_le,
14947702e47cSPaolo Bonzini                 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
14957702e47cSPaolo Bonzini         {"tmr", &openpic_tmr_ops_le,
14967702e47cSPaolo Bonzini                 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
14977702e47cSPaolo Bonzini         {"src", &openpic_src_ops_le,
14987702e47cSPaolo Bonzini                 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
14997702e47cSPaolo Bonzini         {"cpu", &openpic_cpu_ops_le,
15007702e47cSPaolo Bonzini                 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
15017702e47cSPaolo Bonzini         {NULL}
15027702e47cSPaolo Bonzini     };
15037702e47cSPaolo Bonzini     static const MemReg list_be[] = {
15047702e47cSPaolo Bonzini         {"glb", &openpic_glb_ops_be,
15057702e47cSPaolo Bonzini                 OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
15067702e47cSPaolo Bonzini         {"tmr", &openpic_tmr_ops_be,
15077702e47cSPaolo Bonzini                 OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
15087702e47cSPaolo Bonzini         {"src", &openpic_src_ops_be,
15097702e47cSPaolo Bonzini                 OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
15107702e47cSPaolo Bonzini         {"cpu", &openpic_cpu_ops_be,
15117702e47cSPaolo Bonzini                 OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
15127702e47cSPaolo Bonzini         {NULL}
15137702e47cSPaolo Bonzini     };
15147702e47cSPaolo Bonzini     static const MemReg list_fsl[] = {
15157702e47cSPaolo Bonzini         {"msi", &openpic_msi_ops_be,
15167702e47cSPaolo Bonzini                 OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
15177702e47cSPaolo Bonzini         {"summary", &openpic_summary_ops_be,
15187702e47cSPaolo Bonzini                 OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE},
15197702e47cSPaolo Bonzini         {NULL}
15207702e47cSPaolo Bonzini     };
15217702e47cSPaolo Bonzini 
152273d963c0SMichael Roth     if (opp->nb_cpus > MAX_CPU) {
1523c6bd8c70SMarkus Armbruster         error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
152473d963c0SMichael Roth                    TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus,
152573d963c0SMichael Roth                    (uint64_t)0, (uint64_t)MAX_CPU);
152673d963c0SMichael Roth         return;
152773d963c0SMichael Roth     }
152873d963c0SMichael Roth 
15297702e47cSPaolo Bonzini     switch (opp->model) {
15307702e47cSPaolo Bonzini     case OPENPIC_MODEL_FSL_MPIC_20:
15317702e47cSPaolo Bonzini     default:
15327702e47cSPaolo Bonzini         opp->fsl = &fsl_mpic_20;
15337702e47cSPaolo Bonzini         opp->brr1 = 0x00400200;
15347702e47cSPaolo Bonzini         opp->flags |= OPENPIC_FLAG_IDR_CRIT;
15357702e47cSPaolo Bonzini         opp->nb_irqs = 80;
15367702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_MIXED;
15377702e47cSPaolo Bonzini 
15387702e47cSPaolo Bonzini         fsl_common_init(opp);
15397702e47cSPaolo Bonzini         map_list(opp, list_be, &list_count);
15407702e47cSPaolo Bonzini         map_list(opp, list_fsl, &list_count);
15417702e47cSPaolo Bonzini 
15427702e47cSPaolo Bonzini         break;
15437702e47cSPaolo Bonzini 
15447702e47cSPaolo Bonzini     case OPENPIC_MODEL_FSL_MPIC_42:
15457702e47cSPaolo Bonzini         opp->fsl = &fsl_mpic_42;
15467702e47cSPaolo Bonzini         opp->brr1 = 0x00400402;
15477702e47cSPaolo Bonzini         opp->flags |= OPENPIC_FLAG_ILR;
15487702e47cSPaolo Bonzini         opp->nb_irqs = 196;
15497702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_PROXY;
15507702e47cSPaolo Bonzini 
15517702e47cSPaolo Bonzini         fsl_common_init(opp);
15527702e47cSPaolo Bonzini         map_list(opp, list_be, &list_count);
15537702e47cSPaolo Bonzini         map_list(opp, list_fsl, &list_count);
15547702e47cSPaolo Bonzini 
15557702e47cSPaolo Bonzini         break;
15567702e47cSPaolo Bonzini 
15577702e47cSPaolo Bonzini     case OPENPIC_MODEL_RAVEN:
15587702e47cSPaolo Bonzini         opp->nb_irqs = RAVEN_MAX_EXT;
15597702e47cSPaolo Bonzini         opp->vid = VID_REVISION_1_3;
15607702e47cSPaolo Bonzini         opp->vir = VIR_GENERIC;
15617702e47cSPaolo Bonzini         opp->vector_mask = 0xFF;
15627702e47cSPaolo Bonzini         opp->tfrr_reset = 4160000;
15637702e47cSPaolo Bonzini         opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK;
15647702e47cSPaolo Bonzini         opp->idr_reset = 0;
15657702e47cSPaolo Bonzini         opp->max_irq = RAVEN_MAX_IRQ;
15667702e47cSPaolo Bonzini         opp->irq_ipi0 = RAVEN_IPI_IRQ;
15677702e47cSPaolo Bonzini         opp->irq_tim0 = RAVEN_TMR_IRQ;
15687702e47cSPaolo Bonzini         opp->brr1 = -1;
15697702e47cSPaolo Bonzini         opp->mpic_mode_mask = GCR_MODE_MIXED;
15707702e47cSPaolo Bonzini 
15717702e47cSPaolo Bonzini         if (opp->nb_cpus != 1) {
1572cbe72019SAndreas Färber             error_setg(errp, "Only UP supported today");
1573cbe72019SAndreas Färber             return;
15747702e47cSPaolo Bonzini         }
15757702e47cSPaolo Bonzini 
15767702e47cSPaolo Bonzini         map_list(opp, list_le, &list_count);
15777702e47cSPaolo Bonzini         break;
157858b62835SBenjamin Herrenschmidt 
157958b62835SBenjamin Herrenschmidt     case OPENPIC_MODEL_KEYLARGO:
158058b62835SBenjamin Herrenschmidt         opp->nb_irqs = KEYLARGO_MAX_EXT;
158158b62835SBenjamin Herrenschmidt         opp->vid = VID_REVISION_1_2;
158258b62835SBenjamin Herrenschmidt         opp->vir = VIR_GENERIC;
158358b62835SBenjamin Herrenschmidt         opp->vector_mask = 0xFF;
158458b62835SBenjamin Herrenschmidt         opp->tfrr_reset = 4160000;
158558b62835SBenjamin Herrenschmidt         opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK;
158658b62835SBenjamin Herrenschmidt         opp->idr_reset = 0;
158758b62835SBenjamin Herrenschmidt         opp->max_irq = KEYLARGO_MAX_IRQ;
158858b62835SBenjamin Herrenschmidt         opp->irq_ipi0 = KEYLARGO_IPI_IRQ;
158958b62835SBenjamin Herrenschmidt         opp->irq_tim0 = KEYLARGO_TMR_IRQ;
159058b62835SBenjamin Herrenschmidt         opp->brr1 = -1;
159158b62835SBenjamin Herrenschmidt         opp->mpic_mode_mask = GCR_MODE_MIXED;
159258b62835SBenjamin Herrenschmidt 
159358b62835SBenjamin Herrenschmidt         if (opp->nb_cpus != 1) {
159458b62835SBenjamin Herrenschmidt             error_setg(errp, "Only UP supported today");
159558b62835SBenjamin Herrenschmidt             return;
159658b62835SBenjamin Herrenschmidt         }
159758b62835SBenjamin Herrenschmidt 
159858b62835SBenjamin Herrenschmidt         map_list(opp, list_le, &list_count);
159958b62835SBenjamin Herrenschmidt         break;
16007702e47cSPaolo Bonzini     }
16017702e47cSPaolo Bonzini 
16027702e47cSPaolo Bonzini     for (i = 0; i < opp->nb_cpus; i++) {
1603aa2ac1daSPeter Crosthwaite         opp->dst[i].irqs = g_new0(qemu_irq, OPENPIC_OUTPUT_NB);
16047702e47cSPaolo Bonzini         for (j = 0; j < OPENPIC_OUTPUT_NB; j++) {
1605cbe72019SAndreas Färber             sysbus_init_irq(d, &opp->dst[i].irqs[j]);
16067702e47cSPaolo Bonzini         }
16072ada66f9SMark Cave-Ayland 
1608e5f6e732SMark Cave-Ayland         opp->dst[i].raised.queue_size = IRQQUEUE_SIZE_BITS;
16092ada66f9SMark Cave-Ayland         opp->dst[i].raised.queue = bitmap_new(IRQQUEUE_SIZE_BITS);
1610e5f6e732SMark Cave-Ayland         opp->dst[i].servicing.queue_size = IRQQUEUE_SIZE_BITS;
16112ada66f9SMark Cave-Ayland         opp->dst[i].servicing.queue = bitmap_new(IRQQUEUE_SIZE_BITS);
16127702e47cSPaolo Bonzini     }
16137702e47cSPaolo Bonzini 
1614cbe72019SAndreas Färber     sysbus_init_mmio(d, &opp->mem);
1615cbe72019SAndreas Färber     qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq);
16167702e47cSPaolo Bonzini }
16177702e47cSPaolo Bonzini 
16187702e47cSPaolo Bonzini static Property openpic_properties[] = {
16197702e47cSPaolo Bonzini     DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20),
16207702e47cSPaolo Bonzini     DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1),
16217702e47cSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
16227702e47cSPaolo Bonzini };
16237702e47cSPaolo Bonzini 
1624cbe72019SAndreas Färber static void openpic_class_init(ObjectClass *oc, void *data)
16257702e47cSPaolo Bonzini {
1626cbe72019SAndreas Färber     DeviceClass *dc = DEVICE_CLASS(oc);
16277702e47cSPaolo Bonzini 
1628cbe72019SAndreas Färber     dc->realize = openpic_realize;
16297702e47cSPaolo Bonzini     dc->props = openpic_properties;
16307702e47cSPaolo Bonzini     dc->reset = openpic_reset;
1631e5f6e732SMark Cave-Ayland     dc->vmsd = &vmstate_openpic;
163229f8dd66SLaurent Vivier     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
16337702e47cSPaolo Bonzini }
16347702e47cSPaolo Bonzini 
16357702e47cSPaolo Bonzini static const TypeInfo openpic_info = {
1636e1766344SAndreas Färber     .name          = TYPE_OPENPIC,
16377702e47cSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
16387702e47cSPaolo Bonzini     .instance_size = sizeof(OpenPICState),
1639cbe72019SAndreas Färber     .instance_init = openpic_init,
16407702e47cSPaolo Bonzini     .class_init    = openpic_class_init,
16417702e47cSPaolo Bonzini };
16427702e47cSPaolo Bonzini 
16437702e47cSPaolo Bonzini static void openpic_register_types(void)
16447702e47cSPaolo Bonzini {
16457702e47cSPaolo Bonzini     type_register_static(&openpic_info);
16467702e47cSPaolo Bonzini }
16477702e47cSPaolo Bonzini 
16487702e47cSPaolo Bonzini type_init(openpic_register_types)
1649