/*
 * 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.h"

/*******************************************************************************
* Definitions
******************************************************************************/
/*!
 * @brief Default watermark values.
 *
 * The default watermarks are set to zero.
 */
enum _lpspi_default_watermarks
{
    kLpspiDefaultTxWatermark = 0,
    kLpspiDefaultRxWatermark = 0,
};

/*! @brief Typedef for master interrupt handler. */
typedef void (*lpspi_master_isr_t)(LPSPI_Type *base, lpspi_master_handle_t *handle);

/*! @brief Typedef for slave interrupt handler. */
typedef void (*lpspi_slave_isr_t)(LPSPI_Type *base, lpspi_slave_handle_t *handle);

/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief Get instance number for LPSPI module.
*
* @param base LPSPI peripheral base address.
*/
uint32_t LPSPI_GetInstance(LPSPI_Type *base);

/*!
* @brief Configures the LPSPI peripheral chip select polarity.
*
* This function  takes in the desired peripheral chip select (Pcs) and it's corresponding desired polarity and
* configures the Pcs signal to operate with the desired characteristic.
*
* @param base LPSPI peripheral address.
* @param pcs The particular peripheral chip select (parameter value is of type lpspi_which_pcs_t) for which we wish to
*            apply the active high or active low characteristic.
* @param activeLowOrHigh The setting for either "active high, inactive low (0)"  or "active low, inactive high(1)" of
*                        type lpspi_pcs_polarity_config_t.
*/
static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base,
                                    lpspi_which_pcs_t pcs,
                                    lpspi_pcs_polarity_config_t activeLowOrHigh);

/*!
* @brief Combine the write data for 1 byte to 4 bytes.
* This is not a public API.
*/
static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint32_t bytesEachWrite, bool isByteSwap);

/*!
* @brief Separate the read data for 1 byte to 4 bytes.
* This is not a public API.
*/
static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap);

/*!
* @brief Master fill up the TX FIFO with data.
* This is not a public API.
*/
static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle);

/*!
* @brief Master finish up a transfer.
* It would call back if there is callback function and set the state to idle.
* This is not a public API.
*/
static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle);

/*!
* @brief Slave fill up the TX FIFO with data.
* This is not a public API.
*/
static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle);

/*!
* @brief Slave finish up a transfer.
* It would call back if there is callback function and set the state to idle.
* This is not a public API.
*/
static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle);

/*!
* @brief Check the argument for transfer .
* This is not a public API. Not static because lpspi_edma.c will use this API.
*/
bool LPSPI_CheckTransferArgument(lpspi_transfer_t *transfer, uint32_t bitsPerFrame, uint32_t bytesPerFrame);

/*!
* @brief LPSPI common interrupt handler.
*
* @param handle pointer to s_lpspiHandle which stores the transfer state.
*/
static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param);

/*******************************************************************************
* Variables
******************************************************************************/

/* Defines constant value arrays for the baud rate pre-scalar and scalar divider values.*/
static const uint8_t s_baudratePrescaler[] = {1, 2, 4, 8, 16, 32, 64, 128};

/*! @brief Pointers to lpspi bases for each instance. */
static LPSPI_Type *const s_lpspiBases[] = LPSPI_BASE_PTRS;

/*! @brief Pointers to lpspi IRQ number for each instance. */
static const IRQn_Type s_lpspiIRQ[] = LPSPI_IRQS;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/*! @brief Pointers to lpspi clocks for each instance. */
static const clock_ip_name_t s_lpspiClocks[] = LPSPI_CLOCKS;

#if defined(LPSPI_PERIPH_CLOCKS)
static const clock_ip_name_t s_LpspiPeriphClocks[] = LPSPI_PERIPH_CLOCKS;
#endif

#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

/*! @brief Pointers to lpspi handles for each instance. */
static void *s_lpspiHandle[ARRAY_SIZE(s_lpspiBases)] = {NULL};

/*! @brief Pointer to master IRQ handler for each instance. */
static lpspi_master_isr_t s_lpspiMasterIsr;
/*! @brief Pointer to slave IRQ handler for each instance. */
static lpspi_slave_isr_t s_lpspiSlaveIsr;
/* @brief Dummy data for each instance. This data is used when user's tx buffer is NULL*/
volatile uint8_t s_dummyData[ARRAY_SIZE(s_lpspiBases)] = {0};
/**********************************************************************************************************************
* Code
*********************************************************************************************************************/
uint32_t LPSPI_GetInstance(LPSPI_Type *base)
{
    uint8_t instance = 0;

    /* Find the instance index from base address mappings. */
    for (instance = 0; instance < ARRAY_SIZE(s_lpspiBases); instance++)
    {
        if (s_lpspiBases[instance] == base)
        {
            break;
        }
    }

    assert(instance < ARRAY_SIZE(s_lpspiBases));

    return instance;
}

void LPSPI_SetDummyData(LPSPI_Type *base, uint8_t dummyData)
{
    uint32_t instance = LPSPI_GetInstance(base);
    s_dummyData[instance] = dummyData;
}

void LPSPI_MasterInit(LPSPI_Type *base, const lpspi_master_config_t *masterConfig, uint32_t srcClock_Hz)
{
    assert(masterConfig);

    uint32_t tcrPrescaleValue = 0;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)

    uint32_t instance = LPSPI_GetInstance(base);
    /* Enable LPSPI clock */
    CLOCK_EnableClock(s_lpspiClocks[instance]);

#if defined(LPSPI_PERIPH_CLOCKS)
    CLOCK_EnableClock(s_LpspiPeriphClocks[instance]);
#endif

