From: Bin Meng <bin.meng@processmission.com>
To: QEMU <qemu-devel@nongnu.org>
Cc: Peter Maydell <peter.maydell@linaro.org>, qemu-arm@nongnu.org
Subject: [PATCH v2 28/32] hw/arm: Add Phytium E2000 Linux SCMI channel
Date: Tue, 8 Sep 2026 18:41:37 +0800 [thread overview]
Message-ID: <20260908104159.1621764-29-bin.meng@processmission.com> (raw)
In-Reply-To: <20260908104159.1621764-1-bin.meng@processmission.com>
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 <bin.meng@processmission.com>
---
Changes in v2:
- Keep the Linux SCMI channel inside the E2000 SoC
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 bc3491ca27..cb94318430 100644
--- a/hw/arm/phytium_e2000.c
+++ b/hw/arm/phytium_e2000.c
@@ -143,6 +143,8 @@ phytium_e2000_gem_irqmap[PHYTIUM_E2000_NUM_GEMS][MAX_PRIORITY_QUEUES] = {
[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,
@@ -677,6 +679,8 @@ static void phytium_e2000_create_mhu(PhytiumE2000SoCState *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(PhytiumE2000SoCState *s)
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, ¶meter)) {
+ 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, ¶meter)) {
+ 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, ¶meter)) {
+ 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), ®_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
next prev parent reply other threads:[~2026-09-08 10:44 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-08 10:41 [PATCH v2 00/32] hw/arm: Add Phytium E2000Q SoC and board support Bin Meng
2026-09-08 10:41 ` [PATCH v2 01/32] hw/arm: Add Phytium E2000 SoC and Phytium Pi machine Bin Meng
2026-09-08 10:41 ` [PATCH v2 02/32] hw/arm: phytium: Add Phytium E2000 PCIe host Bin Meng
2026-09-08 10:41 ` [PATCH v2 04/32] hw/sd: Add Phytium E2000 MCI controller Bin Meng
2026-09-08 10:41 ` [PATCH v2 05/32] hw/arm: phytium: Connect Phytium E2000 MCI controllers Bin Meng
2026-09-08 10:41 ` [PATCH v2 07/32] hw/arm: phytium: Connect Phytium E2000 GEM controllers Bin Meng
2026-09-08 10:41 ` [PATCH v2 08/32] hw/misc: Add Phytium E2000 DDR controller Bin Meng
2026-09-08 10:41 ` [PATCH v2 09/32] hw/arm: phytium: Connect the " Bin Meng
2026-09-08 10:41 ` [PATCH v2 10/32] hw/misc: Add Phytium E2000 MHU doorbell Bin Meng
2026-09-08 10:41 ` [PATCH v2 11/32] hw/arm: phytium: Connect the Phytium E2000 MHU Bin Meng
2026-09-08 10:41 ` [PATCH v2 12/32] hw/ssi: Add Phytium E2000 QSPI controller Bin Meng
2026-09-08 10:41 ` [PATCH v2 13/32] hw/arm: phytium: Connect the " Bin Meng
2026-09-08 10:41 ` [PATCH v2 14/32] hw/misc: Add Phytium E2000 PBR model Bin Meng
2026-09-08 10:41 ` [PATCH v2 15/32] hw/arm: phytium: Integrate the Phytium E2000 PBR Bin Meng
2026-09-08 10:41 ` [PATCH v2 16/32] hw/arm: phytium: Add Phytium E2000 control region placeholders Bin Meng
2026-09-08 10:41 ` [PATCH v2 17/32] hw/misc: Support Phytium E2000 SCMI CPU power control Bin Meng
2026-09-08 10:41 ` [PATCH v2 18/32] hw/arm: phytium: Select the Phytium E2000 PBR boot medium Bin Meng
2026-09-08 10:41 ` [PATCH v2 19/32] hw/arm: phytium: Connect the Phytium E2000 I2C controller Bin Meng
2026-09-08 10:41 ` [PATCH v2 20/32] hw/arm: phytium: Add Phytium E2000 xHCI controllers Bin Meng
2026-09-08 10:41 ` [PATCH v2 21/32] hw/misc: Model the Phytium E2000 random generator Bin Meng
2026-09-08 10:41 ` [PATCH v2 22/32] hw/arm: phytium: Connect " Bin Meng
2026-09-08 10:41 ` [PATCH v2 23/32] hw/arm: phytium: Support Phytium E2000 direct Linux boot Bin Meng
2026-09-08 10:41 ` [PATCH v2 24/32] hw/arm: phytium: Add Phytium E2000Q COMe machine Bin Meng
2026-09-08 10:41 ` [PATCH v2 26/32] hw/arm: phytium: Connect the Phytium E2000Q COMe QSPI flash Bin Meng
2026-09-08 10:41 ` [PATCH v2 27/32] hw/arm: phytium: Add Phytium E2000 AHCI controllers Bin Meng
2026-09-08 10:41 ` Bin Meng [this message]
2026-09-08 10:41 ` [PATCH v2 29/32] hw/arm: phytium: Connect the Phytium E2000 SMMUv3 Bin Meng
2026-09-08 10:41 ` [PATCH v2 30/32] docs/system/arm: Document Phytium E2000 machines Bin Meng
2026-09-08 10:41 ` [PATCH v2 31/32] tests/functional/aarch64: Add Phytium Pi boot tests Bin Meng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260908104159.1621764-29-bin.meng@processmission.com \
--to=bin.meng@processmission.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).