blob: 48f01b185172731c52d94f33d4c464e88dcc9341 [file] [log] [blame]
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* o Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fsl_lpspi_edma.h"
/***********************************************************************************************************************
* Definitons
***********************************************************************************************************************/
/*!
* @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);
/*!
* @brief Get instance number for LPSPI module.
* This is not a public API and it's extern from fsl_lpspi.c.
* @param base LPSPI peripheral base address
*/
extern uint32_t LPSPI_GetInstance(LPSPI_Type *base);
/*!
* @brief Check the argument for transfer .
* This is not a public API. It's extern from fsl_lpspi.c.
*/
extern bool LPSPI_CheckTransferArgument(lpspi_transfer_t *transfer, uint32_t bitsPerFrame, uint32_t bytesPerFrame);
static void LPSPI_SeparateEdmaReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap);
/***********************************************************************************************************************
* Variables
***********************************************************************************************************************/
/*! @brief Pointers to lpspi edma handles for each instance. */
static lpspi_master_edma_private_handle_t s_lpspiMasterEdmaPrivateHandle[FSL_FEATURE_SOC_LPSPI_COUNT];
static lpspi_slave_edma_private_handle_t s_lpspiSlaveEdmaPrivateHandle[FSL_FEATURE_SOC_LPSPI_COUNT];
/*! @brief Global variable for dummy data value setting. */
extern volatile uint8_t s_dummyData[];
/***********************************************************************************************************************
* 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 = readData;
++rxData;
}
else
{
*rxData = readData >> 24;
++rxData;
}
break;
case 2:
if (!isByteSwap)
{
*rxData = readData;
++rxData;
*rxData = readData >> 8;
++rxData;
}
else
{
*rxData = readData >> 16;
++rxData;
*rxData = readData >> 24;
++rxData;
}
break;
case 4:
*rxData = readData;
++rxData;
*rxData = readData >> 8;
++rxData;
*rxData = readData >> 16;
++rxData;
*rxData = readData >> 24;
++rxData;
break;
default:
assert(false);
break;
}
}
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. */
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;
}
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) + 1;
uint32_t bytesPerFrame = (bitsPerFrame + 7) / 8;
uint32_t temp = 0U;
if (!LPSPI_CheckTransferArgument(transfer, bitsPerFrame, bytesPerFrame))
{
return kStatus_InvalidArgument;
}
/*And since the dma transfer can not support 3 bytes .*/
if ((bytesPerFrame % 4U) == 3)
{
return kStatus_InvalidArgument;
}
/* Check that we're not busy.*/
if (handle->state == kLPSPI_Busy)
{
return kStatus_LPSPI_Busy;
}
handle->state = 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 = s_dummyData[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 + 3) / 4);
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 & kLPSPI_MasterPcsContinuous);
handle->isByteSwap = (bool)(transfer->configFlags & kLPSPI_MasterByteSwap);
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 (!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) && (handle->rxData))
{
return kStatus_InvalidArgument;
}
}
/*Flush FIFO , clear status , disable all the inerrupts.*/
LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(base, 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(handle->isPcsContinuous) | LPSPI_TCR_CONTC(0U) | LPSPI_TCR_BYSW(handle->isByteSwap) |
LPSPI_TCR_PCS(whichPcs);
isThereExtraTxBytes = false;
handle->isThereExtraRxBytes = false;
/*Calculate the bytes for write/read the TX/RX register each time*/
if (bytesPerFrame <= 4)
{
handle->bytesEachWrite = bytesPerFrame;
handle->bytesEachRead = bytesPerFrame;
handle->bytesLastRead = bytesPerFrame;
}
else
{
handle->bytesEachWrite = 4;
handle->bytesEachRead = 4;
handle->bytesLastRead = 4;
if ((transfer->dataSize % 4) != 0)
{
bytesLastWrite = transfer->dataSize % 4;
handle->bytesLastRead = bytesLastWrite;
isThereExtraTxBytes = true;
--handle->writeRegRemainingTimes;
--handle->readRegRemainingTimes;
handle->isThereExtraRxBytes = true;
}
}
LPSPI_DisableDMA(base, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiMasterCallback,
&s_lpspiMasterEdmaPrivateHandle[instance]);
/*Rx*/
EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel);
if (handle->rxData)
{
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 = transferConfigRx.minorLoopBytes;
EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel,
&transferConfigRx, NULL);
EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel,
kEDMA_MajorInterruptEnable);
/*Tx*/
EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel);
if (isThereExtraTxBytes)
{
if (handle->txData)
{
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)
{
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 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, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
LPSPI_Enable(base, true);
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;
LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable);
if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes)
{
while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0)
{
}
readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base);
if (lpspiEdmaPrivateHandle->handle->rxData)
{
LPSPI_SeparateEdmaReadData(
&(lpspiEdmaPrivateHandle->handle->rxData[lpspiEdmaPrivateHandle->handle->rxRemainingByteCount -
lpspiEdmaPrivateHandle->handle->bytesLastRead]),
readData, lpspiEdmaPrivateHandle->handle->bytesLastRead, lpspiEdmaPrivateHandle->handle->isByteSwap);
}
}
lpspiEdmaPrivateHandle->handle->state = kLPSPI_Idle;
if (lpspiEdmaPrivateHandle->handle->callback)
{
lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle,
kStatus_Success, lpspiEdmaPrivateHandle->handle->userData);
}
}
void LPSPI_MasterTransferAbortEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle)
{
assert(handle);
LPSPI_DisableDMA(base, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle);
EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle);
handle->state = kLPSPI_Idle;
}
status_t LPSPI_MasterTransferGetCountEDMA(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, size_t *count)
{
assert(handle);
if (!count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != 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;
}
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. */
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;
}
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) + 1;
uint32_t bytesPerFrame = (bitsPerFrame + 7) / 8;
uint32_t temp = 0U;
uint8_t dummyData = s_dummyData[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) == 3)
{
return kStatus_InvalidArgument;
}
/* Check that we're not busy.*/
if (handle->state == kLPSPI_Busy)
{
return kStatus_LPSPI_Busy;
}
handle->state = 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 + 3) / 4);
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 = (bool)(transfer->configFlags & kLPSPI_MasterByteSwap);
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 (!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) && (handle->rxData))
{
return kStatus_InvalidArgument;
}
}
/*Flush FIFO , clear status , disable all the inerrupts.*/
LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(base, 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(handle->isByteSwap) | LPSPI_TCR_PCS(whichPcs);
isThereExtraTxBytes = false;
handle->isThereExtraRxBytes = false;
/*Calculate the bytes for write/read the TX/RX register each time*/
if (bytesPerFrame <= 4)
{
handle->bytesEachWrite = bytesPerFrame;
handle->bytesEachRead = bytesPerFrame;
handle->bytesLastRead = bytesPerFrame;
}
else
{
handle->bytesEachWrite = 4;
handle->bytesEachRead = 4;
handle->bytesLastRead = 4;
if ((transfer->dataSize % 4) != 0)
{
bytesLastWrite = transfer->dataSize % 4;
handle->bytesLastRead = bytesLastWrite;
isThereExtraTxBytes = true;
--handle->writeRegRemainingTimes;
handle->isThereExtraRxBytes = true;
--handle->readRegRemainingTimes;
}
}
LPSPI_DisableDMA(base, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
EDMA_SetCallback(handle->edmaRxRegToRxDataHandle, EDMA_LpspiSlaveCallback,
&s_lpspiSlaveEdmaPrivateHandle[instance]);
/*Rx*/
if (handle->readRegRemainingTimes > 0)
{
EDMA_ResetChannel(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel);
if (handle->rxData)
{
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 = transferConfigRx.minorLoopBytes;
EDMA_SetTransferConfig(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel,
&transferConfigRx, NULL);
EDMA_EnableChannelInterrupts(handle->edmaRxRegToRxDataHandle->base, handle->edmaRxRegToRxDataHandle->channel,
kEDMA_MajorInterruptEnable);
}
/*Tx*/
EDMA_ResetChannel(handle->edmaTxDataToTxRegHandle->base, handle->edmaTxDataToTxRegHandle->channel);
if (isThereExtraTxBytes)
{
if (handle->txData)
{
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)
{
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, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
LPSPI_Enable(base, true);
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;
LPSPI_DisableDMA(lpspiEdmaPrivateHandle->base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable);
if (lpspiEdmaPrivateHandle->handle->isThereExtraRxBytes)
{
while (LPSPI_GetRxFifoCount(lpspiEdmaPrivateHandle->base) == 0)
{
}
readData = LPSPI_ReadData(lpspiEdmaPrivateHandle->base);
if (lpspiEdmaPrivateHandle->handle->rxData)
{
LPSPI_SeparateEdmaReadData(
&(lpspiEdmaPrivateHandle->handle->rxData[lpspiEdmaPrivateHandle->handle->rxRemainingByteCount -
lpspiEdmaPrivateHandle->handle->bytesLastRead]),
readData, lpspiEdmaPrivateHandle->handle->bytesLastRead, lpspiEdmaPrivateHandle->handle->isByteSwap);
}
}
lpspiEdmaPrivateHandle->handle->state = kLPSPI_Idle;
if (lpspiEdmaPrivateHandle->handle->callback)
{
lpspiEdmaPrivateHandle->handle->callback(lpspiEdmaPrivateHandle->base, lpspiEdmaPrivateHandle->handle,
kStatus_Success, lpspiEdmaPrivateHandle->handle->userData);
}
}
void LPSPI_SlaveTransferAbortEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle)
{
assert(handle);
LPSPI_DisableDMA(base, kLPSPI_RxDmaEnable | kLPSPI_TxDmaEnable);
EDMA_AbortTransfer(handle->edmaRxRegToRxDataHandle);
EDMA_AbortTransfer(handle->edmaTxDataToTxRegHandle);
handle->state = kLPSPI_Idle;
}
status_t LPSPI_SlaveTransferGetCountEDMA(LPSPI_Type *base, lpspi_slave_edma_handle_t *handle, size_t *count)
{
assert(handle);
if (!count)
{
return kStatus_InvalidArgument;
}
/* Catch when there is not an active transfer. */
if (handle->state != 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;
}