#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

    /* Reset to known status */
    LPSPI_Reset(base);

    /* Set LPSPI to master */
    LPSPI_SetMasterSlaveMode(base, kLPSPI_Master);

    /* Set specific PCS to active high or low */
    LPSPI_SetOnePcsPolarity(base, masterConfig->whichPcs, masterConfig->pcsActiveHighOrLow);

    /* Set Configuration Register 1 related setting.*/
    base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK | LPSPI_CFGR1_NOSTALL_MASK)) |
                  LPSPI_CFGR1_OUTCFG(masterConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(masterConfig->pinCfg) |
                  LPSPI_CFGR1_NOSTALL(0);

    /* Set baudrate and delay times*/
    LPSPI_MasterSetBaudRate(base, masterConfig->baudRate, srcClock_Hz, &tcrPrescaleValue);

    /* Set default watermarks */
    LPSPI_SetFifoWatermarks(base, kLpspiDefaultTxWatermark, kLpspiDefaultRxWatermark);

    /* Set Transmit Command Register*/
    base->TCR = LPSPI_TCR_CPOL(masterConfig->cpol) | LPSPI_TCR_CPHA(masterConfig->cpha) |
                LPSPI_TCR_LSBF(masterConfig->direction) | LPSPI_TCR_FRAMESZ(masterConfig->bitsPerFrame - 1) |
                LPSPI_TCR_PRESCALE(tcrPrescaleValue) | LPSPI_TCR_PCS(masterConfig->whichPcs);

    LPSPI_Enable(base, true);

    LPSPI_MasterSetDelayTimes(base, masterConfig->pcsToSckDelayInNanoSec, kLPSPI_PcsToSck, srcClock_Hz);
    LPSPI_MasterSetDelayTimes(base, masterConfig->lastSckToPcsDelayInNanoSec, kLPSPI_LastSckToPcs, srcClock_Hz);
    LPSPI_MasterSetDelayTimes(base, masterConfig->betweenTransferDelayInNanoSec, kLPSPI_BetweenTransfer, srcClock_Hz);

    LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA);
}

void LPSPI_MasterGetDefaultConfig(lpspi_master_config_t *masterConfig)
{
    assert(masterConfig);

    masterConfig->baudRate = 500000;
    masterConfig->bitsPerFrame = 8;
    masterConfig->cpol = kLPSPI_ClockPolarityActiveHigh;
    masterConfig->cpha = kLPSPI_ClockPhaseFirstEdge;
    masterConfig->direction = kLPSPI_MsbFirst;

    masterConfig->pcsToSckDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2;
    masterConfig->lastSckToPcsDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2;
    masterConfig->betweenTransferDelayInNanoSec = 1000000000 / masterConfig->baudRate * 2;

    masterConfig->whichPcs = kLPSPI_Pcs0;
    masterConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow;

    masterConfig->pinCfg = kLPSPI_SdiInSdoOut;
    masterConfig->dataOutConfig = kLpspiDataOutRetained;
}

void LPSPI_SlaveInit(LPSPI_Type *base, const lpspi_slave_config_t *slaveConfig)
{
    assert(slaveConfig);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)

    uint32_t instance = LPSPI_GetInstance(base);
    /* Enable LPSPI clock */
    CLOCK_EnableClock(s_lpspiClocks[instance]);

#if defined(LPSPI_PERIPH_CLOCKS)
    CLOCK_EnableClock(s_LpspiPeriphClocks[instance]);
#endif

#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

    /* Reset to known status */
    LPSPI_Reset(base);

    LPSPI_SetMasterSlaveMode(base, kLPSPI_Slave);

    LPSPI_SetOnePcsPolarity(base, slaveConfig->whichPcs, slaveConfig->pcsActiveHighOrLow);

    base->CFGR1 = (base->CFGR1 & ~(LPSPI_CFGR1_OUTCFG_MASK | LPSPI_CFGR1_PINCFG_MASK)) |
                  LPSPI_CFGR1_OUTCFG(slaveConfig->dataOutConfig) | LPSPI_CFGR1_PINCFG(slaveConfig->pinCfg);

    LPSPI_SetFifoWatermarks(base, kLpspiDefaultTxWatermark, kLpspiDefaultRxWatermark);

    base->TCR = LPSPI_TCR_CPOL(slaveConfig->cpol) | LPSPI_TCR_CPHA(slaveConfig->cpha) |
                LPSPI_TCR_LSBF(slaveConfig->direction) | LPSPI_TCR_FRAMESZ(slaveConfig->bitsPerFrame - 1);

    /* This operation will set the dummy data for edma transfer, no effect in interrupt way. */
    LPSPI_SetDummyData(base, LPSPI_DUMMY_DATA);

    LPSPI_Enable(base, true);
}

void LPSPI_SlaveGetDefaultConfig(lpspi_slave_config_t *slaveConfig)
{
    assert(slaveConfig);

    slaveConfig->bitsPerFrame = 8;                      /*!< Bits per frame, minimum 8, maximum 4096.*/
    slaveConfig->cpol = kLPSPI_ClockPolarityActiveHigh; /*!< Clock polarity. */
    slaveConfig->cpha = kLPSPI_ClockPhaseFirstEdge;     /*!< Clock phase. */
    slaveConfig->direction = kLPSPI_MsbFirst;           /*!< MSB or LSB data shift direction. */

    slaveConfig->whichPcs = kLPSPI_Pcs0;                   /*!< Desired Peripheral Chip Select (pcs) */
    slaveConfig->pcsActiveHighOrLow = kLPSPI_PcsActiveLow; /*!< Desired PCS active high or low */

    slaveConfig->pinCfg = kLPSPI_SdiInSdoOut;
    slaveConfig->dataOutConfig = kLpspiDataOutRetained;
}

void LPSPI_Reset(LPSPI_Type *base)
{
    /* Reset all internal logic and registers, except the Control Register. Remains set until cleared by software.*/
    base->CR |= LPSPI_CR_RST_MASK;

    /* Software reset doesn't reset the CR, so manual reset the FIFOs */
    base->CR |= LPSPI_CR_RRF_MASK | LPSPI_CR_RTF_MASK;

    /* Master logic is not reset and module is disabled.*/
    base->CR = 0x00U;
}

void LPSPI_Deinit(LPSPI_Type *base)
{
    /* Reset to default value */
    LPSPI_Reset(base);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)

    uint32_t instance = LPSPI_GetInstance(base);
    /* Enable LPSPI clock */
    CLOCK_DisableClock(s_lpspiClocks[instance]);

#if defined(LPSPI_PERIPH_CLOCKS)
    CLOCK_DisableClock(s_LpspiPeriphClocks[instance]);
#endif

#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

static void LPSPI_SetOnePcsPolarity(LPSPI_Type *base,
                                    lpspi_which_pcs_t pcs,
                                    lpspi_pcs_polarity_config_t activeLowOrHigh)
{
    uint32_t cfgr1Value = 0;
    /* Clear the PCS polarity bit */
    cfgr1Value = base->CFGR1 & ~(1U << (LPSPI_CFGR1_PCSPOL_SHIFT + pcs));

    /* Configure the PCS polarity bit according to the activeLowOrHigh setting */
    base->CFGR1 = cfgr1Value | ((uint32_t)activeLowOrHigh << (LPSPI_CFGR1_PCSPOL_SHIFT + pcs));
}

