/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2022. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_spi.c
 *
 * @brief This file provides spi integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_spi_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
#ifdef SPI_USE_INTERRUPT_TRANSMIT
uint8_t *g_spiRxBuf[SPI_INDEX_MAX] = {NULL};          /*!< SPIx Rx buffer pointer */
const uint8_t *g_spiTxBuf[SPI_INDEX_MAX] = {NULL};    /*!< SPIx Tx buffer pointer */
uint32_t g_spiRxCount[SPI_INDEX_MAX] = {0};           /*!< SPIx buffer receive counter */
uint32_t g_spiTxCount[SPI_INDEX_MAX] = {0};           /*!< SPIx buffer transmit counter */
uint32_t g_spiBuffSize[SPI_INDEX_MAX] = {0};          /*!< SPIx buffer size data */
uint32_t g_spiStatus[SPI_INDEX_MAX] = {0};            /*!< SPIx interrupt tx/rx work status */
#endif /* SPI_USE_INTERRUPT_TRANSMIT */

/*!< SPI Interrupt callback */
static DeviceCallback_Type s_spiCallbackArray[SPI_INDEX_MAX] = {(DeviceCallback_Type)NULL};

const SPI_InfoType g_spiInfoPool[SPI_INSTANCE_MAX] =
{
    {SPI0, SPI0_IRQn, CLK_SPI0, SRST_SPI0},
};
static uint32_t s_spiTimeOut = 0xfffffff;//unit:us, 32000 about 32ms

/* ====================================  Functions declaration  ===================================== */
#ifdef SPI_USE_INTERRUPT_TRANSMIT
static void SPI_InitInterruptHandler(SPI_Type *SPIx);
static void SPI_InterruptHandler(SPI_Type *SPIx, uint32_t *status);
static void SPI_MasterInterruptHandler(SPI_Type *SPIx, uint32_t *status);
static void SPI_SlaveInterruptHandler(SPI_Type *SPIx, uint32_t *status);
#endif /* SPI_USE_INTERRUPT_TRANSMIT */

static ERROR_Type SPI_ClearTxUFRxOF(SPI_Type *SPIx);

/* ======================================  Functions define  ======================================== */
/*!
* @brief Wait for RX
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_WaitRxFF(SPI_Type *SPIx)
{
    while (!SPI_IsRxFF(SPIx))
    {
        if (SPI_GetTransmitReceiveStatus(SPIx) & SPI_STATUS_MODE_FAULT_MASK)
        {
            break;
        }
    }
}

/*!
* @brief Wait for TX
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_WaitTxEF(SPI_Type *SPIx)
{
    while (!SPI_IsTxEF(SPIx))
    {
        if (SPI_GetTransmitReceiveStatus(SPIx) & SPI_STATUS_MODE_FAULT_MASK)
        {
            break;
        }
    }
}

/*!
* @brief Wait for Busy
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_WaitBusy(SPI_Type *SPIx)
{
    while (SPI_IsBusy(SPIx))
    {
        if (SPI_GetTransmitReceiveStatus(SPIx) & SPI_STATUS_MODE_FAULT_MASK)
        {
            break;
        }
    }
}

/*!
* @brief Initialize SPI module
*
* @param[in] SPIx: SPI type pointer
* @param[in] config: SPI configuration structure pointer which contains the configuration
*                    information for the specified SPI.
* @return none
*/
void SPI_Init(SPI_Type *SPIx, const SPI_ConfigType *config)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));
    DEVICE_ASSERT(IS_SPI_MODE(config->mode));
    DEVICE_ASSERT(IS_SPI_CPOL(config->cpol));
    DEVICE_ASSERT(IS_SPI_CPHA(config->cpha));
    DEVICE_ASSERT(IS_SPI_FRAMESIZE(config->frmSize));

    CKGEN_Enable(g_spiInfoPool[spiIndex].CKGEN_SPIx, ENABLE);       /* Enable SPI clock */
    CKGEN_SoftReset(g_spiInfoPool[spiIndex].SRST_SPIx, ENABLE);     /* Reset SPI Module */

    s_spiCallbackArray[spiIndex] = config->callBack;

#ifdef SPI_USE_INTERRUPT_TRANSMIT
    SPI_InitInterruptHandler(SPIx);
