1105 lines
28 KiB
C
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;
|
|
}
|