[PATCH 16/16] lib: utils/mpxy: Add RPMI client driver for MPXY
Anup Patel
apatel at ventanamicro.com
Tue Aug 6 00:33:38 PDT 2024
From: Rahul Pathak <rpathak at ventanamicro.com>
Add a generic RPMI mailbox client driver which provides a MPXY channel.
Initially, this driver only supports RPMI clock service group but can
be extended to support multiple RPMI service groups.
Signed-off-by: Rahul Pathak <rpathak at ventanamicro.com>
Co-developed-by: Anup Patel <apatel at ventanamicro.com>
Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
include/sbi_utils/mailbox/rpmi_msgprot.h | 93 ++++++
lib/utils/mpxy/Kconfig | 9 +
lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c | 408 +++++++++++++++++++++++
lib/utils/mpxy/objects.mk | 3 +
platform/generic/configs/defconfig | 1 +
5 files changed, 514 insertions(+)
create mode 100644 lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c
diff --git a/include/sbi_utils/mailbox/rpmi_msgprot.h b/include/sbi_utils/mailbox/rpmi_msgprot.h
index f7913ab1..50d34017 100644
--- a/include/sbi_utils/mailbox/rpmi_msgprot.h
+++ b/include/sbi_utils/mailbox/rpmi_msgprot.h
@@ -153,6 +153,7 @@ enum rpmi_servicegroup_id {
RPMI_SRVGRP_SYSTEM_SUSPEND = 0x00003,
RPMI_SRVGRP_HSM = 0x00004,
RPMI_SRVGRP_CPPC = 0x00005,
+ RPMI_SRVGRP_CLOCK = 0x00007,
RPMI_SRVGRP_ID_MAX_COUNT,
};
@@ -417,4 +418,96 @@ struct rpmi_cppc_hart_list_resp {
u32 hartid[(RPMI_MSG_DATA_SIZE(RPMI_SLOT_SIZE_MIN) - (sizeof(u32) * 3)) / sizeof(u32)];
};
+/** RPMI Clock ServiceGroup Service IDs */
+enum rpmi_clock_service_id {
+ RPMI_CLOCK_SRV_ENABLE_NOTIFICATION = 0x01,
+ RPMI_CLOCK_SRV_GET_NUM_CLOCKS = 0x02,
+ RPMI_CLOCK_SRV_GET_ATTRIBUTES = 0x03,
+ RPMI_CLOCK_SRV_GET_SUPPORTED_RATES = 0x04,
+ RPMI_CLOCK_SRV_SET_CONFIG = 0x05,
+ RPMI_CLOCK_SRV_GET_CONFIG = 0x06,
+ RPMI_CLOCK_SRV_SET_RATE = 0x07,
+ RPMI_CLOCK_SRV_GET_RATE = 0x08,
+ RPMI_CLOCK_SRV_MAX_COUNT,
+};
+
+struct rpmi_clock_get_num_clocks_resp {
+ s32 status;
+ u32 num_clocks;
+};
+
+struct rpmi_clock_get_attributes_req {
+ u32 clock_id;
+};
+
+struct rpmi_clock_get_attributes_resp {
+ s32 status;
+#define RPMI_CLOCK_FLAGS_FORMAT_POS 30
+#define RPMI_CLOCK_FLAGS_FORMAT_MASK \
+ (3U << RPMI_CLOCK_FLAGS_CLOCK_FORMAT_POS)
+#define RPMI_CLOCK_FLAGS_FORMAT_DISCRETE 0
+#define RPMI_CLOCK_FLAGS_FORMAT_LINEAR 1
+ u32 flags;
+ u32 num_rates;
+ u32 transition_latency;
+ u8 name[16];
+};
+
+struct rpmi_clock_get_supported_rates_req {
+ u32 clock_id;
+ u32 clock_rate_index;
+};
+
+struct rpmi_clock_get_supported_rates_resp {
+ s32 status;
+ u32 flags;
+ u32 remaining;
+ u32 returned;
+ u32 clock_rate[0];
+};
+
+struct rpmi_clock_set_config_req {
+ u32 clock_id;
+#define RPMI_CLOCK_CONFIG_ENABLE (1U << 0)
+ u32 config;
+};
+
+struct rpmi_clock_set_config_resp {
+ s32 status;
+};
+
+struct rpmi_clock_get_config_req {
+ u32 clock_id;
+};
+
+struct rpmi_clock_get_config_resp {
+ s32 status;
+ u32 config;
+};
+
+struct rpmi_clock_set_rate_req {
+ u32 clock_id;
+#define RPMI_CLOCK_SET_RATE_FLAGS_MASK (3U << 0)
+#define RPMI_CLOCK_SET_RATE_FLAGS_ROUND_DOWN 0
+#define RPMI_CLOCK_SET_RATE_FLAGS_ROUND_UP 1
+#define RPMI_CLOCK_SET_RATE_FLAGS_ROUND_PLAT 2
+ u32 flags;
+ u32 clock_rate_low;
+ u32 clock_rate_high;
+};
+
+struct rpmi_clock_set_rate_resp {
+ s32 status;
+};
+
+struct rpmi_clock_get_rate_req {
+ u32 clock_id;
+};
+
+struct rpmi_clock_get_rate_resp {
+ s32 status;
+ u32 clock_rate_low;
+ u32 clock_rate_high;
+};
+
#endif /* !__RPMI_MSGPROT_H__ */
diff --git a/lib/utils/mpxy/Kconfig b/lib/utils/mpxy/Kconfig
index d084b09a..131fb91a 100644
--- a/lib/utils/mpxy/Kconfig
+++ b/lib/utils/mpxy/Kconfig
@@ -7,4 +7,13 @@ config FDT_MPXY
depends on FDT
default n
+if FDT_MPXY
+
+config FDT_MPXY_RPMI_MBOX
+ bool "FDT MPXY mailbox client driver"
+ depends on FDT_MAILBOX
+ default n
+
+endif
+
endmenu
diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c
new file mode 100644
index 00000000..d396aa7f
--- /dev/null
+++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c
@@ -0,0 +1,408 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Rahul Pathak <rpathak at ventanamicro.com>
+ * Anup Patel <apatel at ventanamicro.com>
+ */
+
+#include <libfdt.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_mpxy.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/mpxy/fdt_mpxy.h>
+#include <sbi_utils/mailbox/fdt_mailbox.h>
+#include <sbi_utils/mailbox/rpmi_msgprot.h>
+#include <sbi/sbi_console.h>
+
+#define RPMI_MAJOR_VER (0x0000)
+#define RPMI_MINOR_VER (0x0001)
+#define RPMI_MSG_SEND_TIMEOUT (10) /* microseconds */
+
+/** Convert the mpxy attribute ID to attribute array index */
+#define attr_id2index(attr_id) (attr_id - SBI_MPXY_ATTR_MSGPROTO_ATTR_START)
+
+enum mpxy_msgprot_rpmi_attr_id {
+ MPXY_MSGPROT_RPMI_ATTR_SERVICEGROUP_ID = SBI_MPXY_ATTR_MSGPROTO_ATTR_START,
+ MPXY_MSGPROT_RPMI_ATTR_MAX_ID,
+};
+
+/**
+ * MPXY message protocol attributes for RPMI
+ * Order of attribute fields must follow the
+ * attribute IDs in `enum mpxy_msgprot_rpmi_attr_id`
+ */
+struct mpxy_rpmi_channel_attrs {
+ u32 servicegrp_id;
+};
+
+/* RPMI mbox data per service group */
+struct mpxy_mbox_data {
+ u32 servicegrp_id;
+ u32 num_services;
+ u32 notifications_support;
+ void *priv_data;
+};
+
+/* RPMI service data per service group */
+struct rpmi_service_data {
+ u8 id;
+ u32 min_tx_len;
+ u32 max_tx_len;
+ u32 min_rx_len;
+ u32 max_rx_len;
+};
+
+/**
+ * MPXY mbox instance per MPXY channel. This ties
+ * an MPXY channel with an RPMI Service group.
+ */
+struct mpxy_mbox {
+ struct mbox_chan *chan;
+ struct mpxy_mbox_data *mbox_data;
+ struct mpxy_rpmi_channel_attrs msgprot_attrs;
+ struct sbi_mpxy_channel channel;
+};
+
+/** Make sure all attributes are packed for direct memcpy */
+#define assert_field_offset(field, attr_offset) \
+ _Static_assert( \
+ ((offsetof(struct mpxy_rpmi_channel_attrs, field)) / \
+ sizeof(u32)) == (attr_offset - SBI_MPXY_ATTR_MSGPROTO_ATTR_START),\
+ "field " #field \
+ " from struct mpxy_rpmi_channel_attrs invalid offset, expected " #attr_offset)
+
+assert_field_offset(servicegrp_id, MPXY_MSGPROT_RPMI_ATTR_SERVICEGROUP_ID);
+
+/**
+ * Discover the RPMI service data using message_id
+ * MPXY message_id == RPMI service_id
+ */
+static struct rpmi_service_data *mpxy_find_rpmi_srvid(u32 message_id,
+ struct mpxy_mbox_data *mbox_data)
+{
+ int mid = 0;
+ struct rpmi_service_data *srv = mbox_data->priv_data;
+ for (mid = 0; srv[mid].id < mbox_data->num_services; mid++) {
+ if (srv[mid].id == (u8)message_id)
+ return &srv[mid];
+ }
+
+ return NULL;
+}
+
+/** Copy attributes word size */
+static void mpxy_copy_attrs(u32 *outmem, u32 *inmem, u32 count)
+{
+ u32 idx;
+ for (idx = 0; idx < count; idx++)
+ outmem[idx] = cpu_to_le32(inmem[idx]);
+}
+
+static int mpxy_mbox_read_attributes(struct sbi_mpxy_channel *channel,
+ u32 *outmem, u32 base_attr_id,
+ u32 attr_count)
+{
+ u32 end_id;
+ struct mpxy_mbox *rmb =
+ container_of(channel, struct mpxy_mbox, channel);
+
+ u32 *attr_array = (u32 *)&rmb->msgprot_attrs;
+
+ end_id = base_attr_id + attr_count - 1;
+
+ if (end_id >= MPXY_MSGPROT_RPMI_ATTR_MAX_ID)
+ return SBI_ERR_BAD_RANGE;
+
+ mpxy_copy_attrs(outmem, &attr_array[attr_id2index(base_attr_id)],
+ attr_count);
+
+ return SBI_SUCCESS;
+}
+
+/**
+ * Verify the channel standard attribute wrt to write permission
+ * and the value to be set if valid or not.
+ * Only attributes needs to be checked which are defined Read/Write
+ * permission. Other with Readonly permission will result in error.
+ *
+ * Attributes values to be written must also be checked because
+ * before writing a range of attributes, we need to make sure that
+ * either complete range of attributes is written successfully or not
+ * at all.
+ */
+static int mpxy_check_write_attr(u32 attr_id, u32 attr_val)
+{
+ int ret = SBI_SUCCESS;
+
+ switch(attr_id) {
+ /** All RO access attributes falls under default */
+ default:
+ ret = SBI_ERR_BAD_RANGE;
+ };
+
+ return ret;
+}
+
+static void mpxy_write_attr(struct mpxy_rpmi_channel_attrs *attrs,
+ u32 attr_id,
+ u32 attr_val)
+{
+ /* No writable attributes in RPMI */
+}
+
+static int mpxy_mbox_write_attributes(struct sbi_mpxy_channel *channel,
+ u32 *outmem, u32 base_attr_id,
+ u32 attr_count)
+{
+ int ret, mem_idx;
+ u32 end_id, attr_val, idx;
+ struct mpxy_mbox *rmb =
+ container_of(channel, struct mpxy_mbox, channel);
+
+ end_id = base_attr_id + attr_count - 1;
+
+ if (end_id >= MPXY_MSGPROT_RPMI_ATTR_MAX_ID)
+ return SBI_ERR_BAD_RANGE;
+
+ mem_idx = 0;
+ for (idx = base_attr_id; idx <= end_id; idx++) {
+ attr_val = le32_to_cpu(outmem[mem_idx++]);
+ ret = mpxy_check_write_attr(idx, attr_val);
+ if (ret)
+ return ret;
+ }
+
+ mem_idx = 0;
+ for (idx = base_attr_id; idx <= end_id; idx++) {
+ attr_val = le32_to_cpu(outmem[mem_idx++]);
+ mpxy_write_attr(&rmb->msgprot_attrs, idx, attr_val);
+ }
+
+ return SBI_SUCCESS;
+}
+
+static int mpxy_mbox_send_message(struct sbi_mpxy_channel *channel,
+ u32 message_id, void *tx, u32 tx_len,
+ void *rx, u32 rx_max_len,
+ unsigned long *ack_len)
+{
+ int ret;
+ u32 rx_len = 0;
+ struct mbox_xfer xfer;
+ struct rpmi_message_args args = {0};
+ struct mpxy_mbox *rmb =
+ container_of(channel, struct mpxy_mbox, channel);
+ struct rpmi_service_data *srv =
+ mpxy_find_rpmi_srvid(message_id, rmb->mbox_data);
+ if (!srv)
+ return SBI_EFAIL;
+
+ if (tx_len < srv->min_tx_len || tx_len > srv->max_tx_len)
+ return SBI_EFAIL;
+
+ if (ack_len) {
+ if (srv->min_rx_len == srv->max_rx_len)
+ rx_len = srv->min_rx_len;
+ else if (srv->max_rx_len < channel->attrs.msg_data_maxlen)
+ rx_len = srv->max_rx_len;
+ else
+ rx_len = channel->attrs.msg_data_maxlen;
+
+ args.type = RPMI_MSG_NORMAL_REQUEST;
+ args.flags = (rx) ? 0 : RPMI_MSG_FLAGS_NO_RX;
+ args.service_id = srv->id;
+ mbox_xfer_init_txrx(&xfer, &args,
+ tx, tx_len, RPMI_DEF_TX_TIMEOUT,
+ rx, rx_len, RPMI_DEF_TX_TIMEOUT);
+ }
+ else {
+ args.type = RPMI_MSG_POSTED_REQUEST;
+ args.flags = RPMI_MSG_FLAGS_NO_RX;
+ args.service_id = srv->id;
+ mbox_xfer_init_tx(&xfer, &args,
+ tx, tx_len, RPMI_DEF_TX_TIMEOUT);
+ }
+
+ ret = mbox_chan_xfer(rmb->chan, &xfer);
+ if (ret)
+ return (ret == SBI_ETIMEDOUT) ? SBI_ETIMEDOUT : SBI_EFAIL;
+
+ if (ack_len)
+ *ack_len = args.rx_data_len;
+
+ return SBI_OK;
+}
+
+static int mpxy_mbox_get_notifications(struct sbi_mpxy_channel *channel,
+ void *eventsbuf, u32 bufsize,
+ unsigned long *events_len)
+{
+ return SBI_ENOTSUPP;
+}
+
+static int mpxy_mbox_init(void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ int rc, len;
+ const fdt32_t *val;
+ u32 channel_id;
+ struct mpxy_mbox *rmb;
+ struct mbox_chan *chan;
+ const struct mpxy_mbox_data *data = match->data;
+
+ /* Allocate context for RPXY mbox client */
+ rmb = sbi_zalloc(sizeof(*rmb));
+ if (!rmb)
+ return SBI_ENOMEM;
+
+ /*
+ * If channel request failed then other end does not support
+ * service group so do nothing.
+ */
+ rc = fdt_mailbox_request_chan(fdt, nodeoff, 0, &chan);
+ if (rc) {
+ sbi_free(rmb);
+ return 0;
+ }
+
+ /* Match channel service group id */
+ if (data->servicegrp_id != chan->chan_args[0]) {
+ mbox_controller_free_chan(chan);
+ sbi_free(rmb);
+ return SBI_EINVAL;
+ }
+
+ val = fdt_getprop(fdt, nodeoff, "riscv,sbi-mpxy-channel-id", &len);
+ if (len > 0 && val)
+ channel_id = fdt32_to_cpu(*val);
+ else {
+ mbox_controller_free_chan(chan);
+ sbi_free(rmb);
+ return SBI_ENODEV;
+ }
+
+ /* Setup MPXY mbox client */
+ /* Channel ID*/
+ rmb->channel.channel_id = channel_id;
+ /* Callback for read RPMI attributes */
+ rmb->channel.read_attributes = mpxy_mbox_read_attributes;
+ /* Callback for write RPMI attributes */
+ rmb->channel.write_attributes = mpxy_mbox_write_attributes;
+ /* Callback for sending RPMI message */
+ rmb->channel.send_message = mpxy_mbox_send_message;
+ /* Callback to get RPMI notifications */
+ rmb->channel.get_notification_events = mpxy_mbox_get_notifications;
+
+ /* No callback to switch events state data */
+ rmb->channel.switch_eventsstate = NULL;
+
+ /* RPMI Message Protocol ID */
+ rmb->channel.attrs.msg_proto_id = SBI_MPXY_MSGPROTO_RPMI_ID;
+ /* RPMI Message Protocol Version */
+ rmb->channel.attrs.msg_proto_version =
+ SBI_MPXY_MSGPROTO_VERSION(RPMI_MAJOR_VER, RPMI_MINOR_VER);
+
+ /* RPMI supported max message data length(bytes), same for
+ * all service groups */
+ rmb->channel.attrs.msg_data_maxlen =
+ RPMI_MSG_DATA_SIZE(RPMI_SLOT_SIZE_MIN);
+ /* RPMI message send timeout(milliseconds)
+ * same for all service groups */
+ rmb->channel.attrs.msg_send_timeout = RPMI_MSG_SEND_TIMEOUT;
+
+ /* RPMI message protocol attribute: service group id */
+ rmb->msgprot_attrs.servicegrp_id = data->servicegrp_id;
+
+ rmb->mbox_data = (struct mpxy_mbox_data *)data;
+ rmb->chan = chan;
+
+ /* Register RPXY service group */
+ rc = sbi_mpxy_register_channel(&rmb->channel);
+ if (rc) {
+ mbox_controller_free_chan(chan);
+ sbi_free(rmb);
+ return rc;
+ }
+
+ return SBI_OK;
+}
+
+static struct rpmi_service_data clock_services[] = {
+{
+ .id = RPMI_CLOCK_SRV_ENABLE_NOTIFICATION,
+ .min_tx_len = sizeof(struct rpmi_enable_notification_req),
+ .max_tx_len = sizeof(struct rpmi_enable_notification_req),
+ .min_rx_len = sizeof(struct rpmi_enable_notification_resp),
+ .max_rx_len = sizeof(struct rpmi_enable_notification_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_GET_NUM_CLOCKS,
+ .min_tx_len = 0,
+ .max_tx_len = 0,
+ .min_rx_len = sizeof(struct rpmi_clock_get_num_clocks_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_get_num_clocks_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_GET_ATTRIBUTES,
+ .min_tx_len = sizeof(struct rpmi_clock_get_attributes_req),
+ .max_tx_len = sizeof(struct rpmi_clock_get_attributes_req),
+ .min_rx_len = sizeof(struct rpmi_clock_get_attributes_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_get_attributes_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_GET_SUPPORTED_RATES,
+ .min_tx_len = sizeof(struct rpmi_clock_get_supported_rates_req),
+ .max_tx_len = sizeof(struct rpmi_clock_get_supported_rates_req),
+ .min_rx_len = sizeof(struct rpmi_clock_get_supported_rates_resp),
+ .max_rx_len = -1U,
+},
+{
+ .id = RPMI_CLOCK_SRV_SET_CONFIG,
+ .min_tx_len = sizeof(struct rpmi_clock_set_config_req),
+ .max_tx_len = sizeof(struct rpmi_clock_set_config_req),
+ .min_rx_len = sizeof(struct rpmi_clock_set_config_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_set_config_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_GET_CONFIG,
+ .min_tx_len = sizeof(struct rpmi_clock_get_config_req),
+ .max_tx_len = sizeof(struct rpmi_clock_get_config_req),
+ .min_rx_len = sizeof(struct rpmi_clock_get_config_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_get_config_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_SET_RATE,
+ .min_tx_len = sizeof(struct rpmi_clock_set_rate_req),
+ .max_tx_len = sizeof(struct rpmi_clock_set_rate_req),
+ .min_rx_len = sizeof(struct rpmi_clock_set_rate_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_set_rate_resp),
+},
+{
+ .id = RPMI_CLOCK_SRV_GET_RATE,
+ .min_tx_len = sizeof(struct rpmi_clock_get_rate_req),
+ .max_tx_len = sizeof(struct rpmi_clock_get_rate_req),
+ .min_rx_len = sizeof(struct rpmi_clock_get_rate_resp),
+ .max_rx_len = sizeof(struct rpmi_clock_get_rate_resp),
+},
+};
+
+static struct mpxy_mbox_data clock_data = {
+ .servicegrp_id = RPMI_SRVGRP_CLOCK,
+ .num_services = RPMI_CLOCK_SRV_MAX_COUNT,
+ .notifications_support = 1,
+ .priv_data = clock_services,
+};
+
+static const struct fdt_match mpxy_mbox_match[] = {
+ { .compatible = "riscv,rpmi-mpxy-clk", .data = &clock_data },
+ { },
+};
+
+struct fdt_mpxy fdt_mpxy_rpmi_mbox = {
+ .match_table = mpxy_mbox_match,
+ .init = mpxy_mbox_init,
+};
diff --git a/lib/utils/mpxy/objects.mk b/lib/utils/mpxy/objects.mk
index 43e73c94..ccb28b55 100644
--- a/lib/utils/mpxy/objects.mk
+++ b/lib/utils/mpxy/objects.mk
@@ -9,3 +9,6 @@
libsbiutils-objs-$(CONFIG_FDT_MPXY) += mpxy/fdt_mpxy.o
libsbiutils-objs-$(CONFIG_FDT_MPXY) += mpxy/fdt_mpxy_drivers.carray.o
+
+carray-fdt_mpxy_drivers-$(CONFIG_FDT_MPXY_RPMI_MBOX) += fdt_mpxy_rpmi_mbox
+libsbiutils-objs-$(CONFIG_FDT_MPXY_RPMI_MBOX) += mpxy/fdt_mpxy_rpmi_mbox.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 1f0880ee..e23b38b2 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -54,3 +54,4 @@ CONFIG_FDT_TIMER=y
CONFIG_FDT_TIMER_MTIMER=y
CONFIG_FDT_TIMER_PLMT=y
CONFIG_FDT_MPXY=y
+CONFIG_FDT_MPXY_RPMI_MBOX=y
--
2.34.1
More information about the opensbi
mailing list