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

#include "hv_vos_Comm.h"

#include "hv_cal_Dma.h"
#include "hv_chip_Config.h"
#include "hv_comm_Define.h"
#include "hv_drv_Uart.h"
#include "hv_comm_Assert.h"

#define UART_LCR_MASK                          0xE0
#define UART_PARITY_MASK                       0x03
#define UART_PARITY_EVEN_MASK                  0x18
#define UART_PARITY_ODD_MASK                   0x08
#define UART_PARITY_NONE_MASK                  0x00

#define UART_DATALEN_MASK                      0x03

#define UART_STOP_POS                          2U
#define UART_STOP_MASK                         (0x01UL << UART_STOP_POS)

/*!< Received Data Available */
#define UART_RECV_INT_EN                       0x01
/*!< Transmit Holding Register Empty Interrupt */
#define UART_TRANEMPTY_INT_EN                  0x02
/*!< Receiver Line Status Interrupt */
#define UART_RECVSTATUS_INT_EN                 0x04
/*!< Modem Status */
#define UART_MODEMSTATUS_INT_EN                0x08

#define UART_FIFO_MASK                         0x06

/* Interrupt Type */
#define UART_INT_MODEM_STATUS                  0x00
#define UART_INT_NO_INTERRUPT_PENDING          0x01
#define UART_INT_THR_EMPTY                     0x02
#define UART_INT_RECEIVED_DATA_AVAILABLE       0x04
#define UART_INT_RECEIVER_LINE_STATUS          0x06
#define UART_INT_BUSY_DETECT                   0x07
#define UART_INT_CHARACTER_TIMEOUT             0x0c

struct _UartSelf
{
    UartInitParam               stInitParam;
    UINT32                      uiBaseOffset;
    /*!< Pointer to UART Tx transfer Buffer */
    const UCHAR8                *pucTxBuffPtr;
    /*!< UART Tx Transfer size              */
    USHORT16                    usTxXferSize;
    /*!< UART Tx Transfer Counter           */
    USHORT16                    usTxXferCount;
    /*!< Pointer to UART Rx transfer Buffer */
    UCHAR8                      *pucRxBuffPtr;
    /*!< UART Rx Transfer size              */
    USHORT16                    usRxXferSize;
    /*!< UART Rx Transfer Counter           */
    USHORT16                    usRxXferCount;
    /*!< UART Tx DMA Handle parameters      */
    DmaSelf               *pstHdmaTx;
    /*!< UART Rx DMA Handle parameters      */
    DmaSelf               *pstHdmaRx;
    /*!< UART communication state           */
    UartStateType               enState;
    /*!< UART Error code                    */
    UINT32                      errorCode;

    /*!< UART Binary Semaphore              */
    HV_VOS_SEMAPHORE_S*         pstUartBinarySema;

};

static UartSelf s_astUart[UART_PORT_MAX];

#define UART_BUFF_SIZE 32
static UCHAR8 g_aucUartRxBuf[UART_BUFF_SIZE] = {0};
static UCHAR8 g_ucUartBufIndex = 0;

static Status Uart_IntReceive(UartSelf *pstSelf)
{
    UINT32 uiValue = 0;
    rs_cpu_sys_uart0_dlh_ier ier = {0};
    uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
    uiValue = cpu_sys_uart0_lsr(uiValue)->reg_DR;
    while (uiValue & 0x1)
    {
        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL);
        if (pstSelf->stInitParam.bNewInterface == HV_TRUE)
        {
            g_aucUartRxBuf[(g_ucUartBufIndex++)%UART_BUFF_SIZE] = uiValue;
        }
        else
        {
            *pstSelf->pucRxBuffPtr++ = (UCHAR8)uiValue;
            if (--pstSelf->usRxXferCount == 0)
            {
                uiValue =  HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
                /* Disable Received Data Available Interrupt */
                ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER & ~UART_RECV_INT_EN;
                HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
                if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
                {
                    pstSelf->enState = UART_STATE_BUSY_TX;
                }
                else
                {
                    pstSelf->enState = UART_STATE_READY;
                }

                if (pstSelf->stInitParam.fCallback)
                {
                    pstSelf->stInitParam.fCallback(UART_INT_RX_COMPLETE, pstSelf->stInitParam.pCallbackArg, 0);
                }
                return HV_SUCCESS;
            }
        }
        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
        uiValue = cpu_sys_uart0_lsr(uiValue)->reg_DR;
    }
    if (pstSelf->stInitParam.bNewInterface == HV_TRUE)
    {
        pstSelf->stInitParam.fCallback(UART_INT_RX_COMPLETE, g_aucUartRxBuf, g_ucUartBufIndex);
        g_ucUartBufIndex = 0;
    }
    return HV_SUCCESS;
}

