MCUXpresso_MIMXRT1021xxxxx/devices/MIMXRT1021/drivers/fsl_flexio_mculcd_edma.c

569 lines
20 KiB
C

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2019,2023 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_flexio_mculcd_edma.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.flexio_mculcd_edma"
#endif
#define EDMA_MAX_MAJOR_COUNT (DMA_CITER_ELINKNO_CITER_MASK >> DMA_CITER_ELINKNO_CITER_SHIFT)
enum
{
kFLEXIO_MCULCD_StateIdle, /*!< No transfer in progress. */
kFLEXIO_MCULCD_StateReadArray, /*!< Reading array in progress. */
kFLEXIO_MCULCD_StateWriteArray, /*!< Writing array in progress. */
kFLEXIO_MCULCD_StateWriteSameValue, /*!< Writing the same value in progress.
*/
};
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief EDMA callback function for FLEXIO MCULCD TX.
*
* For details, see @ref edma_callback.
*/
static void FLEXIO_MCULCD_TxEDMACallback(edma_handle_t *DmaHandle, void *param, bool transferDone, uint32_t tcds);
/*!
* @brief EDMA callback function for FLEXIO MCULCD RX.
*
* For details, see @ref edma_callback.
*/
static void FLEXIO_MCULCD_RxEDMACallback(edma_handle_t *DmaHandle, void *param, bool transferDone, uint32_t tcds);
/*!
* @brief Set EDMA config for FLEXIO MCULCD transfer.
*
* @param base pointer to FLEXIO_MCULCD_Type structure.
* @param handle pointer to flexio_mculcd_edma_handle_t structure to store the
* transfer state.
*/
static void FLEXIO_MCULCD_EDMAConfig(FLEXIO_MCULCD_Type *base, flexio_mculcd_edma_handle_t *handle);
/*!
* @brief Convert the FlexIO shifter number to eDMA modulo.
*
* @param shifterNum The FlexIO shifter number.
* @param modulo The modulo number.
* @retval Get the modulo successfully.
* @retval Could not get the modulo for the shifter number.
*/
static bool FLEXIO_MCULCD_GetEDMAModulo(uint8_t shifterNum, edma_modulo_t *modulo);
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Code
******************************************************************************/
static void FLEXIO_MCULCD_TxEDMACallback(edma_handle_t *DmaHandle, void *param, bool transferDone, uint32_t tcds)
{
tcds = tcds;
flexio_mculcd_edma_handle_t *flexioLcdMcuHandle = (flexio_mculcd_edma_handle_t *)param;
FLEXIO_MCULCD_Type *flexioLcdMcuBase = flexioLcdMcuHandle->base;
if (transferDone)
{
if (flexioLcdMcuHandle->remainingCount >= flexioLcdMcuHandle->minorLoopBytes)
{
FLEXIO_MCULCD_EDMAConfig(flexioLcdMcuBase, flexioLcdMcuHandle);
EDMA_StartTransfer(flexioLcdMcuHandle->txDmaHandle);
}
else
{
FLEXIO_MCULCD_EnableTxDMA(flexioLcdMcuBase, false);
/* Now the data are in shifter, wait for the data send out from the shifter. */
FLEXIO_MCULCD_WaitTransmitComplete();
/* Disable the TX shifter and the timer. */
FLEXIO_MCULCD_ClearMultiBeatsWriteConfig(flexioLcdMcuBase);
/* Send the remaining data. */
if (0U != flexioLcdMcuHandle->remainingCount)
{
if ((uint32_t)kFLEXIO_MCULCD_StateWriteSameValue == flexioLcdMcuHandle->state)
{
FLEXIO_MCULCD_WriteSameValueBlocking(flexioLcdMcuBase, flexioLcdMcuHandle->dataAddrOrSameValue,
flexioLcdMcuHandle->remainingCount);
}
else
{
FLEXIO_MCULCD_WriteDataArrayBlocking(flexioLcdMcuBase,
(uint8_t *)flexioLcdMcuHandle->dataAddrOrSameValue,
flexioLcdMcuHandle->remainingCount);
}
}
/* De-assert nCS. */
FLEXIO_MCULCD_StopTransfer(flexioLcdMcuBase);
/* Change the state. */
flexioLcdMcuHandle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
flexioLcdMcuHandle->dataCount = 0;
flexioLcdMcuHandle->remainingCount = 0;
/* Callback to inform upper layer. */
if (NULL != flexioLcdMcuHandle->completionCallback)
{
flexioLcdMcuHandle->completionCallback(flexioLcdMcuBase, flexioLcdMcuHandle, kStatus_FLEXIO_MCULCD_Idle,
flexioLcdMcuHandle->userData);
}
}
}
}
static void FLEXIO_MCULCD_RxEDMACallback(edma_handle_t *DmaHandle, void *param, bool transferDone, uint32_t tcds)
{
tcds = tcds;
uint32_t i;
uint32_t rxBufAddr;
flexio_mculcd_edma_handle_t *flexioLcdMcuHandle = (flexio_mculcd_edma_handle_t *)param;
FLEXIO_MCULCD_Type *flexioLcdMcuBase = flexioLcdMcuHandle->base;
FLEXIO_Type *flexioBase = flexioLcdMcuBase->flexioBase;
if (transferDone)
{
if (flexioLcdMcuHandle->remainingCount >= (2U * flexioLcdMcuHandle->minorLoopBytes))
{
FLEXIO_MCULCD_EDMAConfig(flexioLcdMcuBase, flexioLcdMcuHandle);
EDMA_StartTransfer(flexioLcdMcuHandle->rxDmaHandle);
}
else
{
FLEXIO_MCULCD_EnableRxDMA(flexioLcdMcuBase, false);
/* Wait the data saved to the shifter buffer. */
while (0U == ((1UL << flexioLcdMcuBase->rxShifterEndIndex) & FLEXIO_GetShifterStatusFlags(flexioBase)))
{
}
/* Disable the RX shifter and the timer. */
FLEXIO_MCULCD_ClearMultiBeatsReadConfig(flexioLcdMcuBase);
rxBufAddr = FLEXIO_MCULCD_GetRxDataRegisterAddress(flexioLcdMcuBase);
/* Read out the data. */
#if (defined(__CORTEX_M) && (__CORTEX_M == 0))
/* Cortex M0 and M0+ only support aligned access. */
for (i = 0; i < flexioLcdMcuHandle->rxShifterNum * 4; i++)
{
((uint8_t *)(flexioLcdMcuHandle->dataAddrOrSameValue))[i] = ((volatile uint8_t *)rxBufAddr)[i];
}
#else
for (i = 0; i < flexioLcdMcuHandle->rxShifterNum; i++)
{
((uint32_t *)(flexioLcdMcuHandle->dataAddrOrSameValue))[i] = ((volatile uint32_t *)rxBufAddr)[i];
}
#endif
flexioLcdMcuHandle->remainingCount -= flexioLcdMcuHandle->minorLoopBytes;
if (0U != flexioLcdMcuHandle->remainingCount)
{
FLEXIO_MCULCD_ReadDataArrayBlocking(
flexioLcdMcuBase,
(uint8_t *)(flexioLcdMcuHandle->dataAddrOrSameValue + flexioLcdMcuHandle->minorLoopBytes),
flexioLcdMcuHandle->remainingCount);
}
/* De-assert nCS. */
FLEXIO_MCULCD_StopTransfer(flexioLcdMcuBase);
/* Change the state. */
flexioLcdMcuHandle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
flexioLcdMcuHandle->dataCount = 0;
flexioLcdMcuHandle->remainingCount = 0;
/* Callback to inform upper layer. */
if (NULL != flexioLcdMcuHandle->completionCallback)
{
flexioLcdMcuHandle->completionCallback(flexioLcdMcuBase, flexioLcdMcuHandle, kStatus_FLEXIO_MCULCD_Idle,
flexioLcdMcuHandle->userData);
}
}
}
}
static void FLEXIO_MCULCD_EDMAConfig(FLEXIO_MCULCD_Type *base, flexio_mculcd_edma_handle_t *handle)
{
edma_transfer_config_t xferConfig = {0};
edma_transfer_size_t transferSize = kEDMA_TransferSize1Bytes;
int16_t offset;
uint32_t majorLoopCounts;
uint32_t transferCount;
#if (8 == FLEXIO_MCULCD_DATA_BUS_WIDTH)
transferSize = kEDMA_TransferSize1Bytes;
offset = 1;
#else
transferSize = kEDMA_TransferSize2Bytes;
offset = 2;
#endif
majorLoopCounts = handle->remainingCount / handle->minorLoopBytes;
/* For reading, the last minor loop data is not tranfered by DMA. */
if ((uint32_t)kFLEXIO_MCULCD_StateReadArray == handle->state)
{
majorLoopCounts--;
}
if (majorLoopCounts > EDMA_MAX_MAJOR_COUNT)
{
majorLoopCounts = EDMA_MAX_MAJOR_COUNT;
}
transferCount = majorLoopCounts * handle->minorLoopBytes;
if ((uint32_t)kFLEXIO_MCULCD_StateReadArray == handle->state)
{
xferConfig.srcAddr = FLEXIO_MCULCD_GetRxDataRegisterAddress(base);
xferConfig.destAddr = handle->dataAddrOrSameValue;
xferConfig.srcTransferSize = kEDMA_TransferSize4Bytes;
xferConfig.destTransferSize = transferSize;
xferConfig.srcOffset = 4;
xferConfig.destOffset = offset;
xferConfig.minorLoopBytes = handle->minorLoopBytes;
xferConfig.majorLoopCounts = majorLoopCounts;
handle->remainingCount -= transferCount;
handle->dataAddrOrSameValue += transferCount;
(void)EDMA_SubmitTransfer(handle->rxDmaHandle, &xferConfig);
EDMA_SetModulo(handle->rxDmaHandle->base, handle->rxDmaHandle->channel, handle->rxEdmaModulo,
kEDMA_ModuloDisable);
}
else
{
if ((uint32_t)kFLEXIO_MCULCD_StateWriteArray == handle->state)
{
xferConfig.srcAddr = handle->dataAddrOrSameValue;
xferConfig.srcOffset = offset;
handle->dataAddrOrSameValue += transferCount;
}
else
{
xferConfig.srcAddr = (uint32_t)(&(handle->dataAddrOrSameValue));
xferConfig.srcOffset = 0;
}
xferConfig.destAddr = FLEXIO_MCULCD_GetTxDataRegisterAddress(base);
xferConfig.srcTransferSize = transferSize;
xferConfig.destTransferSize = kEDMA_TransferSize4Bytes;
xferConfig.destOffset = 4;
xferConfig.minorLoopBytes = handle->minorLoopBytes;
xferConfig.majorLoopCounts = majorLoopCounts;
handle->remainingCount -= transferCount;
(void)EDMA_SubmitTransfer(handle->txDmaHandle, &xferConfig);
EDMA_SetModulo(handle->txDmaHandle->base, handle->txDmaHandle->channel, kEDMA_ModuloDisable,
handle->txEdmaModulo);
}
}
static bool FLEXIO_MCULCD_GetEDMAModulo(uint8_t shifterNum, edma_modulo_t *modulo)
{
bool ret = true;
switch (shifterNum)
{
case 1U:
*modulo = kEDMA_Modulo4bytes;
break;
case 2U:
*modulo = kEDMA_Modulo8bytes;
break;
case 4U:
*modulo = kEDMA_Modulo16bytes;
break;
case 8U:
*modulo = kEDMA_Modulo32bytes;
break;
default:
ret = false;
break;
}
return ret;
}
/*!
* brief Initializes the FLEXO MCULCD master eDMA handle.
*
* This function initializes the FLEXO MCULCD master eDMA handle which can be
* used for other FLEXO MCULCD transactional APIs. For a specified FLEXO MCULCD
* instance, call this API once to get the initialized handle.
*
* param base Pointer to FLEXIO_MCULCD_Type structure.
* param handle Pointer to flexio_mculcd_edma_handle_t structure to store the
* transfer state.
* param callback MCULCD transfer complete callback, NULL means no callback.
* param userData callback function parameter.
* param txDmaHandle User requested eDMA handle for FlexIO MCULCD eDMA TX,
* the DMA request source of this handle should be the first of TX shifters.
* param rxDmaHandle User requested eDMA handle for FlexIO MCULCD eDMA RX,
* the DMA request source of this handle should be the last of RX shifters.
* retval kStatus_Success Successfully create the handle.
*/
status_t FLEXIO_MCULCD_TransferCreateHandleEDMA(FLEXIO_MCULCD_Type *base,
flexio_mculcd_edma_handle_t *handle,
flexio_mculcd_edma_transfer_callback_t callback,
void *userData,
edma_handle_t *txDmaHandle,
edma_handle_t *rxDmaHandle)
{
assert(NULL != handle);
/* Zero the handle. */
(void)memset(handle, 0, sizeof(*handle));
/* Initialize the state. */
handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
/* Register callback and userData. */
handle->completionCallback = callback;
handle->userData = userData;
handle->base = base;
handle->txShifterNum = base->txShifterEndIndex - base->txShifterStartIndex + 1U;
handle->rxShifterNum = base->rxShifterEndIndex - base->rxShifterStartIndex + 1U;
if (NULL != rxDmaHandle)
{
if (!FLEXIO_MCULCD_GetEDMAModulo(handle->rxShifterNum, &handle->rxEdmaModulo))
{
return kStatus_InvalidArgument;
}
handle->rxDmaHandle = rxDmaHandle;
EDMA_SetCallback(rxDmaHandle, FLEXIO_MCULCD_RxEDMACallback, handle);
}
if (NULL != txDmaHandle)
{
if (!FLEXIO_MCULCD_GetEDMAModulo(handle->txShifterNum, &handle->txEdmaModulo))
{
return kStatus_InvalidArgument;
}
handle->txDmaHandle = txDmaHandle;
EDMA_SetCallback(txDmaHandle, FLEXIO_MCULCD_TxEDMACallback, handle);
}
return kStatus_Success;
}
/*!
* brief Performs a non-blocking FlexIO MCULCD transfer using eDMA.
*
* This function returns immediately after transfer initiates. To check whether
* the transfer is completed, user could:
* 1. Use the transfer completed callback;
* 2. Polling function ref FLEXIO_MCULCD_GetTransferCountEDMA
*
* param base pointer to FLEXIO_MCULCD_Type structure.
* param handle pointer to flexio_mculcd_edma_handle_t structure to store the
* transfer state.
* param xfer Pointer to FlexIO MCULCD transfer structure.
* retval kStatus_Success Successfully start a transfer.
* retval kStatus_InvalidArgument Input argument is invalid.
* retval kStatus_FLEXIO_MCULCD_Busy FlexIO MCULCD is not idle, it is running another
* transfer.
*/
status_t FLEXIO_MCULCD_TransferEDMA(FLEXIO_MCULCD_Type *base,
flexio_mculcd_edma_handle_t *handle,
flexio_mculcd_transfer_t *xfer)
{
assert(NULL != handle);
assert(NULL != xfer);
/*
* The data transfer mechanism:
*
* Read:
* Assume the data length is Lr = (n1 * minorLoopBytes + n2), where
* n2 < minorLoopBytes.
* If (n1 <= 1), then all data are sent using blocking method.
* If (n1 > 1), then the beginning ((n1-1) * minorLoopBytes) are read
* using DMA, the left (minorLoopBytes + n2) are read using blocking method.
*
* Write:
* Assume the data length is Lw = (n1 * minorLoopBytes + n2), where
* n2 < minorLoopBytes.
* If (n1 = 0), then all data are sent using blocking method.
* If (n1 >= 1), then the beginning (n1 * minorLoopBytes) are sent
* using DMA, the left n2 are sent using blocking method.
*/
/* Check if the device is busy. */
if ((uint32_t)kFLEXIO_MCULCD_StateIdle != handle->state)
{
return kStatus_FLEXIO_MCULCD_Busy;
}
/* Set the state in handle. */
if (kFLEXIO_MCULCD_ReadArray == xfer->mode)
{
handle->state = (uint32_t)kFLEXIO_MCULCD_StateReadArray;
handle->minorLoopBytes = handle->rxShifterNum * 4UL;
}
else
{
handle->minorLoopBytes = handle->txShifterNum * 4UL;
if (kFLEXIO_MCULCD_WriteArray == xfer->mode)
{
handle->state = (uint32_t)kFLEXIO_MCULCD_StateWriteArray;
}
else
{
handle->state = (uint32_t)kFLEXIO_MCULCD_StateWriteSameValue;
}
}
/*
* For TX, if data is less than one minor loop, then use polling method.
* For RX, if data is less than two minor loop, then use polling method.
*/
if ((xfer->dataSize < handle->minorLoopBytes) ||
((kFLEXIO_MCULCD_ReadArray == xfer->mode) && (xfer->dataSize < 2U * (handle->minorLoopBytes))))
{
FLEXIO_MCULCD_TransferBlocking(base, xfer);
handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
/* Callback to inform upper layer. */
if (NULL != handle->completionCallback)
{
handle->completionCallback(base, handle, kStatus_FLEXIO_MCULCD_Idle, handle->userData);
}
}
else
{
handle->dataCount = xfer->dataSize;
handle->remainingCount = xfer->dataSize;
handle->dataAddrOrSameValue = xfer->dataAddrOrSameValue;
/* Setup DMA to transfer data. */
/* Assert the nCS. */
FLEXIO_MCULCD_StartTransfer(base);
if (!xfer->dataOnly)
{
/* Send the command. */
FLEXIO_MCULCD_WriteCommandBlocking(base, xfer->command);
}
/* Setup the DMA configuration. */
FLEXIO_MCULCD_EDMAConfig(base, handle);
/* Start the transfer. */
if (kFLEXIO_MCULCD_ReadArray == xfer->mode)
{
/* For 6800, assert the RDWR pin. */
if (kFLEXIO_MCULCD_6800 == base->busType)
{
base->setRDWRPin(true);
}
FLEXIO_MCULCD_SetMultiBeatsReadConfig(base);
FLEXIO_MCULCD_EnableRxDMA(base, true);
EDMA_StartTransfer(handle->rxDmaHandle);
}
else
{
/* For 6800, de-assert the RDWR pin. */
if (kFLEXIO_MCULCD_6800 == base->busType)
{
base->setRDWRPin(false);
}
FLEXIO_MCULCD_SetMultiBeatsWriteConfig(base);
FLEXIO_MCULCD_EnableTxDMA(base, true);
EDMA_StartTransfer(handle->txDmaHandle);
}
}
return kStatus_Success;
}
/*!
* brief Aborts a FlexIO MCULCD transfer using eDMA.
*
* param base pointer to FLEXIO_MCULCD_Type structure.
* param handle FlexIO MCULCD eDMA handle pointer.
*/
void FLEXIO_MCULCD_TransferAbortEDMA(FLEXIO_MCULCD_Type *base, flexio_mculcd_edma_handle_t *handle)
{
assert(NULL != handle);
/* Disable dma. */
if (NULL != handle->txDmaHandle)
{
EDMA_AbortTransfer(handle->txDmaHandle);
}
if (NULL != handle->rxDmaHandle)
{
EDMA_AbortTransfer(handle->rxDmaHandle);
}
/* Disable DMA enable bit. */
FLEXIO_MCULCD_EnableTxDMA(handle->base, false);
FLEXIO_MCULCD_EnableRxDMA(handle->base, false);
/* Set the handle state. */
handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
handle->dataCount = 0;
}
/*!
* brief Gets the remaining bytes for FlexIO MCULCD eDMA transfer.
*
* param base pointer to FLEXIO_MCULCD_Type structure.
* param handle FlexIO MCULCD eDMA handle pointer.
* param count Number of count transferred so far by the eDMA transaction.
* retval kStatus_Success Get the transferred count Successfully.
* retval kStatus_NoTransferInProgress No transfer in process.
*/
status_t FLEXIO_MCULCD_TransferGetCountEDMA(FLEXIO_MCULCD_Type *base,
flexio_mculcd_edma_handle_t *handle,
size_t *count)
{
assert(NULL != handle);
assert(NULL != count);
uint32_t state = handle->state;
if ((uint32_t)kFLEXIO_MCULCD_StateIdle == state)
{
return kStatus_NoTransferInProgress;
}
else
{
*count = handle->dataCount - handle->remainingCount;
if ((uint32_t)kFLEXIO_MCULCD_StateReadArray == state)
{
*count -= handle->minorLoopBytes *
EDMA_GetRemainingMajorLoopCount(handle->rxDmaHandle->base, handle->rxDmaHandle->channel);
}
else
{
*count -= handle->minorLoopBytes *
EDMA_GetRemainingMajorLoopCount(handle->txDmaHandle->base, handle->txDmaHandle->channel);
}
}
return kStatus_Success;
}