/**
 * @file hv_cal_Qspi.c
 * @brief qspi driver chip abstract layer file.
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-06-19
 * @copyright Copyright(c),2023-6, Hiview Software. All rights reserved.
 * </table>
 */

#include "hv_cal_Qspi.h"
#include "hv_cal_Dma.h"
#include "hv_vos_Comm.h"
#include "hv_chip_Config.h"
#include "hv_comm_Define.h"
#include "hv_comm_Assert.h"

//#define QSPI_USE_INT_DMA

struct _QspiSelf
{
     /*!< Qspi communication parameters.            */
    QspiInitParam       InitParam;
    /*!< Pointer to qspi command and address buffer.*/
    UCHAR8              *pCmdAddr;
    /*!< Qspi command and address size.             */
    USHORT16            cmdAddrSize;
    /*!< Pointer to qspi transfer buffer.           */
    UCHAR8              *pTxBuff;
    /*!< Qspi transfer size.                        */
    USHORT16            txferSize;
     /*!< Use for getting already transmit Num.     */
    USHORT16            txferCount;
     /*!< Pointer to qspi transfer buffer.          */
    UCHAR8              *pRxBuff;
     /*!< Qspi transfer size.                       */
    USHORT16            rxferSize;
    /*!< Use for getting already receive Num.       */
    USHORT16            rxferCount;
    /*!< The Dummy size of receive.                 */
    USHORT16            usDumSize;
    /*!< Qspi communication state.                  */
    QspiState           State;
    UINT32              baseOffset;
    /*!< Qspi Error code.                           */
    UINT32              ErrorCode;
    QspiTransComplete   TransCompleteFlag;
#ifdef QSPI_USE_INT_DMA
    HV_VOS_SEMAPHORE_S* pstTransCompleteSema;
    
    DmaSelf*            hdmaTx;
    DmaSelf*            hdmaRx;
#endif
    void*               hflash;
};

static QspiSelf s_QspiFlash;

static Status Qspi_WaitOnFlagUntilTimeout(QspiSelf * pstSelf, UINT32 uiFlag,
                                          UINT32 uiStatus, UINT32 uiTimeout);

static void Qspi_CsToGpio(void)
{
    //add here
    return;
}
static void Qspi_GpioToCs(void)
{
    //add here
    return;
}
static void Qspi_SetCsLow(void)
{
    //add here
    return;
}
static void Qspi_SetCsHigh(void)
{
    //add here
    return;
}

static UCHAR8 Qspi_GetFlag(QspiSelf *pstSelf, UINT32 uiFlagBit)
{
    UINT32 uiValue = 0;
    if ((uiFlagBit >> 16) == 0)
    {
        uiValue = HV_R32(CPU_SYS_2_QSPI_RISR + pstSelf->baseOffset);
    }
    else
    {
        uiValue = HV_R32(CPU_SYS_2_QSPI_SR + pstSelf->baseOffset);
    }
    return (uiValue & uiFlagBit & QSPI_FLAG_MASK) == (uiFlagBit & QSPI_FLAG_MASK);
}

static UCHAR8 Qspi_IntSelectEn(QspiSelf *pstSelf, UINT32 uiIntFlag)
{
    UINT32 uiIntFlagRegVal = 0;

    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 0);
    uiIntFlagRegVal = HV_R32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset);
    uiIntFlagRegVal = uiIntFlagRegVal | (uiIntFlag & QSPI_FLAG_MASK);

    HV_W32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset, uiIntFlagRegVal);
    uiIntFlagRegVal = HV_R32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset);
    return HV_SUCCESS;
}

static UCHAR8 Qspi_IntSelectDis(QspiSelf *pstSelf, UINT32 uiIntFlag)
{
    UINT32 uiIntFlagRegVal = 0;

    uiIntFlagRegVal = HV_R32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset);
    uiIntFlagRegVal = uiIntFlagRegVal & (~(uiIntFlag & QSPI_FLAG_MASK));
    HV_W32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset, uiIntFlagRegVal);
    uiIntFlagRegVal = HV_R32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset);
    return HV_SUCCESS;
}

void Hv_Cal_Qspi_XipReset(QspiSelf *pstSelf)
{
    return;
}

static UCHAR8* Qspi_TransmitOne(QspiSelf *pstSelf, UINT32 uiCmdDataRegAddr, UCHAR8* pucData)
{
    UCHAR8   ucDR0Value  = 0;
    USHORT16  usDR0Value = 0;
    rs_cpu_sys_2_qspi_dr0 regDR0Val = {0};

    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
    {
        ucDR0Value = *pucData++;
        regDR0Val.DR0 = (UINT32)ucDR0Value;
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
    {
        regDR0Val.DR0 = CHANGE_TO_U32_BE(pucData);
        pucData = pucData + 4;
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
    {
        usDR0Value = CHANGE_TO_U16_BE(pucData);
        regDR0Val.DR0 = (UINT32)usDR0Value;
        pucData = pucData + 2;
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_24)
    {
        regDR0Val.DR0 = CHANGE_TO_U24_BE(pucData);
        pucData = pucData + 3;
    }

    HV_W32_REG(uiCmdDataRegAddr, regDR0Val);

    return pucData;
}

static UCHAR8* Qspi_ReceiveOne(QspiSelf *pstSelf, UCHAR8* pucData)
{
    UINT32  fromDR0Val_32Bits = 0;
    fromDR0Val_32Bits = HV_R32(CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset);


    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
    {
        *pucData++ =  (UCHAR8)((fromDR0Val_32Bits ) & 0xff);
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
    {
        *((UINT32*)(pucData)) = HV_SWAP32(fromDR0Val_32Bits);
        pucData = pucData + 4;
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
    {
        *((USHORT16*)(pucData)) = CHANGE_ENDIAN_U16((fromDR0Val_32Bits) & 0xffff);
        pucData = pucData + 2;
    }

    return pucData;
}
#ifdef QSPI_USE_INT_DMA
static Status Qspi_IntTransmitCpltCallback(QspiSelf *pstSelf)
{
    if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_TFE, HV_SET, QSPI_TIMEOUT) != HV_SUCCESS)
    {
        return HV_TIMEOUT;
    }
    if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
    {
        return HV_TIMEOUT;
    }
    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
        Qspi_SetCsHigh();
    }
    pstSelf->State = QSPI_STATE_READY;
    pstSelf->TransCompleteFlag = QSPI_TRANS_END;

    Hv_Vos_ReleaseSemaphoreFromISR(pstSelf->pstTransCompleteSema);

    if (pstSelf->InitParam.QspiCpltCallback != NULL)
    {
        pstSelf->InitParam.QspiCpltCallback(QSPI_WRITE,(void*)pstSelf);
    }
    return HV_SUCCESS;
}

static Status Qspi_IntReceiveCpltCallback(QspiSelf *pstSelf)
{
    if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
    {
        return HV_TIMEOUT;
    }

    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
        Qspi_SetCsHigh();
    }
    pstSelf->State = QSPI_STATE_READY;
    pstSelf->TransCompleteFlag = QSPI_TRANS_END;

    Hv_Vos_ReleaseSemaphoreFromISR(pstSelf->pstTransCompleteSema);

    if (pstSelf->InitParam.QspiCpltCallback != NULL)
    {
        pstSelf->InitParam.QspiCpltCallback(QSPI_READ,(void*)pstSelf);
    }
    return HV_SUCCESS;
}

