Files
E3/e3_176_ref/drivers/source/mbox/sdrv_mbox.c
2025-10-21 19:40:27 +08:00

1105 lines
28 KiB
C

/**
* @file sdrv_mbox.c
*
* Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*
* Description: mbox driver file
*
* Revision History:
* -----------------
*/
#include <types.h>
#include <debug.h>
#include <string.h>
#include <bits.h>
#include <param.h>
#include <reg.h>
#include <armv7-r/cache.h>
#include <armv7-r/irq.h>
#include <part.h>
#include "sdrv_mbox.h"
#include <irq.h>
#include <udelay/udelay.h>
#include <core_id.h>
/** @name Register Map
* Register offsets from the base address of sdrv mbox.
*/
#define SDRV_MBOX_TMH0 0x0U
#define SDRV_MBOX_TMH1 0x4U
#define SDRV_MBOX_TMH2 0x8U
#define SDRV_MBOX_TMC(id) (0xcU + ((id) * 0x4U))
#define SDRV_MBOX_TMS 0x1cU
#define SDRV_MBOX_TPWES 0x20U
#define SDRV_MBOX_RMC 0x24U
#define SDRV_MBOX_RWC 0x28U
#define SDRV_MBOX_RMH(id, n, m) ((0x2cU + ((id) * 0x4U)) + ((m) * 0xcU) + ((n) * 0x30U))
#define SDRV_MBOX_MASTERID(id) (0x500U + ((id) * 0x4U))
#define SDRV_MBOX_REMOTE_NUM 8U
#define SDRV_MBOX_MSG_NUM 4U
#define SDRV_MBOX_BUF_NUM 4U
/*
* For E34xx/E36xx chips, iram serves as the shared memory for mailbox communication.
* CONFIG_MBOX_BUF_SIZE defines the size of the mailbox buffer for each core,
* with a default size of 4096 bytes. Users can redefine this in config.h file.
* CONFIG_MBOX_BUF_SIZE must be aligned with 128 bytes, range from 128 to 4096 bytes.
* The total buffer size for all cores is calculated as CONFIG_MBOX_BUF_SIZE * CORE_NUM.
*/
#if (CONFIG_E3 && CONFIG_MBOX_BUF_SIZE)
#if CONFIG_MBOX_BUF_SIZE > 4096U
#error "CONFIG_MBOX_BUF_SIZE for a single core exceeds 4096 bytes."
#endif
#if (CONFIG_MBOX_BUF_SIZE % 128U)
#error "CONFIG_MBOX_BUF_SIZE for a single core is not a multiple of 128 bytes."
#endif
#define SDRV_MBOX_BUF_LEN CONFIG_MBOX_BUF_SIZE
#else
#define SDRV_MBOX_BUF_LEN 4096U
#endif
#define SDRV_MBOX_BANK_LEN (SDRV_MBOX_BUF_LEN/SDRV_MBOX_BUF_NUM)
#if CONFIG_E3
#define SDRV_MBOX_TX_BUF(n) ((n) * SDRV_MBOX_BUF_LEN)
#define SDRV_MBOX_RX_BUF(n) ((n) * SDRV_MBOX_BUF_LEN)
#else
#define SDRV_MBOX_TX_BUF 0x1000U
#define SDRV_MBOX_RX_BUF(n) (0x2000U + ((n) * SDRV_MBOX_BUF_LEN))
#define SDRV_MBOX_TX_BUF_DIFF 0x1000U
#endif
#define SDRV_MBOX_CPUUSERID 0x600U
#define SDRV_MBOX_ERR_INJ_CTRL 0x604U
#define SDRV_MBOX_INTEN 0x7f0U
#define SDRV_MBOX_INTCLR 0x7f4U
#define SDRV_MBOX_INTSTAT 0x7f8U
/** @name Transmit Message Header 0 Register
* Transmit Message Header 0 Register definitions
*/
#define SDRV_MBOX_TMH0_MID_SHIFT 24U
#define SDRV_MBOX_TMH0_MDP_SHIFT 16U
#define SDRV_MBOX_TMH0_MBM_SHIFT 12U
#define SDRV_MBOX_TMH0_MB_SHIFT 11U
#define SDRV_MBOX_TMH0_LEN_SHIFT 0U
/** @name Transmit Message Control Register
* Transmit Message Control Register definitions
*/
#define SDRV_MBOX_TMC_WAK_SHIFT 16U
#define SDRV_MBOX_TMC_CANCEL_SHIFT 8U
#define SDRV_MBOX_TMC_SEND_SHIFT 0U
/** @name Receive Message Header 0 Register
* Receive Message Header 0 Register definitions
* @{
*/
#define SDRV_MBOX_RMH0_IDX_SHIFT 24U
#define SDRV_MBOX_RMH0_MV_SHIFT 16U
#define SDRV_MBOX_RMH0_MBM_SHIFT 12U
#define SDRV_MBOX_RMH0_MB_SHIFT 11U
#define SDRV_MBOX_RMH0_MB_MASK 0xfU
#define SDRV_MBOX_RMH0_LEN_SHIFT 0U
#define SDRV_MBOX_RMH0_LEN_MASK 0x7ffU
#define SDRV_MBOX_MASTERID_NUM 5U
#if COINFIG_SDRV_MBOX_TX_POLL
#define SDRV_MBOX_TX_POLL COINFIG_SDRV_MBOX_TX_POLL
#else
#define SDRV_MBOX_TX_POLL 20U
#endif
/* config sdrv mbox debug */
#define SDRV_MBOX_DEBUG_LVL SSDK_WARNING
#define EXTRA_FMT "sdrv_mbox:"
#if SDRV_MBOX_DEBUG_LVL >= SSDK_ERR
#define SDRV_MBOX_ERR(format, ...) \
ssdk_printf(SSDK_ERR, EXTRA_FMT format, ##__VA_ARGS__)
#else
#define SDRV_MBOX_ERR(format, ...)
#endif
#if SDRV_MBOX_DEBUG_LVL >= SSDK_WARNING
#define SDRV_MBOX_WARN(format, ...) \
ssdk_printf(SSDK_WARNING, EXTRA_FMT format, ##__VA_ARGS__)
#else
#define SDRV_MBOX_WARN(format, ...)
#endif
#if SDRV_MBOX_DEBUG_LVL >= SSDK_INFO
#define SDRV_MBOX_INFO(format, ...) \
ssdk_printf(SSDK_INFO, EXTRA_FMT format, ##__VA_ARGS__)
#else
#define SDRV_MBOX_INFO(format, ...)
#endif
/**
* @brief sdrv mbox read reg.
*
* @param[in] base reg base
* @param[in] offset reg offset
* @param[in] return reg val
*/
static inline uint32_t sdrv_mbox_readl(paddr_t base, uint32_t offset)
{
return readl(base + offset);
}
/**
* @brief sdrv mbox write reg.
*
* @param[in] base reg base
* @param[in] offset reg offset
* @param[in] val reg val
*/
static inline void sdrv_mbox_writel(paddr_t base, uint32_t offset, uint32_t val)
{
writel(val, (base + offset));
}
/**
* @brief sdrv mbox buff clear.
*
* @param[in] base start base
* @param[in] size clear size
*/
static inline void sdrv_mbox_buff_clr(paddr_t base, size_t size)
{
uint32_t addr;
/* Each store must be 32 bits aligned. */
for (addr = base; addr < base + size; addr += 4) {
writel((uint32_t)0x0U,addr);
}
}
/**
* @brief sdrv mbox set master id.
*
* @param[in] base sdrv mbox address
*/
static void sdrv_mbox_set_masterid(paddr_t base)
{
uint32_t i, v;
for (i = 0U; i < SDRV_MBOX_MASTERID_NUM; i++) {
/* set and lock 4 id the same value for each master */
v = ((0x80U + i) << 24U) | ((0x80U + i) << 16U) | ((0x80U +i) << 8U) | (0x80U +i);
sdrv_mbox_writel(base, SDRV_MBOX_MASTERID(i), v);
}
}
static status_t sdrv_mbox_check_masterid(paddr_t base)
{
uint32_t i, j, v;
status_t ret = SDRV_STATUS_OK;
for (i = 0U; i < SDRV_MBOX_MASTERID_NUM; i++) {
/* set 4 id the same value for each master */
v = sdrv_mbox_readl(base, SDRV_MBOX_MASTERID(i));
for (j = 0u; j <= 3U; j ++) {
if ((v & 0xFFU) == 0U) {
ret = SDRV_MBOX_STATUS_INVALID_MASTERID;
break;
}
v = v >> 8U;
}
}
return ret;
}
/**
* @brief sdrv mbox free tx message.
*
* @param[in] base sdrv mbox address
* @param[in] mid message id
* @param[in] mdp remote bitmap
* @param[in] mbm message buffer bitmap
* @param[in] len message len
*/
static void sdrv_mbox_fill_msg_head(paddr_t base, uint8_t mid, uint8_t mdp,
uint8_t mbm, uint16_t len)
{
uint32_t tmh0;
tmh0 = (mid << SDRV_MBOX_TMH0_MID_SHIFT) |
(mdp << SDRV_MBOX_TMH0_MDP_SHIFT) |
(mbm << SDRV_MBOX_TMH0_MBM_SHIFT) |
(((ALIGN(len, 2U)) / 2U) << SDRV_MBOX_TMH0_LEN_SHIFT);
if (mbm != 0U) {
tmh0 |= (1U << SDRV_MBOX_TMH0_MB_SHIFT);
}
sdrv_mbox_writel(base, SDRV_MBOX_TMH0, tmh0);
}
/**
* @brief sdrv mbox fill short msg.
*
* @param[in] base sdrv mbox address
* @param[in] data short msg data
*/
static void sdrv_mbox_fill_short_msg(paddr_t base, uint64_t data)
{
sdrv_mbox_writel(base, SDRV_MBOX_TMH1, (uint32_t)data);
sdrv_mbox_writel(base, SDRV_MBOX_TMH2, (uint32_t)(data >> 32U));
}
/**
* @brief sdrv mbox free tx message.
*
* @param[in] base sdrv mbox address
* @param[in] msg_id message id
*/
static void sdrv_mbox_tx_msg(paddr_t base, uint8_t msg_id)
{
uint32_t tmc;
tmc = sdrv_mbox_readl(base, SDRV_MBOX_TMC(msg_id));
tmc |= (1U << SDRV_MBOX_TMC_SEND_SHIFT);
sdrv_mbox_writel(base, SDRV_MBOX_TMC(msg_id), tmc);
}
/**
* @brief sdrv mbox get rx status.
*
* @param[in] base sdrv mbox address
* @return rx msg id
*/
static uint32_t sdrv_mbox_get_status(paddr_t base)
{
return sdrv_mbox_readl(base, SDRV_MBOX_TMS);
}
/**
* @brief sdrv mbox get rx status.
*
* @param[in] base sdrv mbox address
* @param[in] proc_id processor index
* @param[in] msg_id message id
*/
static void sdrv_mbox_ack(paddr_t base, uint8_t proc_id, uint8_t msg_id)
{
uint32_t rmc;
rmc = sdrv_mbox_readl(base, SDRV_MBOX_RMC);
rmc |= (1U << ((proc_id << 2U) + msg_id));
sdrv_mbox_writel(base, SDRV_MBOX_RMC, rmc);
}
/**
* @brief sdrv mbox get short msg.
*
* @param[in] base sdrv mbox address
* @param[in] proc_id processor index
* @param[in] msg_id message id
* @return short msg data
*/
static uint64_t sdrv_mbox_get_short_msg(paddr_t base, uint8_t proc_id, uint8_t msg_id)
{
uint32_t data_l, data_h;
data_l = sdrv_mbox_readl(base, SDRV_MBOX_RMH(1, proc_id, msg_id));
data_h = sdrv_mbox_readl(base, SDRV_MBOX_RMH(2, proc_id, msg_id));
return ((uint64_t)data_h << 32U) + data_l;
}
/**
* @brief sdrv mbox get msg head.
*
* @param[in] base sdrv mbox address
* @param[in] proc_id processor index
* @param[in] msg_id message id
* @param[out] mbm mbm bitmap
* @return message len
*/
static uint16_t sdrv_mbox_get_msg_head(paddr_t base, uint8_t proc_id, uint8_t msg_id,
uint8_t *mbm)
{
uint32_t rmh0;
rmh0 = sdrv_mbox_readl(base, SDRV_MBOX_RMH(0U, proc_id, msg_id));
*mbm = (rmh0 >> SDRV_MBOX_RMH0_MBM_SHIFT) & SDRV_MBOX_RMH0_MB_MASK;
return (rmh0 & SDRV_MBOX_RMH0_LEN_MASK) * 2U;
}
/**
* @brief sdrv mbox get tx buffer.
*
* @param[in] mem_base sdrv mbox memory address
* @param[in] mb_mask buffer mask
* @return buffer address
*/
static uint8_t *sdrv_mbox_get_txbuf(paddr_t mem_base, uint8_t mb_mask)
{
uint8_t mb_start = 0;
for (mb_start = 0; mb_start < SDRV_MBOX_BUF_NUM; mb_start++) {
if (mb_mask & (0x1U << mb_start)) {
break;
}
}
#if CONFIG_E3
return (uint8_t *)mem_base
+ SDRV_MBOX_TX_BUF(get_core_id())
+ (mb_start * SDRV_MBOX_BANK_LEN);
#else
return (uint8_t *)mem_base
+ SDRV_MBOX_TX_BUF
+ (mb_start * SDRV_MBOX_BANK_LEN);
#endif
}
/**
* @brief sdrv mbox get rx buffer.
*
* @param[in] mem_base sdrv mbox memory address
* @param[in] proc_id processor index
* @param[in] mb_mask buffer mask
* @return buffer address
*/
static uint8_t *sdrv_mbox_get_rxbuf(paddr_t mem_base, uint8_t proc_id, uint8_t mb_mask)
{
uint8_t mb_start = 0;
for (mb_start = 0; mb_start < SDRV_MBOX_BUF_NUM; mb_start++) {
if (mb_mask & (0x1U << mb_start)) {
break;
}
}
/* Becase of the mbox processor index of E3420 (E3430) are 0 (SF0)/3(SX0)/4(SX1)
* and the index of Tx buffer are 0/1/2, it's necessary to convert according to
* the proc_id.
*/
#if (CONFIG_E3420 || CONFIG_E3430)
if (proc_id) {
proc_id >>= 1;
}
#endif
return (uint8_t *)mem_base
+ SDRV_MBOX_RX_BUF(proc_id)
+ (mb_start * SDRV_MBOX_BANK_LEN);
}
/**
* @brief sdrv mbox alloc message
*
* @param[in] sdrv_mbox sdrv mbox
* return alloc msg id
*/
static int sdrv_mbox_alloc_message(sdrv_mbox_t *sdrv_mbox)
{
uint8_t msg_id;
for (msg_id = 0U; msg_id < SDRV_MBOX_MSG_NUM; msg_id++) {
if (!BIT_SET(sdrv_mbox->msg_bitmap, msg_id)) {
sdrv_mbox->msg_bitmap |= (1U << msg_id);
return msg_id;
}
}
return SDRV_MBOX_STATUS_NO_CHANNEL;
}
/**
* @brief sdrv mbox allocate message buffer mask
*
* The message buffer address must be consecutive in totally 4 (SDRV_MBOX_BUF_NUM) buffer banks.
* For example, the value 4'b0110 or 4'b1111 is an valid message buffer mask,
* while the value 4'b0101 is invalid.
*
* @param[in] sdrv_mbox sdrv mbox
* return alloc msg id
*/
static int sdrv_mbox_alloc_mb_mask(sdrv_mbox_t *sdrv_mbox, uint32_t len)
{
uint8_t mb_mask = 0U;
uint8_t mb_start, mb_index;
if (len == 0U) {
return mb_mask;
}
int mb_bank_num = (len - 1) / SDRV_MBOX_BANK_LEN + 1U;
for (mb_start = 0U; mb_start < SDRV_MBOX_BUF_NUM - mb_bank_num + 1; mb_start++) {
for (mb_index = 0; mb_index < mb_bank_num; mb_index++) {
if (!BIT_SET(sdrv_mbox->mb_bitmap, mb_start + mb_index)) {
mb_mask |= (1U << (mb_start + mb_index));
}
else {
mb_mask = 0;
break;
}
}
if (mb_mask) {
sdrv_mbox->mb_bitmap |= mb_mask;
return mb_mask;
}
}
return SDRV_MBOX_STATUS_NO_MSG_BUFFER;
}
/**
* @brief sdrv mbox free message
*
* @param[in] sdrv_mbox sdrv mbox
* @param[in] msg_id msg id
*/
static void sdrv_mbox_free_message(sdrv_mbox_t *sdrv_mbox, uint8_t msg_id, uint8_t mb_mask)
{
sdrv_mbox->msg_bitmap &= ~(1U << msg_id);
if (mb_mask) {
sdrv_mbox->mb_bitmap &= ~mb_mask;
}
}
/**
* @brief sdrv mbox receive message
*
* This function receives mailbox message data.
* It should be called before other core send mailbox data.
*
* @param[in] chan sdrv mbox channel
* @param[in] data msg data
* @param[in] msg len
* @return receive result
*/
int sdrv_mbox_received(sdrv_mbox_chan_t *chan, uint8_t *data, uint32_t len)
{
int ret = 0;
if (chan->callback) {
ret = chan->callback(chan->arg, data, len);
}
return ret;
}
/**
* @brief sdrv mbox receive message data
*
* @param[in] dev mbox dev
* @param[in] proc_id processor index
* @param[in] msg_id msg id
*/
static void sdrv_mbox_get_msg(sdrv_mbox_t *dev, uint8_t proc_id, uint8_t msg_id)
{
sdrv_mbox_chan_t *sdrv_chan;
uint64_t short_data;
uint32_t short_msg;
uint16_t mb_len;
uint8_t mbm;
uint8_t *rx_buf = NULL;
int chan_id;
short_data = sdrv_mbox_get_short_msg(dev->base, proc_id, msg_id);
mb_len = sdrv_mbox_get_msg_head(dev->base, proc_id, msg_id, &mbm);
if (mbm != 0U) {
#if CONFIG_E3
rx_buf = sdrv_mbox_get_rxbuf(dev->mem_base, proc_id, mbm);
if (rx_buf && (mb_len == 0U) && (short_data & 0x1U)) {
mb_len = SDRV_MBOX_BUF_LEN;
}
arch_invalidate_cache_range((addr_t)rx_buf, mb_len);
#else
rx_buf = sdrv_mbox_get_rxbuf(dev->base, proc_id, mbm);
if (rx_buf && (mb_len == 0U) && (short_data & 0x1U)) {
mb_len = SDRV_MBOX_BUF_LEN;
}
#endif
}
for (chan_id = 0; chan_id < SDRV_MBOX_CHN_NUM; chan_id++) {
sdrv_chan = &dev->chan[chan_id];
if ((sdrv_chan->used == true) && (sdrv_chan->rproc & (1U << proc_id))
&& (sdrv_chan->src_addr == SDRV_MBOX_ANY_ADDR
|| sdrv_chan->src_addr == (int16_t)(short_data >> 48U))) {
if (rx_buf) {
sdrv_mbox_received(&dev->chan[chan_id], rx_buf, mb_len);
}
else {
short_msg = (uint32_t)short_data;
sdrv_mbox_received(&dev->chan[chan_id], (uint8_t *)&short_msg, mb_len);
}
}
}
}
/**
* @brief sdrv mbox channel irq handler
*
* @param[in] irq irq number
* @param[in] arg irq arg
* return irq handler result
*/
static int sdrv_mbox_irq_handler(uint32_t irq, void *arg)
{
sdrv_mbox_t *dev = arg;
uint32_t state;
uint8_t proc_id, msg_mask, msg_id;
if (dev == NULL) {
return SDRV_STATUS_INVALID_PARAM;
}
state = sdrv_mbox_get_status(dev->base);
for (proc_id = 0U; proc_id < MBOX_PROC_ID_NUM; proc_id++) {
msg_mask = 0xfU & (state >> (proc_id << 2U));
while (msg_mask) {
msg_id = __builtin_ffs(msg_mask) - 1;
msg_mask &= (msg_mask - 1);
SDRV_MBOX_INFO("recv: %d msg: %d\n", proc_id, msg_id);
sdrv_mbox_get_msg(dev, proc_id, msg_id);
sdrv_mbox_ack(dev->base, proc_id, msg_id);
SDRV_MBOX_INFO("ack: %d msg: %d\n", proc_id, msg_id);
}
}
return SDRV_STATUS_OK;
}
/**
* @brief check sdrv mbox channel tx done
*
* @param[in] chan mbox channel
* return tx done result
*/
static bool sdrv_mbox_tx_done(sdrv_mbox_chan_t *chan)
{
sdrv_mbox_t *dev = chan->dev;
bool ret;
irq_state_t flag;
ret = !((sdrv_mbox_readl(dev->base,
SDRV_MBOX_TMC(chan->msg_id))) & (1U << SDRV_MBOX_TMC_SEND_SHIFT));
if (ret) {
flag = arch_irq_save();
sdrv_mbox_free_message(dev, chan->msg_id, chan->mb_mask);
arch_irq_restore(flag);
}
return ret;
}
/**
* @brief sdrv mbox wait tx complete
*
* @param[in] chan sdrv mbox channel
* @param[in] timeout wait timeout(millisecond), timeout should not be too small.
* @return complete result
*/
static int sdrv_mbox_wait_tx_complete(sdrv_mbox_chan_t *chan, uint32_t timeout)
{
uint64_t wait_time = 0U;
do {
udelay(SDRV_MBOX_TX_POLL);
wait_time += SDRV_MBOX_TX_POLL;
if (wait_time >= (uint64_t)timeout * 1000U) {
return SDRV_MBOX_STATUS_TIMEOUT;
}
}
while (!sdrv_mbox_tx_done(chan));
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox channel cancel send
*
* @param[in] chan mbox channel
*/
static void sdrv_mbox_cancel_send(sdrv_mbox_chan_t *chan)
{
sdrv_mbox_t *dev = chan->dev;
uint32_t tmc;
irq_state_t flag;
tmc = sdrv_mbox_readl(dev->base, SDRV_MBOX_TMC(chan->msg_id));
tmc |= (1U << SDRV_MBOX_TMC_CANCEL_SHIFT);
sdrv_mbox_writel(dev->base, SDRV_MBOX_TMC(chan->msg_id), tmc);
flag = arch_irq_save();
sdrv_mbox_free_message(dev, chan->msg_id, chan->mb_mask);
arch_irq_restore(flag);
}
/**
* @brief sdrv mbox channel send short data
*
* This function is used to send mailbox data without buffers.
* The data length is not more than 4 bytes.
* The mailbox data will be saved in registers and send directly.
*
* @param[in] chan mbox channel
* @param[in] data data ptr
* @param[in] len data length, unit: 2 bytes
* @param[in] timeout wait timeout(millisecond), timeout should not be too small.
* return send result
*/
status_t sdrv_mbox_send_nobuf(sdrv_mbox_chan_t *sdrv_chan, uint8_t *data, uint32_t len, uint32_t timeout)
{
sdrv_mbox_t *dev = sdrv_chan->dev;
uint64_t short_msg = 0U;
int msg_id;
irq_state_t flag;
if (len > SDRV_MBOX_SHORT_BUF_LEN) {
SDRV_MBOX_ERR("send short message length too large!\n");
return SDRV_MBOX_STATUS_LENGTH_ERROR;
}
flag = arch_irq_save();
msg_id = sdrv_mbox_alloc_message(dev);
if (msg_id < 0) {
arch_irq_restore(flag);
SDRV_MBOX_ERR("no mailbox channel!\n");
return SDRV_MBOX_STATUS_NO_CHANNEL;
}
sdrv_chan->msg_id = (uint8_t)msg_id;
sdrv_chan->mb_mask = 0U;
sdrv_mbox_fill_msg_head(dev->base, sdrv_chan->msg_id,
sdrv_chan->rproc, 0U, len);
for (uint8_t i = 0U; i < len; i++) {
short_msg |= ((uint64_t) data[i] << (i * 8U));
}
short_msg |= (uint64_t)sdrv_chan->src_addr << 32U;
short_msg |= (uint64_t)sdrv_chan->dest_addr << 48U;
sdrv_mbox_fill_short_msg(dev->base, short_msg);
sdrv_mbox_tx_msg(dev->base, sdrv_chan->msg_id);
if (timeout == SDRV_MBOX_NO_WAIT) {
sdrv_mbox_free_message(dev, sdrv_chan->msg_id, sdrv_chan->mb_mask);
arch_irq_restore(flag);
return SDRV_STATUS_OK;
}
else {
arch_irq_restore(flag);
if (sdrv_mbox_wait_tx_complete(sdrv_chan, timeout) < 0) {
sdrv_mbox_cancel_send(sdrv_chan);
return SDRV_MBOX_STATUS_TIMEOUT;
}
}
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox channel send data use message buf
*
* This function is used to send mailbox data in standard flow:
* 1. allocate data buffers
* 2. copy data to the buffers
* 3. send data from the buffers
*
* @param[in] chan mbox channel
* @param[in] data data ptr
* @param[in] len data len, unit: 2 bytes
* @param[in] timeout wait timeout(millisecond), timeout should not be too small.
* return send result
*/
status_t sdrv_mbox_send(sdrv_mbox_chan_t *sdrv_chan, uint8_t *data, uint32_t len, uint32_t timeout)
{
sdrv_mbox_t *sdrv_mbox = sdrv_chan->dev;
uint64_t short_msg = 0U;
uint8_t *tx_buf;
int msg_id;
int mb_mask;
irq_state_t flag;
paddr_t mem_base;
if (len > SDRV_MBOX_BUF_LEN) {
SDRV_MBOX_ERR("send message length too large!\n");
return SDRV_MBOX_STATUS_LENGTH_ERROR;
}
flag = arch_irq_save();
msg_id = sdrv_mbox_alloc_message(sdrv_mbox);
if (msg_id < 0) {
arch_irq_restore(flag);
SDRV_MBOX_ERR("no mailbox channel!\n");
return SDRV_MBOX_STATUS_NO_CHANNEL;
}
sdrv_chan->msg_id = (uint8_t)msg_id;
mb_mask = sdrv_mbox_alloc_mb_mask(sdrv_mbox, len);
if (mb_mask < 0) {
sdrv_mbox_free_message(sdrv_mbox, sdrv_chan->msg_id, 0);
arch_irq_restore(flag);
SDRV_MBOX_ERR("no message buffer!\n");
return SDRV_MBOX_STATUS_NO_MSG_BUFFER;
}
sdrv_chan->mb_mask = (uint8_t)mb_mask;
sdrv_mbox_fill_msg_head(sdrv_mbox->base, sdrv_chan->msg_id,
sdrv_chan->rproc,
sdrv_chan->mb_mask, len);
/* A special flag used to check whether length is 2048 * 2 bytes.
Because SDRV_MBOX_TMH0_LEN in TMH0 has only 11 bits,
0 and 2048 are the same value. */
if (len == SDRV_MBOX_BUF_LEN) {
short_msg = 1U;
}
short_msg |= (uint64_t)sdrv_chan->src_addr << 32U;
short_msg |= (uint64_t)sdrv_chan->dest_addr << 48U;
sdrv_mbox_fill_short_msg(sdrv_mbox->base, short_msg);
#if CONFIG_E3
mem_base = sdrv_mbox->mem_base;
#else
mem_base = sdrv_mbox->base;
#endif
tx_buf = sdrv_mbox_get_txbuf(mem_base, sdrv_chan->mb_mask);
memcpy(tx_buf, data, len);
#if CONFIG_E3
arch_clean_cache_range((addr_t)tx_buf, len);
#endif
sdrv_mbox_tx_msg(sdrv_mbox->base, sdrv_chan->msg_id);
if (timeout == SDRV_MBOX_NO_WAIT) {
sdrv_mbox_free_message(sdrv_mbox, sdrv_chan->msg_id, sdrv_chan->mb_mask);
arch_irq_restore(flag);
return SDRV_STATUS_OK;
}
else {
arch_irq_restore(flag);
if (sdrv_mbox_wait_tx_complete(sdrv_chan, timeout) < 0) {
sdrv_mbox_cancel_send(sdrv_chan);
return SDRV_MBOX_STATUS_TIMEOUT;
}
}
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox channel allocate buffer
*
* This function allocates data buffers, and copy mailbox data to the buffers first.
* And then, another function (sdrv_mbox_send_nocopy) is called to send mailbox data.
*
* @param[in] chan mbox channel
* @param[in] len data len, unit: 2 bytes
* return buffer address
*/
uint8_t *sdrv_mbox_alloc_buffer(sdrv_mbox_chan_t *sdrv_chan, uint32_t len)
{
sdrv_mbox_t *dev = sdrv_chan->dev;
uint64_t short_msg = 0U;
uint8_t *tx_buf;
int msg_id;
int mb_mask;
irq_state_t flag;
paddr_t mem_base;
if (len > SDRV_MBOX_BUF_LEN) {
SDRV_MBOX_ERR("send message length too large!\n");
return NULL;
}
flag = arch_irq_save();
msg_id = sdrv_mbox_alloc_message(dev);
if (msg_id < 0) {
arch_irq_restore(flag);
SDRV_MBOX_ERR("no mailbox channel!\n");
return NULL;
}
sdrv_chan->msg_id = (uint8_t)msg_id;
mb_mask = sdrv_mbox_alloc_mb_mask(dev, len);
if (mb_mask < 0) {
sdrv_mbox_free_message(dev, sdrv_chan->msg_id, 0);
arch_irq_restore(flag);
SDRV_MBOX_ERR("no message buffer!\n");
return NULL;
}
sdrv_chan->mb_mask = (uint8_t)mb_mask;
sdrv_mbox_fill_msg_head(dev->base, sdrv_chan->msg_id,
sdrv_chan->rproc,
sdrv_chan->mb_mask, len);
/* A special flag used to check whether length is 2048 * 2 bytes.
Because SDRV_MBOX_TMH0_LEN in TMH0 has only 11 bits,
0 and 2048 are the same value. */
if (len == SDRV_MBOX_BUF_LEN) {
short_msg = 1U;
}
short_msg |= (uint64_t)sdrv_chan->src_addr << 32U;
short_msg |= (uint64_t)sdrv_chan->dest_addr << 48U;
sdrv_mbox_fill_short_msg(dev->base, short_msg);
#if CONFIG_E3
mem_base = dev->mem_base;
#else
mem_base = dev->base;
#endif
tx_buf = sdrv_mbox_get_txbuf(mem_base, sdrv_chan->mb_mask);
arch_irq_restore(flag);
return tx_buf;
}
/**
* @brief sdrv mbox channel send data no copy
*
* This function is used to send mailbox data without repeating to copy data.
* This function is combined with the function sdrv_mbox_alloc_buffer,
* sending the data which have been stored in the buffer.
*
* @param[in] chan mbox channel
* @param[in] data data ptr
* @param[in] len data len, unit: 2 bytes
* @param[in] timeout wait timeout(millisecond), timeout should not be too small.
* @return send result
*/
status_t sdrv_mbox_send_nocopy(sdrv_mbox_chan_t *chan, uint8_t *data, uint32_t len, uint32_t timeout)
{
sdrv_mbox_t *dev = chan->dev;
irq_state_t flag;
flag = arch_irq_save();
#if CONFIG_E3
arch_clean_cache_range((addr_t)data, len);
#endif
sdrv_mbox_tx_msg(dev->base, chan->msg_id);
if (timeout == SDRV_MBOX_NO_WAIT) {
sdrv_mbox_free_message(dev, chan->msg_id, chan->mb_mask);
arch_irq_restore(flag);
return SDRV_STATUS_OK;
}
else {
arch_irq_restore(flag);
if (sdrv_mbox_wait_tx_complete(chan, timeout) < 0) {
sdrv_mbox_cancel_send(chan);
return SDRV_MBOX_STATUS_TIMEOUT;
}
}
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox channel startup
*
* @param[in] chan mbox channel
* @param[in] chan_req mbox channel request param
* return startup result
*/
static bool sdrv_mbox_startup(sdrv_mbox_chan_t *chan, sdrv_mbox_chan_req_t *chan_req)
{
if ((chan_req->rproc >= (1U << MBOX_PROC_ID_NUM)) || (chan_req->rproc == 0U)) {
return false;
}
chan->rproc = chan_req->rproc;
chan->src_addr = chan_req->src_addr;
chan->dest_addr = chan_req->dest_addr;
chan->used = true;
return true;
}
/**
* @brief sdrv mbox channel shutdown
*
* @param[in] chan mbox channel
*/
static void sdrv_mbox_shutdown(sdrv_mbox_chan_t *chan)
{
chan->used = false;
}
/**
* @brief sdrv mbox client request channel
*
* @param[in] dev mbox dev
* @param[in] chan_req mbox channel request param
* @return mbox channel
*/
sdrv_mbox_chan_t *sdrv_mbox_request_channel(sdrv_mbox_t *dev, sdrv_mbox_chan_req_t *chan_req)
{
sdrv_mbox_chan_t *find_chan = NULL;
uint8_t cnt;
for (cnt = 0U; cnt < dev->chan_num; cnt++) {
sdrv_mbox_chan_t *chan = &dev->chan[cnt];
if (!chan->used) {
if (!sdrv_mbox_startup(chan, chan_req)) {
SDRV_MBOX_ERR("error remote processor bitmap!\r\n");
return NULL;
}
chan->callback = NULL;
chan->arg = NULL;
chan->used = true;
find_chan = chan;
break;
}
}
return find_chan;
}
/**
* @brief sdrv mbox release channel
*
* @param[in] chan sdrv mbox chan
* @return SDRV_STATUS_OK or error code
*/
status_t sdrv_mbox_release_channel(sdrv_mbox_chan_t *chan)
{
sdrv_mbox_shutdown(chan);
chan->callback = NULL;
chan->arg = NULL;
chan->used = false;
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox set callback
*
* This function sets mailbox callback function.
*
* @param[in] chan sdrv mbox channel
* @param[in] callback callback function
* @param[in] arg callback argument
* @return SDRV_STATUS_OK or error code
*/
status_t sdrv_mbox_set_callback(sdrv_mbox_chan_t *chan, sdrv_mbox_rxcallback callback, void *arg)
{
chan->callback = callback;
chan->arg = arg;
return SDRV_STATUS_OK;
}
/**
* @brief sdrv mbox get mtu
*
* This function gets maximum transmission unit.
*
* @param[in] dev sdrv mbox dev
* @return mtu
*/
uint32_t sdrv_mbox_mtu(sdrv_mbox_t *dev)
{
return dev->mtu;
}
/**
* @brief sdrv mbox init
*
* This function initializes mailbox device.
*
* @param[in] dev sdrv mbox device
* @param[in] cfg sdrv mbox configuration
*/
status_t sdrv_mbox_init(sdrv_mbox_t *dev, const sdrv_mbox_config_t *cfg)
{
dev->base = cfg->base;
dev->mem_base = cfg->mem_base;
dev->irq = cfg->irq;
dev->chan_num = SDRV_MBOX_CHN_NUM;
for (uint8_t cnt = 0U; cnt < dev->chan_num; cnt++) {
sdrv_mbox_chan_t *chan = &dev->chan[cnt];
chan->dev = dev;
chan->used = false;
}
dev->mtu = SDRV_MBOX_BUF_LEN;
dev->msg_bitmap = 0U;
dev->mb_bitmap = 0U;
if (get_core_id() == CORE_SF) {
sdrv_mbox_set_masterid(dev->base);
}
else {
if (sdrv_mbox_check_masterid(dev->base) != SDRV_STATUS_OK) {
SDRV_MBOX_ERR("no sdrv mailbox master id!\r\n");
return SDRV_MBOX_STATUS_INVALID_MASTERID;
}
}
#if CONFIG_E3
paddr_t tx_buf_base = dev->mem_base + SDRV_MBOX_TX_BUF(get_core_id());
sdrv_mbox_buff_clr(tx_buf_base, SDRV_MBOX_BUF_LEN);
arch_clean_cache_range((addr_t)tx_buf_base, SDRV_MBOX_BUF_LEN);
#else
sdrv_mbox_buff_clr(dev->base + SDRV_MBOX_TX_BUF_DIFF, SDRV_MBOX_BUF_LEN);
#endif
if (dev->irq >= 0) {
irq_attach((uint32_t)(dev->irq), sdrv_mbox_irq_handler, dev);
irq_enable((uint32_t)(dev->irq));
}
return SDRV_STATUS_OK;
}