[PATCH 1/3] riscv: Add SBI MPXY generic interface

Rahul Pathak rahul.pathak at oss.qualcomm.com
Thu Jul 9 19:56:02 CEST 2026


SBI MPXY interface implements the SBI MPXY
extension to interact with supported message
protocols for platform management and control

Signed-off-by: Rahul Pathak <rahul.pathak at oss.qualcomm.com>
---
 arch/riscv/Kconfig           |   9 +
 arch/riscv/include/asm/sbi.h | 268 ++++++++++++++++++
 arch/riscv/lib/Makefile      |   1 +
 arch/riscv/lib/sbi_mpxy.c    | 529 +++++++++++++++++++++++++++++++++++
 4 files changed, 807 insertions(+)
 create mode 100644 arch/riscv/lib/sbi_mpxy.c

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 265b5320777..e0efcd19ce5 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -473,6 +473,15 @@ config SBI_IPI
 	default y if RISCV_SMODE || SPL_RISCV_SMODE
 	depends on SMP
 
+config SBI_MPXY
+	bool "SBI MPXY generic core"
+	depends on RISCV_SMODE || SPL_RISCV_SMODE
+	help
+	  Enable the generic SBI MPXY (Message Proxy Extension) core.
+	  Provides channel discovery, shared memory setup, attribute
+	  caching, and protocol driver registration. Required by
+	  CMD_MPXY and any driver that communicates via MPXY channels.
+
 config XIP
 	bool "XIP mode"
 	help
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 47124dbaac8..d98c98bf4f4 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 <compiler.h>
 
 enum sbi_ext_id {
 	SBI_EXT_0_1_SET_TIMER = 0x0,
@@ -104,6 +105,36 @@ enum sbi_ext_dbcn_fid {
 	SBI_EXT_DBCN_CONSOLE_WRITE_BYTE,
 };
 
+/* MPXY Standard Channel Attribute IDs */
+enum sbi_mpxy_attr_id {
+	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,
+	SBI_MPXY_ATTR_MSGPROTO_ATTR_START	= 0x80000000,
+	SBI_MPXY_ATTR_MSGPROTO_ATTR_END		= 0xffffffff,
+};
+
+/* MPXY Function IDs */
+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,
+};
+
 #ifdef CONFIG_SBI_V01
 #define SBI_EXT_SET_TIMER		SBI_EXT_0_1_SET_TIMER
 #define SBI_FID_SET_TIMER		0