static Status Qspi_IntTransmitHandler(QspiSelf *pstSelf)
{
    UCHAR8  aucDummy[4] = {0};
    UINT32 uiLoop       = 0;
    UINT32 uiIntFlag    = 0;
    UINT32 uiCmdDataRegAddr     = 0;
    UINT32 uiTxFifoSurplusLev   = 0;
    UINT32 uiTxDataNumOnce      = 0;
    UINT32 uiTxDataTotalNum     = pstSelf->cmdAddrSize + pstSelf->txferSize;
    UCHAR8* pucTxData = NULL;

    if (pstSelf->txferSize != 0)
    {
        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
        {
            pucTxData = &pstSelf->pTxBuff[pstSelf->txferCount];
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            pucTxData = &pstSelf->pTxBuff[4 * pstSelf->txferCount];
        }
    }
    uiCmdDataRegAddr = CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset;
    uiTxFifoSurplusLev = QSPI_FIFO_DEPTH - (HV_R32(CPU_SYS_2_QSPI_TXFLR + pstSelf->baseOffset) & 0x1f);
    uiTxDataNumOnce = (uiTxDataTotalNum > uiTxFifoSurplusLev)? uiTxFifoSurplusLev : uiTxDataTotalNum;

    for (uiLoop = 0; uiLoop < uiTxDataNumOnce; uiLoop++)
    {
        if (pstSelf->cmdAddrSize > 0)
        {
            pstSelf->pCmdAddr = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pstSelf->pCmdAddr);
            pstSelf->cmdAddrSize = pstSelf->cmdAddrSize - 1;
            uiCmdDataRegAddr = uiCmdDataRegAddr + 4;
        }
        else
        {
            if (pstSelf->State == QSPI_STATE_BUSY_TX)
            {
                pucTxData = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pucTxData);
                pstSelf->txferCount = pstSelf->txferCount + 1;
            }
            else if (pstSelf->State == QSPI_STATE_BUSY_TXRX)
            {
                Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, aucDummy);
            }
            pstSelf->txferSize = pstSelf->txferSize - 1;
            uiCmdDataRegAddr = uiCmdDataRegAddr + 4;
        }
    }
    if ((pstSelf->cmdAddrSize + pstSelf->txferSize) == 0)
    {
        uiIntFlag = QSPI_FLAG_TXEIR | QSPI_FLAG_TXOIR;
        Qspi_IntSelectDis(pstSelf, uiIntFlag);

        if (pstSelf->State == QSPI_STATE_BUSY_TX)
        {
            Qspi_IntTransmitCpltCallback(pstSelf);
        }
    }
    return HV_SUCCESS;
}

static Status Qspi_IntReceiveHandler(QspiSelf *pstSelf)
{
    UCHAR8  aucDumSize[4]   = {0};
    UINT32 uiLoop           = 0;
    UINT32 uiIntFlag        = 0;
    UINT32 rxFifoDataLev    = 0;
    UCHAR8* pRxData         = NULL;

    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
    {
        pRxData = &pstSelf->pRxBuff[pstSelf->rxferCount];
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
    {
        pRxData = &pstSelf->pRxBuff[2 * pstSelf->rxferCount];
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
    {
        pRxData = &pstSelf->pRxBuff[4 * pstSelf->rxferCount];
    }

    rxFifoDataLev = (HV_R32(CPU_SYS_2_QSPI_RXFLR + pstSelf->baseOffset)) & 0x1f;
    for (uiLoop = 0; uiLoop < rxFifoDataLev; uiLoop++)
    {
        if (pstSelf->usDumSize != 0)
        {
            Qspi_ReceiveOne(pstSelf, aucDumSize);
            pstSelf->usDumSize = pstSelf->usDumSize - 1;
        }
        else
        {
            pRxData = Qspi_ReceiveOne(pstSelf, pRxData);
            pstSelf->rxferSize = pstSelf->rxferSize - 1;
            pstSelf->rxferCount = pstSelf->rxferCount + 1;
        }
    }
    if (pstSelf->rxferSize == 0)
    {
        uiIntFlag = QSPI_FLAG_RXUIR | QSPI_FLAG_RXOIR | QSPI_FLAG_RXFIR;
        Qspi_IntSelectDis(pstSelf, uiIntFlag);
        Qspi_IntReceiveCpltCallback(pstSelf);
    }
    return HV_SUCCESS;
}

static Status Qspi_ConfigDmaTxReg(QspiSelf *pstSelf, UINT32 EnFlag, UINT32 QspiTxThresDma )
{
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 0);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_DMACR, pstSelf->baseOffset, TDMAE, EnFlag);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_DMATDLR, pstSelf->baseOffset, DMATDL, QspiTxThresDma);
    return HV_SUCCESS;
}

static Status Qspi_ConfigDmaRxReg(QspiSelf *pstSelf, UINT32 EnFlag, UINT32 QspiRxThresDma)
{
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 0);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_DMACR, pstSelf->baseOffset, RDMAE, EnFlag);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_DMARDLR, pstSelf->baseOffset, DMARDL, QspiRxThresDma);
    return HV_SUCCESS;
}

static Status Qspi_DmaTransmitCpltCallback(DmaSelf* arg)
{
    QspiSelf *pstSelf = (QspiSelf*)Hv_Cal_Dma_GetParent(arg);

    if (pstSelf->State == QSPI_STATE_BUSY_TX)
    {
        if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
        {
            return HV_TIMEOUT;
        }
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsHigh();
        }
        HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 0);

        pstSelf->State = QSPI_STATE_READY;
        pstSelf->TransCompleteFlag = QSPI_TRANS_END;
        Hv_Cal_Dma_XferCallbackUnBond(pstSelf->hdmaTx);
        Hv_Vos_InvalidAllDCache();

        if (pstSelf->InitParam.QspiCpltCallback != NULL)
        {
            pstSelf->InitParam.QspiCpltCallback(QSPI_WRITE,(void*)pstSelf);
        }

        Hv_Vos_ReleaseSemaphoreFromISR(pstSelf->pstTransCompleteSema);

    }
    return HV_SUCCESS;
}