#endif

    SPI_SetMode(SPIx, config->mode);

    if (config->interruptEn)
    {
        NVIC_EnableIRQ(g_spiInfoPool[spiIndex].SPIx_IRQn);
    }
    else
    {
        NVIC_DisableIRQ(g_spiInfoPool[spiIndex].SPIx_IRQn);
    }

    SPI_SetBaudrate(SPIx, config->sckLow, config->sckHigh);   /* prescale is : low + high + 2 */

    SPI_CSHold(SPIx, config->csHold);
    SPI_CSSetup(SPIx, config->csSetup);
    SPI_SetCSIdle(SPIx, config->csIdle);
    SPI_SetFRMSize(SPIx, config->frmSize);

    SPI_SetCPHA(SPIx, config->cpha);
    SPI_SetCPOL(SPIx, config->cpol);
    SPI_SetTMSBF(SPIx, config->txMsbFirstEn);
    SPI_SetRMSBF(SPIx, config->rxMsbFirstEn);

    SPI_SetCSOE(SPIx, config->csOutputEn);

    SPI_SetContinuousCS(SPIx, config->continuousCSEn);
    SPI_SetModeFault(SPIx, config->modeFaultEn);

    SPI_SetModeFaultInterrupt(SPIx, config->modeFaultInterruptEn);  /* Set spi mode fault detect interrupt */
    SPI_SetTxUInterrupt(SPIx, config->txUFInterruptEn);             /* Set spi tx under flow interrupt */
    SPI_SetRxOInterrupt(SPIx, config->rxOFInterruptEn);             /* Set spi rx over flow interrupt */

    SPI_SetMasterFaultCheckTime(SPIx, 2U);

    SPI_SetWakeup(SPIx, config->wakeUpEn);

    SPI_SetEnable(SPIx, config->spiEn);
}