uint32_t LPSPI_MasterSetBaudRate(LPSPI_Type *base,
                                 uint32_t baudRate_Bps,
                                 uint32_t srcClock_Hz,
                                 uint32_t *tcrPrescaleValue)
{
    assert(tcrPrescaleValue);

    /* For master mode configuration only, if slave mode detected, return 0.
    * Also, the LPSPI module needs to be disabled first, if enabled, return 0
    */
    if ((!LPSPI_IsMaster(base)) || (base->CR & LPSPI_CR_MEN_MASK))
    {
        return 0;
    }

    uint32_t prescaler, bestPrescaler;
    uint32_t scaler, bestScaler;
    uint32_t realBaudrate, bestBaudrate;
    uint32_t diff, min_diff;
    uint32_t desiredBaudrate = baudRate_Bps;

    /* find combination of prescaler and scaler resulting in baudrate closest to the
    * requested value
    */
    min_diff = 0xFFFFFFFFU;

    /* Set to maximum divisor value bit settings so that if baud rate passed in is less
    * than the minimum possible baud rate, then the SPI will be configured to the lowest
    * possible baud rate
    */
    bestPrescaler = 7;
    bestScaler = 255;

    bestBaudrate = 0; /* required to avoid compilation warning */

    /* In all for loops, if min_diff = 0, the exit for loop*/
    for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++)
    {
        for (scaler = 0; (scaler < 256) && min_diff; scaler++)
        {
            realBaudrate = (srcClock_Hz / (s_baudratePrescaler[prescaler] * (scaler + 2U)));

            /* calculate the baud rate difference based on the conditional statement
            * that states that the calculated baud rate must not exceed the desired baud rate
            */
            if (desiredBaudrate >= realBaudrate)
            {
                diff = desiredBaudrate - realBaudrate;
                if (min_diff > diff)
                {
                    /* a better match found */
                    min_diff = diff;
                    bestPrescaler = prescaler;
                    bestScaler = scaler;
                    bestBaudrate = realBaudrate;
                }
            }
        }
    }

    /* Write the best baud rate scalar to the CCR.
    * Note, no need to check for error since we've already checked to make sure the module is
    * disabled and in master mode. Also, there is a limit on the maximum divider so we will not
    * exceed this.
    */
    base->CCR = (base->CCR & ~LPSPI_CCR_SCKDIV_MASK) | LPSPI_CCR_SCKDIV(bestScaler);

    /* return the best prescaler value for user to use later */
    *tcrPrescaleValue = bestPrescaler;

    /* return the actual calculated baud rate */
    return bestBaudrate;
}

void LPSPI_MasterSetDelayScaler(LPSPI_Type *base, uint32_t scaler, lpspi_delay_type_t whichDelay)
{
    /*These settings are only relevant in master mode */
    switch (whichDelay)
    {
        case kLPSPI_PcsToSck:
            base->CCR = (base->CCR & (~LPSPI_CCR_PCSSCK_MASK)) | LPSPI_CCR_PCSSCK(scaler);

            break;
        case kLPSPI_LastSckToPcs:
            base->CCR = (base->CCR & (~LPSPI_CCR_SCKPCS_MASK)) | LPSPI_CCR_SCKPCS(scaler);

            break;
        case kLPSPI_BetweenTransfer:
            base->CCR = (base->CCR & (~LPSPI_CCR_DBT_MASK)) | LPSPI_CCR_DBT(scaler);

            break;
        default:
            assert(false);
            break;
    }
}

