summary refs log tree commit diff stats
path: root/hw/intc/grlib_irqmp.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-02-16 11:05:14 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-02-16 11:05:14 +0000
commitda96ad4a6a2ef26c83b15fa95e7fceef5147269c (patch)
tree3780fdfeef4cb0a04dd2c5b3aa4e444adf38aac7 /hw/intc/grlib_irqmp.c
parent8e31b744fdf2c5d933681e4128acee72a83af4b8 (diff)
parent9a4b35f57eefbfc6977ed47d1f19d839e9e4784d (diff)
downloadfocaccia-qemu-da96ad4a6a2ef26c83b15fa95e7fceef5147269c.tar.gz
focaccia-qemu-da96ad4a6a2ef26c83b15fa95e7fceef5147269c.zip
Merge tag 'hw-misc-20240215' of https://github.com/philmd/qemu into staging
Misc HW patch queue

- Remove unused MIPS SAAR* registers (Phil)
- Remove warning when testing the TC58128 NAND EEPROM (Peter)
- KConfig cleanups around ISA SuperI/O and MIPS (Paolo)
- QDev API uses sanitization (Philippe)
- Split AHCI model as PCI / SysBus (Philippe)
- Add SMP support to SPARC Leon3 board (Clément)

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmXOUD4ACgkQ4+MsLN6t
# wN6gWRAAjf+6Z9VUvvHqZoaSJW49k+GRUelTI2tyN+eGfetAx9dk8aIbpbV1X12d
# pc56jsSi6ICT7baCegtxHszhYJr2e9A2QLCAOJt+Oz87kEGes3ONVVKAk7pwjKxt
# m8pmU3uXWgFvU6PoFBhGBa6LiZBulgLNXBUwzmEhc9PpPkR49ULdDp/qxtWvxOV5
# xYBktFlkiT+AvHq3QWCnDIaw+pH5ghEq9BI4xFOvvvqSqdHEqsGAaiKPa9Po0Gfz
# Ap9qsm4FxKxhGoeQWtAIP8TvN3pFFSXMysziP6Xt1rffKsvF9ioghGKRM6BgQfqD
# ZetjcFbcf7dQu3zZVy8ljYcymMxfZcWWVVq4CMC68lPQE97hz1CT3PJjgd77dKfi
# z60uRkOGaiPW5iIGT9+vdQxZ5K3HivKyjuHOdV8V4HnWO3oqgfDtNHn5RKed0qUg
# g1FoWriJGsDixdx1vd0EoH2/oTxy4HIsFv7a1OjiZyBLjO+EeEZ3+H9pqUHqBxva
# +Dv70z9F1sv5dzcUXH+oCgTbnKlJ90Q+e3vj0wGdlBncVsgIwbtgqYelhUEl+xJX
# Mu6KNUo5ANVP38ZKG0GSMCZHfcUjc5s+5rG55NbTN0HiF56a6D2KlQAuXdUsGE1J
# 7i4cwipJmfxzbdPDlSb3kBxm5pFexEk6nROF9kTHQj3ZBMMvIls=
# =nOX+
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 15 Feb 2024 17:56:14 GMT
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* tag 'hw-misc-20240215' of https://github.com/philmd/qemu: (56 commits)
  hw/ide/ich9: Use AHCIPCIState typedef
  hw/ide/ahci: Move SysBus definitions to 'ahci-sysbus.h'
  hw/ide/ahci: Remove SysbusAHCIState::num_ports field
  hw/ide/ahci: Do not pass 'ports' argument to ahci_realize()
  hw/ide/ahci: Convert AHCIState::ports to unsigned
  hw/ide/ahci: Pass AHCI context to ahci_ide_create_devs()
  hw/ide/ahci: Inline ahci_get_num_ports()
  hw/ide/ahci: Rename AHCI PCI function as 'pdev'
  hw/ide/ahci: Expose AHCIPCIState structure
  hw/i386/q35: Use DEVICE() cast macro with PCIDevice object
  hw/i386/q35: Simplify pc_q35_init() since PCI is always enabled
  MAINTAINERS: Add myself as reviewer for TCG Plugins
  MAINTAINERS: replace Fabien by myself as Leon3 maintainer
  hw/sparc/leon3: Initialize GPIO before realizing CPU devices
  hw/sparc/leon3: Pass DeviceState opaque argument to leon3_start_cpu()
  hw/sparc/leon3: Pass DeviceState opaque argument to leon3_set_pil_in()
  hw/sparc/leon3: check cpu_id in the tiny bootloader
  hw/sparc/leon3: implement multiprocessor
  hw/sparc/leon3: remove SP initialization
  target/sparc: implement asr17 feature for smp
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/intc/grlib_irqmp.c')
-rw-r--r--hw/intc/grlib_irqmp.c110
1 files changed, 76 insertions, 34 deletions
diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c
index 3bfe2544b7..144b121d48 100644
--- a/hw/intc/grlib_irqmp.c
+++ b/hw/intc/grlib_irqmp.c
@@ -1,9 +1,11 @@
 /*
  * QEMU GRLIB IRQMP Emulator
  *
- * (Multiprocessor and extended interrupt not supported)
+ * (Extended interrupt not supported)
  *
- * Copyright (c) 2010-2019 AdaCore
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2010-2024 AdaCore
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -29,7 +31,7 @@
 #include "hw/sysbus.h"
 
 #include "hw/qdev-properties.h"
-#include "hw/sparc/grlib.h"
+#include "hw/intc/grlib_irqmp.h"
 
 #include "trace.h"
 #include "qapi/error.h"
@@ -50,6 +52,10 @@
 #define FORCE_OFFSET     0x80
 #define EXTENDED_OFFSET  0xC0
 
+/* Multiprocessor Status Register  */
+#define MP_STATUS_CPU_STATUS_MASK ((1 << IRQMP_MAX_CPU)-2)
+#define MP_STATUS_NCPU_SHIFT      28
+
 #define MAX_PILS 16
 
 OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP)
