System service completion raises PLIC source 96 from IOSCB, while
software accesses MESSAGE_INT through SYSREG. The MESSAGE_INT pending
bit is not modelled explicitly, so reads return zero even when a
service interrupt is active. Model the bit so service completion and
register accesses use the same state.

Keep the pending state and PLIC output in IOSCB. Link SYSREG to IOSCB
so MESSAGE_INT reads the current pending bit and writes update the
same state.

Signed-off-by: Bin Meng <[email protected]>

---

Changes in v3:
- Separate interrupt state ownership from the NOTIFY fix
- Link SYSREG to IOSCB for MESSAGE_INT reads and writes
- Support both clearing and setting the MESSAGE_INT pending bit

 include/hw/misc/mchp_pfsoc_ioscb.h  |  4 ++++
 include/hw/misc/mchp_pfsoc_sysreg.h |  3 ++-
 hw/misc/mchp_pfsoc_ioscb.c          | 20 ++++++++++++++--
 hw/misc/mchp_pfsoc_sysreg.c         | 37 ++++++++++++++++++++++-------
 hw/riscv/microchip_pfsoc.c          |  5 ++--
 5 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/include/hw/misc/mchp_pfsoc_ioscb.h 
b/include/hw/misc/mchp_pfsoc_ioscb.h
index aa108d2d88..e8386f9794 100644
--- a/include/hw/misc/mchp_pfsoc_ioscb.h
+++ b/include/hw/misc/mchp_pfsoc_ioscb.h
@@ -53,6 +53,7 @@ typedef struct MchpPfSoCIoscbState {
     uint32_t services_sr;
     uint8_t mailbox_data[MCHP_PFSOC_IOSCB_MAILBOX_SIZE];
     char *serial_number;
+    bool irq_pending;
     qemu_irq irq;
 } MchpPfSoCIoscbState;
 
