xref: /openbmc/qemu/hw/intc/slavio_intctl.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
17702e47cSPaolo Bonzini /*
27702e47cSPaolo Bonzini  * QEMU Sparc SLAVIO interrupt controller emulation
37702e47cSPaolo Bonzini  *
47702e47cSPaolo Bonzini  * Copyright (c) 2003-2005 Fabrice Bellard
57702e47cSPaolo Bonzini  *
67702e47cSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
77702e47cSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
87702e47cSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
97702e47cSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
107702e47cSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
117702e47cSPaolo Bonzini  * furnished to do so, subject to the following conditions:
127702e47cSPaolo Bonzini  *
137702e47cSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
147702e47cSPaolo Bonzini  * all copies or substantial portions of the Software.
157702e47cSPaolo Bonzini  *
167702e47cSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177702e47cSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187702e47cSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
197702e47cSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
207702e47cSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
217702e47cSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
227702e47cSPaolo Bonzini  * THE SOFTWARE.
237702e47cSPaolo Bonzini  */
247702e47cSPaolo Bonzini 
2590191d07SPeter Maydell #include "qemu/osdep.h"
26d6454270SMarkus Armbruster #include "migration/vmstate.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
287702e47cSPaolo Bonzini #include "hw/sysbus.h"
29148fbe95SHervé Poussineau #include "hw/intc/intc.h"
3064552b6bSMarkus Armbruster #include "hw/irq.h"
317702e47cSPaolo Bonzini #include "trace.h"
32db1015e9SEduardo Habkost #include "qom/object.h"
337702e47cSPaolo Bonzini 
347702e47cSPaolo Bonzini //#define DEBUG_IRQ_COUNT
357702e47cSPaolo Bonzini 
367702e47cSPaolo Bonzini /*
377702e47cSPaolo Bonzini  * Registers of interrupt controller in sun4m.
387702e47cSPaolo Bonzini  *
397702e47cSPaolo Bonzini  * This is the interrupt controller part of chip STP2001 (Slave I/O), also
407702e47cSPaolo Bonzini  * produced as NCR89C105. See
417702e47cSPaolo Bonzini  * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
427702e47cSPaolo Bonzini  *
437702e47cSPaolo Bonzini  * There is a system master controller and one for each cpu.
447702e47cSPaolo Bonzini  *
457702e47cSPaolo Bonzini  */
467702e47cSPaolo Bonzini 
477702e47cSPaolo Bonzini #define MAX_CPUS 16
487702e47cSPaolo Bonzini #define MAX_PILS 16
497702e47cSPaolo Bonzini 
507702e47cSPaolo Bonzini struct SLAVIO_INTCTLState;
517702e47cSPaolo Bonzini 
527702e47cSPaolo Bonzini typedef struct SLAVIO_CPUINTCTLState {
537702e47cSPaolo Bonzini     MemoryRegion iomem;
547702e47cSPaolo Bonzini     struct SLAVIO_INTCTLState *master;
557702e47cSPaolo Bonzini     uint32_t intreg_pending;
567702e47cSPaolo Bonzini     uint32_t cpu;
577702e47cSPaolo Bonzini     uint32_t irl_out;
587702e47cSPaolo Bonzini } SLAVIO_CPUINTCTLState;
597702e47cSPaolo Bonzini 
607abad863SAndreas Färber #define TYPE_SLAVIO_INTCTL "slavio_intctl"
618063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(SLAVIO_INTCTLState, SLAVIO_INTCTL)
627abad863SAndreas Färber 
63db1015e9SEduardo Habkost struct SLAVIO_INTCTLState {
647abad863SAndreas Färber     SysBusDevice parent_obj;
657abad863SAndreas Färber 
667702e47cSPaolo Bonzini     MemoryRegion iomem;
677702e47cSPaolo Bonzini #ifdef DEBUG_IRQ_COUNT
687702e47cSPaolo Bonzini     uint64_t irq_count[32];
697702e47cSPaolo Bonzini #endif
707702e47cSPaolo Bonzini     qemu_irq cpu_irqs[MAX_CPUS][MAX_PILS];
717702e47cSPaolo Bonzini     SLAVIO_CPUINTCTLState slaves[MAX_CPUS];
727702e47cSPaolo Bonzini     uint32_t intregm_pending;
737702e47cSPaolo Bonzini     uint32_t intregm_disabled;
747702e47cSPaolo Bonzini     uint32_t target_cpu;
75db1015e9SEduardo Habkost };
767702e47cSPaolo Bonzini 
777702e47cSPaolo Bonzini #define INTCTL_MAXADDR 0xf
787702e47cSPaolo Bonzini #define INTCTL_SIZE (INTCTL_MAXADDR + 1)
797702e47cSPaolo Bonzini #define INTCTLM_SIZE 0x14
807702e47cSPaolo Bonzini #define MASTER_IRQ_MASK ~0x0fa2007f
817702e47cSPaolo Bonzini #define MASTER_DISABLE 0x80000000
827702e47cSPaolo Bonzini #define CPU_SOFTIRQ_MASK 0xfffe0000
837702e47cSPaolo Bonzini #define CPU_IRQ_INT15_IN (1 << 15)
847702e47cSPaolo Bonzini #define CPU_IRQ_TIMER_IN (1 << 14)
857702e47cSPaolo Bonzini 
867702e47cSPaolo Bonzini static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs);
877702e47cSPaolo Bonzini 
887702e47cSPaolo Bonzini // per-cpu interrupt controller
slavio_intctl_mem_readl(void * opaque,hwaddr addr,unsigned size)897702e47cSPaolo Bonzini static uint64_t slavio_intctl_mem_readl(void *opaque, hwaddr addr,
907702e47cSPaolo Bonzini                                         unsigned size)
917702e47cSPaolo Bonzini {
927702e47cSPaolo Bonzini     SLAVIO_CPUINTCTLState *s = opaque;
937702e47cSPaolo Bonzini     uint32_t saddr, ret;
947702e47cSPaolo Bonzini 
957702e47cSPaolo Bonzini     saddr = addr >> 2;
967702e47cSPaolo Bonzini     switch (saddr) {
977702e47cSPaolo Bonzini     case 0:
987702e47cSPaolo Bonzini         ret = s->intreg_pending;
997702e47cSPaolo Bonzini         break;
1007702e47cSPaolo Bonzini     default:
1017702e47cSPaolo Bonzini         ret = 0;
1027702e47cSPaolo Bonzini         break;
1037702e47cSPaolo Bonzini     }
1047702e47cSPaolo Bonzini     trace_slavio_intctl_mem_readl(s->cpu, addr, ret);
1057702e47cSPaolo Bonzini 
1067702e47cSPaolo Bonzini     return ret;
1077702e47cSPaolo Bonzini }
1087702e47cSPaolo Bonzini 
slavio_intctl_mem_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size)1097702e47cSPaolo Bonzini static void slavio_intctl_mem_writel(void *opaque, hwaddr addr,
1107702e47cSPaolo Bonzini                                      uint64_t val, unsigned size)
1117702e47cSPaolo Bonzini {
1127702e47cSPaolo Bonzini     SLAVIO_CPUINTCTLState *s = opaque;
1137702e47cSPaolo Bonzini     uint32_t saddr;
1147702e47cSPaolo Bonzini 
1157702e47cSPaolo Bonzini     saddr = addr >> 2;
1167702e47cSPaolo Bonzini     trace_slavio_intctl_mem_writel(s->cpu, addr, val);
1177702e47cSPaolo Bonzini     switch (saddr) {
1187702e47cSPaolo Bonzini     case 1: // clear pending softints
1197702e47cSPaolo Bonzini         val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN;
1207702e47cSPaolo Bonzini         s->intreg_pending &= ~val;
1217702e47cSPaolo Bonzini         slavio_check_interrupts(s->master, 1);
1227702e47cSPaolo Bonzini         trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending);
1237702e47cSPaolo Bonzini         break;
1247702e47cSPaolo Bonzini     case 2: // set softint
1257702e47cSPaolo Bonzini         val &= CPU_SOFTIRQ_MASK;
1267702e47cSPaolo Bonzini         s->intreg_pending |= val;
1277702e47cSPaolo Bonzini         slavio_check_interrupts(s->master, 1);
1287702e47cSPaolo Bonzini         trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending);
1297702e47cSPaolo Bonzini         break;
1307702e47cSPaolo Bonzini     default:
1317702e47cSPaolo Bonzini         break;
1327702e47cSPaolo Bonzini     }
1337702e47cSPaolo Bonzini }
1347702e47cSPaolo Bonzini 
1357702e47cSPaolo Bonzini static const MemoryRegionOps slavio_intctl_mem_ops = {
1367702e47cSPaolo Bonzini     .read = slavio_intctl_mem_readl,
1377702e47cSPaolo Bonzini     .write = slavio_intctl_mem_writel,
1387702e47cSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
1397702e47cSPaolo Bonzini     .valid = {
1407702e47cSPaolo Bonzini         .min_access_size = 4,
1417702e47cSPaolo Bonzini         .max_access_size = 4,
1427702e47cSPaolo Bonzini     },
1437702e47cSPaolo Bonzini };
1447702e47cSPaolo Bonzini 
1457702e47cSPaolo Bonzini // master system interrupt controller
slavio_intctlm_mem_readl(void * opaque,hwaddr addr,unsigned size)1467702e47cSPaolo Bonzini static uint64_t slavio_intctlm_mem_readl(void *opaque, hwaddr addr,
1477702e47cSPaolo Bonzini                                          unsigned size)
1487702e47cSPaolo Bonzini {
1497702e47cSPaolo Bonzini     SLAVIO_INTCTLState *s = opaque;
1507702e47cSPaolo Bonzini     uint32_t saddr, ret;
1517702e47cSPaolo Bonzini 
1527702e47cSPaolo Bonzini     saddr = addr >> 2;
1537702e47cSPaolo Bonzini     switch (saddr) {
1547702e47cSPaolo Bonzini     case 0:
1557702e47cSPaolo Bonzini         ret = s->intregm_pending & ~MASTER_DISABLE;
1567702e47cSPaolo Bonzini         break;
1577702e47cSPaolo Bonzini     case 1:
1587702e47cSPaolo Bonzini         ret = s->intregm_disabled & MASTER_IRQ_MASK;
1597702e47cSPaolo Bonzini         break;
1607702e47cSPaolo Bonzini     case 4:
1617702e47cSPaolo Bonzini         ret = s->target_cpu;
1627702e47cSPaolo Bonzini         break;
1637702e47cSPaolo Bonzini     default:
1647702e47cSPaolo Bonzini         ret = 0;
1657702e47cSPaolo Bonzini         break;
1667702e47cSPaolo Bonzini     }
1677702e47cSPaolo Bonzini     trace_slavio_intctlm_mem_readl(addr, ret);
1687702e47cSPaolo Bonzini 
1697702e47cSPaolo Bonzini     return ret;
1707702e47cSPaolo Bonzini }
1717702e47cSPaolo Bonzini 
slavio_intctlm_mem_writel(void * opaque,hwaddr addr,uint64_t val,unsigned size)1727702e47cSPaolo Bonzini static void slavio_intctlm_mem_writel(void *opaque, hwaddr addr,
1737702e47cSPaolo Bonzini                                       uint64_t val, unsigned size)
1747702e47cSPaolo Bonzini {
1757702e47cSPaolo Bonzini     SLAVIO_INTCTLState *s = opaque;
1767702e47cSPaolo Bonzini     uint32_t saddr;
1777702e47cSPaolo Bonzini 
1787702e47cSPaolo Bonzini     saddr = addr >> 2;
1797702e47cSPaolo Bonzini     trace_slavio_intctlm_mem_writel(addr, val);
1807702e47cSPaolo Bonzini     switch (saddr) {
1817702e47cSPaolo Bonzini     case 2: // clear (enable)
1827702e47cSPaolo Bonzini         // Force clear unused bits
1837702e47cSPaolo Bonzini         val &= MASTER_IRQ_MASK;
1847702e47cSPaolo Bonzini         s->intregm_disabled &= ~val;
1857702e47cSPaolo Bonzini         trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled);
1867702e47cSPaolo Bonzini         slavio_check_interrupts(s, 1);
1877702e47cSPaolo Bonzini         break;
1887702e47cSPaolo Bonzini     case 3: // set (disable; doesn't affect pending)
1897702e47cSPaolo Bonzini         // Force clear unused bits
1907702e47cSPaolo Bonzini         val &= MASTER_IRQ_MASK;
1917702e47cSPaolo Bonzini         s->intregm_disabled |= val;
1927702e47cSPaolo Bonzini         slavio_check_interrupts(s, 1);
1937702e47cSPaolo Bonzini         trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled);
1947702e47cSPaolo Bonzini         break;
1957702e47cSPaolo Bonzini     case 4:
1967702e47cSPaolo Bonzini         s->target_cpu = val & (MAX_CPUS - 1);
1977702e47cSPaolo Bonzini         slavio_check_interrupts(s, 1);
1987702e47cSPaolo Bonzini         trace_slavio_intctlm_mem_writel_target(s->target_cpu);
1997702e47cSPaolo Bonzini         break;
2007702e47cSPaolo Bonzini     default:
2017702e47cSPaolo Bonzini         break;
2027702e47cSPaolo Bonzini     }
2037702e47cSPaolo Bonzini }
2047702e47cSPaolo Bonzini 
2057702e47cSPaolo Bonzini static const MemoryRegionOps slavio_intctlm_mem_ops = {
2067702e47cSPaolo Bonzini     .read = slavio_intctlm_mem_readl,
2077702e47cSPaolo Bonzini     .write = slavio_intctlm_mem_writel,
2087702e47cSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
2097702e47cSPaolo Bonzini     .valid = {
2107702e47cSPaolo Bonzini         .min_access_size = 4,
2117702e47cSPaolo Bonzini         .max_access_size = 4,
2127702e47cSPaolo Bonzini     },
2137702e47cSPaolo Bonzini };
2147702e47cSPaolo Bonzini 
2157702e47cSPaolo Bonzini static const uint32_t intbit_to_level[] = {
2167702e47cSPaolo Bonzini     2, 3, 5, 7, 9, 11, 13, 2,   3, 5, 7, 9, 11, 13, 12, 12,
2177702e47cSPaolo Bonzini     6, 13, 4, 10, 8, 9, 11, 0,  0, 0, 0, 15, 15, 15, 15, 0,
2187702e47cSPaolo Bonzini };
2197702e47cSPaolo Bonzini 
slavio_check_interrupts(SLAVIO_INTCTLState * s,int set_irqs)2207702e47cSPaolo Bonzini static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs)
2217702e47cSPaolo Bonzini {
2227702e47cSPaolo Bonzini     uint32_t pending = s->intregm_pending, pil_pending;
2237702e47cSPaolo Bonzini     unsigned int i, j;
2247702e47cSPaolo Bonzini 
2257702e47cSPaolo Bonzini     pending &= ~s->intregm_disabled;
2267702e47cSPaolo Bonzini 
2277702e47cSPaolo Bonzini     trace_slavio_check_interrupts(pending, s->intregm_disabled);
2287702e47cSPaolo Bonzini     for (i = 0; i < MAX_CPUS; i++) {
2297702e47cSPaolo Bonzini         pil_pending = 0;
2307702e47cSPaolo Bonzini 
2317702e47cSPaolo Bonzini         /* If we are the current interrupt target, get hard interrupts */
2327702e47cSPaolo Bonzini         if (pending && !(s->intregm_disabled & MASTER_DISABLE) &&
2337702e47cSPaolo Bonzini             (i == s->target_cpu)) {
2347702e47cSPaolo Bonzini             for (j = 0; j < 32; j++) {
2357702e47cSPaolo Bonzini                 if ((pending & (1 << j)) && intbit_to_level[j]) {
2367702e47cSPaolo Bonzini                     pil_pending |= 1 << intbit_to_level[j];
2377702e47cSPaolo Bonzini                 }
2387702e47cSPaolo Bonzini             }
2397702e47cSPaolo Bonzini         }
2407702e47cSPaolo Bonzini 
2417702e47cSPaolo Bonzini         /* Calculate current pending hard interrupts for display */
2427702e47cSPaolo Bonzini         s->slaves[i].intreg_pending &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN |
2437702e47cSPaolo Bonzini             CPU_IRQ_TIMER_IN;
2447702e47cSPaolo Bonzini         if (i == s->target_cpu) {
2457702e47cSPaolo Bonzini             for (j = 0; j < 32; j++) {
2467d45e784SPeter Maydell                 if ((s->intregm_pending & (1U << j)) && intbit_to_level[j]) {
2477702e47cSPaolo Bonzini                     s->slaves[i].intreg_pending |= 1 << intbit_to_level[j];
2487702e47cSPaolo Bonzini                 }
2497702e47cSPaolo Bonzini             }
2507702e47cSPaolo Bonzini         }
2517702e47cSPaolo Bonzini 
2527702e47cSPaolo Bonzini         /* Level 15 and CPU timer interrupts are only masked when
2537702e47cSPaolo Bonzini            the MASTER_DISABLE bit is set */
2547702e47cSPaolo Bonzini         if (!(s->intregm_disabled & MASTER_DISABLE)) {
2557702e47cSPaolo Bonzini             pil_pending |= s->slaves[i].intreg_pending &
2567702e47cSPaolo Bonzini                 (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN);
2577702e47cSPaolo Bonzini         }
2587702e47cSPaolo Bonzini 
2597702e47cSPaolo Bonzini         /* Add soft interrupts */
2607702e47cSPaolo Bonzini         pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16;
2617702e47cSPaolo Bonzini 
2627702e47cSPaolo Bonzini         if (set_irqs) {
2637702e47cSPaolo Bonzini             /* Since there is not really an interrupt 0 (and pil_pending
2647702e47cSPaolo Bonzini              * and irl_out bit zero are thus always zero) there is no need
2657702e47cSPaolo Bonzini              * to do anything with cpu_irqs[i][0] and it is OK not to do
2667702e47cSPaolo Bonzini              * the j=0 iteration of this loop.
2677702e47cSPaolo Bonzini              */
2687702e47cSPaolo Bonzini             for (j = MAX_PILS-1; j > 0; j--) {
2697702e47cSPaolo Bonzini                 if (pil_pending & (1 << j)) {
2707702e47cSPaolo Bonzini                     if (!(s->slaves[i].irl_out & (1 << j))) {
2717702e47cSPaolo Bonzini                         qemu_irq_raise(s->cpu_irqs[i][j]);
2727702e47cSPaolo Bonzini                     }
2737702e47cSPaolo Bonzini                 } else {
2747702e47cSPaolo Bonzini                     if (s->slaves[i].irl_out & (1 << j)) {
2757702e47cSPaolo Bonzini                         qemu_irq_lower(s->cpu_irqs[i][j]);
2767702e47cSPaolo Bonzini                     }
2777702e47cSPaolo Bonzini                 }
2787702e47cSPaolo Bonzini             }
2797702e47cSPaolo Bonzini         }
2807702e47cSPaolo Bonzini         s->slaves[i].irl_out = pil_pending;
2817702e47cSPaolo Bonzini     }
2827702e47cSPaolo Bonzini }
2837702e47cSPaolo Bonzini 
2847702e47cSPaolo Bonzini /*
2857702e47cSPaolo Bonzini  * "irq" here is the bit number in the system interrupt register to
2867702e47cSPaolo Bonzini  * separate serial and keyboard interrupts sharing a level.
2877702e47cSPaolo Bonzini  */
slavio_set_irq(void * opaque,int irq,int level)2887702e47cSPaolo Bonzini static void slavio_set_irq(void *opaque, int irq, int level)
2897702e47cSPaolo Bonzini {
2907702e47cSPaolo Bonzini     SLAVIO_INTCTLState *s = opaque;
2917702e47cSPaolo Bonzini     uint32_t mask = 1 << irq;
2927702e47cSPaolo Bonzini     uint32_t pil = intbit_to_level[irq];
2937702e47cSPaolo Bonzini     unsigned int i;
2947702e47cSPaolo Bonzini 
2957702e47cSPaolo Bonzini     trace_slavio_set_irq(s->target_cpu, irq, pil, level);
2967702e47cSPaolo Bonzini     if (pil > 0) {
2977702e47cSPaolo Bonzini         if (level) {
2987702e47cSPaolo Bonzini #ifdef DEBUG_IRQ_COUNT
2997702e47cSPaolo Bonzini             s->irq_count[pil]++;
3007702e47cSPaolo Bonzini #endif
3017702e47cSPaolo Bonzini             s->intregm_pending |= mask;
3027702e47cSPaolo Bonzini             if (pil == 15) {
3037702e47cSPaolo Bonzini                 for (i = 0; i < MAX_CPUS; i++) {
3047702e47cSPaolo Bonzini                     s->slaves[i].intreg_pending |= 1 << pil;
3057702e47cSPaolo Bonzini                 }
3067702e47cSPaolo Bonzini             }
3077702e47cSPaolo Bonzini         } else {
3087702e47cSPaolo Bonzini             s->intregm_pending &= ~mask;
3097702e47cSPaolo Bonzini             if (pil == 15) {
3107702e47cSPaolo Bonzini                 for (i = 0; i < MAX_CPUS; i++) {
3117702e47cSPaolo Bonzini                     s->slaves[i].intreg_pending &= ~(1 << pil);
3127702e47cSPaolo Bonzini                 }
3137702e47cSPaolo Bonzini             }
3147702e47cSPaolo Bonzini         }
3157702e47cSPaolo Bonzini         slavio_check_interrupts(s, 1);
3167702e47cSPaolo Bonzini     }
3177702e47cSPaolo Bonzini }
3187702e47cSPaolo Bonzini 
slavio_set_timer_irq_cpu(void * opaque,int cpu,int level)3197702e47cSPaolo Bonzini static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level)
3207702e47cSPaolo Bonzini {
3217702e47cSPaolo Bonzini     SLAVIO_INTCTLState *s = opaque;
3227702e47cSPaolo Bonzini 
3237702e47cSPaolo Bonzini     trace_slavio_set_timer_irq_cpu(cpu, level);
3247702e47cSPaolo Bonzini 
3257702e47cSPaolo Bonzini     if (level) {
3267702e47cSPaolo Bonzini         s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN;
3277702e47cSPaolo Bonzini     } else {
3287702e47cSPaolo Bonzini         s->slaves[cpu].intreg_pending &= ~CPU_IRQ_TIMER_IN;
3297702e47cSPaolo Bonzini     }
3307702e47cSPaolo Bonzini 
3317702e47cSPaolo Bonzini     slavio_check_interrupts(s, 1);
3327702e47cSPaolo Bonzini }
3337702e47cSPaolo Bonzini 
slavio_set_irq_all(void * opaque,int irq,int level)3347702e47cSPaolo Bonzini static void slavio_set_irq_all(void *opaque, int irq, int level)
3357702e47cSPaolo Bonzini {
3367702e47cSPaolo Bonzini     if (irq < 32) {
3377702e47cSPaolo Bonzini         slavio_set_irq(opaque, irq, level);
3387702e47cSPaolo Bonzini     } else {
3397702e47cSPaolo Bonzini         slavio_set_timer_irq_cpu(opaque, irq - 32, level);
3407702e47cSPaolo Bonzini     }
3417702e47cSPaolo Bonzini }
3427702e47cSPaolo Bonzini 
vmstate_intctl_post_load(void * opaque,int version_id)3437702e47cSPaolo Bonzini static int vmstate_intctl_post_load(void *opaque, int version_id)
3447702e47cSPaolo Bonzini {
3457702e47cSPaolo Bonzini     SLAVIO_INTCTLState *s = opaque;
3467702e47cSPaolo Bonzini 
3477702e47cSPaolo Bonzini     slavio_check_interrupts(s, 0);
3487702e47cSPaolo Bonzini     return 0;
3497702e47cSPaolo Bonzini }
3507702e47cSPaolo Bonzini 
3517702e47cSPaolo Bonzini static const VMStateDescription vmstate_intctl_cpu = {
3527702e47cSPaolo Bonzini     .name ="slavio_intctl_cpu",
3537702e47cSPaolo Bonzini     .version_id = 1,
3547702e47cSPaolo Bonzini     .minimum_version_id = 1,
35545b1f81dSRichard Henderson     .fields = (const VMStateField[]) {
3567702e47cSPaolo Bonzini         VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState),
3577702e47cSPaolo Bonzini         VMSTATE_END_OF_LIST()
3587702e47cSPaolo Bonzini     }
3597702e47cSPaolo Bonzini };
3607702e47cSPaolo Bonzini 
3617702e47cSPaolo Bonzini static const VMStateDescription vmstate_intctl = {
3627702e47cSPaolo Bonzini     .name ="slavio_intctl",
3637702e47cSPaolo Bonzini     .version_id = 1,
3647702e47cSPaolo Bonzini     .minimum_version_id = 1,
3657702e47cSPaolo Bonzini     .post_load = vmstate_intctl_post_load,
36645b1f81dSRichard Henderson     .fields = (const VMStateField[]) {
3677702e47cSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1,
3687702e47cSPaolo Bonzini                              vmstate_intctl_cpu, SLAVIO_CPUINTCTLState),
3697702e47cSPaolo Bonzini         VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState),
3707702e47cSPaolo Bonzini         VMSTATE_UINT32(intregm_disabled, SLAVIO_INTCTLState),
3717702e47cSPaolo Bonzini         VMSTATE_UINT32(target_cpu, SLAVIO_INTCTLState),
3727702e47cSPaolo Bonzini         VMSTATE_END_OF_LIST()
3737702e47cSPaolo Bonzini     }
3747702e47cSPaolo Bonzini };
3757702e47cSPaolo Bonzini 
slavio_intctl_reset(DeviceState * d)3767702e47cSPaolo Bonzini static void slavio_intctl_reset(DeviceState *d)
3777702e47cSPaolo Bonzini {
3787abad863SAndreas Färber     SLAVIO_INTCTLState *s = SLAVIO_INTCTL(d);
3797702e47cSPaolo Bonzini     int i;
3807702e47cSPaolo Bonzini 
3817702e47cSPaolo Bonzini     for (i = 0; i < MAX_CPUS; i++) {
3827702e47cSPaolo Bonzini         s->slaves[i].intreg_pending = 0;
3837702e47cSPaolo Bonzini         s->slaves[i].irl_out = 0;
3847702e47cSPaolo Bonzini     }
3857702e47cSPaolo Bonzini     s->intregm_disabled = ~MASTER_IRQ_MASK;
3867702e47cSPaolo Bonzini     s->intregm_pending = 0;
3877702e47cSPaolo Bonzini     s->target_cpu = 0;
3887702e47cSPaolo Bonzini     slavio_check_interrupts(s, 0);
3897702e47cSPaolo Bonzini }
3907702e47cSPaolo Bonzini 
391148fbe95SHervé Poussineau #ifdef DEBUG_IRQ_COUNT
slavio_intctl_get_statistics(InterruptStatsProvider * obj,uint64_t ** irq_counts,unsigned int * nb_irqs)392148fbe95SHervé Poussineau static bool slavio_intctl_get_statistics(InterruptStatsProvider *obj,
393148fbe95SHervé Poussineau                                          uint64_t **irq_counts,
394148fbe95SHervé Poussineau                                          unsigned int *nb_irqs)
395148fbe95SHervé Poussineau {
396148fbe95SHervé Poussineau     SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj);
397148fbe95SHervé Poussineau     *irq_counts = s->irq_count;
398148fbe95SHervé Poussineau     *nb_irqs = ARRAY_SIZE(s->irq_count);
399148fbe95SHervé Poussineau     return true;
400148fbe95SHervé Poussineau }
401148fbe95SHervé Poussineau #endif
402148fbe95SHervé Poussineau 
slavio_intctl_print_info(InterruptStatsProvider * obj,GString * buf)403b2580720SPhilippe Mathieu-Daudé static void slavio_intctl_print_info(InterruptStatsProvider *obj, GString *buf)
404148fbe95SHervé Poussineau {
405148fbe95SHervé Poussineau     SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj);
406148fbe95SHervé Poussineau     int i;
407148fbe95SHervé Poussineau 
408148fbe95SHervé Poussineau     for (i = 0; i < MAX_CPUS; i++) {
409b2580720SPhilippe Mathieu-Daudé         g_string_append_printf(buf, "per-cpu %d: pending 0x%08x\n", i,
410148fbe95SHervé Poussineau                                s->slaves[i].intreg_pending);
411148fbe95SHervé Poussineau     }
412b2580720SPhilippe Mathieu-Daudé     g_string_append_printf(buf, "master: pending 0x%08x, disabled 0x%08x\n",
413148fbe95SHervé Poussineau                            s->intregm_pending, s->intregm_disabled);
414148fbe95SHervé Poussineau }
415148fbe95SHervé Poussineau 
slavio_intctl_init(Object * obj)416c09008d2Sxiaoqiang.zhao static void slavio_intctl_init(Object *obj)
4177702e47cSPaolo Bonzini {
418c09008d2Sxiaoqiang.zhao     DeviceState *dev = DEVICE(obj);
419c09008d2Sxiaoqiang.zhao     SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj);
420c09008d2Sxiaoqiang.zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
4217702e47cSPaolo Bonzini     unsigned int i, j;
4227702e47cSPaolo Bonzini     char slave_name[45];
4237702e47cSPaolo Bonzini 
4247abad863SAndreas Färber     qdev_init_gpio_in(dev, slavio_set_irq_all, 32 + MAX_CPUS);
425c09008d2Sxiaoqiang.zhao     memory_region_init_io(&s->iomem, obj, &slavio_intctlm_mem_ops, s,
4267702e47cSPaolo Bonzini                           "master-interrupt-controller", INTCTLM_SIZE);
4277abad863SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
4287702e47cSPaolo Bonzini 
4297702e47cSPaolo Bonzini     for (i = 0; i < MAX_CPUS; i++) {
4307702e47cSPaolo Bonzini         snprintf(slave_name, sizeof(slave_name),
4317702e47cSPaolo Bonzini                  "slave-interrupt-controller-%i", i);
4327702e47cSPaolo Bonzini         for (j = 0; j < MAX_PILS; j++) {
4337abad863SAndreas Färber             sysbus_init_irq(sbd, &s->cpu_irqs[i][j]);
4347702e47cSPaolo Bonzini         }
4351437c94bSPaolo Bonzini         memory_region_init_io(&s->slaves[i].iomem, OBJECT(s),
4361437c94bSPaolo Bonzini                               &slavio_intctl_mem_ops,
4377702e47cSPaolo Bonzini                               &s->slaves[i], slave_name, INTCTL_SIZE);
4387abad863SAndreas Färber         sysbus_init_mmio(sbd, &s->slaves[i].iomem);
4397702e47cSPaolo Bonzini         s->slaves[i].cpu = i;
4407702e47cSPaolo Bonzini         s->slaves[i].master = s;
4417702e47cSPaolo Bonzini     }
4427702e47cSPaolo Bonzini }
4437702e47cSPaolo Bonzini 
slavio_intctl_class_init(ObjectClass * klass,void * data)4447702e47cSPaolo Bonzini static void slavio_intctl_class_init(ObjectClass *klass, void *data)
4457702e47cSPaolo Bonzini {
4467702e47cSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
447148fbe95SHervé Poussineau     InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass);
4487702e47cSPaolo Bonzini 
449*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, slavio_intctl_reset);
4507702e47cSPaolo Bonzini     dc->vmsd = &vmstate_intctl;
451148fbe95SHervé Poussineau #ifdef DEBUG_IRQ_COUNT
452148fbe95SHervé Poussineau     ic->get_statistics = slavio_intctl_get_statistics;
453148fbe95SHervé Poussineau #endif
454148fbe95SHervé Poussineau     ic->print_info = slavio_intctl_print_info;
4557702e47cSPaolo Bonzini }
4567702e47cSPaolo Bonzini 
4577702e47cSPaolo Bonzini static const TypeInfo slavio_intctl_info = {
4587abad863SAndreas Färber     .name          = TYPE_SLAVIO_INTCTL,
4597702e47cSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
4607702e47cSPaolo Bonzini     .instance_size = sizeof(SLAVIO_INTCTLState),
461c09008d2Sxiaoqiang.zhao     .instance_init = slavio_intctl_init,
4627702e47cSPaolo Bonzini     .class_init    = slavio_intctl_class_init,
463148fbe95SHervé Poussineau     .interfaces = (InterfaceInfo[]) {
464148fbe95SHervé Poussineau         { TYPE_INTERRUPT_STATS_PROVIDER },
465148fbe95SHervé Poussineau         { }
466148fbe95SHervé Poussineau     },
4677702e47cSPaolo Bonzini };
4687702e47cSPaolo Bonzini 
slavio_intctl_register_types(void)4697702e47cSPaolo Bonzini static void slavio_intctl_register_types(void)
4707702e47cSPaolo Bonzini {
4717702e47cSPaolo Bonzini     type_register_static(&slavio_intctl_info);
4727702e47cSPaolo Bonzini }
4737702e47cSPaolo Bonzini 
4747702e47cSPaolo Bonzini type_init(slavio_intctl_register_types)
475