/*!
* @brief Uninitialize SPI module
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_DeInit(SPI_Type *SPIx)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    NVIC_DisableIRQ(g_spiInfoPool[spiIndex].SPIx_IRQn);
    NVIC_ClearPendingIRQ(g_spiInfoPool[spiIndex].SPIx_IRQn);
    CKGEN_SoftReset(g_spiInfoPool[spiIndex].SRST_SPIx, DISABLE);
    CKGEN_Enable(g_spiInfoPool[spiIndex].CKGEN_SPIx, DISABLE);

    s_spiCallbackArray[spiIndex] = NULL;
}

/*!
* @brief SPI transmission,reception by polling
*
* @param[in] SPIx: SPI type pointer
* @param[in] rxBuffer: point to the receive data
* @param[in] txBuffer: point to the send data
* @param[in] length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or rdbuff is NULL or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_TransmitReceivePoll(SPI_Type *SPIx, uint8_t *rxBuffer, const uint8_t *txBuffer, uint32_t length)
{
    uint32_t  i = 0;
    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (rxBuffer == NULL) || (txBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        /* Disable Tx/Rx only mode */
        SPI_SetTxOnly(SPIx, DISABLE);
        SPI_SetRxOnly(SPIx, DISABLE);

        if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* FrameSize is 9 bits ~ 16 bits */
        {
            if (((uint32_t)txBuffer & 0x01) || ((uint32_t)rxBuffer & 0x01)) /* txBuffer or rxBufer is not half-word alignment */
            {
                ret = ERROR;
                return ret;
            }

            length = length >> 0x01u;
            /* transmit and receive data */
            for (i = 0; i < length; i++)
            {
                SPI_WaitTxEF(SPIx);
                SPI_WriteDataReg(SPIx, ((uint16_t *)txBuffer)[i]);
                SPI_WaitRxFF(SPIx);
                ((uint16_t *)rxBuffer)[i] = SPI_ReadDataReg(SPIx);
            }
        }
        else  /* FrameSize is 4 bits ~ 8 bits */
        {
            /* transmit and receive data */
            for (i = 0; i < length; i++)
            {
                SPI_WaitTxEF(SPIx);
                SPI_WriteDataReg(SPIx, txBuffer[i]);
                SPI_WaitRxFF(SPIx);
                rxBuffer[i] = (uint8_t)SPI_ReadDataReg(SPIx);
            }
        }
        SPI_MasterReleaseCS(SPIx);
    }

    /* Check and Clear Tx under flow/ Rx over flow flag */
    if (SPI_ClearTxUFRxOF(SPIx) == ERROR)
    {
        ret = ERROR;
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

/*!
* @brief SPI transfer by polling, ignore receive (txOnly mode)
*
* @param[in] SPIx: SPI type pointer
* @param[in] txBuffer: point to the send data
* @param[in] length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_TransmitPoll(SPI_Type *SPIx, const uint8_t *txBuffer, uint32_t length)
{
    uint32_t  i;
    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (txBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        SPI_SetTxOnly(SPIx, ENABLE);
        SPI_SetRxOnly(SPIx, DISABLE);

        if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* FrameSize is 9 bits ~ 16 bits */
        {
            if ((uint32_t)txBuffer & 0x01) /* txBuffer is not half-word alignment */
            {
                ret = ERROR;
                return ret;
            }

            length = length >> 0x01u;
            for (i = 0; i < length; i++)
            {
                SPI_WaitTxEF(SPIx);
                SPI_WriteDataReg(SPIx, ((uint16_t *)txBuffer)[i]);
            }
        }
        else  /* FrameSize is 4 bits ~ 8 bits */
        {
            for (i = 0; i < length; i++)
            {
                SPI_WaitTxEF(SPIx);
                SPI_WriteDataReg(SPIx, txBuffer[i]);
            }
        }
        SPI_MasterReleaseCS(SPIx);
    }

    /* Check and Clear Tx under flow/ Rx over flow flag */
    if (SPI_ClearTxUFRxOF(SPIx) == ERROR)
    {
        ret = ERROR;
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

/*!
* @brief SPI read by polling, ignore write (rxOnly mode)
*
* @param[in] SPIx: SPI type pointer
* @param[in] rxBuffer: point to the receive data
* @param[in] Length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_ReceivePoll(SPI_Type *SPIx, uint8_t *rxBuffer, uint32_t length)
{
    uint32_t i = 0;
    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (rxBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        SPI_SetTxOnly(SPIx, DISABLE);
        SPI_SetRxOnly(SPIx, ENABLE);

        /* Master mode receive data */
        if (SPI_IsMaster(SPIx))
        {
            SPI_RxOnlyModeTrig(SPIx);    /* Master rx only mode trriger receive data */
            if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* FrameSize is 9 bits ~ 16 bits */
            {
                if ((uint32_t)rxBuffer & 0x01) /* rxBufer is not half-word alignment */
                {
                    ret = ERROR;
                    return ret;
                }
                length = length >> 0x01u;
                for (i = 0; i < length; i++)
                {
                    if (i == (length - 1))
                    {
                        SPI_SetRxOnly(SPIx, DISABLE);
                    }
                    SPI_WaitRxFF(SPIx);
                    ((uint16_t *)rxBuffer)[i] = SPI_ReadDataReg(SPIx);
                }
            }
            else  /* FrameSize is 4 bits ~ 8 bits */
            {
                for (i = 0; i < length; i++)
                {
                    if (i == (length - 1))
                    {
                        SPI_SetRxOnly(SPIx, DISABLE);
                    }
                    SPI_WaitRxFF(SPIx);
                    rxBuffer[i] = (uint8_t)SPI_ReadDataReg(SPIx);
                }
            }
        }
        else /* Slave mode receive data */
        {
            if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* FrameSize is 9 bits ~ 16 bits */
            {
                if ((uint32_t)rxBuffer & 0x01) /* rxBufer is not half-word alignment */
                {
                    ret = ERROR;
                    return ret;
                }
                length = length >> 0x01u;
                for (i = 0; i < length; i++)
                {
                    SPI_WaitRxFF(SPIx);
                    ((uint16_t *)rxBuffer)[i] = SPI_ReadDataReg(SPIx);
                }
            }
            else  /* FrameSize is 4 bits ~ 8 bits */
            {
                for (i = 0; i < length; i++)
                {
                    SPI_WaitRxFF(SPIx);
                    rxBuffer[i] = (uint8_t)SPI_ReadDataReg(SPIx);
                }
            }
        }
        SPI_MasterReleaseCS(SPIx);
    }

    /* Check and Clear Tx under flow/ Rx over flow flag */
    if (SPI_ClearTxUFRxOF(SPIx) == ERROR)
    {
        ret = ERROR;
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

#ifdef SPI_USE_INTERRUPT_TRANSMIT
/*!
* @brief SPI transmission, reception by interrupt
*
* @param[in] SPIx: SPI type pointer
* @param[in] rxBuffer: point to the receive data
* @param[in] txBuffer: point to the send data
* @param[in] length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or rdbuff is NULL or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_TransmitReceiveInt(SPI_Type *SPIx, uint8_t *rxBuffer, const uint8_t *txBuffer, uint32_t length)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (rxBuffer == NULL) || (txBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        SPI_SetTxOnly(SPIx, DISABLE);
        SPI_SetRxOnly(SPIx, DISABLE);

        if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* FrameSize is 9 bits ~ 16 bits */
        {
            if (((uint32_t)txBuffer & 0x01) || ((uint32_t)rxBuffer & 0x01)) /* txBuffer or rxBufer is not half-word alignment */
            {
                ret = ERROR;
                return ret;
            }
            g_spiBuffSize[spiIndex] = length >> 0x01u;
        }
        else   /* FrameSize is 4 bits ~ 8 bits */
        {
            g_spiBuffSize[spiIndex] = length;
        }
        g_spiRxBuf[spiIndex] = rxBuffer;
        g_spiTxBuf[spiIndex] = txBuffer;
        g_spiRxCount[spiIndex] = 0;
        g_spiTxCount[spiIndex] = 0;
        g_spiStatus[spiIndex] = SPI_STATUS_NONE;
        SPI_SetRxFInterrupt(SPIx, ENABLE);
        SPI_SetTxEInterrupt(SPIx, ENABLE);
    }

    return ret;
}

