The E2000 model only serviced the private PBF mailbox channel. The
vendor Linux driver uses a separate shared-memory channel and therefore
timed out before it could discover any SCMI protocols.

Model the Linux MHU doorbells and Base protocol while leaving
Performance and Sensor unadvertised until their platform data can be
represented faithfully. Document the limitation.

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

 hw/arm/phytium_e2000.c      |   4 +
 hw/misc/phytium_e2000_mhu.c | 457 +++++++++++++++++++++++++++++-------
 2 files changed, 381 insertions(+), 80 deletions(-)

diff --git a/hw/arm/phytium_e2000.c b/hw/arm/phytium_e2000.c
index 3acd9b716b..cfd1ca70c9 100644
--- a/hw/arm/phytium_e2000.c
+++ b/hw/arm/phytium_e2000.c
@@ -232,6 +232,8 @@ static const int 
phytium_e2000_gem_irqmap[PHYTIUM_E2000_NUM_GEMS]
     [3] = { 68, 69, 70, 71 },
 };
 
+static const int phytium_e2000_mhu_irq = 22;
+
 static const int phytium_e2000_pcie_irqmap[PCI_NUM_PINS] = {
     [0] = 4,
     [1] = 5,
@@ -756,6 +758,8 @@ static void phytium_e2000_create_mhu(PhytiumE2000State *s)
     }
     sysbus_realize_and_unref(sbd, &error_fatal);
     sysbus_mmio_map_overlap(sbd, 0, PHYTIUM_E2000_MHU_BASE, 2);
+    sysbus_connect_irq(sbd, 0,
+                       qdev_get_gpio_in(s->gic, phytium_e2000_mhu_irq));
 }
 
 static void phytium_e2000_create_unimplemented(void)