static Status Uart_IntTransmit(UartSelf *pstSelf)
{
    UINT32 uiValue = 0;
    rs_cpu_sys_uart0_dlh_ier ier = {0};

    if (pstSelf->usTxXferCount == 0)
    {
        uiValue =  HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
        /* Disable Transmit Holding Register Empty Interrupt */
        ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER & ~UART_TRANEMPTY_INT_EN;
        HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }

        if (pstSelf->stInitParam.fCallback)
        {
            pstSelf->stInitParam.fCallback(UART_INT_TX_COMPLETE, pstSelf->stInitParam.pCallbackArg, 0);
        }
        return HV_SUCCESS;
    }

    do
    {
        HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL, *pstSelf->pucTxBuffPtr);
        pstSelf->pucTxBuffPtr += 1;
        if (--pstSelf->usTxXferCount == 0)
        {
            return HV_SUCCESS;
        }
        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_USR);
        uiValue = cpu_sys_uart0_usr(uiValue)->reg_TFNF;
    }
    while (pstSelf->stInitParam.bFifoEnable && (uiValue & 0x1));
    return HV_SUCCESS;
}

static Status Uart_DmaRxCpltCallback(DmaSelf* pstArg)
{
    UartSelf *pstSelf = (UartSelf *)Hv_Cal_Dma_GetParent(pstArg);

    if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
    {
        pstSelf->enState = UART_STATE_BUSY_TX;
    }
    else
    {
        pstSelf->enState = UART_STATE_READY;
    }

    if (pstSelf->stInitParam.fCallback)
    {
        pstSelf->stInitParam.fCallback(UART_DMA_RX_COMPLETE, pstSelf->stInitParam.pCallbackArg, 0);
    }
    return HV_SUCCESS;
}


static Status Uart_DmaTxCpltCallback(DmaSelf* pstArg)
{
    UartSelf *pstSelf = (UartSelf *)Hv_Cal_Dma_GetParent(pstArg);

    if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
    {
        pstSelf->enState = UART_STATE_BUSY_RX;
    }
    else
    {
        pstSelf->enState = UART_STATE_READY;
    }

    if (pstSelf->stInitParam.fCallback)
    {
        pstSelf->stInitParam.fCallback(UART_DMA_TX_COMPLETE, pstSelf->stInitParam.pCallbackArg, 0);
    }
    return HV_SUCCESS;
}

