openmv/lib/alif/drivers/source/mhu_sender.c
iabdalkader daf2bb30da misc: Restructure repo.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-04-13 08:28:34 +02:00

298 lines
8.1 KiB
C

/* Copyright (C) 2022 Alif Semiconductor - All Rights Reserved.
* Use, distribution and modification of this code is permitted under the
* terms stated in the Alif Semiconductor Software License Agreement
*
* You should have received a copy of the Alif Semiconductor Software
* License Agreement with this file. If not, please write to:
* contact@alifsemi.com, or visit: https://alifsemi.com/license
*
*/
/**
* @file mhu_sender.c
* @brief MHU sender driver
* @ingroup host_services
* @par
*/
#include "stddef.h"
#include "drivers_common.h"
#include "mhu_driver.h"
#define MHU_RECEIVER_TIMEOUT_MAX 1000000
static MHU_sender_callback s_nr2r_callback;
static MHU_sender_callback s_r2nr_callback;
static MHU_send_msg_acked_callback_t s_chcomb_callback;
static uint32_t s_sender_frame_base_address_list[MHU_NUMBER_MAX];
static uint32_t s_sender_frame_count = 0;
/**
* @fn static bool sender_id_valid(uint32_t sender_id)
* @param sender_id
* @return
*/
static bool sender_id_valid(uint32_t sender_id)
{
return sender_id < s_sender_frame_count;
}
/**
* @brief This functions returns the sender frame base address
* @param[in] sender_id Sender frame id
* @return Sender frame base address
*/
static MHU_sender_frame_register_t * get_sender_frame(uint32_t sender_id)
{
uint32_t sender_address = s_sender_frame_base_address_list[sender_id];
return (MHU_sender_frame_register_t *)sender_address;
}
/**
* @fn static void sender_request_access(uint32_t sender_id)
* @param sender_id
*/
static void sender_request_access(uint32_t sender_id)
{
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
uint32_t address = (uint32_t)&sender_reg_base->ACCESS_REQUEST;
SET_REGISTER_BITS_U32(address, MHU_ACC_REQ);
}
/**
* @fn static void sender_release_access(uint32_t sender_id)
* @param sender_id Sender frame id
*/
static void sender_release_access(uint32_t sender_id)
{
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
uint32_t address = (uint32_t)&sender_reg_base->ACCESS_REQUEST;
CLEAR_REGISTER_BITS_U32(address, MHU_ACC_REQ);
}
/**
* @brief Function sets MHU sender irq enable
* @param sender_reg_base
* @param mask
* @param enable
* @return None
*/
static void set_irq_enable(MHU_sender_frame_register_t * sender_reg_base,
uint32_t mask, bool enable)
{
if (enable)
{
SET_REGISTER_BITS_U32(&sender_reg_base->INT_EN, mask);
}
else
{
CLEAR_REGISTER_BITS_U32(&sender_reg_base->INT_EN, mask);
}
}
/**
* @brief Function clear MHU sender irq status
* @param sender_reg_base
* @param mask
* @return None
*/
static void clear_irq(MHU_sender_frame_register_t * sender_reg_base,
uint32_t mask)
{
WRITE_REGISTER_U32(&sender_reg_base->INT_CLR, mask);
}
/**
* @brief Function returns access ready status
* @param sender_id Sender frame ID
* @return ACCESS_READY status
*/
static bool sender_is_access_ready(uint32_t sender_id)
{
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
return (sender_reg_base->ACCESS_READY & MHU_ACC_RDY) == MHU_ACC_RDY;
}
/**
* @brief Interrupt handler for send message
* @param sender_id MHU frame ID
* @return None
*/
void MHU_send_message_irq_handler(uint32_t sender_id)
{
if (!sender_id_valid(sender_id))
{
return;
}
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
uint32_t status = sender_reg_base->INT_ST;
uint32_t chcomb_status;
if (status & MHU_NR2R)
{
// NOT READY to READY (ACC_RDY = 1)
debug_print("IRQ: Sender id 0x%x : MHU_NR2R\n", sender_id);
clear_irq(sender_reg_base, MHU_NR2R);
if (NULL != s_nr2r_callback)
{
s_nr2r_callback();
}
}
if (status & MHU_R2NR)
{
// READY to NOT READY (ACC_RDY = 0)
debug_print("IRQ: Sender id 0x%x : MHU_R2NR\n", sender_id);
clear_irq(sender_reg_base, MHU_R2NR);
if (NULL != s_r2nr_callback)
{
s_r2nr_callback();
}
}
if (status & MHU_CHCOMB)
{
// Send OS message for Channel combined interrupt
const volatile uint32_t * status_reg_base = &sender_reg_base->CH_INT_ST0;
chcomb_status = status_reg_base[MHU_CHANNEL_COMBINED_CH0_31];
for(uint32_t channel = 0; channel < 2; channel++)
{
if (chcomb_status & (1 << channel))
{
// clear the channel status interrupt
WRITE_REGISTER_U32(&sender_reg_base->CHANNEL[channel].CH_INT_CLR, MHU_CH_CLR);
// To know when the transfer has been processed by the Receiver, the sender must wait for all bits in the
// CH_ST register to read as 0b0.
if (sender_reg_base->CHANNEL[channel].CH_ST == 0 && (sender_reg_base->CHANNEL[channel].CH_INT_EN & MHU_CH_CLR)) {
// Disable channel interrupt
CLEAR_REGISTER_BITS_U32(&sender_reg_base->CHANNEL[channel].CH_INT_EN, MHU_CH_CLR);
sender_release_access(sender_id);
// Invoke the user callback
s_chcomb_callback(sender_id, channel);
}
}
}
}
}
/**
* @brief
* @note This function is called in the services library. The compiler
* tries to optimize it out as it is not called within SERAM
* @return
*/
mhu_send_status_t MHU_send_message(uint32_t sender_id,
uint32_t channel_number,
uint32_t message_data) __attribute__((used));
/**
* @brief This functions sends a message out on a channel
* @param sender_id
* @param channel_number
* @param message_data
* @return
*/
mhu_send_status_t MHU_send_message(uint32_t sender_id,
uint32_t channel_number,
uint32_t message_data)
{
if (!sender_id_valid(sender_id))
{
return MHU_SEND_FAILED;
}
uint32_t timeout;
if (channel_number >= MHU_CHANNELS)
{
debug_print("[MHU] Incorrect channel number\n");
return MHU_SEND_FAILED;
}
/*
* @todo All the wait states below should be replaced by timer OS messages
* sent from corresponding ISRs
* 1. ACCESS_READY
* 2. ACCESS not READY
* 3. MESSAGE ACKed
* 4. MESSAGE NACKed
*/
uint32_t ch_num = channel_number;
// Request access
sender_request_access(sender_id);
// Wait for access
for (timeout = MHU_RECEIVER_TIMEOUT_MAX; timeout > 0; timeout--)
{
if (sender_is_access_ready(sender_id))
{
break;
}
}
if (timeout == 0)
{
debug_print("[MHU] Receiver is busy\n");
// Request access timeout
sender_release_access(sender_id);
return MHU_SEND_RECEIVER_BUSY;
}
else
{
debug_print("[MHU] Request granted\n");
}
// Receiver is ready for data transfer from sender
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
// Enable channel interrupt
WRITE_REGISTER_U32(&sender_reg_base->CHANNEL[ch_num].CH_INT_CLR, MHU_CH_CLR);
WRITE_REGISTER_U32(&sender_reg_base->CHANNEL[ch_num].CH_INT_EN, MHU_CH_CLR);
// Write message to send channel
sender_reg_base->CHANNEL[ch_num].CH_SET = message_data;
return MHU_SEND_OK;
}
/**
*
* @param sender_frame_base_address_list
* @param sender_frame_count
* @param msg_acked_callback
*/
void MHU_sender_initialize(uint32_t sender_frame_base_address_list[],
uint32_t sender_frame_count,
MHU_send_msg_acked_callback_t msg_acked_callback)
{
for (uint32_t i = 0; i < sender_frame_count; i++)
{
s_sender_frame_base_address_list[i] = sender_frame_base_address_list[i];
}
s_sender_frame_count = sender_frame_count;
s_nr2r_callback = NULL;
s_r2nr_callback = NULL;
s_chcomb_callback = msg_acked_callback;
uint32_t mask_disable = (MHU_CHCOMB | MHU_R2NR | MHU_NR2R);
uint32_t mask_enable = (MHU_CHCOMB | MHU_R2NR | MHU_NR2R);
for (uint32_t sender_id = 0; sender_id < sender_frame_count; sender_id++)
{
MHU_sender_frame_register_t * sender_reg_base = get_sender_frame(sender_id);
clear_irq(sender_reg_base, mask_disable);
set_irq_enable(sender_reg_base, mask_disable, false);
set_irq_enable(sender_reg_base, mask_enable, true);
}
}