/*!
* @brief SPI transmission by interrupt, ignore reception (txOnly mode)
*
* @param[in] SPIx: SPI type pointer
* @param[in] txBuffer: point to the send data
* @param[in] length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_TransmitInt(SPI_Type *SPIx, const uint8_t *txBuffer, uint32_t length)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (txBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        SPI_SetTxOnly(SPIx, ENABLE);
        SPI_SetRxOnly(SPIx, DISABLE);

        if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* 9~16 bits frameSize */
        {
            if ((uint32_t)txBuffer & 0x01) /* txBuffer is not half-word alignment */
            {
                ret = ERROR;
                return ret;
            }
            g_spiBuffSize[spiIndex] = length >> 0x01u;
        }
        else   /* 4~8 bits frameSize */
        {
            g_spiBuffSize[spiIndex] = length;
        }
        g_spiRxBuf[spiIndex] = NULL;
        g_spiTxBuf[spiIndex] = txBuffer;
        g_spiRxCount[spiIndex] = 0;
        g_spiTxCount[spiIndex] = 0;
        g_spiStatus[spiIndex] = SPI_STATUS_NONE;
        SPI_SetRxFInterrupt(SPIx, DISABLE);
        SPI_SetTxEInterrupt(SPIx, ENABLE);
    }

    return ret;
}

/*!
* @brief SPI read by interrupt, ignore write (rxOnly mode)
*
* @param[in] SPIx: SPI type pointer
* @param[in] rxBuffer: point to the receive data
* @param[in] length: transfer data length
* @return Function status
*            0: ERROR, length is 0 or txBuffer is NULL
*            1: SUCCESS
*/
ERROR_Type SPI_ReceiveInt(SPI_Type *SPIx, uint8_t *rxBuffer, uint32_t length)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);
    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if ((length == 0) || (rxBuffer == NULL))
    {
        ret = ERROR;
    }
    else
    {
        SPI_SetTxOnly(SPIx, DISABLE);
        SPI_SetRxOnly(SPIx, ENABLE);

        if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS)  /* 9~16 bits frameSize */
        {
            if ((uint32_t)rxBuffer & 0x01) /* rxBufer is not half-word alignment */
            {
                ret = ERROR;
                return ret;
            }
            g_spiBuffSize[spiIndex] = length >> 0x01u;
        }
        else   /* 4~8 bits frameSize */
        {
            g_spiBuffSize[spiIndex] = length;
        }
        g_spiRxBuf[spiIndex] = rxBuffer;
        g_spiTxBuf[spiIndex] = NULL;
        g_spiRxCount[spiIndex] = 0;
        g_spiTxCount[spiIndex] = 0;
        g_spiStatus[spiIndex] = SPI_STATUS_NONE;
        SPI_SetTxEInterrupt(SPIx, DISABLE);
        SPI_SetRxFInterrupt(SPIx, ENABLE);

        /* Master rx only mode trriger receive data */
        if (SPI_IsMaster(SPIx))
        {
            SPI_RxOnlyModeTrig(SPIx);
        }
        else
        {
            /* Do nothing */
        }
    }

    return ret;
}