static HV_VOS_ISR_RESULT_E Uart_IrqHandler(UINT32 uiIrqNum, void *pArg)
{
    UartSelf *pstSelf = (UartSelf *)pArg;
    UINT32 uiValue = 0;
    HV_UNUSED(uiIrqNum);

    uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_FCR);

    switch (uiValue & 0xf)
    {
        case UART_INT_MODEM_STATUS:
        {
            break;
        }
        case UART_INT_NO_INTERRUPT_PENDING:
        {
            break;
        }
        case UART_INT_THR_EMPTY:
        {
            Uart_IntTransmit(pstSelf);
            break;
        }
        case UART_INT_RECEIVED_DATA_AVAILABLE:
        {
            Uart_IntReceive(pstSelf);
            Hv_Vos_ReleaseSemaphoreFromISR(pstSelf->pstUartBinarySema);
            break;
        }
        case UART_INT_RECEIVER_LINE_STATUS:
        {
            pstSelf->errorCode = UART_ERROR_NONE;
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            if (cpu_sys_uart0_lsr(uiValue)->reg_BI & 0x1)
            {
                pstSelf->errorCode |= UART_ERROR_BI;
            }
            if (cpu_sys_uart0_lsr(uiValue)->reg_FE & 0x1)
            {
                pstSelf->errorCode |= UART_ERROR_FE;
            }
            if (cpu_sys_uart0_lsr(uiValue)->reg_PE & 0x1)
            {
                pstSelf->errorCode |= UART_ERROR_PE;
            }
            if (cpu_sys_uart0_lsr(uiValue)->reg_OE & 0x1)
            {
                pstSelf->errorCode |= UART_ERROR_ORE;
            }
            if (cpu_sys_uart0_lsr(uiValue)->reg_RFE & 0x1)
            {
                pstSelf->errorCode |= UART_ERROR_RFE;
            }
            //HV_LOGI("error errorCode = 0x%x\n", pstSelf->errorCode);
            break;
        }
        case UART_INT_BUSY_DETECT:
        {
            //HV_LOGI("busy detect\n");
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_USR);
            HV_UNUSED(uiValue);
            break;
        }
        case UART_INT_CHARACTER_TIMEOUT:
        {
            //HV_LOGI("character timeout\n");
            break;
        }
        default:
        {
            break;
        }
    }
    return HV_VOS_ISR_HANDLED;
}

void Hv_Drv_Uart_SetDmaTx(UartSelf *pstSelf, DmaSelf *pstHdmaTx)
{
    pstSelf->pstHdmaTx = pstHdmaTx;
    Hv_Cal_Dma_SetParent(pstHdmaTx,(void*)pstSelf);
    Hv_Cal_Dma_SetDstAddr(pstHdmaTx,CPU_SYS_UART0_THR_DLL + pstSelf->uiBaseOffset);
    return;
}

void Hv_Drv_Uart_SetDmaRx(UartSelf *pstSelf, DmaSelf *pstHdmaRx)
{
    pstSelf->pstHdmaRx = pstHdmaRx;
    Hv_Cal_Dma_SetParent(pstHdmaRx,(void*)pstSelf);
    Hv_Cal_Dma_SetSrcAddr(pstHdmaRx,CPU_SYS_UART0_THR_DLL + pstSelf->uiBaseOffset);
    return;
}

/** Initialize uart
 *  @param initParam pointer to uart configure parameters
 *  @return uart structure pointer
 */