static Status Qspi_DmaReceiveCpltCallback(DmaSelf* arg)
{

    QspiSelf *pstSelf = (QspiSelf *)Hv_Cal_Dma_GetParent(arg);
    if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
    {
        return HV_TIMEOUT;
    }

    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
        Qspi_SetCsHigh();
    }
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 0);

    pstSelf->State = QSPI_STATE_READY;
    pstSelf->TransCompleteFlag = QSPI_TRANS_END;
    Hv_Cal_Dma_XferCallbackUnBond(pstSelf->hdmaRx);
    Hv_Vos_InvalidAllDCache();
    if (pstSelf->InitParam.QspiCpltCallback != NULL)
    {
        pstSelf->InitParam.QspiCpltCallback(QSPI_READ,(void*)pstSelf);
    }

    Hv_Vos_ReleaseSemaphoreFromISR(pstSelf->pstTransCompleteSema);

    return HV_SUCCESS;
}
#endif
static Status Qspi_WaitOnFlagUntilTimeout(QspiSelf * pstSelf, UINT32 uiFlag, UINT32 Status, UINT32 uiTimeout)
{

    UINT64 tickstart = 0;
    tickstart = Hv_Vos_GetTick();

    if (Status == 1)
    {
        while (Qspi_GetFlag(pstSelf, uiFlag) == 0)
        {
            /* Wait the flag set*/
            if ((uiTimeout != 0) && ((Hv_Vos_GetTick() - tickstart ) > uiTimeout))
            {
                pstSelf->ErrorCode = QSPI_ERROR_TIMEOUT;
                pstSelf->State = QSPI_STATE_READY;
                return HV_TIMEOUT;
            }
        }
    }
    else
    {
        while (Qspi_GetFlag(pstSelf, uiFlag) == 1)
        {
            /* Wait the flag reset*/
            if ((uiTimeout != 0) && ((Hv_Vos_GetTick() - tickstart ) > uiTimeout))
            {
                pstSelf->ErrorCode = QSPI_ERROR_TIMEOUT;
                pstSelf->State = QSPI_STATE_READY;
                return HV_TIMEOUT;
            }
        }
    }
    return HV_SUCCESS;
}
#ifdef QSPI_USE_INT_DMA
static HV_VOS_ISR_RESULT_E  Qspi_IrqHandler(UINT32 irqNum, void *arg)
{
    UINT32 intStaVal    = 0;
    QspiSelf *pstSelf   = (QspiSelf *)arg;

    HV_UNUSED(irqNum);
    intStaVal = HV_R32(CPU_SYS_2_QSPI_ISR + pstSelf->baseOffset);
    if ((intStaVal & QSPI_FLAG_TXEIR) == QSPI_FLAG_TXEIR)
    {
        Qspi_IntTransmitHandler(pstSelf);
    }
    if ((intStaVal & QSPI_FLAG_TXOIR) == QSPI_FLAG_TXOIR)
    {
        HV_LOGI("Qspi Txoir.\n");
    }
    if ((intStaVal & QSPI_FLAG_RXUIR) == QSPI_FLAG_RXUIR)
    {
        HV_LOGI("Qspi Rxuir.\n");
    }
    if ((intStaVal & QSPI_FLAG_RXOIR) == QSPI_FLAG_RXOIR)
    {
        HV_LOGI("Qspi Rxoir.\n");
        HV_R32(CPU_SYS_2_QSPI_RXOICR + pstSelf->baseOffset);
    }
    if ((intStaVal & QSPI_FLAG_RXFIR) == QSPI_FLAG_RXFIR)
    {
        Qspi_IntReceiveHandler(pstSelf);
    }
    if ((intStaVal & QSPI_FLAG_MSTIR) == QSPI_FLAG_MSTIR)
    {
    }
    return HV_VOS_ISR_HANDLED;
}
#endif
void Hv_Cal_Qspi_SetFlashPoint(QspiSelf *pstSelf,void* FlashPoint)
{
    pstSelf->hflash = FlashPoint;
}

void* Hv_Cal_Qspi_GetFlashPoint(QspiSelf *pstSelf)
{
    return pstSelf->hflash;
}
#ifdef QSPI_USE_INT_DMA
/** Tx and Rx Transfer completed callbacks
 *  @param self pointer to a QSPI structure that contains the configuration information.
 *  @param CallbackFunc callback defined by user.
 */
void Hv_Cal_Qspi_SetCpltCallBack(QspiSelf *pstSelf, void * CallbackFunc)
{
    pstSelf->InitParam.QspiCpltCallback = (Hv_Qspi_CpltCallback)CallbackFunc;
}
#else
void Hv_Cal_Qspi_SetCpltCallBack(QspiSelf *pstSelf, void * CallbackFunc){return;}
#endif
QspiRateMode Hv_Cal_Qspi_GetRateMode(QspiSelf *pstSelf)
{
    return pstSelf->InitParam.RateMode;
}

void Hv_Cal_Qspi_SetRateMode(QspiSelf *pstSelf, QspiRateMode  RateMode)
{
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset,0);
    /* Set SPI_FRF bits */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_CTRLR0, pstSelf->baseOffset, SPI_FRF, RateMode);
    pstSelf->InitParam.RateMode = RateMode;
}

/** Set transfer mode as direction
 *  @param self pointer to a QSPI structure that contains the configuration information.
 *  @param Direction 0x0(TX_AND_RX),0x1(TX_ONLY),0x2(RX_ONLY),0x3(EEPROM_READ).
 */
void Hv_Cal_Qspi_SetDirection(QspiSelf *pstSelf, QspiDirection  Direction)
{
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0);
     /* Set TMOD bits */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_CTRLR0, pstSelf->baseOffset, TMOD, Direction);
    pstSelf->InitParam.Direction = Direction;
}

/** Set data frame size
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param FrameWidth Indicates how many bits of data are serially transmitted in a frame.
 */
void Hv_Cal_Qspi_SetFrameBitsWidth(QspiSelf *pstSelf, QspiBitWidth FrameWidth)
{

    UCHAR8  FrameWidthSetVal = FrameWidth;
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset,0);
    /* Set DFS bits */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_CTRLR0, pstSelf->baseOffset, DFS, FrameWidthSetVal);
    pstSelf->InitParam.DataSize = (QspiBitWidth)FrameWidthSetVal;
}

/** Set the baudrate
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param BaudRate baudrate which will set as SSI Clock Divider.
 */
void Hv_Cal_Qspi_SetBaudRate(QspiSelf *pstSelf, QspiDivideRatio BaudRate)
{
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_BAUDR, pstSelf->baseOffset, SCKDV, BaudRate);
    pstSelf->InitParam.BaudRatePrescaler = (QspiDivideRatio)BaudRate;
}

/** Set TOP_QSPI_SPI_CTRLR0 register, contains SSIC_EN, INST_L, ADDR_L, WAIT_CYCLES
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param instrWidth Dual/Quad/Octal mode instruction length in bits
 *  @param AddrWidth Defines length of address to be transmitted.
 *  @param waitCycle Wait cycles in dual/quad/octal mode between control frames transmit
 *  and data reception. Specified as number of SPI clock cycles.
 */