uint32_t LPSPI_MasterSetDelayTimes(LPSPI_Type *base,
                                   uint32_t delayTimeInNanoSec,
                                   lpspi_delay_type_t whichDelay,
                                   uint32_t srcClock_Hz)
{
    uint64_t realDelay, bestDelay;
    uint32_t scaler, bestScaler;
    uint32_t diff, min_diff;
    uint64_t initialDelayNanoSec;
    uint32_t clockDividedPrescaler;

    /* For delay between transfer, an additional scaler value is needed */
    uint32_t additionalScaler = 0;

    /*As the RM note, the LPSPI baud rate clock is itself divided by the PRESCALE setting, which can vary between
     * transfers.*/
    clockDividedPrescaler =
        srcClock_Hz / s_baudratePrescaler[(base->TCR & LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT];

    /* Find combination of prescaler and scaler resulting in the delay closest to the requested value.*/
    min_diff = 0xFFFFFFFFU;

    /* Initialize scaler to max value to generate the max delay */
    bestScaler = 0xFFU;

    /* Calculate the initial (min) delay and maximum possible delay based on the specific delay as
    * the delay divisors are slightly different based on which delay we are configuring.
    */
    if (whichDelay == kLPSPI_BetweenTransfer)
    {
        /* First calculate the initial, default delay, note min delay is 2 clock cycles. Due to large size of
         calculated values (uint64_t), we need to break up the calculation into several steps to ensure
         accurate calculated results
         */
        initialDelayNanoSec = 1000000000U;
        initialDelayNanoSec *= 2U;
        initialDelayNanoSec /= clockDividedPrescaler;

        /* Calculate the maximum delay */
        bestDelay = 1000000000U;
        bestDelay *= 257U; /* based on DBT+2, or 255 + 2 */
        bestDelay /= clockDividedPrescaler;

        additionalScaler = 1U;
    }
    else
    {
        /* First calculate the initial, default delay, min delay is 1 clock cycle. Due to large size of calculated
        values (uint64_t), we need to break up the calculation into several steps to ensure accurate calculated
        results.
        */
        initialDelayNanoSec = 1000000000U;
        initialDelayNanoSec /= clockDividedPrescaler;

        /* Calculate the maximum delay */
        bestDelay = 1000000000U;
        bestDelay *= 256U; /* based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */
        bestDelay /= clockDividedPrescaler;

        additionalScaler = 0;
    }

    /* If the initial, default delay is already greater than the desired delay, then
    * set the delay to their initial value (0) and return the delay. In other words,
    * there is no way to decrease the delay value further.
    */
    if (initialDelayNanoSec >= delayTimeInNanoSec)
    {
        LPSPI_MasterSetDelayScaler(base, 0, whichDelay);
        return initialDelayNanoSec;
    }

    /* If min_diff = 0, the exit for loop */
    for (scaler = 0; (scaler < 256U) && min_diff; scaler++)
    {
        /* Calculate the real delay value as we cycle through the scaler values.
        Due to large size of calculated values (uint64_t), we need to break up the
        calculation into several steps to ensure accurate calculated results
        */
        realDelay = 1000000000U;
        realDelay *= (scaler + 1 + additionalScaler);
        realDelay /= clockDividedPrescaler;

        /* calculate the delay difference based on the conditional statement
        * that states that the calculated delay must not be less then the desired delay
        */
        if (realDelay >= delayTimeInNanoSec)
        {
            diff = realDelay - delayTimeInNanoSec;
            if (min_diff > diff)
            {
                /* a better match found */
                min_diff = diff;
                bestScaler = scaler;
                bestDelay = realDelay;
            }
        }
    }

    /* write the best scaler value for the delay */
    LPSPI_MasterSetDelayScaler(base, bestScaler, whichDelay);

    /* return the actual calculated delay value (in ns) */
    return bestDelay;
}

/*Transactional APIs -- Master*/

void LPSPI_MasterTransferCreateHandle(LPSPI_Type *base,
                                      lpspi_master_handle_t *handle,
                                      lpspi_master_transfer_callback_t callback,
                                      void *userData)
{
    assert(handle);

    /* Zero the handle. */
    memset(handle, 0, sizeof(*handle));

    s_lpspiHandle[LPSPI_GetInstance(base)] = handle;

    /* Set irq handler. */
    s_lpspiMasterIsr = LPSPI_MasterTransferHandleIRQ;

    handle->callback = callback;
    handle->userData = userData;
}

bool LPSPI_CheckTransferArgument(lpspi_transfer_t *transfer, uint32_t bitsPerFrame, uint32_t bytesPerFrame)
{
    assert(transfer);

    /* If the transfer count is zero, then return immediately.*/
    if (transfer->dataSize == 0)
    {
        return false;
    }

    /* If both send buffer and receive buffer is null */
    if ((!(transfer->txData)) && (!(transfer->rxData)))
    {
        return false;
    }

    /*The transfer data size should be integer multiples of bytesPerFrame if bytesPerFrame is less than or equal to 4 .
     *For bytesPerFrame greater than 4 situation:
     *the transfer data size should be equal to bytesPerFrame if the bytesPerFrame is not integer multiples of 4 ,
     *otherwise , the transfer data size can be integer multiples of bytesPerFrame.
     */
    if (bytesPerFrame <= 4)
    {
        if ((transfer->dataSize % bytesPerFrame) != 0)
        {
            return false;
        }
    }
    else
    {
        if ((bytesPerFrame % 4U) != 0)
        {
            if (transfer->dataSize != bytesPerFrame)
            {
                return false;
            }
        }
        else
        {
            if ((transfer->dataSize % bytesPerFrame) != 0)
            {
                return false;
            }
        }
    }

    return true;
}

status_t LPSPI_MasterTransferBlocking(LPSPI_Type *base, lpspi_transfer_t *transfer)
{
    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;
    }

    /* Check that LPSPI is not busy.*/
    if (LPSPI_GetStatusFlags(base) & kLPSPI_ModuleBusyFlag)
    {
        return kStatus_LPSPI_Busy;
    }

    uint8_t *txData = transfer->txData;
    uint8_t *rxData = transfer->rxData;
    uint32_t txRemainingByteCount = transfer->dataSize;
    uint32_t rxRemainingByteCount = transfer->dataSize;

    uint8_t bytesEachWrite;
    uint8_t bytesEachRead;

    uint32_t readData = 0;
    uint32_t wordToSend =
        ((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*/
    uint32_t fifoSize = LPSPI_GetRxFifoSize(base);

    uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT;

    bool isPcsContinuous = (bool)(transfer->configFlags & kLPSPI_MasterPcsContinuous);
    bool isRxMask = false;
    bool isByteSwap = (bool)(transfer->configFlags & kLPSPI_MasterByteSwap);

    LPSPI_FlushFifo(base, true, true);
    LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag);

    if (!rxData)
    {
        isRxMask = true;
    }

    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 (!txData)
        {
            base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK;
        }
        /* The 3-wire mode can't send and receive data at the same time. */
        if ((txData) && (rxData))
        {
            return kStatus_InvalidArgument;
        }
    }
    LPSPI_Enable(base, true);

    base->TCR =
        (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
        LPSPI_TCR_CONT(isPcsContinuous) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_PCS(whichPcs);

    if (bytesPerFrame <= 4)
    {
        bytesEachWrite = bytesPerFrame;
        bytesEachRead = bytesPerFrame;
    }
    else
    {
        bytesEachWrite = 4;
        bytesEachRead = 4;
    }

    /*Write the TX data until txRemainingByteCount is equal to 0 */
    while (txRemainingByteCount > 0)
    {
        if (txRemainingByteCount < bytesEachWrite)
        {
            bytesEachWrite = txRemainingByteCount;
        }

        /*Wait until TX FIFO is not full*/
        while (LPSPI_GetTxFifoCount(base) == fifoSize)
        {
        }

        if (txData)
        {
            wordToSend = LPSPI_CombineWriteData(txData, bytesEachWrite, isByteSwap);
            txData += bytesEachWrite;
        }

        LPSPI_WriteData(base, wordToSend);
        txRemainingByteCount -= bytesEachWrite;

        /*Check whether there is RX data in RX FIFO . Read out the RX data so that the RX FIFO would not overrun.*/
        if (rxData)
        {
            while (LPSPI_GetRxFifoCount(base))
            {
                readData = LPSPI_ReadData(base);
                if (rxRemainingByteCount < bytesEachRead)
                {
                    bytesEachRead = rxRemainingByteCount;
                }

                LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap);
                rxData += bytesEachRead;

                rxRemainingByteCount -= bytesEachRead;
            }
        }
    }

    /* After write all the data in TX FIFO , should write the TCR_CONTC to 0 to de-assert the PCS. Note that TCR
     * register also use the TX FIFO.
     */
    while ((LPSPI_GetTxFifoCount(base) == fifoSize))
    {
    }
    base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK));

    /*Read out the RX data in FIFO*/
    if (rxData)
    {
        while (rxRemainingByteCount > 0)
        {
            while (LPSPI_GetRxFifoCount(base))
            {
                readData = LPSPI_ReadData(base);

                if (rxRemainingByteCount < bytesEachRead)
                {
                    bytesEachRead = rxRemainingByteCount;
                }

                LPSPI_SeparateReadData(rxData, readData, bytesEachRead, isByteSwap);
                rxData += bytesEachRead;

                rxRemainingByteCount -= bytesEachRead;
            }
        }
    }
    else
    {
        /* If no RX buffer, then transfer is not complete until transfer complete flag sets */
        while (!(LPSPI_GetStatusFlags(base) & kLPSPI_TransferCompleteFlag))
        {
        }
    }

    return kStatus_Success;
}

