* [PATCH 0/7] Add support for RPMI to U-Boot
@ 2026-06-26 20:15 Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Romain Caritey,
Mame Maria Mbaye, Tom Rini, Heinrich Schuchardt, Rick Chen,
Leo Yu-Chi Liang
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.
This patchset first adds an abstraction layer for the transport
(UCLASS_RPMI), then adds two transport layer (SBI MPXY and shared memory)
and finally adds two client, or service group in RPMI jargon: Clock and
device power service groups.
This series also include a sandbox mode for which the RPMI transport
doesn't attach to a firmware but contains a simulation of a firmware. This
reuses code from librpmi [2] to create the message responses.
Apart from the spec [1], this series reuses code from the Linux SBI MPXY
driver [3], OpenSBI shared memory transport [4] and Linux RPMI clock driver
[5].
This was tested on a PIC64-HPSC SoC which features an application cluster
and a system controller with isolated address spaces, hence the need for
RPMI. I've tested the interoperability between RPMI clients and transports
with an S-Mode U-boot (using the MPXY transport) and M-Mode U-Boot (using
shared memory transport) and the following device tree fragment:
```
firmware {
rpmi: mailbox@7000000000 {
compatible = "riscv,rpmi-shmem-mbox";
reg = <0x70 0x00000000 0x0 0x3000>,
<0x70 0x00003000 0x0 0x3000>,
<0x70 0x00006000 0x0 0x1000>,
<0x70 0x00007000 0x0 0x1000>;
reg-names = "a2p-req", "p2a-ack", "p2a-req", "a2p-ack";
riscv,slot-size = <64>;
#mbox-cells = <1>;
};
ifndef CONFIG_RISCV_MMODE
clock-service {
compatible = "riscv,rpmi-mpxy-clock";
mboxes = <&rpmi 0x8>;
riscv,sbi-mpxy-channel-id = <0x1000>;
};
device-power-service {
compatible = "riscv,rpmi-mpxy-device-power";
mboxes = <&rpmi 0x9>;
riscv,sbi-mpxy-channel-id = <0x1001>;
};
mpxy_mbox: mailbox {
compatible = "riscv,sbi-mpxy-mbox";
#mbox-cells = <2>;
};
endif
sysc_clk: clock-controller {
compatible = "riscv,rpmi-clock";
ifdef CONFIG_RISCV_MMODE
mboxes = <&rpmi 0x8>;
else
mboxes = <&mpxy_mbox 0x1000 0x0>;
endif
#clock-cells = <1>;
};
sysc_dpwr: power-domain-controller {
compatible = "riscv,rpmi-device-power";
ifdef CONFIG_RISCV_MMODE
mboxes = <&rpmi 0x9>;
else
mboxes = <&mpxy_mbox 0x1001 0x0>;
endif
#power-domain-cells = <1>;
};
};
```
[1]: https://github.com/riscv-non-isa/riscv-rpmi
[2]: https://github.com/riscv-software-src/librpmi
[3]: https://elixir.bootlin.com/linux/v7.0.10/source/drivers/mailbox/riscv-sbi-mpxy-mbox.c
[4]: https://elixir.bootlin.com/opensbi/v1.8.1/source/lib/utils/mailbox/fdt_mailbox_rpmi_shmem.c
[5]: https://elixir.bootlin.com/linux/v7.0.10/source/drivers/clk/clk-rpmi.c
Cc: Rahul Pathak <rahul@summations.net>
Cc: Anup Patel <anup@brainfault.org>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Romain Caritey <romain.caritey@microchip.com>
Cc: Mame Maria Mbaye <MameMaria.Mbaye@microchip.com>
Cc: Tom Rini <trini@konsulko.com>
Cc: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Cc: Rick Chen <rick@andestech.com>
Cc: Leo Yu-Chi Liang <ycliang@andestech.com>
Charles Perry (7):
firmware: add support for RPMI
firmware: rpmi: add support for the SBI MPXY transport
firmware: rpmi: add support for shared memory transport
drivers: clk: add support for RPMI clocks
drivers: power: add support for RPMI power domains
firmware: rpmi: add a test and a sandbox driver
firmware: rpmi: add a test and sandbox for device power
MAINTAINERS | 9 +
arch/riscv/include/asm/sbi.h | 57 +++
arch/sandbox/dts/test.dts | 23 +
configs/sandbox_defconfig | 3 +
drivers/clk/Kconfig | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk_rpmi.c | 346 +++++++++++++++
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/rpmi/Kconfig | 27 ++
drivers/firmware/rpmi/Makefile | 4 +
drivers/firmware/rpmi/rpmi-sandbox-clock.c | 446 +++++++++++++++++++
drivers/firmware/rpmi/rpmi-sandbox-power.c | 174 ++++++++
drivers/firmware/rpmi/rpmi-sandbox.c | 158 +++++++
drivers/firmware/rpmi/rpmi-sandbox.h | 48 +++
drivers/firmware/rpmi/rpmi-sbi-mpxy.c | 449 +++++++++++++++++++
drivers/firmware/rpmi/rpmi-shmem.c | 474 +++++++++++++++++++++
drivers/firmware/rpmi/rpmi-uclass.c | 138 ++++++
drivers/power/domain/Kconfig | 7 +
drivers/power/domain/Makefile | 1 +
drivers/power/domain/rpmi-power-domain.c | 229 ++++++++++
include/dm/uclass-id.h | 1 +
include/rpmi-uclass.h | 79 ++++
include/rpmi.h | 159 +++++++
include/rpmi_proto.h | 143 +++++++
test/dm/Makefile | 1 +
test/dm/rpmi.c | 69 +++
27 files changed, 3055 insertions(+)
create mode 100644 drivers/clk/clk_rpmi.c
create mode 100644 drivers/firmware/rpmi/Kconfig
create mode 100644 drivers/firmware/rpmi/Makefile
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-clock.c
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-power.c
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.c
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.h
create mode 100644 drivers/firmware/rpmi/rpmi-sbi-mpxy.c
create mode 100644 drivers/firmware/rpmi/rpmi-shmem.c
create mode 100644 drivers/firmware/rpmi/rpmi-uclass.c
create mode 100644 drivers/power/domain/rpmi-power-domain.c
create mode 100644 include/rpmi-uclass.h
create mode 100644 include/rpmi.h
create mode 100644 include/rpmi_proto.h
create mode 100644 test/dm/rpmi.c
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/7] firmware: add support for RPMI
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-27 1:09 ` Yao Zi
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini, Simon Glass,
Neil Armstrong, Kory Maincent, Peng Fan, Kuan-Wei Chiu,
Raymond Mao, Quentin Schulz, Stefan Roese, Jerome Forissier,
Philip Molloy, Michael Trimarchi, Michal Simek, Svyatoslav Ryhel,
Ion Agorria, Dinesh Maniyam
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>
---
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 | 138 ++++++++++++++++++++++++
include/dm/uclass-id.h | 1 +
include/rpmi-uclass.h | 79 ++++++++++++++
include/rpmi.h | 159 ++++++++++++++++++++++++++++
include/rpmi_proto.h | 63 +++++++++++
10 files changed, 458 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 571af1964654..8b8ae8e6cc9f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1689,6 +1689,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..b725f9e59441
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-uclass.c
@@ -0,0 +1,138 @@
+// 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>
+
+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 mismatch\n");
+ 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\n");
+ 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 mismatch\n");
+ 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] 11+ messages in thread
* [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-27 1:30 ` Yao Zi
2026-06-26 20:15 ` [PATCH 3/7] firmware: rpmi: add support for shared memory transport Charles Perry
` (4 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 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>
---
arch/riscv/include/asm/sbi.h | 57 ++++
drivers/firmware/rpmi/Kconfig | 9 +
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-sbi-mpxy.c | 449 ++++++++++++++++++++++++++
4 files changed, 516 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..84af4787187a
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
@@ -0,0 +1,449 @@
+// 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 <malloc.h>
+#include <asm/io.h>
+#include <asm/sbi.h>
+#include <asm/types.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/kernel.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 standard channel attributes.
+ */
+struct sbi_mpxy_channel_attrs {
+ /* Message protocol ID */
+ u32 msg_proto_id;
+ /* Message protocol version */
+ u32 msg_proto_version;
+ /* Message protocol maximum message length */
+ u32 msg_max_len;
+ /* Message protocol message send timeout in microseconds */
+ u32 msg_send_timeout;
+ /* Message protocol message completion timeout in microseconds */
+ u32 msg_completion_timeout;
+ /* Bit array for channel capabilities */
+#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)
+ u32 capability;
+};
+
+/* RPMI message protocol specific MPXY attributes */
+enum sbi_mpxy_rpmi_attribute_id {
+ SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID = SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
+ SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION,
+ SBI_MPXY_RPMI_ATTR_IMPL_ID,
+ SBI_MPXY_RPMI_ATTR_IMPL_VERSION,
+ SBI_MPXY_RPMI_ATTR_MAX_ID
+};
+
+/*
+ * RPMI specific SBI MPXY channel attributes.
+ */
+struct sbi_mpxy_rpmi_channel_attrs {
+ /* RPMI service group ID */
+ u32 servicegroup_id;
+ /* RPMI service group version */
+ u32 servicegroup_version;
+ /* RPMI implementation ID */
+ u32 impl_id;
+ /* RPMI implementation version */
+ u32 impl_version;
+};
+
+/* 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;
+ struct sbi_mpxy_channel_attrs attrs;
+ struct sbi_mpxy_rpmi_channel_attrs rpmi_attrs;
+};
+
+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.msg_proto_version;
+ break;
+ case RPMI_ATTR_MAX_MSG_DATA_SIZE:
+ *data = desc->max_xfer_len;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_ID:
+ *data = desc->rpmi_attrs.servicegroup_id;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_VERSION:
+ *data = desc->rpmi_attrs.servicegroup_version;
+ break;
+ case RPMI_ATTR_IMPL_ID:
+ *data = desc->rpmi_attrs.impl_id;
+ break;
+ case RPMI_ATTR_IMPL_VERSION:
+ *data = desc->rpmi_attrs.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 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 (!(desc->attrs.capability &
+ SBI_MPXY_CHAN_CAP_SEND_WITH_RESP))
+ return -EIO;
+
+ mpxy_fid = SBI_EXT_MPXY_SEND_MSG_WITH_RESP;
+ } else {
+ if (!(desc->attrs.capability &
+ 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.msg_proto_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;
+ struct sbi_mpxy_rpmi_channel_attrs *rpmi_attrs;
+ struct sbi_mpxy_channel_attrs *attrs;
+ 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;
+ attrs = &mpxy->channel_descs[i].attrs;
+
+ ret = rpmi_sbi_mpxy_read_attrs(
+ mpxy, channel_id, SBI_MPXY_ATTR_MSG_PROT_ID,
+ sizeof(struct sbi_mpxy_channel_attrs) / sizeof(u32),
+ (u32 *)attrs);
+ if (ret)
+ return ret;
+
+ if (attrs->msg_proto_id == SBI_MPXY_MSGPROTO_RPMI_ID) {
+ rpmi_attrs = &mpxy->channel_descs[i].rpmi_attrs;
+ ret = rpmi_sbi_mpxy_read_attrs(
+ mpxy, channel_id,
+ SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
+ sizeof(struct sbi_mpxy_rpmi_channel_attrs) /
+ sizeof(u32),
+ (u32 *)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.msg_max_len);
+ }
+
+ return 0;
+
+err:
+ if (mpxy->channel_descs)
+ free(mpxy->channel_descs);
+
+ if (mpxy->shmem)
+ free(mpxy->shmem);
+
+ return ret;
+}
+
+static int rpmi_sbi_mpxy_remove(struct udevice *dev)
+{
+ struct rpmi_sbi_mpxy *mpxy = dev_get_priv(dev);
+
+ if (mpxy->channel_descs)
+ free(mpxy->channel_descs);
+
+ if (mpxy->shmem)
+ 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] 11+ messages in thread
* [PATCH 3/7] firmware: rpmi: add support for shared memory transport
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-26 20:15 ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Charles Perry
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 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>
---
drivers/firmware/rpmi/Kconfig | 8 +
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-shmem.c | 474 +++++++++++++++++++++++++++++
include/rpmi_proto.h | 23 ++
4 files changed, 506 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..a3e529d4c685
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-shmem.c
@@ -0,0 +1,474 @@
+// 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 <malloc.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <asm/types.h>
+#include <asm/barrier.h>
+#include <linux/delay.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)
+{
+ if (chan->con_priv)
+ 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] 11+ messages in thread
* [PATCH 4/7] drivers: clk: add support for RPMI clocks
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
` (2 preceding siblings ...)
2026-06-26 20:15 ` [PATCH 3/7] firmware: rpmi: add support for shared memory transport Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-27 1:47 ` Yao Zi
2026-06-26 20:15 ` [PATCH 5/7] drivers: power: add support for RPMI power domains Charles Perry
` (2 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini,
Lukasz Majewski, Neil Armstrong, Simon Glass, Kory Maincent,
Peng Fan, Kuan-Wei Chiu, Raymond Mao, Quentin Schulz,
Stefan Roese, Philip Molloy, Jerome Forissier, Michal Simek,
Michael Trimarchi, Peter Korsgaard
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>
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk_rpmi.c | 346 +++++++++++++++++++++++++++++++++++++++++
include/rpmi_proto.h | 39 +++++
5 files changed, 394 insertions(+)
create mode 100644 drivers/clk/clk_rpmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8b8ae8e6cc9f..0bf6582ef493 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1692,6 +1692,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 c2da7b3938b6..c8cb4a4a71cd 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 5f0c0d8a5c28..b0d9ca3cf9bb 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..d2efb057600b
--- /dev/null
+++ b/drivers/clk/clk_rpmi.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_CLK
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <asm/types.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;
+ u32 transition_latency;
+ 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);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ return 0;
+}
+
+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);
+ if (status)
+ return rpmi_to_linux_error(status);
+
+ return _rpmi_clk_get_rate(priv, clkid);
+}
+
+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)
+{
+ return _rpmi_clk_set_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK,
+ rate);
+}
+
+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);
+ def->transition_latency = le32_to_cpu(rx.transition_latency);
+ 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:
+ if (priv->defs)
+ 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;
+
+ if (priv->defs)
+ 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] 11+ messages in thread
* [PATCH 5/7] drivers: power: add support for RPMI power domains
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
` (3 preceding siblings ...)
2026-06-26 20:15 ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-26 20:15 ` [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver Charles Perry
2026-06-26 20:15 ` [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power Charles Perry
6 siblings, 0 replies; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini, Jaehoon Chung,
Peng Fan, Neil Armstrong, Simon Glass, Kory Maincent,
Kuan-Wei Chiu, Raymond Mao, Quentin Schulz, Stefan Roese,
Jerome Forissier, Philip Molloy, Tien Fong Chee, Casey Connolly,
Balaji Selvanathan, Alif Zakuan Yuslaimi, Sumit Garg,
Markus Schneider-Pargmann (TI.com), Marek Vasut, Aswin Murugan
The RISC-V Platform Management Interface (RPMI) defines the device power
service group [1] that is used to control power domains. This is used to
implement a UCLASS_POWER_DOMAIN driver.
[1]: https://github.com/riscv-non-isa/riscv-rpmi (chapter 4.9)
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
MAINTAINERS | 1 +
drivers/power/domain/Kconfig | 7 +
drivers/power/domain/Makefile | 1 +
drivers/power/domain/rpmi-power-domain.c | 229 +++++++++++++++++++++++
include/rpmi_proto.h | 18 ++
5 files changed, 256 insertions(+)
create mode 100644 drivers/power/domain/rpmi-power-domain.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0bf6582ef493..d2b18d9b13db 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1694,6 +1694,7 @@ M: Charles Perry <charles.perry@microchip.com>
S: Maintained
F: drivers/clk/clk_rpmi.c
F: drivers/firmware/rpmi/
+F: drivers/power/domain/rpmi-power-domain.c
F: include/rpmi*
SANDBOX
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 4112b777371d..e8484b193cad 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -106,6 +106,13 @@ config RENESAS_R8A78000_POWER_DOMAIN
and reset driver. The MDLC is responsible for managing both
power domains and resets on R-Car R8A78000 X5H SoC.
+config RPMI_POWER_DOMAIN
+ bool "Enable the RPMI Power domain driver"
+ depends on POWER_DOMAIN && RPMI_FIRMWARE
+ help
+ Enable this option if you want to support power domains managed by
+ the RISC-V Platform Management Interface (RPMI) protocol.
+
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index 110153d5cf81..9c42160e18be 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
obj-$(CONFIG_MESON_SECURE_POWER_DOMAIN) += meson-secure-pwrc.o
obj-$(CONFIG_RENESAS_R8A78000_POWER_DOMAIN) += renesas-r8a78000-power-domain.o
+obj-$(CONFIG_RPMI_POWER_DOMAIN) += rpmi-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_SCMI_POWER_DOMAIN) += scmi-power-domain.o
diff --git a/drivers/power/domain/rpmi-power-domain.c b/drivers/power/domain/rpmi-power-domain.c
new file mode 100644
index 000000000000..f00ebab86686
--- /dev/null
+++ b/drivers/power/domain/rpmi-power-domain.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <power-domain.h>
+#include <power-domain-uclass.h>
+#include <rpmi.h>
+#include <dm/device_compat.h>
+
+struct rpmi_get_num_pd_rx {
+ __le32 status;
+ __le32 num_pd;
+};
+
+struct rpmi_get_attrs_tx {
+ __le32 pd_id;
+};
+
+struct rpmi_get_attrs_rx {
+ __le32 status;
+ __le32 flags;
+ __le32 trans_latency;
+ char name[RPMI_DPWR_NAME_LEN];
+};
+
+struct rpmi_set_state_tx {
+ __le32 pd_id;
+ __le32 state;
+};
+
+struct rpmi_set_state_rx {
+ __le32 status;
+};
+
+struct rpmi_pd_def {
+ u32 transition_latency;
+ char name[RPMI_DPWR_NAME_LEN + 1];
+};
+
+struct rpmi_pd_priv {
+ struct rpmi_chan chan;
+ struct udevice *dev;
+ u32 num_pd;
+ struct rpmi_pd_def *defs;
+};
+
+static int rpmi_pd_get_attrs(struct rpmi_pd_priv *priv, u32 pd_id,
+ struct rpmi_pd_def *def)
+{
+ struct rpmi_get_attrs_tx tx = {
+ .pd_id = cpu_to_le32(pd_id),
+ };
+ struct rpmi_get_attrs_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_DPWR_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);
+
+ def->transition_latency = le32_to_cpu(rx.trans_latency);
+ memcpy(def->name, rx.name, RPMI_DPWR_NAME_LEN);
+ def->name[RPMI_DPWR_NAME_LEN] = '\0';
+
+ return 0;
+}
+
+static int rpmi_pd_set_state(struct rpmi_pd_priv *priv, u32 pd_id, u32 state)
+{
+ struct rpmi_set_state_tx tx = {
+ .pd_id = cpu_to_le32(pd_id),
+ .state = cpu_to_le32(state),
+ };
+ struct rpmi_set_state_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_DPWR_SRV_SET_DPWR_STATE,
+ &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 0;
+}
+
+static int rpmi_power_domain_on(struct power_domain *power_domain)
+{
+ return rpmi_pd_set_state(dev_get_plat(power_domain->dev),
+ power_domain->id, RPMI_DPWR_STATE_ON);
+}
+
+static int rpmi_power_domain_off(struct power_domain *power_domain)
+{
+ return rpmi_pd_set_state(dev_get_plat(power_domain->dev),
+ power_domain->id, RPMI_DPWR_STATE_OFF);
+}
+
+static int rpmi_power_domain_xlate(struct power_domain *power_domain,
+ struct ofnode_phandle_args *args)
+{
+ struct rpmi_pd_priv *priv = dev_get_plat(power_domain->dev);
+
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ if (args->args[0] >= priv->num_pd)
+ return -EINVAL;
+
+ power_domain->id = args->args[0];
+
+ return 0;
+}
+
+static int rpmi_pd_get_num_domains(struct rpmi_pd_priv *priv)
+{
+ struct rpmi_get_num_pd_rx rx;
+ int ret, status;
+
+ ret = rpmi_send_with_resp(&priv->chan, RPMI_DPWR_SRV_GET_NUM_DOMAINS,
+ 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_pd = le32_to_cpu(rx.num_pd);
+
+ return 0;
+}
+
+static int rpmi_pd_probe(struct udevice *dev)
+{
+ struct rpmi_pd_priv *priv = (struct rpmi_pd_priv *)dev_get_plat(dev);
+ struct rpmi_chan *chan = &priv->chan;
+ int ret, i;
+
+ priv->dev = dev;
+
+ ret = rpmi_open(chan);
+ if (ret)
+ return ret;
+
+ ret = rpmi_check_versions(chan, RPMI_MKVER(1, 0),
+ RPMI_SRVGRP_DEVICE_POWER, RPMI_MKVER(1, 0));
+ if (ret)
+ goto err_out;
+
+ ret = rpmi_pd_get_num_domains(priv);
+ if (ret)
+ goto err_out;
+
+ priv->defs = calloc(priv->num_pd, sizeof(struct rpmi_pd_def));
+ if (!priv->defs) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ for (i = 0; i < priv->num_pd; i++) {
+ ret = rpmi_pd_get_attrs(priv, i, &priv->defs[i]);
+ if (ret)
+ goto err_out;
+ }
+
+ dev_dbg(dev, "num pd = %i\n", priv->num_pd);
+
+ return 0;
+
+err_out:
+ if (priv->defs)
+ free(priv->defs);
+ rpmi_close(chan);
+
+ return ret;
+}
+
+static int rpmi_pd_remove(struct udevice *dev)
+{
+ struct rpmi_pd_priv *priv = (struct rpmi_pd_priv *)dev_get_plat(dev);
+ struct rpmi_chan *chan = &priv->chan;
+
+ if (priv->defs)
+ free(priv->defs);
+ rpmi_close(chan);
+
+ return 0;
+}
+
+static int rpmi_pd_to_plat(struct udevice *dev)
+{
+ struct rpmi_pd_priv *priv = (struct rpmi_pd_priv *)dev_get_plat(dev);
+
+ return rpmi_get_by_index(dev, 0, &priv->chan);
+}
+
+static const struct power_domain_ops rpmi_pd_ops = {
+ .of_xlate = rpmi_power_domain_xlate,
+ .on = rpmi_power_domain_on,
+ .off = rpmi_power_domain_off,
+};
+
+static const struct udevice_id rpmi_pd_ids[] = {
+ { .compatible = "riscv,rpmi-device-power" },
+ {}
+};
+
+U_BOOT_DRIVER(rpmi_pd) = {
+ .name = "rpmi-device-power",
+ .id = UCLASS_POWER_DOMAIN,
+ .of_match = rpmi_pd_ids,
+ .probe = rpmi_pd_probe,
+ .remove = rpmi_pd_remove,
+ .ops = &rpmi_pd_ops,
+ .of_to_plat = rpmi_pd_to_plat,
+ .plat_auto = sizeof(struct rpmi_pd_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/rpmi_proto.h b/include/rpmi_proto.h
index dc0937ce7053..d2512617f75c 100644
--- a/include/rpmi_proto.h
+++ b/include/rpmi_proto.h
@@ -122,4 +122,22 @@ enum rpmi_clock_type {
#define RPMI_CLOCK_RATE_INVALID (-1ULL)
+/* RPMI Device_Power (DPWR) service IDs */
+enum rpmi_dpwr_service_id {
+ RPMI_DPWR_SRV_ENABLE_NOTIFICATION = 0x01,
+ RPMI_DPWR_SRV_GET_NUM_DOMAINS = 0x02,
+ RPMI_DPWR_SRV_GET_ATTRIBUTES = 0x03,
+ RPMI_DPWR_SRV_SET_DPWR_STATE = 0x04,
+ RPMI_DPWR_SRV_GET_DPWR_STATE = 0x05,
+ RPMI_DPWR_SRV_ID_MAX,
+};
+
+enum rpmi_dpwr_state {
+ RPMI_DPWR_STATE_INVALID = -1,
+ RPMI_DPWR_STATE_ON = 0,
+ RPMI_DPWR_STATE_OFF = 3,
+};
+
+#define RPMI_DPWR_NAME_LEN 16
+
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
` (4 preceding siblings ...)
2026-06-26 20:15 ` [PATCH 5/7] drivers: power: add support for RPMI power domains Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
2026-06-26 20:15 ` [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power Charles Perry
6 siblings, 0 replies; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Tom Rini, Simon Glass,
Neil Armstrong, Peng Fan, Kory Maincent, Yao Zi, Kuan-Wei Chiu,
Raymond Mao, Quentin Schulz, Stefan Roese, Jerome Forissier,
Philip Molloy, Mattijs Korpershoek, Marek Vasut, Heiko Schocher,
Rasmus Villemoes, Martin Schwan, Lucien.Jheng, Michal Simek,
Pieter Van Trappen, Dinesh Maniyam, 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>
---
MAINTAINERS | 1 +
arch/sandbox/dts/test.dts | 12 +
configs/sandbox_defconfig | 2 +
drivers/firmware/rpmi/Kconfig | 3 +-
drivers/firmware/rpmi/Makefile | 1 +
drivers/firmware/rpmi/rpmi-sandbox-clock.c | 446 +++++++++++++++++++++
drivers/firmware/rpmi/rpmi-sandbox.c | 156 +++++++
drivers/firmware/rpmi/rpmi-sandbox.h | 47 +++
test/dm/Makefile | 1 +
test/dm/rpmi.c | 48 +++
10 files changed, 716 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-clock.c
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.c
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox.h
create mode 100644 test/dm/rpmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d2b18d9b13db..96fee47ade6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1696,6 +1696,7 @@ F: drivers/clk/clk_rpmi.c
F: drivers/firmware/rpmi/
F: drivers/power/domain/rpmi-power-domain.c
F: include/rpmi*
+F: test/dm/rpmi.c
SANDBOX
M: Simon Glass <sjg@chromium.org>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 0887de4333b0..e5d6f398fb35 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -874,6 +874,18 @@
sm: secure-monitor {
compatible = "sandbox,sm";
};
+
+ rpmi: rpmi {
+ compatible = "sandbox,rpmi-transport";
+ #mbox-cells = <1>;
+ };
+
+ rpmi_clock: rpmi-clock {
+ compatible = "riscv,rpmi-clock";
+ mboxes = <&rpmi 0x8>;
+ #clock-cells = <1>;
+ clocks = <&rpmi_clock 0>, <&rpmi_clock 1>, <&rpmi_clock 2>, <&rpmi_clock 3>;
+ };
};
fpga {
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ba800f7d19da..f1637e1ff09c 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -191,6 +191,7 @@ CONFIG_BUTTON_GPIO=y
CONFIG_CLK=y
CONFIG_CLK_K210=y
CONFIG_CLK_K210_SET_RATE=y
+CONFIG_CLK_RPMI=y
CONFIG_SANDBOX_CLK_CCF=y
CONFIG_CLK_SCMI=y
CONFIG_CPU=y
@@ -207,6 +208,7 @@ CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_ARM_FFA_TRANSPORT=y
CONFIG_SCMI_FIRMWARE=y
+CONFIG_RPMI_FIRMWARE=y
CONFIG_FPGA_ALTERA=y
CONFIG_FPGA_STRATIX_II=y
CONFIG_FPGA_STRATIX_V=y
diff --git a/drivers/firmware/rpmi/Kconfig b/drivers/firmware/rpmi/Kconfig
index 421a1fa9deba..b462d3693987 100644
--- a/drivers/firmware/rpmi/Kconfig
+++ b/drivers/firmware/rpmi/Kconfig
@@ -1,6 +1,7 @@
config RPMI_FIRMWARE
bool "Enable RPMI support"
- depends on RISCV && DM && OF_CONTROL
+ depends on RISCV || SANDBOX
+ depends on DM && OF_CONTROL
select FIRMWARE
help
RISC-V Platform Management Interface (RPMI) defines an OS-agnostic
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index 4f4bcb929db4..150e98a72df9 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1,3 +1,4 @@
obj-y += rpmi-uclass.o
obj-$(CONFIG_RPMI_SBI_MPXY) += rpmi-sbi-mpxy.o
obj-$(CONFIG_RPMI_SHMEM) += rpmi-shmem.o
+obj-$(CONFIG_SANDBOX) += rpmi-sandbox.o rpmi-sandbox-clock.o
diff --git a/drivers/firmware/rpmi/rpmi-sandbox-clock.c b/drivers/firmware/rpmi/rpmi-sandbox-clock.c
new file mode 100644
index 000000000000..fa205d067b24
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox-clock.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <rpmi.h>
+#include "rpmi-sandbox.h"
+
+struct rpmi_clock {
+ enum rpmi_clock_type type;
+ const char *name;
+ u32 rate_count;
+ const u64 *rate_array;
+ u32 transition_latency;
+ enum rpmi_clock_state state;
+ u64 rate;
+};
+
+const u64 discrete_rates[] = { 20000000, 333333333, 500000000, 1000000000 };
+
+const u64 linear_rates[] = { 50000000, 100000000, 1000000 };
+
+#define DISCRETE_CLOCK(name, current_state, current_rate) \
+ { RPMI_CLK_TYPE_DISCRETE, \
+ name, \
+ ARRAY_SIZE(discrete_rates), \
+ discrete_rates, \
+ 0, \
+ current_state, \
+ current_rate }
+
+#define LINEAR_CLOCK(name, current_state, current_rate) \
+ { RPMI_CLK_TYPE_LINEAR, name, ARRAY_SIZE(linear_rates), \
+ linear_rates, 0, current_state, \
+ current_rate }
+
+struct rpmi_clock clock_simulation[] = {
+ DISCRETE_CLOCK("CLK0", RPMI_CLK_STATE_DISABLED, discrete_rates[0]),
+ DISCRETE_CLOCK("CLK1", RPMI_CLK_STATE_DISABLED, discrete_rates[1]),
+ DISCRETE_CLOCK("CLK2", RPMI_CLK_STATE_ENABLED, discrete_rates[2]),
+ LINEAR_CLOCK("CLK3", RPMI_CLK_STATE_ENABLED, linear_rates[0]),
+};
+
+static u32 clock_count = ARRAY_SIZE(clock_simulation);
+
+static struct rpmi_clock *rpmi_get_clock(u32 clkid)
+{
+ if (clkid >= clock_count)
+ return NULL;
+
+ return &clock_simulation[clkid];
+}
+
+static enum rpmi_error_codes
+rpmi_clock_set_rate(u32 clkid, enum rpmi_clock_rate_match match, u64 rate)
+{
+ struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+ if (!clk)
+ return RPMI_ERR_INVALID_PARAM;
+
+ clk->rate = rate;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_set_state(u32 clkid,
+ enum rpmi_clock_state state)
+{
+ struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+ if (!clk)
+ return RPMI_ERR_INVALID_PARAM;
+
+ clk->state = state;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_get_state(u32 clkid,
+ enum rpmi_clock_state *state)
+{
+ struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+ if (!clk || !state)
+ return RPMI_ERR_INVALID_PARAM;
+
+ *state = clk->state;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_clock_get_rate(u32 clkid, u64 *rate)
+{
+ struct rpmi_clock *clk = rpmi_get_clock(clkid);
+
+ if (!clk || !rate)
+ return RPMI_ERR_INVALID_PARAM;
+
+ *rate = clk->rate;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_num_clocks(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u32 *resp = (void *)response_data;
+
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+ resp[1] = cpu_to_le32(clock_count);
+
+ *response_datalen = 2 * sizeof(*resp);
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_attributes(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ struct rpmi_clock *clk;
+ u16 resp_dlen;
+ u32 flags = 0;
+
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ clk = rpmi_get_clock(clkid);
+ if (!clk) {
+ resp_dlen = sizeof(*resp);
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ /* encode CLOCK_FORMAT */
+ if (clk->type == RPMI_CLK_TYPE_LINEAR)
+ flags |= 1;
+
+ resp[3] = cpu_to_le32(clk->transition_latency);
+ resp[2] = cpu_to_le32(clk->rate_count);
+ resp[1] = cpu_to_le32(flags);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ if (clk->name)
+ strlcpy((char *)&resp[4], clk->name, RPMI_CLK_NAME_LEN);
+
+ resp_dlen = 8 * sizeof(*resp);
+
+done:
+ *response_datalen = resp_dlen;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_supp_rates(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u32 i = 0, j = 0;
+ u32 rate_count;
+ u32 resp_dlen = 0, clk_rate_idx = 0;
+ u32 max_rates, remaining = 0, returned = 0;
+ const u64 *rate_array;
+ struct rpmi_clock *clk;
+
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ clk = rpmi_get_clock(clkid);
+ if (clk) {
+ resp_dlen = sizeof(*resp);
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ rate_count = clk->rate_count;
+ rate_array = clk->rate_array;
+ if (!rate_count || !rate_array) {
+ resp_dlen = sizeof(*resp);
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_NOTSUPP);
+ goto done;
+ }
+
+ clk_rate_idx = cpu_to_le32(((const u32 *)request_data)[1]);
+
+ if (clk->type == RPMI_CLK_TYPE_LINEAR) {
+ /* max, min and step */
+ for (i = 0; i < 3; i++) {
+ resp[4 + 2 * i] = cpu_to_le32((u32)(rate_array[i]));
+ resp[5 + 2 * i] =
+ cpu_to_le32((u32)(rate_array[i] >> 32));
+ }
+ remaining = 0;
+ returned = 3;
+ } else if (clk->type == RPMI_CLK_TYPE_DISCRETE) {
+ if (clk_rate_idx > rate_count) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ resp_dlen = sizeof(*resp);
+ goto done;
+ }
+
+ /* max rates a rpmi message can accommodate */
+ max_rates = (trans->msg_data_size - (4 * sizeof(*resp))) /
+ sizeof(u64);
+ remaining = rate_count - clk_rate_idx;
+ if (remaining > max_rates)
+ returned = max_rates;
+ else
+ returned = remaining;
+
+ for (i = clk_rate_idx, j = 0;
+ i <= (clk_rate_idx + returned - 1); i++, j++) {
+ resp[4 + 2 * j] = cpu_to_le32((u32)(rate_array[i]));
+ resp[5 + 2 * j] =
+ cpu_to_le32((u32)(rate_array[i] >> 32));
+ }
+
+ remaining = rate_count - (clk_rate_idx + returned);
+ } else {
+ return RPMI_ERR_FAILED;
+ }
+
+ resp[3] = cpu_to_le32((u32)returned);
+ resp[2] = cpu_to_le32((u32)remaining);
+ /* No flags currently supported */
+ resp[1] = cpu_to_le32(0);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = (4 * sizeof(*resp)) + (returned * sizeof(u64));
+
+done:
+ *response_datalen = resp_dlen;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_set_config(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ enum rpmi_error_codes status;
+ u32 cfg, new_state;
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ if (clkid >= clock_count) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ cfg = cpu_to_le32(((const u32 *)request_data)[1]);
+
+ /* get command from 0th index bit in config field */
+ new_state = (cfg & 0b1) ? RPMI_CLK_STATE_ENABLED :
+ RPMI_CLK_STATE_DISABLED;
+
+ /* change clock config synchronously */
+ status = rpmi_clock_set_state(clkid, new_state);
+ resp[0] = cpu_to_le32((u32)status);
+
+done:
+ *response_datalen = sizeof(*resp);
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_config(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u16 resp_dlen;
+ enum rpmi_error_codes status;
+ enum rpmi_clock_state state;
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ if (clkid >= clock_count) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ resp_dlen = sizeof(*resp);
+ goto done;
+ }
+
+ status = rpmi_clock_get_state(clkid, &state);
+ if (status) {
+ resp[0] = cpu_to_le32((u32)status);
+ resp_dlen = sizeof(*resp);
+ goto done;
+ }
+
+ /** RPMI config field only return enabled or disabled state */
+ state = (state == RPMI_CLK_STATE_ENABLED) ? 1 : 0;
+
+ resp[1] = cpu_to_le32((u32)state);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = 2 * sizeof(*resp);
+
+done:
+ *response_datalen = resp_dlen;
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_set_rate(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ enum rpmi_error_codes status;
+ enum rpmi_clock_rate_match rate_match;
+ u32 flags;
+ u32 rate_low, rate_hi;
+ u64 rate_u64;
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ if (clkid >= clock_count) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ flags = cpu_to_le32(((const u32 *)request_data)[1]);
+
+ /* get rate match mode from flags */
+ rate_match = flags & 0b11;
+ if (rate_match >= RPMI_CLK_RATE_MATCH_MAX) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ rate_low = cpu_to_le32(((const u32 *)request_data)[2]);
+ rate_hi = cpu_to_le32(((const u32 *)request_data)[3]);
+
+ rate_u64 = ((u64)rate_hi << 32) | rate_low;
+ if (rate_u64 == RPMI_CLOCK_RATE_INVALID || rate_u64 == 0) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ status = rpmi_clock_set_rate(clkid, rate_match, rate_u64);
+ resp[0] = cpu_to_le32((u32)status);
+
+done:
+ *response_datalen = sizeof(*resp);
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_clock_sg_get_rate(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u16 resp_dlen;
+ enum rpmi_error_codes status;
+ u64 rate_u64;
+ u32 *resp = (void *)response_data;
+
+ u32 clkid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ if (clkid >= clock_count) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ resp_dlen = sizeof(*resp);
+ goto done;
+ }
+
+ status = rpmi_clock_get_rate(clkid, &rate_u64);
+ if (status) {
+ resp[0] = cpu_to_le32((u32)status);
+ resp_dlen = sizeof(*resp);
+ goto done;
+ }
+
+ resp[2] = cpu_to_le32((u32)(rate_u64 >> 32));
+ resp[1] = cpu_to_le32((u32)(rate_u64));
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = 3 * sizeof(*resp);
+
+done:
+ *response_datalen = resp_dlen;
+ return RPMI_SUCCESS;
+}
+
+static const struct rpmi_service rpmi_clock_services[RPMI_CLK_SRV_ID_MAX_COUNT] = {
+ [RPMI_CLK_SRV_GET_NUM_CLOCKS] = {
+ .service_id = RPMI_CLK_SRV_GET_NUM_CLOCKS,
+ .min_a2p_request_datalen = 0,
+ .process_a2p_request = rpmi_clock_sg_get_num_clocks,
+ },
+ [RPMI_CLK_SRV_GET_ATTRIBUTES] = {
+ .service_id = RPMI_CLK_SRV_GET_ATTRIBUTES,
+ .min_a2p_request_datalen = 4,
+ .process_a2p_request = rpmi_clock_sg_get_attributes,
+ },
+ [RPMI_CLK_SRV_GET_SUPPORTED_RATES] = {
+ .service_id = RPMI_CLK_SRV_GET_SUPPORTED_RATES,
+ .min_a2p_request_datalen = 8,
+ .process_a2p_request = rpmi_clock_sg_get_supp_rates,
+ },
+ [RPMI_CLK_SRV_SET_CONFIG] = {
+ .service_id = RPMI_CLK_SRV_SET_CONFIG,
+ .min_a2p_request_datalen = 8,
+ .process_a2p_request = rpmi_clock_sg_set_config,
+ },
+ [RPMI_CLK_SRV_GET_CONFIG] = {
+ .service_id = RPMI_CLK_SRV_GET_CONFIG,
+ .min_a2p_request_datalen = 4,
+ .process_a2p_request = rpmi_clock_sg_get_config,
+ },
+ [RPMI_CLK_SRV_SET_RATE] = {
+ .service_id = RPMI_CLK_SRV_SET_RATE,
+ .min_a2p_request_datalen = 16,
+ .process_a2p_request = rpmi_clock_sg_set_rate,
+ },
+ [RPMI_CLK_SRV_GET_RATE] = {
+ .service_id = RPMI_CLK_SRV_GET_RATE,
+ .min_a2p_request_datalen = 4,
+ .process_a2p_request = rpmi_clock_sg_get_rate,
+ },
+};
+
+const struct rpmi_service_group rpmi_sandbox_clock_sg = {
+ .name = "clk",
+ .max_service_id = RPMI_CLK_SRV_GET_RATE,
+ .servicegroup_version = RPMI_MKVER(1, 0),
+ .services = rpmi_clock_services,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.c b/drivers/firmware/rpmi/rpmi-sandbox.c
new file mode 100644
index 000000000000..5597c27067eb
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <dm.h>
+#include <log.h>
+#include <rpmi-uclass.h>
+#include <dm/device_compat.h>
+
+#include "rpmi-sandbox.h"
+
+#define RPMI_SANDBOX_MIN_SLOT_SIZE 64
+#define RPMI_SANDBOX_SHMEM_HEADER_SIZE 8
+#define RPMI_SANDBOX_MAX_MSG_SIZE \
+ (RPMI_SANDBOX_MIN_SLOT_SIZE - RPMI_SANDBOX_SHMEM_HEADER_SIZE)
+
+#define DRIVER_NAME "rpmi-sandbox"
+#define DRIVER_NAME_LEN (sizeof(DRIVER_NAME))
+
+struct rpmi_sandbox {
+ struct udevice *dev;
+};
+
+const struct rpmi_service_group *rpmi_sandbox_sg(u32 servicegroup_id)
+{
+ switch (servicegroup_id) {
+ case RPMI_SRVGRP_CLOCK:
+ return &rpmi_sandbox_clock_sg;
+ default:
+ return NULL;
+ }
+}
+
+static int rpmi_sandbox_get_attr(struct rpmi_chan *chan,
+ enum rpmi_attribute_id id, u32 *data)
+{
+ const struct rpmi_service_group *sg = rpmi_sandbox_sg(chan->id);
+
+ if (!sg)
+ return -EOPNOTSUPP;
+
+ switch (id) {
+ case RPMI_ATTR_SPEC_VERSION:
+ *data = RPMI_MKVER(1, 0);
+ break;
+ case RPMI_ATTR_MAX_MSG_DATA_SIZE:
+ *data = RPMI_SANDBOX_MAX_MSG_SIZE;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_ID:
+ *data = chan->id;
+ break;
+ case RPMI_ATTR_SERVICEGROUP_VERSION:
+ *data = sg->servicegroup_version;
+ break;
+ case RPMI_ATTR_IMPL_ID:
+ *data = 0;
+ break;
+ case RPMI_ATTR_IMPL_VERSION:
+ *data = RPMI_MKVER(1, 0);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int rpmi_sandbox_request(struct rpmi_chan *chan, u32 service_id,
+ const void *request, unsigned long request_len,
+ void *response, unsigned long max_response_len,
+ unsigned long *out_response_len)
+{
+ const struct rpmi_service_group *sg = rpmi_sandbox_sg(chan->id);
+ const struct rpmi_service *service;
+ u8 slot[RPMI_SANDBOX_MAX_MSG_SIZE];
+ struct rpmi_transport trans = {
+ .msg_data_size = sizeof(slot),
+ };
+ u16 response_data_len = 0;
+ enum rpmi_error_codes err;
+
+ if (!sg)
+ return -EOPNOTSUPP;
+
+ if (service_id > sg->max_service_id)
+ return -EOPNOTSUPP;
+
+ service = &sg->services[service_id];
+
+ if (!service)
+ return -EOPNOTSUPP;
+
+ if (request_len < service->min_a2p_request_datalen)
+ return -EINVAL;
+
+ if (!service->process_a2p_request)
+ return -ENOSYS;
+
+ err = service->process_a2p_request(&trans, request_len, request,
+ &response_data_len, slot);
+
+ if (err)
+ return rpmi_to_linux_error(err);
+
+ if (out_response_len)
+ *out_response_len = response_data_len;
+
+ if (response)
+ memcpy(response, slot,
+ min(max_response_len, (unsigned long)response_data_len));
+
+ return 0;
+}
+
+static int rpmi_sandbox_of_xlate(struct rpmi_chan *chan,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ chan->id = args->args[0];
+
+ return 0;
+}
+
+static int rpmi_sandbox_probe(struct udevice *dev)
+{
+ struct rpmi_sandbox *priv = dev_get_priv(dev);
+
+ priv->dev = dev;
+
+ return 0;
+}
+
+static const struct udevice_id rpmi_sandbox_ids[] = {
+ { .compatible = "sandbox,rpmi-transport" },
+ {}
+};
+
+static const struct rpmi_transport_ops rpmi_sandbox_ops = {
+ .of_xlate = rpmi_sandbox_of_xlate,
+ .get_attr = rpmi_sandbox_get_attr,
+ .request = rpmi_sandbox_request,
+};
+
+U_BOOT_DRIVER(rpmi_sandbox) = {
+ .name = "rpmi-sandbox",
+ .id = UCLASS_RPMI,
+ .of_match = rpmi_sandbox_ids,
+ .probe = rpmi_sandbox_probe,
+ .priv_auto = sizeof(struct rpmi_sandbox),
+ .ops = &rpmi_sandbox_ops,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.h b/drivers/firmware/rpmi/rpmi-sandbox.h
new file mode 100644
index 000000000000..132d1e9e8be6
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#ifndef _RPMI_SANDBOX_H
+#define _RPMI_SANDBOX_H
+
+#include <stdbool.h>
+#include <asm/types.h>
+#include <rpmi.h>
+
+struct rpmi_transport {
+ int msg_data_size;
+};
+
+struct rpmi_service {
+ /** ID of the service */
+ u8 service_id;
+
+ /** Minimum data length for handling request */
+ u16 min_a2p_request_datalen;
+
+ /** Callback to process a2p request */
+ enum rpmi_error_codes (*process_a2p_request)(
+ struct rpmi_transport *trans, u16 request_data_len,
+ const u8 *request_data, u16 *response_data_len,
+ u8 *response_data);
+};
+
+struct rpmi_service_group {
+ /** Name of the service group */
+ const char *name;
+
+ /** Maximum service ID of the service group */
+ int max_service_id;
+
+ /** Service group version */
+ u32 servicegroup_version;
+
+ /** Array of services indexed by service ID */
+ const struct rpmi_service *services;
+};
+
+extern const struct rpmi_service_group rpmi_sandbox_clock_sg;
+
+#endif
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 0e3c63568ddb..ee03b4e13de2 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
obj-$(CONFIG_$(PHASE_)DM_RNG) += rng.o
obj-$(CONFIG_DM_RTC) += rtc.o
obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o
+obj-$(CONFIG_RPMI_FIRMWARE) += rpmi.o
obj-$(CONFIG_SCSI) += scsi.o
obj-$(CONFIG_DM_SERIAL) += serial.o
obj-$(CONFIG_DM_SPI_FLASH) += sf.o
diff --git a/test/dm/rpmi.c b/test/dm/rpmi.c
new file mode 100644
index 000000000000..1ea29780a480
--- /dev/null
+++ b/test/dm/rpmi.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#include <dm.h>
+#include <rpmi.h>
+#include <clk.h>
+#include <stdio.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static int dm_test_rpmi_clock(struct unit_test_state *uts)
+{
+ struct udevice *rpmi_dev, *clk_dev;
+ struct clk clk0, clk1, clk2, clk3;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RPMI, "rpmi", &rpmi_dev));
+
+ ut_assertok(
+ uclass_get_device_by_name(UCLASS_CLK, "rpmi-clock", &clk_dev));
+
+ // Grab clock 0 and retrieve the rate
+ ut_assertok(clk_get_by_index(clk_dev, 0, &clk0));
+ ut_asserteq_64(20000000, clk_get_rate(&clk0));
+
+ // Change to 1GHz
+ ut_asserteq(1000000000, clk_set_rate(&clk0, 1000000000));
+
+ // Revert back to 200MHz
+ ut_asserteq(20000000, clk_set_rate(&clk0, 20000000));
+
+ // Grab clock 1
+ ut_assertok(clk_get_by_index(clk_dev, 1, &clk1));
+ ut_asserteq_64(333333333, clk_get_rate(&clk1));
+
+ // Grab clock 2
+ ut_assertok(clk_get_by_index(clk_dev, 2, &clk2));
+ ut_asserteq_64(500000000, clk_get_rate(&clk2));
+
+ // Grab clock 3
+ ut_assertok(clk_get_by_index(clk_dev, 3, &clk3));
+ ut_asserteq_64(50000000, clk_get_rate(&clk3));
+
+ return 0;
+}
+DM_TEST(dm_test_rpmi_clock, UTF_SCAN_FDT);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
` (5 preceding siblings ...)
2026-06-26 20:15 ` [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver Charles Perry
@ 2026-06-26 20:15 ` Charles Perry
6 siblings, 0 replies; 11+ messages in thread
From: Charles Perry @ 2026-06-26 20:15 UTC (permalink / raw)
To: u-boot
Cc: Rahul Pathak, Anup Patel, Charles Perry, Simon Glass, Tom Rini,
Mattijs Korpershoek, Marek Vasut, Heiko Schocher,
Svyatoslav Ryhel, Markus Schneider-Pargmann (TI.com),
Neil Armstrong, Lucien.Jheng, Martin Schwan, Michal Simek,
Peng Fan, Pieter Van Trappen, Dinesh Maniyam
Add a simulation of the device power service group using code from
librpmi [1] and adapted.
[1]: https://github.com/riscv-software-src/librpmi/blob/3f3c4ca47339a8795432e260f17e1fc5091c9a3a/lib/rpmi_service_group_device_power.c
Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
arch/sandbox/dts/test.dts | 11 ++
configs/sandbox_defconfig | 1 +
drivers/firmware/rpmi/Makefile | 2 +-
drivers/firmware/rpmi/rpmi-sandbox-power.c | 174 +++++++++++++++++++++
drivers/firmware/rpmi/rpmi-sandbox.c | 2 +
drivers/firmware/rpmi/rpmi-sandbox.h | 1 +
test/dm/rpmi.c | 21 +++
7 files changed, 211 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/rpmi/rpmi-sandbox-power.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e5d6f398fb35..7e25053851f4 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -886,6 +886,12 @@
#clock-cells = <1>;
clocks = <&rpmi_clock 0>, <&rpmi_clock 1>, <&rpmi_clock 2>, <&rpmi_clock 3>;
};
+
+ rpmi_dpwr: rpmi-power-domain {
+ compatible = "riscv,rpmi-device-power";
+ mboxes = <&rpmi 0x9>;
+ #power-domain-cells = <1>;
+ };
};
fpga {
@@ -1927,6 +1933,11 @@
regul1-supply = <®ul1_scmi>;
};
+ rpmi-power-domain-test {
+ compatible = "sandbox,power-domain-test";
+ power-domains = <&rpmi_dpwr 1>;
+ };
+
pinctrl {
compatible = "sandbox,pinctrl";
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index f1637e1ff09c..59967bd2ee0f 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -289,6 +289,7 @@ CONFIG_PINCONF=y
CONFIG_PINCTRL_SANDBOX=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_POWER_DOMAIN=y
+CONFIG_RPMI_POWER_DOMAIN=y
CONFIG_SANDBOX_POWER_DOMAIN=y
CONFIG_SCMI_POWER_DOMAIN=y
CONFIG_DM_PMIC=y
diff --git a/drivers/firmware/rpmi/Makefile b/drivers/firmware/rpmi/Makefile
index 150e98a72df9..c73ad92baec1 100644
--- a/drivers/firmware/rpmi/Makefile
+++ b/drivers/firmware/rpmi/Makefile
@@ -1,4 +1,4 @@
obj-y += rpmi-uclass.o
obj-$(CONFIG_RPMI_SBI_MPXY) += rpmi-sbi-mpxy.o
obj-$(CONFIG_RPMI_SHMEM) += rpmi-shmem.o
-obj-$(CONFIG_SANDBOX) += rpmi-sandbox.o rpmi-sandbox-clock.o
+obj-$(CONFIG_SANDBOX) += rpmi-sandbox.o rpmi-sandbox-clock.o rpmi-sandbox-power.o
diff --git a/drivers/firmware/rpmi/rpmi-sandbox-power.c b/drivers/firmware/rpmi/rpmi-sandbox-power.c
new file mode 100644
index 000000000000..aba0ecdda0a6
--- /dev/null
+++ b/drivers/firmware/rpmi/rpmi-sandbox-power.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_RPMI
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <rpmi.h>
+#include "rpmi-sandbox.h"
+
+struct rpmi_dpwr {
+ const char *name;
+ u32 trans_latency;
+ enum rpmi_dpwr_state state;
+};
+
+struct rpmi_dpwr dpwr_simulation[] = {
+ { "DPWR0", 0, RPMI_DPWR_STATE_ON },
+ { "DPWR1", 0, RPMI_DPWR_STATE_OFF },
+};
+
+static u32 dpwr_count = ARRAY_SIZE(dpwr_simulation);
+
+static struct rpmi_dpwr *rpmi_get_dpwr(u32 dpwrid)
+{
+ if (dpwrid >= ARRAY_SIZE(dpwr_simulation))
+ return NULL;
+
+ return &dpwr_simulation[dpwrid];
+}
+
+static enum rpmi_error_codes
+rpmi_dpwr_get_num_domains(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u32 *resp = (void *)response_data;
+
+ resp[1] = cpu_to_le32(dpwr_count);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ *response_datalen = 2 * sizeof(*resp);
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes
+rpmi_dpwr_get_attributes(struct rpmi_transport *trans, u16 request_datalen,
+ const u8 *request_data, u16 *response_datalen,
+ u8 *response_data)
+{
+ u16 resp_dlen = sizeof(u32);
+ u32 *resp = (void *)response_data;
+ struct rpmi_dpwr *dpwr;
+
+ u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ dpwr = rpmi_get_dpwr(dpwrid);
+ if (!dpwr) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ if (dpwr->name)
+ strlcpy((char *)&resp[3], dpwr->name, RPMI_DPWR_NAME_LEN);
+
+ resp[2] = cpu_to_le32(dpwr->trans_latency);
+ resp[1] = cpu_to_le32((u32)0x0);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = 3 * sizeof(*resp) + RPMI_DPWR_NAME_LEN;
+
+done:
+ *response_datalen = resp_dlen;
+
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_dpwr_get_state(struct rpmi_transport *trans,
+ u16 request_datalen,
+ const u8 *request_data,
+ u16 *response_datalen,
+ u8 *response_data)
+{
+ u16 resp_dlen = sizeof(u32);
+ u32 *resp = (void *)response_data;
+ u32 dpwr_state = 0;
+ struct rpmi_dpwr *dpwr;
+
+ u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+
+ dpwr = rpmi_get_dpwr(dpwrid);
+ if (!dpwr) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ dpwr_state = dpwr->state;
+
+ resp[1] = cpu_to_le32((u32)dpwr_state);
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = 2 * sizeof(*resp);
+
+done:
+ *response_datalen = resp_dlen;
+ return RPMI_SUCCESS;
+}
+
+static enum rpmi_error_codes rpmi_dpwr_set_state(struct rpmi_transport *trans,
+ u16 request_datalen,
+ const u8 *request_data,
+ u16 *response_datalen,
+ u8 *response_data)
+{
+ u16 resp_dlen = sizeof(u32);
+ u32 *resp = (void *)response_data;
+ struct rpmi_dpwr *dpwr;
+
+ u32 dpwrid = cpu_to_le32(((const u32 *)request_data)[0]);
+ u32 dpwr_state = cpu_to_le32(((const u32 *)request_data)[1]);
+
+ dpwr = rpmi_get_dpwr(dpwrid);
+ if (!dpwr) {
+ resp[0] = cpu_to_le32((u32)RPMI_ERR_INVALID_PARAM);
+ goto done;
+ }
+
+ dpwr->state = dpwr_state;
+
+ resp[0] = cpu_to_le32((u32)RPMI_SUCCESS);
+
+ resp_dlen = sizeof(*resp);
+
+done:
+ *response_datalen = resp_dlen;
+ return RPMI_SUCCESS;
+}
+
+static struct rpmi_service rpmi_dpwr_services[RPMI_DPWR_SRV_ID_MAX] = {
+ [RPMI_DPWR_SRV_GET_NUM_DOMAINS] = {
+ .service_id = RPMI_DPWR_SRV_GET_NUM_DOMAINS,
+ .min_a2p_request_datalen = 0,
+ .process_a2p_request = rpmi_dpwr_get_num_domains,
+ },
+ [RPMI_DPWR_SRV_GET_ATTRIBUTES] = {
+ .service_id = RPMI_DPWR_SRV_GET_ATTRIBUTES,
+ .min_a2p_request_datalen = 4,
+ .process_a2p_request = rpmi_dpwr_get_attributes,
+ },
+ [RPMI_DPWR_SRV_SET_DPWR_STATE] = {
+ .service_id = RPMI_DPWR_SRV_SET_DPWR_STATE,
+ .min_a2p_request_datalen = 8,
+ .process_a2p_request = rpmi_dpwr_set_state,
+ },
+ [RPMI_DPWR_SRV_GET_DPWR_STATE] = {
+ .service_id = RPMI_DPWR_SRV_GET_DPWR_STATE,
+ .min_a2p_request_datalen = 4,
+ .process_a2p_request = rpmi_dpwr_get_state,
+ },
+};
+
+const struct rpmi_service_group rpmi_sandbox_dpwr_sg = {
+ .name = "dpwr",
+ .max_service_id = RPMI_DPWR_SRV_GET_DPWR_STATE,
+ .servicegroup_version = RPMI_MKVER(1, 0),
+ .services = rpmi_dpwr_services,
+};
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.c b/drivers/firmware/rpmi/rpmi-sandbox.c
index 5597c27067eb..c34c61da5d9e 100644
--- a/drivers/firmware/rpmi/rpmi-sandbox.c
+++ b/drivers/firmware/rpmi/rpmi-sandbox.c
@@ -29,6 +29,8 @@ const struct rpmi_service_group *rpmi_sandbox_sg(u32 servicegroup_id)
switch (servicegroup_id) {
case RPMI_SRVGRP_CLOCK:
return &rpmi_sandbox_clock_sg;
+ case RPMI_SRVGRP_DEVICE_POWER:
+ return &rpmi_sandbox_dpwr_sg;
default:
return NULL;
}
diff --git a/drivers/firmware/rpmi/rpmi-sandbox.h b/drivers/firmware/rpmi/rpmi-sandbox.h
index 132d1e9e8be6..7dea5035929e 100644
--- a/drivers/firmware/rpmi/rpmi-sandbox.h
+++ b/drivers/firmware/rpmi/rpmi-sandbox.h
@@ -43,5 +43,6 @@ struct rpmi_service_group {
};
extern const struct rpmi_service_group rpmi_sandbox_clock_sg;
+extern const struct rpmi_service_group rpmi_sandbox_dpwr_sg;
#endif
diff --git a/test/dm/rpmi.c b/test/dm/rpmi.c
index 1ea29780a480..e886b9ab2da4 100644
--- a/test/dm/rpmi.c
+++ b/test/dm/rpmi.c
@@ -9,6 +9,7 @@
#include <stdio.h>
#include <dm/device-internal.h>
#include <dm/test.h>
+#include <asm/power-domain.h>
#include <test/ut.h>
static int dm_test_rpmi_clock(struct unit_test_state *uts)
@@ -46,3 +47,23 @@ static int dm_test_rpmi_clock(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_rpmi_clock, UTF_SCAN_FDT);
+
+static int dm_test_rpmi_dpwr(struct unit_test_state *uts)
+{
+ struct udevice *rpmi_dev, *dpwr_dev, *dpwr_consumer;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RPMI, "rpmi", &rpmi_dev));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+ "rpmi-power-domain", &dpwr_dev));
+
+ ut_assertok(uclass_get_device_by_name(
+ UCLASS_MISC, "rpmi-power-domain-test", &dpwr_consumer));
+
+ ut_assertok(sandbox_power_domain_test_get(dpwr_consumer));
+ ut_assertok(sandbox_power_domain_test_on(dpwr_consumer));
+ ut_assertok(sandbox_power_domain_test_off(dpwr_consumer));
+
+ return 0;
+}
+DM_TEST(dm_test_rpmi_dpwr, UTF_SCAN_FDT);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/7] firmware: add support for RPMI
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
@ 2026-06-27 1:09 ` Yao Zi
0 siblings, 0 replies; 11+ messages in thread
From: Yao Zi @ 2026-06-27 1:09 UTC (permalink / raw)
To: Charles Perry, u-boot
Cc: Rahul Pathak, Anup Patel, Tom Rini, Simon Glass, Neil Armstrong,
Kory Maincent, Peng Fan, Kuan-Wei Chiu, Raymond Mao,
Quentin Schulz, Stefan Roese, Jerome Forissier, Philip Molloy,
Michael Trimarchi, Michal Simek, Svyatoslav Ryhel, Ion Agorria,
Dinesh Maniyam, Yao Zi
On Fri, Jun 26, 2026 at 01:15:42PM -0700, Charles Perry wrote:
> 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>
> ---
> 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 | 138 ++++++++++++++++++++++++
> include/dm/uclass-id.h | 1 +
> include/rpmi-uclass.h | 79 ++++++++++++++
> include/rpmi.h | 159 ++++++++++++++++++++++++++++
> include/rpmi_proto.h | 63 +++++++++++
> 10 files changed, 458 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/drivers/firmware/rpmi/rpmi-uclass.c b/drivers/firmware/rpmi/rpmi-uclass.c
> new file mode 100644
> index 000000000000..b725f9e59441
> --- /dev/null
> +++ b/drivers/firmware/rpmi/rpmi-uclass.c
> @@ -0,0 +1,138 @@
> +// 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>
Should we sort the headers? This applies to rest of the series, too.
...
> +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 mismatch\n");
The error is caused by a "too old" protocol version instead of a
"mismatched" one, shouldn't we be precise here?
Furthermore, it would be good to print the the expected and implemented
versions as well to ease debugging. debug() could be used so we don't
get the binary size increased in a normal build. So do the
servicegroup_id and servicegroup_version's case.
> + 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\n");
> + 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 mismatch\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
...
> diff --git a/include/rpmi.h b/include/rpmi.h
> new file mode 100644
> index 000000000000..de802026b31a
> --- /dev/null
> +++ b/include/rpmi.h
...
> +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;
RPMI_ERR_HW_FAULT means "failed due to hardware fault" according to
RPMI specification, does it really count as "-EFAULT" which is
"bad address"?
> + 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;
> + }
> +}
Regards,
Yao Zi
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
@ 2026-06-27 1:30 ` Yao Zi
0 siblings, 0 replies; 11+ messages in thread
From: Yao Zi @ 2026-06-27 1:30 UTC (permalink / raw)
To: Charles Perry, u-boot
Cc: Rahul Pathak, Anup Patel, Rick Chen, Leo, Tom Rini, Yao Zi
On Fri, Jun 26, 2026 at 01:15:43PM -0700, Charles Perry wrote:
> 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>
> ---
> arch/riscv/include/asm/sbi.h | 57 ++++
> drivers/firmware/rpmi/Kconfig | 9 +
> drivers/firmware/rpmi/Makefile | 1 +
> drivers/firmware/rpmi/rpmi-sbi-mpxy.c | 449 ++++++++++++++++++++++++++
> 4 files changed, 516 insertions(+)
> create mode 100644 drivers/firmware/rpmi/rpmi-sbi-mpxy.c
...
> diff --git a/drivers/firmware/rpmi/rpmi-sbi-mpxy.c b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
> new file mode 100644
> index 000000000000..84af4787187a
> --- /dev/null
> +++ b/drivers/firmware/rpmi/rpmi-sbi-mpxy.c
...
> +/* RPMI message protocol specific MPXY attributes */
> +enum sbi_mpxy_rpmi_attribute_id {
> + SBI_MPXY_RPMI_ATTR_SERVICEGROUP_ID = SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
> + SBI_MPXY_RPMI_ATTR_SERVICEGROUP_VERSION,
> + SBI_MPXY_RPMI_ATTR_IMPL_ID,
> + SBI_MPXY_RPMI_ATTR_IMPL_VERSION,
> + SBI_MPXY_RPMI_ATTR_MAX_ID
> +};
> +
> +/*
> + * RPMI specific SBI MPXY channel attributes.
> + */
> +struct sbi_mpxy_rpmi_channel_attrs {
Would it make sense to organize sbi_mpxy_rpmi_channel_attrs as an
array indexed by sbi_mpxy_rpmi_attributed_id subtracted by
SBI_MPXY_RPMI_ATTR_MSGPROTO_ATTR_START? With which we could simplify
rpmi_sbi_mpxy_get_attr().
> + /* RPMI service group ID */
> + u32 servicegroup_id;
> + /* RPMI service group version */
> + u32 servicegroup_version;
> + /* RPMI implementation ID */
> + u32 impl_id;
> + /* RPMI implementation version */
> + u32 impl_version;
> +};
...
> +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.msg_proto_version;
> + break;
> + case RPMI_ATTR_MAX_MSG_DATA_SIZE:
> + *data = desc->max_xfer_len;
> + break;
> + case RPMI_ATTR_SERVICEGROUP_ID:
> + *data = desc->rpmi_attrs.servicegroup_id;
> + break;
> + case RPMI_ATTR_SERVICEGROUP_VERSION:
> + *data = desc->rpmi_attrs.servicegroup_version;
> + break;
> + case RPMI_ATTR_IMPL_ID:
> + *data = desc->rpmi_attrs.impl_id;
> + break;
> + case RPMI_ATTR_IMPL_VERSION:
> + *data = desc->rpmi_attrs.impl_version;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + 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.msg_max_len);
> + }
> +
> + return 0;
> +
> +err:
> + if (mpxy->channel_descs)
> + free(mpxy->channel_descs);
> +
> + if (mpxy->shmem)
> + free(mpxy->shmem);
free() is no-op when the argument is NULL, so the check is redundant.
This applies to rpmi_sbi_mpxy_remove() and other similar cases in the
series, too.
> +
> + return ret;
> +}
> +
> +static int rpmi_sbi_mpxy_remove(struct udevice *dev)
> +{
> + struct rpmi_sbi_mpxy *mpxy = dev_get_priv(dev);
> +
> + if (mpxy->channel_descs)
> + free(mpxy->channel_descs);
> +
> + if (mpxy->shmem)
> + free(mpxy->shmem);
> +
> + return 0;
> +}
Regards,
Yao Zi
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/7] drivers: clk: add support for RPMI clocks
2026-06-26 20:15 ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Charles Perry
@ 2026-06-27 1:47 ` Yao Zi
0 siblings, 0 replies; 11+ messages in thread
From: Yao Zi @ 2026-06-27 1:47 UTC (permalink / raw)
To: Charles Perry, u-boot
Cc: Rahul Pathak, Anup Patel, Tom Rini, Lukasz Majewski,
Neil Armstrong, Simon Glass, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Raymond Mao, Quentin Schulz, Stefan Roese,
Philip Molloy, Jerome Forissier, Michal Simek, Michael Trimarchi,
Peter Korsgaard, Yao Zi
On Fri, Jun 26, 2026 at 01:15:45PM -0700, Charles Perry wrote:
> 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>
> ---
> MAINTAINERS | 1 +
> drivers/clk/Kconfig | 7 +
> drivers/clk/Makefile | 1 +
> drivers/clk/clk_rpmi.c | 346 +++++++++++++++++++++++++++++++++++++++++
> include/rpmi_proto.h | 39 +++++
> 5 files changed, 394 insertions(+)
> create mode 100644 drivers/clk/clk_rpmi.c
...
> diff --git a/drivers/clk/clk_rpmi.c b/drivers/clk/clk_rpmi.c
> new file mode 100644
> index 000000000000..d2efb057600b
> --- /dev/null
> +++ b/drivers/clk/clk_rpmi.c
> @@ -0,0 +1,346 @@
...
> +struct rpmi_clk_def {
> + u32 num_rates;
> + u32 transition_latency;
rpmi_clk_def.transition_latency only gets assigned in
rpmi_clk_get_attrs() and never used. Since U-Boot's clock infrastructure
doesn't care about it at all, maybe we could drop the field?
> + enum rpmi_clock_type type;
> + char name[RPMI_CLK_NAME_LEN + 1];
> +};
...
> +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);
> + if (status)
> + return rpmi_to_linux_error(status);
> +
> + return 0;
Please return rpmi_to_linux_error(status) and drop the if above.
> +}
> +
> +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 int _rpmi_clk_set_rate(struct rpmi_clk_priv *priv, u32 clkid,
> + unsigned long rate)
This function is declared to return int, but...
> +{
> + 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);
> + if (status)
> + return rpmi_to_linux_error(status);
> +
> + return _rpmi_clk_get_rate(priv, clkid);
_rpmi_clk_get_rate() returns ulong. This unnecessarily truncates the
rate on 64bit platform when it's above approximately 2.1GHz.
> +}
Regards,
Yao Zi
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-27 1:47 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
2026-06-27 1:09 ` Yao Zi
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
2026-06-27 1:30 ` Yao Zi
2026-06-26 20:15 ` [PATCH 3/7] firmware: rpmi: add support for shared memory transport Charles Perry
2026-06-26 20:15 ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Charles Perry
2026-06-27 1:47 ` Yao Zi
2026-06-26 20:15 ` [PATCH 5/7] drivers: power: add support for RPMI power domains Charles Perry
2026-06-26 20:15 ` [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver Charles Perry
2026-06-26 20:15 ` [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power Charles Perry
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox