/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2020 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_flexio_spi_edma.h" /******************************************************************************* * Definitions ******************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.flexio_spi_edma" #endif /*base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, false); /* change the state */ spiPrivateHandle->handle->txInProgress = false; /* All finished, call the callback */ if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false)) { if (spiPrivateHandle->handle->callback != NULL) { (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success, spiPrivateHandle->handle->userData); } } } } static void FLEXIO_SPI_RxEDMACallback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds) { tcds = tcds; flexio_spi_master_edma_private_handle_t *spiPrivateHandle = (flexio_spi_master_edma_private_handle_t *)param; if (transferDone) { /* Disable Rx dma */ FLEXIO_SPI_EnableDMA(spiPrivateHandle->base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, false); /* change the state */ spiPrivateHandle->handle->rxInProgress = false; /* All finished, call the callback */ if ((spiPrivateHandle->handle->txInProgress == false) && (spiPrivateHandle->handle->rxInProgress == false)) { if (spiPrivateHandle->handle->callback != NULL) { (spiPrivateHandle->handle->callback)(spiPrivateHandle->base, spiPrivateHandle->handle, kStatus_Success, spiPrivateHandle->handle->userData); } } } } static void FLEXIO_SPI_EDMAConfig(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle, flexio_spi_transfer_t *xfer) { edma_transfer_config_t xferConfig = {0}; flexio_spi_shift_direction_t direction = kFLEXIO_SPI_MsbFirst; uint8_t bytesPerFrame; /* Configure the values in handle. */ switch (xfer->flags) { case (uint8_t)kFLEXIO_SPI_8bitMsb: bytesPerFrame = 1U; direction = kFLEXIO_SPI_MsbFirst; break; case (uint8_t)kFLEXIO_SPI_8bitLsb: bytesPerFrame = 1U; direction = kFLEXIO_SPI_LsbFirst; break; case (uint8_t)kFLEXIO_SPI_16bitMsb: bytesPerFrame = 2U; direction = kFLEXIO_SPI_MsbFirst; break; case (uint8_t)kFLEXIO_SPI_16bitLsb: bytesPerFrame = 2U; direction = kFLEXIO_SPI_LsbFirst; break; default: bytesPerFrame = 1U; direction = kFLEXIO_SPI_MsbFirst; assert(true); break; } /* Save total transfer size. */ handle->transferSize = xfer->dataSize; /* Configure tx transfer EDMA. */ xferConfig.destAddr = FLEXIO_SPI_GetTxDataRegisterAddress(base, direction); xferConfig.destOffset = 0; if (bytesPerFrame == 1U) { xferConfig.srcTransferSize = kEDMA_TransferSize1Bytes; xferConfig.destTransferSize = kEDMA_TransferSize1Bytes; xferConfig.minorLoopBytes = 1U; } else { if (direction == kFLEXIO_SPI_MsbFirst) { xferConfig.destAddr -= 1U; } xferConfig.srcTransferSize = kEDMA_TransferSize2Bytes; xferConfig.destTransferSize = kEDMA_TransferSize2Bytes; xferConfig.minorLoopBytes = 2U; } /* Configure DMA channel. */ if (xfer->txData != NULL) { xferConfig.srcOffset = (int16_t)bytesPerFrame; xferConfig.srcAddr = (uint32_t)(xfer->txData); } else { /* Disable the source increasement and source set to dummyData. */ xferConfig.srcOffset = 0; xferConfig.srcAddr = (uint32_t)(&s_dummyData); } xferConfig.majorLoopCounts = (xfer->dataSize / xferConfig.minorLoopBytes); /* Store the initially configured eDMA minor byte transfer count into the FLEXIO SPI handle */ handle->nbytes = (uint8_t)xferConfig.minorLoopBytes; if (handle->txHandle != NULL) { (void)EDMA_SubmitTransfer(handle->txHandle, &xferConfig); } /* Configure rx transfer EDMA. */ if (xfer->rxData != NULL) { xferConfig.srcAddr = FLEXIO_SPI_GetRxDataRegisterAddress(base, direction); if (bytesPerFrame == 2U) { if (direction == kFLEXIO_SPI_LsbFirst) { xferConfig.srcAddr -= 1U; } } xferConfig.srcOffset = 0; xferConfig.destAddr = (uint32_t)(xfer->rxData); xferConfig.destOffset = (int16_t)bytesPerFrame; (void)EDMA_SubmitTransfer(handle->rxHandle, &xferConfig); handle->rxInProgress = true; FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_RxDmaEnable, true); EDMA_StartTransfer(handle->rxHandle); } /* Always start tx transfer. */ if (handle->txHandle != NULL) { handle->txInProgress = true; FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_TxDmaEnable, true); EDMA_StartTransfer(handle->txHandle); } } /*! * brief Initializes the FlexIO SPI master eDMA handle. * * This function initializes the FlexIO SPI master eDMA handle which can be used for other FlexIO SPI master * transactional * APIs. * For a specified FlexIO SPI instance, call this API once to get the initialized handle. * * param base Pointer to FLEXIO_SPI_Type structure. * param handle Pointer to flexio_spi_master_edma_handle_t structure to store the transfer state. * param callback SPI callback, NULL means no callback. * param userData callback function parameter. * param txHandle User requested eDMA handle for FlexIO SPI RX eDMA transfer. * param rxHandle User requested eDMA handle for FlexIO SPI TX eDMA transfer. * retval kStatus_Success Successfully create the handle. * retval kStatus_OutOfRange The FlexIO SPI eDMA type/handle table out of range. */ status_t FLEXIO_SPI_MasterTransferCreateHandleEDMA(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle, flexio_spi_master_edma_transfer_callback_t callback, void *userData, edma_handle_t *txHandle, edma_handle_t *rxHandle) { assert(handle != NULL); uint8_t index = 0; /* Find the an empty handle pointer to store the handle. */ for (index = 0U; index < (uint8_t)FLEXIO_SPI_HANDLE_COUNT; index++) { if (s_edmaPrivateHandle[index].base == NULL) { s_edmaPrivateHandle[index].base = base; s_edmaPrivateHandle[index].handle = handle; break; } } if (index == (uint16_t)FLEXIO_SPI_HANDLE_COUNT) { return kStatus_OutOfRange; } /* Set spi base to handle. */ handle->txHandle = txHandle; handle->rxHandle = rxHandle; /* Register callback and userData. */ handle->callback = callback; handle->userData = userData; /* Set SPI state to idle. */ handle->txInProgress = false; handle->rxInProgress = false; /* Install callback for Tx/Rx dma channel. */ if (handle->txHandle != NULL) { EDMA_SetCallback(handle->txHandle, FLEXIO_SPI_TxEDMACallback, &s_edmaPrivateHandle[index]); } if (handle->rxHandle != NULL) { EDMA_SetCallback(handle->rxHandle, FLEXIO_SPI_RxEDMACallback, &s_edmaPrivateHandle[index]); } return kStatus_Success; } /*! * brief Performs a non-blocking FlexIO SPI transfer using eDMA. * * note This interface returns immediately after transfer initiates. Call * FLEXIO_SPI_MasterGetTransferCountEDMA to poll the transfer status and check * whether the FlexIO SPI transfer is finished. * * param base Pointer to FLEXIO_SPI_Type structure. * param handle Pointer to flexio_spi_master_edma_handle_t structure to store the transfer state. * param xfer Pointer to FlexIO SPI transfer structure. * retval kStatus_Success Successfully start a transfer. * retval kStatus_InvalidArgument Input argument is invalid. * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer. */ status_t FLEXIO_SPI_MasterTransferEDMA(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle, flexio_spi_transfer_t *xfer) { assert(handle != NULL); assert(xfer != NULL); uint32_t dataMode = 0; uint16_t timerCmp = (uint16_t)base->flexioBase->TIMCMP[base->timerIndex[0]]; timerCmp &= 0x00FFU; /* Check if the device is busy. */ if ((handle->txInProgress) || (handle->rxInProgress)) { return kStatus_FLEXIO_SPI_Busy; } /* Check if input parameter invalid. */ if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U)) { return kStatus_InvalidArgument; } /* configure data mode. */ if ((xfer->flags == (uint8_t)kFLEXIO_SPI_8bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_8bitLsb)) { dataMode = (8UL * 2UL - 1UL) << 8U; } else if ((xfer->flags == (uint8_t)kFLEXIO_SPI_16bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_16bitLsb)) { dataMode = (16UL * 2UL - 1UL) << 8U; } else { dataMode = 8UL * 2UL - 1UL; } dataMode |= timerCmp; base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode; FLEXIO_SPI_EDMAConfig(base, handle, xfer); return kStatus_Success; } /*! * brief Gets the remaining bytes for FlexIO SPI eDMA transfer. * * param base Pointer to FLEXIO_SPI_Type structure. * param handle FlexIO SPI eDMA handle pointer. * param count Number of bytes transferred so far by the non-blocking transaction. */ status_t FLEXIO_SPI_MasterTransferGetCountEDMA(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle, size_t *count) { assert(handle != NULL); if (NULL == count) { return kStatus_InvalidArgument; } if (handle->rxInProgress) { *count = (handle->transferSize - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount( handle->rxHandle->base, handle->rxHandle->channel)); } else { *count = (handle->transferSize - (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount( handle->txHandle->base, handle->txHandle->channel)); } return kStatus_Success; } /*! * brief Aborts a FlexIO SPI transfer using eDMA. * * param base Pointer to FLEXIO_SPI_Type structure. * param handle FlexIO SPI eDMA handle pointer. */ void FLEXIO_SPI_MasterTransferAbortEDMA(FLEXIO_SPI_Type *base, flexio_spi_master_edma_handle_t *handle) { assert(handle != NULL); /* Disable dma. */ EDMA_StopTransfer(handle->txHandle); EDMA_StopTransfer(handle->rxHandle); /* Disable DMA enable bit. */ FLEXIO_SPI_EnableDMA(base, (uint32_t)kFLEXIO_SPI_DmaAllEnable, false); /* Set the handle state. */ handle->txInProgress = false; handle->rxInProgress = false; } /*! * brief Performs a non-blocking FlexIO SPI transfer using eDMA. * * note This interface returns immediately after transfer initiates. Call * FLEXIO_SPI_SlaveGetTransferCountEDMA to poll the transfer status and * check whether the FlexIO SPI transfer is finished. * * param base Pointer to FLEXIO_SPI_Type structure. * param handle Pointer to flexio_spi_slave_edma_handle_t structure to store the transfer state. * param xfer Pointer to FlexIO SPI transfer structure. * retval kStatus_Success Successfully start a transfer. * retval kStatus_InvalidArgument Input argument is invalid. * retval kStatus_FLEXIO_SPI_Busy FlexIO SPI is not idle, is running another transfer. */ status_t FLEXIO_SPI_SlaveTransferEDMA(FLEXIO_SPI_Type *base, flexio_spi_slave_edma_handle_t *handle, flexio_spi_transfer_t *xfer) { assert(handle != NULL); assert(xfer != NULL); uint32_t dataMode = 0U; /* Check if the device is busy. */ if ((handle->txInProgress) || (handle->rxInProgress)) { return kStatus_FLEXIO_SPI_Busy; } /* Check if input parameter invalid. */ if (((xfer->txData == NULL) && (xfer->rxData == NULL)) || (xfer->dataSize == 0U)) { return kStatus_InvalidArgument; } /* configure data mode. */ if ((xfer->flags == (uint8_t)kFLEXIO_SPI_8bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_8bitLsb)) { dataMode = 8U * 2U - 1U; } else if ((xfer->flags == (uint8_t)kFLEXIO_SPI_16bitMsb) || (xfer->flags == (uint8_t)kFLEXIO_SPI_16bitLsb)) { dataMode = 16U * 2U - 1U; } else { dataMode = 8U * 2U - 1U; } base->flexioBase->TIMCMP[base->timerIndex[0]] = dataMode; FLEXIO_SPI_EDMAConfig(base, handle, xfer); return kStatus_Success; }