UartSelf *Hv_Drv_Uart_Init(UartInitParam *pstInitParam)
{
    rs_cpu_sys_uart0_fcr fcr = {0};
    rs_cpu_sys_uart0_lcr_ext lcr_ext = {0};
    UartSelf *pstSelf;
    UINT32 uiIrqNum = 0;

    HV_ASSERT_TRUE_RET(pstInitParam != NULL,NULL);
    HV_ASSERT_PEEK_TRUE(pstInitParam->enPort < UART_PORT_MAX);
    pstSelf = &s_astUart[pstInitParam->enPort];
    HV_MEMCPY(&s_astUart[pstInitParam->enPort], pstInitParam, sizeof(UartInitParam));

    if (pstInitParam->enPort == UART_PORT_0)
    {
        pstSelf->uiBaseOffset = 0;
        uiIrqNum = HV_CHIP_IRQ_UART0;
    }
    else if (pstInitParam->enPort == UART_PORT_1)
    {
        pstSelf->uiBaseOffset = HV_UART1_BASE_ADDRESS - HV_UART0_BASE_ADDRESS;
        uiIrqNum = HV_CHIP_IRQ_UART1;
    }
    else
    {
    }

    if (pstInitParam->bFifoEnable)
    {
        fcr.reg_FIFOE = 1;
        fcr.reg_RFIFOR = 1;
        fcr.reg_XFIFOR = 1;
        fcr.TET = pstInitParam->enTxFiFoTrig;
        fcr.RT = pstInitParam->enRxFiFoTrig;
    }
    if (pstInitParam->b9BitMode)
    {
        lcr_ext.reg_DLS_E       = 1;
        lcr_ext.reg_ADDR_MATCH  = 0;
        lcr_ext.reg_SEND_ADDR   = 0;
        lcr_ext.reg_TRANSMIT_MODE   = 1;
        HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_LCR_EXT, lcr_ext);
    }
    if (pstInitParam->bNewInterface)
    {
        if (pstInitParam->fCallback == NULL)
        {
            HV_LOGE("no callback regisstered\n");
        }
    }

    HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_FCR, fcr);
    Hv_Drv_Uart_SetBaudrate(pstSelf, pstInitParam->uiBaudrate);
    Hv_Drv_Uart_SetFormat(pstSelf, pstInitParam->enParity, pstInitParam->enStopbit, pstInitParam->enWordlen);

    HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, UART_RECVSTATUS_INT_EN |  UART_MODEMSTATUS_INT_EN);
    Hv_Vos_AttachIsr(uiIrqNum, Uart_IrqHandler, pstSelf);
    //Hv_Vos_SetIrqPrio(uiIrqNum,pstInitParam->uiPriority);
    Hv_Vos_UnmaskIrq(uiIrqNum);
    Hv_Vos_ConfigIrq(uiIrqNum, HV_TRUE, 0);

    pstSelf->errorCode = UART_ERROR_NONE;
    pstSelf->enState = UART_STATE_READY;
    pstSelf->pstUartBinarySema = Hv_Vos_InitSemaphore(1, 0);

    return pstSelf;
}

/** brief De-initialize uart
 *  @param self uart pointer
 */
void Hv_Drv_Uart_Cleanup(UartSelf *pstSelf)
{
    HV_ASSERT(pstSelf->stInitParam.enPort < UART_PORT_MAX);

    Hv_Drv_Uart_CleanFifo(pstSelf);
    if (pstSelf->stInitParam.enPort == UART_PORT_0)
    {
        Hv_Vos_ConfigIrq(HV_CHIP_IRQ_UART0, HV_FALSE, 0);
        Hv_Vos_MaskIrq(HV_CHIP_IRQ_UART0);
        Hv_Vos_DetachIsr(HV_CHIP_IRQ_UART0, Uart_IrqHandler);
    }
    else if (pstSelf->stInitParam.enPort == UART_PORT_1)
    {
        Hv_Vos_ConfigIrq(HV_CHIP_IRQ_UART1, HV_FALSE, 0);
        Hv_Vos_MaskIrq(HV_CHIP_IRQ_UART1);
        Hv_Vos_DetachIsr(HV_CHIP_IRQ_UART1, Uart_IrqHandler);
    }
    else
    {
    }
    pstSelf->stInitParam.fCallback = NULL;
    pstSelf->stInitParam.pCallbackArg = NULL;
    pstSelf->enState = UART_STATE_RESET;

    return;
}

/** brief De-initialize uart
 *  @param self uart pointer
 */
void Hv_Drv_Uart_CleanFifo(UartSelf *pstSelf)
{
    UINT32 uiValue = 0;

    uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_FCR);
    HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_FCR, uiValue | UART_FIFO_MASK);
    return;
}

/** Detection puiBaudrate of uart
 *  @param enPort uart enPort
 *  @param puiBaudrate detection puiBaudrate value
 *  @return result
 */
