From: Charles Perry <charles.perry@microchip.com>
To: <u-boot@lists.denx.de>
Cc: Rahul Pathak <rahul@summations.net>,
Anup Patel <anup@brainfault.org>,
Charles Perry <charles.perry@microchip.com>,
Tom Rini <trini@konsulko.com>
Subject: [PATCH 3/7] firmware: rpmi: add support for shared memory transport
Date: Fri, 26 Jun 2026 13:15:44 -0700 [thread overview]
Message-ID: <20260626201613.1035208-4-charles.perry@microchip.com> (raw)
In-Reply-To: <20260626201613.1035208-1-charles.perry@microchip.com>
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
next prev parent reply other threads:[~2026-06-26 21:20 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
2026-06-27 1:09 ` Yao Zi
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
2026-06-27 1:30 ` Yao Zi
2026-06-26 20:15 ` Charles Perry [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260626201613.1035208-4-charles.perry@microchip.com \
--to=charles.perry@microchip.com \
--cc=anup@brainfault.org \
--cc=rahul@summations.net \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox