IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/MBox.c (446 lines of code) (raw):

/* Copyright (c) Codethink Ltd. All rights reserved. Licensed under the MIT License. */ #include "CPUFreq.h" #include "MBox.h" #include "NVIC.h" #include "mt3620/mbox.h" #include <stdbool.h> #include <stddef.h> #define MBOX_MAX_INDEX (MT3620_UNIT_MBOX_CA7 + MT3620_UNIT_MBOX_COUNT - 1) #define MBOX_DEFAULT_PRIORITY 2 struct MBox { bool fifo_open; Platform_Unit unit; void (*rx_cb) (void*); void (*tx_confirmed_cb) (void*); void (*fifo_state_change_cb)(void*, MBox_FIFO_State state); void *user_data; int8_t non_full_threshold; int8_t non_empty_threshold; void (*sw_int_cb) (void*, uint8_t port); }; static MBox context[MT3620_UNIT_MBOX_COUNT] = {0}; static inline int32_t MBox__Get_Index(Platform_Unit unit) { int32_t index = unit - MT3620_UNIT_MBOX_CA7; return ((index > MBOX_MAX_INDEX) || (index < 0)) ? -1 : index; } static void MBox_FIFO__Toggle_Interrupts(MBox *handle, bool enable) { if (!handle) { return; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return; } MT3620_MBOX_FIELD_WRITE( index, mbox_int_en, int_fifo_wr, enable && !!handle->rx_cb); MT3620_MBOX_FIELD_WRITE( index, mbox_int_en, int_fifo_rd, enable && !!handle->tx_confirmed_cb); MT3620_MBOX_FIELD_WRITE( index, mbox_int_en, int_fifo_nf, enable && !!handle->fifo_state_change_cb); MT3620_MBOX_FIELD_WRITE( index, mbox_int_en, int_fifo_ne, enable && !!handle->fifo_state_change_cb); if (enable && handle->rx_cb) { NVIC_EnableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_RX), MBOX_DEFAULT_PRIORITY); } else { NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT(index, MT3620_MBOX_INT_RX)); } if (enable && handle->tx_confirmed_cb) { NVIC_EnableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_CONFIRMED), MBOX_DEFAULT_PRIORITY); } else { NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_CONFIRMED)); } if (enable && handle->fifo_state_change_cb) { NVIC_EnableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_FIFO_NF), MBOX_DEFAULT_PRIORITY); NVIC_EnableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_FIFO_NE), MBOX_DEFAULT_PRIORITY); } else { NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_FIFO_NF)); NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_TX_FIFO_NE)); } } MBox* MBox_FIFO_Open( Platform_Unit unit, void (*rx_cb) (void*), void (*tx_confirmed_cb) (void*), void (*fifo_state_change_cb)(void*, MBox_FIFO_State state), void *user_data, int8_t non_full_threshold, int8_t non_empty_threshold) { int32_t index = MBox__Get_Index(unit); if (index == -1) { return NULL; } MBox *handle = &context[index]; if (handle->fifo_open) { return NULL; } // TODO: determine if MB needs a reset on open MBox_FIFO_Reset(handle, true); handle->fifo_open = true; handle->unit = unit; handle->rx_cb = rx_cb; handle->tx_confirmed_cb = tx_confirmed_cb; handle->fifo_state_change_cb = fifo_state_change_cb; handle->user_data = user_data; handle->non_full_threshold = non_full_threshold; handle->non_empty_threshold = non_empty_threshold; // Configure Thresholds if ((non_full_threshold > 0) && (non_full_threshold <= MT3620_MBOX_FIFO_COUNT_MAX)) { mt3620_mbox[index]->mbox_nf_thrs = non_full_threshold; } if ((non_empty_threshold > 0) && (non_empty_threshold <= MT3620_MBOX_FIFO_COUNT_MAX)) { mt3620_mbox[index]->mbox_ne_thrs = non_empty_threshold; } // Configure Interrupts MBox_FIFO__Toggle_Interrupts(handle, true); return handle; } void MBox_FIFO_Close(MBox *handle) { if (!handle || !handle->fifo_open) { return; } // TODO: is this required? MBox_FIFO_Reset(handle, false); handle->fifo_open = false; handle->rx_cb = NULL; handle->tx_confirmed_cb = NULL; handle->fifo_state_change_cb = NULL; handle->non_full_threshold = -1; handle->non_empty_threshold = -1; MBox_FIFO__Toggle_Interrupts(handle, false); } void MBox_FIFO_Reset(MBox *handle, bool both) { if (!handle) { return; } // Note that the MB on the partner core must also be reset int32_t index = MBox__Get_Index(handle->unit); if (index != -1) { if (both) { MT3620_MBOX_FIELD_WRITE( index, mbox_gen_ctrl, soft_rst, 1); } else { MT3620_MBOX_FIELD_WRITE( index, mbox_gen_ctrl, soft_rst_myself, 1); } } } int32_t MBox_FIFO_Write( MBox *handle, const uint32_t *cmd, const uint32_t *data, uintptr_t length) { if (!handle || !handle->fifo_open || length > MT3620_MBOX_FIFO_COUNT_MAX) { return ERROR_PARAMETER; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } if (length > (MT3620_MBOX_FIFO_COUNT_MAX - mt3620_mbox[index]->fifo_push_cnt)) { return ERROR_MBOX_FIFO_INSUFFICIENT_SPACE; } // CMD write increments the write ptr, so we write any DATA first for (unsigned i = 0; i < length; i++) { if (data) { mt3620_mbox[index]->data_push = data[i]; } mt3620_mbox[index]->cmd_push = cmd[i]; } return ERROR_NONE; } int32_t MBox_FIFO_ReadSync( MBox *handle, uint32_t *cmd, uint32_t *data, uintptr_t length) { if (!handle || !handle->fifo_open || length > MT3620_MBOX_FIFO_COUNT_MAX) { return ERROR_PARAMETER; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } for (unsigned i = 0; i < length; i++) { // wait until data is available to read while (mt3620_mbox[index]->fifo_pop_cnt == 0); // As above, CMD read decrements the read ptr, so we read DATA first if (data) { data[i] = mt3620_mbox[index]->data_pop; } cmd[i] = mt3620_mbox[index]->cmd_pop; } return ERROR_NONE; } uintptr_t MBox_FIFO_Writes_Pending(const MBox *handle) { if (!handle || !handle->fifo_open) { return 0; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return 0; } return mt3620_mbox[index]->fifo_push_cnt; } uintptr_t MBox_FIFO_Reads_Available(const MBox *handle) { if (!handle || !handle->fifo_open) { return 0; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return 0; } return mt3620_mbox[index]->fifo_pop_cnt; } /// Semaphore int32_t MBox_Semaphore_Acquire(MBox* handle) { if (!handle) { return ERROR_PARAMETER; } if (handle->unit == MT3620_UNIT_MBOX_CM4) { return ERROR_MBOX_SEMAPHORE_NOT_PRESENT; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } if (mt3620_mbox[index]->semaphore_p != 1U) { return ERROR_MBOX_SEMAPHORE_REQUEST_DENIED; } else { return ERROR_NONE; } } int32_t MBox_Semaphore_Release(MBox* handle) { if (!handle) { return ERROR_PARAMETER; } if (handle->unit == MT3620_UNIT_MBOX_CM4) { return ERROR_MBOX_SEMAPHORE_NOT_PRESENT; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } if (mt3620_mbox[index]->semaphore_p != 1U) { return ERROR_MBOX_SEMAPHORE_REQUEST_DENIED; } else { mt3620_mbox[index]->semaphore_p = 0; return ERROR_NONE; } } /// SW Interrupts int32_t MBox_SW_Interrupt_Setup( MBox *handle, uint8_t int_enable_flags, void (*sw_int_cb)(void*, uint8_t port)) { if (!handle) { return ERROR_PARAMETER; } handle->sw_int_cb = sw_int_cb; int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } mt3620_mbox[index]->sw_rx_int_en = int_enable_flags; // Configure Interrupts if (sw_int_cb) { NVIC_EnableIRQ(MT3620_MBOX_INTERRUPT( index, MT3620_MBOX_INT_SW_INT), MBOX_DEFAULT_PRIORITY); } else { NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT(index, MT3620_MBOX_INT_SW_INT)); } return ERROR_NONE; } void MBox_SW_Interrupt_Teardown(MBox *handle) { if (!handle) { return; } handle->sw_int_cb = NULL; int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return; } mt3620_mbox[index]->sw_rx_int_en = 0; NVIC_DisableIRQ(MT3620_MBOX_INTERRUPT(index, MT3620_MBOX_INT_SW_INT)); } int32_t MBox_SW_Interrupt_Trigger(MBox *handle, uint8_t port) { if (!handle || (port >= MBOX_SW_INT_PORT_COUNT)) { return ERROR_PARAMETER; } int32_t index = MBox__Get_Index(handle->unit); if (index == -1) { return ERROR_HARDWARE_STATE; } mt3620_mbox[index]->sw_tx_int_port = 1U << port; return ERROR_NONE; } /// IRQs static void MBox_IRQ(MBox *handle, mt3620_mbox_int cb_type) { if (!handle || !handle->fifo_open) { return; } switch (cb_type) { case MT3620_MBOX_INT_RX: if (handle->rx_cb) { handle->rx_cb(handle->user_data); } break; case MT3620_MBOX_INT_TX_CONFIRMED: if (handle->tx_confirmed_cb) { handle->tx_confirmed_cb(handle->user_data); } break; case MT3620_MBOX_INT_TX_FIFO_NF: if (handle->fifo_state_change_cb) { handle->fifo_state_change_cb( handle->user_data, MBOX_FIFO_STATE_NOT_FULL); } break; case MT3620_MBOX_INT_TX_FIFO_NE: if (handle->fifo_state_change_cb) { handle->fifo_state_change_cb( handle->user_data, MBOX_FIFO_STATE_NOT_EMPTY); } break; case MT3620_MBOX_INT_SW_INT: { if (!handle->sw_int_cb) { break; } uint8_t flags = (mt3620_mbox[MBox__Get_Index(handle->unit)]->sw_rx_int_sts) & (mt3620_mbox[MBox__Get_Index(handle->unit)]->sw_rx_int_en); for (uint8_t port = 0; port < MBOX_SW_INT_PORT_COUNT; port++, flags >>= port) { if (flags & 1U) { handle->sw_int_cb(handle->user_data, port); } } break; } default: break; } } static inline void mbox_rd_int(int32_t index) { if (MT3620_MBOX_FIELD_READ(index, mbox_int_en, int_fifo_rd) && MT3620_MBOX_FIELD_READ(index, mbox_int_sts, int_fifo_rd)) { MBox *handle = &(context[index]); MBox_IRQ(handle, MT3620_MBOX_INT_TX_CONFIRMED); } MT3620_MBOX_FIELD_WRITE(index, mbox_int_sts, int_fifo_rd, 1); } static inline void mbox_nf_int(int32_t index) { if (MT3620_MBOX_FIELD_READ(index, mbox_int_en, int_fifo_nf) && MT3620_MBOX_FIELD_READ(index, mbox_int_sts, int_fifo_nf)) { MBox *handle = &(context[MBox__Get_Index(MT3620_UNIT_MBOX_CA7)]); MBox_IRQ(handle, MT3620_MBOX_INT_TX_FIFO_NF); } MT3620_MBOX_FIELD_WRITE(index, mbox_int_sts, int_fifo_nf, 1); } static inline void mbox_wr_int(int32_t index) { if (MT3620_MBOX_FIELD_READ(index, mbox_int_en, int_fifo_wr) && MT3620_MBOX_FIELD_READ(index, mbox_int_sts, int_fifo_wr)) { MBox *handle = &(context[MBox__Get_Index(MT3620_UNIT_MBOX_CA7)]); MBox_IRQ(handle, MT3620_MBOX_INT_RX); } MT3620_MBOX_FIELD_WRITE(index, mbox_int_sts, int_fifo_wr, 1); } static inline void mbox_ne_int(int32_t index) { if (MT3620_MBOX_FIELD_READ(index, mbox_int_en, int_fifo_ne) && MT3620_MBOX_FIELD_READ(index, mbox_int_sts, int_fifo_ne)) { MBox *handle = &(context[MBox__Get_Index(MT3620_UNIT_MBOX_CA7)]); MBox_IRQ(handle, MT3620_MBOX_INT_TX_FIFO_NE); } MT3620_MBOX_FIELD_WRITE(index, mbox_int_sts, int_fifo_ne, 1); } static inline void mbox_sw_int(int32_t index) { if ((mt3620_mbox[index]->sw_rx_int_en & mt3620_mbox[index]->sw_rx_int_sts) != 0) { MBox *handle = &(context[MBox__Get_Index(MT3620_UNIT_MBOX_CA7)]); MBox_IRQ(handle, MT3620_MBOX_INT_SW_INT); } // Reset interrupt flags mt3620_mbox[index]->sw_rx_int_sts = 0xFF; } /// M4 <-> A7 Mailbox void cm4_mbox_m4a2a7n_rd_int(void) { mbox_rd_int(MBox__Get_Index(MT3620_UNIT_MBOX_CA7)); } void cm4_mbox_m4a2a7n_nf_int(void) { mbox_nf_int(MBox__Get_Index(MT3620_UNIT_MBOX_CA7)); } void cm4_mbox_a7n2m4a_wr_int(void) { mbox_wr_int(MBox__Get_Index(MT3620_UNIT_MBOX_CA7)); } void cm4_mbox_a7n2m4a_ne_int(void) { mbox_ne_int(MBox__Get_Index(MT3620_UNIT_MBOX_CA7)); } void cm4_mbox_a7n_fifo_int(void) { // TODO: find if is this triggered for all of above & implement } void cm4_mbox_a7n2m4a_sw_int(void) { mbox_sw_int(MBox__Get_Index(MT3620_UNIT_MBOX_CA7)); } /// M4 <-> M4 Mailbox void cm4_mbox_m4a2m4b_rd_int(void) { mbox_rd_int(MBox__Get_Index(MT3620_UNIT_MBOX_CM4)); } void cm4_mbox_m4a2m4b_nf_int(void) { mbox_nf_int(MBox__Get_Index(MT3620_UNIT_MBOX_CM4)); } void cm4_mbox_m4b2m4a_wr_int(void) { mbox_wr_int(MBox__Get_Index(MT3620_UNIT_MBOX_CM4)); } void cm4_mbox_m4b2m4a_ne_int(void) { mbox_ne_int(MBox__Get_Index(MT3620_UNIT_MBOX_CM4)); } void cm4_mbox_m4b_fifo_int(void) { // TODO: find if is this triggered for all of above & implement } void cm4_mbox_m4b2m4a_sw_int(void) { mbox_sw_int(MBox__Get_Index(MT3620_UNIT_MBOX_CM4)); }