Status Hv_Drv_Uart_BaudrateDetection(UartPortIndex enPort, UINT32 *puiBaudrate)
{
    UINT32 uiValue      = 0;
    UINT32 uiBaseOffset = 0;

    HV_ASSERT(enPort < UART_PORT_MAX);
    if (enPort == UART_PORT_0)
    {
        uiBaseOffset = 0;
    }
    else if (enPort == UART_PORT_1)
    {
        uiBaseOffset = HV_UART1_BASE_ADDRESS - HV_UART0_BASE_ADDRESS;
    }
    else
    {
    }

    /* auto baud rate enable */
    HV_W32_FIELD_EX(CPU_SYS_UART0_UART_CTL0, uiBaseOffset, reg_abr_en, 1);
    while(1)
    {
        uiValue = HV_R32(uiBaseOffset + CPU_SYS_UART0_UART_CTL0);
        if (cpu_sys_uart0_uart_ctl0(uiValue)->reg_abrf == 1
            && cpu_sys_uart0_uart_ctl0(uiValue)->reg_bit_num != 0)
        {
            *puiBaudrate = HV_CHIP_CLK_APB / cpu_sys_uart0_uart_ctl0(uiValue)->reg_bit_num;
            HV_LOGI("get Baudrate = %d\n", *puiBaudrate);
            break;
        }
    }

    /* auto baud rate disable */
    HV_W32_FIELD_EX(CPU_SYS_UART0_UART_CTL0, uiBaseOffset, reg_abr_en, 0);
    return HV_SUCCESS;
}

/** Set baudrate of uart
 *  @param self uart pointer
 *  @param baudrate baudrate
 *  @return result
 */
Status Hv_Drv_Uart_SetBaudrate(UartSelf *pstSelf, UINT32 uiBaudrate)
{
    UINT32 uiDivider = 0;
    FLOAT32 fBrd = 0;

    fBrd = (HV_CHIP_CLK_APB >> 4) / (FLOAT32)uiBaudrate + 1 / 2;
    uiDivider = (UINT32)fBrd;

    /* Enable setup uiBaudrate */
    HV_W32_FIELD_EX(CPU_SYS_UART0_LCR, pstSelf->uiBaseOffset, reg_DLAB, 1);
    HV_W32_FIELD_EX(CPU_SYS_UART0_THR_DLL, pstSelf->uiBaseOffset, reg_THR_DLL, uiDivider & 0xff);
    HV_W32_FIELD_EX(CPU_SYS_UART0_DLH_IER, pstSelf->uiBaseOffset, reg_DLH_IER, (uiDivider >> 8) & 0xff);
    /* Disable setup uiBaudrate */
    HV_W32_FIELD_EX(CPU_SYS_UART0_LCR, pstSelf->uiBaseOffset, reg_DLAB, 0);
    return HV_SUCCESS;
}

/** Set serial format of homebus
 *  @param self uart pointer
 *  @param parity parity
 *  @param stopbit stop bit
 *  @param wordlen word length
 *  @return result
 */
Status Hv_Drv_Uart_SetFormat(UartSelf *pstSelf, UartParity enParity, UartStopBit enStopbit, UartWordLen enWordlen)
{
    UINT32 uiData   = 0;
    UINT32 uiValue  = 0;

    pstSelf->stInitParam.enParity = enParity;
    pstSelf->stInitParam.enStopbit = enStopbit;
    pstSelf->stInitParam.enWordlen = enWordlen;
    uiData = (((pstSelf->stInitParam.enStopbit << UART_STOP_POS) & UART_STOP_MASK) | (pstSelf->stInitParam.enWordlen & UART_DATALEN_MASK));
    uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LCR);
    switch (pstSelf->stInitParam.enParity & UART_PARITY_MASK)
    {
        case UART_PARITY_EVEN:
        {
            uiValue = (uiValue & UART_LCR_MASK) | UART_PARITY_EVEN_MASK | uiData;
            break;
        }
        case UART_PARITY_ODD:
        {
            uiValue = (uiValue & UART_LCR_MASK) | UART_PARITY_ODD_MASK | uiData;
            break;
        }
        default:
        {
            uiValue = (uiValue & UART_LCR_MASK) | UART_PARITY_NONE_MASK | uiData;
            break;
        }
    }
    HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LCR, uiValue);
    return HV_SUCCESS;
}

