/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2019 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); /* 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; }