diff mbox series

[16/16] lib: utils/mpxy: Add RPMI client driver for MPXY

Message ID 20240806073338.1856901-17-apatel@ventanamicro.com
State New
Headers show
Series RPMI and SBI MPXY support for OpenSBI | expand

Commit Message

Anup Patel Aug. 6, 2024, 7:33 a.m. UTC
From: Rahul Pathak <rpathak@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@ventanamicro.com>
Co-developed-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Anup Patel <apatel@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 mbox series

Patch

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@ventanamicro.com>
+ *   Anup Patel <apatel@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