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