hw/riscv/meson.build | 1 +
hw/riscv/rpmi-fdt.c | 103 ++++++++++++++++++++++++++++++++++++
include/hw/riscv/rpmi-fdt.h | 30 +++++++++++
3 files changed, 134 insertions(+)
create mode 100644 hw/riscv/rpmi-fdt.c
create mode 100644 include/hw/riscv/rpmi-fdt.h
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index b70a054579..529ec273fd 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -3,6 +3,7 @@ riscv_ss.add(files('boot.c'))
riscv_ss.add(files('fdt-common.c'))
riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
riscv_ss.add(files('riscv_hart.c'))
+riscv_ss.add(files('rpmi-fdt.c'))
riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c'))
riscv_ss.add(when: 'CONFIG_SHAKTI_C', if_true: files('shakti_c.c'))
diff --git a/hw/riscv/rpmi-fdt.c b/hw/riscv/rpmi-fdt.c
new file mode 100644
index 0000000000..b2b3daea1c
--- /dev/null
+++ b/hw/riscv/rpmi-fdt.c
@@ -0,0 +1,103 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * RISC-V RPMI device-tree helpers
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ * Author:
+ * Subrahmanya Lingappa <[email protected]>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "hw/riscv/rpmi-fdt.h"
+#include "hw/misc/riscv_rpmi.h"
+#include "system/device_tree.h"
+
+void riscv_rpmi_fdt_add_mbox(void *fdt,
+ const RiscvRpmiFdtMboxConfig *cfg,
+ uint32_t *phandle,
+ uint32_t *mbox_handle)
+{
+ g_autofree char *name = NULL;
+ hwaddr a2p_req_base, p2a_ack_base, p2a_req_base, a2p_ack_base;
+ static const char * const regnames_all[RPMI_ALL_NUM_REGS] = {
+ "a2p-req", "p2a-ack", "p2a-req", "a2p-ack", "a2p-doorbell"
+ };
+ static const char * const regnames_a2p[RPMI_A2P_NUM_REGS] = {
+ "a2p-req", "p2a-ack", "a2p-doorbell"
+ };
+
+ a2p_req_base = cfg->shmem_base;
+ p2a_ack_base = a2p_req_base + cfg->a2p_req_size;
+ p2a_req_base = p2a_ack_base + cfg->a2p_req_size;
+ a2p_ack_base = p2a_req_base + cfg->p2a_req_size;
+
+ *mbox_handle = (*phandle)++;
+ name = g_strdup_printf("/soc/mailbox@%" HWADDR_PRIx, cfg->shmem_base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,rpmi-shmem-mbox");
+ qemu_fdt_setprop_cell(fdt, name, "riscv,slot-size", RPMI_QUEUE_SLOT_SIZE);
+ qemu_fdt_setprop_cell(fdt, name, "#mbox-cells", 1);
+
+ if (cfg->p2a_req_size) {
+ qemu_fdt_setprop_string_array(fdt, name, "reg-names",
+ (char **)®names_all,
+ ARRAY_SIZE(regnames_all));
+ qemu_fdt_setprop_cells(fdt, name, "reg",
+ (uint32_t)(a2p_req_base >> 32), (uint32_t)a2p_req_base,
+ 0x0, cfg->a2p_req_size,
+ (uint32_t)(p2a_ack_base >> 32), (uint32_t)p2a_ack_base,
+ 0x0, cfg->a2p_req_size,
+ (uint32_t)(p2a_req_base >> 32), (uint32_t)p2a_req_base,
+ 0x0, cfg->p2a_req_size,
+ (uint32_t)(a2p_ack_base >> 32), (uint32_t)a2p_ack_base,
+ 0x0, cfg->p2a_req_size,
+ (uint32_t)(cfg->doorbell_base >> 32), (uint32_t)cfg->doorbell_base,
+ 0x0, cfg->doorbell_size);
+ } else {
+ qemu_fdt_setprop_string_array(fdt, name, "reg-names",
+ (char **)®names_a2p,
+ ARRAY_SIZE(regnames_a2p));
+ qemu_fdt_setprop_cells(fdt, name, "reg",
+ (uint32_t)(a2p_req_base >> 32), (uint32_t)a2p_req_base,
+ 0x0, cfg->a2p_req_size,
+ (uint32_t)(p2a_ack_base >> 32), (uint32_t)p2a_ack_base,
+ 0x0, cfg->a2p_req_size,
+ (uint32_t)(cfg->doorbell_base >> 32), (uint32_t)cfg->doorbell_base,
+ 0x0, cfg->doorbell_size);
+ }
+
+ qemu_fdt_setprop_cells(fdt, name, "phandle", *mbox_handle);
+}
+
+void riscv_rpmi_fdt_add_service(void *fdt, hwaddr shmem_base,
+ const char *node_name,
+ const char *compatible,
+ uint32_t mbox_handle,
+ uint32_t service_group,
+ bool has_mpxy_channel,
+ uint32_t mpxy_channel)
+{
+ g_autofree char *name = g_strdup_printf("/soc/mailbox@%" HWADDR_PRIx "/%s",
+ shmem_base, node_name);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", compatible);
+ qemu_fdt_setprop_cells(fdt, name, "mboxes", mbox_handle, service_group);
+ if (has_mpxy_channel) {
+ qemu_fdt_setprop_cell(fdt, name, "riscv,sbi-mpxy-channel-id",
+ mpxy_channel);
+ }
+}
+
+void riscv_rpmi_fdt_add_service_node(void *fdt, hwaddr shmem_base,
+ const RiscvRpmiServiceConfig *service,
+ uint32_t mbox_handle)
+{
+ riscv_rpmi_fdt_add_service(fdt, shmem_base, service->node_name,
+ service->compatible, mbox_handle,
+ service->service_group,
+ service->has_mpxy_channel,
+ service->mpxy_channel);
+}
diff --git a/include/hw/riscv/rpmi-fdt.h b/include/hw/riscv/rpmi-fdt.h
new file mode 100644
index 0000000000..f053ae5d57
--- /dev/null
+++ b/include/hw/riscv/rpmi-fdt.h
@@ -0,0 +1,30 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * RISC-V RPMI device-tree helpers
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ * Author:
+ * Subrahmanya Lingappa <[email protected]>
+ */
+
+#ifndef HW_RISCV_RPMI_FDT_H
+#define HW_RISCV_RPMI_FDT_H
+
+#include "exec/hwaddr.h"
+#include "hw/misc/riscv_rpmi.h"
+
+typedef struct RiscvRpmiFdtMboxConfig {
+ hwaddr shmem_base;
+ hwaddr doorbell_base;
+ uint32_t a2p_req_size;
+ uint32_t p2a_req_size;
+ uint32_t doorbell_size;
+} RiscvRpmiFdtMboxConfig;
+
+void riscv_rpmi_fdt_add_mbox(void *fdt,
+ const RiscvRpmiFdtMboxConfig *cfg,
+ uint32_t *phandle,
+ uint32_t *mbox_handle);
+
+#endif