@@ -128,6 +159,8 @@ enum sbi_ext_dbcn_fid {
 #define SBI_FID_REMOTE_SFENCE_VMA_ASID	SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID
 #endif
 
+/* SBI spec version fields */
+#define SBI_SPEC_VERSION_DEFAULT	0x1
 #define SBI_SPEC_VERSION_MAJOR_SHIFT	24
 #define SBI_SPEC_VERSION_MAJOR_MASK	0x7f
 #define SBI_SPEC_VERSION_MINOR_MASK	0xffffff
@@ -140,11 +173,65 @@ enum sbi_ext_dbcn_fid {
 #define SBI_ERR_DENIED			-4
 #define SBI_ERR_INVALID_ADDRESS		-5
 
+/* SBI MPXY extension defines */
+#define SBI_MPXY_MAX_CHANNELS		16
+#define SBI_MPXY_MAX_MSG_WORDS		64
+#define SBI_MPXY_SHMEM_ALIGN		4096
+#define SBI_MPXY_MSGPROTO_RPMI_ID	0x0
+
 struct sbiret {
 	long error;
 	long value;
 };
 
+struct mpxy_channel;
+
+/**
+ * struct mpxy_proto_ops - Message protocol driver operations
+ *
+ * @proto_id:	Protocol identifier
+ * @name:	Protocol name
+ * @readattr:	Read a protocol-specific attribute (attr_id >= 0x80000000)
+ * @writeattr:	Write a protocol-specific attribute
+ * @send:	Send a message and receive a response
+ */
+struct mpxy_proto_ops {
+	u32		 proto_id;
+	const char	*name;
+
+	int (*readattr)(struct mpxy_channel *chan, u32 attr_id, u32 *attr_val);
+	int (*writeattr)(struct mpxy_channel *chan, u32 attr_id, u32 attr_val);
+};
+
+/* Standard channel attributes as returned by firmware */
+struct sbi_mpxy_channel_attrs {
+	u32 msg_proto_id;
+	u32 msg_proto_version;
+	u32 msg_max_len;
+	u32 msg_send_timeout;
+	u32 msg_completion_timeout;
+	u32 capability;
+	u32 sse_event_id;
+	u32 msi_control;
+	u32 msi_addr_lo;
+	u32 msi_addr_hi;
+	u32 msi_data;
+	u32 events_state_ctrl;
+};
+
+/*
+ * struct mpxy_channel - Represents a single MPXY channel
+ *
+ * @channel_id:	MPXY Channel ID
+ * @std_attrs:	MPXY channel standard attributes
+ * @proto:	Pointer to the protocol driver instance
+ */
+struct mpxy_channel {
+	u32				 channel_id;
+	struct sbi_mpxy_channel_attrs	 std_attrs;
+	const struct mpxy_proto_ops	*proto;
+};
+
 struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
 			unsigned long arg1, unsigned long arg2,
 			unsigned long arg3, unsigned long arg4,
@@ -165,6 +252,26 @@ void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
 				unsigned long size,
 				unsigned long asid);
 #endif
+
+/* Get the major version of SBI */
+static inline unsigned long sbi_major_version(long version)
+{
+	return (version >> SBI_SPEC_VERSION_MAJOR_SHIFT) &
+		SBI_SPEC_VERSION_MAJOR_MASK;
+}
+
+/* Get the minor version of SBI */
+static inline unsigned long sbi_minor_version(long version)
+{
+	return (version & SBI_SPEC_VERSION_MINOR_MASK);
+}
+
+/* Check if current SBI specification version is 0.1 or not */
+static inline int sbi_spec_is_0_1(long version)
+{
+	return (version == SBI_SPEC_VERSION_DEFAULT) ? 1 : 0;
+}
+
 void sbi_set_timer(uint64_t stime_value);
 long sbi_get_spec_version(void);
 int sbi_get_impl_id(void);
@@ -176,4 +283,165 @@ int sbi_get_mimpid(long *mimpid);
 void sbi_srst_reset(unsigned long type, unsigned long reason);
 int sbi_dbcn_write_byte(unsigned char ch);
 
+/**
+ * sbi_set_timer() - Program the next timer event
+ * @stime_value:	Absolute time value at which the timer should fire
+ */
+void sbi_set_timer(uint64_t stime_value);
+
+/**
+ * sbi_get_spec_version() - Get the SBI specification version implemented
+ *			    by the running SBI implementation
+ */
+long sbi_get_spec_version(void);
+
+/**
+ * sbi_get_impl_id() - Get the SBI implementation ID
+ */
+int sbi_get_impl_id(void);
+
+/**
+ * sbi_get_impl_version() - Get the SBI implementation version
+ * @version:	Output pointer to store the implementation version
+ */
+int sbi_get_impl_version(long *version);
+
+/**
+ * sbi_probe_extension() - Check whether an SBI extension is available
+ * @ext:	SBI extension ID to probe
+ */
+int sbi_probe_extension(int ext);
+
+/**
+ * sbi_get_mvendorid() - Get the value of the mvendorid CSR
+ * @mvendorid:	Output pointer to store the mvendorid value
+ */
+int sbi_get_mvendorid(long *mvendorid);
+
+/**
+ * sbi_get_marchid() - Get the value of the marchid CSR
+ * @marchid:	Output pointer to store the marchid value
+ */
+int sbi_get_marchid(long *marchid);
+
+/**
+ * sbi_get_mimpid() - Get the value of the mimpid CSR
+ * @mimpid:	Output pointer to store the mimpid value
+ */
+int sbi_get_mimpid(long *mimpid);
+
+/**
+ * sbi_srst_reset() - Request a system reset via the SBI SRST extension
+ * @type:	Reset type (e.g. shutdown, cold reboot, warm reboot)
+ * @reason:	Reset reason code
+ */
+void sbi_srst_reset(unsigned long type, unsigned long reason);
+
+/**
+ * sbi_dbcn_write_byte() - Write a single byte via the SBI Debug Console
+ *			   extension
+ * @ch:		Byte to write
+ */
+int sbi_dbcn_write_byte(unsigned char ch);
+
+/**
+ * sbi_mpxy_register_proto() - Register a message protocol driver
+ * @ops:	Protocol ops to register
+ */
+int sbi_mpxy_register_proto(const struct mpxy_proto_ops *ops);
+
+/**
+ * sbi_mpxy_probe() - Probe MPXY extension, set up shmem, scan channels
+ */
+int sbi_mpxy_probe(void);
+
+/**
+ * sbi_mpxy_is_available() - Check whether MPXY was successfully probed
+ */
+bool sbi_mpxy_is_available(void);
+
+/**
+ * sbi_mpxy_shmem_ptr() - Return pointer to the shared memory window
+ */
+void *sbi_mpxy_shmem_ptr(void);
+
+/**
+ * sbi_mpxy_shmem_size() - Return size of shared memory in bytes
+ */
+u32 sbi_mpxy_shmem_size(void);
+
+/**
+ * sbi_mpxy_channel_count() - Return number of discovered channels
+ */
+int sbi_mpxy_channel_count(void);
+
+/**
+ * sbi_mpxy_get_channel() - Get channel by index
+ * @channel_id:	Index in [0, sbi_mpxy_channel_count())
+ */
+struct mpxy_channel *sbi_mpxy_get_channel(int channel_id);
+
+/**
+ * sbi_mpxy_find_channel() - Find channel by channel ID
+ * @channel_id:	Channel ID to search for
+ */
+struct mpxy_channel *sbi_mpxy_find_channel(u32 channel_id);
+
+/**
+ * sbi_mpxy_read_attrs() - Read a contiguous range of channel attributes
+ * @chan:		Target channel
+ * @base_attr_id:	First attribute ID in the range
+ * @attr_count:		Number of attributes to read
+ * @attrs_buf:		Output buffer (attr_count u32 words)
+ */
+int sbi_mpxy_read_attrs(struct mpxy_channel *chan, u32 base_attr_id,
+			u32 attr_count, u32 *attrs_buf);
+
+/**
+ * sbi_mpxy_write_attrs() - Write a contiguous range of channel attributes
+ *
+ * @chan:		Target channel
+ * @base_attr_id:	First attribute ID in the range
+ * @attr_count:		Number of attributes to write
+ * @attrs_buf:		Input buffer (attr_count u32 words)
+ */
+int sbi_mpxy_write_attrs(struct mpxy_channel *chan, u32 base_attr_id,
+			 u32 attr_count, u32 *attrs_buf);
+
+/**
+ * sbi_mpxy_send_message_with_resp() - Send a message and receive a response
+ * @chan:	Target channel
+ * @msg_id:	Message ID
+ * @tx:		TX payload buffer (copied into shmem before ecall)
+ * @tx_len:	TX payload length in bytes
+ * @rx:		RX buffer (copied from shmem after ecall)
+ * @max_rx_len:	RX buffer capacity in bytes
+ * @rx_len:	Output: actual response length in bytes (from sret.value)
+ */
+int sbi_mpxy_send_message_with_resp(struct mpxy_channel *chan, u32 msg_id,
+				    void *tx, unsigned long tx_len,
+				    void *rx, unsigned long max_rx_len,
+				    unsigned long *rx_len);
+
+/**
+ * sbi_mpxy_send_message_without_resp() - Send a message with no response
+ * @chan:	Target channel
+ * @msg_id:	Message ID
+ * @tx:		TX payload buffer
+ * @tx_len:	TX payload length in bytes
+ */
+int sbi_mpxy_send_message_without_resp(struct mpxy_channel *chan, u32 msg_id,
+				       void *tx, unsigned long tx_len);
+
+/**
+ * sbi_mpxy_send() - Word-oriented send used by the mpxy command
+ * @chan:	Target channel
+ * @tx:		tx[0]=msg_id, tx[1..n-1]=payload words
+ * @tx_words:	Total words including msg_id
+ * @rx:		Receive buffer
+ * @rx_words:	In: capacity; Out: words received
+ */
+int sbi_mpxy_send(struct mpxy_channel *chan, u32 *tx, int tx_words,
+		  u32 *rx, int *rx_words);
+
 #endif
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index f1f50918eff..32f8fb5dbbe 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ANDES_PLICSW) += andes_plicsw.o
 else
 obj-$(CONFIG_SBI) += sbi.o
 obj-$(CONFIG_SBI_IPI) += sbi_ipi.o
