/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_lpspi_edma.h" /*********************************************************************************************************************** * Definitions ***********************************************************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.lpspi_edma" #endif /*! * @brief Structure definition for dspi_master_edma_private_handle_t. The structure is private. */ typedef struct _lpspi_master_edma_private_handle { LPSPI_Type *base; /*!< LPSPI peripheral base address. */ lpspi_master_edma_handle_t *handle; /*!< lpspi_master_edma_handle_t handle */ } lpspi_master_edma_private_handle_t; /*! * @brief Structure definition for dspi_slave_edma_private_handle_t. The structure is private. */ typedef struct _lpspi_slave_edma_private_handle { LPSPI_Type *base; /*!< LPSPI peripheral base address. */ lpspi_slave_edma_handle_t *handle; /*!< lpspi_slave_edma_handle_t handle */ } lpspi_slave_edma_private_handle_t; /*********************************************************************************************************************** * Prototypes ***********************************************************************************************************************/ /*! * @brief EDMA_LpspiMasterCallback after the LPSPI master transfer completed by using EDMA. * This is not a public API. */ static void EDMA_LpspiMasterCallback(edma_handle_t *edmaHandle, void *g_lpspiEdmaPrivateHandle, bool transferDone, uint32_t tcds); /*! * @brief EDMA_LpspiSlaveCallback after the LPSPI slave transfer completed by using EDMA. * This is not a public API. */ static void EDMA_LpspiSlaveCallback(edma_handle_t *edmaHandle, void *g_lpspiEdmaPrivateHandle, bool transferDone, uint32_t tcds); static void LPSPI_SeparateEdmaReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap); /*********************************************************************************************************************** * Variables ***********************************************************************************************************************/ /*! @brief Pointers to lpspi bases for each instance. */ static LPSPI_Type *const s_lpspiBases[] = LPSPI_BASE_PTRS; /*! @brief Pointers to lpspi edma handles for each instance. */ static lpspi_master_edma_private_handle_t s_lpspiMasterEdmaPrivateHandle[ARRAY_SIZE(s_lpspiBases)]; static lpspi_slave_edma_private_handle_t s_lpspiSlaveEdmaPrivateHandle[ARRAY_SIZE(s_lpspiBases)]; /*********************************************************************************************************************** * Code ***********************************************************************************************************************/ static void LPSPI_SeparateEdmaReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap) { assert(rxData); switch (bytesEachRead) { case 1: if (!isByteSwap) { *rxData = (uint8_t)readData; ++rxData; } else { *rxData = (uint8_t)(readData >> 24); ++rxData; } break; case 2: if (!isByteSwap) { *rxData = (uint8_t)readData; ++rxData; *rxData = (uint8_t)(readData >> 8); ++rxData; } else { *rxData = (uint8_t)(readData >> 16); ++rxData; *rxData = (uint8_t)(readData >> 24); ++rxData; } break; case 4: *rxData = (uint8_t)readData; ++rxData; *rxData = (uint8_t)(readData >> 8); ++rxData; *rxData = (uint8_t)(readData >> 16); ++rxData; *rxData = (uint8_t)(readData >> 24); ++rxData; break; default: assert(false); break; } } /*! * brief Initializes the LPSPI master eDMA handle. * * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a * specified LPSPI instance, call this API once to get the initialized handle. * * Note that the LPSPI eDMA has a separated (Rx and Rx as two sources) or shared (Rx and Tx are the same source) DMA * request source. * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and * Tx DMAMUX source for edmaIntermediaryToTxRegHandle. * (2) For a shared DMA request source, enable and set the Rx/Rx DMAMUX source for edmaRxRegToRxDataHandle. * * param base LPSPI peripheral base address. * param handle LPSPI handle pointer to lpspi_master_edma_handle_t. * param callback LPSPI callback. * param userData callback function parameter. * param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. * param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. */ void LPSPI_MasterTransferCreateHandleEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, lpspi_master_edma_transfer_callback_t callback, void *userData, edma_handle_t *edmaRxRegToRxDataHandle, edma_handle_t *edmaTxDataToTxRegHandle) { assert(handle); assert(edmaRxRegToRxDataHandle); assert(edmaTxDataToTxRegHandle); /* Zero the handle. */ (void)memset(handle, 0, sizeof(*handle)); uint32_t instance = LPSPI_GetInstance(base); s_lpspiMasterEdmaPrivateHandle[instance].base = base; s_lpspiMasterEdmaPrivateHandle[instance].handle = handle; handle->callback = callback; handle->userData = userData; handle->edmaRxRegToRxDataHandle = edmaRxRegToRxDataHandle; handle->edmaTxDataToTxRegHandle = edmaTxDataToTxRegHandle; } /*! * brief LPSPI master transfer data using eDMA. * * This function transfers data using eDMA. This is a non-blocking function, which returns right away. When all data * is transferred, the callback function is called. * * Note: * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. * For bytesPerFrame greater than 4: * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. * param transfer pointer to lpspi_transfer_t structure. * return status of status_t. */ status_t LPSPI_MasterTransferEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, lpspi_transfer_t *transfer) { assert(handle); assert(transfer); uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1U; uint32_t bytesPerFrame = (bitsPerFrame + 7U) / 8U; uint32_t temp = 0U; bool isByteSwap; bool isPcsContinuous; if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame)) { return kStatus_InvalidArgument; } /*And since the dma transfer can not support 3 bytes .*/ if ((bytesPerFrame % 4U) == 3U) { return kStatus_InvalidArgument; } /* Check that we're not busy.*/ if (handle->state == (uint8_t)kLPSPI_Busy) { return kStatus_LPSPI_Busy; } handle->state = (uint8_t)kLPSPI_Busy; uint32_t instance = LPSPI_GetInstance(base); uint32_t rxAddr = LPSPI_GetRxRegisterAddress(base); uint32_t txAddr = LPSPI_GetTxRegisterAddress(base); uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; /*Because DMA is fast enough , so set the RX and TX watermarks to 0 .*/ uint8_t txWatermark = 0; uint8_t rxWatermark = 0; /*Used for byte swap*/ uint32_t dif = 0; uint8_t bytesLastWrite = 0; bool isThereExtraTxBytes = false; uint8_t dummyData = g_lpspiDummyData[instance]; edma_transfer_config_t transferConfigRx; edma_transfer_config_t transferConfigTx; edma_tcd_t *softwareTCD_extraBytes = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[1]) & (~0x1FU)); edma_tcd_t *softwareTCD_pcsContinuous = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[2]) & (~0x1FU)); handle->txData = transfer->txData; handle->rxData = transfer->rxData; handle->txRemainingByteCount = transfer->dataSize; handle->rxRemainingByteCount = transfer->dataSize; handle->totalByteCount = transfer->dataSize; handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U); handle->readRegRemainingTimes = handle->writeRegRemainingTimes; handle->txBuffIfNull = ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); /*The TX and RX FIFO sizes are always the same*/ handle->fifoSize = LPSPI_GetRxFifoSize(base); handle->isPcsContinuous = (bool)(transfer->configFlags & (uint32_t)kLPSPI_MasterPcsContinuous); handle->isByteSwap = (bool)(transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap); isPcsContinuous = handle->isPcsContinuous; isByteSwap = handle->isByteSwap; LPSPI_SetFifoWatermarks(base, txWatermark, rxWatermark); /*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ LPSPI_Enable(base, false); base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ temp = base->CFGR1; temp &= LPSPI_CFGR1_PINCFG_MASK; if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) { if (NULL == handle->txData) { base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; } /* The 3-wire mode can't send and receive data at the same time. */ if ((handle->txData != NULL) && (handle->rxData != NULL)) { return kStatus_InvalidArgument; } } LPSPI_Enable(base, true); /*Flush FIFO , clear status , disable all the inerrupts.*/ LPSPI_FlushFifo(base, true, true); LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); /* For DMA transfer , we'd better not masked the transmit data and receive data in TCR since the transfer flow is * hard to controlled by software. */ base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_BYSW_MASK | LPSPI_TCR_PCS_MASK)) | LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(0U) | LPSPI_TCR_BYSW(isByteSwap) | LPSPI_TCR_PCS(whichPcs); isThereExtraTxBytes = false; handle->isThereExtraRxBytes = false; /*Calculate the bytes for write/read the TX/RX register each time*/ if (bytesPerFrame <= 4U) { handle->bytesEachWrite = (uint8_t)bytesPerFrame; handle->bytesEachRead = (uint8_t)bytesPerFrame; handle->bytesLastRead = (uint8_t)bytesPerFrame; } else { handle->bytesEachWrite = 4U; handle->bytesEachRead = 4U; handle->bytesLastRead = 4U; if ((transfer->dataSize % 4U) != 0U) { bytesLastWrite = (uint8_t)(transfer->dataSize % 4U); handle->bytesLastRead = bytesLastWrite; isThereExtraTxBytes = true; --handle->writeRegRemainingTimes; --handle->readRegRemainingTimes; handle->isThereExtraRxBytes = true; } } LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiMasterCallback, &s_lpspiMasterEdmaPrivateHandle[instance]); /*Rx*/ EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); if (handle->rxData != NULL) { transferConfigRx.destAddr = (uint32_t) & (handle->rxData[0]); transferConfigRx.destOffset = 1; } else { transferConfigRx.destAddr = (uint32_t) & (handle->rxBuffIfNull); transferConfigRx.destOffset = 0; } transferConfigRx.destTransferSize = kEDMA_TransferSize1Bytes; dif = 0; switch (handle->bytesEachRead) { case (1U): transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; transferConfigRx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigRx.srcTransferSize = kEDMA_TransferSize2Bytes; transferConfigRx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; case (4U): transferConfigRx.srcTransferSize = kEDMA_TransferSize4Bytes; transferConfigRx.minorLoopBytes = 4; break; default: transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; transferConfigRx.minorLoopBytes = 1; assert(false); break; } transferConfigRx.srcAddr = (uint32_t)rxAddr + dif; transferConfigRx.srcOffset = 0; transferConfigRx.majorLoopCounts = handle->readRegRemainingTimes; /* Store the initially configured eDMA minor byte transfer count into the LPSPI handle */ handle->nbytes = (uint8_t)transferConfigRx.minorLoopBytes; EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, &transferConfigRx, NULL); EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, (uint32_t)kEDMA_MajorInterruptEnable); /*Tx*/ EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel); if (isThereExtraTxBytes) { if (handle->txData != NULL) { transferConfigTx.srcAddr = (uint32_t) & (transfer->txData[transfer->dataSize - bytesLastWrite]); transferConfigTx.srcOffset = 1; } else { transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); transferConfigTx.srcOffset = 0; } transferConfigTx.destOffset = 0; transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; dif = 0; switch (bytesLastWrite) { case (1U): transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; transferConfigTx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; default: transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; assert(false); break; } transferConfigTx.destAddr = (uint32_t)txAddr + dif; transferConfigTx.majorLoopCounts = 1; EDMA_TcdReset(softwareTCD_extraBytes); if (handle->isPcsContinuous) { EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, softwareTCD_pcsContinuous); } else { EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, NULL); } } if (handle->isPcsContinuous) { handle->transmitCommand = base->TCR & ~(LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK); transferConfigTx.srcAddr = (uint32_t) & (handle->transmitCommand); transferConfigTx.srcOffset = 0; transferConfigTx.destAddr = (uint32_t) & (base->TCR); transferConfigTx.destOffset = 0; transferConfigTx.srcTransferSize = kEDMA_TransferSize4Bytes; transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; transferConfigTx.minorLoopBytes = 4; transferConfigTx.majorLoopCounts = 1; EDMA_TcdReset(softwareTCD_pcsContinuous); EDMA_TcdSetTransferConfig(softwareTCD_pcsContinuous, &transferConfigTx, NULL); } if (handle->txData != NULL) { transferConfigTx.srcAddr = (uint32_t)(handle->txData); transferConfigTx.srcOffset = 1; } else { transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); transferConfigTx.srcOffset = 0; } transferConfigTx.destOffset = 0; transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; dif = 0U; switch (handle->bytesEachRead) { case (1U): transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; transferConfigTx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; case (4U): transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; transferConfigTx.minorLoopBytes = 4; break; default: transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; assert(false); break; } transferConfigTx.destAddr = (uint32_t)txAddr + dif; transferConfigTx.majorLoopCounts = handle->writeRegRemainingTimes; if (isThereExtraTxBytes) { EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, &transferConfigTx, softwareTCD_extraBytes); } else if (handle->isPcsContinuous) { EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, &transferConfigTx, softwareTCD_pcsContinuous); } else { EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, &transferConfigTx, NULL); } EDMA_StartTransfer(handle->edmaTxDataToTxRegHandle); EDMA_StartTransfer(handle->edmaRxRegToRxDataHandle); LPSPI_EnableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); return kStatus_Success; } static void EDMA_LpspiMasterCallback(edma_handle_t *edmaHandle, void *g_lpspiEdmaPrivateHandle, bool transferDone, uint32_t tcds) { assert(edmaHandle); assert(g_lpspiEdmaPrivateHandle); uint32_t readData; lpspi_master_edma_private_handle_t *lpspiEdmaPrivateHandle; lpspiEdmaPrivateHandle = (lpspi_master_edma_private_handle_t *)g_lpspiEdmaPrivateHandle; size_t rxRemainingByteCount = lpspiEdmaPrivateHandle->handle->rxRemainingByteCount; uint8_t bytesLastRead = lpspiEdmaPrivateHandle->handle->bytesLastRead; bool isByteSwap = lpspiEdmaPrivateHandle->handle->isByteSwap; LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, (uint32_t)kLPSPI_TxDmaEnable | (uint32_t)kLPSPI_RxDmaEnable); if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes) { while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0U) { } readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base); if (lpspiEdmaPrivateHandle->handle->rxData != NULL) { LPSPI_SeparateEdmaReadData(&(lpspiEdmaPrivateHandle->handle->rxData[rxRemainingByteCount - bytesLastRead]), readData, bytesLastRead, isByteSwap); } } lpspiEdmaPrivateHandle->handle->state = (uint8_t)kLPSPI_Idle; if (lpspiEdmaPrivateHandle->handle->callback != NULL) { lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle, kStatus_Success, lpspiEdmaPrivateHandle->handle->userData); } } /*! * brief LPSPI master aborts a transfer which is using eDMA. * * This function aborts a transfer which is using eDMA. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. */ void LPSPI_MasterTransferAbortEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle) { assert(handle); LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle); EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle); handle->state = (uint8_t)kLPSPI_Idle; } /*! * brief Gets the master eDMA transfer remaining bytes. * * This function gets the master eDMA transfer remaining bytes. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_master_edma_handle_t structure which stores the transfer state. * param count Number of bytes transferred so far by the EDMA transaction. * return status of status_t. */ status_t LPSPI_MasterTransferGetCountEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, size_t *count) { assert(handle); if (NULL == count) { return kStatus_InvalidArgument; } /* Catch when there is not an active transfer. */ if (handle->state != (uint8_t)kLPSPI_Busy) { *count = 0; return kStatus_NoTransferInProgress; } size_t remainingByte; remainingByte = (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); *count = handle->totalByteCount - remainingByte; return kStatus_Success; } /*! * brief Initializes the LPSPI slave eDMA handle. * * This function initializes the LPSPI eDMA handle which can be used for other LPSPI transactional APIs. Usually, for a * specified LPSPI instance, call this API once to get the initialized handle. * * Note that LPSPI eDMA has a separated (Rx and Tx as two sources) or shared (Rx and Tx as the same source) DMA request * source. * * (1) For a separated DMA request source, enable and set the Rx DMAMUX source for edmaRxRegToRxDataHandle and * Tx DMAMUX source for edmaTxDataToTxRegHandle. * (2) For a shared DMA request source, enable and set the Rx/Rx DMAMUX source for edmaRxRegToRxDataHandle . * * param base LPSPI peripheral base address. * param handle LPSPI handle pointer to lpspi_slave_edma_handle_t. * param callback LPSPI callback. * param userData callback function parameter. * param edmaRxRegToRxDataHandle edmaRxRegToRxDataHandle pointer to edma_handle_t. * param edmaTxDataToTxRegHandle edmaTxDataToTxRegHandle pointer to edma_handle_t. */ void LPSPI_SlaveTransferCreateHandleEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, lpspi_slave_edma_transfer_callback_t callback, void *userData, edma_handle_t *edmaRxRegToRxDataHandle, edma_handle_t *edmaTxDataToTxRegHandle) { assert(handle); assert(edmaRxRegToRxDataHandle); assert(edmaTxDataToTxRegHandle); /* Zero the handle. */ (void)memset(handle, 0, sizeof(*handle)); uint32_t instance = LPSPI_GetInstance(base); s_lpspiSlaveEdmaPrivateHandle[instance].base = base; s_lpspiSlaveEdmaPrivateHandle[instance].handle = handle; handle->callback = callback; handle->userData = userData; handle->edmaRxRegToRxDataHandle = edmaRxRegToRxDataHandle; handle->edmaTxDataToTxRegHandle = edmaTxDataToTxRegHandle; } /*! * brief LPSPI slave transfers data using eDMA. * * This function transfers data using eDMA. This is a non-blocking function, which return right away. When all data * is transferred, the callback function is called. * * Note: * The transfer data size should be an integer multiple of bytesPerFrame if bytesPerFrame is less than or equal to 4. * For bytesPerFrame greater than 4: * The transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not an integer multiple of 4. * Otherwise, the transfer data size can be an integer multiple of bytesPerFrame. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. * param transfer pointer to lpspi_transfer_t structure. * return status of status_t. */ status_t LPSPI_SlaveTransferEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, lpspi_transfer_t *transfer) { assert(handle); assert(transfer); uint32_t bitsPerFrame = ((base->TCR & LPSPI_TCR_FRAMESZ_MASK) >> LPSPI_TCR_FRAMESZ_SHIFT) + 1U; uint32_t bytesPerFrame = (bitsPerFrame + 7U) / 8U; uint32_t temp = 0U; bool isByteSwap; uint8_t dummyData = g_lpspiDummyData[LPSPI_GetInstance(base)]; if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame)) { return kStatus_InvalidArgument; } /*And since the dma transfer can not support 3 bytes .*/ if ((bytesPerFrame % 4U) == 3U) { return kStatus_InvalidArgument; } /* Check that we're not busy.*/ if (handle->state == (uint8_t)kLPSPI_Busy) { return kStatus_LPSPI_Busy; } handle->state = (uint8_t)kLPSPI_Busy; uint32_t rxAddr = LPSPI_GetRxRegisterAddress(base); uint32_t txAddr = LPSPI_GetTxRegisterAddress(base); edma_tcd_t *softwareTCD_extraBytes = (edma_tcd_t *)((uint32_t)(&handle->lpspiSoftwareTCD[1]) & (~0x1FU)); uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT; /*Because DMA is fast enough , so set the RX and TX watermarks to 0 .*/ uint8_t txWatermark = 0; uint8_t rxWatermark = 0; /*Used for byte swap*/ uint32_t dif = 0; uint8_t bytesLastWrite = 0; uint32_t instance = LPSPI_GetInstance(base); edma_transfer_config_t transferConfigRx; edma_transfer_config_t transferConfigTx; bool isThereExtraTxBytes = false; handle->txData = transfer->txData; handle->rxData = transfer->rxData; handle->txRemainingByteCount = transfer->dataSize; handle->rxRemainingByteCount = transfer->dataSize; handle->totalByteCount = transfer->dataSize; handle->writeRegRemainingTimes = (transfer->dataSize / bytesPerFrame) * ((bytesPerFrame + 3U) / 4U); handle->readRegRemainingTimes = handle->writeRegRemainingTimes; handle->txBuffIfNull = ((uint32_t)dummyData) | ((uint32_t)dummyData << 8) | ((uint32_t)dummyData << 16) | ((uint32_t)dummyData << 24); /*The TX and RX FIFO sizes are always the same*/ handle->fifoSize = LPSPI_GetRxFifoSize(base); handle->isByteSwap = ((transfer->configFlags & (uint32_t)kLPSPI_MasterByteSwap) != 0U); isByteSwap = handle->isByteSwap; LPSPI_SetFifoWatermarks(base, txWatermark, rxWatermark); /*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */ LPSPI_Enable(base, false); base->CFGR1 &= (~LPSPI_CFGR1_NOSTALL_MASK); /* Check if using 3-wire mode and the txData is NULL, set the output pin to tristated. */ temp = base->CFGR1; temp &= LPSPI_CFGR1_PINCFG_MASK; if ((temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdiInSdiOut)) || (temp == LPSPI_CFGR1_PINCFG(kLPSPI_SdoInSdoOut))) { if (NULL == handle->txData) { base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK; } /* The 3-wire mode can't send and receive data at the same time. */ if ((handle->txData != NULL) && (handle->rxData != NULL)) { return kStatus_InvalidArgument; } } LPSPI_Enable(base, true); /*Flush FIFO , clear status , disable all the inerrupts.*/ LPSPI_FlushFifo(base, true, true); LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag); LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable); /* For DMA transfer , we'd better not masked the transmit data and receive data in TCR since the transfer flow is * hard to controlled by software. */ base->TCR = (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_BYSW_MASK)) | LPSPI_TCR_CONTC(0U) | LPSPI_TCR_BYSW(isByteSwap) | LPSPI_TCR_PCS(whichPcs); isThereExtraTxBytes = false; handle->isThereExtraRxBytes = false; /*Calculate the bytes for write/read the TX/RX register each time*/ if (bytesPerFrame <= 4U) { handle->bytesEachWrite = (uint8_t)bytesPerFrame; handle->bytesEachRead = (uint8_t)bytesPerFrame; handle->bytesLastRead = (uint8_t)bytesPerFrame; } else { handle->bytesEachWrite = 4U; handle->bytesEachRead = 4U; handle->bytesLastRead = 4U; if ((transfer->dataSize % 4U) != 0U) { bytesLastWrite = (uint8_t)(transfer->dataSize % 4U); handle->bytesLastRead = bytesLastWrite; isThereExtraTxBytes = true; --handle->writeRegRemainingTimes; handle->isThereExtraRxBytes = true; --handle->readRegRemainingTimes; } } LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiSlaveCallback, &s_lpspiSlaveEdmaPrivateHandle[instance]); /*Rx*/ if (handle->readRegRemainingTimes > 0U) { EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); if (handle->rxData != NULL) { transferConfigRx.destAddr = (uint32_t) & (handle->rxData[0]); transferConfigRx.destOffset = 1; } else { transferConfigRx.destAddr = (uint32_t) & (handle->rxBuffIfNull); transferConfigRx.destOffset = 0; } transferConfigRx.destTransferSize = kEDMA_TransferSize1Bytes; dif = 0; switch (handle->bytesEachRead) { case (1U): transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; transferConfigRx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigRx.srcTransferSize = kEDMA_TransferSize2Bytes; transferConfigRx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; case (4U): transferConfigRx.srcTransferSize = kEDMA_TransferSize4Bytes; transferConfigRx.minorLoopBytes = 4; break; default: transferConfigRx.srcTransferSize = kEDMA_TransferSize1Bytes; transferConfigRx.minorLoopBytes = 1; assert(false); break; } transferConfigRx.srcAddr = (uint32_t)rxAddr + dif; transferConfigRx.srcOffset = 0; transferConfigRx.majorLoopCounts = handle->readRegRemainingTimes; /* Store the initially configured eDMA minor byte transfer count into the DSPI handle */ handle->nbytes = (uint8_t)transferConfigRx.minorLoopBytes; EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, &transferConfigRx, NULL); EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel, (uint32_t)kEDMA_MajorInterruptEnable); } /*Tx*/ EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel); if (isThereExtraTxBytes) { if (handle->txData != NULL) { transferConfigTx.srcAddr = (uint32_t) & (transfer->txData[transfer->dataSize - bytesLastWrite]); transferConfigTx.srcOffset = 1; } else { transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); transferConfigTx.srcOffset = 0; } transferConfigTx.destOffset = 0; transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; dif = 0; switch (bytesLastWrite) { case (1U): transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; transferConfigTx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; default: transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; assert(false); break; } transferConfigTx.destAddr = (uint32_t)txAddr + dif; transferConfigTx.majorLoopCounts = 1; EDMA_TcdReset(softwareTCD_extraBytes); EDMA_TcdSetTransferConfig(softwareTCD_extraBytes, &transferConfigTx, NULL); } if (handle->txData != NULL) { transferConfigTx.srcAddr = (uint32_t)(handle->txData); transferConfigTx.srcOffset = 1; } else { transferConfigTx.srcAddr = (uint32_t)(&handle->txBuffIfNull); transferConfigTx.srcOffset = 0; } transferConfigTx.destOffset = 0; transferConfigTx.srcTransferSize = kEDMA_TransferSize1Bytes; dif = 0; switch (handle->bytesEachRead) { case (1U): transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; if (handle->isByteSwap) { dif = 3; } break; case (2U): transferConfigTx.destTransferSize = kEDMA_TransferSize2Bytes; transferConfigTx.minorLoopBytes = 2; if (handle->isByteSwap) { dif = 2; } break; case (4U): transferConfigTx.destTransferSize = kEDMA_TransferSize4Bytes; transferConfigTx.minorLoopBytes = 4; break; default: transferConfigTx.destTransferSize = kEDMA_TransferSize1Bytes; transferConfigTx.minorLoopBytes = 1; assert(false); break; } transferConfigTx.destAddr = (uint32_t)txAddr + dif; transferConfigTx.majorLoopCounts = handle->writeRegRemainingTimes; if (isThereExtraTxBytes) { EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, &transferConfigTx, softwareTCD_extraBytes); } else { EDMA_SetTransferConfig(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel, &transferConfigTx, NULL); } EDMA_StartTransfer(handle->edmaTxDataToTxRegHandle); EDMA_StartTransfer(handle->edmaRxRegToRxDataHandle); LPSPI_EnableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); return kStatus_Success; } static void EDMA_LpspiSlaveCallback(edma_handle_t *edmaHandle, void *g_lpspiEdmaPrivateHandle, bool transferDone, uint32_t tcds) { assert(edmaHandle); assert(g_lpspiEdmaPrivateHandle); uint32_t readData; lpspi_slave_edma_private_handle_t *lpspiEdmaPrivateHandle; lpspiEdmaPrivateHandle = (lpspi_slave_edma_private_handle_t *)g_lpspiEdmaPrivateHandle; size_t rxRemainingByteCount = lpspiEdmaPrivateHandle->handle->rxRemainingByteCount; uint8_t bytesLastRead = lpspiEdmaPrivateHandle->handle->bytesLastRead; bool isByteSwap = lpspiEdmaPrivateHandle->handle->isByteSwap; LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, (uint32_t)kLPSPI_TxDmaEnable | (uint32_t)kLPSPI_RxDmaEnable); if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes) { while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0U) { } readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base); if (lpspiEdmaPrivateHandle->handle->rxData != NULL) { LPSPI_SeparateEdmaReadData(&(lpspiEdmaPrivateHandle->handle->rxData[rxRemainingByteCount - bytesLastRead]), readData, bytesLastRead, isByteSwap); } } lpspiEdmaPrivateHandle->handle->state = (uint8_t)kLPSPI_Idle; if (lpspiEdmaPrivateHandle->handle->callback != NULL) { lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle, kStatus_Success, lpspiEdmaPrivateHandle->handle->userData); } } /*! * brief LPSPI slave aborts a transfer which is using eDMA. * * This function aborts a transfer which is using eDMA. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. */ void LPSPI_SlaveTransferAbortEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle) { assert(handle); LPSPI_DisableDMA(base, (uint32_t)kLPSPI_RxDmaEnable | (uint32_t)kLPSPI_TxDmaEnable); EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle); EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle); handle->state = (uint8_t)kLPSPI_Idle; } /*! * brief Gets the slave eDMA transfer remaining bytes. * * This function gets the slave eDMA transfer remaining bytes. * * param base LPSPI peripheral base address. * param handle pointer to lpspi_slave_edma_handle_t structure which stores the transfer state. * param count Number of bytes transferred so far by the eDMA transaction. * return status of status_t. */ status_t LPSPI_SlaveTransferGetCountEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, size_t *count) { assert(handle); if (NULL == count) { return kStatus_InvalidArgument; } /* Catch when there is not an active transfer. */ if (handle->state != (uint8_t)kLPSPI_Busy) { *count = 0; return kStatus_NoTransferInProgress; } size_t remainingByte; remainingByte = (uint32_t)handle->nbytes * EDMA_GetRemainingMajorLoopCount(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel); *count = handle->totalByteCount - remainingByte; return kStatus_Success; }