Add a simulation of the device power service group using code from
librpmi [1] and adapted.

[1]: 
https://github.com/riscv-software-src/librpmi/blob/3f3c4ca47339a8795432e260f17e1fc5091c9a3a/lib/rpmi_service_group_device_power.c

Signed-off-by: Charles Perry <[email protected]>
---
 arch/sandbox/dts/test.dts                  |  11 ++
 configs/sandbox_defconfig                  |   1 +
 drivers/firmware/rpmi/Makefile             |   2 +-
 drivers/firmware/rpmi/rpmi-sandbox-power.c | 174 +++++++++++++++++++++
 drivers/firmware/rpmi/rpmi-sandbox.c       |   2 +
 drivers/firmware/rpmi/rpmi-sandbox.h       |   1 +
 test/dm/rpmi.c                             |  21 +++
 7 files changed, 211 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-power.c

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e5d6f398fb35..7e25053851f4 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -886,6 +886,12 @@
                        #clock-cells = <1>;
                        clocks = <&rpmi_clock 0>, <&rpmi_clock 1>, <&rpmi_clock 
2>, <&rpmi_clock 3>;
                };
+
+               rpmi_dpwr: rpmi-power-domain {
+                       compatible = "riscv,rpmi-device-power";
+                       mboxes = <&rpmi 0x9>;
+                       #power-domain-cells = <1>;
+               };
        };
 
        fpga {
@@ -1927,6 +1933,11 @@
                regul1-supply = <&regul1_scmi>;
        };
 