diff --git a/hw/misc/phytium_e2000_mhu.c b/hw/misc/phytium_e2000_mhu.c
index 554448398b..f158918af5 100644
--- a/hw/misc/phytium_e2000_mhu.c
+++ b/hw/misc/phytium_e2000_mhu.c
@@ -1,9 +1,8 @@
 /*
  * Phytium E2000 MHU/SCMI doorbell
  *
- * This is a boot-oriented SCMI transport proxy. It acknowledges requests in
- * shared SRAM so PBF can complete clock and platform setup; it is not a full
- * SCMI protocol server.
+ * This models the PBF firmware transport and the SCMI Base protocol used by
+ * Linux. Protocols whose platform data is not available remain unsupported.
  *
  * Copyright (c) 2026 Process Mission
  *
@@ -16,34 +15,65 @@
 #include "qemu/osdep.h"
 #include "hw/misc/phytium_e2000_mhu.h"
 
-#include "hw/core/register.h"
+#include "hw/core/irq.h"
+#include "hw/core/registerfields.h"
 #include "migration/vmstate.h"
 #include "qapi/error.h"
 #include "qemu/bitops.h"
+#include "qemu/log.h"
 #include "qemu/module.h"
 #include "system/address-spaces.h"
 #include "target/arm/arm-powerctl.h"
 #include "target/arm/cpu.h"
 
-#define PHYTIUM_E2000_PBF_SCMI_MBOX_BASE  0x32a10400
-#define PHYTIUM_E2000_SCMI_STATUS_OFFSET  0x04
-#define PHYTIUM_E2000_SCMI_LEN_OFFSET     0x14
-#define PHYTIUM_E2000_SCMI_HEADER_OFFSET  0x18
-#define PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET 0x1c
-#define PHYTIUM_E2000_SCMI_STATUS_FREE    BIT(0)
-
-#define SCMI_MESSAGE_ID(header)    extract32((header), 0, 8)
-#define SCMI_PROTOCOL_ID(header)   extract32((header), 10, 8)
-#define SCMI_PROTOCOL_POWER_DOMAIN 0x11
-#define SCMI_PROTOCOL_PHYTIUM      0x81
-#define SCMI_POWER_STATE_SET       0x4
-#define SCMI_PHYTIUM_GET_PSOSTAT   0x3
-#define SCMI_POWER_STATE_TYPE      BIT(30)
-#define SCMI_POWER_STATE_ID_MASK   (SCMI_POWER_STATE_TYPE - 1)
+#define PHYTIUM_E2000_PBF_SCMI_MBOX_BASE     0x32a10400
+#define PHYTIUM_E2000_OS_SCMI_MBOX_BASE      0x32a11400
+#define PHYTIUM_E2000_OS_SCMI_MBOX_SIZE      0x400
+#define PHYTIUM_E2000_SCMI_STATUS_OFFSET     0x04
+#define PHYTIUM_E2000_SCMI_FLAGS_OFFSET      0x10
+#define PHYTIUM_E2000_SCMI_LEN_OFFSET        0x14
+#define PHYTIUM_E2000_SCMI_HEADER_OFFSET     0x18
+#define PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET    0x1c
+#define PHYTIUM_E2000_SCMI_STATUS_FREE       BIT(0)
+#define PHYTIUM_E2000_SCMI_FLAG_INTR_ENABLED BIT(0)
+#define PHYTIUM_E2000_SCMI_MAX_FRAME_SIZE    \
+    (PHYTIUM_E2000_OS_SCMI_MBOX_SIZE - \
+     PHYTIUM_E2000_SCMI_HEADER_OFFSET)
+
+#define SCMI_MESSAGE_ID(header)              extract32((header), 0, 8)
+#define SCMI_MESSAGE_TYPE(header)            extract32((header), 8, 2)
+#define SCMI_PROTOCOL_ID(header)             extract32((header), 10, 8)
+#define SCMI_RESERVED(header)                extract32((header), 28, 4)
+#define SCMI_MESSAGE_TYPE_COMMAND            0
+#define SCMI_PROTOCOL_BASE                   0x10
+#define SCMI_PROTOCOL_POWER_DOMAIN           0x11
+#define SCMI_PROTOCOL_PHYTIUM                0x81
+#define SCMI_PROTOCOL_VERSION                0x0
+#define SCMI_PROTOCOL_ATTRIBUTES             0x1
+#define SCMI_PROTOCOL_MESSAGE_ATTRIBUTES     0x2
+#define SCMI_BASE_DISCOVER_VENDOR            0x3
+#define SCMI_BASE_DISCOVER_SUB_VENDOR        0x4
+#define SCMI_BASE_DISCOVER_IMPLEMENT_VERSION 0x5
+#define SCMI_BASE_DISCOVER_LIST_PROTOCOLS    0x6
+#define SCMI_BASE_DISCOVER_AGENT             0x7
+#define SCMI_BASE_NOTIFY_ERRORS              0x8
+#define SCMI_POWER_STATE_SET                 0x4
+#define SCMI_PHYTIUM_GET_PSOSTAT             0x3
+#define SCMI_POWER_STATE_TYPE                BIT(30)
+#define SCMI_POWER_STATE_ID_MASK             (SCMI_POWER_STATE_TYPE - 1)
+/* Values reported by the E2000 SCP firmware */
+#define SCMI_BASE_VERSION                    0x00020000
+#define SCMI_BASE_IMPLEMENTATION_VERSION     0x02050000
+#define SCMI_BASE_NUM_PROTOCOLS              0
+#define SCMI_BASE_NUM_AGENTS                 2
+#define SCMI_DOORBELL_COMPLETE               BIT(31)
 
 #define SCMI_SUCCESS            0
+#define SCMI_NOT_SUPPORTED      (-1)
 #define SCMI_INVALID_PARAMETERS (-2)
+#define SCMI_NOT_FOUND          (-4)
 #define SCMI_GENERIC_ERROR      (-8)
+#define SCMI_PROTOCOL_ERROR     (-10)
 
 #define PHYTIUM_E2000_PBF_ROOT_ANCHOR       \
     (PHYTIUM_E2000_PBR_BOOT_SRAM_BASE + 0xf00)
@@ -52,11 +82,14 @@
 #define PHYTIUM_E2000_CPU_LOCK_OWNER_OFFSET 0x30
 #define PHYTIUM_E2000_CPU_ON_COMPLETE       0xabcdef98
 
-/*
- * The SDK defines AP OS status/set/clear at 0x100/0x108/0x110 within a
- * channel. PBF selects the channel at MHU offset 0x200, producing the global
- * offsets below. Writes to AP_OS_SET are the request notification.
- */
+REG32(AP_RX_STAT, 0x000)
+REG32(AP_RX_SET, 0x008)
+REG32(AP_RX_CLR, 0x010)
+REG32(AP_TX_STAT, 0x100)
+REG32(AP_TX_SET, 0x108)
+REG32(AP_TX_CLR, 0x110)
+
+/* PBF selects the AP OS channel at offset 0x200 */
 REG32(AP_OS_STAT, 0x300)
 REG32(AP_OS_SET, 0x308)
 REG32(AP_OS_CLR, 0x310)