void Hv_Cal_Qspi_SetMultiIOCtrl(QspiSelf *pstSelf, QspiInstruAddrType transType,UCHAR8 instrWidth,
                                UCHAR8 AddrWidth, UINT32 waitCycle)
{
    rs_cpu_sys_2_qspi_spi_ctrlr0 spiCtrlVal;
    UCHAR8 instrWidthVal = instrWidth / 4;
    UCHAR8 addrWidthVal = AddrWidth / 4;

    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0);
    HV_R32_REG(CPU_SYS_2_QSPI_SPI_CTRLR0 + pstSelf->baseOffset,spiCtrlVal);
    spiCtrlVal.TRANS_TYPE = transType;
    spiCtrlVal.INST_L = instrWidthVal;
    spiCtrlVal.ADDR_L = addrWidthVal;
    spiCtrlVal.WAIT_CYCLES = waitCycle;
    HV_W32_REG(CPU_SYS_2_QSPI_SPI_CTRLR0 + pstSelf->baseOffset,spiCtrlVal);
}

/** Set the length of address to be transmitted.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param AddrWidth Defines length of address to be transmitted.
 */
void Hv_Cal_Qspi_SetAddrBitsWidth(QspiSelf *pstSelf, UCHAR8 AddrWidth)
{
    UCHAR8 addrWidthSetVal = AddrWidth / 4;
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset,0);
    /* Set addr bits */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SPI_CTRLR0, pstSelf->baseOffset, ADDR_L, addrWidthSetVal);
}

/** Sets the number of data frames to be continuously received by the DWC_ssi.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param readDataNum number of data frames
 */
void Hv_Cal_Qspi_SetReadNumber(QspiSelf *pstSelf, UINT32 readDataNum)
{
    UCHAR8  ReadNumSetVal = 0;
    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
    {
        readDataNum = readDataNum / 2;
    }
    else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
    {
        readDataNum = readDataNum / 4;
    }
    ReadNumSetVal = readDataNum - 1;
    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0);
    /* Set read data ctrl1 reg */
    HV_W32(CPU_SYS_2_QSPI_CTRLR1 + pstSelf->baseOffset, ReadNumSetVal);
}

/** Initializes the qspi flash peripheral according to the specified parameters.
 *  @param initParam pointer to the configuration information.
 *  @return qspi flash structure pointer
 */
QspiSelf* Hv_Cal_Qspi_Init( QspiInitParam *initParam)
{

    QspiSelf *pstSelf = NULL;
    rs_cpu_sys_2_qspi_ctrlr0  regCtrl0Val           = {0};
    rs_cpu_sys_2_qspi_spi_ctrlr0  regSpiCtrl0Val    = {0};
    rs_cpu_sys_2_qspi_baudr  regBaudrVal            = {0};
    rs_cpu_sys_2_qspi_txftlr regTxftflrVal          = {0};
    rs_cpu_sys_2_qspi_rxftlr regRxftflrVal          = {0};

    HV_ASSERT_TRUE_RET(initParam != NULL,NULL);

    HV_MEMSET(&s_QspiFlash, 0, sizeof(QspiSelf));
    pstSelf = &s_QspiFlash;
    pstSelf->baseOffset = 0;

    HV_MEMCPY(&pstSelf->InitParam, initParam, sizeof(*initParam));

    HV_W32_FIELD(SYS_REG_SH5_TOP0_REG_SH5_WDG_FILED2, reg_sh5_qspi_xip_en, 0);

    /* Disable qspi master */
    HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset,0);
    /* Clear qspi master interrupt status*/
    HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);
    /* ping all interrupt*/
    HV_W32(CPU_SYS_2_QSPI_IMR + pstSelf->baseOffset, 0x00);

    /*set Receive Data (rxd) Sample Delay.*/
    //HV_W32_FIELD_EX(CPU_SYS_2_QSPI_RX_SAMPLE_DELAY, pstSelf->baseOffset, RSD, 2);

    regCtrl0Val.DFS = initParam->DataSize;
    regCtrl0Val.SCPH = initParam->CLKPhase;
    regCtrl0Val.SCPOL = initParam->CLKPolarity;
    regCtrl0Val.TMOD = initParam->Direction;
    regCtrl0Val.SPI_FRF = initParam->RateMode;
    regCtrl0Val.SSI_IS_MST = 1;
    regCtrl0Val.SSTE = 0;
    HV_W32_REG(CPU_SYS_2_QSPI_CTRLR0 + pstSelf->baseOffset, regCtrl0Val);

    /* Config qspi master BAUDR reg*/
    regBaudrVal.SCKDV = initParam->BaudRatePrescaler;
    HV_W32_REG(CPU_SYS_2_QSPI_BAUDR + pstSelf->baseOffset, regBaudrVal);

    /* Config qspi master TXFTLR and RXFTLR reg*/
    regTxftflrVal.TFT = QSPI_FIFO_DEPTH/2;
    regRxftflrVal.RFT = 0;
    HV_W32_REG(CPU_SYS_2_QSPI_TXFTLR + pstSelf->baseOffset, regTxftflrVal);
    HV_W32_REG(CPU_SYS_2_QSPI_RXFTLR + pstSelf->baseOffset, regRxftflrVal);
#ifdef QSPI_USE_INT_DMA
    Hv_Vos_AttachIsr(HV_CHIP_IRQ_QSPI, Qspi_IrqHandler, pstSelf);
    Hv_Vos_UnmaskIrq(HV_CHIP_IRQ_QSPI);
    Hv_Vos_ConfigIrq(HV_CHIP_IRQ_QSPI,HV_TRUE,0);
#endif
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SER, pstSelf->baseOffset, SER, 1);
    pstSelf->State = QSPI_STATE_READY;
    pstSelf->ErrorCode = QSPI_ERROR_NONE;
#ifdef QSPI_USE_INT_DMA
    pstSelf->pstTransCompleteSema = Hv_Vos_InitSemaphore(1, 0);
#endif
    if (initParam->CsSel == QSPI_CS_BY_GPIO)
    {
      Qspi_CsToGpio();
      Qspi_SetCsHigh();
    }
    return pstSelf;
}

/** De-initialize qspi flash
 *  @param self qspi flash pointer
 *  @return result
 */
Status Hv_Cal_Qspi_Cleanup(QspiSelf *pstSelf)
{

    pstSelf->State = QSPI_STATE_BUSY;
    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
       Qspi_GpioToCs();
    }
