U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Charles Perry <charles.perry@microchip.com>
To: <u-boot@lists.denx.de>
Cc: Rahul Pathak <rahul@summations.net>,
	Anup Patel <anup@brainfault.org>,
	Charles Perry <charles.perry@microchip.com>,
	Tom Rini <trini@konsulko.com>, Simon Glass <sjg@chromium.org>,
	Neil Armstrong <neil.armstrong@linaro.org>,
	Peng Fan <peng.fan@nxp.com>,
	Kory Maincent <kory.maincent@bootlin.com>, "Yao Zi" <me@ziyao.cc>,
	Kuan-Wei Chiu <visitorckw@gmail.com>,
	Raymond Mao <raymond.mao@riscstar.com>,
	Quentin Schulz <quentin.schulz@cherry.de>,
	"Stefan Roese" <stefan.roese@mailbox.org>,
	Jerome Forissier <jerome.forissier@arm.com>,
	Philip Molloy <philip.molloy@analog.com>,
	"Mattijs Korpershoek" <mkorpershoek@kernel.org>,
	Marek Vasut <marek.vasut@mailbox.org>,
	Heiko Schocher <hs@nabladev.com>,
	Rasmus Villemoes <ravi@prevas.dk>,
	"Martin Schwan" <m.schwan@phytec.de>,
	Lucien.Jheng <lucienzx159@gmail.com>,
	"Michal Simek" <michal.simek@amd.com>,
	Pieter Van Trappen <pieter.van.trappen@cern.ch>,
	Dinesh Maniyam <dinesh.maniyam@altera.com>,
	Mateusz Furdyna <mateusz.furdyna@nokia.com>,
	Svyatoslav Ryhel <clamor95@gmail.com>