status_t LPSPI_MasterTransferNonBlocking(LPSPI_Type *base, lpspi_master_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;
    }

    /* Check that we're not busy.*/
    if (handle->state == kLPSPI_Busy)
    {
        return kStatus_LPSPI_Busy;
    }

    handle->state = kLPSPI_Busy;

    bool isRxMask = false;

    uint8_t txWatermark;

    uint32_t whichPcs = (transfer->configFlags & LPSPI_MASTER_PCS_MASK) >> LPSPI_MASTER_PCS_SHIFT;

    handle->txData = transfer->txData;
    handle->rxData = transfer->rxData;
    handle->txRemainingByteCount = transfer->dataSize;
    handle->rxRemainingByteCount = transfer->dataSize;
    handle->totalByteCount = transfer->dataSize;

    handle->writeTcrInIsr = false;

    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);

    /*Set the RX and TX watermarks to reduce the ISR times.*/
    if (handle->fifoSize > 1)
    {
        txWatermark = 1;
        handle->rxWatermark = handle->fifoSize - 2;
    }
    else
    {
        txWatermark = 0;
        handle->rxWatermark = 0;
    }

    LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark);

    LPSPI_Enable(base, false);
    /*Transfers will stall when transmit FIFO is empty or receive FIFO is full. */
    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;
        }
    }
    LPSPI_Enable(base, true);

    /*Flush FIFO , clear status , disable all the inerrupts.*/
    LPSPI_FlushFifo(base, true, true);
    LPSPI_ClearStatusFlags(base, kLPSPI_AllStatusFlag);
    LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable);

    /* If there is not rxData , can mask the receive data (receive data is not stored in receive FIFO).
     * For master transfer , we'd better not masked the transmit data in TCR since the transfer flow is hard to
     * controlled by software.*/
    if (handle->rxData == NULL)
    {
        isRxMask = true;
        handle->rxRemainingByteCount = 0;
    }

    base->TCR =
        (base->TCR & ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_PCS_MASK)) |
        LPSPI_TCR_CONT(handle->isPcsContinuous) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) |
        LPSPI_TCR_PCS(whichPcs);

    /*Calculate the bytes for write/read the TX/RX register each time*/
    if (bytesPerFrame <= 4)
    {
        handle->bytesEachWrite = bytesPerFrame;
        handle->bytesEachRead = bytesPerFrame;
    }
    else
    {
        handle->bytesEachWrite = 4;
        handle->bytesEachRead = 4;
    }

    /* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX ,
     * and you should also enable the INTMUX interupt in your application.
     */
    EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]);

    /*TCR is also shared the FIFO , so wait for TCR written.*/
    while (LPSPI_GetTxFifoCount(base) != 0)
    {
    }
    /*Fill up the TX data in FIFO */
    LPSPI_MasterTransferFillUpTxFifo(base, handle);

    /* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data.
     * The IRQ handler will get the status of RX and TX interrupt flags.
     */
    if (handle->rxData)
    {
        /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
         *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
         */
        if ((handle->readRegRemainingTimes) <= handle->rxWatermark)
        {
            base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->readRegRemainingTimes - 1);
        }

        LPSPI_EnableInterrupts(base, kLPSPI_RxInterruptEnable);
    }
    else
    {
        LPSPI_EnableInterrupts(base, kLPSPI_TxInterruptEnable);
    }

    return kStatus_Success;
}

static void LPSPI_MasterTransferFillUpTxFifo(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
    assert(handle);

    uint32_t wordToSend = 0;

    /* Make sure the difference in remaining TX and RX byte counts does not exceed FIFO depth
    * and that the number of TX FIFO entries does not exceed the FIFO depth.
    * But no need to make the protection if there is no rxData.
    */
    while ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize)) &&
           (((handle->readRegRemainingTimes - handle->writeRegRemainingTimes) < handle->fifoSize) ||
            (handle->rxData == NULL)))
    {
        if (handle->txRemainingByteCount < handle->bytesEachWrite)
        {
            handle->bytesEachWrite = handle->txRemainingByteCount;
        }

        if (handle->txData)
        {
            wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap);
            handle->txData += handle->bytesEachWrite;
        }
        else
        {
            wordToSend = handle->txBuffIfNull;
        }

        /*Write the word to TX register*/
        LPSPI_WriteData(base, wordToSend);

        /*Decrease the write TX register times.*/
        --handle->writeRegRemainingTimes;

        /*Decrease the remaining TX byte count.*/
        handle->txRemainingByteCount -= handle->bytesEachWrite;

        if (handle->txRemainingByteCount == 0)
        {
            /* If PCS is continuous, update TCR to de-assert PCS */
            if (handle->isPcsContinuous)
            {
                /* Only write to the TCR if the FIFO has room */
                if ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize)))
                {
                    base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK));
                    handle->writeTcrInIsr = false;
                }
                /* Else, set a global flag to tell the ISR to do write to the TCR */
                else
                {
                    handle->writeTcrInIsr = true;
                }
            }
            break;
        }
    }
}

static void LPSPI_MasterTransferComplete(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
    assert(handle);

    /* Disable interrupt requests*/
    LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable);

    handle->state = kLPSPI_Idle;

    if (handle->callback)
    {
        handle->callback(base, handle, kStatus_Success, handle->userData);
    }
}

status_t LPSPI_MasterTransferGetCount(LPSPI_Type *base, lpspi_master_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;

    if (handle->rxData)
    {
        remainingByte = handle->rxRemainingByteCount;
    }
    else
    {
        remainingByte = handle->txRemainingByteCount;
    }

    *count = handle->totalByteCount - remainingByte;

    return kStatus_Success;
}

void LPSPI_MasterTransferAbort(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
    assert(handle);

    /* Disable interrupt requests*/
    LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable);

    LPSPI_Reset(base);

    handle->state = kLPSPI_Idle;
    handle->txRemainingByteCount = 0;
    handle->rxRemainingByteCount = 0;
}