#ifdef QSPI_USE_INT_DMA
    if (pstSelf->hdmaTx !=NULL)
    {
       Qspi_ConfigDmaTxReg(pstSelf, QSPI_DMATX_DISABLE, QSPI_DMA_TX_THRESHOLD);
       Hv_Cal_Dma_CleanUp(pstSelf->hdmaTx);
    }
    if (pstSelf->hdmaRx !=NULL)
    {
       Qspi_ConfigDmaRxReg(pstSelf, QSPI_DMARX_DISABLE, QSPI_DMA_RX_THRESHOLD);
       Hv_Cal_Dma_CleanUp(pstSelf->hdmaRx);
    }

    Hv_Vos_MaskIrq(HV_CHIP_IRQ_QSPI);
    Hv_Vos_DetachIsr(HV_CHIP_IRQ_QSPI, Qspi_IrqHandler);
#endif
    Hv_Cal_Qspi_XipReset(pstSelf);
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 1);

    HV_W32_FIELD(SYS_REG_SH5_TOP0_REG_SH5_WDG_FILED2, reg_sh5_qspi_xip_en, 1);

    pstSelf->ErrorCode = QSPI_ERROR_NONE;
    pstSelf->State = QSPI_STATE_RESET;
#ifdef QSPI_USE_INT_DMA
    Hv_Vos_CleanupSemaphore(pstSelf->pstTransCompleteSema);
#endif
    return HV_SUCCESS;
}
#ifdef QSPI_USE_INT_DMA
void Hv_Cal_Qspi_SetDmaTx(QspiSelf *pstSelf, DmaSelf *hdmaTx)
{
    pstSelf->hdmaTx = hdmaTx;
    Hv_Cal_Dma_SetParent(hdmaTx,(void*)pstSelf);
    Hv_Cal_Dma_SetDstAddr(hdmaTx,CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset);
    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
    {
        Hv_Cal_Dma_SetSrcFrameSize(hdmaTx,DMA_DATA_SIZE_32Bits);
        Hv_Cal_Dma_SetDstFrameSize(hdmaTx,DMA_DATA_SIZE_8Bits);
    }
}

void Hv_Cal_Qspi_SetDmaRx(QspiSelf *pstSelf, DmaSelf *hdmaRx)
{
    pstSelf->hdmaRx = hdmaRx;
    Hv_Cal_Dma_SetParent(hdmaRx,(void*)pstSelf);
    Hv_Cal_Dma_SetSrcAddr(hdmaRx,CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset);
    if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
    {
        Hv_Cal_Dma_SetSrcFrameSize(hdmaRx,DMA_DATA_SIZE_8Bits);
        Hv_Cal_Dma_SetDstFrameSize(hdmaRx,DMA_DATA_SIZE_32Bits);
    }
}
#else
void Hv_Cal_Qspi_SetDmaTx(QspiSelf *pstSelf, DmaSelf *hdmaTx){return;}
void Hv_Cal_Qspi_SetDmaRx(QspiSelf *pstSelf, DmaSelf *hdmaRx){return;}
#endif
/** Write data to qspi flash in blocking mode.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param pCmdSize command length
 *  @param pTxData pointer to data buffer to be transmited
 *  @param size amount of data to be sent
 *  @param timeout timeout duration
 *  @retval result
 */
Status Hv_Cal_Qspi_PollingWrite(QspiSelf *pstSelf, UCHAR8* pCmdAddr, USHORT16 cmdAddrSize,
                                UCHAR8* pucTxData, USHORT16 dataSize, UINT32 uiTimeout)
{

    UINT32 uiLoop           = 0;
    UINT32 uiTxTotalSize    = 0;
    UINT32 uiTxFifoSurpLev  = 0;
    UINT32 uiTxNumOnce      = 0;
    UINT32 uiCmdDataRegAddrBase = 0;
    UINT32 uiCmdDataRegAddr = 0;

    Hv_Vos_TaskEnterCritical();

    if (pstSelf->State ==  QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_TX))
        {
            Hv_Vos_TaskExitCritical();
            return HV_FAILURE;
        }

        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
        {
            cmdAddrSize = cmdAddrSize/2;
            dataSize = dataSize/2;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            cmdAddrSize = cmdAddrSize/4;
            dataSize = dataSize/4;
        }
        uiTxTotalSize = cmdAddrSize + dataSize;
        uiCmdDataRegAddrBase = CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset;
        uiCmdDataRegAddr = uiCmdDataRegAddrBase;
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsLow();
        }
        HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 1);

        pstSelf->State = QSPI_STATE_BUSY_TX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;

        while (uiTxTotalSize > 0)
        {
            uiTxFifoSurpLev = QSPI_FIFO_DEPTH
                              - (HV_R32(CPU_SYS_2_QSPI_TXFLR + pstSelf->baseOffset) & 0x1f);
            uiTxNumOnce = (uiTxTotalSize > uiTxFifoSurpLev)? uiTxFifoSurpLev : uiTxTotalSize;
            for (uiLoop = 0; uiLoop < uiTxNumOnce; uiLoop++)
            {
                if (cmdAddrSize == 0)
                {

                    pucTxData = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pucTxData);
                }
                else
                {
                    pCmdAddr = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pCmdAddr);
                    cmdAddrSize--;
                }
                uiCmdDataRegAddr = uiCmdDataRegAddr + 4;
                uiTxTotalSize--;
                if (uiCmdDataRegAddr == uiCmdDataRegAddrBase + QSPI_DR_ADDR_RANGE_TOTAL)
                {

                    uiCmdDataRegAddr = uiCmdDataRegAddrBase;
                }

            }
        }
        if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_TFE, HV_SET, uiTimeout) != HV_SUCCESS)
        {
            Hv_Vos_TaskExitCritical();
            return HV_TIMEOUT;
        }
        if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
        {
            Hv_Vos_TaskExitCritical();
            return HV_TIMEOUT;
        }
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsHigh();
        }

        pstSelf->State = QSPI_STATE_READY;
        Hv_Vos_TaskExitCritical();
        return HV_SUCCESS;
    }
    else
    {
        Hv_Vos_TaskExitCritical();
        return HV_FAILURE;
    }
}

/** Write data to qspi flash in blocking mode.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param pCmdSize command length
 *  @param pTxData pointer to data buffer to be transmited
 *  @param size amount of data to be sent
 *  @param timeout timeout duration
 *  @retval result
 */
Status Hv_Cal_Qspi_MutiIoPollingWrite(QspiSelf *pstSelf, UCHAR8* pCmdAddr, USHORT16 cmdAddrSize,
                                      UCHAR8* pucTxData, USHORT16 dataSize, UINT32 uiTimeout)
{
    return Hv_Cal_Qspi_PollingWrite(pstSelf, pCmdAddr, cmdAddrSize, pucTxData, dataSize, uiTimeout);
}

/** Read data from qspi flash in blocking mode.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param cmdAddrSize command length
 *  @param pRxData pointer to data buffer
 *  @param dataSize amount of data to be sent
 *  @param dSize the dummy size of receive
 *  @param timeout timeout duration
 *  @retval result
 */
