/** * @file sdrv_mbox.c * * Copyright (c) 2022 Semidrive Semiconductor. * All rights reserved. * * Description: mbox driver file * * Revision History: * ----------------- */ #include #include #include #include #include #include #include #include #include #include "sdrv_mbox.h" #include #include #include /** @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; }