void LPSPI_MasterTransferHandleIRQ(LPSPI_Type *base, lpspi_master_handle_t *handle)
{
    assert(handle);

    uint32_t readData;

    if (handle->rxData != NULL)
    {
        if (handle->rxRemainingByteCount)
        {
            /* First, disable the interrupts to avoid potentially triggering another interrupt
            * while reading out the RX FIFO as more data may be coming into the RX FIFO. We'll
            * re-enable the interrupts based on the LPSPI state after reading out the FIFO.
            */
            LPSPI_DisableInterrupts(base, kLPSPI_RxInterruptEnable);

            while ((LPSPI_GetRxFifoCount(base)) && (handle->rxRemainingByteCount))
            {
                /*Read out the data*/
                readData = LPSPI_ReadData(base);

                /*Decrease the read RX register times.*/
                --handle->readRegRemainingTimes;

                if (handle->rxRemainingByteCount < handle->bytesEachRead)
                {
                    handle->bytesEachRead = handle->rxRemainingByteCount;
                }

                LPSPI_SeparateReadData(handle->rxData, readData, handle->bytesEachRead, handle->isByteSwap);
                handle->rxData += handle->bytesEachRead;

                /*Decrease the remaining RX byte count.*/
                handle->rxRemainingByteCount -= handle->bytesEachRead;
            }

            /* Re-enable the interrupts only if rxCount indicates there is more data to receive,
            * else we may get a spurious interrupt.
            * */
            if (handle->rxRemainingByteCount)
            {
                /* Set the TDF and RDF interrupt enables simultaneously to avoid race conditions */
                LPSPI_EnableInterrupts(base, kLPSPI_RxInterruptEnable);
            }
        }

        /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
         *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
         */
        if ((handle->readRegRemainingTimes) <= (handle->rxWatermark))
        {
            base->FCR =
                (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
                LPSPI_FCR_RXWATER((handle->readRegRemainingTimes > 1) ? (handle->readRegRemainingTimes - 1U) : (0U));
        }
    }

    if (handle->txRemainingByteCount)
    {
        LPSPI_MasterTransferFillUpTxFifo(base, handle);
    }
    else
    {
        if ((LPSPI_GetTxFifoCount(base) < (handle->fifoSize)))
        {
            if ((handle->isPcsContinuous) && (handle->writeTcrInIsr))
            {
                base->TCR = (base->TCR & ~(LPSPI_TCR_CONTC_MASK));
                handle->writeTcrInIsr = false;
            }
        }
    }

    if ((handle->txRemainingByteCount == 0) && (handle->rxRemainingByteCount == 0) && (!handle->writeTcrInIsr))
    {
        /* If no RX buffer, then transfer is not complete until transfer complete flag sets */
        if (handle->rxData == NULL)
        {
            if (LPSPI_GetStatusFlags(base) & kLPSPI_TransferCompleteFlag)
            {
                /* Complete the transfer and disable the interrupts */
                LPSPI_MasterTransferComplete(base, handle);
            }
            else
            {
                LPSPI_EnableInterrupts(base, kLPSPI_TransferCompleteInterruptEnable);
                LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable);
            }
        }
        else
        {
            /* Complete the transfer and disable the interrupts */
            LPSPI_MasterTransferComplete(base, handle);
        }
    }
}

/*Transactional APIs -- Slave*/
void LPSPI_SlaveTransferCreateHandle(LPSPI_Type *base,
                                     lpspi_slave_handle_t *handle,
                                     lpspi_slave_transfer_callback_t callback,
                                     void *userData)
{
    assert(handle);

    /* Zero the handle. */
    memset(handle, 0, sizeof(*handle));

    s_lpspiHandle[LPSPI_GetInstance(base)] = handle;

    /* Set irq handler. */
    s_lpspiSlaveIsr = LPSPI_SlaveTransferHandleIRQ;

    handle->callback = callback;
    handle->userData = userData;
}

status_t LPSPI_SlaveTransferNonBlocking(LPSPI_Type *base, lpspi_slave_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;
    }

    /* Check that we're not busy.*/
    if (handle->state == kLPSPI_Busy)
    {
        return kStatus_LPSPI_Busy;
    }
    handle->state = kLPSPI_Busy;

    bool isRxMask = false;
    bool isTxMask = false;

    uint32_t whichPcs = (transfer->configFlags & LPSPI_SLAVE_PCS_MASK) >> LPSPI_SLAVE_PCS_SHIFT;

    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;

    /*The TX and RX FIFO sizes are always the same*/
    handle->fifoSize = LPSPI_GetRxFifoSize(base);

    handle->isByteSwap = (bool)(transfer->configFlags & kLPSPI_SlaveByteSwap);

    /*Set the RX and TX watermarks to reduce the ISR times.*/
    uint8_t txWatermark;
    if (handle->fifoSize > 1)
    {
        txWatermark = 1;
        handle->rxWatermark = handle->fifoSize - 2;
    }
    else
    {
        txWatermark = 0;
        handle->rxWatermark = 0;
    }
    LPSPI_SetFifoWatermarks(base, txWatermark, handle->rxWatermark);

    /* 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)
        {
            LPSPI_Enable(base, false);
            base->CFGR1 |= LPSPI_CFGR1_OUTCFG_MASK;
            LPSPI_Enable(base, true);
        }
        /* 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);

    /*If there is not rxData , can mask the receive data (receive data is not stored in receive FIFO).*/
    if (handle->rxData == NULL)
    {
        isRxMask = true;
        handle->rxRemainingByteCount = 0;
    }

    /*If there is not txData , can mask the transmit data (no data is loaded from transmit FIFO and output pin
     * is tristated).
     */
    if (handle->txData == NULL)
    {
        isTxMask = true;
        handle->txRemainingByteCount = 0;
    }

    base->TCR = (base->TCR &
                 ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_RXMSK_MASK | LPSPI_TCR_TXMSK_MASK |
                   LPSPI_TCR_PCS_MASK)) |
                LPSPI_TCR_CONT(0) | LPSPI_TCR_CONTC(0) | LPSPI_TCR_RXMSK(isRxMask) | LPSPI_TCR_TXMSK(isTxMask) |
                LPSPI_TCR_PCS(whichPcs);

    /*Calculate the bytes for write/read the TX/RX register each time*/
    if (bytesPerFrame <= 4)
    {
        handle->bytesEachWrite = bytesPerFrame;
        handle->bytesEachRead = bytesPerFrame;
    }
    else
    {
        handle->bytesEachWrite = 4;
        handle->bytesEachRead = 4;
    }

    /* Enable the NVIC for LPSPI peripheral. Note that below code is useless if the LPSPI interrupt is in INTMUX ,
     * and you should also enable the INTMUX interupt in your application.
     */
    EnableIRQ(s_lpspiIRQ[LPSPI_GetInstance(base)]);

    /*TCR is also shared the FIFO , so wait for TCR written.*/
    while (LPSPI_GetTxFifoCount(base) != 0)
    {
    }

    /*Fill up the TX data in FIFO */
    if (handle->txData)
    {
        LPSPI_SlaveTransferFillUpTxFifo(base, handle);
    }

    /* Since SPI is a synchronous interface, we only need to enable the RX interrupt if there is RX data.
     * The IRQ handler will get the status of RX and TX interrupt flags.
     */
    if (handle->rxData)
    {
        /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
         *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
         */
        if ((handle->readRegRemainingTimes) <= handle->rxWatermark)
        {
            base->FCR = (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) | LPSPI_FCR_RXWATER(handle->readRegRemainingTimes - 1);
        }

        LPSPI_EnableInterrupts(base, kLPSPI_RxInterruptEnable);
    }
    else
    {
        LPSPI_EnableInterrupts(base, kLPSPI_TxInterruptEnable);
    }

    if (handle->rxData)
    {
        /* RX FIFO overflow request enable */
        LPSPI_EnableInterrupts(base, kLPSPI_ReceiveErrorInterruptEnable);
    }
    if (handle->txData)
    {
        /* TX FIFO underflow request enable */
        LPSPI_EnableInterrupts(base, kLPSPI_TransmitErrorInterruptEnable);
    }

    return kStatus_Success;
}

