blob: b32a412939834b194e0d0e22a2c6f28ce22613ad [file] [log] [blame]
/*
* SPDX-FileCopyrightText: <text>Copyright 2024-2025 Arm Limited and/or its
* affiliates <open-source-office@arm.com></text>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT arm_mhuv3
#include <zephyr/drivers/mbox.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
#include <zephyr/logging/log.h>
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);