/** Receives an amount of data in polling mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be received
 *  @param timeout timeout duration ms
 *  @return result
 */
Status Hv_Drv_Uart_Receive(UartSelf *pstSelf, UCHAR8 *pucData, USHORT16 usSize, UINT32 uiTimeout)
{
    UINT32 uiValue      = 0;
    UINT64 ulStartTick    = 0;

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_TX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_TX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }
        pstSelf->usRxXferSize   = usSize;
        pstSelf->usRxXferCount  = usSize;

        ulStartTick = Hv_Vos_GetTick();
        while (pstSelf->usRxXferCount > 0)
        {
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            uiValue = cpu_sys_uart0_lsr(uiValue)->reg_DR;
            if (uiValue == 1)
            {
                pstSelf->usRxXferCount--;
                uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL);
                *pucData++ = (UCHAR8)uiValue;
            }
            else
            {
                if (uiTimeout != 0 && Hv_Vos_GetTick() - ulStartTick >= uiTimeout)
                {
                    if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
                    {
                        pstSelf->enState = UART_STATE_BUSY_TX;
                    }
                    else
                    {
                        pstSelf->enState = UART_STATE_READY;
                    }
                    HV_LOGE("Receive timeout!\n");
                    return HV_FAILURE;
                }
            }
        }
        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** enable uart receive int��
 *  @param ucPort uart port
 *  @return result
 */
Status Hv_Drv_Uart_EnableReceive(UCHAR8 ucPort)
{
    UINT32 uiValue = 0;
    rs_cpu_sys_uart0_dlh_ier ier = {0};
    UartSelf *pstSelf = &s_astUart[ucPort];

    if (pstSelf == NULL)
    {
        HV_LOGE("port not inited\n");
        return HV_FAILURE;
    }

    uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
    /* Enable Received Data Available Interrupt */
    ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER | UART_RECV_INT_EN;
    HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
    return HV_SUCCESS;
}


/** Receives an amount of data in non blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be received
 *  @return result
 */
Status Hv_Drv_Uart_IntReceive(UartSelf *pstSelf, UCHAR8 *pucData, USHORT16 usSize)
{
    UINT32 uiValue = 0;
    rs_cpu_sys_uart0_dlh_ier ier = {0};

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_TX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_TX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }

        pstSelf->pucRxBuffPtr   = pucData;
        pstSelf->usRxXferSize   = usSize;
        pstSelf->usRxXferCount  = usSize;

        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
        /* Enable Received Data Available Interrupt */
        ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER | UART_RECV_INT_EN;
        HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Receives an amount of data in blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be received
 *  @return result
 */
Status Hv_Drv_Uart_BlockReceive(UartSelf *pstSelf, UCHAR8 *pucData, USHORT16 usSize)
{
    while (1)
    {
        /* Due to UART_RECV_INT_EN is set in Hv_Drv_Uart_IntReceive,
           so must run it before P operation.*/
        if (Hv_Drv_Uart_IntReceive(pstSelf, pucData, usSize) == HV_SUCCESS)
        {
            break;
        }
        else
        {
           /* P operation */
           Hv_Vos_AcquireSemaphore(pstSelf->pstUartBinarySema);
        }
    }
    return HV_SUCCESS;
}

/** Receives an amount of data in non blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be received
 *  @return result
 */
Status Hv_Drv_Uart_DmaReceive(UartSelf *pstSelf, UCHAR8 *pucData, USHORT16 usSize)
{

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_TX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_TX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }

        pstSelf->pucRxBuffPtr   = pucData;
        pstSelf->usRxXferSize   = usSize;
        pstSelf->usRxXferCount  = usSize;

        Hv_Cal_Dma_XferCallbackBond(pstSelf->pstHdmaRx, Uart_DmaRxCpltCallback);
        Hv_Cal_Dma_SetBlockSize(pstSelf->pstHdmaRx, usSize);
        Hv_Cal_Dma_SetDstAddr(pstSelf->pstHdmaRx, (UINT32)pucData);
        Hv_Cal_Dma_ChannelEn(pstSelf->pstHdmaRx);

        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Get character by UART
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @return result
 */