static void LPSPI_SlaveTransferFillUpTxFifo(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
    assert(handle);

    uint32_t wordToSend = 0;

    while (LPSPI_GetTxFifoCount(base) < (handle->fifoSize))
    {
        if (handle->txRemainingByteCount < handle->bytesEachWrite)
        {
            handle->bytesEachWrite = handle->txRemainingByteCount;
        }

        wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap);
        handle->txData += handle->bytesEachWrite;

        /*Decrease the remaining TX byte count.*/
        handle->txRemainingByteCount -= handle->bytesEachWrite;

        /*Write the word to TX register*/
        LPSPI_WriteData(base, wordToSend);

        if (handle->txRemainingByteCount == 0)
        {
            break;
        }
    }
}

static void LPSPI_SlaveTransferComplete(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
    assert(handle);

    status_t status = 0;

    /* Disable interrupt requests*/
    LPSPI_DisableInterrupts(base, kLPSPI_AllInterruptEnable);

    if (handle->state == kLPSPI_Error)
    {
        status = kStatus_LPSPI_Error;
    }
    else
    {
        status = kStatus_Success;
    }

    handle->state = kLPSPI_Idle;

    if (handle->callback)
    {
        handle->callback(base, handle, status, handle->userData);
    }
}

status_t LPSPI_SlaveTransferGetCount(LPSPI_Type *base, lpspi_slave_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;

    if (handle->rxData)
    {
        remainingByte = handle->rxRemainingByteCount;
    }
    else
    {
        remainingByte = handle->txRemainingByteCount;
    }

    *count = handle->totalByteCount - remainingByte;

    return kStatus_Success;
}

void LPSPI_SlaveTransferAbort(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
    assert(handle);

    /* Disable interrupt requests*/
    LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable);

    LPSPI_Reset(base);

    handle->state = kLPSPI_Idle;
    handle->txRemainingByteCount = 0;
    handle->rxRemainingByteCount = 0;
}

void LPSPI_SlaveTransferHandleIRQ(LPSPI_Type *base, lpspi_slave_handle_t *handle)
{
    assert(handle);

    uint32_t readData;   /* variable to store word read from RX FIFO */
    uint32_t wordToSend; /* variable to store word to write to TX FIFO */

    if (handle->rxData != NULL)
    {
        if (handle->rxRemainingByteCount > 0)
        {
            while (LPSPI_GetRxFifoCount(base))
            {
                /*Read out the data*/
                readData = LPSPI_ReadData(base);

                /*Decrease the read RX register times.*/
                --handle->readRegRemainingTimes;

                if (handle->rxRemainingByteCount < handle->bytesEachRead)
                {
                    handle->bytesEachRead = handle->rxRemainingByteCount;
                }

                LPSPI_SeparateReadData(handle->rxData, readData, handle->bytesEachRead, handle->isByteSwap);
                handle->rxData += handle->bytesEachRead;

                /*Decrease the remaining RX byte count.*/
                handle->rxRemainingByteCount -= handle->bytesEachRead;

                if ((handle->txRemainingByteCount > 0) && (handle->txData != NULL))
                {
                    if (handle->txRemainingByteCount < handle->bytesEachWrite)
                    {
                        handle->bytesEachWrite = handle->txRemainingByteCount;
                    }

                    wordToSend = LPSPI_CombineWriteData(handle->txData, handle->bytesEachWrite, handle->isByteSwap);
                    handle->txData += handle->bytesEachWrite;

                    /*Decrease the remaining TX byte count.*/
                    handle->txRemainingByteCount -= handle->bytesEachWrite;

                    /*Write the word to TX register*/
                    LPSPI_WriteData(base, wordToSend);
                }

                if (handle->rxRemainingByteCount == 0)
                {
                    break;
                }
            }
        }

        /*Set rxWatermark to (readRegRemainingTimes-1) if readRegRemainingTimes less than rxWatermark. Otherwise there
         *is not RX interrupt for the last datas because the RX count is not greater than rxWatermark.
         */
        if ((handle->readRegRemainingTimes) <= (handle->rxWatermark))
        {
            base->FCR =
                (base->FCR & (~LPSPI_FCR_RXWATER_MASK)) |
                LPSPI_FCR_RXWATER((handle->readRegRemainingTimes > 1) ? (handle->readRegRemainingTimes - 1U) : (0U));
        }
    }
    else if ((handle->txRemainingByteCount) && (handle->txData != NULL))
    {
        LPSPI_SlaveTransferFillUpTxFifo(base, handle);
    }
    else
    {
        __NOP();
    }

    if ((handle->txRemainingByteCount == 0) && (handle->rxRemainingByteCount == 0))
    {
        /* If no RX buffer, then transfer is not complete until transfer complete flag sets and the TX FIFO empty*/
        if (handle->rxData == NULL)
        {
            if ((LPSPI_GetStatusFlags(base) & kLPSPI_FrameCompleteFlag) && (LPSPI_GetTxFifoCount(base) == 0))
            {
                /* Complete the transfer and disable the interrupts */
                LPSPI_SlaveTransferComplete(base, handle);
            }
            else
            {
                LPSPI_ClearStatusFlags(base, kLPSPI_FrameCompleteFlag);
                LPSPI_EnableInterrupts(base, kLPSPI_FrameCompleteInterruptEnable);
                LPSPI_DisableInterrupts(base, kLPSPI_TxInterruptEnable | kLPSPI_RxInterruptEnable);
            }
        }
        else
        {
            /* Complete the transfer and disable the interrupts */
            LPSPI_SlaveTransferComplete(base, handle);
        }
    }

    /* Catch tx fifo underflow conditions, service only if tx under flow interrupt enabled */
    if ((LPSPI_GetStatusFlags(base) & kLPSPI_TransmitErrorFlag) && (base->IER & LPSPI_IER_TEIE_MASK))
    {
        LPSPI_ClearStatusFlags(base, kLPSPI_TransmitErrorFlag);
        /* Change state to error and clear flag */
        if (handle->txData)
        {
            handle->state = kLPSPI_Error;
        }
        handle->errorCount++;
    }
    /* Catch rx fifo overflow conditions, service only if rx over flow interrupt enabled */
    if ((LPSPI_GetStatusFlags(base) & kLPSPI_ReceiveErrorFlag) && (base->IER & LPSPI_IER_REIE_MASK))
    {
        LPSPI_ClearStatusFlags(base, kLPSPI_ReceiveErrorFlag);
        /* Change state to error and clear flag */
        if (handle->txData)
        {
            handle->state = kLPSPI_Error;
        }
        handle->errorCount++;
    }
}