/*!
* @brief Get SPI interrupt transfer status
*
* @param[in] SPIx: SPI type pointer
* @return SPI transfer status
*               BIT0: SPI_STATUS_RX_OVERFLOW_MASK  0: No Rx over flow  1: Occour Rx over flow
*               BIT1: SPI_STATUS_TX_UNDERFLOW_MASK 0: No Tx under flow 1: Occour Tx under flow
*               BIT2: SPI_STATUS_MODE_FAULT_MASK   0: No Mode fault    1: Occour Mode fault
*               BIT3: SPI_STATUS_RX_FINISH_MASK    0: Rx by interrupt is not finished    1: Rx by interrupt is finished
*               BIT4: SPI_STATUS_TX_FINISH_MASK    0: Tx by interrupt is not finished    1: Tx by interrupt is finished
*/
uint32_t SPI_GetTransmitReceiveStatus(SPI_Type *SPIx)
{
    uint8_t  spiIndex = ((uint32_t)SPIx - (uint32_t)SPI0) >> 12;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    return g_spiStatus[spiIndex];
}
/*!
* @brief Reset SPI interrupt transfer status
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_ResetTransmitReceiveStatus(SPI_Type *SPIx)
{
    uint8_t  spiIndex = ((uint32_t)SPIx - (uint32_t)SPI0) >> 12;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    g_spiStatus[spiIndex] = SPI_STATUS_NONE;
}

/*!
* @brief Wait SPI receive(interrupt mode) finish
*
* @param[in] SPIx: SPI type pointer
* @return Function status
*            0: ERROR, timeout
*            1: SUCCESS
*/
ERROR_Type SPI_WaitReceiveFinish(SPI_Type *SPIx)
{
    uint32_t timer = 0;
    ERROR_Type ret = SUCCESS;

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    while (!(SPI_GetTransmitReceiveStatus(SPIx) & SPI_STATUS_RX_FINISH_MASK))
    {
        timer++;
        if ((timer > s_spiTimeOut) || ((SPI_GetTransmitReceiveStatus(SPIx) & SPI_STATUS_MODE_FAULT_MASK)))
        {
            SPI_ResetTransmitReceiveStatus(SPIx);
            ret = ERROR;
            break;
        }
    }

    SPI_ResetTransmitReceiveStatus(SPIx);

    return ret;
}

#endif /* SPI_USE_INTERRUPT_TRANSMIT */

/*!
* @brief Release SPI Master CS to stop continuous selection of Slave
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_MasterReleaseCS(SPI_Type *SPIx)
{
    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));
    if (SPI_IsMaster(SPIx))
    {
        SPI_WaitBusy(SPIx);
        SPI_CSRelease(SPIx);
    }
}

/*!
* @brief Clear SPI Tx under flow and Rx over flow status
*
* @param[in] SPIx: SPI type pointer
* @return Function status
*            0: ERROR, occour Tx under flow or Rx over flow flag
*            1: SUCCESS, no Tx under flow and Rx over flow flag
*/
static ERROR_Type SPI_ClearTxUFRxOF(SPI_Type *SPIx)
{
    ERROR_Type ret = SUCCESS;

    /* Clear Tx under flow flag */
    if (SPI_IsTxUF(SPIx))
    {
        SPI_ClearTxUF(SPIx);
        ret = ERROR;
    }
    else
    {
        /* Do nothing */
    }

    /* Clear Rx over flow flag */
    if (SPI_IsRxOF(SPIx))
    {
        SPI_ClearRxOF(SPIx);
        ret = ERROR;
    }
    else
    {
        /* Do nothing */
    }

    return ret;
}

/*!
* @brief Set SPI interrupt callback
*
* @param[in] SPIx: SPI type pointer
* @return none
*/
void SPI_SetCallback(SPI_Type *SPIx, const DeviceCallback_Type callback)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    s_spiCallbackArray[spiIndex] = callback;
}

#ifdef SPI_USE_INTERRUPT_TRANSMIT
/*!
* @brief Initialize SPI interrupt handler
*
* @param[in] void
* @return none
*/
static void SPI_InitInterruptHandler(SPI_Type *SPIx)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    if (spiIndex < SPI_INDEX_MAX)
    {
        g_spiRxBuf[spiIndex] = NULL;
        g_spiTxBuf[spiIndex] = NULL;
        g_spiRxCount[spiIndex] = 0;
        g_spiTxCount[spiIndex] = 0;
        g_spiStatus[spiIndex] = SPI_STATUS_NONE;
    }
}