Status Hv_Cal_Qspi_PollingRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                               UCHAR8 *pRxData, USHORT16 dataSize, UINT32 uiTimeout)
{
    UCHAR8  aucDummy[4]     = {0};
    UINT32 uiTxTotalSize    = 0;
    UINT32 uiRxTotalSize    = 0;
    UINT32 uiDummySize      = 0;
    UINT32 uiLoop           = 0;
    UINT32 uiTxFifoSurpLev  = 0;
    UINT32 uiRxFifoLev      = 0;
    UINT32 uiTxNumOnce      = 0;
    UINT32 uiCmdDataRegAddrBase = 0;
    UINT32 uiCmdDataRegAddr = 0;
    UINT64 tickstart = Hv_Vos_GetTick();

    Hv_Vos_TaskEnterCritical();

    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
        Qspi_SetCsLow();
    }

    /* Enable qspi. */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 1);

    if (pstSelf->State == QSPI_STATE_READY)
    {

        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_RX))
        {
            Hv_Vos_TaskExitCritical();
            return HV_FAILURE;
        }

        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
        {
            cmdAddrSize = cmdAddrSize/2;
            dataSize = dataSize/2;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            cmdAddrSize = cmdAddrSize/4;
            dataSize = dataSize/4;
        }
        if (dataSize != 0)
        {
            uiDummySize = cmdAddrSize;
        }
        uiTxTotalSize = cmdAddrSize + dataSize;
        uiRxTotalSize = cmdAddrSize + dataSize;
        uiCmdDataRegAddrBase = CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset;
        uiCmdDataRegAddr = uiCmdDataRegAddrBase;
        HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);
        pstSelf->State = QSPI_STATE_BUSY_TXRX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;

        while (uiTxTotalSize > 0)
        {
            uiTxFifoSurpLev = QSPI_FIFO_DEPTH
                              - (HV_R32(CPU_SYS_2_QSPI_TXFLR + pstSelf->baseOffset) & 0x1f);
            uiTxNumOnce = (uiTxTotalSize > uiTxFifoSurpLev)? uiTxFifoSurpLev : uiTxTotalSize;
            for (uiLoop = 0; uiLoop < uiTxNumOnce; uiLoop++)
            {
                if (cmdAddrSize == 0)
                {
                    Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, aucDummy);
                }
                else
                {
                    pCmdAddr = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pCmdAddr);
                    cmdAddrSize--;
                }
                uiCmdDataRegAddr = uiCmdDataRegAddr + 4;
                uiTxTotalSize--;
                if (uiCmdDataRegAddr == uiCmdDataRegAddrBase + QSPI_DR_ADDR_RANGE_TOTAL)
                {

                    uiCmdDataRegAddr = uiCmdDataRegAddrBase;
                }

            }

            uiRxFifoLev = (HV_R32(CPU_SYS_2_QSPI_RXFLR + pstSelf->baseOffset)) & 0x1f;
            for (uiLoop = 0; uiLoop < uiRxFifoLev; uiLoop++)
            {
                if (uiDummySize == 0)
                {
                    pRxData = Qspi_ReceiveOne(pstSelf, pRxData);
                }
                else
                {
                    Qspi_ReceiveOne(pstSelf, aucDummy);
                    uiDummySize--;
                }
                uiRxTotalSize--;
            }
        }

        while ( uiRxTotalSize > 0)
        {
            uiRxFifoLev = (HV_R32(CPU_SYS_2_QSPI_RXFLR + pstSelf->baseOffset)) & 0x1f;
            for (uiLoop = 0; uiLoop < uiRxFifoLev; uiLoop++)
            {
                if (uiDummySize > 0)
                {
                    Qspi_ReceiveOne(pstSelf, aucDummy);
                    uiDummySize--;
                }
                else
                {
                    pRxData = Qspi_ReceiveOne(pstSelf, pRxData);
                }
                uiRxTotalSize--;
            }
            if ((Hv_Vos_GetTick() - tickstart ) > uiTimeout)
            {
                HV_LOGI("qspi read uiTimeout\n");
                Hv_Vos_TaskExitCritical();
                return HV_TIMEOUT;
            }
        }


        if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
        {
            Hv_Vos_TaskExitCritical();
            return HV_TIMEOUT;
        }
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsHigh();
        }
        pstSelf->State = QSPI_STATE_READY;
        Hv_Vos_TaskExitCritical();
        return HV_SUCCESS;
    }
    else
    {
        Hv_Vos_TaskExitCritical();
        return HV_FAILURE;
    }
}

/** Read data from qspi flash in blocking mode.
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param cmdAddrSize command length
 *  @param pRxData pointer to data buffer
 *  @param dataSize amount of data to be sent
 *  @param timeout timeout duration
 *  @retval result
 */
Status Hv_Cal_Qspi_MultiIoPollingRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                                      UCHAR8 *pRxData, USHORT16 dataSize, UINT32 uiTimeout)
{

    UINT32 uiRxTotalSize        = 0;
    UINT32 uiCmdDataRegAddrBase = 0;
    UINT32 uiCmdDataRegAddr     = 0;
    UINT32 uiLoop               = 0;
    UINT32 uiRxFifoLev          = 0;

    HV_UNUSED(uiTimeout);

    Hv_Vos_TaskEnterCritical();

    if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
    {
        Qspi_SetCsLow();
    }

    /* Enable qspi. */
    HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 1);
    if (pstSelf->State == QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_RX))
        {
            Hv_Vos_TaskExitCritical();
            return HV_FAILURE;
        }

        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
        {
            cmdAddrSize = cmdAddrSize/2;
            dataSize = dataSize/2;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            cmdAddrSize = cmdAddrSize/4;
            dataSize = dataSize/4;
        }
        uiRxTotalSize = dataSize;
        uiCmdDataRegAddrBase = CPU_SYS_2_QSPI_DR0 + pstSelf->baseOffset;
        uiCmdDataRegAddr = uiCmdDataRegAddrBase;
        HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);
        pstSelf->State = QSPI_STATE_BUSY_TXRX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;

        for (uiLoop = 0;uiLoop < cmdAddrSize; uiLoop++)
        {
            pCmdAddr = Qspi_TransmitOne(pstSelf, uiCmdDataRegAddr, pCmdAddr);
            uiCmdDataRegAddr = uiCmdDataRegAddr + 4;

        }

        while (uiRxTotalSize > 0)
        {
            uiRxFifoLev = (HV_R32(CPU_SYS_2_QSPI_RXFLR + pstSelf->baseOffset)) & 0x1f;

            for (uiLoop = 0; uiLoop < uiRxFifoLev; uiLoop++)
            {
                pRxData = Qspi_ReceiveOne(pstSelf, pRxData);
                uiRxTotalSize--;
            }

        }
        if (Qspi_WaitOnFlagUntilTimeout(pstSelf, QSPI_FLAG_BUSY, HV_RESET, QSPI_TIMEOUT) != HV_SUCCESS)
        {
            Hv_Vos_TaskExitCritical();
            return HV_TIMEOUT;
        }
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsHigh();
        }
        pstSelf->State = QSPI_STATE_READY;
        Hv_Vos_TaskExitCritical();
        return HV_SUCCESS;
    }
    else
    {
        Hv_Vos_TaskExitCritical();
        return HV_FAILURE;
    }
}
#ifdef QSPI_USE_INT_DMA
/** Write data to qspi flash with interrupt
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param cmdAddrSize command length
 *  @param pTxData pointer to data buffer to be transmited
 *  @param size amount of data to be sent
 *  @retval result
 */