Subject: [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver
Date: Fri, 26 Jun 2026 13:15:47 -0700	[thread overview]
Message-ID: <20260626201613.1035208-7-charles.perry@microchip.com> (raw)
In-Reply-To: <20260626201613.1035208-1-charles.perry@microchip.com>

Create a new transport layer that sends the messages to a simulated RPMI
firmware.

For now, this only contains a simulation of the clock service group
which is implemented using code from librpmi [1] and adapted.

Also add a test in test/dm that makes use of the RPMI clock driver.

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

Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
 MAINTAINERS                                |   1 +
 arch/sandbox/dts/test.dts                  |  12 +
 configs/sandbox_defconfig                  |   2 +
 drivers/firmware/rpmi/Kconfig              |   3 +-
 drivers/firmware/rpmi/Makefile             |   1 +
 drivers/firmware/rpmi/rpmi-sandbox-clock.c | 446 +++++++++++++++++++++
 drivers/firmware/rpmi/rpmi-sandbox.c       | 156 +++++++
 drivers/firmware/rpmi/rpmi-sandbox.h       |  47 +++
 test/dm/Makefile                           |   1 +
 test/dm/rpmi.c                             |  48 +++
 10 files changed, 716 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-clock.c
 create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.c
 create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.h
 create mode 100644 test/dm/rpmi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d2b18d9b13db..96fee47ade6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1696,6 +1696,7 @@ F:	drivers/clk/clk_rpmi.c
 F:	drivers/firmware/rpmi/
 F:	drivers/power/domain/rpmi-power-domain.c
 F:	include/rpmi*
+F:	test/dm/rpmi.c
 
 SANDBOX
 M:	Simon Glass <sjg@chromium.org>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 0887de4333b0..e5d6f398fb35 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -874,6 +874,18 @@
 		sm: secure-monitor {
 			compatible = "sandbox,sm";
 		};
+
+		rpmi: rpmi {
+			compatible = "sandbox,rpmi-transport";
+			#mbox-cells = <1>;
+		};
+
+		rpmi_clock: rpmi-clock {
+			compatible = "riscv,rpmi-clock";
+			mboxes = <&rpmi 0x8>;
+			#clock-cells = <1>;
+			clocks = <&rpmi_clock 0>, <&rpmi_clock 1>, <&rpmi_clock 2>, <&rpmi_clock 3>;
+		};
 	};
 
 	fpga {
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ba800f7d19da..f1637e1ff09c 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -191,6 +191,7 @@ CONFIG_BUTTON_GPIO=y
 CONFIG_CLK=y
 CONFIG_CLK_K210=y
 CONFIG_CLK_K210_SET_RATE=y
+CONFIG_CLK_RPMI=y
 CONFIG_SANDBOX_CLK_CCF=y
 CONFIG_CLK_SCMI=y
 CONFIG_CPU=y
@@ -207,6 +208,7 @@ CONFIG_FASTBOOT_FLASH=y
 CONFIG_FASTBOOT_FLASH_MMC_DEV=0
 CONFIG_ARM_FFA_TRANSPORT=y
 CONFIG_SCMI_FIRMWARE=y
+CONFIG_RPMI_FIRMWARE=y
 CONFIG_FPGA_ALTERA=y
 CONFIG_FPGA_STRATIX_II=y
 CONFIG_FPGA_STRATIX_V=y
diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
index 421a1fa9deba..b462d3693987 100644
--- a/drivers/firmware/rpmi/Kconfig
+++ b/drivers/firmware/rpmi/Kconfig
@@ -1,6 +1,7 @@
 config RPMI_FIRMWARE
 	bool "Enable RPMI support"
-	depends on RISCV && DM && OF_CONTROL
+	depends on RISCV || SANDBOX
+	depends on DM && OF_CONTROL
 	select FIRMWARE
 	help
 	  RISC-V Platform Management Interface (RPMI) defines an OS-agnostic
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index 4f4bcb929db4..150e98a72df9 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1,3 +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
diff --git a/drivers/firmware/rpmi/rpmi-sandbox-clock.c b/drivers/firmware/rpmi/rpmi-sandbox-clock.c
new file mode 100644
index 000000000000..fa205d067b24
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox-clock.c
@@ -0,0 +1,446 @@
+// 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_clock {
+	enum rpmi_clock_type type;
+	const char *name;
+	u32 rate_count;
+	const u64 *rate_array;
+	u32 transition_latency;
+	enum rpmi_clock_state state;
+	u64 rate;
+};
+
+const u64 discrete_rates[] = { 20000000, 333333333, 500000000, 1000000000 };
+
+const u64 linear_rates[] = { 50000000, 100000000, 1000000 };
+
+#define DISCRETE_CLOCK(name, current_state, current_rate) \
+	{ RPMI_CLK_TYPE_DISCRETE,                         \
+	  name,                                           \
+	  ARRAY_SIZE(discrete_rates),                     \
+	  discrete_rates,                                 \
+	  0,                                              \
+	  current_state,                                  \
+	  current_rate }
+
+#define LINEAR_CLOCK(name, current_state, current_rate)         \
+	{ RPMI_CLK_TYPE_LINEAR, name, ARRAY_SIZE(linear_rates), \
+	  linear_rates,		0,    current_state,            \
+	  current_rate }
+
+struct rpmi_clock clock_simulation[] = {
+	DISCRETE_CLOCK("CLK0", RPMI_CLK_STATE_DISABLED, discrete_rates[0]),
+	DISCRETE_CLOCK("CLK1", RPMI_CLK_STATE_DISABLED, discrete_rates[1]),
+	DISCRETE_CLOCK("CLK2", RPMI_CLK_STATE_ENABLED, discrete_rates[2]),
+	LINEAR_CLOCK("CLK3", RPMI_CLK_STATE_ENABLED, linear_rates[0]),
+};
+
+static u32 clock_count = ARRAY_SIZE(clock_simulation);
+
+static struct rpmi_clock *rpmi_get_clock(u32 clkid)
+{
+	if (clkid >= clock_count)
+		return NULL;
+
+	return &clock_simulation[clkid];
+}
+
+static enum rpmi_error_codes
+rpmi_clock_set_rate(u32 clkid, enum rpmi_clock_rate_match match, u64 rate)
+{
+	struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+	if (!clk)
+		return RPMI_ERR_INVALID_PARAM;
+
+	clk->rate = rate;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_set_state(u32 clkid,
+						  enum rpmi_clock_state state)
+{
+	struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+	if (!clk)
+		return RPMI_ERR_INVALID_PARAM;
+
+	clk->state = state;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_get_state(u32 clkid,
+						  enum rpmi_clock_state *state)
+{
+	struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+	if (!clk || !state)
+		return RPMI_ERR_INVALID_PARAM;
+
+	*state = clk->state;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_get_rate(u32 clkid, u64 *rate)
+{
+	struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+	if (!clk || !rate)
+		return RPMI_ERR_INVALID_PARAM;
+
+	*rate = clk->rate;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_num_clocks(struct rpmi_transport *trans, u16 request_datalen,
+			     const u8 *request_data, u16 *response_datalen,
+			     u8 *response_data)
+{
+	u32 *resp = (void *)response_data;
+
+	resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+	resp[1] = cpu_to_le32(clock_count);
+
+	*response_datalen = 2 * sizeof(*resp);
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_attributes(struct rpmi_transport *trans, u16 request_datalen,
+			     const u8 *request_data, u16 *response_datalen,
+			     u8 *response_data)
+{
+	struct rpmi_clock *clk;
+	u16 resp_dlen;
+	u32 flags = 0;
+
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	clk = rpmi_get_clock(clkid);
+	if (!clk) {
+		resp_dlen = sizeof(*resp);
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	/* encode CLOCK_FORMAT */
+	if (clk->type == RPMI_CLK_TYPE_LINEAR)
+		flags |= 1;
+
+	resp[3] = cpu_to_le32(clk->transition_latency);
+	resp[2] = cpu_to_le32(clk->rate_count);
+	resp[1] = cpu_to_le32(flags);
+	resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+	if (clk->name)
+		strlcpy((char *)&resp[4], clk->name, RPMI_CLK_NAME_LEN);
+
+	resp_dlen = 8 * sizeof(*resp);
+
+done:
+	*response_datalen = resp_dlen;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_supp_rates(struct rpmi_transport *trans, u16 request_datalen,
+			     const u8 *request_data, u16 *response_datalen,
+			     u8 *response_data)
+{
+	u32 i = 0, j = 0;
+	u32 rate_count;
+	u32 resp_dlen = 0, clk_rate_idx = 0;
+	u32 max_rates, remaining = 0, returned = 0;
+	const u64 *rate_array;
+	struct rpmi_clock *clk;
+
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	clk = rpmi_get_clock(clkid);
+	if (clk) {
+		resp_dlen = sizeof(*resp);
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	rate_count = clk->rate_count;
+	rate_array = clk->rate_array;
+	if (!rate_count || !rate_array) {
+		resp_dlen = sizeof(*resp);
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_NOTSUPP);
+		goto done;
+	}
+
+	clk_rate_idx = cpu_to_le32(((const u32 *)request_data)[1]);
+
+	if (clk->type == RPMI_CLK_TYPE_LINEAR) {
+		/* max, min and step */
+		for (i = 0; i < 3; i++) {
+			resp[4 + 2 * i] = cpu_to_le32((u32)(rate_array[i]));
+			resp[5 + 2 * i] =
+				cpu_to_le32((u32)(rate_array[i] >> 32));
+		}
+		remaining = 0;
+		returned = 3;
+	} else if (clk->type == RPMI_CLK_TYPE_DISCRETE) {
+		if (clk_rate_idx > rate_count) {
+			resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+			resp_dlen = sizeof(*resp);
+			goto done;
+		}
+
+		/* max rates a rpmi message can accommodate */
+		max_rates = (trans->msg_data_size - (4 * sizeof(*resp))) /
+			    sizeof(u64);
+		remaining = rate_count - clk_rate_idx;
+		if (remaining > max_rates)
+			returned = max_rates;
+		else
+			returned = remaining;
+
+		for (i = clk_rate_idx, j = 0;
+		     i <= (clk_rate_idx + returned - 1); i++, j++) {
+			resp[4 + 2 * j] = cpu_to_le32((u32)(rate_array[i]));
+			resp[5 + 2 * j] =
+				cpu_to_le32((u32)(rate_array[i] >> 32));
+		}
+
+		remaining = rate_count - (clk_rate_idx + returned);
+	} else {
+		return RPMI_ERR_FAILED;
+	}
+
+	resp[3] = cpu_to_le32((u32)returned);
+	resp[2] = cpu_to_le32((u32)remaining);
+	/* No flags currently supported */
+	resp[1] = cpu_to_le32(0);
+	resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+	resp_dlen = (4 * sizeof(*resp)) + (returned * sizeof(u64));
+
+done:
+	*response_datalen = resp_dlen;
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_set_config(struct rpmi_transport *trans, u16 request_datalen,
+			 const u8 *request_data, u16 *response_datalen,
+			 u8 *response_data)
+{
+	enum rpmi_error_codes status;
+	u32 cfg, new_state;
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	if (clkid >= clock_count) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	cfg = cpu_to_le32(((const u32 *)request_data)[1]);
+
+	/* get command from 0th index bit in config field */
+	new_state = (cfg & 0b1) ? RPMI_CLK_STATE_ENABLED :
+				  RPMI_CLK_STATE_DISABLED;
+
+	/* change clock config synchronously */
+	status = rpmi_clock_set_state(clkid, new_state);
+	resp[0] = cpu_to_le32((u32)status);
+
+done:
+	*response_datalen = sizeof(*resp);
+
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_config(struct rpmi_transport *trans, u16 request_datalen,
+			 const u8 *request_data, u16 *response_datalen,
+			 u8 *response_data)
+{
+	u16 resp_dlen;
+	enum rpmi_error_codes status;
+	enum rpmi_clock_state state;
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	if (clkid >= clock_count) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		resp_dlen = sizeof(*resp);
+		goto done;
+	}
+
+	status = rpmi_clock_get_state(clkid, &state);
+	if (status) {
+		resp[0] = cpu_to_le32((u32)status);
+		resp_dlen = sizeof(*resp);
+		goto done;
+	}
+
+	/** RPMI config field only return enabled or disabled state */
+	state = (state == RPMI_CLK_STATE_ENABLED) ? 1 : 0;
+
+	resp[1] = cpu_to_le32((u32)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_clock_sg_set_rate(struct rpmi_transport *trans, u16 request_datalen,
+		       const u8 *request_data, u16 *response_datalen,
+		       u8 *response_data)
+{
+	enum rpmi_error_codes status;
+	enum rpmi_clock_rate_match rate_match;
+	u32 flags;
+	u32 rate_low, rate_hi;
+	u64 rate_u64;
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	if (clkid >= clock_count) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	flags = cpu_to_le32(((const u32 *)request_data)[1]);
+
+	/* get rate match mode from flags */
+	rate_match = flags & 0b11;
+	if (rate_match >= RPMI_CLK_RATE_MATCH_MAX) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	rate_low = cpu_to_le32(((const u32 *)request_data)[2]);
+	rate_hi = cpu_to_le32(((const u32 *)request_data)[3]);
+
+	rate_u64 = ((u64)rate_hi << 32) | rate_low;
+	if (rate_u64 == RPMI_CLOCK_RATE_INVALID || rate_u64 == 0) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		goto done;
+	}
+
+	status = rpmi_clock_set_rate(clkid, rate_match, rate_u64);
+	resp[0] = cpu_to_le32((u32)status);
+
+done:
+	*response_datalen = sizeof(*resp);
+	return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_rate(struct rpmi_transport *trans, u16 request_datalen,
+		       const u8 *request_data, u16 *response_datalen,
+		       u8 *response_data)
+{
+	u16 resp_dlen;
+	enum rpmi_error_codes status;
+	u64 rate_u64;
+	u32 *resp = (void *)response_data;
+
+	u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+	if (clkid >= clock_count) {
+		resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+		resp_dlen = sizeof(*resp);
+		goto done;
+	}
+
+	status = rpmi_clock_get_rate(clkid, &rate_u64);
+	if (status) {
+		resp[0] = cpu_to_le32((u32)status);
+		resp_dlen = sizeof(*resp);
+		goto done;
+	}
+
+	resp[2] = cpu_to_le32((u32)(rate_u64 >> 32));
+	resp[1] = cpu_to_le32((u32)(rate_u64));
+	resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+	resp_dlen = 3 * sizeof(*resp);
+
+done:
+	*response_datalen = resp_dlen;
+	return RPMI_SUCCESS;
+}
+
+static const struct rpmi_service rpmi_clock_services[RPMI_CLK_SRV_ID_MAX_COUNT] = {
+	[RPMI_CLK_SRV_GET_NUM_CLOCKS] = {
+		.service_id = RPMI_CLK_SRV_GET_NUM_CLOCKS,
+		.min_a2p_request_datalen = 0,
+		.process_a2p_request = rpmi_clock_sg_get_num_clocks,
+	},
+	[RPMI_CLK_SRV_GET_ATTRIBUTES] = {
+		.service_id = RPMI_CLK_SRV_GET_ATTRIBUTES,
+		.min_a2p_request_datalen = 4,
+		.process_a2p_request = rpmi_clock_sg_get_attributes,
+	},
+	[RPMI_CLK_SRV_GET_SUPPORTED_RATES] = {
+		.service_id = RPMI_CLK_SRV_GET_SUPPORTED_RATES,
+		.min_a2p_request_datalen = 8,
+		.process_a2p_request = rpmi_clock_sg_get_supp_rates,
+	},
+	[RPMI_CLK_SRV_SET_CONFIG] = {
+		.service_id = RPMI_CLK_SRV_SET_CONFIG,
+		.min_a2p_request_datalen = 8,
+		.process_a2p_request = rpmi_clock_sg_set_config,
+	},
+	[RPMI_CLK_SRV_GET_CONFIG] = {
+		.service_id = RPMI_CLK_SRV_GET_CONFIG,
+		.min_a2p_request_datalen = 4,
+		.process_a2p_request = rpmi_clock_sg_get_config,
+	},
+	[RPMI_CLK_SRV_SET_RATE] = {
+		.service_id = RPMI_CLK_SRV_SET_RATE,
+		.min_a2p_request_datalen = 16,
+		.process_a2p_request = rpmi_clock_sg_set_rate,
+	},
+	[RPMI_CLK_SRV_GET_RATE] = {
+		.service_id = RPMI_CLK_SRV_GET_RATE,
+		.min_a2p_request_datalen = 4,
+		.process_a2p_request = rpmi_clock_sg_get_rate,
+	},
+};
+
+const struct rpmi_service_group rpmi_sandbox_clock_sg = {
+	.name = "clk",
+	.max_service_id = RPMI_CLK_SRV_GET_RATE,
+	.servicegroup_version = RPMI_MKVER(1, 0),
+	.services = rpmi_clock_services,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.c b/drivers/firmware/rpmi/rpmi-sandbox.c
new file mode 100644
index 000000000000..5597c27067eb
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <dm.h>
+#include <log.h>
+#include <rpmi-uclass.h>
+#include <dm/device_compat.h>
+
+#include "rpmi-sandbox.h"
+
+#define RPMI_SANDBOX_MIN_SLOT_SIZE 64
+#define RPMI_SANDBOX_SHMEM_HEADER_SIZE 8
+#define RPMI_SANDBOX_MAX_MSG_SIZE \
+	(RPMI_SANDBOX_MIN_SLOT_SIZE - RPMI_SANDBOX_SHMEM_HEADER_SIZE)
+
+#define DRIVER_NAME "rpmi-sandbox"
+#define DRIVER_NAME_LEN (sizeof(DRIVER_NAME))
+
+struct rpmi_sandbox {
+	struct udevice *dev;
+};
+
+const struct rpmi_service_group *rpmi_sandbox_sg(u32 servicegroup_id)
+{
+	switch (servicegroup_id) {
+	case RPMI_SRVGRP_CLOCK:
+		return &rpmi_sandbox_clock_sg;
+	default:
+		return NULL;
+	}
+}
+
+static int rpmi_sandbox_get_attr(struct rpmi_chan *chan,
+				 enum rpmi_attribute_id id, u32 *data)
+{
+	const struct rpmi_service_group *sg = rpmi_sandbox_sg(chan->id);
+
+	if (!sg)
+		return -EOPNOTSUPP;
+
+	switch (id) {
+	case RPMI_ATTR_SPEC_VERSION:
+		*data = RPMI_MKVER(1, 0);
+		break;
+	case RPMI_ATTR_MAX_MSG_DATA_SIZE:
+		*data = RPMI_SANDBOX_MAX_MSG_SIZE;
+		break;
+	case RPMI_ATTR_SERVICEGROUP_ID:
+		*data = chan->id;
+		break;
+	case RPMI_ATTR_SERVICEGROUP_VERSION:
+		*data = sg->servicegroup_version;
+		break;
+	case RPMI_ATTR_IMPL_ID:
+		*data = 0;
+		break;
+	case RPMI_ATTR_IMPL_VERSION:
+		*data = RPMI_MKVER(1, 0);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int rpmi_sandbox_request(struct rpmi_chan *chan, u32 service_id,
+				const void *request, unsigned long request_len,
+				void *response, unsigned long max_response_len,
+				unsigned long *out_response_len)
+{
+	const struct rpmi_service_group *sg = rpmi_sandbox_sg(chan->id);
+	const struct rpmi_service *service;
+	u8 slot[RPMI_SANDBOX_MAX_MSG_SIZE];
+	struct rpmi_transport trans = {
+		.msg_data_size = sizeof(slot),
+	};
+	u16 response_data_len = 0;
+	enum rpmi_error_codes err;
+
+	if (!sg)
+		return -EOPNOTSUPP;
+
+	if (service_id > sg->max_service_id)
+		return -EOPNOTSUPP;
+
+	service = &sg->services[service_id];
+
+	if (!service)
+		return -EOPNOTSUPP;
+
+	if (request_len < service->min_a2p_request_datalen)
+		return -EINVAL;
+
+	if (!service->process_a2p_request)
+		return -ENOSYS;
+
+	err = service->process_a2p_request(&trans, request_len, request,
+					   &response_data_len, slot);
+
+	if (err)
+		return rpmi_to_linux_error(err);
+
+	if (out_response_len)
+		*out_response_len = response_data_len;
+
+	if (response)
+		memcpy(response, slot,
+		       min(max_response_len, (unsigned long)response_data_len));
+
+	return 0;
+}
+
+static int rpmi_sandbox_of_xlate(struct rpmi_chan *chan,
+				 struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 1)
+		return -EINVAL;
+
+	chan->id = args->args[0];
+
+	return 0;
+}
+
+static int rpmi_sandbox_probe(struct udevice *dev)
+{
+	struct rpmi_sandbox *priv = dev_get_priv(dev);
+
+	priv->dev = dev;
+
+	return 0;
+}
+
+static const struct udevice_id rpmi_sandbox_ids[] = {
+	{ .compatible = "sandbox,rpmi-transport" },
+	{}
+};
+
+static const struct rpmi_transport_ops rpmi_sandbox_ops = {
+	.of_xlate = rpmi_sandbox_of_xlate,
+	.get_attr = rpmi_sandbox_get_attr,
+	.request = rpmi_sandbox_request,
+};
+
+U_BOOT_DRIVER(rpmi_sandbox) = {
+	.name = "rpmi-sandbox",
+	.id = UCLASS_RPMI,
+	.of_match = rpmi_sandbox_ids,
+	.probe = rpmi_sandbox_probe,
+	.priv_auto = sizeof(struct rpmi_sandbox),
+	.ops = &rpmi_sandbox_ops,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.h b/drivers/firmware/rpmi/rpmi-sandbox.h
new file mode 100644
index 000000000000..132d1e9e8be6
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#ifndef _RPMI_SANDBOX_H
+#define _RPMI_SANDBOX_H
+
+#include <stdbool.h>
+#include <asm/types.h>
+#include <rpmi.h>
+
+struct rpmi_transport {
+	int msg_data_size;
+};
+
+struct rpmi_service {
+	/** ID of the service */
+	u8 service_id;
+
+	/** Minimum data length for handling request */
+	u16 min_a2p_request_datalen;
+
+	/** Callback to process a2p request */
+	enum rpmi_error_codes (*process_a2p_request)(
+		struct rpmi_transport *trans, u16 request_data_len,
+		const u8 *request_data, u16 *response_data_len,
+		u8 *response_data);
+};
+
+struct rpmi_service_group {
+	/** Name of the service group */
+	const char *name;
+
+	/** Maximum service ID of the service group */
+	int max_service_id;
+
+	/** Service group version */
+	u32 servicegroup_version;
+
+	/** Array of services indexed by service ID */
+	const struct rpmi_service *services;
+};
+
+extern const struct rpmi_service_group rpmi_sandbox_clock_sg;
+
+#endif
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 0e3c63568ddb..ee03b4e13de2 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
 obj-$(CONFIG_$(PHASE_)DM_RNG) += rng.o
 obj-$(CONFIG_DM_RTC) += rtc.o
 obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o
+obj-$(CONFIG_RPMI_FIRMWARE) += rpmi.o
 obj-$(CONFIG_SCSI) += scsi.o
 obj-$(CONFIG_DM_SERIAL) += serial.o
 obj-$(CONFIG_DM_SPI_FLASH) += sf.o
diff --git a/test/dm/rpmi.c b/test/dm/rpmi.c
new file mode 100644
index 000000000000..1ea29780a480
--- /dev/null
+++ b/test/dm/rpmi.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#include <dm.h>
+#include <rpmi.h>
+#include <clk.h>
+#include <stdio.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static int dm_test_rpmi_clock(struct unit_test_state *uts)
+{
+	struct udevice *rpmi_dev, *clk_dev;
+	struct clk clk0, clk1, clk2, clk3;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_RPMI, "rpmi", &rpmi_dev));
+
+	ut_assertok(
+		uclass_get_device_by_name(UCLASS_CLK, "rpmi-clock", &clk_dev));
+
+	// Grab clock 0 and retrieve the rate
+	ut_assertok(clk_get_by_index(clk_dev, 0, &clk0));
+	ut_asserteq_64(20000000, clk_get_rate(&clk0));
+
+	// Change to 1GHz
+	ut_asserteq(1000000000, clk_set_rate(&clk0, 1000000000));
+
+	// Revert back to 200MHz
+	ut_asserteq(20000000, clk_set_rate(&clk0, 20000000));
+
+	// Grab clock 1
+	ut_assertok(clk_get_by_index(clk_dev, 1, &clk1));
+	ut_asserteq_64(333333333, clk_get_rate(&clk1));
+
+	// Grab clock 2
+	ut_assertok(clk_get_by_index(clk_dev, 2, &clk2));
+	ut_asserteq_64(500000000, clk_get_rate(&clk2));
+
+	// Grab clock 3
+	ut_assertok(clk_get_by_index(clk_dev, 3, &clk3));
+	ut_asserteq_64(50000000, clk_get_rate(&clk3));
+
+	return 0;
+}
+DM_TEST(dm_test_rpmi_clock, UTF_SCAN_FDT);
-- 
2.47.3


  parent reply	other threads:[~2026-06-26 21:21 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
2026-06-27  1:09   ` Yao Zi
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
2026-06-27  1:30   ` Yao Zi
2026-06-26 20:15 ` [PATCH 3/7] firmware: rpmi: add support for shared memory transport Charles Perry
2026-06-26 20:15 ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Charles Perry
2026-06-27  1:47   ` Yao Zi
2026-06-26 20:15 ` [PATCH 5/7] drivers: power: add support for RPMI power domains Charles Perry
2026-06-26 20:15 ` Charles Perry [this message]
2026-06-26 20:15 ` [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power Charles Perry

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=20260626201613.1035208-7-charles.perry@microchip.com \
    --to=charles.perry@microchip.com \
    --cc=anup@brainfault.org \
    --cc=clamor95@gmail.com \
    --cc=dinesh.maniyam@altera.com \
    --cc=hs@nabladev.com \
    --cc=jerome.forissier@arm.com \
    --cc=kory.maincent@bootlin.com \
    --cc=lucienzx159@gmail.com \
    --cc=m.schwan@phytec.de \
    --cc=marek.vasut@mailbox.org \
    --cc=mateusz.furdyna@nokia.com \
    --cc=me@ziyao.cc \
    --cc=michal.simek@amd.com \
    --cc=mkorpershoek@kernel.org \
    --cc=neil.armstrong@linaro.org \
    --cc=peng.fan@nxp.com \
    --cc=philip.molloy@analog.com \
    --cc=pieter.van.trappen@cern.ch \
    --cc=quentin.schulz@cherry.de \
    --cc=rahul@summations.net \
    --cc=ravi@prevas.dk \
    --cc=raymond.mao@riscstar.com \
    --cc=sjg@chromium.org \
    --cc=stefan.roese@mailbox.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    --cc=visitorckw@gmail.com \
    /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