@@ -61,14 +67,17 @@ struct IRQMP {
 
     MemoryRegion iomem;
 
+    unsigned int ncpus;
     IRQMPState *state;
-    qemu_irq irq;
+    qemu_irq start_signal[IRQMP_MAX_CPU];
+    qemu_irq irq[IRQMP_MAX_CPU];
 };
 
 struct IRQMPState {
     uint32_t level;
     uint32_t pending;
     uint32_t clear;
+    uint32_t mpstatus;
     uint32_t broadcast;
 
     uint32_t mask[IRQMP_MAX_CPU];
@@ -80,37 +89,35 @@ struct IRQMPState {
 
 static void grlib_irqmp_check_irqs(IRQMPState *state)
 {
-    uint32_t      pend   = 0;
-    uint32_t      level0 = 0;
-    uint32_t      level1 = 0;
+    int i;
 
     assert(state != NULL);
     assert(state->parent != NULL);
 
-    /* IRQ for CPU 0 (no SMP support) */
-    pend = (state->pending | state->force[0])
-        & state->mask[0];
-
-    level0 = pend & ~state->level;
-    level1 = pend &  state->level;
+    for (i = 0; i < state->parent->ncpus; i++) {
+        uint32_t pend = (state->pending | state->force[i]) & state->mask[i];
+        uint32_t level0 = pend & ~state->level;
+        uint32_t level1 = pend &  state->level;
 
-    trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
-                                 state->mask[0], level1, level0);
+        trace_grlib_irqmp_check_irqs(state->pending, state->force[i],
+                                     state->mask[i], level1, level0);
 
-    /* Trigger level1 interrupt first and level0 if there is no level1 */
-    qemu_set_irq(state->parent->irq, level1 ?: level0);
+        /* Trigger level1 interrupt first and level0 if there is no level1 */
+        qemu_set_irq(state->parent->irq[i], level1 ?: level0);
+    }
 }
 
-static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask)
+static void grlib_irqmp_ack_mask(IRQMPState *state, unsigned int cpu,
+                                 uint32_t mask)
 {
     /* Clear registers */
     state->pending  &= ~mask;
-    state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
+    state->force[cpu] &= ~mask;
 
     grlib_irqmp_check_irqs(state);
 }
 
-void grlib_irqmp_ack(DeviceState *dev, int intno)
+void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno)
 {
     IRQMP        *irqmp = GRLIB_IRQMP(dev);
     IRQMPState   *state;
@@ -124,7 +131,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno)
 
     trace_grlib_irqmp_ack(intno);
 
-    grlib_irqmp_ack_mask(state, mask);
+    grlib_irqmp_ack_mask(state, cpu, mask);
 }
 
 static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