static uint32_t LPSPI_CombineWriteData(uint8_t *txData, uint32_t bytesEachWrite, bool isByteSwap)
{
    assert(txData);

    uint32_t wordToSend = 0;

    switch (bytesEachWrite)
    {
        case 1:
            wordToSend = *txData;
            ++txData;
            break;

        case 2:
            if (!isByteSwap)
            {
                wordToSend = *txData;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 8U;
                ++txData;
            }
            else
            {
                wordToSend = (unsigned)(*txData) << 8U;
                ++txData;
                wordToSend |= *txData;
                ++txData;
            }

            break;

        case 3:
            if (!isByteSwap)
            {
                wordToSend = *txData;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 8U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 16U;
                ++txData;
            }
            else
            {
                wordToSend = (unsigned)(*txData) << 16U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 8U;
                ++txData;
                wordToSend |= *txData;
                ++txData;
            }
            break;

        case 4:
            if (!isByteSwap)
            {
                wordToSend = *txData;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 8U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 16U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 24U;
                ++txData;
            }
            else
            {
                wordToSend = (unsigned)(*txData) << 24U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 16U;
                ++txData;
                wordToSend |= (unsigned)(*txData) << 8U;
                ++txData;
                wordToSend |= *txData;
                ++txData;
            }
            break;

        default:
            assert(false);
            break;
    }
    return wordToSend;
}

static void LPSPI_SeparateReadData(uint8_t *rxData, uint32_t readData, uint32_t bytesEachRead, bool isByteSwap)
{
    assert(rxData);

    switch (bytesEachRead)
    {
        case 1:
            *rxData = readData;
            ++rxData;
            break;

        case 2:
            if (!isByteSwap)
            {
                *rxData = readData;
                ++rxData;
                *rxData = readData >> 8;
                ++rxData;
            }
            else
            {
                *rxData = readData >> 8;
                ++rxData;
                *rxData = readData;
                ++rxData;
            }
            break;

        case 3:
            if (!isByteSwap)
            {
                *rxData = readData;
                ++rxData;
                *rxData = readData >> 8;
                ++rxData;
                *rxData = readData >> 16;
                ++rxData;
            }
            else
            {
                *rxData = readData >> 16;
                ++rxData;
                *rxData = readData >> 8;
                ++rxData;
                *rxData = readData;
                ++rxData;
            }
            break;

        case 4:
            if (!isByteSwap)
            {
                *rxData = readData;
                ++rxData;
                *rxData = readData >> 8;
                ++rxData;
                *rxData = readData >> 16;
                ++rxData;
                *rxData = readData >> 24;
                ++rxData;
            }
            else
            {
                *rxData = readData >> 24;
                ++rxData;
                *rxData = readData >> 16;
                ++rxData;
                *rxData = readData >> 8;
                ++rxData;
                *rxData = readData;
                ++rxData;
            }
            break;

        default:
            assert(false);
            break;
    }
}

static void LPSPI_CommonIRQHandler(LPSPI_Type *base, void *param)
{
    if (LPSPI_IsMaster(base))
    {
        s_lpspiMasterIsr(base, (lpspi_master_handle_t *)param);
    }
    else
    {
        s_lpspiSlaveIsr(base, (lpspi_slave_handle_t *)param);
    }
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
  exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
#endif
}

#if defined(LPSPI0)
void LPSPI0_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[0]);
    LPSPI_CommonIRQHandler(LPSPI0, s_lpspiHandle[0]);
}
#endif

#if defined(LPSPI1)
void LPSPI1_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[1]);
    LPSPI_CommonIRQHandler(LPSPI1, s_lpspiHandle[1]);
}
#endif

#if defined(LPSPI2)
void LPSPI2_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[2]);
    LPSPI_CommonIRQHandler(LPSPI2, s_lpspiHandle[2]);
}
#endif

#if defined(LPSPI3)
void LPSPI3_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[3]);
    LPSPI_CommonIRQHandler(LPSPI3, s_lpspiHandle[3]);
}
#endif

#if defined(LPSPI4)
void LPSPI4_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[4]);
    LPSPI_CommonIRQHandler(LPSPI4, s_lpspiHandle[4]);
}
#endif

#if defined(LPSPI5)
void LPSPI5_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[5]);
    LPSPI_CommonIRQHandler(LPSPI5, s_lpspiHandle[5]);
}
#endif

#if defined(DMA__LPSPI0)
void DMA_SPI0_INT_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]);
    LPSPI_CommonIRQHandler(DMA__LPSPI0, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI0)]);
}
#endif

#if defined(DMA__LPSPI1)
void DMA_SPI1_INT_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]);
    LPSPI_CommonIRQHandler(DMA__LPSPI1, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI1)]);
}
#endif
#if defined(DMA__LPSPI2)
void DMA_SPI2_INT_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]);
    LPSPI_CommonIRQHandler(DMA__LPSPI2, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI2)]);
}
#endif

#if defined(DMA__LPSPI3)
void DMA_SPI3_INT_DriverIRQHandler(void)
{
    assert(s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]);
    LPSPI_CommonIRQHandler(DMA__LPSPI3, s_lpspiHandle[LPSPI_GetInstance(DMA__LPSPI3)]);
}
#endif