@@ -67,8 +100,9 @@ REG32(AP_OS_CLR, 0x310)
 struct PhytiumE2000MHUState {
     SysBusDevice parent_obj;
 
+    MemoryRegion iomem;
+    qemu_irq irq;
     uint32_t regs[PHYTIUM_E2000_MHU_R_MAX];
-    RegisterInfo regs_info[PHYTIUM_E2000_MHU_R_MAX];
     uint64_t cpu_mpidrs[PHYTIUM_E2000_MHU_MAX_CPUS];
     CPUState *cpus[PHYTIUM_E2000_MHU_MAX_CPUS];
     /* Firmware-owned slot address supplied by the PBR before realization */
@@ -122,34 +156,34 @@ static bool phytium_e2000_phys_writeq(hwaddr addr, 
uint64_t value)
                                sizeof(buf)) == MEMTX_OK;
 }
 
-static uint32_t phytium_e2000_scmi_readl(hwaddr offset)
+static bool phytium_e2000_phys_write(hwaddr addr, const void *buf, size_t size)
 {
-    uint8_t buf[sizeof(uint32_t)];
-
-    address_space_read(&address_space_memory,
-                       PHYTIUM_E2000_PBF_SCMI_MBOX_BASE + offset,
-                       MEMTXATTRS_UNSPECIFIED, buf, sizeof(buf));
-    return ldl_le_p(buf);
+    return address_space_write(&address_space_memory, addr,
+                               MEMTXATTRS_UNSPECIFIED, buf, size) == MEMTX_OK;
 }
 
-static void phytium_e2000_scmi_writel(hwaddr offset, uint32_t value)
+static uint32_t phytium_e2000_scmi_readl(hwaddr base, hwaddr offset)
 {
-    uint8_t buf[sizeof(uint32_t)];
+    uint32_t value = 0;
 
-    stl_le_p(buf, value);
-    address_space_write(&address_space_memory,
-                        PHYTIUM_E2000_PBF_SCMI_MBOX_BASE + offset,
-                        MEMTXATTRS_UNSPECIFIED, buf, sizeof(buf));
+    phytium_e2000_phys_readl(base + offset, &value);
+    return value;
+}
+
+static void phytium_e2000_scmi_writel(hwaddr base, hwaddr offset,
+                                      uint32_t value)
+{
+    phytium_e2000_phys_writel(base + offset, value);
 }
 