+obj-$(CONFIG_SBI_MPXY) += sbi_mpxy.o
 endif
 obj-y	+= interrupts.o
 ifeq ($(CONFIG_$(PHASE_)SYSRESET),)
diff --git a/arch/riscv/lib/sbi_mpxy.c b/arch/riscv/lib/sbi_mpxy.c
new file mode 100644
index 00000000000..f95cedaabac
--- /dev/null
+++ b/arch/riscv/lib/sbi_mpxy.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SBI MPXY (Message Proxy) Generic Library
+ *
+ * Copyright (c) 2026, Rahul Pathak <rahul.pathak at oss.qualcomm.com>
+ */
+
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/sbi.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+/** Maximum number of registered protocol drivers */
+#define MPXY_MAX_PROTOS		8
+
+/**
+ * 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[];
+};
+
+/**
+ * MPXY Context
+ */
+struct mpxy_context {
+	phys_addr_t		 shmem_addr;
+	u32			 shmem_size;
+	struct mpxy_channel	 channels[SBI_MPXY_MAX_CHANNELS];
+	int			 channel_count;
+};
+
+static struct mpxy_context mpxy_ctx;
+static int mpxy_proto_count;
+static const struct mpxy_proto_ops *mpxy_protos[MPXY_MAX_PROTOS];
+
+static bool mpxy_probe_done;
+static bool mpxy_available;
+
+/**
+ * Protocol registration
+ */
+int sbi_mpxy_register_proto(const struct mpxy_proto_ops *ops)
+{
+	if (!ops)
+		return -EINVAL;
+
+	if (mpxy_proto_count >= MPXY_MAX_PROTOS) {
+		log_err("sbi_mpxy: protocol table full, cannot register %s\n",
+			ops->name ? ops->name : "unknown");
+		return -ENOSPC;
+	}
+
+	mpxy_protos[mpxy_proto_count++] = ops;
+	return 0;
+}
+
+/**
+ * Return MPXY shared memory base pointer
+ */
+static inline void *shmem_base(void)
+{
+	return (void *)(uintptr_t)mpxy_ctx.shmem_addr;
+}
+
+/**
+ * Probe MPXY extension
+ */
+static int mpxy_check_extension(void)
+{
+	long ret;
+
+	ret = sbi_get_spec_version();
+	if (ret < 0 || sbi_spec_is_0_1(ret)) {
+		log_err("sbi_mpxy: SBI v0.2+ not detected\n");
+		return -ENOTSUPP;
+	}
+
+	ret = sbi_probe_extension(SBI_EXT_MPXY);
+	if (ret <= 0) {
+		log_err("sbi_mpxy: MPXY extension not available\n");
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * Setup the MPXY shared memory
+ */
+static int mpxy_setup_shmem(void)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_SHMEM_SIZE,
+			0, 0, 0, 0, 0, 0);
+	if (ret.error) {
+		log_err("sbi_mpxy: GET_SHMEM_SIZE failed (%ld)\n", ret.error);
+		return -EINVAL;
+	}
+
+	mpxy_ctx.shmem_size = ret.value;
+
+	mpxy_ctx.shmem_addr = (phys_addr_t)(uintptr_t)memalign(SBI_MPXY_SHMEM_ALIGN,
+								mpxy_ctx.shmem_size);
+	if (!mpxy_ctx.shmem_addr) {
+		log_err("sbi_mpxy: failed to allocate %u bytes for shmem\n",
+			mpxy_ctx.shmem_size);
+		return -ENOMEM;
+	}
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM,
+			mpxy_ctx.shmem_addr, 0, 0, 0, 0, 0);
+	if (ret.error) {
+		log_err("sbi_mpxy: SET_SHMEM failed (%ld)\n", ret.error);
+		free((void *)(uintptr_t)mpxy_ctx.shmem_addr);
+		mpxy_ctx.shmem_addr = 0;
+		return -EINVAL;
+	}
+
+	log_debug("sbi_mpxy: shmem at 0x%llx, size %u\n",
+		  (unsigned long long)mpxy_ctx.shmem_addr,
+		  mpxy_ctx.shmem_size);
+
+	return 0;
+}
+
+/**
+ * Get MPXY channels present in the system
+ */
+static int mpxy_scan_channels(void)
+{
+	struct sbi_mpxy_channel_ids_data *sdata = shmem_base();
+	u32 start = 0, total = 0;
+	u32 remaining, returned;
+	struct sbiret ret;
+	int i;
+
+	do {
+		ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_CHANNEL_IDS,
+				start, 0, 0, 0, 0, 0);
+		if (ret.error) {
+			log_err("sbi_mpxy: GET_CHANNEL_IDS failed (%ld)\n",
+				ret.error);
+			return -EINVAL;
+		}
+
+		remaining = le32_to_cpu(sdata->remaining);
+		returned  = le32_to_cpu(sdata->returned);
+
+		for (i = 0; i < returned; i++) {
+			if (total >= SBI_MPXY_MAX_CHANNELS) {
+				log_warning("sbi_mpxy: channel table full, "
+					    "truncating at %d\n",
+					    SBI_MPXY_MAX_CHANNELS);
+				goto done;
+			}
+			mpxy_ctx.channels[total].channel_id =
+				le32_to_cpu(sdata->channel_array[i]);
+			total++;
+		}
+
+		start += returned;
+
+	} while (remaining);
+
+done:
+	mpxy_ctx.channel_count = total;
+	log_debug("sbi_mpxy: found %d channel(s)\n", total);
+	return 0;
+}
+
+
+static void mpxy_bind_protocols(void)
+{
+	int i, j;
+
+	for (i = 0; i < mpxy_ctx.channel_count; i++) {
+		struct mpxy_channel *chan = &mpxy_ctx.channels[i];
+
+		chan->proto = NULL;
+		for (j = 0; j < mpxy_proto_count; j++) {
+			if (mpxy_protos[j]->proto_id ==
+			    chan->std_attrs.msg_proto_id) {
+				chan->proto = mpxy_protos[j];
+				break;
+			}
+		}
+
+		if (!chan->proto)
+			log_warning("sbi_mpxy: no driver for channel 0x%x "
+				    "protocol 0x%x\n",
+				    chan->channel_id,
+				    chan->std_attrs.msg_proto_id);
+		else
+			log_debug("sbi_mpxy: channel 0x%x bound to %s\n",
+				  chan->channel_id, chan->proto->name);
+	}
+}
+
+int sbi_mpxy_probe(void)
+{
+	int i, ret;
+
+	if (mpxy_probe_done)
+		return mpxy_available ? 0 : -ENOTSUPP;
+
+	mpxy_probe_done = true;
+
+	ret = mpxy_check_extension();
+	if (ret)
+		goto fail;
+
+	ret = mpxy_setup_shmem();
+	if (ret)
+		goto fail;
+
+	ret = mpxy_scan_channels();
+	if (ret)
+		goto fail;
+
+	for (i = 0; i < mpxy_ctx.channel_count; i++) {
+		ret = sbi_mpxy_read_attrs(&mpxy_ctx.channels[i], 0,
+					  sizeof(struct sbi_mpxy_channel_attrs) /
+					  sizeof(u32),
+					  (u32 *)&mpxy_ctx.channels[i].std_attrs);
+		if (ret)
+			goto fail;
+	}
+
+	mpxy_bind_protocols();
+
+	mpxy_available = true;
+	return 0;
+
+fail:
+	mpxy_available = false;
+	return ret;
+}
+
+bool sbi_mpxy_is_available(void)
+{
+	return mpxy_available;
+}
+
+int sbi_mpxy_channel_count(void)
+{
+	if (!sbi_mpxy_is_available())
+		return 0;
+
+	return mpxy_ctx.channel_count;
+}
+
+struct mpxy_channel *sbi_mpxy_get_channel(int channel_id)
+{
+	if (channel_id < 0 || channel_id >= mpxy_ctx.channel_count)
+		return NULL;
+	return &mpxy_ctx.channels[channel_id];
+}
+
+struct mpxy_channel *sbi_mpxy_find_channel(u32 channel_id)
+{
+	int i;
+
+	for (i = 0; i < mpxy_ctx.channel_count; i++) {
+		if (mpxy_ctx.channels[i].channel_id == channel_id)
+			return &mpxy_ctx.channels[i];
+	}
+	return NULL;
+}
+
+/**
+ * Return pointer to the MPXY shared memory
+ */
+void *sbi_mpxy_shmem_ptr(void)
+{
+	if (!sbi_mpxy_is_available())
+		return NULL;
+	return shmem_base();
+}
+
+/**
+ * Get the MPXY shared memory size
+ */
+u32 sbi_mpxy_shmem_size(void)
+{
+	if (!sbi_mpxy_is_available())
+		return 0;
+
+	return mpxy_ctx.shmem_size;
+}
+
+/**
+ * sbi_mpxy_read_attrs() - Read a contiguous range of channel attributes
+ *
+ * @chan:		Target channel
+ * @base_attr_id:	First attribute ID in the range
+ * @attr_count:		Number of attributes to read
+ * @attrs_buf:		Output buffer (attr_count u32 words)
+ */
+int sbi_mpxy_read_attrs(struct mpxy_channel *chan, u32 base_attr_id,
+			u32 attr_count, u32 *attrs_buf)
+{
+	struct sbiret ret;
+	__le32 *shmem;
+	u32 end_id;
+	int i;
+
+	if (!chan || !attr_count || !attrs_buf)
+		return -EINVAL;
+
+	if (attr_count > mpxy_ctx.shmem_size / sizeof(u32))
+		return -EINVAL;
+
+	shmem = (__le32 *)shmem_base();
+	if (!shmem)
+		return -ENXIO;
+
+	end_id = base_attr_id + attr_count - 1;
+
+	if (base_attr_id < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) {
+		/* Standard attributes */
+		if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX)
+			return -EINVAL;
+
+		ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS,
+				chan->channel_id, base_attr_id, attr_count,
+				0, 0, 0);
+		if (ret.error)
+			return -EINVAL;
+
+		for (i = 0; i < attr_count; i++)
+			attrs_buf[i] = le32_to_cpu(shmem[i]);
+
+		return 0;
+	}
+
+	if (base_attr_id >= SBI_MPXY_ATTR_MSGPROTO_ATTR_START &&
+	    end_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_END) {
+		/* Protocol-specific attributes */
+		if (!chan->proto || !chan->proto->readattr)
+			return -ENOSYS;
+
+		ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS,
+				chan->channel_id, base_attr_id, attr_count,
+				0, 0, 0);
+		if (ret.error)
+			return -EINVAL;
+
+		for (i = 0; i < attr_count; i++)
+			attrs_buf[i] = le32_to_cpu(shmem[i]);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * sbi_mpxy_write_attrs() - Write a contiguous range of channel attributes
+ *
+ * @chan:		Target channel
+ * @base_attr_id:	First attribute ID in the range
+ * @attr_count:		Number of attributes to write
+ * @attrs_buf:		Input buffer (attr_count u32 words)
+ */
+int sbi_mpxy_write_attrs(struct mpxy_channel *chan, u32 base_attr_id,
+			 u32 attr_count, u32 *attrs_buf)
+{
+	struct sbiret ret;
+	__le32 *shmem;
+	u32 end_id;
+	int i;
+
+	if (!chan || !attr_count || !attrs_buf)
+		return -EINVAL;
+
+	if (attr_count > mpxy_ctx.shmem_size / sizeof(u32))
+		return -EINVAL;
+
+	shmem = (__le32 *)shmem_base();
+	if (!shmem)
+		return -ENXIO;
+
+	end_id = base_attr_id + attr_count - 1;
+
+	if (base_attr_id < SBI_MPXY_ATTR_STD_ATTR_MAX_IDX) {
+		/* Standard attributes */
+		if (end_id >= SBI_MPXY_ATTR_STD_ATTR_MAX_IDX)
+			return -EINVAL;
+
+		for (i = 0; i < attr_count; i++)
+			shmem[i] = cpu_to_le32(attrs_buf[i]);
+
+		ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_WRITE_ATTRS,
+				chan->channel_id, base_attr_id, attr_count,
+				0, 0, 0);
+		return ret.error ? -EINVAL : 0;
+	}
+
+	if (base_attr_id >= SBI_MPXY_ATTR_MSGPROTO_ATTR_START &&
+	    end_id < SBI_MPXY_ATTR_MSGPROTO_ATTR_END) {
+		/* Protocol-specific attributes */
+		if (!chan->proto || !chan->proto->writeattr)
+			return -ENOSYS;
+
+		for (i = 0; i < attr_count; i++) {
+			ret.error = chan->proto->writeattr(chan,
+							   base_attr_id + i,
+							   attrs_buf[i]);
+			if (ret.error)
+				return ret.error;
+		}
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * sbi_mpxy_send_message_with_resp() - Send a message and receive a response
+ */
+int sbi_mpxy_send_message_with_resp(struct mpxy_channel *chan, u32 msg_id,
+				    void *tx, unsigned long tx_len,
+				    void *rx, unsigned long max_rx_len,
+				    unsigned long *rx_len)
+{
+	unsigned long rx_bytes;
+	struct sbiret ret;
+	void *shmem;
+
+	if (!chan)
+		return -EINVAL;
+	if (!tx && tx_len)
+		return -EINVAL;
+
+	shmem = sbi_mpxy_shmem_ptr();
+	if (!shmem)
+		return -ENXIO;
+
+	if (tx_len)
+		memcpy(shmem, tx, tx_len);
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SEND_MSG_WITH_RESP,
+			chan->channel_id, msg_id, tx_len, 0, 0, 0);
+	if (ret.error)
+		return -EINVAL;
+
+	if (rx) {
+		rx_bytes = ret.value;
+		if (rx_bytes > max_rx_len)
+			return -ENOSPC;
+		memcpy(rx, shmem, rx_bytes);
+		if (rx_len)
+			*rx_len = rx_bytes;
+	}
+
+	return 0;
+}
+
+/**
+ * sbi_mpxy_send_message_without_resp() - Send a message with no response
+ */
+int sbi_mpxy_send_message_without_resp(struct mpxy_channel *chan, u32 msg_id,
+				       void *tx, unsigned long tx_len)
+{
+	struct sbiret ret;
+	void *shmem;
+
+	if (!chan)
+		return -EINVAL;
+	if (!tx && tx_len)
+		return -EINVAL;
+
+	shmem = sbi_mpxy_shmem_ptr();
+	if (!shmem)
+		return -ENXIO;
+
+	if (tx_len)
+		memcpy(shmem, tx, tx_len);
+
+	ret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP,
+			chan->channel_id, msg_id, tx_len, 0, 0, 0);
+
+	return ret.error ? -EINVAL : 0;
+}
+
+/**
+ * sbi_mpxy_send() - Word-oriented send used by the mpxy command
+ *
+ * tx[0] = msg_id, tx[1..n-1] = payload words.
+ * Wraps sbi_mpxy_send_message_with_resp().
+ */
+int sbi_mpxy_send(struct mpxy_channel *chan, u32 *tx, int tx_words,
+		  u32 *rx, int *rx_words)
+{
+	unsigned long rx_len = 0;
+	unsigned long tx_len;
+	u32 msg_id;
+	int ret;
+
+	if (!chan || !tx || !rx || !rx_words)
+		return -EINVAL;
+
+	if (tx_words < 1)
+		return -EINVAL;
+
+	msg_id = tx[0];
+	tx_len = (tx_words - 1) * sizeof(u32);
+
+	ret = sbi_mpxy_send_message_with_resp(chan, msg_id,
+					      tx_words > 1 ? &tx[1] : NULL,
+					      tx_len,
+					      rx,
+					      (unsigned long)(*rx_words) * sizeof(u32),
+					      &rx_len);
+	if (ret)
+		return ret;
+
+	*rx_words = (int)(rx_len / sizeof(u32));
+	return 0;
+}
-- 
2.43.0



More information about the U-Boot mailing list