Status Hv_Cal_Qspi_IntWrite(QspiSelf *pstSelf, UCHAR8* pCmdAddr, USHORT16 cmdAddrSize,
                            UCHAR8* pucTxData, USHORT16 dataSize)
{
    UINT32 uiIntFlag = 0;
    if (pstSelf->State == QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_TX))
        {
            return HV_FAILURE;
        }

        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
        {
            pstSelf->cmdAddrSize = cmdAddrSize;
            pstSelf->txferSize = dataSize;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
        {
            pstSelf->cmdAddrSize = cmdAddrSize/2;
            pstSelf->txferSize = dataSize/2;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            pstSelf->cmdAddrSize = cmdAddrSize/4;
            pstSelf->txferSize = dataSize/4;
        }

        pstSelf->pCmdAddr = pCmdAddr;
        pstSelf->pTxBuff = pucTxData;
        pstSelf->txferCount = 0;

        HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);
        uiIntFlag = QSPI_FLAG_TXEIR | QSPI_FLAG_TXOIR;
        Qspi_IntSelectEn(pstSelf, uiIntFlag);

        pstSelf->State = QSPI_STATE_BUSY_TX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;
        pstSelf->TransCompleteFlag = QSPI_TRANS_IN_PROCESS;
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsLow();
        }

        /* Enable qspi. */
        HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 1);
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Read data from qspi flash with interrupt
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param cmdAddrSize command length
 *  @param pRxData pointer to data buffer
 *  @param dataSize amount of data to be sent
 *  @param dSize the dummy size of receive
 *  @retval result
 */
Status Hv_Cal_Qspi_IntRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                           UCHAR8 *pRxData, USHORT16 dataSize)
{

    UINT32 uiIntFlag = 0;

    if (pstSelf->State == QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_RX))
        {
            return HV_FAILURE;
        }

        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
        {
            pstSelf->cmdAddrSize = cmdAddrSize;
            pstSelf->rxferSize = dataSize;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_16)
        {
            pstSelf->cmdAddrSize = cmdAddrSize/2;
            pstSelf->rxferSize = dataSize/2;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            pstSelf->cmdAddrSize = cmdAddrSize/4;
            pstSelf->rxferSize = dataSize/4;
        }

        pstSelf->pCmdAddr = pCmdAddr;
        pstSelf->pRxBuff = pRxData;
        pstSelf->rxferCount = 0;

        if (pstSelf->InitParam.RateMode == QSPI_STANDARD)
        {
            pstSelf->txferSize = pstSelf->rxferSize;
            pstSelf->usDumSize = pstSelf->cmdAddrSize;
        }
        else {
            pstSelf->txferSize = 0;
            pstSelf->usDumSize = 0;
        }
        HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);
        uiIntFlag = QSPI_FLAG_TXEIR | QSPI_FLAG_TXOIR | QSPI_FLAG_RXUIR | QSPI_FLAG_RXOIR | QSPI_FLAG_RXFIR;
        Qspi_IntSelectEn(pstSelf, uiIntFlag);

        pstSelf->State = QSPI_STATE_BUSY_TXRX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;
        pstSelf->TransCompleteFlag = QSPI_TRANS_IN_PROCESS;
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsLow();
        }

        /* Enable qspi. */
        HV_W32_FIELD_EX(CPU_SYS_2_QSPI_SSIENR, pstSelf->baseOffset, SSIC_EN, 1);
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Write data to qspi flash with DMA
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pTxData pointer to data buffer to be transmited
 *  @param size amount of data to be sent
 *  @retval result
 */
Status Hv_Cal_Qspi_DmaWrite(QspiSelf *pstSelf, UCHAR8* pucTxData, USHORT16 dataSize)
{
    static BOOL bFirstFlag  = HV_TRUE;
    USHORT16 usBlockSize    = 0;

    if (pstSelf->State == QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_TX))
        {
            return HV_FAILURE;
        }
        if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
        {
            usBlockSize = dataSize/4;
        }
        else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
        {
            usBlockSize = dataSize/4;
        }
        HV_R32(CPU_SYS_2_QSPI_ICR + pstSelf->baseOffset);

        if (bFirstFlag == HV_TRUE)
        {
            Qspi_ConfigDmaTxReg(pstSelf, QSPI_DMATX_ENABLE, QSPI_DMA_TX_THRESHOLD);
            bFirstFlag = HV_FALSE;
        }
        /* Enable qspi. */
        HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0x1);
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsLow();
        }
        Hv_Cal_Dma_XferCallbackBond(pstSelf->hdmaTx, Qspi_DmaTransmitCpltCallback);
        Hv_Cal_Dma_SetBlockSize(pstSelf->hdmaTx, usBlockSize);
        Hv_Cal_Dma_SetSrcAddr(pstSelf->hdmaTx, (UINT32)pucTxData);
        Hv_Cal_Dma_ChannelEn(pstSelf->hdmaTx);
        pstSelf->State = QSPI_STATE_BUSY_TX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;
        pstSelf->TransCompleteFlag = QSPI_TRANS_IN_PROCESS;

        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Read data from qspi flash with DMA
 *  @param self Pointer to a QSPI structure that contains the configuration information.
 *  @param pCmdAddr pointer to command data buffer
 *  @param cmdAddrSize command length
 *  @param pRxData pointer to data buffer
 *  @param dataSize amount of data to be sent
 *  @retval result
 */