+       rpmi-power-domain-test {
+               compatible = "sandbox,power-domain-test";
+               power-domains = <&rpmi_dpwr 1>;
+       };
+
        pinctrl {
                compatible = "sandbox,pinctrl";
 
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index f1637e1ff09c..59967bd2ee0f 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -289,6 +289,7 @@ CONFIG_PINCONF=y
 CONFIG_PINCTRL_SANDBOX=y
 CONFIG_PINCTRL_SINGLE=y
 CONFIG_POWER_DOMAIN=y
+CONFIG_RPMI_POWER_DOMAIN=y
 CONFIG_SANDBOX_POWER_DOMAIN=y
 CONFIG_SCMI_POWER_DOMAIN=y
 CONFIG_DM_PMIC=y
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index 150e98a72df9..c73ad92baec1 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1,4 +1,4 @@
 obj-y  += rpmi-uclass.o
 obj-$(CONFIG_RPMI_SBI_MPXY) += rpmi-sbi-mpxy.o
 obj-$(CONFIG_RPMI_SHMEM) += rpmi-shmem.o
-obj-$(CONFIG_SANDBOX)  += rpmi-sandbox.o rpmi-sandbox-clock.o
+obj-$(CONFIG_SANDBOX)  += rpmi-sandbox.o rpmi-sandbox-clock.o 
rpmi-sandbox-power.o
diff --git a/drivers/firmware/rpmi/rpmi-sandbox-power.c 
b/drivers/firmware/rpmi/rpmi-sandbox-power.c
new file mode 100644
index 000000000000..aba0ecdda0a6
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox-power.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <rpmi.h>
+#include "rpmi-sandbox.h"
+
+struct rpmi_dpwr {
+       const char *name;
+       u32 trans_latency;
+       enum rpmi_dpwr_state state;
+};
+
+struct rpmi_dpwr dpwr_simulation[] = {
+       { "DPWR0", 0, RPMI_DPWR_STATE_ON },
+       { "DPWR1", 0, RPMI_DPWR_STATE_OFF },
+};
+
+static u32 dpwr_count = ARRAY_SIZE(dpwr_simulation);
+
+static struct rpmi_dpwr *rpmi_get_dpwr(u32 dpwrid)
+{
+       if (dpwrid >= ARRAY_SIZE(dpwr_simulation))
+               return NULL;
+
+       return &dpwr_simulation[dpwrid];
+}
+
+static enum rpmi_error_codes
+rpmi_dpwr_get_num_domains(struct rpmi_transport *trans, u16 request_datalen,
+                         const u8 *request_data, u16 *response_datalen,
+                         u8 *response_data)
+{
+       u32 *resp = (void *)response_data;
+
+       resp[1] = cpu_to_le32(dpwr_count);
+       resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+       *response_datalen = 2 * sizeof(*resp);
+
+       return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_dpwr_get_attributes(struct rpmi_transport *trans, u16 request_datalen,
+                        const u8 *request_data, u16 *response_datalen,
+                        u8 *response_data)
+{
+       u16 resp_dlen = sizeof(u32);
+       u32 *resp = (void *)response_data;
+       struct rpmi_dpwr *dpwr;
+
+       u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+       dpwr = rpmi_get_dpwr(dpwrid);
+       if (!dpwr) {
+               resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+               goto done;
+       }
+
+       if (dpwr->name)
+               strlcpy((char *)&resp[3], dpwr->name, RPMI_DPWR_NAME_LEN);
+
+       resp[2] = cpu_to_le32(dpwr->trans_latency);
+       resp[1] = cpu_to_le32((u32)0x0);
+       resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+       resp_dlen = 3 * sizeof(*resp) + RPMI_DPWR_NAME_LEN;
+
+done:
+       *response_datalen = resp_dlen;
+
+       return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_dpwr_get_state(struct rpmi_transport *trans,
+                                                u16 request_datalen,
+                                                const u8 *request_data,
+                                                u16 *response_datalen,
+                                                u8 *response_data)
+{
+       u16 resp_dlen = sizeof(u32);
+       u32 *resp = (void *)response_data;
+       u32 dpwr_state = 0;
+       struct rpmi_dpwr *dpwr;
+
+       u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+       dpwr = rpmi_get_dpwr(dpwrid);
+       if (!dpwr) {
+               resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+               goto done;
+       }
+
+       dpwr_state = dpwr->state;
+
+       resp[1] = cpu_to_le32((u32)dpwr_state);
+       resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+       resp_dlen = 2 * sizeof(*resp);
+
+done:
+       *response_datalen = resp_dlen;
+       return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_dpwr_set_state(struct rpmi_transport *trans,
+                                                u16 request_datalen,
+                                                const u8 *request_data,
+                                                u16 *response_datalen,
+                                                u8 *response_data)
+{
+       u16 resp_dlen = sizeof(u32);
+       u32 *resp = (void *)response_data;
+       struct rpmi_dpwr *dpwr;
+
+       u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+       u32 dpwr_state = cpu_to_le32(((const u32 *)request_data)[1]);
+
+       dpwr = rpmi_get_dpwr(dpwrid);
+       if (!dpwr) {
+               resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+               goto done;
+       }
+
+       dpwr->state = dpwr_state;
+
+       resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+       resp_dlen = sizeof(*resp);
+
+done:
+       *response_datalen = resp_dlen;
+       return RPMI_SUCCESS;
+}
+
+static struct rpmi_service rpmi_dpwr_services[RPMI_DPWR_SRV_ID_MAX] = {
+       [RPMI_DPWR_SRV_GET_NUM_DOMAINS] = {
+               .service_id = RPMI_DPWR_SRV_GET_NUM_DOMAINS,
+               .min_a2p_request_datalen = 0,
+               .process_a2p_request = rpmi_dpwr_get_num_domains,
+       },
+       [RPMI_DPWR_SRV_GET_ATTRIBUTES] = {
+               .service_id = RPMI_DPWR_SRV_GET_ATTRIBUTES,
+               .min_a2p_request_datalen = 4,
+               .process_a2p_request = rpmi_dpwr_get_attributes,
+       },
+       [RPMI_DPWR_SRV_SET_DPWR_STATE] = {
+               .service_id = RPMI_DPWR_SRV_SET_DPWR_STATE,
+               .min_a2p_request_datalen = 8,
+               .process_a2p_request = rpmi_dpwr_set_state,
+       },
+       [RPMI_DPWR_SRV_GET_DPWR_STATE] = {
+               .service_id = RPMI_DPWR_SRV_GET_DPWR_STATE,
+               .min_a2p_request_datalen = 4,
+               .process_a2p_request = rpmi_dpwr_get_state,
+       },
+};
+
+const struct rpmi_service_group rpmi_sandbox_dpwr_sg = {
+       .name = "dpwr",
+       .max_service_id = RPMI_DPWR_SRV_GET_DPWR_STATE,
+       .servicegroup_version = RPMI_MKVER(1, 0),
+       .services = rpmi_dpwr_services,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.c 
b/drivers/firmware/rpmi/rpmi-sandbox.c
index 5597c27067eb..c34c61da5d9e 100644
--- a/drivers/firmware/rpmi/rpmi-sandbox.c
+++ b/drivers/firmware/rpmi/rpmi-sandbox.c
@@ -29,6 +29,8 @@ const struct rpmi_service_group *rpmi_sandbox_sg(u32 
servicegroup_id)
        switch (servicegroup_id) {
        case RPMI_SRVGRP_CLOCK:
                return &rpmi_sandbox_clock_sg;
+       case RPMI_SRVGRP_DEVICE_POWER:
+               return &rpmi_sandbox_dpwr_sg;
        default:
                return NULL;
        }
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.h 
b/drivers/firmware/rpmi/rpmi-sandbox.h
index 132d1e9e8be6..7dea5035929e 100644
--- a/drivers/firmware/rpmi/rpmi-sandbox.h
+++ b/drivers/firmware/rpmi/rpmi-sandbox.h
@@ -43,5 +43,6 @@ struct rpmi_service_group {
 };
 
 extern const struct rpmi_service_group rpmi_sandbox_clock_sg;
+extern const struct rpmi_service_group rpmi_sandbox_dpwr_sg;
 
 #endif
diff --git a/test/dm/rpmi.c b/test/dm/rpmi.c
index 1ea29780a480..e886b9ab2da4 100644
--- a/test/dm/rpmi.c
+++ b/test/dm/rpmi.c
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <dm/device-internal.h>
 #include <dm/test.h>
+#include <asm/power-domain.h>
 #include <test/ut.h>
 
 static int dm_test_rpmi_clock(struct unit_test_state *uts)
@@ -46,3 +47,23 @@ static int dm_test_rpmi_clock(struct unit_test_state *uts)
        return 0;
 }
 DM_TEST(dm_test_rpmi_clock, UTF_SCAN_FDT);
+
+static int dm_test_rpmi_dpwr(struct unit_test_state *uts)
+{
+       struct udevice *rpmi_dev, *dpwr_dev, *dpwr_consumer;
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_RPMI, "rpmi", &rpmi_dev));
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+                                             "rpmi-power-domain", &dpwr_dev));
+
+       ut_assertok(uclass_get_device_by_name(
+               UCLASS_MISC, "rpmi-power-domain-test", &dpwr_consumer));
+
+       ut_assertok(sandbox_power_domain_test_get(dpwr_consumer));
+       ut_assertok(sandbox_power_domain_test_on(dpwr_consumer));
+       ut_assertok(sandbox_power_domain_test_off(dpwr_consumer));
+
+       return 0;
+}
+DM_TEST(dm_test_rpmi_dpwr, UTF_SCAN_FDT);
-- 
2.47.3

Reply via email to