-static void phytium_e2000_scmi_publish(uint32_t len)
+static void phytium_e2000_scmi_publish(hwaddr base, uint32_t len)
 {
-    phytium_e2000_scmi_writel(PHYTIUM_E2000_SCMI_LEN_OFFSET, len);
+    phytium_e2000_scmi_writel(base, PHYTIUM_E2000_SCMI_LEN_OFFSET, len);
     /*
      * Publish the free bit last. PBF polls this field as the ownership handoff
      * and may consume the response immediately after observing it.
      */
-    phytium_e2000_scmi_writel(PHYTIUM_E2000_SCMI_STATUS_OFFSET,
+    phytium_e2000_scmi_writel(base, PHYTIUM_E2000_SCMI_STATUS_OFFSET,
                               PHYTIUM_E2000_SCMI_STATUS_FREE);
 }
 
@@ -287,8 +321,10 @@ static uint32_t 
phytium_e2000_mhu_psostat(PhytiumE2000MHUState *s)
 static int32_t phytium_e2000_mhu_set_power_state(PhytiumE2000MHUState *s)
 {
     uint32_t domain_id = phytium_e2000_scmi_readl(
+        PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
         PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET + 4);
     uint32_t power_state = phytium_e2000_scmi_readl(
+        PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
         PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET + 8);
     uint32_t core_mask = power_state & SCMI_POWER_STATE_ID_MASK;
     bool power_on = power_state & SCMI_POWER_STATE_TYPE;
@@ -347,8 +383,10 @@ static int32_t 
phytium_e2000_mhu_set_power_state(PhytiumE2000MHUState *s)
 static void phytium_e2000_mhu_complete_scmi(PhytiumE2000MHUState *s)
 {
     uint32_t header = phytium_e2000_scmi_readl(
+        PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
         PHYTIUM_E2000_SCMI_HEADER_OFFSET);
     uint32_t len = MAX(phytium_e2000_scmi_readl(
+        PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
         PHYTIUM_E2000_SCMI_LEN_OFFSET), (uint32_t)sizeof(uint32_t));
     int32_t scmi_status = SCMI_SUCCESS;
 
@@ -359,7 +397,8 @@ static void 
phytium_e2000_mhu_complete_scmi(PhytiumE2000MHUState *s)
      */
     if (SCMI_PROTOCOL_ID(header) == SCMI_PROTOCOL_PHYTIUM &&
         SCMI_MESSAGE_ID(header) == SCMI_PHYTIUM_GET_PSOSTAT) {
-        phytium_e2000_scmi_writel(PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET + 4,
+        phytium_e2000_scmi_writel(PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
+                                  PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET + 4,
                                   phytium_e2000_mhu_psostat(s));
         len = 3 * sizeof(uint32_t);
     } else if (SCMI_PROTOCOL_ID(header) == SCMI_PROTOCOL_POWER_DOMAIN &&
@@ -368,9 +407,202 @@ static void 
phytium_e2000_mhu_complete_scmi(PhytiumE2000MHUState *s)
         len = 2 * sizeof(uint32_t);
     }
 
-    phytium_e2000_scmi_writel(PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET,
+    phytium_e2000_scmi_writel(PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
+                              PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET,
                               scmi_status);
-    phytium_e2000_scmi_publish(len);
+    phytium_e2000_scmi_publish(PHYTIUM_E2000_PBF_SCMI_MBOX_BASE, len);
+}
+
+static bool phytium_e2000_scmi_base_message_supported(uint32_t message_id)
+{
+    return message_id <= SCMI_BASE_DISCOVER_AGENT;
+}
+
+static int32_t phytium_e2000_mhu_complete_base(uint32_t message_id,
+                                               uint32_t request_len,
+                                               uint8_t response[16],
+                                               size_t *response_len)
+{
+    uint32_t parameter;
+
+    *response_len = 0;
+
+    switch (message_id) {
+    case SCMI_PROTOCOL_VERSION:
+        if (request_len != sizeof(uint32_t)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        stl_le_p(response, SCMI_BASE_VERSION);
+        *response_len = sizeof(uint32_t);
+        return SCMI_SUCCESS;
+    case SCMI_PROTOCOL_ATTRIBUTES:
+        if (request_len != sizeof(uint32_t)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        response[0] = SCMI_BASE_NUM_PROTOCOLS;
+        response[1] = SCMI_BASE_NUM_AGENTS;
+        *response_len = sizeof(uint32_t);
+        return SCMI_SUCCESS;
+    case SCMI_PROTOCOL_MESSAGE_ATTRIBUTES:
+        if (request_len != 2 * sizeof(uint32_t) ||
+            !phytium_e2000_phys_readl(
+                PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+                PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET, &parameter)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        if (!phytium_e2000_scmi_base_message_supported(parameter)) {
+            return SCMI_NOT_FOUND;
+        }
+        stl_le_p(response, 0);
+        *response_len = sizeof(uint32_t);
+        return SCMI_SUCCESS;
+    case SCMI_BASE_DISCOVER_VENDOR:
+        if (request_len != sizeof(uint32_t)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        memcpy(response, "Phytium", sizeof("Phytium"));
+        *response_len = 16;
+        return SCMI_SUCCESS;
+    case SCMI_BASE_DISCOVER_SUB_VENDOR:
+        if (request_len != sizeof(uint32_t)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        memcpy(response, "E2000", sizeof("E2000"));
+        *response_len = 16;
+        return SCMI_SUCCESS;
+    case SCMI_BASE_DISCOVER_IMPLEMENT_VERSION:
+        if (request_len != sizeof(uint32_t)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        stl_le_p(response, SCMI_BASE_IMPLEMENTATION_VERSION);
+        *response_len = sizeof(uint32_t);
+        return SCMI_SUCCESS;
+    case SCMI_BASE_DISCOVER_LIST_PROTOCOLS:
+        if (request_len != 2 * sizeof(uint32_t) ||
+            !phytium_e2000_phys_readl(
+                PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+                PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET, &parameter)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        if (parameter) {
+            return SCMI_INVALID_PARAMETERS;
+        }
+        stl_le_p(response, 0);
+        *response_len = sizeof(uint32_t);
+        return SCMI_SUCCESS;
+    case SCMI_BASE_DISCOVER_AGENT:
+        if (request_len != 2 * sizeof(uint32_t) ||
+            !phytium_e2000_phys_readl(
+                PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+                PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET, &parameter)) {
+            return SCMI_PROTOCOL_ERROR;
+        }
+        if (parameter > SCMI_BASE_NUM_AGENTS - 1) {
+            return SCMI_NOT_FOUND;
+        }
+        /*
+         * The E2000 SCP advertises Base v2.0 but returns the v1 16-byte agent
+         * name. Match this quirk because the SDK 5.10 client parses that
+         * layout rather than the v2 agent-ID-plus-name response.
+         */
+        if (parameter == 0) {
+            memcpy(response, "platform", sizeof("platform"));
+        } else {
+            memcpy(response, "OSPM", sizeof("OSPM"));
+        }
+        *response_len = 16;
+        return SCMI_SUCCESS;
+    case SCMI_BASE_NOTIFY_ERRORS:
+    default:
+        return SCMI_NOT_SUPPORTED;
+    }
+}
+
+static bool phytium_e2000_mhu_publish_os_response(uint32_t header,
+                                                  int32_t status,
+                                                  const uint8_t *response,
+                                                  size_t response_len)
+{
+    hwaddr payload = PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+                     PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET;
+
+    if (!phytium_e2000_phys_writel(payload, status) ||
+        (response_len &&
+         !phytium_e2000_phys_write(payload + sizeof(uint32_t), response,
+                                   response_len)) ||
+        !phytium_e2000_phys_writel(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_HEADER_OFFSET, header) ||
+        !phytium_e2000_phys_writel(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_LEN_OFFSET,
+            2 * sizeof(uint32_t) + response_len) ||
+        !phytium_e2000_phys_writel(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_STATUS_OFFSET,
+            PHYTIUM_E2000_SCMI_STATUS_FREE)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: failed to publish SCMI response\n");
+        return false;
+    }
+
+    return true;
+}
+
+static bool phytium_e2000_mhu_complete_os_scmi(PhytiumE2000MHUState *s)
+{
+    uint8_t response[16] = { 0 };
+    uint32_t flags;
+    uint32_t header;
+    uint32_t len;
+    size_t response_len = 0;
+    int32_t status;
+
+    if (!phytium_e2000_phys_readl(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_LEN_OFFSET, &len) ||
+        !phytium_e2000_phys_readl(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_HEADER_OFFSET, &header) ||
+        !phytium_e2000_phys_readl(
+            PHYTIUM_E2000_OS_SCMI_MBOX_BASE +
+            PHYTIUM_E2000_SCMI_FLAGS_OFFSET, &flags)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: failed to read SCMI request\n");
+        return false;
+    }
+
+    if (len < sizeof(uint32_t) || len > PHYTIUM_E2000_SCMI_MAX_FRAME_SIZE ||
+        !QEMU_IS_ALIGNED(len, sizeof(uint32_t)) ||
+        (flags & ~PHYTIUM_E2000_SCMI_FLAG_INTR_ENABLED) ||
+        SCMI_MESSAGE_TYPE(header) != SCMI_MESSAGE_TYPE_COMMAND ||
+        SCMI_RESERVED(header)) {
+        status = SCMI_PROTOCOL_ERROR;
+    } else if (SCMI_PROTOCOL_ID(header) != SCMI_PROTOCOL_BASE) {
+        status = SCMI_NOT_SUPPORTED;
+    } else {
+        status = phytium_e2000_mhu_complete_base(
+            SCMI_MESSAGE_ID(header), len, response, &response_len);
+    }
+
+    g_assert(response_len <= sizeof(response));
+    if (!phytium_e2000_mhu_publish_os_response(
+            header, status, response, response_len)) {
+        return false;
+    }
+
+    /*
+     * The vendor mailbox poll callback recognizes only BIT(31) as transmit
+     * completion. Raise RX only when the requester selected interrupts.
+     */
+    s->regs[A_AP_TX_STAT / sizeof(uint32_t)] = SCMI_DOORBELL_COMPLETE;
+    if (flags & PHYTIUM_E2000_SCMI_FLAG_INTR_ENABLED) {
+        s->regs[A_AP_RX_STAT / sizeof(uint32_t)] |=
+            SCMI_DOORBELL_COMPLETE;
+        qemu_set_irq(s->irq, 1);
+    }
+
+    return true;
 }
 
 void phytium_e2000_mhu_seed_mailbox(void)
@@ -379,41 +611,98 @@ void phytium_e2000_mhu_seed_mailbox(void)
      * PBR leaves the shared channel available before releasing PBF. Seed the
      * same ownership and success state even before the first doorbell write.
      */
-    phytium_e2000_scmi_writel(PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET,
+    phytium_e2000_scmi_writel(PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
+                              PHYTIUM_E2000_SCMI_PAYLOAD_OFFSET,
                               SCMI_SUCCESS);
-    phytium_e2000_scmi_publish(sizeof(uint32_t));
+    phytium_e2000_scmi_publish(PHYTIUM_E2000_PBF_SCMI_MBOX_BASE,
+                               sizeof(uint32_t));
 }
 
-static void phytium_e2000_mhu_doorbell_post_write(RegisterInfo *reg,
-                                                  uint64_t value)
+static void phytium_e2000_mhu_update_irq(PhytiumE2000MHUState *s)
 {
-    PhytiumE2000MHUState *s = PHYTIUM_E2000_MHU(reg->opaque);
+    qemu_set_irq(s->irq,
+                 s->regs[A_AP_RX_STAT / sizeof(uint32_t)] != 0);
+}
 
-    /*
-     * Complete requests synchronously because no separate SCP CPU executes in
-     * this model. Zero writes only update doorbell storage.
-     */
-    if (value) {
-        phytium_e2000_mhu_complete_scmi(s);
+static uint64_t phytium_e2000_mhu_read(void *opaque, hwaddr offset,
+                                       unsigned size)
+{
+    PhytiumE2000MHUState *s = PHYTIUM_E2000_MHU(opaque);
+
+    switch (offset) {
+    case A_AP_RX_STAT:
+    case A_AP_TX_STAT:
+    case A_AP_OS_STAT:
+    case A_AP_OS_SET:
+    case A_AP_OS_CLR:
+        return s->regs[offset / sizeof(uint32_t)];
+    case A_AP_RX_SET:
+    case A_AP_RX_CLR:
+    case A_AP_TX_SET:
+    case A_AP_TX_CLR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: read from write-only register "
+                      "at 0x%" HWADDR_PRIx "\n", offset);
+        return 0;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: read from unimplemented register "
+                      "at 0x%" HWADDR_PRIx "\n", offset);
+        return 0;
     }
 }
 
-static const RegisterAccessInfo phytium_e2000_mhu_regs_info[] = {
-    /*
-     * The functional transport does not model an SCP interrupt line. STAT is
-     * therefore idle, SET completes the shared-memory transaction, and CLR
-     * remains ordinary register storage for the firmware acknowledge path.
-     */
-    { .name = "AP_OS_STAT", .addr = A_AP_OS_STAT,
-      .ro = UINT32_MAX },
-    { .name = "AP_OS_SET", .addr = A_AP_OS_SET,
-      .post_write = phytium_e2000_mhu_doorbell_post_write },
-    { .name = "AP_OS_CLR", .addr = A_AP_OS_CLR },
-};
+static void phytium_e2000_mhu_write(void *opaque, hwaddr offset,
+                                    uint64_t value, unsigned size)
+{
+    PhytiumE2000MHUState *s = PHYTIUM_E2000_MHU(opaque);
+    uint32_t val = value;
+
+    switch (offset) {
+    case A_AP_RX_SET:
+        s->regs[A_AP_RX_STAT / sizeof(uint32_t)] |= val;
+        phytium_e2000_mhu_update_irq(s);
+        break;
+    case A_AP_RX_CLR:
+        s->regs[A_AP_RX_STAT / sizeof(uint32_t)] &= ~val;
+        phytium_e2000_mhu_update_irq(s);
+        break;
+    case A_AP_TX_SET:
+        if (val) {
+            s->regs[A_AP_TX_STAT / sizeof(uint32_t)] |= val;
+            phytium_e2000_mhu_complete_os_scmi(s);
+        }
+        break;
+    case A_AP_TX_CLR:
+        s->regs[A_AP_TX_STAT / sizeof(uint32_t)] &= ~val;
+        break;
+    case A_AP_OS_SET:
+        s->regs[A_AP_OS_SET / sizeof(uint32_t)] = val;
+        if (val) {
+            phytium_e2000_mhu_complete_scmi(s);
+        }
+        break;
+    case A_AP_OS_CLR:
+        s->regs[A_AP_OS_CLR / sizeof(uint32_t)] = val;
+        break;
+    case A_AP_RX_STAT:
+    case A_AP_TX_STAT:
+    case A_AP_OS_STAT:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: write to read-only register "
+                      "at 0x%" HWADDR_PRIx "\n", offset);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "phytium-e2000-mhu: write to unimplemented register "
+                      "at 0x%" HWADDR_PRIx "\n", offset);
+        break;
+    }
+}
 
 static const MemoryRegionOps phytium_e2000_mhu_ops = {
-    .read = register_read_memory,
-    .write = register_write_memory,
+    .read = phytium_e2000_mhu_read,
+    .write = phytium_e2000_mhu_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
     .valid = {
         .min_access_size = 4,
@@ -425,12 +714,12 @@ static const MemoryRegionOps phytium_e2000_mhu_ops = {
 static void phytium_e2000_mhu_reset(DeviceState *dev)
 {
     PhytiumE2000MHUState *s = PHYTIUM_E2000_MHU(dev);
-    int i;
 
-    for (i = 0; i < ARRAY_SIZE(phytium_e2000_mhu_regs_info); i++) {
-        register_reset(&s->regs_info[
-            phytium_e2000_mhu_regs_info[i].addr / sizeof(uint32_t)]);
-    }
+    memset(s->regs, 0, sizeof(s->regs));
+    phytium_e2000_mhu_update_irq(s);
+    phytium_e2000_scmi_writel(PHYTIUM_E2000_OS_SCMI_MBOX_BASE,
+                              PHYTIUM_E2000_SCMI_STATUS_OFFSET,
+                              PHYTIUM_E2000_SCMI_STATUS_FREE);
 }
 
 void phytium_e2000_mhu_connect_cpu(PhytiumE2000MHUState *s,
@@ -466,13 +755,12 @@ void 
phytium_e2000_mhu_set_secondary_vector_slot(PhytiumE2000MHUState *s,
 static void phytium_e2000_mhu_init(Object *obj)
 {
     PhytiumE2000MHUState *s = PHYTIUM_E2000_MHU(obj);
-    RegisterInfoArray *reg_array;
 
-    reg_array = register_init_block32(
-        DEVICE(obj), phytium_e2000_mhu_regs_info,
-        ARRAY_SIZE(phytium_e2000_mhu_regs_info), s->regs_info, s->regs,
-        &phytium_e2000_mhu_ops, false, PHYTIUM_E2000_MHU_MMIO_SIZE);
-    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &reg_array->mem);
+    memory_region_init_io(&s->iomem, obj, &phytium_e2000_mhu_ops, s,
+                          TYPE_PHYTIUM_E2000_MHU,
+                          PHYTIUM_E2000_MHU_MMIO_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
 }
 
 static void phytium_e2000_mhu_finalize(Object *obj)
@@ -485,10 +773,19 @@ static void phytium_e2000_mhu_finalize(Object *obj)
     }
 }
 
+static int phytium_e2000_mhu_post_load(void *opaque, int version_id)
+{
+    PhytiumE2000MHUState *s = opaque;
+
+    phytium_e2000_mhu_update_irq(s);
+    return 0;
+}
+
 static const VMStateDescription phytium_e2000_mhu_vmsd = {
     .name = TYPE_PHYTIUM_E2000_MHU,
     .version_id = 1,
     .minimum_version_id = 1,
+    .post_load = phytium_e2000_mhu_post_load,
     .fields = (const VMStateField[]) {
         VMSTATE_UINT32_ARRAY(regs, PhytiumE2000MHUState,
                              PHYTIUM_E2000_MHU_R_MAX),
-- 
2.53.0


Reply via email to