[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