Status Hv_Cal_Qspi_DmaRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                           UCHAR8* pRxData, USHORT16 dataSize)
{
    static BOOL bFirstFlag = HV_TRUE;
    UINT32 uiTxTotal    = 0;
    UINT32 uiRxTotal    = 0;
    UINT32 uiCmdVal     = 0;
    UINT32 uiAddrVal    = 0;


    if (pstSelf->State == QSPI_STATE_READY)
    {
        if ( (pstSelf->InitParam.Direction != QSPI_DIRECTION_TXRX)
              && (pstSelf->InitParam.Direction != QSPI_DIRECTION_RX))
        {
            return HV_FAILURE;
        }
        if (pstSelf->InitParam.RateMode == QSPI_STANDARD)
        {
            if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
            {
                uiTxTotal = (cmdAddrSize + dataSize)/4;
                uiRxTotal = cmdAddrSize + dataSize;
            }
            else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
            {
                uiTxTotal = (cmdAddrSize + dataSize)/4;
                uiRxTotal = (cmdAddrSize + dataSize)/4;
            }
        }
        else
        {
            uiCmdVal = (UINT32)pCmdAddr[0];
            if (cmdAddrSize == 4)
            {
                uiAddrVal = (UINT32)((UINT32)pCmdAddr[1]<<16)
                            | ((UINT32)pCmdAddr[2]<<8) | ((UINT32)pCmdAddr[3]);
            }
            else if (cmdAddrSize == 5)
            {
                uiAddrVal = (UINT32)((UINT32)pCmdAddr[1]<<24) | ((UINT32)pCmdAddr[2]<<16)
                            | ((UINT32)pCmdAddr[3]<<8) | ((UINT32)pCmdAddr[4]);
            }
            if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_8)
            {
                uiRxTotal = dataSize;
            }
            else if (pstSelf->InitParam.DataSize == QSPI_BITWIDTH_32)
            {
                uiRxTotal = dataSize/4;
            }
        }
        if (pstSelf->InitParam.CsSel == QSPI_CS_BY_GPIO)
        {
            Qspi_SetCsLow();
        }
        Hv_Cal_Dma_XferCallbackBond(pstSelf->hdmaRx, Qspi_DmaReceiveCpltCallback);
        Hv_Cal_Dma_SetBlockSize(pstSelf->hdmaRx, uiRxTotal);
        Hv_Cal_Dma_SetDstAddr(pstSelf->hdmaRx, (UINT32)pRxData);

        Hv_Cal_Dma_ChannelEn(pstSelf->hdmaRx);

        if (bFirstFlag == HV_TRUE)
        {
            Qspi_ConfigDmaTxReg(pstSelf, QSPI_DMATX_ENABLE, QSPI_DMA_TX_THRESHOLD);
            Qspi_ConfigDmaRxReg(pstSelf, QSPI_DMARX_ENABLE, QSPI_DMA_RX_THRESHOLD);
            bFirstFlag = HV_FALSE;
        }

        HV_W32(CPU_SYS_2_QSPI_SSIENR + pstSelf->baseOffset, 0x1);
        if (pstSelf->InitParam.RateMode == QSPI_STANDARD)
        {
            Hv_Cal_Dma_XferCallbackBond(pstSelf->hdmaTx, Qspi_DmaTransmitCpltCallback);
            Hv_Cal_Dma_SetBlockSize(pstSelf->hdmaTx, uiTxTotal);
            Hv_Cal_Dma_SetSrcAddr(pstSelf->hdmaTx, (UINT32)pCmdAddr);
            Hv_Cal_Dma_ChannelEn(pstSelf->hdmaTx);
        }
        else
        {
            HV_W32(CPU_SYS_2_QSPI_DR0  + pstSelf->baseOffset, uiCmdVal);
            HV_W32(CPU_SYS_2_QSPI_DR0  + pstSelf->baseOffset, uiAddrVal);
        }
        pstSelf->State = QSPI_STATE_BUSY_TXRX;
        pstSelf->ErrorCode = QSPI_ERROR_NONE;
        pstSelf->TransCompleteFlag = QSPI_TRANS_IN_PROCESS;

        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

BOOL Hv_Cal_Qspi_TransferIsComplete(QspiSelf *pstSelf)
{

    if (pstSelf->TransCompleteFlag == QSPI_TRANS_IN_PROCESS)
    {
        return HV_FALSE;
    }
    else if (pstSelf->TransCompleteFlag == QSPI_TRANS_END)
    {
        return HV_TRUE;
    }
    return HV_FALSE;

}
#else
Status Hv_Cal_Qspi_IntWrite(QspiSelf *pstSelf, UCHAR8* pCmdAddr, USHORT16 cmdAddrSize,
                            UCHAR8* pucTxData, USHORT16 dataSize){return HV_FALSE;}
Status Hv_Cal_Qspi_IntRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                           UCHAR8 *pRxData, USHORT16 dataSize){return HV_FALSE;}
Status Hv_Cal_Qspi_DmaWrite(QspiSelf *pstSelf, UCHAR8* pucTxData, USHORT16 dataSize){return HV_FALSE;}
Status Hv_Cal_Qspi_DmaRead(QspiSelf *pstSelf,  UCHAR8 *pCmdAddr, USHORT16 cmdAddrSize,
                           UCHAR8* pRxData, USHORT16 dataSize){return HV_FALSE;}
BOOL Hv_Cal_Qspi_TransferIsComplete(QspiSelf *pstSelf){return HV_FALSE;};
#endif
VOID Hv_Cal_Qspi_WP_Enable(UCHAR8 ucEnable)
{
    UCHAR8 ucValue = 0;
    HV_R32_DECL_VAR();

    ucValue = HV_R32_FIELD(STANDBY_SH08_TOP_STBY_CTRL_1C, reg_prog_spi_wp_io_ctrl);
    if (ucEnable)
    {
        ucValue |= 0x1;
        HV_W32_FIELD(RX_EDP_RX_MAC_WR_REG_00_50, reg_00_dprx_sw_irqhpd_set, 0x2);
    }
    else
    {
        ucValue &= ~0x1;
        HV_W32_FIELD(RX_EDP_RX_MAC_WR_REG_00_50, reg_00_dprx_sw_irqhpd_set, 0x0);
    }
    HV_W32_FIELD(STANDBY_SH08_TOP_STBY_CTRL_1C, reg_prog_spi_wp_io_ctrl, ucValue);
    HV_LOGV("Hv_Cal_Qspi_WP_Enable %x, value %x\n", ucEnable,ucValue);
    return;
}

VOID Hv_Cal_Qspi_WP_Init(VOID)
{
    HV_LOGI("Hv_Cal_Qspi_WP_Init  \n");
    HV_W32_FIELD(STANDBY_SH08_TOP_STBY_CTRL_0, reg_prog_spi_wp_func_sel, 0x1);

    HV_W32_FIELD(SYS_SH06_TOP_04, reg_rx_dp0_rst_n , 0x1);
    HV_W32_FIELD(RX_HSRX_TOP_00, reg_dprx0_apb_clk_en , 0x1);
    HV_W32_FIELD(STANDBY_SH08_TOP_STBY_CTRL_0, reg_dp_rx0_hpd_func_sel , 0x0);
    HV_W32_FIELD(STANDBY_SH08_TOP_STBY_CTRL_8, reg_dp_rx0_hpd_io_ctrl  , 0x69);
    HV_W32_FIELD(RX_EDP_RX_MAC_WR_REG_00_24, reg_00_dprx_hpd_setting_rdy  , 0x3);
    HV_W32_FIELD(RX_EDP_RX_MAC_WR_REG_00_50, reg_00_dprx_sw_irqhpd_set, 0x2);
    Hv_Cal_Qspi_WP_Enable(HV_TRUE);
    return;
}