Status Hv_Drv_Uart_GetChar(UartSelf *pstSelf, UCHAR8 *pucData)
{
    UINT32 uiValue = 0;

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_TX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_TX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }

        do
        {
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            uiValue = cpu_sys_uart0_lsr(uiValue)->reg_DR;
        }while(uiValue == 0);

        uiValue     = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL);
        *pucData    = (UCHAR8)uiValue;

        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}
/** Output address by UART with 9bit mode
 *  @param self uart pointer
 *  @param data device address
 *  @return result
 */
Status Hv_Drv_Uart_PutAddr(UartSelf *pstSelf, UCHAR8 ucAddr)
{
    UINT32 uiValue = 0;

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        do
        {
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            uiValue = cpu_sys_uart0_lsr(uiValue)->reg_THRE;
        } while(uiValue == 0);

        uiValue = (1 << 8) | ucAddr;
        HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL, uiValue);

        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Output character by UART
 *  @param self uart pointer
 *  @param data outputed character
 *  @return result
 */
Status Hv_Drv_Uart_PutChar(UartSelf *pstSelf, UCHAR8 ucData)
{
    UINT32 uiValue = 0;

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        do
        {
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            uiValue = cpu_sys_uart0_lsr(uiValue)->reg_THRE;
        } while(uiValue == 0);

        HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL, ucData);

        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Sends an amount of data in blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be sent
 *  @param timeout timeout duration ms
 *  @return result
 */
Status Hv_Drv_Uart_Transmit(UartSelf *pstSelf, const UCHAR8 *pucData, USHORT16 usSize, UINT32 uiTimeout)
{
    UINT32 uiValue      = 0;
    UINT64 ulStartTick  = 0;

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        pstSelf->usTxXferSize   = usSize;
        pstSelf->usTxXferCount  = usSize;

        ulStartTick = Hv_Vos_GetTick();
        while (pstSelf->usTxXferCount > 0)
        {
            uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR);
            uiValue = cpu_sys_uart0_lsr(uiValue)->reg_THRE;

            if (uiValue == 1)
            {
                pstSelf->usTxXferCount--;
                HV_W32(pstSelf->uiBaseOffset + CPU_SYS_UART0_THR_DLL, *pucData);
                pucData += 1;
            }
            else
            {
                if (uiTimeout != 0 && Hv_Vos_GetTick() - ulStartTick >= uiTimeout)
                {
                    if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
                    {
                        pstSelf->enState = UART_STATE_BUSY_RX;
                    }
                    else
                    {
                        pstSelf->enState = UART_STATE_READY;
                    }
                    HV_LOGE("Transmit timeout!\n");
                    return HV_FAILURE;
                }
            }
        }

        if (pstSelf->enState == UART_STATE_BUSY_TX_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_READY;
        }
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Sends an amount of data
 *  @param ucPort uart port
 *  @param data pointer to data buffer
 *  @param size amount of data to be sent
 *  @return result
 */
