/**************************************************************************//**
 * @file     hbi.c
 * @version  V3.00
 * @brief    HyperBus Interface (HBI) driver source file
 *
 * @copyright SPDX-License-Identifier: Apache-2.0
 * @copyright Copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include "NuMicro.h"


/** @addtogroup Standard_Driver Standard Driver
  @{
*/

/** @addtogroup HBI_Driver HBI Driver
  @{
*/

#define WAIT_WHILE_RETURN(x, timeout, ret)   while(x){if((timeout)-- <= 0) return (ret);}
#define WAIT_WHILE_BREAK(x, timeout, err)   while(x){if((timeout)-- <= 0){g_HBI_i32ErrCode = (err); break;}}

int32_t g_HBI_i32ErrCode = 0;       /*!< HBI global error code */

/** @addtogroup HBI_EXPORTED_FUNCTIONS HBI Exported Functions
  @{
*/


/**
  * @brief      Reset HBI Device
  * @return     None
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_Reset(void)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    HBI->CMD = HBI_CMD_RESET_HRAM;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}

/**
  * @brief      Exit from Hybrid sleep and deep Power down function
  * @return     None
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_ExitHSAndDPD(void)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    HBI->CMD = HBI_CMD_EXIT_HS_PD;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}

/**
  * @brief      Read HyperRAM register space
  * @param[in]  u32Addr  Address of HyperRAM register space
  *                 - \ref HYPERRAM_ID_REG0       : 0x0000_0000 = Identification Register 0
  *                 - \ref HYPERRAM_ID_REG1       : 0x0000_0002 = Identification Register 1
  *                 - \ref HYPERRAM_CONFIG_REG0   : 0x0000_1000 = Configuration Register 0
  *                 - \ref HYPERRAM_CONFIG_REG1   : 0x0000_1002 = Configuration Register 1
  * @return   The data of HyperRAM register.
  * @return   -1  An illeagal register space
  * @return   -2  Wait Hyper RAM time-out
  */
int32_t HBI_ReadHyperRAMReg(uint32_t u32Addr)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    if( (u32Addr == HYPERRAM_ID_REG0) || (u32Addr == HYPERRAM_ID_REG1) || (u32Addr == HYPERRAM_CONFIG_REG0) || (u32Addr == HYPERRAM_CONFIG_REG1) )
    {
        HBI->ADR = u32Addr;
        HBI->CMD = HBI_CMD_READ_HRAM_REGISTER;
        WAIT_WHILE_RETURN(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
        return *((uint16_t *)&HBI->RDATA);
    }
    else
    {
        return -1;
    }
}

/**
  * @brief      Write HyperRAM register space
  * @param[in]  u32Addr  Address of HyperRAM register space
  *                 - \ref HYPERRAM_ID_REG0       : 0x0000_0000 = Identification Register 0
  *                 - \ref HYPERRAM_ID_REG1       : 0x0000_0002 = Identification Register 1
  *                 - \ref HYPERRAM_CONFIG_REG0   : 0x0000_1000 = Configuration Register 0
  *                 - \ref HYPERRAM_CONFIG_REG1   : 0x0000_1002 = Configuration Register 1
  * @param[in]
  * @return   0   success.
  * @return   -1  An illeagal register space
  * @return   -2  Wait Hyper RAM time-out
  */
int32_t HBI_WriteHyperRAMReg(uint32_t u32Addr, uint32_t u32Value)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    if( (u32Addr == HYPERRAM_ID_REG0) || (u32Addr == HYPERRAM_ID_REG1) || (u32Addr == HYPERRAM_CONFIG_REG0) || (u32Addr == HYPERRAM_CONFIG_REG1) )
    {
        HBI->ADR = u32Addr;
        HBI->WDATA = u32Value;
        HBI->CMD = HBI_CMD_WRITE_HRAM_REGISTER;
        WAIT_WHILE_RETURN(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
        return 0;
    }
    else
    {
        return -1;
    }
}

/**
  * @brief      Read 2 Bytes from HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @return     The 16 bit data of HyperRAM space.
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
uint32_t HBI_Read2Byte(uint32_t u32Addr)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    if(u32Addr & 0x1)
    {
        g_HBI_i32ErrCode = HBI_ERR_ALIGN;
    }

    HBI->ADR = u32Addr;
    HBI->CMD = HBI_CMD_READ_HRAM_2_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
    return *((uint16_t *)&HBI->RDATA);
}

/**
  * @brief      Read 4 bytes from HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @return     The 32bit data of HyperRAM space.
  */
uint32_t HBI_Read4Byte(uint32_t u32Addr)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    if(u32Addr & 0x3)
    {
        g_HBI_i32ErrCode = HBI_ERR_ALIGN;
    }

    HBI->ADR = u32Addr;
    HBI->CMD = HBI_CMD_READ_HRAM_4_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
    return HBI->RDATA;
}

/**
  * @brief      Write 1 byte to HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @param[in]  u8Data   8 bits data to be written to HyperRAM space
  * @return     None.
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_Write1Byte(uint32_t u32Addr, uint8_t u8Data)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    HBI->ADR = u32Addr;
    HBI->WDATA = u8Data;
    HBI->CMD = HBI_CMD_WRITE_HRAM_1_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}

/**
  * @brief      Write 2 bytes to HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @param[in]  u16Data  16 bits data to be written to HyperRAM space
  * @return     None.
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_Write2Byte(uint32_t u32Addr, uint16_t u16Data)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    if(u32Addr & 0x1)
    {
        g_HBI_i32ErrCode = HBI_ERR_ALIGN;
    }

    HBI->ADR = u32Addr;
    HBI->WDATA = u16Data;
    HBI->CMD = HBI_CMD_WRITE_HRAM_2_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}

/**
  * @brief      Write 3 bytes to HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @param[in]  u32Data  24 bits data to be written to HyperRAM space
  * @return     None.
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_Write3Byte(uint32_t u32Addr, uint32_t u32Data)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;

    HBI->ADR = u32Addr;
    HBI->WDATA = u32Data;
    HBI->CMD = HBI_CMD_WRITE_HRAM_3_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}

/**
  * @brief      Write 4 byte to HyperRAM space
  * @param[in]  u32Addr  Address of HyperRAM space
  * @param[in]  u32Data  32 bits data to be written to HyperRAM space
  * @return     None.
  * @note       This function sets g_HBI_i32ErrCode to HBI_ERR_TIMEOUT if waiting Hyper RAM time-out.
  */
void HBI_Write4Byte(uint32_t u32Addr, uint32_t u32Data)
{
    int32_t i32TimeOutCnt = HBI_TIMEOUT;
    if(u32Addr & 0x3)
    {
        g_HBI_i32ErrCode = HBI_ERR_ALIGN;
    }

    HBI->ADR = u32Addr;
    HBI->WDATA = u32Data;
    HBI->CMD = HBI_CMD_WRITE_HRAM_4_BYTE;
    WAIT_WHILE_BREAK(HBI->CMD != HBI_CMD_HRAM_IDLE, i32TimeOutCnt, HBI_ERR_TIMEOUT);
}


/*@}*/ /* end of group HBI_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group HBI_Driver */

/*@}*/ /* end of group Standard_Driver */
