Files
mongoose/tutorials/nxp/frdm-rw612-xpresso-freertos-lwip/drivers/fsl_gdma.c
T

372 lines
13 KiB
C

/*
* Copyright 2021-2023 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_gdma.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.gdma"
#endif
/* Is address aligned? */
#define GDMA_IS_ADDR_ALIGNED(addr, aligned_size) (((uint32_t)(addr) & ((aligned_size)-1UL)) == 0U)
/* Is the wrap used? */
#define GDMA_IS_WRAP_BURST(burstsize) (((uint8_t)(burstsize)&0x04U) != 0U)
/* Get real width from enum gdma_transfer_width_t. */
#define GDMA_REAL_XFER_WIDTH(width) (s_gdmaRealWidth[(uint8_t)(width)&0x03U])
/* Get real burst size from gdma_burst_size_t. */
#define GDMA_REAL_XFER_BUSTSIZE(burstsize) (s_gdmaRealBurstSize[(uint8_t)(burstsize)&0x03U])
/*******************************************************************************
* Prototypes
******************************************************************************/
/* Typedef for interrupt handler. */
typedef void (*gdma_isr_t)(GDMA_Type *base);
/*!
* @brief Verify the configuration.
*
* Verify the configuration, to make sure the parameters are valid.
*
* @param config Pointer to the transfer configuration.
* @return Return true if the configuration is valid, otherwise return false.
*/
static bool GDMA_VerifyTransferConfig(const gdma_channel_xfer_config_t *config);
/*******************************************************************************
* Variables
******************************************************************************/
/* @brief Pointers to transfer handle for each GDMA channel. */
static gdma_handle_t *s_gdmaHandles[FSL_FEATURE_GDMA_CHANNEL_NUM];
static const uint8_t s_gdmaRealBurstSize[] = {1U, 4U, 8U, 16U};
static const uint8_t s_gdmaRealWidth[] = {1U, 1U, 2U, 4U};
/* ISR for transactional APIs. */
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
static gdma_isr_t s_gdmaIsr = (gdma_isr_t)DefaultISR;
#else
static gdma_isr_t s_gdmaIsr;
#endif
/*******************************************************************************
* Code
******************************************************************************/
/*
* brief Verify the configuration.
*
* Verify the configuration, to make sure the parameters are valid.
*
* param config Pointer to the transfer configuration.
* return Return true if the configuration is valid, otherwise return false.
*/
static bool GDMA_VerifyTransferConfig(const gdma_channel_xfer_config_t *config)
{
bool ret = false;
uint8_t srcWidth = GDMA_REAL_XFER_WIDTH(config->srcWidth);
uint8_t destWidth = GDMA_REAL_XFER_WIDTH(config->destWidth);
uint8_t srcBurstSize = GDMA_REAL_XFER_BUSTSIZE(config->srcBurstSize);
uint8_t destBurstSize = GDMA_REAL_XFER_BUSTSIZE(config->destBurstSize);
uint8_t srcAddrAlignSize;
uint8_t destAddrAlignSize;
do
{
/* SRCBSIZE * SRCWIDTH == DESTBSIZE * DESTWIDTH */
if ((srcWidth * srcBurstSize) != (destWidth * destBurstSize))
{
break;
}
/*
* Address alignment:
*
* From GDMA's view, the address only need to be aligned with the WIDTH (no matter wrap used or not).
* When integrating with AHB and wrap is used, the address should be aligned to WIDTH * BURST_SIZE.
*/
srcAddrAlignSize = (GDMA_IS_WRAP_BURST(config->srcBurstSize)) ? (srcWidth * srcBurstSize) : srcWidth;
destAddrAlignSize = (GDMA_IS_WRAP_BURST(config->destBurstSize)) ? (destWidth * destBurstSize) : destWidth;
if (!((GDMA_IS_ADDR_ALIGNED(config->srcAddr, srcAddrAlignSize)) &&
(GDMA_IS_ADDR_ALIGNED(config->destAddr, destAddrAlignSize))))
{
break;
}
ret = true;
} while (false);
return ret;
}
/*
* brief Initializes GDMA peripheral.
*
* This function enable the GDMA clock, set descriptor table and
* enable GDMA peripheral.
*
* param base GDMA peripheral base address.
*/
void GDMA_Init(GDMA_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
CLOCK_EnableClock(kCLOCK_Gdma);
#endif
}
/*
* brief Deinitializes GDMA peripheral.
*
* This function gates the GDMA clock.
*
* param base GDMA peripheral base address.
*/
void GDMA_Deinit(GDMA_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
CLOCK_DisableClock(kCLOCK_Gdma);
#endif
}
/*
* brief Set channel transfer configuration..
*
* This function configures the channel transfer, after configured, GDMA_StartChannel
* could be called to start the transfer.
*
* This function must be called when previous transfer finished. Application can use
* GDMA_IsChannelBusy to check whether the channel has finished the previous work.
*
* param base GDMA base address.
* param channel GDMA channel number.
* config Pointer to the transfer configuration.
* retval kStatus_Fail GDMA is busy with previous transfer.
* retval kStatus_Success Configuration set successfully.
* retval kStatus_InvalidArgument Configuration wrong.
*/
status_t GDMA_SetChannelTransferConfig(GDMA_Type *base, uint8_t channel, const gdma_channel_xfer_config_t *config)
{
assert(NULL != config);
if (!GDMA_VerifyTransferConfig(config))
{
return kStatus_InvalidArgument;
}
if (GDMA_IsChannelBusy(base, channel))
{
return kStatus_Fail;
}
GDMA_ClearChannelInterruptFlags(base, channel, (uint32_t)kGDMA_AllInterruptFlag);
base->CH[channel].SADR = config->srcAddr;
base->CH[channel].DADR = config->destAddr;
base->CH[channel].CTRL =
GDMA_CTRL_PROT(config->ahbProt) /* Protection info for AHB master bus */
| (config->srcAddrInc ? GDMA_CTRL_SRCADDRINC_MASK : 0UL) /* Source address increment. */
| (config->destAddrInc ? GDMA_CTRL_DESTADDRINC_MASK : 0UL) /* Destination address increment. */
| GDMA_CTRL_SRCWIDTH(config->srcWidth) /* Source peripheral/memory transfer width. */
| GDMA_CTRL_DESTWIDTH(config->destWidth) /* Destination peripheral/memory transfer width. */
| GDMA_CTRL_SRCBSIZE(config->srcBurstSize) /* Source peripheral/memory transfer burst size */
| GDMA_CTRL_DESTBSIZE(config->destBurstSize) /* Destination peripheral/memory transfer burst size */
| GDMA_CTRL_LEN(config->transferLen); /* Length of the transfer in bytes */
if (config->enableLinkList)
{
base->CH[channel].LLI =
(config->linkListAddr & GDMA_LLI_LLI_MASK) /* LLI address. */
| (config->enableDescInterrupt ? GDMA_LLI_DESC_INT_EN_MASK : 0UL) /* Enable descriptor interrupt. */
| (config->stopAfterDescFinished ? GDMA_LLI_STOP_MASK : 0UL); /* Stop after descriptor finished. */
base->CH[channel].CONFIG |= GDMA_CONFIG_LLE_MASK;
}
else
{
base->CH[channel].LLI = GDMA_LLI_STOP_MASK;
base->CH[channel].CONFIG &= ~GDMA_CONFIG_LLE_MASK;
}
return kStatus_Success;
}
/*
* brief Creates the GDMA handle.
*
* This function is called if using transaction API for GDMA. This function
* initializes the internal state of GDMA handle.
*
* param handle GDMA handle pointer. It stores callback function and parameters.
* param base GDMA peripheral base address.
* param channel GDMA channel number.
*/
void GDMA_CreateHandle(gdma_handle_t *handle, GDMA_Type *base, uint8_t channel)
{
assert(NULL != handle);
(void)memset(handle, 0, sizeof(*handle));
handle->gdma = base;
handle->channel = channel;
s_gdmaHandles[channel] = handle;
s_gdmaIsr = GDMA_IRQHandle;
(void)EnableIRQ(GDMA_IRQn);
}
/*
* brief Installs a callback function for the GDMA transfer.
*
* This callback is called in GDMA IRQ handler to inform user the interrupt status.
*
* param handle GDMA handle pointer.
* param callback GDMA callback function pointer.
* param userData Parameter for callback function.
*/
void GDMA_SetCallback(gdma_handle_t *handle, gdma_callback_t callback, void *userData)
{
assert(handle != NULL);
handle->callback = callback;
handle->userData = userData;
}
/*
* brief Submits the GDMA channel transfer request.
*
* After this function, user could call GDMA_StartTransfer to start GDMA transfer.
*
* This function must be called when previous transfer finished. Application can use
* GDMA_IsChannelBusy to check whether the channel has finished the previous work.
*
* param handle GDMA handle pointer.
* param config Pointer to GDMA transfer configuration structure.
* retval kStatus_Fail GDMA is busy with previous transfer.
* retval kStatus_Success Configuration set successfully.
* retval kStatus_InvalidArgument Configuration wrong.
*/
status_t GDMA_SubmitTransfer(gdma_handle_t *handle, gdma_channel_xfer_config_t *config)
{
status_t status;
assert(NULL != handle);
status = GDMA_SetChannelTransferConfig(handle->gdma, handle->channel, config);
if (status == kStatus_Success)
{
GDMA_EnableChannelInterrupts(
handle->gdma, handle->channel,
(uint32_t)kGDMA_DescriptorTransferDoneInterruptEnable | (uint32_t)kGDMA_AddressErrorInterruptEnable |
(uint32_t)kGDMA_BusErrorInterruptEnable | (uint32_t)kGDMA_TransferDoneInterruptEnable);
}
return status;
}
/*
* brief GDMA start transfer.
*
* User can call this function after GDMA_SubmitTransfer.
*
* param handle GDMA handle pointer.
*/
void GDMA_StartTransfer(gdma_handle_t *handle)
{
assert(NULL != handle);
GDMA_StartChannel(handle->gdma, handle->channel);
}
/*
* brief Abort running transfer by handle.
*
* This function aborts GDMA transfer specified by handle.
*
* param handle GDMA handle pointer.
*/
void GDMA_AbortTransfer(gdma_handle_t *handle)
{
assert(NULL != handle);
GDMA_StopChannel(handle->gdma, handle->channel);
GDMA_DisableChannelInterrupts(handle->gdma, handle->channel, (uint32_t)kGDMA_AllInterruptEnable);
}
/*
* brief GDMA IRQ handler.
*
* This function checks all GDMA channel interrupts and inform application
* the interrupt flags through user registered callback.
*
* param base GDMA peripheral.
*/
void GDMA_IRQHandle(GDMA_Type *base)
{
uint8_t channel;
uint32_t interrupts;
gdma_handle_t *handle;
for (channel = 0U; channel < (uint8_t)FSL_FEATURE_GDMA_CHANNEL_NUM; channel++)
{
interrupts = GDMA_GetChannelInterruptFlags(base, channel);
/*
* If channels unmasked interrupt happened. This flag is only assert
* only when any unmasked interrupt happened.
*/
if (0U != (interrupts & (uint32_t)kGDMA_ChannelInterruptFlag))
{
GDMA_ClearChannelInterruptFlags(base, channel, interrupts);
handle = s_gdmaHandles[channel];
if (NULL != handle)
{
/* If error happened or transfer finished successfully. */
if (0U != (interrupts & ((uint32_t)kGDMA_AddressErrorFlag | (uint32_t)kGDMA_BusErrorFlag |
(uint32_t)kGDMA_TransferDoneFlag)))
{
GDMA_DisableChannelInterrupts(base, channel, (uint32_t)kGDMA_AllInterruptEnable);
}
if (NULL != handle->callback)
{
handle->callback(handle, handle->userData, interrupts);
}
}
else
{
/*
* If interrupt occurs but handle not created, then disable the interrupts,
* because to use the transactional APIs, user must create handle, then
* setup configuration and enable interrupts for data transfer.
*
* Generally will not reach here.
*/
GDMA_DisableChannelInterrupts(base, channel, (uint32_t)kGDMA_AllInterruptEnable);
}
}
}
}
void GDMA_DriverIRQHandler(void);
void GDMA_DriverIRQHandler(void)
{
s_gdmaIsr(GDMA);
SDK_ISR_EXIT_BARRIER;
}