diff --git a/drivers/mbox/CMakeLists.txt b/drivers/mbox/CMakeLists.txt
index e339c971ec8..5e4f61b9d03 100644
--- a/drivers/mbox/CMakeLists.txt
+++ b/drivers/mbox/CMakeLists.txt
@@ -21,3 +21,4 @@ zephyr_library_sources_ifdef(CONFIG_MBOX_STM32_HSEM mbox_stm32_hsem.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_IVSHMEM mbox_ivshmem.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_TI_OMAP_MAILBOX mbox_ti_omap.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_RENESAS_RZ_MHU mbox_renesas_rz_mhu.c)
+zephyr_library_sources_ifdef(CONFIG_MBOX_MHUV3 mbox_mhuv3.c)
diff --git a/drivers/mbox/Kconfig b/drivers/mbox/Kconfig
index df2d8f0794f..fe456b0b89e 100644
--- a/drivers/mbox/Kconfig
+++ b/drivers/mbox/Kconfig
@@ -25,6 +25,7 @@ source "drivers/mbox/Kconfig.esp32"
source "drivers/mbox/Kconfig.ivshmem"
source "drivers/mbox/Kconfig.ti_omap"
source "drivers/mbox/Kconfig.renesas_rz"
+source "drivers/mbox/Kconfig.mhuv3"
config MBOX_INIT_PRIORITY
diff --git a/drivers/mbox/Kconfig.mhuv3 b/drivers/mbox/Kconfig.mhuv3
new file mode 100644
index 00000000000..6e28dae1f2f
--- /dev/null
+++ b/drivers/mbox/Kconfig.mhuv3
@@ -0,0 +1,27 @@
+#
+# SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its
+# affiliates
+#
+# SPDX-License-Identifier: Apache-2.0
+
+config MBOX_MHUV3
+ bool "ARM MHUv3 mailbox driver"
+ depends on DT_HAS_ARM_MHUV3_ENABLED
+ default y
+ help
+ Driver for Arm MHUv3 (Message Handling Unit v3)
+
+if MBOX_MHUV3
+
+config MBOX_MHUV3_NUM_DBCH
+ int "ARM MHUv3 mailbox doorbell number of channels"
+ default 0
+ help
+ Maximum number of doorbell channels allowed in the Postbox/Mailbox.
+ This number has to be less than or equal to (NUM_DBCH + 1) in PBX_DBCH_CFG0/MBX_DBCH_CFG0 registers.
+ If the doorbell extension is supported then this number has to be bigger than zero.
+ MHUv3 allows up to 128 doorbell channels.
+ This configuration parameter is needed to avoid the dynamic memory allocation of doorbell channels
+ data structures.
+
+endif
diff --git a/drivers/mbox/mbox_mhuv3.c b/drivers/mbox/mbox_mhuv3.c
new file mode 100644
index 00000000000..b32a4129398
--- /dev/null
+++ b/drivers/mbox/mbox_mhuv3.c
@@ -0,0 +1,1563 @@
+/*
+ * SPDX-FileCopyrightText: Copyright 2024-2025 Arm Limited and/or its
+ * affiliates
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT arm_mhuv3
+
+#include
+
+#include
+#include
+
+#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
+#include
+LOG_MODULE_REGISTER(mbox_mhuv3);
+
+/* ====== MHUv3 Registers ====== */
+
+/* Maximum number of Doorbell channel windows */
+#define MHUV3_DBCW_MAX 128
+/* Number of DBCH combined interrupt status registers */
+#define MHUV3_DBCH_CMB_INT_ST_REG_CNT 4
+
+/* Number of FFCH combined interrupt status registers */
+#define MHUV3_FFCH_CMB_INT_ST_REG_CNT 2
+
+#define MHUV3_FLAG_BITS 32
+
+#define MHUV3_MAJOR_VERSION 2
+
+/* Postbox/Mailbox Block Identifier */
+#define BLK_ID_BLK_ID GENMASK(3, 0)
+/* Postbox/Mailbox Feature Support 0 */
+#define FEAT_SPT0_DBE_SPT GENMASK(3, 0)
+#define FEAT_SPT0_FE_SPT GENMASK(7, 4)
+#define FEAT_SPT0_FCE_SPT GENMASK(11, 8)
+/* Postbox/Mailbox Feature Support 1 */
+#define FEAT_SPT1_AUTO_OP_SPT GENMASK(3, 0)
+/* Postbox/Mailbox Doorbell Channel Configuration 0 */
+#define DBCH_CFG0_NUM_DBCH GENMASK(7, 0)
+/* Postbox/Mailbox FIFO Channel Configuration 0 */
+#define FFCH_CFG0_NUM_FFCH GENMASK(7, 0)
+#define FFCH_CFG0_P8BA_SPT BIT(8)
+#define FFCH_CFG0_P16BA_SPT BIT(9)
+#define FFCH_CFG0_P32BA_SPT BIT(10)
+#define FFCH_CFG0_P64BA_SPT BIT(11)
+#define FFCH_CFG0_FFCH_DEPTH GENMASK(25, 16)
+/* Postbox/Mailbox Fast Channel Configuration 0 */
+#define FCH_CFG0_NUM_FCH GENMASK(9, 0)
+#define FCH_CFG0_FCGI_SPT BIT(10) /* MBX-only */
+#define FCH_CFG0_NUM_FCG GENMASK(15, 11)
+#define FCH_CFG0_NUM_FCH_PER_FCG GENMASK(20, 16)
+#define FCH_CFG0_FCH_WS GENMASK(28, 21)
+/* Postbox/Mailbox Control */
+#define CTRL_OP_REQ BIT(0)
+#define CTRL_CH_OP_MSK BIT(1)
+/* Mailbox Fast Channel Control */
+#define FCH_CTRL_INT_EN BIT(2)
+/* Postbox/Mailbox Implementer Identification Register */
+#define IIDR_IMPLEMENTER GENMASK(11, 0)
+#define IIDR_REVISION GENMASK(15, 12)
+#define IIDR_VARIANT GENMASK(19, 16)
+#define IIDR_PRODUCT_ID GENMASK(31, 20)
+/* Postbox/Mailbox Architecture Identification Register */
+#define AIDR_ARCH_MINOR_REV GENMASK(3, 0)
+#define AIDR_ARCH_MAJOR_REV GENMASK(7, 4)
+/* Postbox/Mailbox Doorbell/FIFO/Fast Channel Control */
+#define XBCW_CTRL_COMB_EN BIT(0)
+/* Postbox Doorbell Interrupt Status/Clear/Enable */
+#define PDBCW_INT_TFR_ACK BIT(0)
+
+/* Padding bitfields/fields represents hole in the regs MMIO */
+
+/* CTRL_Page */
+struct ctrl_page {
+ uint32_t blk_id;
+ uint8_t reserved0[12];
+ uint32_t feat_spt0;
+ uint32_t feat_spt1;
+ uint8_t reserved1[8];
+ uint32_t dbch_cfg0;
+ uint8_t reserved2[12];
+ uint32_t ffch_cfg0;
+ uint8_t reserved3[12];
+ uint32_t fch_cfg0;
+ uint8_t reserved4[188];
+ uint32_t x_ctrl;
+ /*-- MBX-only registers --*/
+ uint8_t reserved5[60];
+ uint32_t fch_ctrl;
+ uint32_t fcg_int_en;
+ uint8_t reserved6[696];
+ /*-- End of MBX-only ---- */
+ uint32_t dbch_int_st[MHUV3_DBCH_CMB_INT_ST_REG_CNT];
+ uint32_t ffch_int_st[MHUV3_FFCH_CMB_INT_ST_REG_CNT];
+ /*-- MBX-only registers --*/
+ uint8_t reserved7[88];
+ uint32_t fcg_int_st;
+ uint8_t reserved8[12];
+ uint32_t fcg_grp_int_st[32];
+ uint8_t reserved9[2760];
+ /*-- End of MBX-only ---- */
+ uint32_t iidr;
+ uint32_t aidr;
+ uint32_t imp_def_id[12];
+} __packed;
+
+/* DBCW_Page */
+struct pdbcw_page {
+ uint32_t st;
+ uint8_t reserved0[8];
+ uint32_t set;
+ uint32_t int_st;
+ uint32_t int_clr;
+ uint32_t int_en;
+ uint32_t ctrl;
+} __packed;
+
+struct mdbcw_page {
+ uint32_t st;
+ uint32_t st_msk;
+ uint32_t clr;
+ uint8_t reserved0[4];
+ uint32_t msk_st;
+ uint32_t msk_set;
+ uint32_t msk_clr;
+ uint32_t ctrl;
+} __packed;
+
+struct dummy_page {
+ uint8_t reserved0[KB(4)];
+} __packed;
+
+struct mhuv3_pbx_frame_reg {
+ struct ctrl_page ctrl;
+ struct pdbcw_page dbcw[MHUV3_DBCW_MAX];
+ struct dummy_page ffcw;
+ struct dummy_page fcw;
+ uint8_t reserved0[KB(4) * 11];
+ struct dummy_page impdef;
+} __packed;
+
+struct mhuv3_mbx_frame_reg {
+ struct ctrl_page ctrl;
+ struct mdbcw_page dbcw[MHUV3_DBCW_MAX];
+ struct dummy_page ffcw;
+ struct dummy_page fcw;
+ uint8_t reserved0[KB(4) * 11];
+ struct dummy_page impdef;
+} __packed;
+
+/* ====== MHUv3 data structures ====== */
+
+enum mbox_mhuv3_frame {
+ PBX_FRAME,
+ MBX_FRAME,
+};
+
+static char *mbox_mhuv3_str[] = {
+ "PBX",
+ "MBX"
+};
+
+enum mbox_mhuv3_extension_type {
+ DBE_EXT,
+ FCE_EXT,
+ FE_EXT,
+ NUM_EXT
+};
+
+static char *mbox_mhuv3_ext_str[] = {
+ "DBE",
+ "FCE",
+ "FE"
+};
+
+/** @brief MHUv3 channel information. */
+struct mbox_mhuv3_channel {
+ /** Channel window index associated to this mailbox channel. */
+ uint32_t ch_idx;
+ /**
+ * Doorbell bit number within the @ch_idx window.
+ * Only relevant to Doorbell transport.
+ */
+ uint32_t doorbell;
+ /** Transport protocol specific operations for this channel. */
+ const struct mbox_mhuv3_protocol_ops *ops;
+ /** Callback function to execute on incoming message interrupts. */
+ mbox_callback_t cb;
+ /** Pointer to some private data provided at registration time */
+ void *user_data;
+};
+
+/** @brief MHUv3 operations */
+struct mbox_mhuv3_protocol_ops {
+ /** Receiver enable callback. */
+ int (*rx_enable)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Receiver disable callback. */
+ int (*rx_disable)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Read available Sender in-band LE data (if any). */
+ int *(*read_data)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /**
+ * Acknowledge data reception to the Sender. Any out-of-band data
+ * has to have been already retrieved before calling this.
+ */
+ int (*rx_complete)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Sender enable callback. */
+ int (*tx_enable)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Sender disable callback. */
+ int (*tx_disable)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Report back to the Sender if the last transfer has completed. */
+ int (*last_tx_done)(const struct device *dev, struct mbox_mhuv3_channel *chan);
+ /** Send data to the receiver. */
+ int (*send_data)(const struct device *dev, struct mbox_mhuv3_channel *chan,
+ const struct mbox_msg *msg);
+};
+
+/**
+ * @brief MHUv3 extension descriptor
+ */
+struct mbox_mhuv3_extension {
+ /** Type of extension. */
+ enum mbox_mhuv3_extension_type type;
+ /** Max number of channels found for this extension. */
+ uint32_t num_chans;
+ /**
+ * First channel number assigned to this extension, picked from
+ * the set of all mailbox channels descriptors created.
+ */
+ uint32_t base_ch_idx;
+ /** Extension specific helper to parse DT and lookup associated
+ * channel from the related 'mboxes' property.
+ */
+ struct mbox_mhuv3_channel *(*get_channel)(const struct device *dev,
+ uint32_t channel,
+ uint32_t param);
+ /** Extension specific helper to setup the combined irq. */
+ void (*combined_irq_setup)(const struct device *dev);
+ /** Extension specific helper to initialize channels. */
+ int (*channels_init)(const struct device *dev, uint32_t *base_ch_idx);
+ /**
+ * Extension specific helper to lookup which channel
+ * triggered the combined irq.
+ */
+ struct mbox_mhuv3_channel *(*chan_from_comb_irq_get)(const struct device *dev);
+ /** Extension specifier helper to get maximum data size. */
+ int (*mtu_get)(void);
+ /** Array of per-channel pending doorbells. */
+ uint32_t pending_db[MHUV3_DBCW_MAX];
+ /** Protect access to pending_db */
+ struct k_spinlock pending_lock;
+};
+
+/**
+ * @brief MHUv3 mailbox configuration data
+ */
+struct mbox_mhuv3_config {
+ /** A reference to the MHUv3 control page for this block. */
+ struct ctrl_page *ctrl;
+ union {
+ /** Base address of the PBX register mapping region. */
+ struct mhuv3_pbx_frame_reg *pbx;
+ /** Base address of the MBX register mapping region. */
+ struct mhuv3_mbx_frame_reg *mbx;
+ };
+ /** Interrupt configuration function pointer. */
+ void (*cmb_irq_config)(const struct device *dev);
+};
+
+/**
+ * @brief MHUv3 mailbox controller data
+ */
+struct mbox_mhuv3_data {
+ /** Frame type: MBX_FRAME or PBX_FRAME. */
+ enum mbox_mhuv3_frame frame;
+ /** Flag to indicate if the MHU supports AutoOp full mode. */
+ bool auto_op_full;
+ /** MHUv3 controller architectural major version. */
+ uint32_t major;
+ /** MHUv3 controller architectural minor version. */
+ uint32_t minor;
+ /** MHUv3 controller IIDR implementer. */
+ uint32_t implem;
+ /** MHUv3 controller IIDR revision. */
+ uint32_t rev;
+ /** MHUv3 controller IIDR variant. */
+ uint32_t var;
+ /** MHUv3 controller IIDR product_id. */
+ uint32_t prod_id;
+ /** The total number of channels discovered across all extensions. */
+ uint32_t num_chans;
+ /** Array holding descriptors for any found implemented extension. */
+ struct mbox_mhuv3_extension ext[NUM_EXT];
+ /** Array of channels */
+ /**
+ * TODO: The total number of channels should be increased when the rest of the
+ * extensions get supported.
+ */
+ struct mbox_mhuv3_channel channels[CONFIG_MBOX_MHUV3_NUM_DBCH][MHUV3_FLAG_BITS];
+};
+
+typedef int (*mhuv3_extension_initializer)(const struct device *dev);
+
+/* =========================== Utility Functions =========================== */
+
+/**
+ * @brief Reads a bitmask from a 32-bit memory-mapped register.
+ *
+ * Extracts and returns the value of bits specified by a bitmask
+ * from the register at the given memory address.
+ *
+ * @param addr Address of the 32-bit register.
+ * @param bitmask Bitmask specifying the bits to extract.
+ *
+ * @return Extracted bitmask value.
+ */
+static ALWAYS_INLINE uint32_t read_bitmask32(mem_addr_t addr, uint32_t bitmask)
+{
+ uint32_t reg = sys_read32(addr);
+
+ return FIELD_GET(bitmask, reg);
+}
+
+/**
+ * @brief Writes a bitmask to a 32-bit memory-mapped register.
+ *
+ * Updates bits specified by a bitmask in the register at the given
+ * memory address with the provided data value.
+ *
+ * @param data Value to write to the specified bits.
+ * @param addr Address of the 32-bit register.
+ * @param bitmask Bitmask specifying the bits to update.
+ */
+static ALWAYS_INLINE void write_bitmask32(uint32_t data, mem_addr_t addr, uint32_t bitmask)
+{
+ uint32_t reg = sys_read32(addr);
+
+ reg &= ~bitmask;
+ reg |= FIELD_PREP(bitmask, data);
+ sys_write32(reg, addr);
+}
+
+/* =================== Doorbell transport protocol operations =============== */
+
+/**
+ * @brief Enables transfer acknowledgment events for a channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_tx_enable(const struct device *dev, struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ /* Enable Transfer Acknowledgment events */
+ write_bitmask32(0x1,
+ (mem_addr_t)&cfg->pbx->dbcw[chan->ch_idx].int_en,
+ PDBCW_INT_TFR_ACK);
+
+ return 0;
+}
+
+/**
+ * @brief Disables transfer acknowledgment events for a channel.
+ *
+ * Disables Transfer Acknowledgment events and clears the corresponding
+ * interrupt and pending doorbell for the specified channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_tx_disable(const struct device *dev, struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ struct pdbcw_page *dbcw = cfg->pbx->dbcw;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ /* Disable Channel Transfer Acknowledgment events */
+ write_bitmask32(0x0, (mem_addr_t)&dbcw[chan->ch_idx].int_en, PDBCW_INT_TFR_ACK);
+
+ /* Clear Channel Transfer Acknowledgment and pending doorbell */
+ write_bitmask32(0x1, (mem_addr_t)&dbcw[chan->ch_idx].int_clr, PDBCW_INT_TFR_ACK);
+ K_SPINLOCK(&ext->pending_lock) {
+ ext->pending_db[chan->ch_idx] = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Enables transfer events for a channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_rx_enable(const struct device *dev, struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ /* Unmask Channel Transfer events */
+ sys_set_bit((mem_addr_t)&cfg->mbx->dbcw[chan->ch_idx].msk_clr, chan->doorbell);
+
+ return 0;
+}
+
+/**
+ * @brief Disables transfer events for a channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_rx_disable(const struct device *dev, struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ /* Mask Channel Transfer events */
+ sys_set_bit((mem_addr_t)&cfg->mbx->dbcw[chan->ch_idx].msk_set, chan->doorbell);
+
+ return 0;
+}
+
+/**
+ * @brief Completes a transfer event for a channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_rx_complete(const struct device *dev,
+ struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ /* Clearing the pending transfer generates the Channel Transfer Acknowledge */
+ sys_set_bit((mem_addr_t)&cfg->mbx->dbcw[chan->ch_idx].clr, chan->doorbell);
+
+ return 0;
+}
+
+/**
+ * @brief Checks if the last transfer for a channel is complete.
+ *
+ * Determines whether the last transfer for the specified channel in the
+ * Doorbell Channel Window (DBCW) register is complete. If the transfer is
+ * complete, it also clears the pending doorbell for the channel.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ *
+ * @return 0 if the last transfer is complete, or a negative error code otherwise.
+ */
+static int mbox_mhuv3_doorbell_last_tx_done(const struct device *dev,
+ struct mbox_mhuv3_channel *chan)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ bool done = !(sys_test_bit((mem_addr_t)&cfg->pbx->dbcw[chan->ch_idx].st, chan->doorbell));
+
+ if (done) {
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+
+ /* Take care to clear the pending doorbell also when polling */
+ K_SPINLOCK(&ext->pending_lock) {
+ sys_clear_bit((mem_addr_t)&ext->pending_db[chan->ch_idx], chan->doorbell);
+ }
+
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+/**
+ * @brief Sends data via the specified channel.
+ *
+ * Attempts to send data using the specified channel. If the channel is
+ * busy (i.e., a transfer is already in progress), it returns an error.
+ * It also sets the appropriate flags to indicate pending data.
+ *
+ * @param dev Pointer to the device structure.
+ * @param chan Pointer to the MHUv3 channel structure.
+ * @param msg Pointer to the message structure containing data to be sent.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_doorbell_send_data(const struct device *dev,
+ struct mbox_mhuv3_channel *chan,
+ const struct mbox_msg *msg)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ int ret = 0;
+
+ if (chan->ch_idx >= MHUV3_DBCW_MAX) {
+ return -EINVAL;
+ }
+
+ if (msg) {
+ LOG_WRN("Doorbell extension does not support sending data");
+ }
+
+ K_SPINLOCK(&ext->pending_lock) {
+ if (sys_test_bit((mem_addr_t)&ext->pending_db[chan->ch_idx], chan->doorbell)) {
+ ret = -EBUSY;
+ } else {
+ sys_set_bit((mem_addr_t)&ext->pending_db[chan->ch_idx], chan->doorbell);
+ }
+ }
+
+ if (ret == 0) {
+ sys_set_bit((mem_addr_t)&cfg->pbx->dbcw[chan->ch_idx].set, chan->doorbell);
+ }
+
+ return ret;
+}
+
+static const struct mbox_mhuv3_protocol_ops mhuv3_doorbell_ops = {
+ .tx_enable = mbox_mhuv3_doorbell_tx_enable,
+ .tx_disable = mbox_mhuv3_doorbell_tx_disable,
+ .rx_enable = mbox_mhuv3_doorbell_rx_enable,
+ .rx_disable = mbox_mhuv3_doorbell_rx_disable,
+ .rx_complete = mbox_mhuv3_doorbell_rx_complete,
+ .last_tx_done = mbox_mhuv3_doorbell_last_tx_done,
+ .send_data = mbox_mhuv3_doorbell_send_data,
+};
+
+/**
+ * @brief Retrieves the MHUv3 channel based on the provided channel ID.
+ *
+ * This function extracts the channel and doorbell numbers from the
+ * given Flattened Channel ID, and retrieves the corresponding channel
+ * from the Doorbell extension. The Flattened Channel ID is calculated
+ * using the formula: Channel ID * MHUV3_FLAG_BITS + Doorbell.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The Flattened Channel ID that contains both the
+ * channel and doorbell information.
+ *
+ * @return Pointer to the corresponding MHUv3 channel structure.
+ */
+static struct mbox_mhuv3_channel *mbox_mhuv3_get_channel(const struct device *dev,
+ mbox_channel_id_t channel_id)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+ uint32_t channel, doorbell;
+
+ /**
+ * TODO: The type of extension, channel and doorbell should be
+ * extracted from a struct passed to this function from device tree,
+ * but mbox_channel_id_t is only a Flattened Channel ID,
+ * where Flattened Channel ID = Channel ID * MHUV3_FLAG_BITS + Doorbell
+ * Until this is changed, Pin the type to Doorbell extension and use
+ * the next formula to get the actual channel and doorbell numbers.
+ */
+ enum mbox_mhuv3_extension_type type = DBE_EXT;
+
+ channel = channel_id / MHUV3_FLAG_BITS;
+ doorbell = channel_id % MHUV3_FLAG_BITS;
+
+ return data->ext[type].get_channel(dev, channel, doorbell);
+}
+
+/**
+ * @brief Sends data using the specified MHUv3 sender channel.
+ *
+ * This function checks if the last transmission is complete for the
+ * specified channel. If not, it returns an error. If the channel is
+ * available, it proceeds to send the data using the channel's send_data
+ * operation.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The ID of the channel to use for sending data.
+ * @param msg Pointer to the message structure containing the data to be sent.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_sender_send_data(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ const struct mbox_msg *msg)
+{
+ struct mbox_mhuv3_channel *chan = mbox_mhuv3_get_channel(dev, channel_id);
+ int ret;
+
+ ret = chan->ops->last_tx_done(dev, chan);
+ if (ret) {
+ return ret;
+ }
+
+ return chan->ops->send_data(dev, chan, msg);
+}
+
+/**
+ * @brief Attempts to send data using the MHUv3 receiver channel (not supported).
+ *
+ * This function logs an error as transmitting data on an MHUv3 receiver channel
+ * is not supported and returns an I/O error.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The ID of the channel (unused).
+ * @param msg Pointer to the message structure (unused).
+ *
+ * @return -ENOTSUP to indicate an I/O error as transmission is not supported.
+ */
+static int mbox_mhuv3_receiver_send_data(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ const struct mbox_msg *msg)
+{
+ ARG_UNUSED(dev);
+ ARG_UNUSED(channel_id);
+ ARG_UNUSED(msg);
+
+ LOG_ERR("Trying to transmit on a MBX MHUv3 frame");
+
+ return -ENOTSUP;
+}
+
+/**
+ * @brief Sends data based on the frame type.
+ *
+ * This function determines whether to send data using the sender or receiver
+ * functions based on the current frame type.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The ID of the channel to use for sending data.
+ * @param msg Pointer to the message structure containing the data to be sent.
+ *
+ * @return 0 on success, an error code on failure.
+ */
+static int mbox_mhuv3_send_data(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ const struct mbox_msg *msg)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->frame == PBX_FRAME) {
+ return mbox_mhuv3_sender_send_data(dev, channel_id, msg);
+ }
+
+ return mbox_mhuv3_receiver_send_data(dev, channel_id, msg);
+}
+
+/**
+ * @brief Retrieves the MHUv3 channel based on the specified channel and doorbell.
+ *
+ * This function returns the channel structure for the given channel and doorbell
+ * numbers.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel The channel number to retrieve.
+ * @param doorbell The doorbell number to retrieve.
+ *
+ * @return Pointer to the corresponding MHUv3 channel structure, or NULL if invalid.
+ */
+static struct mbox_mhuv3_channel *mbox_mhuv3_dbe_get_channel(const struct device *dev,
+ uint32_t channel,
+ uint32_t doorbell)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+
+ if (channel >= ext->num_chans || doorbell >= MHUV3_FLAG_BITS) {
+ LOG_ERR("Couldn't find a valid channel (%d: %d)", channel, doorbell);
+ return NULL;
+ }
+
+ return &data->channels[ext->base_ch_idx + channel][doorbell];
+}
+
+/**
+ * @brief Configures the combined IRQ for the MHUv3 device.
+ *
+ * This function sets up the combined interrupt handling for the specified device,
+ * configuring the interrupt enable, clear, and control registers for each channel.
+ * It handles both PBX and non-PBX frame types differently.
+ *
+ * @param dev Pointer to the device structure.
+ */
+static void mbox_mhuv3_dbe_combined_irq_setup(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ uint32_t i;
+
+ if (data->frame == PBX_FRAME) {
+ struct pdbcw_page *dbcw = cfg->pbx->dbcw;
+
+ for (i = 0; i < ext->num_chans; i++) {
+ write_bitmask32(0x1, (mem_addr_t)&dbcw[i].int_clr, PDBCW_INT_TFR_ACK);
+ write_bitmask32(0x0, (mem_addr_t)&dbcw[i].int_en, PDBCW_INT_TFR_ACK);
+ write_bitmask32(0x1, (mem_addr_t)&dbcw[i].ctrl, XBCW_CTRL_COMB_EN);
+ }
+ } else {
+ struct mdbcw_page *dbcw = cfg->mbx->dbcw;
+
+ for (i = 0; i < ext->num_chans; i++) {
+ sys_write32(0xFFFFFFFFU, (mem_addr_t)&dbcw[i].clr);
+ sys_write32(0xFFFFFFFFU, (mem_addr_t)&dbcw[i].msk_set);
+ write_bitmask32(0x1, (mem_addr_t)&dbcw[i].ctrl, XBCW_CTRL_COMB_EN);
+ }
+ }
+}
+
+/**
+ * @brief Initializes the MHUv3 DBE channels.
+ *
+ * This function initializes the MHUv3 DBE channels by assigning the base index,
+ * configuring each channel with its corresponding index and doorbell, and
+ * setting the appropriate channel operations. It updates the base channel index
+ * after initialization.
+ *
+ * @param dev Pointer to the device structure.
+ * @param base_ch_idx Pointer to the base channel index.
+ *
+ * @return 0 on success.
+ */
+static int mbox_mhuv3_dbe_channels_init(const struct device *dev, uint32_t *base_ch_idx)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ struct mbox_mhuv3_channel *chan;
+
+ __ASSERT(((*base_ch_idx + ext->num_chans) * MHUV3_FLAG_BITS) < data->num_chans,
+ "The number of allocated channels is less than required by the MHUv3 extension");
+
+ ext->base_ch_idx = *base_ch_idx;
+ chan = &data->channels[*base_ch_idx];
+
+ for (uint32_t i = 0; i < ext->num_chans; i++) {
+ for (uint32_t k = 0; k < MHUV3_FLAG_BITS; k++) {
+ chan->ch_idx = i;
+ chan->doorbell = k;
+ chan->ops = &mhuv3_doorbell_ops;
+ chan++;
+ }
+ }
+
+ *base_ch_idx += ext->num_chans;
+
+ return 0;
+}
+
+/**
+ * @brief Searches for the active doorbell for the given MHUv3 DBE channel.
+ *
+ * This function checks the interrupt status and identifies which doorbell has been
+ * triggered for the specified channel. It returns the doorbell index if found or
+ * logs a warning if an unexpected interrupt occurs.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel The channel to check for the doorbell.
+ * @param doorbell Pointer to store the found doorbell index.
+ *
+ * @return true if a doorbell is found, false if an unexpected IRQ is encountered.
+ */
+static bool mbox_mhuv3_dbe_doorbell_search(const struct device *dev,
+ uint32_t channel,
+ uint32_t *doorbell)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ uint32_t st;
+
+ __ASSERT(channel < MHUV3_DBCW_MAX,
+ "Number of channels exceeds the maximum number of doorbell channel windows.");
+
+ if (data->frame == PBX_FRAME) {
+ uint32_t active_doorbells, fired_doorbells;
+
+ st = read_bitmask32((mem_addr_t)&cfg->pbx->dbcw[channel].int_st, PDBCW_INT_TFR_ACK);
+ if (!st) {
+ goto unexpected_irq;
+ }
+ active_doorbells = sys_read32((mem_addr_t)&cfg->pbx->dbcw[channel].st);
+ K_SPINLOCK(&ext->pending_lock) {
+ fired_doorbells = ext->pending_db[channel] & ~active_doorbells;
+ if (!fired_doorbells) {
+ K_SPINLOCK_BREAK;
+ }
+
+ *doorbell = find_lsb_set(fired_doorbells) - 1;
+ ext->pending_db[channel] &= ~BIT(*doorbell);
+ }
+ if (!fired_doorbells) {
+ goto unexpected_irq;
+ }
+ /* If no other pending doorbells, clear the transfer acknowledge */
+ if (!(fired_doorbells & ~BIT(*doorbell))) {
+ write_bitmask32(0x1, (mem_addr_t)&cfg->pbx->dbcw[channel].int_clr,
+ PDBCW_INT_TFR_ACK);
+ }
+ } else {
+ st = sys_read32((mem_addr_t)&cfg->mbx->dbcw[channel].st_msk);
+ if (!st) {
+ goto unexpected_irq;
+ }
+
+ *doorbell = find_lsb_set(st) - 1;
+ }
+
+ return true;
+
+unexpected_irq:
+ LOG_WRN("Unexpected IRQ on %s channel:%d", mbox_mhuv3_str[data->frame], channel);
+
+ return false;
+}
+
+/**
+ * @brief Retrieves the channel corresponding to a combined interrupt.
+ *
+ * This function checks the combined interrupt status for the MHUv3 DBE device and
+ * identifies the channel and doorbell associated with the interrupt. It returns the
+ * matching channel if found, or NULL if no valid channel is identified.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return Pointer to the mbox_mhuv3_channel if a valid channel is found, NULL otherwise.
+ */
+static struct mbox_mhuv3_channel *mbox_mhuv3_dbe_chan_from_comb_irq_get(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+
+ for (uint32_t i = 0; i < MHUV3_DBCH_CMB_INT_ST_REG_CNT; i++) {
+ uint32_t channel, doorbell, int_st;
+
+ int_st = sys_read32((mem_addr_t)&cfg->ctrl->dbch_int_st[i]);
+ if (!int_st) {
+ continue;
+ }
+
+ channel = i * MHUV3_FLAG_BITS + (find_lsb_set(int_st) - 1);
+ if (channel >= ext->num_chans) {
+ LOG_ERR("Invalid %s channel: %d", mbox_mhuv3_str[data->frame], channel);
+ return NULL;
+ }
+
+ if (!mbox_mhuv3_dbe_doorbell_search(dev, channel, &doorbell)) {
+ continue;
+ }
+
+ LOG_DBG("Found %s channel [%d], doorbell[%d]",
+ mbox_mhuv3_str[data->frame], channel, doorbell);
+ return &data->channels[channel][doorbell];
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Retrieves the maximum transmit units for doorbell extension.
+ *
+ * @return 0 as doorbell extension does not support transmitting data.
+ */
+static int mbox_mhuv3_dbe_mtu_get(void)
+{
+ return 0;
+}
+
+/**
+ * @brief Initializes the Doorbell Extension (DBE) for the MHUv3 device.
+ *
+ * This function initializes the Doorbell Extension (DBE) by reading the number
+ * of channels from the device's control registers, and setting up necessary handlers.
+ * If the feature is not supported, it returns early. It also updates the device's
+ * data with the number of channels available and the extension's function pointers.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return 0 if initialization is successful, negative error code if there is a failure.
+ */
+static int mbox_mhuv3_dbe_init(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[DBE_EXT];
+ uint32_t num_chans;
+
+ if (!read_bitmask32((mem_addr_t)&cfg->ctrl->feat_spt0, FEAT_SPT0_DBE_SPT)) {
+ return 0;
+ }
+
+ LOG_DBG("%s: Initializing Doorbell Extension.", mbox_mhuv3_str[data->frame]);
+
+ ext->type = DBE_EXT;
+ /* Note that, by the spec, the number of channels is (num_dbch + 1) */
+ num_chans = read_bitmask32((mem_addr_t)&cfg->ctrl->dbch_cfg0, DBCH_CFG0_NUM_DBCH) + 1;
+ __ASSERT(CONFIG_MBOX_MHUV3_NUM_DBCH >= num_chans,
+ "The number of configured doorbell channels is less than required by the MHUv3 extension");
+ ext->num_chans = num_chans;
+ ext->get_channel = mbox_mhuv3_dbe_get_channel;
+ ext->combined_irq_setup = mbox_mhuv3_dbe_combined_irq_setup;
+ ext->channels_init = mbox_mhuv3_dbe_channels_init;
+ ext->chan_from_comb_irq_get = mbox_mhuv3_dbe_chan_from_comb_irq_get;
+ ext->mtu_get = mbox_mhuv3_dbe_mtu_get;
+
+ data->num_chans += ext->num_chans * MHUV3_FLAG_BITS;
+
+ LOG_DBG("%s: found %d DBE channels.", mbox_mhuv3_str[data->frame], ext->num_chans);
+
+ return 0;
+}
+
+/**
+ * @brief Initializes the Fast Channel Extension (FCE) for the MHUv3 device.
+ *
+ * This function initializes the Fast Channel Extension (FCE). Currently,
+ * FCE is not supported.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return 0, indicating that the function completes successfully.
+ */
+static int mbox_mhuv3_fce_init(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (!read_bitmask32((mem_addr_t)&cfg->ctrl->feat_spt0, FEAT_SPT0_FCE_SPT)) {
+ return 0;
+ }
+
+ LOG_DBG("%s: Fast Channel Extension is not supported by driver.",
+ mbox_mhuv3_str[data->frame]);
+
+ return 0;
+}
+
+/**
+ * @brief Initializes the FIFO Extension (FE) for the MHUv3 device.
+ *
+ * This function initializes the FIFO Extension (FE). Currently,
+ * FE is not supported.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return 0, indicating that the function completes successfully.
+ */
+static int mbox_mhuv3_fe_init(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (!read_bitmask32((mem_addr_t)&cfg->ctrl->feat_spt0, FEAT_SPT0_FE_SPT)) {
+ return 0;
+ }
+
+ LOG_DBG("%s: FIFO Extension is not supported by driver.", mbox_mhuv3_str[data->frame]);
+
+ return 0;
+}
+
+static mhuv3_extension_initializer mhuv3_extension_init[NUM_EXT] = {
+ mbox_mhuv3_dbe_init,
+ mbox_mhuv3_fce_init,
+ mbox_mhuv3_fe_init,
+};
+
+/**
+ * @brief Initializes the channels for the MHUv3 device.
+ *
+ * This function allocates memory for the channels array and initializes
+ * each channel based on the device's extensions. It calls the `channels_init`
+ * function for each extension and updates the base channel index accordingly.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_initialize_channels(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+ uint32_t base_ch_idx = 0;
+ int ret = 0;
+
+ for (uint32_t i = 0; i < NUM_EXT && !ret; i++) {
+ struct mbox_mhuv3_extension *ext = &data->ext[i];
+
+ if (ext->num_chans > 0) {
+ ret = ext->channels_init(dev, &base_ch_idx);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Initializes the MHUv3 frame for the device.
+ *
+ * This function reads the block ID to determine the frame type (PBX or MBX),
+ * retrieves the version and implementer information, and checks if the frame
+ * is supported. It also handles the initialization of extensions for the MHUv3 device.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return 0 on success, or a negative error code on failure (e.g., -EIO, -ENOMEM).
+ */
+static int mbox_mhuv3_frame_init(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+ struct mbox_mhuv3_data *data = dev->data;
+
+ data->frame = read_bitmask32((mem_addr_t)&cfg->ctrl->blk_id, BLK_ID_BLK_ID);
+ if (data->frame > MBX_FRAME) {
+ LOG_ERR("Invalid Frame type- %d", data->frame);
+ return -EIO;
+ }
+
+ data->major = read_bitmask32((mem_addr_t)&cfg->ctrl->aidr, AIDR_ARCH_MAJOR_REV);
+ data->minor = read_bitmask32((mem_addr_t)&cfg->ctrl->aidr, AIDR_ARCH_MINOR_REV);
+ data->implem = read_bitmask32((mem_addr_t)&cfg->ctrl->iidr, IIDR_IMPLEMENTER);
+ data->rev = read_bitmask32((mem_addr_t)&cfg->ctrl->iidr, IIDR_REVISION);
+ data->var = read_bitmask32((mem_addr_t)&cfg->ctrl->iidr, IIDR_VARIANT);
+ data->prod_id = read_bitmask32((mem_addr_t)&cfg->ctrl->iidr, IIDR_PRODUCT_ID);
+ if (data->major != MHUV3_MAJOR_VERSION) {
+ LOG_ERR("Unsupported MHU %s block - major:%d minor:%d",
+ mbox_mhuv3_str[data->frame], data->major, data->minor);
+ return -EIO;
+ }
+
+ data->auto_op_full = !!read_bitmask32((mem_addr_t)&cfg->ctrl->feat_spt1,
+ FEAT_SPT1_AUTO_OP_SPT);
+ /* Request the PBX/MBX to remain operational */
+ if (data->auto_op_full) {
+ write_bitmask32(0x1, (mem_addr_t)&cfg->ctrl->x_ctrl, CTRL_OP_REQ);
+ }
+
+ LOG_DBG("Found MHU %s block - major:%d minor:%d\n"
+ " implem:0x%X rev:0x%X var:0x%X prod_id:0x%X",
+ mbox_mhuv3_str[data->frame], data->major, data->minor,
+ data->implem, data->rev, data->var, data->prod_id);
+
+ for (uint32_t i = 0; i < NUM_EXT; i++) {
+ uint32_t ret;
+ /*
+ * Note that extensions initialization fails only when such
+ * extension initialization routine fails and the extensions
+ * was found to be supported in hardware and in software.
+ */
+ ret = mhuv3_extension_init[i](dev);
+ if (ret) {
+ LOG_ERR("Failed to initialize %s %s: %d",
+ mbox_mhuv3_str[data->frame],
+ mbox_mhuv3_ext_str[i],
+ ret);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Handles the PBX combined interrupt for the MHUv3 device.
+ *
+ * This function iterates through all available extensions and checks for channels
+ * that are part of the combined interrupt mechanism.
+ *
+ * @param dev Pointer to the device structure.
+ */
+static void mbox_mhuv3_pbx_comb_interrupt(const struct device *dev)
+{
+ uint32_t found = 0;
+
+ for (uint32_t i = 0; i < NUM_EXT; i++) {
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[i];
+ struct mbox_mhuv3_channel *chan;
+
+ /* FCE does not participate to the PBX combined */
+ if (i == FCE_EXT || ext->num_chans == 0) {
+ continue;
+ }
+
+ chan = ext->chan_from_comb_irq_get(dev);
+ if (!chan) {
+ continue;
+ }
+
+ found++;
+
+ if (!chan->ops) {
+ LOG_WRN("TX Ack on UNBOUND channel (%u)", chan->ch_idx);
+ continue;
+ }
+ }
+
+ if (found == 0) {
+ LOG_WRN("Failed to find channel for the TX interrupt");
+ }
+}
+
+/**
+ * @brief Handles the MBX combined interrupt for the MHUv3 device.
+ *
+ * This function iterates through all available extensions and checks for channels
+ * that are part of the combined interrupt mechanism. It reads data from the channel
+ * if available and invokes the appropriate callback for the channel.
+ *
+ * @param dev Pointer to the device structure.
+ */
+static void mbox_mhuv3_mbx_comb_interrupt(const struct device *dev)
+{
+ uint32_t found = 0;
+
+ for (uint32_t i = 0; i < NUM_EXT; i++) {
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_extension *ext = &data->ext[i];
+ struct mbox_mhuv3_channel *chan;
+ uint32_t flattened_ch_idx;
+
+ if (ext->num_chans == 0) {
+ continue;
+ }
+
+ chan = ext->chan_from_comb_irq_get(dev);
+ if (!chan) {
+ continue;
+ }
+
+ found++;
+
+ if (!chan->ops) {
+ LOG_WRN("RX Data on UNBOUND channel (%u)", chan->ch_idx);
+ }
+
+ if (chan->ops->read_data) {
+ void *data = chan->ops->read_data(dev, chan);
+
+ if (!data) {
+ LOG_ERR("Failed to read in-band data.");
+ goto rx_ack;
+ }
+ }
+
+ /* Call channel call back */
+ flattened_ch_idx = chan->ch_idx * MHUV3_FLAG_BITS + chan->doorbell;
+ chan->cb(dev, flattened_ch_idx, chan->user_data, NULL);
+
+rx_ack:
+ if (chan->ops->rx_complete) {
+ chan->ops->rx_complete(dev, chan);
+ }
+ }
+
+ if (found == 0) {
+ LOG_ERR("Failed to find channel for the RX interrupt");
+ }
+}
+
+/**
+ * @brief Handles the combined interrupt for the MHUv3 device, based on the frame type.
+ *
+ * This function checks the current frame type of the MHUv3 device (PBX or MBX) and
+ * delegates the interrupt handling to the appropriate function.
+ *
+ * @param dev Pointer to the device structure.
+ */
+static void mbox_mhuv3_comb_interrupt(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->frame == PBX_FRAME) {
+ mbox_mhuv3_pbx_comb_interrupt(dev);
+ } else {
+ mbox_mhuv3_mbx_comb_interrupt(dev);
+ }
+}
+
+/**
+ * @brief Sets up the PBX for the MHUv3 device.
+ *
+ * This function initializes the PBX-related IRQs (interrupts) for the MHUv3 device.
+ * If no combined IRQ configuration is provided, it defaults to using PBX in Tx polling mode.
+ *
+ * @param dev Pointer to the device structure.
+ * @return 0 on success.
+ */
+static int mbox_mhuv3_setup_pbx(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (cfg->cmb_irq_config != NULL) {
+ cfg->cmb_irq_config(dev);
+
+ for (uint32_t i = 0; i < NUM_EXT; i++) {
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->ext[i].num_chans > 0) {
+ data->ext[i].combined_irq_setup(dev);
+ }
+ }
+
+ LOG_DBG("MHUv3 PBX IRQs initialized.");
+
+ return 0;
+ }
+
+ LOG_INF("Using PBX in Tx polling mode.");
+
+ return 0;
+}
+
+/**
+ * @brief Sets up the MBX for the MHUv3 device.
+ *
+ * This function initializes the MBX-related IRQs (interrupts) for the MHUv3 device.
+ *
+ * @param dev Pointer to the device structure.
+ * @return 0 on success, -EINVAL if combined IRQ configuration is missing.
+ */
+static int mbox_mhuv3_setup_mbx(const struct device *dev)
+{
+ const struct mbox_mhuv3_config *cfg = dev->config;
+
+ if (cfg->cmb_irq_config == NULL) {
+ LOG_ERR("MBX combined IRQ is missing!");
+ return -EINVAL;
+ }
+
+ cfg->cmb_irq_config(dev);
+
+ for (uint32_t i = 0; i < NUM_EXT; i++) {
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->ext[i].num_chans > 0) {
+ data->ext[i].combined_irq_setup(dev);
+ }
+ }
+
+ LOG_DBG("MHUv3 MBX IRQs initialized.");
+
+ return 0;
+}
+
+/**
+ * @brief Initializes the IRQs for the MHUv3 device.
+ *
+ * This function initializes the IRQs (interrupts) based on the frame type
+ * of the MHUv3 device.
+ *
+ * @param dev Pointer to the device structure.
+ * @return 0 on success, or the error code returned by the setup functions.
+ */
+static int mbox_mhuv3_irqs_init(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ LOG_DBG("Initializing %s block.", mbox_mhuv3_str[data->frame]);
+
+ if (data->frame == PBX_FRAME) {
+ return mbox_mhuv3_setup_pbx(dev);
+ }
+
+ return mbox_mhuv3_setup_mbx(dev);
+}
+
+/**
+ * @brief Initializes the MHUv3 device.
+ *
+ * This function initializes the MHUv3 device by performing the following steps:
+ * 1. Initializes the frame type.
+ * 2. Initializes the IRQs.
+ * 3. Initializes the channels.
+ *
+ * If any of the initialization steps fail, the error code is returned immediately.
+ *
+ * @param dev Pointer to the device structure.
+ * @return 0 on success, or the error code from any of the initialization functions.
+ */
+static int mbox_mhuv3_init(const struct device *dev)
+{
+ int ret;
+
+ ret = mbox_mhuv3_frame_init(dev);
+ if (ret) {
+ return ret;
+ }
+
+ ret = mbox_mhuv3_irqs_init(dev);
+ if (ret) {
+ return ret;
+ }
+
+ ret = mbox_mhuv3_initialize_channels(dev);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Attempts to register a callback for a specified channel on the sender.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The ID of the channel for the callback.
+ * @param cb The callback function to be registered.
+ * @param user_data User data to be passed to the callback.
+ *
+ * @return Always returns -ENOTSUP as callback registration for a sender device is not supported.
+ */
+static int mbox_mhuv3_sender_register_callback(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ mbox_callback_t cb, void *user_data)
+{
+ ARG_UNUSED(dev);
+ ARG_UNUSED(channel_id);
+ ARG_UNUSED(cb);
+ ARG_UNUSED(user_data);
+
+ LOG_ERR("Trying to register a callback on a PBX MHUv3 frame");
+
+ return -ENOTSUP;
+}
+
+/**
+ * @brief Registers a callback function for a specified channel on the receiver.
+ *
+ * This function associates a callback with the specified channel. The callback
+ * will be invoked when the receiver processes data for that channel. The user
+ * data will be passed to the callback when it is called.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The channel ID for which the callback is being registered.
+ * @param cb The callback function to register for the receiver.
+ * @param user_data User-specific data that will be passed to the callback.
+ *
+ * @return 0 on success, or -EINVAL if the specified channel is invalid.
+ */
+static int mbox_mhuv3_receiver_register_callback(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ mbox_callback_t cb, void *user_data)
+{
+ struct mbox_mhuv3_channel *chan = mbox_mhuv3_get_channel(dev, channel_id);
+
+ if (!chan) {
+ return -EINVAL;
+ }
+
+ chan->cb = cb;
+ chan->user_data = user_data;
+
+ return 0;
+}
+
+/**
+ * @brief Registers a callback function for a specified channel, based on the frame type.
+ *
+ * @param dev Pointer to the device structure.
+ * @param channel_id The channel ID for which the callback is being registered.
+ * @param cb The callback function to register.
+ * @param user_data User-specific data that will be passed to the callback.
+ *
+ * @return 0 on success, or an error code from the sender or receiver callback registration.
+ */
+static int mbox_mhuv3_register_callback(const struct device *dev,
+ mbox_channel_id_t channel_id,
+ mbox_callback_t cb, void *user_data)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->frame == PBX_FRAME) {
+ return mbox_mhuv3_sender_register_callback(dev, channel_id, cb, user_data);
+ }
+
+ return mbox_mhuv3_receiver_register_callback(dev, channel_id, cb, user_data);
+}
+
+/**
+ * @brief Retrieves the maximum transmission unit (MTU) for the sender device.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return The MTU size for the sender channel.
+ */
+static int mbox_mhuv3_sender_mtu_get(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ /**
+ * TODO: The maximum transmit units depends on the channel extension.
+ * The type of extension, channel and doorbell should be
+ * extracted from a struct passed to this function from device tree.
+ * Until this is changed, Pin the type to Doorbell extension which
+ * is the only supported extension.
+ */
+
+ enum mbox_mhuv3_extension_type type = DBE_EXT;
+
+ return data->ext[type].mtu_get();
+}
+
+/**
+ * @brief Attempts to retrieve the maximum transmission unit (MTU) for the receiver device.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return Always returns 0.
+ */
+static int mbox_mhuv3_receiver_mtu_get(const struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ LOG_ERR("Trying to get maximum transmit units on a MBX MHUv3 frame");
+
+ return 0;
+}
+
+/**
+ * @brief Retrieves the Maximum Transmission Unit (MTU) for the MHUv3 device.
+ *
+ * This function determines the MTU based on the frame type (PBX or MBX) and calls
+ * the appropriate function to retrieve the MTU for the sender or receiver.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return The MTU value for the sender or receiver, depending on the frame type.
+ */
+static int mbox_mhuv3_mtu_get(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ if (data->frame == PBX_FRAME) {
+ return mbox_mhuv3_sender_mtu_get(dev);
+ }
+
+ return mbox_mhuv3_receiver_mtu_get(dev);
+}
+
+/**
+ * @brief Retrieves the maximum number of channels available in the MHUv3 device.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return The number of available channels for the MHUv3 device.
+ */
+static uint32_t mbox_mhuv3_max_channels_get(const struct device *dev)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+
+ return data->num_chans;
+}
+
+/**
+ * @brief Enables or disables a channel based on the MHUv3 frame type.
+ *
+ * This function handles enabling or disabling the transmission (TX) or reception (RX)
+ * functionality of a channel.
+ *
+ * @param dev The device handle representing the MHUv3 system.
+ * @param channel_id The ID of the channel to enable or disable.
+ * @param enabled Boolean flag to enable (true) or disable (false) the channel.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+static int mbox_mhuv3_set_enabled(const struct device *dev,
+ mbox_channel_id_t channel_id, bool enabled)
+{
+ struct mbox_mhuv3_data *data = dev->data;
+ struct mbox_mhuv3_channel *chan = mbox_mhuv3_get_channel(dev, channel_id);
+ int ret;
+
+ if (!chan) {
+ return -EINVAL;
+ }
+
+ if (data->frame == PBX_FRAME) {
+ if (enabled) {
+ ret = chan->ops->tx_enable(dev, chan);
+ } else {
+ ret = chan->ops->tx_disable(dev, chan);
+ }
+ } else {
+ if (enabled) {
+ ret = chan->ops->rx_enable(dev, chan);
+ } else {
+ ret = chan->ops->rx_disable(dev, chan);
+ }
+ }
+
+ return ret;
+}
+
+static DEVICE_API(mbox, mhuv3_driver_api) = {
+ .send = mbox_mhuv3_send_data,
+ .register_callback = mbox_mhuv3_register_callback,
+ .mtu_get = mbox_mhuv3_mtu_get,
+ .max_channels_get = mbox_mhuv3_max_channels_get,
+ .set_enabled = mbox_mhuv3_set_enabled,
+};
+
+#define MHUV3_INIT(n) \
+ \
+static void mbox_mhuv3_cmb_irq_config_##n(const struct device *dev) \
+{ \
+ IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, combined, irq), \
+ DT_INST_IRQ_BY_NAME(n, combined, priority), \
+ mbox_mhuv3_comb_interrupt, \
+ DEVICE_DT_INST_GET(n), \
+ 0); \
+ irq_enable(DT_INST_IRQ_BY_NAME(n, combined, irq)); \
+} \
+ \
+static const struct mbox_mhuv3_config mhuv3_cfg_##n = { \
+ (struct ctrl_page *)DT_INST_REG_ADDR(n), \
+ DT_INST_REG_ADDR(n), \
+ mbox_mhuv3_cmb_irq_config_##n, \
+}; \
+ \
+struct mbox_mhuv3_data mhuv3_data_##n; \
+ \
+DEVICE_DT_INST_DEFINE(n, \
+ &mbox_mhuv3_init, \
+ NULL, \
+ &mhuv3_data_##n, \
+ &mhuv3_cfg_##n, \
+ POST_KERNEL, \
+ CONFIG_MBOX_INIT_PRIORITY, \
+ &mhuv3_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(MHUV3_INIT);
diff --git a/dts/bindings/mbox/arm,mhuv3.yaml b/dts/bindings/mbox/arm,mhuv3.yaml
new file mode 100644
index 00000000000..7b6ceee1736
--- /dev/null
+++ b/dts/bindings/mbox/arm,mhuv3.yaml
@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its
+# affiliates
+#
+# SPDX-License-Identifier: Apache-2.0
+
+description: ARM MHUv3 (Message Handling Unit v3)
+
+compatible: "arm,mhuv3"
+
+include: [base.yaml, mailbox-controller.yaml]
+
+properties:
+ reg:
+ required: true
+
+ interrupts:
+ required: true
+
+ interrupt-names:
+ required: true
+
+mbox-cells:
+ - channel