/*!
* @brief SPI handles Tx and Rx in interrupt
*
* @param[in] SPIx: SPI type pointer
* @param[inout] status: input the status reg value, then output status reg value with interrupt enable mask
* @return none
*/
static void SPI_InterruptHandler(SPI_Type *SPIx, uint32_t *status)
{
    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if (SPI_IsMaster(SPIx))
    {
        SPI_MasterInterruptHandler(SPIx, status);
    }
    else
    {
        SPI_SlaveInterruptHandler(SPIx, status);
    }
}

/*!
* @brief Set SPI master callback function
*
* @param[in] SPIx: SPI type pointer
* @param[inout] status: input the status reg value, then output status reg value with interrupt enable mask
* @return none
*/
static void SPI_MasterInterruptHandler(SPI_Type *SPIx, uint32_t *status)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if (SPI_IsTxUF(SPIx) && SPI_IsTxUFInterruptEn(SPIx))
    {
        SPI_ClearTxUF(SPIx);
        g_spiStatus[spiIndex] |= SPI_STATUS_TX_UNDERFLOW_MASK;
        *status |= SPI_STATUS_TXUF_Msk;
    }

    if (SPI_IsRxOF(SPIx) && SPI_IsRxOFInterruptEn(SPIx))
    {
        SPI_ClearRxOF(SPIx);
        g_spiStatus[spiIndex] |= SPI_STATUS_RX_OVERFLOW_MASK;
        *status |= SPI_STATUS_RXOF_Msk;
    }

    if (SPI_IsRxFF(SPIx) && SPI_IsRxFFInterruptEn(SPIx))
    {
        if (g_spiRxCount[spiIndex] < g_spiBuffSize[spiIndex])
        {
            if (g_spiRxBuf[spiIndex] != NULL)
            {
                /* Disable Rx only mode when Receive last data, to avoid next read sck in Rx only mode */
                if (g_spiRxCount[spiIndex] == (g_spiBuffSize[spiIndex] - 1))
                {
                    SPI_SetRxOnly(SPIx, DISABLE);
                }
                /* Handle Receive data */
                if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS) /* FrameSize is 9 bits ~ 16 bits */
                {
                    ((uint16_t *)g_spiRxBuf[spiIndex])[g_spiRxCount[spiIndex]++] = SPI_ReadDataReg(SPIx);
                }
                else  /* FrameSize is 4 bits ~ 8 bits */
                {
                    g_spiRxBuf[spiIndex][g_spiRxCount[spiIndex]++] = (uint8_t)SPI_ReadDataReg(SPIx);
                }
            }
        }

        if (g_spiRxCount[spiIndex] >= g_spiBuffSize[spiIndex])
        {
            SPI_SetRxFInterrupt(SPIx, DISABLE);
            g_spiStatus[spiIndex] |= SPI_STATUS_RX_FINISH_MASK;
            SPI_MasterReleaseCS(SPIx);
        }
    }

    if (SPI_IsTxEF(SPIx) && SPI_IsTxEFInterruptEn(SPIx))
    {
        if (g_spiTxCount[spiIndex] < g_spiBuffSize[spiIndex])
        {
            if (g_spiTxBuf[spiIndex] != NULL)
            {
                /* Handle Receive data */
                if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS) /* FrameSize is 9 bits ~ 16 bits */
                {
                    SPI_WriteDataReg(SPIx, ((uint16_t *)g_spiTxBuf[spiIndex])[g_spiTxCount[spiIndex]++]);
                }
                else  /* FrameSize is 4 bits ~ 8 bits */
                {
                    SPI_WriteDataReg(SPIx, g_spiTxBuf[spiIndex][g_spiTxCount[spiIndex]++]);
                }
            }
        }

        else if (g_spiTxCount[spiIndex] >= g_spiBuffSize[spiIndex])
        {
            SPI_SetTxEInterrupt(SPIx, DISABLE);
            g_spiStatus[spiIndex] |= SPI_STATUS_TX_FINISH_MASK;
            SPI_MasterReleaseCS(SPIx);
        }
    }

    if (SPI_IsModeFault(SPIx) && SPI_IsModeFaultInterruptEn(SPIx))
    {
        SPI_SoftwareReset(SPIx);
        g_spiStatus[spiIndex] |= SPI_STATUS_MODE_FAULT_MASK;
        *status |= SPI_STATUS_MODEF_Msk;
    }
}

