rt-thread/bsp/imxrt/libraries/MIMXRT1020/MIMXRT1021/drivers/fsl_flexspi_edma.c

365 lines
13 KiB
C

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2020 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_flexspi_edma.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.flexspi_edma"
#endif
/*<! Structure definition for flexspi_edma_private_handle_t. The structure is private. */
typedef struct _flexspi_edma_private_handle
{
FLEXSPI_Type *base;
flexspi_edma_handle_t *handle;
} flexspi_edma_private_handle_t;
/* FLEXSPI EDMA transfer handle, _flexspi_edma_tansfer_states. */
enum
{
kFLEXSPI_Idle, /* FLEXSPI Bus idle. */
kFLEXSPI_Busy /* FLEXSPI Bus busy. */
};
/*******************************************************************************
* Variables
******************************************************************************/
/*! @brief Pointers to flexspi bases for each instance. */
static FLEXSPI_Type *const s_flexspiBases[] = FLEXSPI_BASE_PTRS;
/*<! Private handle only used for internally. */
static flexspi_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_flexspiBases)];
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief FLEXSPI EDMA transfer finished callback function.
*
* This function is called when FLEXSPI EDMA transfer finished. It disables the FLEXSPI
* TX/RX EDMA request and sends status to FLEXSPI callback.
*
* @param handle The EDMA handle.
* @param param Callback function parameter.
*/
static void FLEXSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds);
/*******************************************************************************
* Code
******************************************************************************/
static uint8_t FLEXSPI_CalculatePower(uint8_t value)
{
uint8_t power = 0;
while (value >> 1 != 0U)
{
power++;
value = value >> 1;
}
return power;
}
static void FLEXSPI_TransferEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
flexspi_edma_private_handle_t *flexspiPrivateHandle = (flexspi_edma_private_handle_t *)param;
/* Avoid warning for unused parameters. */
handle = handle;
tcds = tcds;
if (transferDone)
{
/* Wait for bus idle. */
while (!FLEXSPI_GetBusIdleStatus(flexspiPrivateHandle->base))
{
}
/* Disable transfer. */
FLEXSPI_TransferAbortEDMA(flexspiPrivateHandle->base, flexspiPrivateHandle->handle);
if (flexspiPrivateHandle->handle->completionCallback != NULL)
{
flexspiPrivateHandle->handle->completionCallback(flexspiPrivateHandle->base, flexspiPrivateHandle->handle,
kStatus_Success, flexspiPrivateHandle->handle->userData);
}
}
}
/*!
* brief Initializes the FLEXSPI handle for transfer which is used in transactional functions and set the callback.
*
* param base FLEXSPI peripheral base address
* param handle Pointer to flexspi_edma_handle_t structure
* param callback FLEXSPI callback, NULL means no callback.
* param userData User callback function data.
* param txDmaHandle User requested DMA handle for TX DMA transfer.
* param rxDmaHandle User requested DMA handle for RX DMA transfer.
*/
void FLEXSPI_TransferCreateHandleEDMA(FLEXSPI_Type *base,
flexspi_edma_handle_t *handle,
flexspi_edma_callback_t callback,
void *userData,
edma_handle_t *txDmaHandle,
edma_handle_t *rxDmaHandle)
{
assert(handle);
uint32_t instance = FLEXSPI_GetInstance(base);
s_edmaPrivateHandle[instance].base = base;
s_edmaPrivateHandle[instance].handle = handle;
(void)memset(handle, 0, sizeof(*handle));
handle->state = kFLEXSPI_Idle;
handle->txDmaHandle = txDmaHandle;
handle->rxDmaHandle = rxDmaHandle;
handle->nsize = kFLEXPSI_EDMAnSize1Bytes;
handle->completionCallback = callback;
handle->userData = userData;
}
/*!
* brief Update FLEXSPI EDMA transfer source data transfer size(SSIZE) and destination data transfer size(DSIZE).
*
* param base FLEXSPI peripheral base address
* param handle Pointer to flexspi_edma_handle_t structure
* param nsize FLEXSPI DMA transfer data transfer size(SSIZE/DSIZE), by default the size is
* kFLEXPSI_EDMAnSize1Bytes(one byte).
* see flexspi_edma_transfer_nsize_t .
*/
void FLEXSPI_TransferUpdateSizeEDMA(FLEXSPI_Type *base,
flexspi_edma_handle_t *handle,
flexspi_edma_transfer_nsize_t nsize)
{
handle->nsize = nsize;
}
/*!
* brief Transfers FLEXSPI data using an eDMA non-blocking method.
*
* This function writes/receives data to/from the FLEXSPI transmit/receive FIFO. This function is non-blocking.
* param base FLEXSPI peripheral base address.
* param handle Pointer to flexspi_edma_handle_t structure
* param xfer FLEXSPI transfer structure.
* retval kStatus_FLEXSPI_Busy FLEXSPI is busy transfer.
* retval kStatus_InvalidArgument The watermark configuration is invalid, the watermark should be power of
2 to do successfully EDMA transfer.
* retval kStatus_Success FLEXSPI successfully start edma transfer.
*/
status_t FLEXSPI_TransferEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle, flexspi_transfer_t *xfer)
{
uint32_t configValue = 0;
status_t result = kStatus_Success;
edma_transfer_config_t xferConfig;
uint32_t instance = FLEXSPI_GetInstance(base);
uint8_t power = 0;
assert(handle);
assert(xfer);
/* Check if the FLEXSPI bus is idle - if not return busy status. */
if (handle->state != (uint32_t)kFLEXSPI_Idle)
{
result = kStatus_FLEXSPI_Busy;
}
else
{
handle->transferSize = xfer->dataSize;
handle->state = kFLEXSPI_Busy;
/* Clear sequence pointer before sending data to external devices. */
base->FLSHCR2[xfer->port] |= FLEXSPI_FLSHCR2_CLRINSTRPTR_MASK;
/* Clear former pending status before start this transfer. */
base->INTR |= FLEXSPI_INTR_AHBCMDERR_MASK | FLEXSPI_INTR_IPCMDERR_MASK | FLEXSPI_INTR_AHBCMDGE_MASK |
FLEXSPI_INTR_IPCMDGE_MASK;
/* Configure base address. */
base->IPCR0 = xfer->deviceAddress;
/* Reset fifos. */
base->IPTXFCR |= FLEXSPI_IPTXFCR_CLRIPTXF_MASK;
base->IPRXFCR |= FLEXSPI_IPRXFCR_CLRIPRXF_MASK;
/* Configure data size. */
if ((xfer->cmdType == kFLEXSPI_Read) || (xfer->cmdType == kFLEXSPI_Write))
{
configValue = FLEXSPI_IPCR1_IDATSZ(xfer->dataSize);
}
/* Configure sequence ID. */
configValue |= FLEXSPI_IPCR1_ISEQID(xfer->seqIndex) | FLEXSPI_IPCR1_ISEQNUM((uint32_t)xfer->SeqNumber - 1U);
base->IPCR1 = configValue;
}
if ((xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
{
handle->count = (uint8_t)((base->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1U;
if (xfer->dataSize < 8U * (uint32_t)handle->count)
{
handle->nbytes = (uint8_t)xfer->dataSize;
}
else
{
/* Check the handle->count is power of 2 */
if (((handle->count) & (handle->count - 1U)) != 0U)
{
return kStatus_InvalidArgument;
}
/* Store the initially configured eDMA minor byte transfer count into the FLEXSPI handle */
handle->nbytes = (8U * handle->count);
}
power = FLEXSPI_CalculatePower(8U * handle->count);
/* Prepare transfer. */
EDMA_PrepareTransfer(&xferConfig, xfer->data, (uint32_t)handle->nsize,
(void *)(uint32_t *)FLEXSPI_GetTxFifoAddress(base), (uint32_t)handle->nsize,
(uint32_t)handle->nbytes, xfer->dataSize, kEDMA_MemoryToMemory);
/* Submit transfer. */
(void)EDMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
handle->txDmaHandle->base->TCD[handle->txDmaHandle->channel].ATTR |= DMA_ATTR_DMOD(power);
EDMA_SetCallback(handle->txDmaHandle, FLEXSPI_TransferEDMACallback,
&s_edmaPrivateHandle[FLEXSPI_GetInstance(base)]);
EDMA_StartTransfer(handle->txDmaHandle);
/* Enable FLEXSPI TX EDMA. */
FLEXSPI_EnableTxDMA(base, true);
/* Start Transfer. */
base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
}
else if (xfer->cmdType == kFLEXSPI_Read)
{
handle->count = (uint8_t)((base->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1U;
if (xfer->dataSize < 8U * (uint32_t)handle->count)
{
handle->nbytes = (uint8_t)xfer->dataSize;
}
else
{
/* Check the handle->count is power of 2 */
if (((handle->count) & (handle->count - 1U)) != 0U)
{
return kStatus_InvalidArgument;
}
/* Store the initially configured eDMA minor byte transfer count into the FLEXSPI handle */
handle->nbytes = (8U * handle->count);
}
power = FLEXSPI_CalculatePower(8U * handle->count);
/* Prepare transfer. */
EDMA_PrepareTransfer(&xferConfig, (void *)(uint32_t *)FLEXSPI_GetRxFifoAddress(base), (uint32_t)handle->nsize,
xfer->data, (uint32_t)handle->nsize, (uint32_t)handle->nbytes, xfer->dataSize,
kEDMA_MemoryToMemory);
/* Submit transfer. */
(void)EDMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
handle->rxDmaHandle->base->TCD[handle->rxDmaHandle->channel].ATTR |= DMA_ATTR_SMOD(power);
EDMA_SetCallback(handle->rxDmaHandle, FLEXSPI_TransferEDMACallback, &s_edmaPrivateHandle[instance]);
EDMA_StartTransfer(handle->rxDmaHandle);
/* Enable FLEXSPI RX EDMA. */
FLEXSPI_EnableRxDMA(base, true);
/* Start Transfer. */
base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
}
else
{
/* Start Transfer. */
base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
/* Wait for bus idle. */
while (!FLEXSPI_GetBusIdleStatus(base))
{
}
result = FLEXSPI_CheckAndClearError(base, base->INTR);
handle->state = kFLEXSPI_Idle;
if (handle->completionCallback != NULL)
{
handle->completionCallback(base, handle, result, handle->userData);
}
}
return result;
}
/*!
* brief Aborts the transfer data using eDMA.
*
* This function aborts the transfer data using eDMA.
*
* param base FLEXSPI peripheral base address.
* param handle Pointer to flexspi_edma_handle_t structure
*/
void FLEXSPI_TransferAbortEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle)
{
assert(handle);
if ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXDMAEN_MASK) != 0x00U)
{
FLEXSPI_EnableTxDMA(base, false);
EDMA_AbortTransfer(handle->txDmaHandle);
}
if ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXDMAEN_MASK) != 0x00U)
{
FLEXSPI_EnableRxDMA(base, false);
EDMA_AbortTransfer(handle->rxDmaHandle);
}
handle->state = kFLEXSPI_Idle;
}
status_t FLEXSPI_TransferGetTransferCountEDMA(FLEXSPI_Type *base, flexspi_edma_handle_t *handle, size_t *count)
{
assert(handle);
assert(count);
status_t result = kStatus_Success;
if (handle->state != (uint32_t)kFLEXSPI_Busy)
{
result = kStatus_NoTransferInProgress;
}
else
{
if ((base->IPRXFCR & FLEXSPI_IPRXFCR_RXDMAEN_MASK) != 0x00U)
{
*count = (handle->transferSize -
(uint32_t)handle->nbytes *
EDMA_GetRemainingMajorLoopCount(handle->rxDmaHandle->base, handle->rxDmaHandle->channel));
}
else if ((base->IPTXFCR & FLEXSPI_IPTXFCR_TXDMAEN_MASK) != 0x00U)
{
*count = (handle->transferSize -
(uint32_t)handle->nbytes *
EDMA_GetRemainingMajorLoopCount(handle->txDmaHandle->base, handle->txDmaHandle->channel));
}
else
{
; /* Intentional empty for MISRA C-2012 rule 15.7. */
}
}
return result;
}