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

Signed-off-by: Rahul Pathak <[email protected]>
---
 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 <[email protected]>
+ */
+
+#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

Reply via email to