Status Hv_Drv_Uart_Send(UCHAR8 ucPort, const UCHAR8 *pucData, USHORT16 usSize)
{
    UINT32 uiValue = 0;
    UartSelf *pstSelf = &s_astUart[ucPort];

    if (pstSelf == NULL)
    {
        HV_LOGE("port not inited\n");
        return HV_FAILURE;
    }

    rs_cpu_sys_uart0_dlh_ier ier = {0};

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        pstSelf->pucTxBuffPtr   = pucData;
        pstSelf->usTxXferSize   = usSize;
        pstSelf->usTxXferCount  = usSize;

        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
        /* Enable Transmit Holding Register Empty Interrupt */
        ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER | UART_TRANEMPTY_INT_EN;
        HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Sends an amount of data in non blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be sent
 *  @return result
 */
Status Hv_Drv_Uart_IntTransmit(UartSelf *pstSelf, const UCHAR8 *pucData, USHORT16 usSize)
{
    UINT32 uiValue = 0;
    rs_cpu_sys_uart0_dlh_ier ier = {0};

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {
        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        pstSelf->pucTxBuffPtr   = pucData;
        pstSelf->usTxXferSize   = usSize;
        pstSelf->usTxXferCount  = usSize;

        uiValue = HV_R32(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER);
        /* Enable Transmit Holding Register Empty Interrupt */
        ier.reg_DLH_IER = cpu_sys_uart0_dlh_ier(uiValue)->reg_DLH_IER | UART_TRANEMPTY_INT_EN;
        HV_W32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_DLH_IER, ier);
        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Sends an amount of data in non blocking mode
 *  @param self uart pointer
 *  @param data pointer to data buffer
 *  @param size amount of data to be sent
 *  @return result
 */
Status Hv_Drv_Uart_DmaTransmit(UartSelf *pstSelf, const UCHAR8 *pucData, USHORT16 usSize)
{

    if (pucData == NULL || usSize == 0)
    {
        return HV_INVALID;
    }

    if (pstSelf->enState == UART_STATE_READY || pstSelf->enState == UART_STATE_BUSY_RX)
    {

        if (pstSelf->enState == UART_STATE_BUSY_RX)
        {
            pstSelf->enState = UART_STATE_BUSY_TX_RX;
        }
        else
        {
            pstSelf->enState = UART_STATE_BUSY_TX;
        }

        pstSelf->pucTxBuffPtr   = pucData;
        pstSelf->usTxXferSize   = usSize;
        pstSelf->usTxXferCount  = usSize;

        Hv_Cal_Dma_XferCallbackBond(pstSelf->pstHdmaTx, Uart_DmaTxCpltCallback);
        Hv_Cal_Dma_SetBlockSize(pstSelf->pstHdmaTx, usSize);
        Hv_Cal_Dma_SetSrcAddr(pstSelf->pstHdmaTx, (UINT32)pucData);
        Hv_Cal_Dma_ChannelEn(pstSelf->pstHdmaTx);

        return HV_SUCCESS;
    }
    else
    {
        return HV_FAILURE;
    }
}

/** Get errorCode of uart
 *  @param self uart pointer
 *  @return errorCode result
 */
UINT32 Hv_Drv_Uart_GetErrorCode(UartSelf *pstSelf)
{
    HV_LOGI("error errorCode = 0x%x\n", pstSelf->errorCode);
    return pstSelf->errorCode;
}

/** Get empty of uart
 *  @param self uart pointer
 *  @return result empty or not
 */
BOOL Hv_Drv_Uart_IsEmpty(UartSelf *pstSelf)
{
    rs_cpu_sys_uart0_lsr lsr = {0};

    HV_R32_REG(pstSelf->uiBaseOffset + CPU_SYS_UART0_LSR, lsr);
    return !lsr.reg_DR;
}

/**
 * @brief uart enable for log.
  * @param[in] bEnble: on/off
 */
VOID Hv_Drv_EnableUart(BOOL bEnble)
{
    HV_LOGI("EnableUart %d",bEnble);
    /* uart0_tx swtich */
    if (bEnble)
    {
        HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_4, reg_uart0_tx_func_sel , 1);
        //HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_4, reg_uart0_rx_func_sel , 1);
        //HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_8, reg_uart0_sin_in_sel , 0);
    }
    else
    {
        HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_4, reg_uart0_tx_func_sel , 0);
        //HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_4, reg_uart0_rx_func_sel , 0);
        //HV_W32_FIELD(RX_SH08_TOP_RX_CTRL_8, reg_uart0_sin_in_sel , 2;
    }

    return;
}