@@ -150,7 +157,6 @@ static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
             s->pending |= 1 << irq;
         }
         grlib_irqmp_check_irqs(s);
-
     }
 }
 
@@ -179,10 +185,12 @@ static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
         return state->force[0];
 
     case CLEAR_OFFSET:
-    case MP_STATUS_OFFSET:
         /* Always read as 0 */
         return 0;
 
+    case MP_STATUS_OFFSET:
+        return state->mpstatus;
+
     case BROADCAST_OFFSET:
         return state->broadcast;
 
@@ -221,8 +229,9 @@ static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
 static void grlib_irqmp_write(void *opaque, hwaddr addr,
                               uint64_t value, unsigned size)
 {
-    IRQMP      *irqmp = opaque;
+    IRQMP *irqmp = opaque;
     IRQMPState *state;
+    int i;
 
     assert(irqmp != NULL);
     state = irqmp->state;
@@ -251,11 +260,24 @@ static void grlib_irqmp_write(void *opaque, hwaddr addr,
 
     case CLEAR_OFFSET:
         value &= ~1; /* clean up the value */
-        grlib_irqmp_ack_mask(state, value);
+        for (i = 0; i < irqmp->ncpus; i++) {
+            grlib_irqmp_ack_mask(state, i, value);
+        }
         return;
 
     case MP_STATUS_OFFSET:
-        /* Read Only (no SMP support) */
+        /*
+         * Writing and reading operations are reversed for the CPU status.
+         * Writing "1" will start the CPU, but reading "1" means that the CPU
+         * is power-down.
+         */
+        value &= MP_STATUS_CPU_STATUS_MASK;
+        for (i = 0; i < irqmp->ncpus; i++) {
+            if ((value >> i) & 1) {
+                qemu_set_irq(irqmp->start_signal[i], 1);
+                state->mpstatus &= ~(1 << i);
+            }
+        }
         return;
 
     case BROADCAST_OFFSET:
@@ -322,35 +344,55 @@ static void grlib_irqmp_reset(DeviceState *d)
 
     memset(irqmp->state, 0, sizeof *irqmp->state);
     irqmp->state->parent = irqmp;
+    irqmp->state->mpstatus = ((irqmp->ncpus - 1) << MP_STATUS_NCPU_SHIFT) |
+        ((1 << irqmp->ncpus) - 2);
 }
 
-static void grlib_irqmp_init(Object *obj)
+static void grlib_irqmp_realize(DeviceState *dev, Error **errp)
 {
-    IRQMP *irqmp = GRLIB_IRQMP(obj);
-    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+    IRQMP *irqmp = GRLIB_IRQMP(dev);
+
+    if ((!irqmp->ncpus) || (irqmp->ncpus > IRQMP_MAX_CPU)) {
+        error_setg(errp, "Invalid ncpus properties: "
+                   "%u, must be 0 < ncpus =< %u.", irqmp->ncpus,
+                   IRQMP_MAX_CPU);
+    }
+
+    qdev_init_gpio_in(dev, grlib_irqmp_set_irq, MAX_PILS);
 
-    qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS);
-    qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1);
-    memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp,
+    /*
+     * Transitionning from 0 to 1 starts the CPUs. The opposite can't
+     * happen.
+     */
+    qdev_init_gpio_out_named(dev, irqmp->start_signal, "grlib-start-cpu",
+                             IRQMP_MAX_CPU);
+    qdev_init_gpio_out_named(dev, irqmp->irq, "grlib-irq", irqmp->ncpus);
+    memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp,
                           "irqmp", IRQMP_REG_SIZE);
 
     irqmp->state = g_malloc0(sizeof *irqmp->state);
 
-    sysbus_init_mmio(dev, &irqmp->iomem);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &irqmp->iomem);
 }
 
+static Property grlib_irqmp_properties[] = {
+    DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    dc->realize = grlib_irqmp_realize;
     dc->reset = grlib_irqmp_reset;
+    device_class_set_props(dc, grlib_irqmp_properties);
 }
 
 static const TypeInfo grlib_irqmp_info = {
     .name          = TYPE_GRLIB_IRQMP,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(IRQMP),
-    .instance_init = grlib_irqmp_init,
     .class_init    = grlib_irqmp_class_init,
 };