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