@@ -61,4 +62,7 @@ typedef struct MchpPfSoCIoscbState {
 #define MCHP_PFSOC_IOSCB(obj) \
     OBJECT_CHECK(MchpPfSoCIoscbState, (obj), TYPE_MCHP_PFSOC_IOSCB)
 
+bool mchp_pfsoc_ioscb_get_irq_pending(MchpPfSoCIoscbState *s);
+void mchp_pfsoc_ioscb_set_irq_pending(MchpPfSoCIoscbState *s, bool pending);
+
 #endif /* MCHP_PFSOC_IOSCB_H */
diff --git a/include/hw/misc/mchp_pfsoc_sysreg.h 
b/include/hw/misc/mchp_pfsoc_sysreg.h
index c2b3688a94..d6c9027f2d 100644
--- a/include/hw/misc/mchp_pfsoc_sysreg.h
+++ b/include/hw/misc/mchp_pfsoc_sysreg.h
@@ -24,13 +24,14 @@
 #define MCHP_PFSOC_SYSREG_H
 
 #include "hw/core/sysbus.h"
+#include "hw/misc/mchp_pfsoc_ioscb.h"
 
 #define MCHP_PFSOC_SYSREG_REG_SIZE  0x2000
 
 typedef struct MchpPfSoCSysregState {
     SysBusDevice parent;
     MemoryRegion sysreg;
-    qemu_irq irq;
+    MchpPfSoCIoscbState *ioscb;
 } MchpPfSoCSysregState;
 
 #define TYPE_MCHP_PFSOC_SYSREG "mchp.pfsoc.sysreg"
diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c
index bde1917c10..1f77da6852 100644
--- a/hw/misc/mchp_pfsoc_ioscb.c
+++ b/hw/misc/mchp_pfsoc_ioscb.c
@@ -209,6 +209,22 @@ static const MemoryRegionOps mchp_pfsoc_io_calib_ddr_ops = 
{
 #define SERVICES_STATUS_SUCCESS             0
 #define SERVICES_STATUS_FAILED              1
 
+static void mchp_pfsoc_ioscb_update_irq(MchpPfSoCIoscbState *s)
+{
+    qemu_set_irq(s->irq, s->irq_pending);
+}
+
+bool mchp_pfsoc_ioscb_get_irq_pending(MchpPfSoCIoscbState *s)
+{
+    return s->irq_pending;
+}
+
+void mchp_pfsoc_ioscb_set_irq_pending(MchpPfSoCIoscbState *s, bool pending)
+{
+    s->irq_pending = pending;
+    mchp_pfsoc_ioscb_update_irq(s);
+}
+
 static void services_cr_write(MchpPfSoCIoscbState *s, uint32_t value)
 {
     uint32_t command;
@@ -248,7 +264,7 @@ static void services_cr_write(MchpPfSoCIoscbState *s, 
uint32_t value)
 
     s->services_sr = status << SERVICES_SR_STATUS_SHIFT;
     if (value & SERVICES_CR_NOTIFY) {
-        qemu_irq_raise(s->irq);
+        mchp_pfsoc_ioscb_set_irq_pending(s, true);
     }
 }
 
@@ -339,7 +355,7 @@ static void mchp_pfsoc_ioscb_reset(DeviceState *dev)
     s->services_cr = 0;
     s->services_sr = 0;
     memset(s->mailbox_data, 0, sizeof(s->mailbox_data));
-    qemu_irq_lower(s->irq);
+    mchp_pfsoc_ioscb_set_irq_pending(s, false);
 }
 
 static const Property mchp_pfsoc_ioscb_properties[] = {
diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c
index 1d9154280a..4c52338c8a 100644
--- a/hw/misc/mchp_pfsoc_sysreg.c
+++ b/hw/misc/mchp_pfsoc_sysreg.c
@@ -24,20 +24,22 @@
 #include "qemu/bitops.h"
 #include "qemu/log.h"
 #include "qapi/error.h"
-#include "hw/core/irq.h"
+#include "hw/core/qdev-properties.h"
 #include "hw/core/sysbus.h"
 #include "hw/misc/mchp_pfsoc_sysreg.h"
 #include "system/runstate.h"
 
-#define CLOCK_CONFIG_CR 0x8
-#define RTC_CLOCK_CR    0xc
-#define MSS_RESET_CR    0x18
-#define ENVM_CR         0xb8
-#define MESSAGE_INT     0x118c
+#define CLOCK_CONFIG_CR     0x8
+#define RTC_CLOCK_CR        0xc
+#define MSS_RESET_CR        0x18
+#define ENVM_CR             0xb8
+#define MESSAGE_INT         0x118c
+#define MESSAGE_INT_PENDING BIT(0)
 
 static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr offset,
                                        unsigned size)
 {
+    MchpPfSoCSysregState *s = opaque;
     uint32_t val = 0;
 
     switch (offset) {
@@ -56,6 +58,10 @@ static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr 
offset,
         /* Indicate the eNVM is running at the configured divider rate */
         val = BIT(6);
         break;
+    case MESSAGE_INT:
+        val = mchp_pfsoc_ioscb_get_irq_pending(s->ioscb) ?
+              MESSAGE_INT_PENDING : 0;
+        break;
     default:
         qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read "
                       "(size %d, offset 0x%" HWADDR_PRIx ")\n",
@@ -77,7 +83,12 @@ static void mchp_pfsoc_sysreg_write(void *opaque, hwaddr 
offset,
         }
         break;
     case MESSAGE_INT:
-        qemu_irq_lower(s->irq);
+        /*
+         * MESSAGE_INT bit 0 is read/write: writing zero clears the interrupt
+         * and writing one sets it. IOSCB owns the state and PLIC output.
+         */
+        mchp_pfsoc_ioscb_set_irq_pending(s->ioscb,
+                                        value & MESSAGE_INT_PENDING);
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write "
@@ -97,20 +108,30 @@ static void mchp_pfsoc_sysreg_realize(DeviceState *dev, 
Error **errp)
 {
     MchpPfSoCSysregState *s = MCHP_PFSOC_SYSREG(dev);
 
+    if (!s->ioscb) {
+        error_setg(errp, "The 'ioscb' link must be set");
+        return;
+    }
+
     memory_region_init_io(&s->sysreg, OBJECT(dev),
                           &mchp_pfsoc_sysreg_ops, s,
                           "mchp.pfsoc.sysreg",
                           MCHP_PFSOC_SYSREG_REG_SIZE);
     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sysreg);
-    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
 }
 
+static const Property mchp_pfsoc_sysreg_properties[] = {
+    DEFINE_PROP_LINK("ioscb", MchpPfSoCSysregState, ioscb,
+                     TYPE_MCHP_PFSOC_IOSCB, MchpPfSoCIoscbState *),
+};
+
 static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, const void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->desc = "Microchip PolarFire SoC SYSREG module";
     dc->realize = mchp_pfsoc_sysreg_realize;
+    device_class_set_props(dc, mchp_pfsoc_sysreg_properties);
 }
 
 static const TypeInfo mchp_pfsoc_sysreg_info = {
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index 36c894b677..38f4e7d799 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -315,12 +315,11 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, 
Error **errp)
     }
 
     /* SYSREG */
+    object_property_set_link(OBJECT(&s->sysreg), "ioscb", OBJECT(&s->ioscb),
+                             &error_abort);
     sysbus_realize(SYS_BUS_DEVICE(&s->sysreg), errp);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysreg), 0,
                     memmap[MICROCHIP_PFSOC_SYSREG].base);
-    sysbus_connect_irq(SYS_BUS_DEVICE(&s->sysreg), 0,
-                       qdev_get_gpio_in(DEVICE(s->plic),
-                       MICROCHIP_PFSOC_MAILBOX_IRQ));
 
     /* AXISW */
     create_unimplemented_device("microchip.pfsoc.axisw",
-- 
2.53.0


Reply via email to