* [PATCH v2 1/5] firmware: add support for RPMI
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
@ 2026-07-08 21:51 ` Charles Perry
2026-07-08 21:51 ` [PATCH v2 2/5] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
` (4 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Charles Perry @ 2026-07-08 21:51 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini, Simon Glass,
Neil Armstrong, David Lechner, Julien Stephan, Peng Fan,
Kory Maincent, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Marek Vasut, Michael Trimarchi, Svyatoslav Ryhel, Dinesh Maniyam,
Ion Agorria
RISC-V Platform Management Interface (RPMI) defines an OS-agnostic
protocol for communication between an Application Processor (AP) and a
platform microcontroller (PuC) [1]
Add UCLASS_RPMI to abstract the implementation details of an RPMI
transport layer. A transport driver should implement the struct
rpmi_transport_ops callback interface while an RPMI client driver should
use struct rpmi_chan and the rpmi_* API:
* rpmi_get_by_index()
* rpmi_open()
* rpmi_close()
* rpmi_get_attr()
* rpmi_send_with_resp()
* rpmi_send_posted()
The most important part of the API is rpmi_send_with_resp() and
rpmi_send_posted() which is used to send a message and possibly receive
a response from the firmware.
rpmi_get_attr() can be used to retrieve attributes of the channel like
the implementation ID or the maximum message size. This is to retrieve
information that lives outside of the service group of the rpmi_chan,
either in the "base service group" or in the transport layer itself.
rpmi_get_by_index() can be used to initialize a struct rpmi_chan using
info from the device tree.
rpmi_open() and rpmi_close() must be called once at initialization and
deinitialization by clients to let the transport layer do some stateful
things if needed.
A helper function is also added: rpmi_check_versions() which is just a
wrapper around rpmi_get_attr() to do some basic version checking that
all clients should perform.
[1]: https://github.com/riscv-non-isa/riscv-rpmi
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
Changes in v2: (from Yao Zi review)
- Reorder headers
- Improve error messages in rpmi_check_versions()
---
MAINTAINERS | 6 ++
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/rpmi/Kconfig | 9 ++
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-uclass.c | 144 +++++++++++++++++++++++++
include/dm/uclass-id.h | 1 +
include/rpmi-uclass.h | 79 ++++++++++++++
include/rpmi.h | 159 ++++++++++++++++++++++++++++
include/rpmi_proto.h | 63 +++++++++++
10 files changed, 464 insertions(+)
create mode 100644 drivers/firmware/rpmi/Kconfig
create mode 100644 drivers/firmware/rpmi/Makefile
create mode 100644 drivers/firmware/rpmi/rpmi-uclass.c
create mode 100644 include/rpmi-uclass.h
create mode 100644 include/rpmi.h
create mode 100644 include/rpmi_proto.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 030f92a84f9f..812942a2d8a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1651,6 +1651,12 @@ F: drivers/usb/gadget/f_rockusb.c
F: cmd/rockusb.c
F: doc/README.rockusb
+RPMI
+M: Charles Perry <charles.perry@microchip.com>
+S: Maintained
+F: drivers/firmware/rpmi/
+F: include/rpmi*
+
SANDBOX
M: Simon Glass <sjg@chromium.org>
S: Maintained
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 220de7319506..d1c3c2f666ee 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -49,3 +49,4 @@ config ARM_SMCCC_FEATURES
source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/firmware/scmi/Kconfig"
+source "drivers/firmware/rpmi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 9e7b118b074f..7d7b09a5d392 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_SANDBOX) += firmware-sandbox.o
obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o
obj-$(CONFIG_SCMI_FIRMWARE) += scmi/
+obj-$(CONFIG_RPMI_FIRMWARE) += rpmi/
diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
new file mode 100644
index 000000000000..54fd77ad8f48
--- /dev/null
+++ b/drivers/firmware/rpmi/Kconfig
@@ -0,0 +1,9 @@
+config RPMI_FIRMWARE
+ bool "Enable RPMI support"
+ depends on RISCV && DM && OF_CONTROL
+ select FIRMWARE
+ help
+ RISC-V Platform Management Interface (RPMI) defines an OS-agnostic
+ protocol for communication between an Application Processor (AP) and a
+ platform microcontroller (PuC). The RPMI specification is available
+ at: https://github.com/riscv-non-isa/riscv-rpmi
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
new file mode 100644
index 000000000000..d69c718d150e
--- /dev/null
+++ b/drivers/firmware/rpmi/Makefile
@@ -0,0 +1 @@
+obj-y += rpmi-uclass.o
diff --git a/drivers/firmware/rpmi/rpmi-uclass.c b/drivers/firmware/rpmi/rpmi-uclass.c
new file mode 100644
index 000000000000..a18d5912a63d
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-uclass.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <log.h>
+#include <rpmi-uclass.h>
+
+static inline struct rpmi_transport_ops *rpmi_dev_ops(struct udevice *dev)
+{
+ return (struct rpmi_transport_ops *)dev->driver->ops;
+}
+
+int rpmi_get_by_index(struct udevice *dev, int index, struct rpmi_chan *chan)
+{
+ struct ofnode_phandle_args args;
+ struct rpmi_transport_ops *ops;
+ struct udevice *rpmi_dev;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, "mboxes", "#mbox-cells", 0, index,
+ &args);
+ if (ret)
+ return ret;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_RPMI, args.node, &rpmi_dev);
+ if (ret)
+ return ret;
+
+ ops = rpmi_dev_ops(rpmi_dev);
+
+ if (!ops->of_xlate)
+ return -ENOSYS;
+
+ chan->dev = rpmi_dev;
+
+ ret = ops->of_xlate(chan, &args);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int rpmi_open(struct rpmi_chan *chan)
+{
+ struct rpmi_transport_ops *ops = rpmi_dev_ops(chan->dev);
+
+ if (ops->open)
+ return ops->open(chan);
+
+ return 0;
+}
+
+void rpmi_close(struct rpmi_chan *chan)
+{
+ struct rpmi_transport_ops *ops = rpmi_dev_ops(chan->dev);
+
+ if (ops->close)
+ ops->close(chan);
+}
+
+int rpmi_get_attr(struct rpmi_chan *chan, enum rpmi_attribute_id id, u32 *data)
+{
+ struct rpmi_transport_ops *ops = rpmi_dev_ops(chan->dev);
+
+ if (!ops->get_attr)
+ return -ENOSYS;
+
+ return ops->get_attr(chan, id, data);
+}
+
+int rpmi_send_with_resp(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)
+{
+ struct rpmi_transport_ops *ops = rpmi_dev_ops(chan->dev);
+
+ if (!ops->request)
+ return -ENOSYS;
+
+ return ops->request(chan, service_id, request, request_len, response,
+ max_response_len, out_response_len);
+}
+
+int rpmi_send_posted(struct rpmi_chan *chan, u32 service_id,
+ const void *request, unsigned long request_len)
+{
+ return rpmi_send_with_resp(chan, service_id, request, request_len, NULL,
+ 0, NULL);
+}
+
+int rpmi_check_versions(struct rpmi_chan *chan, u32 min_proto_version,
+ u32 servicegroup_id_expect,
+ u32 min_servicegroup_version)
+{
+ u32 proto_version, servicegroup_id, servicegroup_version;
+ int ret;
+
+ ret = rpmi_get_attr(chan, RPMI_ATTR_SPEC_VERSION, &proto_version);
+ if (ret)
+ return ret;
+
+ if (proto_version < min_proto_version) {
+ dev_err(chan->dev,
+ "protocol version too old, found 0x%x, expected min 0x%x\n",
+ proto_version, min_proto_version);
+ return -EINVAL;
+ }
+
+ ret = rpmi_get_attr(chan, RPMI_ATTR_SERVICEGROUP_ID, &servicegroup_id);
+ if (ret)
+ return ret;
+
+ if (servicegroup_id != servicegroup_id_expect) {
+ dev_err(chan->dev,
+ "service group match fail, found 0x%x, expected 0x%x\n",
+ servicegroup_id, servicegroup_id_expect);
+ return -EINVAL;
+ }
+
+ ret = rpmi_get_attr(chan, RPMI_ATTR_SERVICEGROUP_VERSION,
+ &servicegroup_version);
+ if (ret)
+ return ret;
+
+ if (servicegroup_version < min_servicegroup_version) {
+ dev_err(chan->dev,
+ "service group version too old, found 0x%x, expected min 0x%x\n",
+ servicegroup_version, min_servicegroup_version);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+UCLASS_DRIVER(rpmi) = {
+ .id = UCLASS_RPMI,
+ .name = "rpmi",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 36b5d87c304f..9e86780dcfb7 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -128,6 +128,7 @@ enum uclass_id {
UCLASS_RESET, /* Reset controller device */
UCLASS_RKMTD, /* Rockchip MTD device */
UCLASS_RNG, /* Random Number Generator */
+ UCLASS_RPMI, /* RPMI transport */
UCLASS_RTC, /* Real time clock device */
UCLASS_SCMI_AGENT, /* Interface with an SCMI server */
UCLASS_SCMI_BASE, /* Interface for SCMI Base protocol */
diff --git a/include/rpmi-uclass.h b/include/rpmi-uclass.h
new file mode 100644
index 000000000000..1b41f9529a83
--- /dev/null
+++ b/include/rpmi-uclass.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#ifndef _RPMI_UCLASS_H
+#define _RPMI_UCLASS_H
+
+#include <rpmi.h>
+
+/**
+ * struct rpmi_transport_ops - The functions that an RPMI transport driver must implement.
+ */
+struct rpmi_transport_ops {
+ /**
+ * of_xlate - Translate a client's device-tree (OF) mailbox specifier.
+ *
+ * Mandatory, cannot be null.
+ *
+ * @chan: The channel to hold the translation result.
+ * @args: The mailbox specifier values from device tree.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*of_xlate)(struct rpmi_chan *chan,
+ struct ofnode_phandle_args *args);
+
+ /**
+ * get_attr - Get an RPMI attribute
+ *
+ * Mandatory, cannot be null.
+ *
+ * @chan: RPMI channel associated to the request.
+ * @id: ID of the attribute to get.
+ * @data: buffer to hold the parameter.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*get_attr)(struct rpmi_chan *chan, enum rpmi_attribute_id id,
+ u32 *data);
+
+ /**
+ * request - send a request and possibly receive a response.
+ *
+ * Mandatory, cannot be null.
+ *
+ * @chan: RPMI channel associated to the request.
+ * @service_id: RPMI service ID to call.
+ * @request: buffer to hold the request.
+ * @request_len: length of the request buffer.
+ * @response: buffer to hold the response. If NULL, this is a posted request.
+ * @max_response_len: size of the response buffer. If 0, this is a posted request.
+ * @out_response_len: size of the actual response. Out parameter.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*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);
+
+ /**
+ * open - Perform channel initialization.
+ *
+ * Optional, can be null.
+ *
+ * @chan: RPMI channel to initialize.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*open)(struct rpmi_chan *chan);
+
+ /**
+ * close - Perform channel deinitialization.
+ *
+ * Optional, can be null.
+ *
+ * @chan: RPMI channel to deinitialize.
+ */
+ void (*close)(struct rpmi_chan *chan);
+};
+
+#endif
diff --git a/include/rpmi.h b/include/rpmi.h
new file mode 100644
index 000000000000..de802026b31a
--- /dev/null
+++ b/include/rpmi.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#ifndef _RPMI_H
+#define _RPMI_H
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <rpmi_proto.h>
+
+/* RPMI version encode/decode macros */
+#define RPMI_VER_MAJOR(__ver) upper_16_bits(__ver)
+#define RPMI_VER_MINOR(__ver) lower_16_bits(__ver)
+#define RPMI_MKVER(__maj, __min) (((u32)(__maj) << 16) | (u16)(__min))
+
+/* RPMI attribute IDs */
+enum rpmi_attribute_id {
+ RPMI_ATTR_SPEC_VERSION,
+ RPMI_ATTR_MAX_MSG_DATA_SIZE,
+ RPMI_ATTR_SERVICEGROUP_ID,
+ RPMI_ATTR_SERVICEGROUP_VERSION,
+ RPMI_ATTR_IMPL_ID,
+ RPMI_ATTR_IMPL_VERSION,
+ RPMI_ATTR_MAX_ID
+};
+
+static inline int rpmi_to_linux_error(int rpmi_error)
+{
+ switch (rpmi_error) {
+ case RPMI_SUCCESS:
+ return 0;
+ case RPMI_ERR_INVALID_PARAM:
+ case RPMI_ERR_BAD_RANGE:
+ case RPMI_ERR_INVALID_STATE:
+ return -EINVAL;
+ case RPMI_ERR_DENIED:
+ return -EPERM;
+ case RPMI_ERR_INVALID_ADDR:
+ case RPMI_ERR_HW_FAULT:
+ return -EFAULT;
+ case RPMI_ERR_ALREADY:
+ return -EALREADY;
+ case RPMI_ERR_BUSY:
+ return -EBUSY;
+ case RPMI_ERR_TIMEOUT:
+ return -ETIMEDOUT;
+ case RPMI_ERR_IO:
+ return -ECOMM;
+ case RPMI_ERR_FAILED:
+ case RPMI_ERR_NOTSUPP:
+ case RPMI_ERR_NO_DATA:
+ case RPMI_ERR_EXTENSION:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+struct udevice;
+
+/**
+ * struct rpmi_chan - A handle to a single RPMI channel.
+ *
+ * One RPMI channel is associated to one service group.
+ */
+struct rpmi_chan {
+ struct udevice *dev;
+ /* Written by rpmi_get_by_index or rpmi_open */
+ unsigned long id;
+ void *con_priv;
+};
+
+/**
+ * rpmi_get_by_index - Get an RPMI channel by integer index.
+ *
+ * Initialize an RPMI channel using hardware information such as device tree
+ * data. No communication with the PuC occurs when calling this function.
+ *
+ * @dev: The client device.
+ * @index: The index of the RPMI channel to request.
+ * @chan: A pointer to a channel object to initialize.
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_get_by_index(struct udevice *dev, int index, struct rpmi_chan *chan);
+
+/**
+ * rpmi_open - Initialize an RPMI channel.
+ *
+ * Initialize an RPMI channel. Communication with the PuC is performed. Must be
+ * called after rpmi_get_by_index.
+ *
+ * @chan: The channel to initialize.
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_open(struct rpmi_chan *chan);
+
+/**
+ * rpmi_close - Deinitialize an RPMI channel.
+ *
+ * @chan: The channel to deinitialize.
+ */
+void rpmi_close(struct rpmi_chan *chan);
+
+/**
+ * rpmi_get_attr - Get an RPMI channel attribute.
+ *
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_get_attr(struct rpmi_chan *chan, enum rpmi_attribute_id id, u32 *data);
+
+/**
+ * rpmi_send_with_resp - Send a request and receive a response.
+ *
+ * @chan: RPMI channel associated to the request.
+ * @service_id: RPMI service ID to call.
+ * @request: buffer to hold the request. Can be NULL if request_len is zero.
+ * @request_len: length of the request buffer.
+ * @response: buffer to hold the response. Cannot be NULL.
+ * @max_response_len: length of the response buffer.
+ * @out_response_len: length of the actual response. Out parameter.
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_send_with_resp(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);
+
+/**
+ * rpmi_send_posted - Send a posted request.
+ *
+ * @chan: RPMI channel associated to the request.
+ * @service_id: RPMI service ID to call.
+ * @request: buffer to hold the request. Can be NULL if request_len is zero.
+ * @request_len: length of the request buffer.
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_send_posted(struct rpmi_chan *chan, u32 service_id,
+ const void *request, unsigned long request_len);
+
+/**
+ * rpmi_check_versions - helper function to check version numbers of the
+ * implementation.
+ *
+ * @chan: RPMI channel associated to the request.
+ * @min_proto_version: Minimum RPMI protocol version required. Caller
+ * should use the RPMI_MKVER() macro.
+ * @servicegroup_id_expect: Service group ID expected.
+ * @min_servicegroup_version: Minimum Service group implementation version
+ * required. Caller should use the RPMI_MKVER()
+ * macro.
+ * @return: 0 if OK, or a negative error code.
+ */
+int rpmi_check_versions(struct rpmi_chan *chan, u32 min_proto_version,
+ u32 servicegroup_id_expect,
+ u32 min_servicegroup_version);
+
+#endif
diff --git a/include/rpmi_proto.h b/include/rpmi_proto.h
new file mode 100644
index 000000000000..012ae93d7317
--- /dev/null
+++ b/include/rpmi_proto.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Values defined by the RPMI spec.
+ */
+
+#ifndef _RPMI_PROTO_H
+#define _RPMI_PROTO_H
+
+/* RPMI error codes */
+enum rpmi_error_codes {
+ RPMI_SUCCESS = 0,
+ RPMI_ERR_FAILED = -1,
+ RPMI_ERR_NOTSUPP = -2,
+ RPMI_ERR_INVALID_PARAM = -3,
+ RPMI_ERR_DENIED = -4,
+ RPMI_ERR_INVALID_ADDR = -5,
+ RPMI_ERR_ALREADY = -6,
+ RPMI_ERR_EXTENSION = -7,
+ RPMI_ERR_HW_FAULT = -8,
+ RPMI_ERR_BUSY = -9,
+ RPMI_ERR_INVALID_STATE = -10,
+ RPMI_ERR_BAD_RANGE = -11,
+ RPMI_ERR_TIMEOUT = -12,
+ RPMI_ERR_IO = -13,
+ RPMI_ERR_NO_DATA = -14,
+ RPMI_ERR_RESERVED_START = -15,
+ RPMI_ERR_RESERVED_END = -127,
+ RPMI_ERR_VENDOR_START = -128,
+};
+
+/** RPMI ServiceGroups IDs */
+enum rpmi_servicegroup_id {
+ RPMI_SRVGRP_ID_MIN = 0,
+ RPMI_SRVGRP_BASE = 0x0001,
+ RPMI_SRVGRP_SYSTEM_MSI = 0x0002,
+ RPMI_SRVGRP_SYSTEM_RESET = 0x0003,
+ RPMI_SRVGRP_SYSTEM_SUSPEND = 0x0004,
+ RPMI_SRVGRP_HSM = 0x0005,
+ RPMI_SRVGRP_CPPC = 0x0006,
+ RPMI_SRVGRP_VOLTAGE = 0x0007,
+ RPMI_SRVGRP_CLOCK = 0x0008,
+ RPMI_SRVGRP_DEVICE_POWER = 0x0009,
+ RPMI_SRVGRP_PERFORMANCE = 0x000A,
+ RPMI_SRVGRP_MANAGEMENT_MODE = 0x000B,
+ RPMI_SRVGRP_RAS_AGENT = 0x000C,
+ RPMI_SRVGRP_REQUEST_FORWARD = 0x000D,
+ RPMI_SRVGRP_LOGGING = 0x000E,
+ RPMI_SRVGRP_ID_MAX_COUNT,
+
+ /* Reserved range for service groups */
+ RPMI_SRVGRP_RESERVE_START = RPMI_SRVGRP_ID_MAX_COUNT,
+ RPMI_SRVGRP_RESERVE_END = 0x7BFF,
+
+ /* Experimental service groups range */
+ RPMI_SRVGRP_EXPERIMENTAL_START = 0x7C00,
+ RPMI_SRVGRP_EXPERIMENTAL_END = 0x7FFF,
+
+ /* Vendor/Implementation-specific service groups range */
+ RPMI_SRVGRP_VENDOR_START = 0x8000,
+ RPMI_SRVGRP_VENDOR_END = 0xFFFF,
+};
+
+#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 2/5] firmware: rpmi: add support for the SBI MPXY transport
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
2026-07-08 21:51 ` [PATCH v2 1/5] firmware: add support for RPMI Charles Perry
@ 2026-07-08 21:51 ` Charles Perry
2026-07-08 21:51 ` [PATCH v2 3/5] firmware: rpmi: add support for shared memory transport Charles Perry
` (3 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Charles Perry @ 2026-07-08 21:51 UTC (permalink / raw)
To: u-boot; +Cc: Rahul Pathak, Anup Patel, Charles Perry, Rick Chen, Leo, Tom Rini
This adds an RPMI transport driver for the Supervisor Binary Interface
(SBI) message proxy (MPXY) extension [1].
This is for S-Mode U-Boot where some supervisor software (such as
OpenSBI) runs in M-Mode and handles the low level communication with the
PuC.
[1]: https://github.com/riscv-non-isa/riscv-sbi-doc (chapter 20)
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
Changes in v2: (from Yao Zi review)
- Reorder headers
- Remove struct sbi_mpxy_rpmi_channel_attrs and struct
sbi_mpxy_channel_attrs for simpler u32 arrays
- Don't null check pointers before free()
---
arch/riscv/include/asm/sbi.h | 57 ++++
drivers/firmware/rpmi/Kconfig | 9 +
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-sbi-mpxy.c | 410 ++++++++++++++++++++++++++
4 files changed, 477 insertions(+)
create mode 100644 drivers/firmware/rpmi/rpmi-sbi-mpxy.c
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 47124dbaac84..10e03257d8e6 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -10,6 +10,7 @@
#define _ASM_RISCV_SBI_H
#include <linux/types.h>
+#include <linux/errno.h>
enum sbi_ext_id {
SBI_EXT_0_1_SET_TIMER = 0x0,
@@ -104,6 +105,17 @@ enum sbi_ext_dbcn_fid {
SBI_EXT_DBCN_CONSOLE_WRITE_BYTE,
};
+enum sbi_ext_mpxy_fid {
+ SBI_EXT_MPXY_GET_SHMEM_SIZE = 0,
+ SBI_EXT_MPXY_SET_SHMEM,
+ SBI_EXT_MPXY_GET_CHANNEL_IDS,
+ SBI_EXT_MPXY_READ_ATTRS,
+ SBI_EXT_MPXY_WRITE_ATTRS,
+ SBI_EXT_MPXY_SEND_MSG_WITH_RESP,
+ SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP,
+ SBI_EXT_MPXY_GET_NOTIFICATION_EVENTS,
+};
+
#ifdef CONFIG_SBI_V01
#define SBI_EXT_SET_TIMER SBI_EXT_0_1_SET_TIMER
#define SBI_FID_SET_TIMER 0
@@ -132,6 +144,14 @@ enum sbi_ext_dbcn_fid {
#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
+static inline unsigned long sbi_mk_version(unsigned long major,
+ unsigned long minor)
+{
+ return ((major & SBI_SPEC_VERSION_MAJOR_MASK)
+ << SBI_SPEC_VERSION_MAJOR_SHIFT) |
+ (minor & SBI_SPEC_VERSION_MINOR_MASK);
+}
+
/* SBI return error codes */
#define SBI_SUCCESS 0
#define SBI_ERR_FAILURE -1
@@ -139,6 +159,43 @@ enum sbi_ext_dbcn_fid {
#define SBI_ERR_INVALID_PARAM -3
#define SBI_ERR_DENIED -4
#define SBI_ERR_INVALID_ADDRESS -5
+#define SBI_ERR_ALREADY_AVAILABLE -6
+#define SBI_ERR_ALREADY_STARTED -7
+#define SBI_ERR_ALREADY_STOPPED -8
+#define SBI_ERR_NO_SHMEM -9
+#define SBI_ERR_INVALID_STATE -10
+#define SBI_ERR_BAD_RANGE -11
+#define SBI_ERR_TIMEOUT -12
+#define SBI_ERR_IO -13
+#define SBI_ERR_DENIED_LOCKED -14
+
+static inline int sbi_to_linux_error(int err)
+{
+ switch (err) {
+ case SBI_SUCCESS:
+ return 0;
+ case SBI_ERR_DENIED:
+ case SBI_ERR_DENIED_LOCKED:
+ return -EPERM;
+ case SBI_ERR_INVALID_PARAM:
+ case SBI_ERR_INVALID_STATE:
+ return -EINVAL;
+ case SBI_ERR_BAD_RANGE:
+ return -ERANGE;
+ case SBI_ERR_INVALID_ADDRESS:
+ return -EFAULT;
+ case SBI_ERR_NO_SHMEM:
+ return -ENOMEM;
+ case SBI_ERR_TIMEOUT:
+ return -ETIMEDOUT;
+ case SBI_ERR_IO:
+ return -EIO;
+ case SBI_ERR_NOT_SUPPORTED:
+ case SBI_ERR_FAILURE:
+ default:
+ return -EOPNOTSUPP;
+ };
+}
struct sbiret {
long error;
diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
index 54fd77ad8f48..708d931263ff 100644
--- a/drivers/firmware/rpmi/Kconfig
+++ b/drivers/firmware/rpmi/Kconfig
@@ -7,3 +7,12 @@ config RPMI_FIRMWARE
protocol for communication between an Application Processor (AP) and a
platform microcontroller (PuC). The RPMI specification is available
at: https://github.com/riscv-non-isa/riscv-rpmi
+
+config RPMI_SBI_MPXY
+ bool "RPMI over SBI MPXY transport"
+ depends on RPMI_FIRMWARE && SBI
+ help
+ Enable support for transport of RPMI messages over the message proxy
+ (MPXY) extension of the supervisor binary interface (SBI). The
+ specification for the MPXY extension is available at:
+ https://github.com/riscv-non-isa/riscv-sbi-doc (chapter 20)
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index d69c718d150e..71c4aeaed910 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1 +1,2 @@
obj-y += rpmi-uclass.o
+obj-$(CONFIG_RPMI_SBI_MPXY) += rpmi-sbi-mpxy.o
diff --git a/drivers/firmware/rpmi/rpmi-sbi-mpxy.c b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
new file mode 100644
index 000000000000..9ccca0eb59b2
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <asm/io.h>
+#include <asm/sbi.h>
+#include <asm/types.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <log.h>
+#include <malloc.h>
+#include <rpmi-uclass.h>
+#include <string.h>
+
+/* SBI MPXY channel IDs data in shared memory */
+struct sbi_mpxy_channel_ids_data {
+ /* Remaining number of channel ids */
+ __le32 remaining;
+ /* Returned channel ids in current function call */
+ __le32 returned;
+ /* Returned channel id array */
+ __le32 channel_array[];
+};
+
+enum sbi_mpxy_attribute_id {
+ /* Standard channel attributes managed by MPXY framework */
+ SBI_MPXY_ATTR_MSG_PROT_ID = 0x00000000,
+ SBI_MPXY_ATTR_MSG_PROT_VER = 0x00000001,
+ SBI_MPXY_ATTR_MSG_MAX_LEN = 0x00000002,
+ SBI_MPXY_ATTR_MSG_SEND_TIMEOUT = 0x00000003,
+ SBI_MPXY_ATTR_MSG_COMPLETION_TIMEOUT = 0x00000004,
+ SBI_MPXY_ATTR_CHANNEL_CAPABILITY = 0x00000005,
+ SBI_MPXY_ATTR_SSE_EVENT_ID = 0x00000006,
+ SBI_MPXY_ATTR_MSI_CONTROL = 0x00000007,
+ SBI_MPXY_ATTR_MSI_ADDR_LO = 0x00000008,
+ SBI_MPXY_ATTR_MSI_ADDR_HI = 0x00000009,
+ SBI_MPXY_ATTR_MSI_DATA = 0x0000000A,
+ SBI_MPXY_ATTR_EVENTS_STATE_CONTROL = 0x0000000B,
+ SBI_MPXY_ATTR_STD_ATTR_MAX_IDX,
+ /*
+ * Message protocol specific attributes, managed by
+ * the message protocol specification.
+ */
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_START = 0x80000000,
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_END = 0xffffffff
+};
+
+/* SBI_MPXY_ATTR_CHANNEL_CAPABILITY bits */
+#define SBI_MPXY_CHAN_CAP_MSI BIT(0)
+#define SBI_MPXY_CHAN_CAP_SSE BIT(1)
+#define SBI_MPXY_CHAN_CAP_EVENTS_STATE BIT(2)
+#define SBI_MPXY_CHAN_CAP_SEND_WITH_RESP BIT(3)
+#define SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP BIT(4)
+#define SBI_MPXY_CHAN_CAP_GET_NOTIFICATIONS BIT(5)
+
+/* RPMI message protocol specific MPXY attributes */
+enum sbi_mpxy_rpmi_attribute_id {
+ SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID,
+ SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION,
+ SBI_MPXY_RPMI_ATTR_IMPL_ID,
+ SBI_MPXY_RPMI_ATTR_IMPL_VERSION,
+ SBI_MPXY_RPMI_ATTR_MAX_ID
+};
+
+/* Possible values of MSG_PROT_ID attribute as-per SBI v3.0 (or higher) */
+enum sbi_mpxy_msgproto_id {
+ SBI_MPXY_MSGPROTO_RPMI_ID = 0x0,
+};
+
+struct sbi_mpxy_chan_desc {
+ u32 id;
+ u32 max_xfer_len;
+ u32 attrs[SBI_MPXY_ATTR_STD_ATTR_MAX_IDX];
+ u32 rpmi_attrs[SBI_MPXY_RPMI_ATTR_MAX_ID];
+};
+
+struct rpmi_sbi_mpxy {
+ struct udevice *dev;
+ void *shmem;
+ unsigned long shmem_size;
+ u32 channel_count;
+ struct sbi_mpxy_chan_desc *channel_descs;
+};
+
+static int rpmi_sbi_mpxy_read_attrs(struct rpmi_sbi_mpxy *mpxy, u32 channel_id,
+ u32 base_attrid, u32 attr_count,
+ u32 *attrs_buf)
+{
+ struct sbiret sret;
+ int i;
+
+ sret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS, channel_id,
+ base_attrid, attr_count, 0, 0, 0);
+ if (sret.error)
+ return sbi_to_linux_error(sret.error);
+
+ for (i = 0; i < attr_count; i++)
+ attrs_buf[i] = le32_to_cpu(((__le32 *)mpxy->shmem)[i]);
+
+ return 0;
+}
+
+static int rpmi_sbi_mpxy_send_message(void *shmem, u32 mpxy_fid, u32 channel_id,
+ u32 msg_id, const void *tx,
+ unsigned long tx_len, void *rx,
+ unsigned long max_rx_len,
+ unsigned long *rx_len)
+{
+ unsigned long rx_bytes;
+ struct sbiret sret;
+
+ if (tx)
+ memcpy(shmem, tx, tx_len);
+
+ sret = sbi_ecall(SBI_EXT_MPXY, mpxy_fid, channel_id, msg_id, tx_len, 0,
+ 0, 0);
+
+ if (rx && !sret.error) {
+ rx_bytes = sret.value;
+ if (rx_bytes > max_rx_len)
+ return -ENOSPC;
+
+ memcpy(rx, shmem, rx_bytes);
+ if (rx_len)
+ *rx_len = rx_bytes;
+ }
+
+ return sbi_to_linux_error(sret.error);
+}
+
+static int rpmi_sbi_mpxy_get_attr(struct rpmi_chan *chan,
+ enum rpmi_attribute_id id, u32 *data)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(chan->dev);
+ struct sbi_mpxy_chan_desc *desc = &mpxy->channel_descs[chan->id];
+
+ switch (id) {
+ case RPMI_ATTR_SPEC_VERSION:
+ *data = desc->attrs[SBI_MPXY_ATTR_MSG_PROT_VER];
+ break;
+ case RPMI_ATTR_MAX_MSG_DATA_SIZE:
+ *data = desc->max_xfer_len;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_ID:
+ *data = desc->rpmi_attrs[SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID];
+ break;
+ case RPMI_ATTR_SERVICEGROUP_VERSION:
+ *data = desc->rpmi_attrs[SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION];
+ break;
+ case RPMI_ATTR_IMPL_ID:
+ *data = desc->rpmi_attrs[SBI_MPXY_RPMI_ATTR_IMPL_ID];
+ break;
+ case RPMI_ATTR_IMPL_VERSION:
+ *data = desc->rpmi_attrs[SBI_MPXY_RPMI_ATTR_IMPL_VERSION];
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int rpmi_sbi_mpxy_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)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(chan->dev);
+ struct sbi_mpxy_chan_desc *desc = &mpxy->channel_descs[chan->id];
+ u32 caps = desc->attrs[SBI_MPXY_ATTR_CHANNEL_CAPABILITY];
+ u32 mpxy_fid;
+
+ if (!request && request_len)
+ return -EINVAL;
+
+ if (request_len > desc->max_xfer_len)
+ return -EINVAL;
+
+ if (!response && max_response_len)
+ return -EINVAL;
+
+ if (response) {
+ if (!(caps & SBI_MPXY_CHAN_CAP_SEND_WITH_RESP))
+ return -EIO;
+
+ mpxy_fid = SBI_EXT_MPXY_SEND_MSG_WITH_RESP;
+ } else {
+ if (!(caps & SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP))
+ return -EIO;
+
+ mpxy_fid = SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP;
+ }
+
+ return rpmi_sbi_mpxy_send_message(mpxy->shmem, mpxy_fid, desc->id,
+ service_id, request, request_len,
+ response, max_response_len,
+ out_response_len);
+}
+
+static int rpmi_sbi_mpxy_of_xlate(struct rpmi_chan *chan,
+ struct ofnode_phandle_args *args)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(chan->dev);
+ u32 channel_id, proto;
+ int i;
+
+ if (args->args_count != 2)
+ return -EINVAL;
+
+ channel_id = args->args[0];
+ proto = args->args[1];
+
+ for (i = 0; i < mpxy->channel_count; i++) {
+ if (mpxy->channel_descs[i].id == channel_id &&
+ mpxy->channel_descs[i].attrs[SBI_MPXY_ATTR_MSG_PROT_ID] ==
+ proto) {
+ chan->id = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int rpmi_sbi_mpxy_get_shmem_size(unsigned long *shmem_size)
+{
+ struct sbiret sret;
+
+ sret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_SHMEM_SIZE, 0, 0, 0, 0,
+ 0, 0);
+ if (sret.error)
+ return sbi_to_linux_error(sret.error);
+
+ /* The shared memory size MUST be at least 4096 bytes */
+ if (sret.value < PAGE_SIZE)
+ return -ENODEV;
+
+ /* The shared memory size MUST be multiple of 4096 bytes */
+ if (sret.value & (PAGE_SIZE - 1))
+ return -ENODEV;
+
+ if (shmem_size)
+ *shmem_size = sret.value;
+
+ return 0;
+}
+
+static int rpmi_sbi_mpxy_get_channel_ids(struct rpmi_sbi_mpxy *mpxy)
+{
+ struct sbi_mpxy_channel_ids_data *sdata = mpxy->shmem;
+ u32 remaining, returned, channel_id, start_index = 0;
+ struct sbiret sret;
+ int i, ret;
+
+ do {
+ sret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_CHANNEL_IDS,
+ start_index, 0, 0, 0, 0, 0);
+ if (sret.error)
+ return sbi_to_linux_error(sret.error);
+
+ remaining = le32_to_cpu(sdata->remaining);
+ returned = le32_to_cpu(sdata->returned);
+
+ // Allocate memory on the first call.
+ if (!mpxy->channel_descs) {
+ mpxy->channel_count = returned + remaining;
+ if (mpxy->channel_count == 0)
+ return -ENODEV;
+
+ mpxy->channel_descs =
+ calloc(mpxy->channel_count,
+ sizeof(struct sbi_mpxy_chan_desc));
+ if (!mpxy->channel_descs)
+ return -ENOMEM;
+
+ dev_dbg(mpxy->dev, "channel count = %i\n",
+ mpxy->channel_count);
+ }
+
+ if (remaining && !returned)
+ return -EPROTO;
+
+ if (start_index + returned > mpxy->channel_count)
+ return -EPROTO;
+
+ for (i = 0; i < returned; i++) {
+ mpxy->channel_descs[start_index + i].id =
+ le32_to_cpu(sdata->channel_array[i]);
+ }
+ start_index += returned;
+ } while (remaining);
+
+ for (i = 0; i < mpxy->channel_count; i++) {
+ channel_id = mpxy->channel_descs[i].id;
+
+ ret = rpmi_sbi_mpxy_read_attrs(mpxy, channel_id,
+ SBI_MPXY_ATTR_MSG_PROT_ID,
+ SBI_MPXY_ATTR_STD_ATTR_MAX_IDX,
+ mpxy->channel_descs[i].attrs);
+ if (ret)
+ return ret;
+
+ if (mpxy->channel_descs[i].attrs[SBI_MPXY_ATTR_MSG_PROT_ID] ==
+ SBI_MPXY_MSGPROTO_RPMI_ID) {
+ ret = rpmi_sbi_mpxy_read_attrs(
+ mpxy, channel_id,
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
+ SBI_MPXY_RPMI_ATTR_MAX_ID,
+ mpxy->channel_descs[i].rpmi_attrs);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rpmi_sbi_mpxy_probe(struct udevice *dev)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(dev);
+ struct sbi_mpxy_chan_desc *desc;
+ unsigned long flags;
+ struct sbiret sret;
+ int i, ret;
+
+ mpxy->dev = dev;
+
+ /* Probe for SBI MPXY extension */
+ if (sbi_get_spec_version() < sbi_mk_version(1, 0) ||
+ sbi_probe_extension(SBI_EXT_MPXY) <= 0) {
+ dev_info(dev, "SBI MPXY extension not available\n");
+ return -ENODEV;
+ }
+
+ /* Find-out shared memory size */
+ ret = rpmi_sbi_mpxy_get_shmem_size(&mpxy->shmem_size);
+ if (ret) {
+ dev_err(dev, "failed to get MPXY shared memory size\n");
+ return -ENODEV;
+ }
+
+ mpxy->shmem = memalign(PAGE_SIZE, mpxy->shmem_size);
+ if (!mpxy->shmem)
+ return -ENOMEM;
+
+ /* Setup shmem in OVERWRITE mode. (flags[1:0] = 00b) */
+ flags = 0;
+ sret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM,
+ virt_to_phys(mpxy->shmem), 0, flags, 0, 0, 0);
+ if (sret.error) {
+ ret = sbi_to_linux_error(sret.error);
+ goto err;
+ }
+
+ ret = rpmi_sbi_mpxy_get_channel_ids(mpxy);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < mpxy->channel_count; i++) {
+ desc = &mpxy->channel_descs[i];
+ desc->max_xfer_len =
+ min((u32)mpxy->shmem_size,
+ desc->attrs[SBI_MPXY_ATTR_MSG_MAX_LEN]);
+ }
+
+ return 0;
+
+err:
+ free(mpxy->channel_descs);
+ free(mpxy->shmem);
+
+ return ret;
+}
+
+static int rpmi_sbi_mpxy_remove(struct udevice *dev)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(dev);
+
+ free(mpxy->channel_descs);
+ free(mpxy->shmem);
+
+ return 0;
+}
+
+static const struct udevice_id rpmi_sbi_mpxy_ids[] = {
+ { .compatible = "riscv,sbi-mpxy-mbox" },
+ {}
+};
+
+static const struct rpmi_transport_ops rpmi_sbi_mpxy_ops = {
+ .of_xlate = rpmi_sbi_mpxy_of_xlate,
+ .get_attr = rpmi_sbi_mpxy_get_attr,
+ .request = rpmi_sbi_mpxy_request,
+};
+
+U_BOOT_DRIVER(rpmi_sbi_mpxy) = {
+ .name = "rpmi-sbi-mpxy",
+ .id = UCLASS_RPMI,
+ .of_match = rpmi_sbi_mpxy_ids,
+ .probe = rpmi_sbi_mpxy_probe,
+ .remove = rpmi_sbi_mpxy_remove,
+ .priv_auto = sizeof(struct rpmi_sbi_mpxy),
+ .ops = &rpmi_sbi_mpxy_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 3/5] firmware: rpmi: add support for shared memory transport
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
2026-07-08 21:51 ` [PATCH v2 1/5] firmware: add support for RPMI Charles Perry
2026-07-08 21:51 ` [PATCH v2 2/5] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
@ 2026-07-08 21:51 ` Charles Perry
2026-07-08 21:51 ` [PATCH v2 4/5] drivers: clk: add support for RPMI clocks Charles Perry
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Charles Perry @ 2026-07-08 21:51 UTC (permalink / raw)
To: u-boot; +Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini
Add support for transport of RPMI messages over shared memory as
described in the RPMI spec [1]. This uses two or four ring buffers
composed of a fixed number of "slots" to pass messages between the AP
and the PuC. U-Boot is the Application processor (AP) and sends messages
through the a2p-req queue and receives response though the p2a-ack
queue. There is no support for platform microcontroller (PuC) initiated
requests nor notifications.
This transport can be used in M or S mode U-Boot, but in the S-mode
case, one must make sure that the supervisor software isn't already
using the AP side of the shared memory.
[1]: https://github.com/riscv-non-isa/riscv-rpmi (chapter 2)
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
Changes in v2: (from Yao Zi review)
- Reorder headers
- Don't null check pointers before free()
---
drivers/firmware/rpmi/Kconfig | 8 +
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-shmem.c | 473 +++++++++++++++++++++++++++++
include/rpmi_proto.h | 23 ++
4 files changed, 505 insertions(+)
create mode 100644 drivers/firmware/rpmi/rpmi-shmem.c
diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
index 708d931263ff..421a1fa9deba 100644
--- a/drivers/firmware/rpmi/Kconfig
+++ b/drivers/firmware/rpmi/Kconfig
@@ -16,3 +16,11 @@ config RPMI_SBI_MPXY
(MPXY) extension of the supervisor binary interface (SBI). The
specification for the MPXY extension is available at:
https://github.com/riscv-non-isa/riscv-sbi-doc (chapter 20)
+
+config RPMI_SHMEM
+ bool "RPMI over shared memory transport"
+ depends on RPMI_FIRMWARE
+ help
+ Enable support for transport of RPMI messages over shared memory as
+ described in chapter 2 of the RPMI specification, available at:
+ https://github.com/riscv-non-isa/riscv-rpmi
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index 71c4aeaed910..4f4bcb929db4 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1,2 +1,3 @@
obj-y += rpmi-uclass.o
obj-$(CONFIG_RPMI_SBI_MPXY) += rpmi-sbi-mpxy.o
+obj-$(CONFIG_RPMI_SHMEM) += rpmi-shmem.o
diff --git a/drivers/firmware/rpmi/rpmi-shmem.c b/drivers/firmware/rpmi/rpmi-shmem.c
new file mode 100644
index 000000000000..8014e86d336f
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-shmem.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <asm/barrier.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <log.h>
+#include <malloc.h>
+#include <rpmi-uclass.h>
+
+#define RPMI_SLOT_SIZE_MIN 64
+
+enum rpmi_queue_idx {
+ RPMI_QUEUE_IDX_A2P_REQ = 0,
+ RPMI_QUEUE_IDX_P2A_ACK = 1,
+ RPMI_QUEUE_IDX_P2A_REQ = 2,
+ RPMI_QUEUE_IDX_A2P_ACK = 3,
+ RPMI_QUEUE_IDX_MAX_COUNT = 4,
+ RPMI_A2P_DOORBELL = 5,
+};
+
+struct shmem_queue {
+ u32 num_slot;
+ __le32 *headptr;
+ __le32 *tailptr;
+ void *buffer;
+};
+
+struct rpmi_shmem {
+ struct udevice *dev;
+ struct shmem_queue a2p_req;
+ struct shmem_queue p2a_ack;
+ int slot_size;
+ int rx_timeout_ms;
+ u32 token;
+ void *doorbell;
+ u32 doorbell_value;
+ u32 spec_version;
+ u32 impl_version;
+ u32 impl_id;
+};
+
+struct rpmi_shmem_chan_priv {
+ struct rpmi_shmem *priv;
+ u32 servicegroup_id;
+ u32 servicegroup_version;
+};
+
+static int rpmi_shmem_queue_idx(const char *name)
+{
+ static const struct {
+ int id;
+ const char *name;
+ } id_names[] = {
+ { RPMI_QUEUE_IDX_A2P_REQ, "a2p-req" },
+ { RPMI_QUEUE_IDX_P2A_ACK, "p2a-ack" },
+ { RPMI_QUEUE_IDX_P2A_REQ, "p2a-req" },
+ { RPMI_QUEUE_IDX_A2P_ACK, "a2p-ack" },
+ { RPMI_A2P_DOORBELL, "a2p-doorbell" },
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(id_names); i++) {
+ if (strcmp(name, id_names[i].name) == 0)
+ return id_names[i].id;
+ }
+
+ return -ENODEV;
+}
+
+static bool rpmi_smq_queue_full(struct shmem_queue *qctx)
+{
+ return ((le32_to_cpu(*qctx->tailptr) + 1) % qctx->num_slot ==
+ le32_to_cpu(*qctx->headptr));
+}
+
+/* 31 0
+ * +---------------------+-----------------------+
+ * | FLAGS | SERVICE_ID | SERVICEGROUP_ID |
+ * +---------------------+-----------------------+
+ * | TOKEN | DATA LENGTH |
+ * +---------------------+-----------------------+
+ * | DATA/PAYLOAD |
+ * +---------------------------------------------+
+ */
+struct rpmi_message_header {
+ __le16 servicegroup_id;
+ u8 service_id;
+ u8 flags;
+ __le16 datalen;
+ __le16 token;
+} __packed;
+
+static int rpmi_smq_rx(struct rpmi_shmem *priv, struct shmem_queue *qctx,
+ u32 service_group_id, u32 service_id, u16 token,
+ void *response, unsigned long max_response_len,
+ unsigned long *out_response_len)
+{
+ struct rpmi_message_header *msg;
+ u32 dlen, headidx, tailidx;
+ int rxretry = 0;
+ void *src;
+
+ headidx = le32_to_cpu(*qctx->headptr);
+
+ /* Wait for the Rx message with matching token, discard any other
+ * messages received in the mean time
+ */
+ while (true) {
+ tailidx = le32_to_cpu(*qctx->tailptr);
+
+ if (headidx != tailidx) {
+ msg = (void *)qctx->buffer +
+ (headidx * priv->slot_size);
+ if (le16_to_cpu(msg->token) == token)
+ break;
+
+ headidx = (headidx + 1) % qctx->num_slot;
+ }
+
+ mdelay(1);
+ rxretry += 1;
+ if (rxretry > priv->rx_timeout_ms)
+ return -ETIMEDOUT;
+ }
+
+ /* Extract data from the message */
+ if (response) {
+ dlen = le16_to_cpu(msg->datalen);
+ if (dlen > max_response_len)
+ dlen = max_response_len;
+ if (out_response_len)
+ *out_response_len = dlen;
+ src = (void *)msg + sizeof(struct rpmi_message_header);
+ memcpy(response, src, dlen);
+ }
+
+ /* Update the head index */
+ headidx = (headidx + 1) % qctx->num_slot;
+ *qctx->headptr = cpu_to_le32(headidx);
+
+ return 0;
+}
+
+static int rpmi_smq_tx(struct rpmi_shmem *priv, struct shmem_queue *qctx,
+ u32 service_group_id, u32 service_id,
+ enum rpmi_message_type type, u16 token,
+ const void *request, unsigned long request_len)
+{
+ struct rpmi_message_header header = { 0 };
+ u32 tailidx;
+ void *dst;
+
+ /* Tx sanity checks */
+ if (request_len >
+ (priv->slot_size - sizeof(struct rpmi_message_header)))
+ return -EINVAL;
+
+ if (rpmi_smq_queue_full(qctx))
+ return -ENOMEM;
+
+ tailidx = le32_to_cpu(*qctx->tailptr);
+
+ /* Prepare the header to be written into the slot */
+ header.servicegroup_id = cpu_to_le16(service_group_id);
+ header.service_id = service_id;
+ header.flags = type;
+ header.datalen = cpu_to_le16(request_len);
+ header.token = cpu_to_le16(token);
+
+ /* Write header into the slot */
+ dst = (char *)qctx->buffer + (tailidx * priv->slot_size);
+ memcpy(dst, &header, sizeof(header));
+ dst += sizeof(header);
+
+ /* Write data into the slot */
+ if (request)
+ memcpy(dst, request, request_len);
+
+ /* Make sure queue chanages are visible to PuC before updating tail */
+ wmb();
+
+ /* Update the tail/write index */
+ tailidx = (tailidx + 1) % qctx->num_slot;
+ *qctx->tailptr = cpu_to_le32(tailidx);
+
+ /* Ring the RPMI doorbell if present */
+ if (priv->doorbell)
+ writel(priv->doorbell_value, priv->doorbell);
+
+ return 0;
+}
+
+static int rpmi_shmem_request_priv(struct rpmi_shmem *priv, u32 servicegroup_id,
+ u32 service_id, const void *request,
+ unsigned long request_len, void *response,
+ unsigned long max_response_len,
+ unsigned long *out_response_len)
+{
+ enum rpmi_message_type type = response ? RPMI_MSG_NORMAL_REQUEST :
+ RPMI_MSG_POSTED_REQUEST;
+ int err;
+
+ if (!request && request_len)
+ return -EINVAL;
+
+ err = rpmi_smq_tx(priv, &priv->a2p_req, servicegroup_id, service_id,
+ type, priv->token, request, request_len);
+ if (err) {
+ dev_err(priv->dev, "tx failed %i\n", err);
+ goto out;
+ }
+
+ if (response) {
+ err = rpmi_smq_rx(priv, &priv->p2a_ack, servicegroup_id,
+ service_id, priv->token, response,
+ max_response_len, out_response_len);
+ if (err) {
+ dev_err(priv->dev, "rx failed %i\n", err);
+ goto out;
+ }
+ }
+
+out:
+ priv->token++;
+ return err;
+}
+
+static int rpmi_shmem_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)
+{
+ struct rpmi_shmem *priv = dev_get_priv(chan->dev);
+ struct rpmi_shmem_chan_priv *chan_priv = chan->con_priv;
+
+ return rpmi_shmem_request_priv(priv, chan_priv->servicegroup_id,
+ service_id, request, request_len,
+ response, max_response_len,
+ out_response_len);
+}
+
+static int rpmi_shmem_get_attr(struct rpmi_chan *chan,
+ enum rpmi_attribute_id id, u32 *data)
+{
+ struct rpmi_shmem *priv = dev_get_priv(chan->dev);
+ struct rpmi_shmem_chan_priv *chan_priv = chan->con_priv;
+
+ switch (id) {
+ case RPMI_ATTR_SPEC_VERSION:
+ *data = priv->spec_version;
+ break;
+ case RPMI_ATTR_MAX_MSG_DATA_SIZE:
+ *data = priv->slot_size - sizeof(struct rpmi_message_header);
+ break;
+ case RPMI_ATTR_SERVICEGROUP_ID:
+ *data = chan_priv->servicegroup_id;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_VERSION:
+ *data = chan_priv->servicegroup_version;
+ break;
+ case RPMI_ATTR_IMPL_ID:
+ *data = priv->impl_id;
+ break;
+ case RPMI_ATTR_IMPL_VERSION:
+ *data = priv->impl_version;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int rpmi_shmem_base_service_req(struct rpmi_shmem *priv, u32 service_id,
+ const u32 *arg_in, u32 *arg_out)
+{
+ int len = 0;
+ u32 rx[2];
+ u32 tx[1];
+ int ret;
+
+ if (arg_in) {
+ tx[0] = cpu_to_le32(*arg_in);
+ len = sizeof(*arg_in);
+ }
+
+ ret = rpmi_shmem_request_priv(priv, RPMI_SRVGRP_BASE, service_id, tx,
+ len, rx, sizeof(rx), NULL);
+ if (ret) {
+ dev_err(priv->dev, "BASE service %i failed (%i)\n", service_id,
+ ret);
+ return ret;
+ }
+
+ if (rx[0]) {
+ ret = rpmi_to_linux_error(le32_to_cpu(rx[0]));
+ dev_err(priv->dev, "BASE service %i returned code %i\n",
+ service_id, ret);
+ return ret;
+ }
+
+ if (arg_out)
+ *arg_out = le32_to_cpu(rx[1]);
+
+ return 0;
+}
+
+static int rpmi_shmem_of_xlate(struct rpmi_chan *chan,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ // args[0] is the servicegroup id.
+ chan->id = args->args[0];
+
+ return 0;
+}
+
+static int rpmi_shmem_open(struct rpmi_chan *chan)
+{
+ struct rpmi_shmem *priv = dev_get_priv(chan->dev);
+ u32 servicegroup_id, servicegroup_version;
+ struct rpmi_shmem_chan_priv *chan_priv;
+ int ret;
+
+ servicegroup_id = chan->id;
+
+ ret = rpmi_shmem_base_service_req(priv,
+ RPMI_BASE_SRV_PROBE_SERVICE_GROUP,
+ &servicegroup_id,
+ &servicegroup_version);
+ if (ret)
+ return ret;
+
+ chan_priv = malloc(sizeof(*chan_priv));
+ if (!chan_priv)
+ return -ENOMEM;
+
+ chan->con_priv = chan_priv;
+
+ chan_priv->priv = priv;
+ chan_priv->servicegroup_id = servicegroup_id;
+ chan_priv->servicegroup_version = servicegroup_version;
+
+ return 0;
+}
+
+static void rpmi_shmem_close(struct rpmi_chan *chan)
+{
+ free(chan->con_priv);
+}
+
+static int rpmi_shmem_probe(struct udevice *dev)
+{
+ struct rpmi_shmem *priv = dev_get_priv(dev);
+ int i, ret, qidx, qcount, qslotcnt;
+ struct shmem_queue *queue;
+ u32 arg_in, arg_out;
+ const char *qname;
+ fdt_size_t qsize;
+ void *start;
+
+ priv->dev = dev;
+ priv->rx_timeout_ms = 20;
+
+ priv->slot_size = dev_read_u32_default(dev, "riscv,slot-size", 0);
+ if (priv->slot_size < RPMI_SLOT_SIZE_MIN)
+ return -EINVAL;
+
+ priv->doorbell_value =
+ dev_read_u32_default(dev, "riscv,a2p-doorbell-value", 0);
+
+ qcount = dev_read_string_count(dev, "reg-names");
+ if (qcount < 0 || qcount > 5)
+ return -EINVAL;
+
+ for (i = 0; i < qcount; i++) {
+ ret = dev_read_string_index(dev, "reg-names", i, &qname);
+ if (ret)
+ continue;
+
+ qidx = rpmi_shmem_queue_idx(qname);
+ if (qidx < 0)
+ continue;
+
+ start = dev_read_addr_size_index_ptr(dev, i, &qsize);
+ if (!start)
+ continue;
+
+ if (qidx == RPMI_A2P_DOORBELL) {
+ priv->doorbell = start;
+ } else {
+ qslotcnt = qsize / priv->slot_size;
+ if (qslotcnt < 4)
+ continue;
+
+ if (qidx == RPMI_QUEUE_IDX_A2P_REQ)
+ queue = &priv->a2p_req;
+ else if (qidx == RPMI_QUEUE_IDX_P2A_ACK)
+ queue = &priv->p2a_ack;
+ else
+ continue;
+
+ queue->headptr = start;
+ queue->tailptr = start + priv->slot_size;
+ queue->buffer = start + (2 * priv->slot_size);
+ queue->num_slot = qslotcnt - 2;
+ }
+ }
+
+ if (!priv->a2p_req.num_slot || !priv->p2a_ack.num_slot)
+ return -EINVAL;
+
+ arg_in = RPMI_SRVGRP_BASE;
+ ret = rpmi_shmem_base_service_req(
+ priv, RPMI_BASE_SRV_PROBE_SERVICE_GROUP, &arg_in, &arg_out);
+ if (ret)
+ return ret;
+
+ if (arg_out < RPMI_MKVER(1, 0))
+ return -EINVAL;
+
+ ret = rpmi_shmem_base_service_req(
+ priv, RPMI_BASE_SRV_GET_IMPLEMENTATION_VERSION, NULL, &arg_out);
+ if (ret)
+ return ret;
+ priv->impl_version = arg_out;
+
+ ret = rpmi_shmem_base_service_req(
+ priv, RPMI_BASE_SRV_GET_IMPLEMENTATION_IDN, NULL, &arg_out);
+ if (ret)
+ return ret;
+ priv->impl_id = arg_out;
+
+ ret = rpmi_shmem_base_service_req(priv, RPMI_BASE_SRV_GET_SPEC_VERSION,
+ NULL, &arg_out);
+ if (ret)
+ return ret;
+ priv->spec_version = arg_out;
+
+ return 0;
+}
+
+static const struct udevice_id rpmi_shmem_ids[] = {
+ { .compatible = "riscv,rpmi-shmem-mbox" },
+ {}
+};
+
+static const struct rpmi_transport_ops rpmi_shmem_ops = {
+ .of_xlate = rpmi_shmem_of_xlate,
+ .get_attr = rpmi_shmem_get_attr,
+ .request = rpmi_shmem_request,
+ .open = rpmi_shmem_open,
+ .close = rpmi_shmem_close,
+};
+
+U_BOOT_DRIVER(rpmi_shmem) = {
+ .name = "rpmi-shmem",
+ .id = UCLASS_RPMI,
+ .of_match = rpmi_shmem_ids,
+ .probe = rpmi_shmem_probe,
+ .priv_auto = sizeof(struct rpmi_shmem),
+ .ops = &rpmi_shmem_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/rpmi_proto.h b/include/rpmi_proto.h
index 012ae93d7317..4b557bfeedf5 100644
--- a/include/rpmi_proto.h
+++ b/include/rpmi_proto.h
@@ -60,4 +60,27 @@ enum rpmi_servicegroup_id {
RPMI_SRVGRP_VENDOR_END = 0xFFFF,
};
+/** RPMI Base ServiceGroup Service IDs */
+enum rpmi_base_service_id {
+ RPMI_BASE_SRV_ENABLE_NOTIFICATION = 0x01,
+ RPMI_BASE_SRV_GET_IMPLEMENTATION_VERSION = 0x02,
+ RPMI_BASE_SRV_GET_IMPLEMENTATION_IDN = 0x03,
+ RPMI_BASE_SRV_GET_SPEC_VERSION = 0x04,
+ RPMI_BASE_SRV_GET_PLATFORM_INFO = 0x05,
+ RPMI_BASE_SRV_PROBE_SERVICE_GROUP = 0x06,
+ RPMI_BASE_SRV_GET_ATTRIBUTES = 0x07,
+};
+
+/** RPMI Messages Types */
+enum rpmi_message_type {
+ /* Normal request backed with ack */
+ RPMI_MSG_NORMAL_REQUEST = 0x0,
+ /* Request without any ack */
+ RPMI_MSG_POSTED_REQUEST = 0x1,
+ /* Acknowledgment for normal request message */
+ RPMI_MSG_ACKNOWLDGEMENT = 0x2,
+ /* Notification message */
+ RPMI_MSG_NOTIFICATION = 0x3,
+};
+
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 4/5] drivers: clk: add support for RPMI clocks
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
` (2 preceding siblings ...)
2026-07-08 21:51 ` [PATCH v2 3/5] firmware: rpmi: add support for shared memory transport Charles Perry
@ 2026-07-08 21:51 ` Charles Perry
2026-07-08 21:51 ` [PATCH v2 5/5] firmware: rpmi: add a test and a sandbox driver Charles Perry
2026-07-09 13:02 ` [PATCH v2 0/5] Add support for RPMI to U-Boot Michal Simek
5 siblings, 0 replies; 14+ messages in thread
From: Charles Perry @ 2026-07-08 21:51 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini,
Lukasz Majewski, Neil Armstrong, Simon Glass, David Lechner,
Peng Fan, Julien Stephan, Kory Maincent, Kuan-Wei Chiu,
Bastien Curutchet, Raymond Mao, Marek Vasut, Michal Simek,
Quentin Schulz, Peter Korsgaard, Michael Trimarchi, Johan Jonker,
Alexander Sverdlin
The RISC-V Platform Management Interface (RPMI) defines a service group
for control and monitoring of clocks [1]. This can be exposed as a
UCLASS_CLK driver.
[1]: https://github.com/riscv-non-isa/riscv-rpmi (chapter 4.8)
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
Changes in v2: (from Yao Zi review)
- Reorder headers
- Don't null check pointers before free()
- Remove transition_latency from struct rpmi_clk_def as its unused
- Simplify last return path of _rpmi_clk_enable_disable()
- Move the call to _rpmi_clk_get_rate() from _rpmi_clk_set_rate() to
rpmi_clk_set_rate()
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk_rpmi.c | 344 +++++++++++++++++++++++++++++++++++++++++
include/rpmi_proto.h | 39 +++++
5 files changed, 392 insertions(+)
create mode 100644 drivers/clk/clk_rpmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 812942a2d8a7..1e8f57c93a55 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1654,6 +1654,7 @@ F: doc/README.rockusb
RPMI
M: Charles Perry <charles.perry@microchip.com>
S: Maintained
+F: drivers/clk/clk_rpmi.c
F: drivers/firmware/rpmi/
F: include/rpmi*
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index addcece4da36..9167ae11dc8c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -181,6 +181,13 @@ config CLK_OCTEON
help
Enable this to support the clocks on Octeon MIPS platforms.
+config CLK_RPMI
+ bool "Enable RPMI clock driver"
+ depends on CLK && RPMI_FIRMWARE
+ help
+ Enable this option if you want to support clock devices exposed
+ by RISC-V Platform Management Interface (RPMI).
+
config SANDBOX_CLK_CCF
bool "Sandbox Common Clock Framework [CCF] support"
depends on SANDBOX
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index c37ef75d420f..e76b95bdfcd5 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_QCOM) += qcom/
obj-$(CONFIG_CLK_RENESAS) += renesas/
+obj-$(CONFIG_CLK_RPMI) += clk_rpmi.o
obj-$(CONFIG_$(PHASE_)CLK_SCMI) += clk_scmi.o
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_CLK_SOPHGO) += sophgo/
diff --git a/drivers/clk/clk_rpmi.c b/drivers/clk/clk_rpmi.c
new file mode 100644
index 000000000000..42b1616ee7ec
--- /dev/null
+++ b/drivers/clk/clk_rpmi.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_CLK
+
+#include <asm/types.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
+#include <rpmi.h>
+
+struct rpmi_get_num_clocks_rx {
+ __le32 status;
+ __le32 num_clocks;
+};
+
+struct rpmi_get_rate_rx {
+ __le32 status;
+ __le32 lo;
+ __le32 hi;
+};
+
+struct rpmi_get_config_rx {
+ __le32 status;
+ __le32 state;
+};
+
+struct rpmi_set_rate_tx {
+ __le32 clkid;
+ __le32 flags;
+ __le32 lo;
+ __le32 hi;
+};
+
+struct rpmi_set_config_tx {
+ __le32 clkid;
+ __le32 config;
+};
+
+struct rpmi_get_attrs_rx {
+ __le32 status;
+#define RPMI_CLK_TYPE_MASK GENMASK(1, 0)
+ __le32 flags;
+ __le32 num_rates;
+ __le32 transition_latency;
+ char name[RPMI_CLK_NAME_LEN];
+};
+
+struct rpmi_clk_def {
+ u32 num_rates;
+ enum rpmi_clock_type type;
+ char name[RPMI_CLK_NAME_LEN + 1];
+};
+
+struct rpmi_clk_priv {
+ struct rpmi_chan chan;
+ u32 num_clocks;
+ struct rpmi_clk_def *defs;
+};
+
+static int _rpmi_clk_enable_disable(struct rpmi_clk_priv *priv, u32 clkid,
+ bool ena)
+{
+ struct rpmi_set_config_tx tx = {
+ .clkid = cpu_to_le32(clkid),
+ .config = cpu_to_le32(ena ? RPMI_CLK_STATE_ENABLED :
+ RPMI_CLK_STATE_DISABLED),
+ };
+ __le32 rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_SET_CONFIG, &tx,
+ sizeof(tx), &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx);
+
+ return rpmi_to_linux_error(status);
+}
+
+static ulong _rpmi_clk_get_rate(struct rpmi_clk_priv *priv, u32 clkid)
+{
+ __le32 tx = cpu_to_le32(clkid);
+ struct rpmi_get_rate_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_RATE, &tx,
+ sizeof(tx), &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx.status);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ return (((u64)(le32_to_cpu(rx.hi)) << 32) | (u32)(le32_to_cpu(rx.lo)));
+}
+
+static __maybe_unused int _rpmi_clk_get_config(struct rpmi_clk_priv *priv,
+ u32 clkid)
+{
+ __le32 tx = cpu_to_le32(clkid);
+ struct rpmi_get_config_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_CONFIG, &tx,
+ sizeof(tx), &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx.status);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ return le32_to_cpu(rx.state);
+}
+
+static int _rpmi_clk_set_rate(struct rpmi_clk_priv *priv, u32 clkid,
+ unsigned long rate)
+{
+ struct rpmi_set_rate_tx tx = {
+ .clkid = cpu_to_le32(clkid),
+ .flags = 0,
+ .lo = cpu_to_le32(lower_32_bits(rate)),
+ .hi = cpu_to_le32(upper_32_bits(rate)),
+ };
+ __le32 rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_SET_RATE, &tx,
+ sizeof(tx), &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx);
+
+ return rpmi_to_linux_error(status);
+}
+
+static int _rpmi_clk_get_num_clocks(struct rpmi_clk_priv *priv)
+{
+ struct rpmi_get_num_clocks_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_NUM_CLOCKS,
+ NULL, 0, &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx.status);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ priv->num_clocks = le32_to_cpu(rx.num_clocks);
+
+ return 0;
+}
+
+static int rpmi_clk_enable(struct clk *clk)
+{
+ return _rpmi_clk_enable_disable(dev_get_plat(clk->dev),
+ clk->id & CLK_ID_MSK, true);
+}
+
+static int rpmi_clk_disable(struct clk *clk)
+{
+ return _rpmi_clk_enable_disable(dev_get_plat(clk->dev),
+ clk->id & CLK_ID_MSK, false);
+}
+
+static ulong rpmi_clk_get_rate(struct clk *clk)
+{
+ return _rpmi_clk_get_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK);
+}
+
+static ulong rpmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+ int ret;
+
+ ret = _rpmi_clk_set_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK,
+ rate);
+ if (ret)
+ return ret;
+
+ return _rpmi_clk_get_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK);
+}
+
+static __maybe_unused void rpmi_clk_dump(struct udevice *dev)
+{
+ struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+ struct rpmi_clk_def *def;
+ u64 clk_rate = 0;
+ int i, state;
+
+ printf(" Rate State Id Name\n");
+ printf("--------------------------------------\n");
+
+ for (i = 0; i < priv->num_clocks; i++) {
+ def = &priv->defs[i];
+ clk_rate = _rpmi_clk_get_rate(priv, i);
+ state = _rpmi_clk_get_config(priv, i);
+ printf("%18lld %s %4i %s\n", clk_rate,
+ (state == RPMI_CLK_STATE_ENABLED) ? " ON" : " OFF", i,
+ def->name);
+ }
+}
+
+static int rpmi_clk_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+ struct rpmi_clk_priv *priv = dev_get_plat(clk->dev);
+
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ if (args->args[0] >= priv->num_clocks)
+ return -EINVAL;
+
+ clk->id = CLK_ID(clk->dev, args->args[0]);
+ clk->data = 0;
+
+ return 0;
+}
+
+static int rpmi_clk_get_attrs(struct rpmi_clk_priv *priv, u32 clkid,
+ struct rpmi_clk_def *def)
+{
+ __le32 tx = cpu_to_le32(clkid);
+ struct rpmi_get_attrs_rx rx;
+ u8 format;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_ATTRIBUTES, &tx,
+ sizeof(tx), &rx, sizeof(rx), NULL);
+ if (ret)
+ return ret;
+
+ status = le32_to_cpu(rx.status);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ format = le32_to_cpu(rx.flags) & RPMI_CLK_TYPE_MASK;
+ if (format >= RPMI_CLK_TYPE_MAX)
+ return -EINVAL;
+
+ def->type = format;
+ def->num_rates = le32_to_cpu(rx.num_rates);
+ memcpy(def->name, rx.name, RPMI_CLK_NAME_LEN);
+ def->name[RPMI_CLK_NAME_LEN] = '\0';
+
+ return 0;
+}
+
+static int rpmi_clk_probe(struct udevice *dev)
+{
+ struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+ struct rpmi_chan *chan = &priv->chan;
+ int ret, i;
+
+ ret = rpmi_open(chan);
+ if (ret)
+ return ret;
+
+ ret = rpmi_check_versions(chan, RPMI_MKVER(1, 0), RPMI_SRVGRP_CLOCK,
+ RPMI_MKVER(1, 0));
+ if (ret)
+ goto err_out;
+
+ ret = _rpmi_clk_get_num_clocks(priv);
+ if (ret)
+ goto err_out;
+
+ priv->defs = calloc(priv->num_clocks, sizeof(struct rpmi_clk_def));
+ if (!priv->defs) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ for (i = 0; i < priv->num_clocks; i++) {
+ ret = rpmi_clk_get_attrs(priv, i, &priv->defs[i]);
+ if (ret)
+ goto err_out;
+ }
+
+ dev_dbg(dev, "num clock = %i\n", priv->num_clocks);
+
+ return 0;
+
+err_out:
+ free(priv->defs);
+ rpmi_close(chan);
+
+ return ret;
+}
+
+static int rpmi_clk_remove(struct udevice *dev)
+{
+ struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+ struct rpmi_chan *chan = &priv->chan;
+
+ free(priv->defs);
+ rpmi_close(chan);
+
+ return 0;
+}
+
+static int rpmi_clk_to_plat(struct udevice *dev)
+{
+ struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+
+ return rpmi_get_by_index(dev, 0, &priv->chan);
+}
+
+static struct clk_ops rpmi_clk_ops = {
+ .of_xlate = rpmi_clk_xlate,
+ .set_rate = rpmi_clk_set_rate,
+ .get_rate = rpmi_clk_get_rate,
+ .enable = rpmi_clk_enable,
+ .disable = rpmi_clk_disable,
+#if IS_ENABLED(CONFIG_CMD_CLK)
+ .dump = rpmi_clk_dump,
+#endif
+};
+
+static const struct udevice_id rpmi_clk_ids[] = {
+ { .compatible = "riscv,rpmi-clock" },
+ {}
+};
+
+U_BOOT_DRIVER(rpmi_clk) = {
+ .name = "rpmi-clk",
+ .id = UCLASS_CLK,
+ .of_match = rpmi_clk_ids,
+ .probe = rpmi_clk_probe,
+ .remove = rpmi_clk_remove,
+ .ops = &rpmi_clk_ops,
+ .of_to_plat = rpmi_clk_to_plat,
+ .plat_auto = sizeof(struct rpmi_clk_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/rpmi_proto.h b/include/rpmi_proto.h
index 4b557bfeedf5..dc0937ce7053 100644
--- a/include/rpmi_proto.h
+++ b/include/rpmi_proto.h
@@ -83,4 +83,43 @@ enum rpmi_message_type {
RPMI_MSG_NOTIFICATION = 0x3,
};
+/* RPMI clock service IDs */
+enum rpmi_clock_service_id {
+ RPMI_CLK_SRV_ENABLE_NOTIFICATION = 0x01,
+ RPMI_CLK_SRV_GET_NUM_CLOCKS = 0x02,
+ RPMI_CLK_SRV_GET_ATTRIBUTES = 0x03,
+ RPMI_CLK_SRV_GET_SUPPORTED_RATES = 0x04,
+ RPMI_CLK_SRV_SET_CONFIG = 0x05,
+ RPMI_CLK_SRV_GET_CONFIG = 0x06,
+ RPMI_CLK_SRV_SET_RATE = 0x07,
+ RPMI_CLK_SRV_GET_RATE = 0x08,
+ RPMI_CLK_SRV_ID_MAX_COUNT
+};
+
+/** Clock rate match mode */
+enum rpmi_clock_rate_match {
+ RPMI_CLK_RATE_MATCH_PLATFORM = 0,
+ RPMI_CLK_RATE_MATCH_ROUND_DOWN = 1,
+ RPMI_CLK_RATE_MATCH_ROUND_UP = 2,
+ RPMI_CLK_RATE_MATCH_MAX
+};
+
+/** Supported clock states */
+enum rpmi_clock_state {
+ RPMI_CLK_STATE_DISABLED = 0,
+ RPMI_CLK_STATE_ENABLED = 1,
+ RPMI_CLK_STATE_MAX
+};
+
+/** Clock type based on rate format */
+enum rpmi_clock_type {
+ RPMI_CLK_TYPE_DISCRETE = 0,
+ RPMI_CLK_TYPE_LINEAR = 1,
+ RPMI_CLK_TYPE_MAX
+};
+
+#define RPMI_CLK_NAME_LEN 16
+
+#define RPMI_CLOCK_RATE_INVALID (-1ULL)
+
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v2 5/5] firmware: rpmi: add a test and a sandbox driver
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
` (3 preceding siblings ...)
2026-07-08 21:51 ` [PATCH v2 4/5] drivers: clk: add support for RPMI clocks Charles Perry
@ 2026-07-08 21:51 ` Charles Perry
2026-07-09 13:02 ` [PATCH v2 0/5] Add support for RPMI to U-Boot Michal Simek
5 siblings, 0 replies; 14+ messages in thread
From: Charles Perry @ 2026-07-08 21:51 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini, Simon Glass,
Neil Armstrong, David Lechner, Julien Stephan, Peng Fan,
Kory Maincent, Kuan-Wei Chiu, Bastien Curutchet, Raymond Mao,
Marek Vasut, Mattijs Korpershoek, Heiko Schocher, Michal Simek,
Martin Schwan, Dinesh Maniyam, Lucien.Jheng, Pieter Van Trappen,
Rasmus Villemoes, Mateusz Furdyna, Svyatoslav Ryhel
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>
---
Changes in v2:
- Reorder headers
---
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 1e8f57c93a55..b494e0ad965d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1657,6 +1657,7 @@ S: Maintained
F: drivers/clk/clk_rpmi.c
F: drivers/firmware/rpmi/
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 074e5c06ec82..aff8bd178cb9 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 512dbd6a0c6c..72cead3c19d6 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -194,6 +194,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
@@ -210,6 +211,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..7cb4fd11a46d
--- /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 <clk.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <rpmi.h>
+#include <stdio.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
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-08 21:51 [PATCH v2 0/5] Add support for RPMI to U-Boot Charles Perry
` (4 preceding siblings ...)
2026-07-08 21:51 ` [PATCH v2 5/5] firmware: rpmi: add a test and a sandbox driver Charles Perry
@ 2026-07-09 13:02 ` Michal Simek
2026-07-09 14:53 ` Heinrich Schuchardt
2026-07-09 14:54 ` Charles Perry
5 siblings, 2 replies; 14+ messages in thread
From: Michal Simek @ 2026-07-09 13:02 UTC (permalink / raw)
To: Charles Perry, u-boot
Cc: Rahul Pathak, Anup Patel, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Romain Caritey, Mame Maria Mbaye,
Tom Rini, Heinrich Schuchardt, Rick Chen, Leo Yu-Chi Liang,
Souvik Chakravarty
On 7/8/26 23:51, Charles Perry wrote:
> Hello,
>
> This series adds support for RISC-V Platform Management Interface (RPMI) to
> U-Boot. RPMI is an OS-agnostic protocol for communication between an
> Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
> goals and purpose of RPMI are similar to ARM's SCMI.
From the first look it looks like SCMI. Why do you introducing something what
can be replaced by SCMI?
And SCMI has only specific ARM transport layer but the rest is arch independent.
In our case where we have Microblaze V in programmable logic I can't see any
reason to use RPMI for talking to the same server if I need to do it from ARM
side too via SCMI.
I pretty much think that there should be communication with ARM and instead of
creating another firmware interface talk to each other and have only one which
can be used across multiple architectures.
Thanks,
Michal
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 13:02 ` [PATCH v2 0/5] Add support for RPMI to U-Boot Michal Simek
@ 2026-07-09 14:53 ` Heinrich Schuchardt
2026-07-09 14:54 ` Charles Perry
1 sibling, 0 replies; 14+ messages in thread
From: Heinrich Schuchardt @ 2026-07-09 14:53 UTC (permalink / raw)
To: Michal Simek
Cc: Rahul Pathak, Anup Patel, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Romain Caritey, Mame Maria Mbaye,
Tom Rini, Rick Chen, Leo Yu-Chi Liang, Souvik Chakravarty, u-boot,
Charles Perry
On 7/9/26 15:02, Michal Simek wrote:
>
>
> On 7/8/26 23:51, Charles Perry wrote:
>> Hello,
>>
>> This series adds support for RISC-V Platform Management Interface
>> (RPMI) to
>> U-Boot. RPMI is an OS-agnostic protocol for communication between an
>> Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
>> goals and purpose of RPMI are similar to ARM's SCMI.
>
> From the first look it looks like SCMI. Why do you introducing
> something what can be replaced by SCMI?
> And SCMI has only specific ARM transport layer but the rest is arch
> independent.
>
> In our case where we have Microblaze V in programmable logic I can't see
> any reason to use RPMI for talking to the same server if I need to do it
> from ARM side too via SCMI.
>
> I pretty much think that there should be communication with ARM and
> instead of creating another firmware interface talk to each other and
> have only one which can be used across multiple architectures.
>
> Thanks,
> Michal
Hello Michal,
The specifications for RISC-V are produced by RISC-V International. AMD
is a strategic member. Please, join the relevant technical groups to
collaborate on specifications.
RISC-V has defined the Supervisor Binary Interface (SBI) specification
which is available at
https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v3.0/riscv-sbi.pdf.
The Message Proxy (MPXY) extension defines how communication channels
between different parts of software can be established.
RPMI is one of the defined messaging protocols. The ratified
specification is available at
https://github.com/riscv-non-isa/riscv-rpmi/releases/download/v1.0/riscv-rpmi.pdf.
Best regards
Heinrich
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 13:02 ` [PATCH v2 0/5] Add support for RPMI to U-Boot Michal Simek
2026-07-09 14:53 ` Heinrich Schuchardt
@ 2026-07-09 14:54 ` Charles Perry
2026-07-09 15:20 ` Michal Simek
1 sibling, 1 reply; 14+ messages in thread
From: Charles Perry @ 2026-07-09 14:54 UTC (permalink / raw)
To: Michal Simek
Cc: Charles Perry, u-boot, Rahul Pathak, Anup Patel, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang, Souvik Chakravarty
On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
>
>
> On 7/8/26 23:51, Charles Perry wrote:
> > Hello,
> >
> > This series adds support for RISC-V Platform Management Interface (RPMI) to
> > U-Boot. RPMI is an OS-agnostic protocol for communication between an
> > Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
> > goals and purpose of RPMI are similar to ARM's SCMI.
>
> From the first look it looks like SCMI. Why do you introducing something
> what can be replaced by SCMI?
> And SCMI has only specific ARM transport layer but the rest is arch independent.
Yes, there are alot of similarities between RPMI and SCMI. I found some
justification for this in some Linux Plumbers slide deck on RISC-V power
management by Paul Walmsley [1]:
* The hardware is more sophisticated
* The software is more sophisticated
* Some stakeholders haven’t had input in the past
* RISC-V “big tent” philosophy
I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
same arguments may as well apply to RPMI vs SCMI.
Some other arguments:
* The microcontroller side is made easier with RPMI because of librpmi [2].
SCMI has SCP-firmware [3] which is a quite complex project compared to
librpmi. Also SCP-firmware doesn't accept contribution anymore.
* RPMI is already in Linux.
For what I'm doing, RPMI is what gave me the first results (controlling
clocks) the quickest because all the pieces were present in Linux, OpenSBI
and librpmi. There are however lots of missing service drivers in Linux and
some other important OS like u-boot don't have support for RPMI at all. So
even though I gave you a bunch of reason for saying yes to RPMI, I do have
some doubt about how long it will take to bring RPMI on par with SCMI,
making the SCMI-for-RISCV transport that you suggest more appealing.
[1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
[2]: https://github.com/riscv-software-src/librpmi
[3]: https://gitlab.arm.com/firmware/SCP-firmware
>
> In our case where we have Microblaze V in programmable logic I can't see any
> reason to use RPMI for talking to the same server if I need to do it from
> ARM side too via SCMI.
>
> I pretty much think that there should be communication with ARM and instead
> of creating another firmware interface talk to each other and have only one
> which can be used across multiple architectures.
>
> Thanks,
> Michal
Thanks,
Charles
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 14:54 ` Charles Perry
@ 2026-07-09 15:20 ` Michal Simek
2026-07-09 16:57 ` Conor Dooley
2026-07-10 3:08 ` Anup Patel
0 siblings, 2 replies; 14+ messages in thread
From: Michal Simek @ 2026-07-09 15:20 UTC (permalink / raw)
To: Charles Perry
Cc: u-boot, Rahul Pathak, Anup Patel, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Romain Caritey, Mame Maria Mbaye,
Tom Rini, Heinrich Schuchardt, Rick Chen, Leo Yu-Chi Liang,
Souvik Chakravarty
On 7/9/26 16:54, Charles Perry wrote:
> On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
>>
>>
>> On 7/8/26 23:51, Charles Perry wrote:
>>> Hello,
>>>
>>> This series adds support for RISC-V Platform Management Interface (RPMI) to
>>> U-Boot. RPMI is an OS-agnostic protocol for communication between an
>>> Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
>>> goals and purpose of RPMI are similar to ARM's SCMI.
>>
>> From the first look it looks like SCMI. Why do you introducing something
>> what can be replaced by SCMI?
>> And SCMI has only specific ARM transport layer but the rest is arch independent.
>
> Yes, there are alot of similarities between RPMI and SCMI. I found some
> justification for this in some Linux Plumbers slide deck on RISC-V power
> management by Paul Walmsley [1]:
>
> * The hardware is more sophisticated
> * The software is more sophisticated
> * Some stakeholders haven’t had input in the past
> * RISC-V “big tent” philosophy
>
> I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
> same arguments may as well apply to RPMI vs SCMI.
>
> Some other arguments:
>
> * The microcontroller side is made easier with RPMI because of librpmi [2].
> SCMI has SCP-firmware [3] which is a quite complex project compared to
> librpmi. Also SCP-firmware doesn't accept contribution anymore.
> * RPMI is already in Linux.
>
> For what I'm doing, RPMI is what gave me the first results (controlling
> clocks) the quickest because all the pieces were present in Linux, OpenSBI
> and librpmi. There are however lots of missing service drivers in Linux and
> some other important OS like u-boot don't have support for RPMI at all. So
> even though I gave you a bunch of reason for saying yes to RPMI, I do have
> some doubt about how long it will take to bring RPMI on par with SCMI,
> making the SCMI-for-RISCV transport that you suggest more appealing.
>
> [1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
> [2]: https://github.com/riscv-software-src/librpmi
> [3]: https://gitlab.arm.com/firmware/SCP-firmware
>
>>
>> In our case where we have Microblaze V in programmable logic I can't see any
>> reason to use RPMI for talking to the same server if I need to do it from
>> ARM side too via SCMI.
>>
>> I pretty much think that there should be communication with ARM and instead
>> of creating another firmware interface talk to each other and have only one
>> which can be used across multiple architectures.
I have sent RFC patch to eliminate ARM from SCMI here
https://lore.kernel.org/all/d7f7e8c9589d937b60e43168845ab4fda15037a3.1783603600.git.michal.simek@amd.com/
and feedback is quite positive. I think it is more or less question to Microchip
if you want to take responsibility on another firmware interface (I understand
that it is approved, etc) or just use what it is around for longer time.
Thanks,
Michal
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 15:20 ` Michal Simek
@ 2026-07-09 16:57 ` Conor Dooley
2026-07-10 3:14 ` Anup Patel
2026-07-10 3:08 ` Anup Patel
1 sibling, 1 reply; 14+ messages in thread
From: Conor Dooley @ 2026-07-09 16:57 UTC (permalink / raw)
To: Michal Simek
Cc: Charles Perry, u-boot, Rahul Pathak, Anup Patel, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang, Souvik Chakravarty
[-- Attachment #1: Type: text/plain, Size: 3529 bytes --]
On Thu, Jul 09, 2026 at 05:20:31PM +0200, Michal Simek wrote:
>
>
> On 7/9/26 16:54, Charles Perry wrote:
> > On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
> > >
> > >
> > > On 7/8/26 23:51, Charles Perry wrote:
> > > > Hello,
> > > >
> > > > This series adds support for RISC-V Platform Management Interface (RPMI) to
> > > > U-Boot. RPMI is an OS-agnostic protocol for communication between an
> > > > Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
> > > > goals and purpose of RPMI are similar to ARM's SCMI.
> > >
> > > From the first look it looks like SCMI. Why do you introducing something
> > > what can be replaced by SCMI?
> > > And SCMI has only specific ARM transport layer but the rest is arch independent.
> >
> > Yes, there are alot of similarities between RPMI and SCMI. I found some
> > justification for this in some Linux Plumbers slide deck on RISC-V power
> > management by Paul Walmsley [1]:
> >
> > * The hardware is more sophisticated
> > * The software is more sophisticated
> > * Some stakeholders haven’t had input in the past
> > * RISC-V “big tent” philosophy
> >
> > I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
> > same arguments may as well apply to RPMI vs SCMI.
> >
> > Some other arguments:
> >
> > * The microcontroller side is made easier with RPMI because of librpmi [2].
> > SCMI has SCP-firmware [3] which is a quite complex project compared to
> > librpmi. Also SCP-firmware doesn't accept contribution anymore.
> > * RPMI is already in Linux.
> >
> > For what I'm doing, RPMI is what gave me the first results (controlling
> > clocks) the quickest because all the pieces were present in Linux, OpenSBI
> > and librpmi. There are however lots of missing service drivers in Linux and
> > some other important OS like u-boot don't have support for RPMI at all. So
> > even though I gave you a bunch of reason for saying yes to RPMI, I do have
> > some doubt about how long it will take to bring RPMI on par with SCMI,
> > making the SCMI-for-RISCV transport that you suggest more appealing.
> >
> > [1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
> > [2]: https://github.com/riscv-software-src/librpmi
> > [3]: https://gitlab.arm.com/firmware/SCP-firmware
> >
> > >
> > > In our case where we have Microblaze V in programmable logic I can't see any
> > > reason to use RPMI for talking to the same server if I need to do it from
> > > ARM side too via SCMI.
> > >
> > > I pretty much think that there should be communication with ARM and instead
> > > of creating another firmware interface talk to each other and have only one
> > > which can be used across multiple architectures.
>
> I have sent RFC patch to eliminate ARM from SCMI here
>
> https://lore.kernel.org/all/d7f7e8c9589d937b60e43168845ab4fda15037a3.1783603600.git.michal.simek@amd.com/
>
> and feedback is quite positive. I think it is more or less question to
> Microchip if you want to take responsibility on another firmware interface
> (I understand that it is approved, etc) or just use what it is around for
> longer time.
I don't think it is a question to just Microchip, it's a question to be asked
to all the various companies and projects using RISC-V. For example will
upstream OpenSBI accept support for SCMI, or will they say "we have RPMI,
use that"?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 16:57 ` Conor Dooley
@ 2026-07-10 3:14 ` Anup Patel
0 siblings, 0 replies; 14+ messages in thread
From: Anup Patel @ 2026-07-10 3:14 UTC (permalink / raw)
To: Conor Dooley
Cc: Michal Simek, Charles Perry, u-boot, Rahul Pathak, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang, Souvik Chakravarty
On Thu, Jul 9, 2026 at 10:27 PM Conor Dooley <conor@kernel.org> wrote:
>
> On Thu, Jul 09, 2026 at 05:20:31PM +0200, Michal Simek wrote:
> >
> >
> > On 7/9/26 16:54, Charles Perry wrote:
> > > On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
> > > >
> > > >
> > > > On 7/8/26 23:51, Charles Perry wrote:
> > > > > Hello,
> > > > >
> > > > > This series adds support for RISC-V Platform Management Interface (RPMI) to
> > > > > U-Boot. RPMI is an OS-agnostic protocol for communication between an
> > > > > Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
> > > > > goals and purpose of RPMI are similar to ARM's SCMI.
> > > >
> > > > From the first look it looks like SCMI. Why do you introducing something
> > > > what can be replaced by SCMI?
> > > > And SCMI has only specific ARM transport layer but the rest is arch independent.
> > >
> > > Yes, there are alot of similarities between RPMI and SCMI. I found some
> > > justification for this in some Linux Plumbers slide deck on RISC-V power
> > > management by Paul Walmsley [1]:
> > >
> > > * The hardware is more sophisticated
> > > * The software is more sophisticated
> > > * Some stakeholders haven’t had input in the past
> > > * RISC-V “big tent” philosophy
> > >
> > > I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
> > > same arguments may as well apply to RPMI vs SCMI.
> > >
> > > Some other arguments:
> > >
> > > * The microcontroller side is made easier with RPMI because of librpmi [2].
> > > SCMI has SCP-firmware [3] which is a quite complex project compared to
> > > librpmi. Also SCP-firmware doesn't accept contribution anymore.
> > > * RPMI is already in Linux.
> > >
> > > For what I'm doing, RPMI is what gave me the first results (controlling
> > > clocks) the quickest because all the pieces were present in Linux, OpenSBI
> > > and librpmi. There are however lots of missing service drivers in Linux and
> > > some other important OS like u-boot don't have support for RPMI at all. So
> > > even though I gave you a bunch of reason for saying yes to RPMI, I do have
> > > some doubt about how long it will take to bring RPMI on par with SCMI,
> > > making the SCMI-for-RISCV transport that you suggest more appealing.
> > >
> > > [1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
> > > [2]: https://github.com/riscv-software-src/librpmi
> > > [3]: https://gitlab.arm.com/firmware/SCP-firmware
> > >
> > > >
> > > > In our case where we have Microblaze V in programmable logic I can't see any
> > > > reason to use RPMI for talking to the same server if I need to do it from
> > > > ARM side too via SCMI.
> > > >
> > > > I pretty much think that there should be communication with ARM and instead
> > > > of creating another firmware interface talk to each other and have only one
> > > > which can be used across multiple architectures.
> >
> > I have sent RFC patch to eliminate ARM from SCMI here
> >
> > https://lore.kernel.org/all/d7f7e8c9589d937b60e43168845ab4fda15037a3.1783603600.git.michal.simek@amd.com/
> >
> > and feedback is quite positive. I think it is more or less question to
> > Microchip if you want to take responsibility on another firmware interface
> > (I understand that it is approved, etc) or just use what it is around for
> > longer time.
>
> I don't think it is a question to just Microchip, it's a question to be asked
> to all the various companies and projects using RISC-V. For example will
> upstream OpenSBI accept support for SCMI, or will they say "we have RPMI,
> use that"?
For upstream OpenSBI, the preference is RVI specifications and open
standards (such as UEFI, ACPI, Device Tree, etc).
Regards,
Anup
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-09 15:20 ` Michal Simek
2026-07-09 16:57 ` Conor Dooley
@ 2026-07-10 3:08 ` Anup Patel
2026-07-10 6:36 ` Michal Simek
1 sibling, 1 reply; 14+ messages in thread
From: Anup Patel @ 2026-07-10 3:08 UTC (permalink / raw)
To: Michal Simek
Cc: Charles Perry, u-boot, Rahul Pathak, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang, Souvik Chakravarty
On Thu, Jul 9, 2026 at 8:50 PM Michal Simek <michal.simek@amd.com> wrote:
>
>
>
> On 7/9/26 16:54, Charles Perry wrote:
> > On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
> >>
> >>
> >> On 7/8/26 23:51, Charles Perry wrote:
> >>> Hello,
> >>>
> >>> This series adds support for RISC-V Platform Management Interface (RPMI) to
> >>> U-Boot. RPMI is an OS-agnostic protocol for communication between an
> >>> Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
> >>> goals and purpose of RPMI are similar to ARM's SCMI.
> >>
> >> From the first look it looks like SCMI. Why do you introducing something
> >> what can be replaced by SCMI?
> >> And SCMI has only specific ARM transport layer but the rest is arch independent.
> >
> > Yes, there are alot of similarities between RPMI and SCMI. I found some
> > justification for this in some Linux Plumbers slide deck on RISC-V power
> > management by Paul Walmsley [1]:
> >
> > * The hardware is more sophisticated
> > * The software is more sophisticated
> > * Some stakeholders haven’t had input in the past
> > * RISC-V “big tent” philosophy
> >
> > I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
> > same arguments may as well apply to RPMI vs SCMI.
> >
> > Some other arguments:
> >
> > * The microcontroller side is made easier with RPMI because of librpmi [2].
> > SCMI has SCP-firmware [3] which is a quite complex project compared to
> > librpmi. Also SCP-firmware doesn't accept contribution anymore.
> > * RPMI is already in Linux.
> >
> > For what I'm doing, RPMI is what gave me the first results (controlling
> > clocks) the quickest because all the pieces were present in Linux, OpenSBI
> > and librpmi. There are however lots of missing service drivers in Linux and
> > some other important OS like u-boot don't have support for RPMI at all. So
> > even though I gave you a bunch of reason for saying yes to RPMI, I do have
> > some doubt about how long it will take to bring RPMI on par with SCMI,
> > making the SCMI-for-RISCV transport that you suggest more appealing.
> >
> > [1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
> > [2]: https://github.com/riscv-software-src/librpmi
> > [3]: https://gitlab.arm.com/firmware/SCP-firmware
> >
> >>
> >> In our case where we have Microblaze V in programmable logic I can't see any
> >> reason to use RPMI for talking to the same server if I need to do it from
> >> ARM side too via SCMI.
> >>
> >> I pretty much think that there should be communication with ARM and instead
> >> of creating another firmware interface talk to each other and have only one
> >> which can be used across multiple architectures.
>
> I have sent RFC patch to eliminate ARM from SCMI here
>
> https://lore.kernel.org/all/d7f7e8c9589d937b60e43168845ab4fda15037a3.1783603600.git.michal.simek@amd.com/
>
> and feedback is quite positive. I think it is more or less question to Microchip
> if you want to take responsibility on another firmware interface (I understand
> that it is approved, etc) or just use what it is around for longer time.
>
Simply renaming the Linux sources does not make the SCMI specification
to be open and community driven. The real problem is SCMI specification
development is not open and community driven with appropriate permissive
license.
Some of the note worthy highlights of RPMI ecosystem as compared to
SCMI are:
1) RPMI specification is an open an community driven specification licensed
under "Creative Commons Attribution 4.0".
2) The scope of RPMI specification is much bigger than SCMI specification where
RPMI is trying to consolidate all required messaging protocols under one RPMI
specification. RPMI defines some unique service groups in-addition to
the service
groups providing SCMI-style functionality such as:
2.1) Collaborative Processor Performance Control (CPPC)
Allows application processor to abstract out CPPC support to a
microcontroller. It also has fast-channels for hot-path CPPC registers
such as desired performance and current performance level along
with RISC-V specific fast-mechanism to access delivered and
reference counter.
2.2) System MSI
Allows system-level events to be sent to application processors as MSIs.
This is used for graceful shutdown/reboot request, hotplug events, etc
2.3) Request Forward
Enables one domain / world to receive messages forwarded from
another domain.
2.4) Managment Mode (MM)
Allows UFEI / EDK2 firmware running on application processors to talk
to managment mode (other domain / world / microcontroller) for secure
variable updates and other MM services
2.5) RAS Agent
Allows UEFI / EDK2 firmware to discover error sources handled by
firmware and generate RAS related ACPI tables at boot-time
2.6) Truested Execution Environment (TEE) (Work-in-progress)
Provides a rich and extensible TEE communication interface between
non-secure world (Linux) and trusted world (OP-TEE and others).
2.7) ... and more to come ...
3) The LibRPMI project which a open-source reference implementation of
RPMI is permissively licensed under BSD-2-clause which allows platform
vendors use any RTOS / baremetal-app as their microcontroller firmware.
This library is also shipped as a shared library allowing QEMU to use
LibRPMI for emulating RPMI services for TCG and KVM guests.
4) The following Linux RPMI drivers are already available in upstream:
MPXY mailbox controller, RPMI clock, and RPMI system MSI whereas
the CPPC, LPI and system suspend drivers already work as-is for RISC-V.
The upstreaming of RPMI performance driver is in-progress and should
be merged soon. The OpenSBI (M-mode firmware) already supports
almost all service groups defined by the RPMI v1.0 specification.
Based on the above facts, I would suggest AMD to consider using
RPMI over SCMI.
Regards,
Anup
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] Add support for RPMI to U-Boot
2026-07-10 3:08 ` Anup Patel
@ 2026-07-10 6:36 ` Michal Simek
0 siblings, 0 replies; 14+ messages in thread
From: Michal Simek @ 2026-07-10 6:36 UTC (permalink / raw)
To: Anup Patel
Cc: Charles Perry, u-boot, Rahul Pathak, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang, Souvik Chakravarty
On 7/10/26 05:08, Anup Patel wrote:
> On Thu, Jul 9, 2026 at 8:50 PM Michal Simek <michal.simek@amd.com> wrote:
>>
>>
>>
>> On 7/9/26 16:54, Charles Perry wrote:
>>> On Thu, Jul 09, 2026 at 03:02:11PM +0200, Michal Simek wrote:
>>>>
>>>>
>>>> On 7/8/26 23:51, Charles Perry wrote:
>>>>> Hello,
>>>>>
>>>>> This series adds support for RISC-V Platform Management Interface (RPMI) to
>>>>> U-Boot. RPMI is an OS-agnostic protocol for communication between an
>>>>> Application Processor (AP) and a Platform Microcontroller (PuC) [1]. The
>>>>> goals and purpose of RPMI are similar to ARM's SCMI.
>>>>
>>>> From the first look it looks like SCMI. Why do you introducing something
>>>> what can be replaced by SCMI?
>>>> And SCMI has only specific ARM transport layer but the rest is arch independent.
>>>
>>> Yes, there are alot of similarities between RPMI and SCMI. I found some
>>> justification for this in some Linux Plumbers slide deck on RISC-V power
>>> management by Paul Walmsley [1]:
>>>
>>> * The hardware is more sophisticated
>>> * The software is more sophisticated
>>> * Some stakeholders haven’t had input in the past
>>> * RISC-V “big tent” philosophy
>>>
>>> I think this is referring to the SBI spec of RISC-V vs arm's PSCI but the
>>> same arguments may as well apply to RPMI vs SCMI.
>>>
>>> Some other arguments:
>>>
>>> * The microcontroller side is made easier with RPMI because of librpmi [2].
>>> SCMI has SCP-firmware [3] which is a quite complex project compared to
>>> librpmi. Also SCP-firmware doesn't accept contribution anymore.
>>> * RPMI is already in Linux.
>>>
>>> For what I'm doing, RPMI is what gave me the first results (controlling
>>> clocks) the quickest because all the pieces were present in Linux, OpenSBI
>>> and librpmi. There are however lots of missing service drivers in Linux and
>>> some other important OS like u-boot don't have support for RPMI at all. So
>>> even though I gave you a bunch of reason for saying yes to RPMI, I do have
>>> some doubt about how long it will take to bring RPMI on par with SCMI,
>>> making the SCMI-for-RISCV transport that you suggest more appealing.
>>>
>>> [1]: https://lpc.events/event/2/contributions/197/attachments/133/165/RISC-V_Platform_Power_Management.pdf (slide 22)
>>> [2]: https://github.com/riscv-software-src/librpmi
>>> [3]: https://gitlab.arm.com/firmware/SCP-firmware
>>>
>>>>
>>>> In our case where we have Microblaze V in programmable logic I can't see any
>>>> reason to use RPMI for talking to the same server if I need to do it from
>>>> ARM side too via SCMI.
>>>>
>>>> I pretty much think that there should be communication with ARM and instead
>>>> of creating another firmware interface talk to each other and have only one
>>>> which can be used across multiple architectures.
>>
>> I have sent RFC patch to eliminate ARM from SCMI here
>>
>> https://lore.kernel.org/all/d7f7e8c9589d937b60e43168845ab4fda15037a3.1783603600.git.michal.simek@amd.com/
>>
>> and feedback is quite positive. I think it is more or less question to Microchip
>> if you want to take responsibility on another firmware interface (I understand
>> that it is approved, etc) or just use what it is around for longer time.
>>
>
> Simply renaming the Linux sources does not make the SCMI specification
> to be open and community driven. The real problem is SCMI specification
> development is not open and community driven with appropriate permissive
> license.
>
> Some of the note worthy highlights of RPMI ecosystem as compared to
> SCMI are:
>
> 1) RPMI specification is an open an community driven specification licensed
> under "Creative Commons Attribution 4.0".
>
> 2) The scope of RPMI specification is much bigger than SCMI specification where
> RPMI is trying to consolidate all required messaging protocols under one RPMI
> specification. RPMI defines some unique service groups in-addition to
> the service
> groups providing SCMI-style functionality such as:
> 2.1) Collaborative Processor Performance Control (CPPC)
> Allows application processor to abstract out CPPC support to a
> microcontroller. It also has fast-channels for hot-path CPPC registers
> such as desired performance and current performance level along
> with RISC-V specific fast-mechanism to access delivered and
> reference counter.
> 2.2) System MSI
> Allows system-level events to be sent to application processors as MSIs.
> This is used for graceful shutdown/reboot request, hotplug events, etc
> 2.3) Request Forward
> Enables one domain / world to receive messages forwarded from
> another domain.
> 2.4) Managment Mode (MM)
> Allows UFEI / EDK2 firmware running on application processors to talk
> to managment mode (other domain / world / microcontroller) for secure
> variable updates and other MM services
> 2.5) RAS Agent
> Allows UEFI / EDK2 firmware to discover error sources handled by
> firmware and generate RAS related ACPI tables at boot-time
> 2.6) Truested Execution Environment (TEE) (Work-in-progress)
> Provides a rich and extensible TEE communication interface between
> non-secure world (Linux) and trusted world (OP-TEE and others).
> 2.7) ... and more to come ...
>
> 3) The LibRPMI project which a open-source reference implementation of
> RPMI is permissively licensed under BSD-2-clause which allows platform
> vendors use any RTOS / baremetal-app as their microcontroller firmware.
> This library is also shipped as a shared library allowing QEMU to use
> LibRPMI for emulating RPMI services for TCG and KVM guests.
>
> 4) The following Linux RPMI drivers are already available in upstream:
> MPXY mailbox controller, RPMI clock, and RPMI system MSI whereas
> the CPPC, LPI and system suspend drivers already work as-is for RISC-V.
> The upstreaming of RPMI performance driver is in-progress and should
> be merged soon. The OpenSBI (M-mode firmware) already supports
> almost all service groups defined by the RPMI v1.0 specification.
>
> Based on the above facts, I would suggest AMD to consider using
> RPMI over SCMI.
In the Linux kernel when I do grep RPMI I see that it is just tighten to RISCV.
If this is community driven effort and based on what you are describing above
(based on you better then SCMI) why is this connected only to RISCV. I would be
very surprised if this was labeled like this that none raised this concern.
If you look at FirmwareHandoff as another example of community driver
specification which is architecture neutral or trying to be.
https://github.com/FirmwareHandoff/
I would rather see one specification which is trying to solve the same issue
where vendors are talking to each other to achieve the same goal. Instead of
another firmware specification which should be adopted, reviewed, tested and
used to serve the same purpose. And also not solving problems on heterogeneous
systems with mix of architectures.
Anyway if this is real community driven spec I am happy to join that discussion
and ask above question. If this is arch neutral I can't see any reason why there
should be riscv in compatible string and Kconfigs setup for RISCV only.
Thanks,
Michal
^ permalink raw reply [flat|nested] 14+ messages in thread