/*!
* @brief Set SPI slave callback function
*
* @param[in] SPIx: SPI type pointer
* @param[inout] status: input the status reg value, then output status reg value with interrupt enable mask
* @return none
*/
static void SPI_SlaveInterruptHandler(SPI_Type *SPIx, uint32_t *status)
{
    SPI_IndexType spiIndex = SPI_INDEX(SPIx);

    DEVICE_ASSERT(IS_SPI_PERIPH(SPIx));

    if (SPI_IsTxUF(SPIx) && SPI_IsTxUFInterruptEn(SPIx))
    {
        SPI_ClearTxUF(SPIx);
        SPI_SetTxEInterrupt(SPIx, DISABLE);
        g_spiStatus[spiIndex] |= SPI_STATUS_TX_UNDERFLOW_MASK;
        *status |= SPI_STATUS_TXUF_Msk;
    }

    if (SPI_IsRxOF(SPIx) && SPI_IsRxOFInterruptEn(SPIx))
    {
        SPI_ClearRxOF(SPIx);
        SPI_SetRxFInterrupt(SPIx, DISABLE);
        g_spiStatus[spiIndex] |= SPI_STATUS_RX_OVERFLOW_MASK;
        *status |= SPI_STATUS_RXOF_Msk;
    }

    if (SPI_IsRxFF(SPIx) && SPI_IsRxFFInterruptEn(SPIx))
    {
        if (g_spiRxCount[spiIndex] < g_spiBuffSize[spiIndex])
        {
            if (g_spiRxBuf[spiIndex] != NULL)
            {
                /* Handle Receive data */
                if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS) /* FrameSize is 9 bits ~ 16 bits */
                {
                    ((uint16_t *)g_spiRxBuf[spiIndex])[g_spiRxCount[spiIndex]++] = SPI_ReadDataReg(SPIx);
                }
                else  /* FrameSize is 4 bits ~ 8 bits */
                {
                    g_spiRxBuf[spiIndex][g_spiRxCount[spiIndex]++] = (uint8_t)SPI_ReadDataReg(SPIx);
                }
            }
        }

        if (g_spiRxCount[spiIndex] >= g_spiBuffSize[spiIndex])
        {
            SPI_SetRxFInterrupt(SPIx, DISABLE);
            g_spiStatus[spiIndex] |= SPI_STATUS_RX_FINISH_MASK;
        }
    }

    if (SPI_IsTxEF(SPIx) && SPI_IsTxEFInterruptEn(SPIx))
    {
        if (g_spiTxCount[spiIndex] < g_spiBuffSize[spiIndex])
        {
            if (g_spiTxBuf[spiIndex] != NULL)
            {
                /* Handle Receive data */
                if ((SPI_FrameSizeType)SPI_GetFRMSize(SPIx) > SPI_FRAME_SIZE_8BITS) /* FrameSize is 9 bits ~ 16 bits */
                {
                    SPI_WriteDataReg(SPIx, ((uint16_t *)g_spiTxBuf[spiIndex])[g_spiTxCount[spiIndex]++]);
                }
                else  /* FrameSize is 4 bits ~ 8 bits */
                {
                    SPI_WriteDataReg(SPIx, g_spiTxBuf[spiIndex][g_spiTxCount[spiIndex]++]);
                }
            }
        }

        else if (g_spiTxCount[spiIndex] >= g_spiBuffSize[spiIndex])
        {
            SPI_SetTxEInterrupt(SPIx, DISABLE);
            g_spiStatus[spiIndex] |= SPI_STATUS_TX_FINISH_MASK;
        }
    }
}
#endif /* SPI_USE_INTERRUPT_TRANSMIT */

/*!
* @brief override the SPI0_IRQHandler
*
* @param[in] none
* @return none
*/
void SPI0_IRQHandler(void)
{
    uint32_t statusReg = SPI0->STATUS;
    uint32_t cfg1Reg = SPI0->CFG1;

#ifdef SPI_USE_INTERRUPT_TRANSMIT
    SPI_InterruptHandler(SPI0, &statusReg);
#endif

    if (s_spiCallbackArray[SPI0_INDEX])
    {
        s_spiCallbackArray[SPI0_INDEX](SPI0, statusReg, cfg1Reg);
    }
}

/* =============================================  EOF  ============================================== */
