/**************************************************************************//**
* @file     uart.c
* @version  V1.00
* @brief    N9H30 UART driver source file
*
* SPDX-License-Identifier: Apache-2.0
* @copyright (C) 2018 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "N9H30.h"
#include "sys.h"
#include "uart.h"

/** @addtogroup N9H30_Device_Driver N9H30 Device Driver
  @{
*/

/** @addtogroup N9H30_UART_Driver UART Driver
  @{
*/

/** @addtogroup N9H30_UART_EXPORTED_CONSTANTS UART Exported Constants
  @{
*/

/*@}*/ /* end of group N9H30_UART_EXPORTED_CONSTANTS */

/// @cond HIDDEN_SYMBOLS

/*-----------------------------------------*/
/* marco, type and constant definitions    */
/*-----------------------------------------*/
/*
    Define debug level
*/
//#define UART_DEBUG
//#define UART_FLOWCONTROL_DEBUG
//#define UART1_DEBUG
//#define UART2_DEBUG

#ifdef UART_DEBUG
#define UDEBUG          sysprintf
#else
#define UDEBUG(...)
#endif  /* UART_DEBUG */

#ifdef UART_FLOWCONTROL_DEBUG
#define FDEBUG          sysprintf
#else
#define FDEBUG(...)
#endif  /* UART_FLOWCONTROL_DEBUG */

#ifdef UART1_DEBUG
#define U1DEBUG         sysprintf
#else
#define U1DEBUG(...)
#endif  /* UART1_DEBUG */

#ifdef UART2_DEBUG
#define U2DEBUG         sysprintf
#else
#define U2DEBUG(...)
#endif  /* UART1_DEBUG */

/*-----------------------------------------*/
/* global file scope (static) variables    */
/*-----------------------------------------*/
static UART_BUFFER_T UART_DEV[UART_NUM];

static UINT32 UARTTXBUFSIZE[UART_NUM] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500};  /* UART0~10 Tx buffer size */
static UINT32 UARTRXBUFSIZE[UART_NUM] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500};    /* UART0~10 Rx buffer size */


/*
    UART flag declarations.
*/
static volatile CHAR _uart_cDSRState0 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState1 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState2 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState3 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState4 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState5 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState6 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState7 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState8 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState9 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cDSRState10 = 0;   /* set 1, state change                         */
static volatile CHAR _uart_cBIIState_0 = 0;  /* set 1, UART channel 0 break interrupt occur */
static volatile CHAR _uart_cBIIState_1 = 0;  /* set 1, UART channel 1 break interrupt occur */
static volatile CHAR _uart_cBIIState_2 = 0;  /* set 1, UART channel 2 break interrupt occur */
static volatile CHAR _uart_cBIIState_3 = 0;  /* set 1, UART channel 3 break interrupt occur */
static volatile CHAR _uart_cBIIState_4 = 0;  /* set 1, UART channel 4 break interrupt occur */
static volatile CHAR _uart_cBIIState_5 = 0;  /* set 1, UART channel 0 break interrupt occur */
static volatile CHAR _uart_cBIIState_6 = 0;  /* set 1, UART channel 1 break interrupt occur */
static volatile CHAR _uart_cBIIState_7 = 0;  /* set 1, UART channel 2 break interrupt occur */
static volatile CHAR _uart_cBIIState_8 = 0;  /* set 1, UART channel 3 break interrupt occur */
static volatile CHAR _uart_cBIIState_9 = 0;  /* set 1, UART channel 4 break interrupt occur */
static volatile CHAR _uart_cBIIState_10 = 0;  /* set 1, UART channel 4 break interrupt occur */
static volatile CHAR _uart_cCTSState0 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState1 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState2 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState3 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState4 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState5 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState6 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState7 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState8 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState9 = 0;    /* set 1, state change                         */
static volatile CHAR _uart_cCTSState10 = 0;    /* set 1, state change                         */

/*
    Define flow control flags & parameters.
*/
#define HWFLOWCONTROL   1
#define SWFLOWCONTROL   2
static volatile CHAR _uart_cFlowControlMode = 0;  /* default no flow control */
static volatile CHAR _uart_cHWTXStopped = 0;      /* Use for H/W flow control. Set 1, stop TX. Set 0, start TX.   */
static volatile CHAR _uart_cHWRXStopped = 0;      /* Use for H/W flow control. Set 1, stop RX. Set 0, start RX.   */
static volatile CHAR _uart_cSWTXStopped = 0;      /* Use for S/W flow control. Set 1, rec Xoff. Set 0, rec Xon.   */
static volatile CHAR _uart_cSWRXStopped = 0;      /* Use for S/W flow control. Set 1, send Xoff. Set 0, send Xon. */
//static INT _uart_nMaxRxBuf = 0;                   /* used in uartReceiveChars() */
//static INT _uart_nMinRxBuf = 0;                   /* used in uartReadRxBuf()    */


/*-----------------------------------------*/
/* prototypes of static functions          */
/*-----------------------------------------*/
static UINT32 _uartTxBufGetNextOne(INT nNum, UINT32 uPointer);
static UINT32 _uartRxBufGetNextOne(INT nNum, UINT32 uPointer);
static void _uartEnableInterrupt(INT nNum, UINT32 uVal);
static void _uartDisableInterrupt(INT nNum, UINT32 uVal);
static void _uartReceiveChars(INT nNum);
static void _uartTransmitChars(INT nNum);
static void _uartCheckModemStatus(INT nNum);
static INT _uartSetBaudRate(INT nNum, UART_T *val);
static void _uartInstallISR(UINT8 ucNum);
static BOOL _uartBUFSpaceAlloc(INT nNum);
static BOOL _uartCheckTxBufSpace(INT nNum, UINT32 uHead, UINT32 uTail, UINT32 uLen);
static INT32 _uartReadRxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen);
static void _uartWriteTxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen);
static INT _uartConfigureUART(PVOID pvParam);
static INT _uartPerformIrDA(INT nNum, UINT32 uCmd, UINT32 uCmd1);
static INT _uartGetRegisterValue(INT nNum, PVOID pvReg);


void RS485_HANDLE(INT nNum)
{
    UINT32 volatile uRegISR, uRegFSR, uRegALT_CSR;

    uRegISR = inpw(REG_UART0_ISR+(nNum*UARTOFFSET));
    uRegFSR = inpw(REG_UART0_FSR+(nNum*UARTOFFSET));

    if((uRegISR & UART_ISR_RLS_IF_Msk) && (uRegISR & UART_ISR_RDA_IF_Msk))  /* RLS INT & RDA INT */  //For RS485 Detect Address
    {
        if(uRegFSR & UART_FSR_RS485_ADD_DETF_Msk) { /* ADD_IF, RS485 mode */
            _uartReceiveChars(nNum);
            outpw((REG_UART0_FSR+(nNum*UARTOFFSET)), UART_FSR_RS485_ADD_DETF_Msk);  /* clear ADD_IF flag */
        }
    } else if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) { /* Rx Ready or Time-out INT*/
        /* Handle received data */
        _uartReceiveChars(nNum);
    }

    if(uRegISR & UART_ISR_RLS_IF_Msk) {
        uRegFSR = inpw(REG_UART0_FSR+(nNum*UARTOFFSET));
        if(uRegFSR & UART_FSR_BIF_Msk)
            _uart_cBIIState_0 = 1;
    }
}

void uart0ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR;

    uRegISR = inpw(REG_UART0_ISR) & 0xff;

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART0);

    if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
        _uartReceiveChars(UART0);

    if(uRegISR & UART_ISR_RLS_IF_Msk) {
        uRegFSR = inpw(REG_UART0_FSR);
        if(uRegFSR & UART_FSR_BIF_Msk)
            _uart_cBIIState_0 = 1;
    }

}

void uart1ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART1_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART1_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART1);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART1);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART1);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART1_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState1 = 1;
            } else
                _uartCheckModemStatus(UART1);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART1_FSR);
            U1DEBUG("U1 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_1 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U1 OEI!\n");
        }
    }
}

void uart2ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART2_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART2_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART2);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART2);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART2);

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART2_FSR);
            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_2 = 1;
        }

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART2_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState2 = 1;
            } else
                _uartCheckModemStatus(UART2);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART2_FSR);
            U1DEBUG("U2 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_2 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U2 OEI!\n");
        }
    }
}

void uart3ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART3_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART3_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART3);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART3);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk))
            _uartReceiveChars(UART3);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART3_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState3 = 1;
            } else
                _uartCheckModemStatus(UART3);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART3_FSR);
            U1DEBUG("U3 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_3 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U3 OEI!\n");
        }
    }

}

void uart4ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART4_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART4_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART4);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART4);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART4);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART4_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState4 = 1;
            } else
                _uartCheckModemStatus(UART4);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART4_FSR);
            U1DEBUG("U4 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_4 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U4 OEI!\n");
        }
    }

}

void uart5ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART5_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART5_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART5);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART5);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART5);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART5_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState5 = 1;
            } else
                _uartCheckModemStatus(UART5);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART5_FSR);
            U1DEBUG("U5 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_5 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U5 OEI!\n");
        }
    }

}

void uart6ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART6_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART6_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART6);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART6);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART6);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART6_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState6 = 1;
            } else
                _uartCheckModemStatus(UART6);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART6_FSR);
            U1DEBUG("U6 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_6 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U6 OEI!\n");
        }
    }

}

void uart7ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART7_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART7_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART7);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART7);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART7);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART7_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState7 = 1;
            } else
                _uartCheckModemStatus(UART7);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART7_FSR);
            U1DEBUG("U7 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_7 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U7 OEI!\n");
        }
    }

}

void uart8ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART8_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART8_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART8);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART8);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UART8);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART8_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState8 = 1;
            } else
                _uartCheckModemStatus(UART8);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART8_FSR);
            U1DEBUG("U8 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_8 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U8 OEI!\n");
        }
    }

}

void uart9ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UART9_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UART9_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UART9);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UART9);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk))  /* Received Data Available interrupt */
            _uartReceiveChars(UART9);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UART9_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState9 = 1;
            } else
                _uartCheckModemStatus(UART9);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UART9_FSR);
            U1DEBUG("U9 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_9 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U9 OEI!\n");
        }
    }

}

void uart10ISR(void)
{
    UINT32 volatile uRegISR, uRegFSR, uRegMSR, uRegFUN_SEL;

    uRegISR = inpw(REG_UARTA_ISR) & 0xff;
    uRegFUN_SEL = inpw(REG_UARTA_FUN_SEL);

    if(uRegISR & UART_ISR_THRE_IF_Msk)  /* TX empty interrupt, check LSR 4 kinds of error further */
        _uartTransmitChars(UARTA);

    if(uRegFUN_SEL == 0x3) {
        RS485_HANDLE(UARTA);
    } else {
        if( uRegISR & (UART_ISR_RDA_IF_Msk | UART_ISR_TOUT_IF_Msk)) /* Received Data Available interrupt */
            _uartReceiveChars(UARTA);

        if(uRegISR & UART_ISR_MODEM_IF_Msk) {
            if (_uart_cFlowControlMode == 0) {
                uRegMSR = inpw(REG_UARTA_MSR);

                if (uRegMSR & 0x01)
                    _uart_cCTSState10 = 1;
            } else
                _uartCheckModemStatus(UARTA);  /* H/W flow control */
        }

        if(uRegISR & UART_ISR_RLS_IF_Msk) {
            uRegFSR = inpw(REG_UARTA_FSR);
            U1DEBUG("U10 Irpt_RLS [0x%x]!\n", uRegFSR);

            if(uRegFSR & UART_FSR_BIF_Msk)
                _uart_cBIIState_10 = 1;

            if (uRegFSR & UART_FSR_RX_OVER_IF_Msk)
                U1DEBUG("U10 OEI!\n");
        }
    }

}

static UINT32 _uartTxBufGetNextOne(INT nNum, UINT32 uPointer)
{
    if((uPointer + 1) == UARTTXBUFSIZE[nNum])
        return (UINT32)NULL;
    else
        return (uPointer + 1);
}

static UINT32 _uartRxBufGetNextOne(INT nNum, UINT32 uPointer)
{
    if((uPointer + 1) == UARTRXBUFSIZE[nNum])
        return (UINT32)NULL;
    else
        return (uPointer + 1);
}

static void _uartEnableInterrupt(INT nNum, UINT32 uVal)
{
    UINT32 uReg = 0;

    uReg = inpw(REG_UART0_IER+(nNum * UARTOFFSET));
    uReg |= uVal;
    outpw(REG_UART0_IER+(nNum * UARTOFFSET), uReg);
}

static void _uartDisableInterrupt(INT nNum, UINT32 uVal)
{
    UINT32 uReg = 0;

    if(uVal == DISABLEALLIER)
        outpw(REG_UART0_IER+(nNum * UARTOFFSET), 0);
    else {
        uReg = inpw(REG_UART0_IER+(nNum * UARTOFFSET));
        uReg &= ~uVal;
        outpw(REG_UART0_IER+(nNum * UARTOFFSET), uReg);
    }
}

static void _uartReceiveChars(INT nNum)
{
    //UINT32 volatile uRegLSR, uBuf = 0;
    UINT32 volatile uRegFSR, uRegALT_CSR, uRegFUN_SEL, uRegFCR, uRegLINSR, uRegISR;
    UINT32 volatile uBuf = 0;
    UINT32 volatile uOffset = nNum * UARTOFFSET;
    INT nMaxCount = 256;
    UCHAR ucChar;

    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    //uRegFSR = inpw(REG_UART0_FSR+(nNum * UARTOFFSET));
    uRegFUN_SEL = inpw(REG_UART0_FUN_SEL+uOffset);

    do {
        uRegFSR = inpw(REG_UART0_FSR+uOffset);
        uRegLINSR = inpw(REG_UART0_LIN_SR+uOffset);
        uRegISR = inpw(REG_UART0_ISR+uOffset);
        ucChar = inpb(REG_UART0_RBR+uOffset);

        if( (uRegFSR & UART_FSR_RS485_ADD_DETF_Msk) && (uRegFUN_SEL == 0x3) ) {
            uRegALT_CSR = inpw(REG_UART0_ALT_CSR+(nNum*UARTOFFSET));
            uRegFCR = inpw(REG_UART0_FCR+(nNum*UARTOFFSET));
            if( uRegALT_CSR & UART_ALT_CSR_RS485_NMM_Msk ) {
                if ( ucChar == (uRegALT_CSR >> UART_ALT_CSR_ADDR_MATCH_Pos)) {
                    uRegFCR &= ~UART_FCR_RX_DIS_Msk;  /* Enable RS485 RX */
                    outpw((REG_UART0_FCR+(nNum*UARTOFFSET)), uRegFCR);
                } else {
                    uRegFCR |= UART_FCR_RX_DIS_Msk;  /* Disable RS485 RX */
                    uRegFCR |= UART_FCR_RFR_Msk;  /* Clear data from RX FIFO */
                    outpw((REG_UART0_FCR+(nNum*UARTOFFSET)), uRegFCR);
                    break;
                }
            }
        }


        uBuf = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);
        if(uBuf == dev->uUartRxHead) { /* Rx buffer full */
            //ucChar = inpb(REG_UART0_RBR+(nNum * UARTOFFSET));

            if (_uart_cHWRXStopped)
                U1DEBUG("[%d] buf full!\n", nNum);

            break;
        }

        //ucChar = inpb(REG_UART0_RBR+(nNum * UARTOFFSET));

        dev->pucUartRxBuf[dev->uUartRxTail] = ucChar;

        /* Check LSR for BII, FEI, PEI, OEI */
        dev->pucUARTFlag[dev->uUartRxTail] = 0;

        if(uRegFSR & UART_FSR_BIF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_BIF_Msk;
            U1DEBUG("BIF!\n");
        } else if(uRegFSR & UART_FSR_FEF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_FEF_Msk;
            U1DEBUG("FEF!\n");
        } else if(uRegFSR & UART_FSR_PEF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_PEF_Msk;
            U1DEBUG("PEF!\n");
        } else if (uRegFSR & UART_FSR_RX_OVER_IF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_RX_OVER_IF_Msk;
            U1DEBUG("OVER_IF!\n");
        } else if(uRegFSR & UART_FSR_RS485_ADD_DETF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_RS485_ADD_DETF_Msk;
            U1DEBUG("RS485_ADD_DET_IF!\n");
        }

        if(uRegFUN_SEL == 0x1) {
            if(uRegISR & UART_ISR_LIN_RX_BREAK_IF_Msk) {
                dev->pucLINFlag[dev->uUartRxTail] = uRegLINSR;

                // Clear ISR and LIN Status
                outpw(REG_UART0_ISR, UART_ISR_LIN_RX_BREAK_IF_Msk);
                outpw(REG_UART0_LIN_SR, 0x30F);
            }
        }

        dev->uUartRxTail = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);

        /* overrun error is special case, H/W ignore the character */
        if(uRegFSR & UART_FSR_RX_OVER_IF_Msk) {
            dev->pucUARTFlag[dev->uUartRxTail] = UART_FSR_RX_OVER_IF_Msk;
            dev->uUartRxTail = _uartRxBufGetNextOne(nNum, dev->uUartRxTail);
        }

        uRegFSR = inpw(REG_UART0_FSR+(nNum * UARTOFFSET));
    } while((!(uRegFSR & UART_FSR_RX_EMPTY_Msk)) && (nMaxCount-- > 0));

}

static void _uartTransmitChars(INT nNum)
{
    UINT32 volatile i;

    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    if(dev->uUartTxHead != dev->uUartTxTail) { /* buffer is not empty */
        for (i=0; i<8; i++) {
            outpw(REG_UART0_THR+(nNum * UARTOFFSET), dev->pucUartTxBuf[dev->uUartTxHead]);
            dev->uUartTxHead = _uartTxBufGetNextOne(nNum, dev->uUartTxHead);

            if(dev->uUartTxHead == dev->uUartTxTail) { /* buffer empty */
                _uartDisableInterrupt(nNum, UART_IER_THRE_IEN_Msk);
                break;
            }
        }
    }
}

/*
    Call by uart1ISR().
*/
static void _uartCheckModemStatus(INT nNum)
{
    UINT32 volatile uRegMSR;
    UINT32 uOffset = nNum * UARTOFFSET;

    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    FDEBUG("\n Modem INT\n");
    uRegMSR = inpw(REG_UART0_MSR + uOffset);
    if(_uart_cHWTXStopped) {
        if(!(uRegMSR & 0x10)) { /* CTS high, external signal is low */
            _uart_cHWTXStopped = 0;
            FDEBUG("H/W flow control ...\n");

            /* 2007.11.12 modify, PT23 HHWu */
            if(dev->uUartTxHead != dev->uUartTxTail) {  /* buffer is not empty */
                _uartEnableInterrupt(nNum, UART_IER_THRE_IEN_Msk);  /* enable TX empty interrupt */
                FDEBUG("buf not empty, TX continued\n");
            }
        }
    } else {
        if( !(uRegMSR & 0x10) ) { /* CTS low, external signal is high */
            _uart_cHWTXStopped = 1;
            _uartDisableInterrupt(nNum, UART_IER_THRE_IEN_Msk);  /* disable TX empty interrupt */
            FDEBUG("H/W flow control, TX stopped\n");
        }
    }
}

static INT _uartSetBaudRate(INT nNum, UART_T *val)
{
    UINT32 u32Reg;
    UINT32 uOffset = nNum * UARTOFFSET;
    UINT32 u32Baud_Div;
    UINT32 u32Clk = val->uFreq;
    UINT32 u32baudrate = val->uBaudRate;

    //if (val->uFreq > 200000000)  /* Max frequency 200MHz */
    //  return -1;

    u32Baud_Div = UART_BAUD_MODE2_DIVIDER(u32Clk, u32baudrate);

    if(u32Baud_Div > 0xFFFF)
        u32Reg = (UART_BAUD_MODE0 | UART_BAUD_MODE0_DIVIDER(u32Clk, u32baudrate));
    else
        u32Reg = (UART_BAUD_MODE2 | u32Baud_Div);

    outpw(REG_UART0_BAUD + uOffset, u32Reg);

    return 0;
}

static void _uartInstallISR(UINT8 ucNum)
{
    UART_BUFFER_T *dev;

    IRQn_Type IRQ;

    dev = (UART_BUFFER_T *) &UART_DEV[ucNum];

    if(ucNum == UART0) {
        IRQ= UART0_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart0ISR);
    } else if(ucNum == UART1) {
        IRQ= UART1_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart1ISR);
    } else if(ucNum == UART2) {
        IRQ= UART2_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart2ISR);
    } else if(ucNum == UART3) {
        IRQ= UART3_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart3ISR);
    } else if(ucNum == UART4) {
        IRQ= UART4_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart4ISR);
    } else if(ucNum == UART5) {
        IRQ= UART5_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart5ISR);
    } else if(ucNum == UART6) {
        IRQ= UART6_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart6ISR);
    } else if(ucNum == UART7) {
        IRQ= UART7_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart7ISR);
    } else if(ucNum == UART8) {
        IRQ= UART8_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart8ISR);
    } else if(ucNum == UART9) {
        IRQ= UART9_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart9ISR);
    } else if(ucNum == UARTA) {
        IRQ= UART10_IRQn;
        dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)uart10ISR);
    } else {
        return;
    }

    //dev->pvUartVector = sysInstallISR((IRQ_LEVEL_1 | HIGH_LEVEL_SENSITIVE), IRQ, (PVOID)pvNewISR);
    sysSetLocalInterrupt(ENABLE_IRQ);                            /* enable CPSR I bit */
    sysEnableInterrupt(IRQ);
    //DrvUART_EnableInt(TEST_PORT,(DRVUART_RLSINT|DRVUART_THREINT|DRVUART_RDAINT));


}

static BOOL _uartBUFSpaceAlloc(INT nNum)
{
    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    /* Memory allocate Tx buffer */
    dev->pucUartTxBuf = (PUINT8) malloc(UARTTXBUFSIZE[nNum] * sizeof(UINT8));
    if(dev->pucUartTxBuf == NULL)
        return FALSE;

    /* Memory allocate Rx buffer */
    dev->pucUartRxBuf = (PUINT8) malloc(UARTRXBUFSIZE[nNum] * sizeof(UINT8));
    if(dev->pucUartRxBuf == NULL) {
        free(dev->pucUartTxBuf);
        return FALSE;
    }

    /* Memory allocate Rx character flag */
    dev->pucUARTFlag = (PINT) malloc(UARTRXBUFSIZE[nNum] * sizeof(INT));
    if(dev->pucUARTFlag == NULL) {
        free(dev->pucUartTxBuf);
        free(dev->pucUartRxBuf);
        return FALSE;
    }

    /* initial memory */
    memset(dev->pucUartTxBuf, 0, UARTTXBUFSIZE[nNum] * sizeof(UINT8));
    memset(dev->pucUartRxBuf, 0, UARTRXBUFSIZE[nNum] * sizeof(UINT8));
    memset(dev->pucUARTFlag, 0, UARTRXBUFSIZE[nNum] * sizeof(INT));

    /* inital struct UART_BUFFER_STRUCT, uUartTxHead, uUartTxTail, uUartRxHead, uUartRxTail */
    dev->uUartTxHead = dev->uUartTxTail = (UINT32)NULL;
    dev->uUartRxHead = dev->uUartRxTail = (UINT32)NULL;

    return TRUE;
}

static BOOL _uartCheckTxBufSpace(INT nNum, UINT32 uHead, UINT32 uTail, UINT32 uLen)
{
    UINT32 uBuf;

    uBuf = _uartTxBufGetNextOne(nNum, uTail);
    if(uBuf == uHead)   /* Tx buffer full */
        return FALSE;

    if(uHead == uTail)  /* Tx buffer empty */
        return TRUE;

    if(uTail > uHead) {
        if(uLen >= (UARTTXBUFSIZE[nNum]-(uTail-uHead)))  /* 2007.10.29 fix pointer bug, PT23 HHWu */
            return FALSE;  /* Tx buffer space isn't enough */
        else
            return TRUE;
    } else {
        /* case: uTail < uHead */
        if(uLen >= (uHead-uTail))  /* 2007.10.29 fix pointer bug, PT23 HHWu */
            return FALSE;  /* Tx buffer space isn't enough */
        else
            return TRUE;
    }

    //return TRUE;
}

static INT32 _uartReadRxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen)
{
    UINT32 i;
    UINT32 uOffset = nNum * UARTOFFSET;
    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    if(dev->bIsUseUARTRxInt == TRUE) {

        // disable Rx interrupt ...

        if(dev->uUartRxHead == dev->uUartRxTail)
            return 0;

        for(i = uLen ; i > 0 ; i--) {
            *pucBuf++ = dev->pucUartRxBuf[dev->uUartRxHead];
            dev->uUartRxHead = _uartRxBufGetNextOne(nNum, dev->uUartRxHead);

            if(dev->uUartRxHead == dev->uUartRxTail)
                break;
        }

        uLen = uLen-i+1;
    } else { /* pooling mode */
        for(i = 0 ; i < uLen; i++) {
            while(!(inpw(REG_UART0_FSR+uOffset) & UART_FSR_RX_EMPTY_Msk));
            *pucBuf++ = inpb(REG_UART0_RBR+uOffset);
        }
    }

    return (uLen);
}

static void _uartWriteTxBuf(INT nNum, PUINT8 pucBuf, UINT32 uLen)
{
    UINT32 i;
    UINT32 uOffset = nNum * UARTOFFSET;
    UART_BUFFER_T *dev;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    /* Check interrupt or polling mode first */
    if(dev->bIsUseUARTTxInt == TRUE) {
        while(uLen--) {
            dev->pucUartTxBuf[dev->uUartTxTail] = *pucBuf++;
            dev->uUartTxTail = _uartTxBufGetNextOne(nNum, dev->uUartTxTail);
        }

        if (!(inpw(REG_UART0_IER+uOffset) & UART_IER_THRE_IEN_Msk))  /* Enable Tx empty interrupt */
            _uartEnableInterrupt(nNum, UART_IER_THRE_IEN_Msk);
    } else { /* pooling mode */
        for(i = 0 ; i < uLen ; i++) {
            /* Wait until the transmitter buffer is empty */
            while(!(inpw(REG_UART0_FSR+uOffset) & UART_FSR_TE_FLAG_Msk));
            outpw(REG_UART0_THR+uOffset, *pucBuf++);
        }
    }
}

static INT _uartConfigureUART(PVOID pvParam)
{
    INT retval;
    BOOL bIsMemoryAllocOk;
    UINT32 u32Reg;
    UINT32 uOffset;
    UINT32 uNum=0;

    UART_T *param = (UART_T *) pvParam;

    uOffset = param->ucUartNo * UARTOFFSET;
    uNum = param->ucUartNo;

    /* Check UART channel */
    if ( uNum > UARTA )
        return UART_ERR_CHANNEL_INVALID;

    /* Check the supplied parity */
    if ( (param->ucParity != PARITY_NONE) &&
         (param->ucParity != PARITY_EVEN) &&
         (param->ucParity != PARITY_ODD)  &&
         (param->ucParity != (PARITY_ODD | PARITY_STICK)) &&
         (param->ucParity != (PARITY_EVEN | PARITY_STICK)) )
        return UART_ERR_PARITY_INVALID;

    /* Check the supplied number of data bits */
    if ( (param->ucDataBits != DATA_BITS_5) &&
         (param->ucDataBits != DATA_BITS_6) &&
         (param->ucDataBits != DATA_BITS_7) &&
         (param->ucDataBits != DATA_BITS_8) )
        return UART_ERR_DATA_BITS_INVALID;

    /* Check the supplied number of stop bits */
    if ( (param->ucStopBits != STOP_BITS_1) &&
         (param->ucStopBits != STOP_BITS_2) )
        return UART_ERR_STOP_BITS_INVALID;

    /* Check the supplied number of trigger level bytes */
    if ( (param -> ucUartNo == UART1) || (param -> ucUartNo == UART2) || (param -> ucUartNo == UART4) ||
         (param -> ucUartNo == UART6) || (param -> ucUartNo == UART8) || (param -> ucUartNo == UARTA)) {
        /* UART1,2,4,6,8,A */
        if ( (param->ucRxTriggerLevel != UART_FCR_RFITL_1BYTE)   &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_4BYTES)  &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_8BYTES)  &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_14BYTES) &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_30BYTES) &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_46BYTES) &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_62BYTES) )
            return UART_ERR_TRIGGERLEVEL_INVALID;
    } else {
        /* UART0,3,5,7,9 */
        if ( (param->ucRxTriggerLevel != UART_FCR_RFITL_1BYTE)  &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_4BYTES) &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_8BYTES) &&
             (param->ucRxTriggerLevel != UART_FCR_RFITL_30BYTES) )
            return UART_ERR_TRIGGERLEVEL_INVALID;
    }

    /* Enable UART clock */
    if(param->ucUartNo < ALLCHANNEL) {
        outpw(REG_CLK_PCLKEN0, inpw(REG_CLK_PCLKEN0) | (1 << (16+param->ucUartNo)) );
    }

    /* Reset TX/RX FIFOs */
    u32Reg = inpw(REG_UART0_FCR+uOffset);
    outpw(REG_UART0_FCR+uOffset, (u32Reg | (0x03 << 1)) );

    /* Setup baud rate */
    retval = _uartSetBaudRate(param->ucUartNo, param);
    if (retval < 0)
        return UART_ERR_SET_BAUDRATE_FAIL;

    /* Setup parity, data bits, and stop bits */
    outpw(REG_UART0_LCR+uOffset, (param->ucParity | param->ucDataBits | param->ucStopBits));

    /* Setup Rx time out value */
    outpw(REG_UART0_TOR+uOffset, 0x80+0x20);

    /* Setup FIFO trigger level */
    outpw(REG_UART0_FCR+uOffset, param->ucRxTriggerLevel);

    /* only exec once unless call uartClose() */
    if(UART_DEV[param->ucUartNo].bIsUARTInitial == FALSE) {
        /* Configure GPIO function */
        //_uartConfigureGPIO(param->ucUartNo);

        /* Allocate Tx, Rx buffer */
        bIsMemoryAllocOk = _uartBUFSpaceAlloc(param->ucUartNo);
        if(bIsMemoryAllocOk == FALSE)
            return UART_ERR_ALLOC_MEMORY_FAIL;

        /* Hook UART interrupt service routine */
        _uartInstallISR(param->ucUartNo);

        /* Enable Rx interrupt */
        if(UART_DEV[param->ucUartNo].bIsUseUARTRxInt == TRUE)
            _uartEnableInterrupt(param->ucUartNo, UART_IER_RDA_IEN_Msk);

    }

    UART_DEV[param->ucUartNo].bIsUARTInitial = TRUE;  /* it's important to set TRUE */
    return 0;
}

static INT _uartPerformIrDA(INT nNum, UINT32 uCmd, UINT32 uCmd1)  /* UART2 only */
{
    UINT32 uOffset = nNum * UARTOFFSET;
    UINT32 baud;

    switch(uCmd) {
        case ENABLEIrDA:
            //_uart_bIsPerformIrDA = TRUE;

            baud = inpw(REG_UART0_BAUD+uOffset);
            baud = baud & (0x0000ffff);
            baud = baud + 2;
            baud = baud / 16;
            baud = baud - 2;

            outpw(REG_UART0_BAUD+uOffset, baud);

            if(uCmd1 == IrDA_TX)
                outpw(REG_UART0_IRCR+uOffset, UART_IRCR_TX_SELECT_Msk);
            else if(uCmd1 == IrDA_RX)
                outpw(REG_UART0_IRCR+uOffset, 0x0);
            else
                return UART_ERR_IrDA_COMMAND_INVALID;

            outpw(REG_UART0_FUN_SEL+uOffset, 0x2); // Select IrDA mode

            break;

        case DISABLEIrDA:
            //_uart_bIsPerformIrDA = FALSE;
            outpw(REG_UART0_IRCR+uOffset, 0x40);  /* Set default value, INV_TX set 0, INV_RX set 1 */
            outpw(REG_UART0_FUN_SEL+uOffset, 0x0); // Select UART mode
            break;

        default:
            return UART_ERR_IrDA_COMMAND_INVALID;
    }

    return 0;
}

/*
    Remark:
    1. LCR & LSR aren't support yet.
*/
static INT _uartGetRegisterValue(INT nNum, PVOID pvReg)
{
    INT nCnt = 0;
    UINT32 uOffset = nNum * UARTOFFSET;

    UART_REGISTER_T *reg = (UART_REGISTER_T *) pvReg;

    memset(reg, 0, sizeof(UART_REGISTER_T));

    /* Read IER */
    reg->uUartReg[nCnt][0] = REG_UART0_IER + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_IER + uOffset);

    /* Read FCR */
    reg->uUartReg[nCnt][0] = REG_UART0_FCR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_FCR + uOffset);

    /* Read LCR */
    reg->uUartReg[nCnt][0] = REG_UART0_LCR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_LCR + uOffset);

    /* Read MCR, MSR */
    reg->uUartReg[nCnt][0] = REG_UART0_MCR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_MCR + uOffset);
    reg->uUartReg[nCnt][0] = REG_UART0_MSR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_MSR + uOffset);

    /* Read FSR */
    reg->uUartReg[nCnt][0] = REG_UART0_FSR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_FSR + uOffset);

    /* Read ISR */
    reg->uUartReg[nCnt][0] = REG_UART0_ISR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_ISR + uOffset);

    /* Read TOR */
    reg->uUartReg[nCnt][0] = REG_UART0_TOR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_TOR + uOffset);

    /* Read BAUD */
    reg->uUartReg[nCnt][0] = REG_UART0_BAUD + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_BAUD + uOffset);

    /* Read IRCR */
    reg->uUartReg[nCnt][0] = REG_UART0_IRCR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_IRCR + uOffset);

    /* Read ALT_CSR */
    reg->uUartReg[nCnt][0] = REG_UART0_ALT_CSR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_ALT_CSR + uOffset);

    /* Read FUN_SEL */
    reg->uUartReg[nCnt][0] = REG_UART0_FUN_SEL + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_FUN_SEL + uOffset);

    /* Read LIN_CTL */
    reg->uUartReg[nCnt][0] = REG_UART0_LIN_CTL + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_LIN_CTL + uOffset);

    /* Read LIN_SR */
    reg->uUartReg[nCnt][0] = REG_UART0_LIN_SR + uOffset;
    reg->uUartReg[nCnt++][1] = inpw(REG_UART0_LIN_SR + uOffset);

    return (nCnt);
}

/// @endcond HIDDEN_SYMBOLS

/** @addtogroup N9H30_UART_EXPORTED_FUNCTIONS UART Exported Functions
  @{
*/

/**
  * @brief    The function is used to initial device struct parameters.
  *
  * @return   0
  */
INT uartInit(void)
{
    INT i;

    /* Initial UART_BUFFER_T struct */
    for(i = 0; i < UART_NUM ; i++)
        UART_DEV[i].bIsUARTInitial = FALSE;

    for(i = 0; i < UART_NUM ; i++)
        UART_DEV[i].bIsUseUARTTxInt = TRUE;

    for(i = 0; i < UART_NUM ; i++)
        UART_DEV[i].bIsUseUARTRxInt = TRUE;

    return 0;
}

/**
  * @brief    The function is used to config UART channel.
  *
  * @param[in]    uart: UART Port. ( UART0 / UART1 / UART2 / UART3 / UART 4 /UART 5 /
  *                                 UART6 / UART7 / UART8 / UART9 / UARTA )
  *
  * @return   UART_EIO: UART config Fail
  *           Successful: UART config success
  */
INT uartOpen(PVOID uart)
{
    INT nValue = 0;
    UART_T *dev = (UART_T *) uart;

    if((nValue = _uartConfigureUART(uart)) < 0) {
        if(nValue != UART_ERR_CHANNEL_INVALID)
            UART_DEV[dev->ucUartNo].nErrno = nValue;

        return UART_EIO;
    } else
        UART_DEV[dev->ucUartNo].nErrno = 0;

    return Successful;
}

/**
  * @brief    The function is used to read RX FIFO returned data or RX driver buffer.
  *
  * @param[in]    nNum: UART Port. ( UART0 / UART1 / UART2 / UART3 / UART 4 /UART 5 /
  *                                 UART6 / UART7 / UART8 / UART9 / UARTA )
  * @param[out]   pucBuf: The buffer to receive.
  *
  * @param[in]    uLen: The the read bytes number of data.
  *
  * @return   UART_EIO: UART read Fail
  *           DataLength: Receive byte count
  */
INT32 uartRead(INT nNum, PUINT8 pucBuf, UINT32 uLen)
{
    UART_BUFFER_T *dev;
    INT32 DataLength;

    //if((nNum < UART0) || (nNum > UART4))
    //  return UART_ENODEV;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    /* Check UART initial status */
    if(dev->bIsUARTInitial == FALSE)
        return UART_EIO;

    /* Check uLen value */
    if((uLen > UARTRXBUFSIZE[nNum]) || (uLen == 0))
        return UART_EIO;

    DataLength = _uartReadRxBuf(nNum, pucBuf, uLen);

    return (DataLength);

}


/**
  * @brief    The function is used to write data to TX FIFO directly or TX driver buffer.
  *
  * @param[in]    nNum: UART channel. ( UART0 / UART1 / UART2 / UART3 / UART 4 /UART 5 /
  *                                     UART6 / UART7 / UART8 / UART9 / UARTA )
  * @param[out]   pucBuf: Transmit buffer pointer.
  *
  * @param[in]    uLen: Transmit buffer length.
  *
  * @return   UART_EIO: UART transmit Fail
  *           uLen: write length on success
  */
INT32 uartWrite(INT nNum, PUINT8 pucBuf, UINT32 uLen)
{
    BOOL bIsTxBufEnough;

    UART_BUFFER_T *dev;

    //if((nNum < UART0) || (nNum > UART4))
    //  return UART_ENODEV;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];
    dev->nErrno = 0;

    /* Check UART initial status */
    if(dev->bIsUARTInitial == FALSE)
        return UART_EIO;

    /* Check uLen value */
    if((uLen > UARTWRITESIZE) || (uLen == 0))
        return UART_EIO;

    /* Check UART Tx buffer */
    if(dev->bIsUseUARTTxInt == TRUE) {
        bIsTxBufEnough = _uartCheckTxBufSpace(nNum, dev->uUartTxHead, dev->uUartTxTail, uLen);
        if(bIsTxBufEnough == FALSE) {
            //sysprintf("Tx buf not enough\n");
            dev->nErrno = UART_ERR_TX_BUF_NOT_ENOUGH;
            return UART_EIO;
        }
    }

    /* Move data to UART Tx buffer then transmit */
    _uartWriteTxBuf(nNum, pucBuf, uLen);

    return (uLen);
}

/**
  * @brief    Support some UART driver commands for application.
  *
  * @param[in]    nNum: UART channel. ( UART0 / UART1 / UART2 / UART3 / UART 4 /UART 5 /
  *                                     UART6 / UART7 / UART8 / UART9 / UARTA )
  *
  * @param[in]    uCmd: Command.
  *
  * @param[in]    uArg0: Arguments for the command.
  *
  * @param[in]    uArg1: Arguments for the command.
  *
  * @return   UART_ENODEV: UART channel out of range
  *           UART_EIO: No activated or argument error or configure UART fail
  *           Successful: Success
  */
INT uartIoctl(INT nNum, UINT32 uCmd, UINT32 uArg0, UINT32 uArg1)
{
    INT32 retval;
    UINT32 uReg;
    UINT32 uOffset = nNum * UARTOFFSET;

    UART_BUFFER_T *dev;

    if((nNum < UART0) || (nNum > UARTA))
        return UART_ENODEV;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    /* Check UART initial status */
    if(dev->bIsUARTInitial == FALSE) {
        if((uCmd != UART_IOC_GETERRNO) &&
           (uCmd != UART_IOC_GETUARTREGISTERVALUE))
            return UART_EIO;
    }

    switch(uCmd) {
        case UART_IOC_SETTXMODE:
            if(uArg0 == UARTINTMODE)
                dev->bIsUseUARTTxInt = TRUE;
            else if(uArg0 == UARTPOLLMODE)
                dev->bIsUseUARTTxInt = FALSE;
            else {
                dev->nErrno = UART_ERR_OPERATE_MODE_INVALID;
                return UART_EIO;
            }

            break;

        case UART_IOC_SETRXMODE:
            if(uArg0 == UARTINTMODE) {
                dev->bIsUseUARTRxInt = TRUE;
                _uartEnableInterrupt(nNum, UART_IER_RDA_IEN_Msk);
            } else if(uArg0 == UARTPOLLMODE) {
                dev->bIsUseUARTRxInt = FALSE;
                _uartDisableInterrupt(nNum, UART_IER_RDA_IEN_Msk);
            } else {
                dev->nErrno = UART_ERR_OPERATE_MODE_INVALID;
                return UART_EIO;
            }

            break;

        case UART_IOC_GETRECCHARINFO:  // ..... not test yet
            memcpy((PVOID) uArg0, (PVOID) dev, sizeof(struct UART_BUFFER_STRUCT));
            break;

        case UART_IOC_SETUARTPARAMETER:  // ..... not test yet
            if((retval = _uartConfigureUART((PVOID) uArg0)) < 0) {
                dev->nErrno = retval;
                return UART_EIO;
            }

            break;

        case UART_IOC_PERFORMIrDA:

            if((retval = _uartPerformIrDA(nNum, uArg0, uArg1)) < 0) {
                dev->nErrno = retval;
                return UART_EIO;
            }

            break;

        case UART_IOC_GETUARTREGISTERVALUE:
            return (_uartGetRegisterValue(nNum, (PVOID) uArg0));
        //break;

        case UART_IOC_GETERRNO:
            *(PUINT32)uArg0 = dev->nErrno;
            break;

        case UART_IOC_SETMODEMINTERRUPT:

            if(uArg0 == UART_ENABLE_MODEM_INT)
                _uartEnableInterrupt(nNum, UART_IER_MODEM_IEN_Msk);
            else if(uArg0 == UART_DISABLE_MODEM_INT)
                _uartDisableInterrupt(nNum, UART_IER_MODEM_IEN_Msk);
            else
                return UART_EIO;

            break;

        case UART_IOC_GETCTSSTATE:

            if(nNum == UART1) {
                *(PUINT32)uArg0 = _uart_cCTSState1;                        /* CTS state */
                _uart_cCTSState1 = 0;
            } else if(nNum == UART2) {
                *(PUINT32)uArg0 = _uart_cCTSState2;                        /* CTS state */
                _uart_cCTSState2 = 0;
            } else if(nNum == UART3) {
                *(PUINT32)uArg0 = _uart_cCTSState3;                        /* CTS state */
                _uart_cCTSState3 = 0;
            } else if(nNum == UART4) {
                *(PUINT32)uArg0 = _uart_cCTSState4;                        /* CTS state */
                _uart_cCTSState4 = 0;
            } else if(nNum == UART5) {
                *(PUINT32)uArg0 = _uart_cCTSState5;                        /* CTS state */
                _uart_cCTSState5 = 0;
            } else if(nNum == UART6) {
                *(PUINT32)uArg0 = _uart_cCTSState6;                        /* CTS state */
                _uart_cCTSState6 = 0;
            } else if(nNum == UART7) {
                *(PUINT32)uArg0 = _uart_cCTSState7;                        /* CTS state */
                _uart_cCTSState7 = 0;
            } else if(nNum == UART8) {
                *(PUINT32)uArg0 = _uart_cCTSState8;                        /* CTS state */
                _uart_cCTSState8 = 0;
            } else if(nNum == UART9) {
                *(PUINT32)uArg0 = _uart_cCTSState9;                        /* CTS state */
                _uart_cCTSState9 = 0;
            } else if(nNum == UARTA) {
                *(PUINT32)uArg0 = _uart_cCTSState10;                        /* CTS state */
                _uart_cCTSState10 = 0;
            }

            *(PUINT32)uArg1 = (inpw(REG_UART0_MSR+uOffset) & (1 << 4)) >> 4;  /* get CTS# value */

            break;

        case UART_IOC_SETRTSSIGNAL:

            if(uArg0 == UART_RTS_HIGH)      /* set RTS signal high */
                outpw(REG_UART0_MCR+uOffset, inpw(REG_UART0_MCR+uOffset) & ~0x02);
            else if(uArg0 == UART_RTS_LOW)  /* set RTS signal low  */
                outpw(REG_UART0_MCR+uOffset, inpw(REG_UART0_MCR+uOffset) | 0x02);
            else
                return UART_EIO;

            break;

        case UART_IOC_SETINTERRUPT:
            if(uArg0 == 1)       /* enable interrupt  */
                _uartEnableInterrupt(nNum, uArg1);
            else if(uArg0 == 0)  /* disable interrupt */
                _uartDisableInterrupt(nNum, uArg1);
            else
                return UART_EIO;

            break;

        case UART_IOC_SETBREAKCONTROL:
            uReg = inpw(REG_UART0_LCR + uOffset);
            if(uArg0 == 1) {     /* set break contorl bit  */
                uReg |= UART_LCR_BCB_Msk;
                outpw(REG_UART0_LCR + uOffset, uReg);
            } else if(uArg0 == 0) { /* clear break contorl bit */
                uReg &= ~UART_LCR_BCB_Msk;
                outpw(REG_UART0_LCR + uOffset, uReg);
            } else
                return UART_EIO;

            break;

        case UART_IOC_GETBIISTATE:
            switch(nNum) {
                case UART0:
                    *(PUINT32)uArg0 = _uart_cBIIState_0;
                    break;
                case UART1:
                    *(PUINT32)uArg0 = _uart_cBIIState_1;
                    break;
                case UART2:
                    *(PUINT32)uArg0 = _uart_cBIIState_2;
                    break;
                case UART3:
                    *(PUINT32)uArg0 = _uart_cBIIState_3;
                    break;
                case UART4:
                    *(PUINT32)uArg0 = _uart_cBIIState_4;
                    break;
                case UART5:
                    *(PUINT32)uArg0 = _uart_cBIIState_5;
                    break;
                case UART6:
                    *(PUINT32)uArg0 = _uart_cBIIState_6;
                    break;
                case UART7:
                    *(PUINT32)uArg0 = _uart_cBIIState_7;
                    break;
                case UART8:
                    *(PUINT32)uArg0 = _uart_cBIIState_8;
                    break;
                case UART9:
                    *(PUINT32)uArg0 = _uart_cBIIState_9;
                    break;
                case UARTA:
                    *(PUINT32)uArg0 = _uart_cBIIState_10;
                    break;

                default:
                    break;
            }
            break;

        /* H/W S/W flow control function */
        case UART_IOC_ENABLEHWFLOWCONTROL:

            /* H/W & S/W are alternative */
            if(_uart_cFlowControlMode == SWFLOWCONTROL)
                return UART_EIO;

            _uart_cFlowControlMode = HWFLOWCONTROL;

            /* Implement H/W flow control on TX & RX interrupt mode. */
            //dev->bIsUseUARTTxInt = TRUE;
            //dev->bIsUseUARTRxInt = TRUE;
            _uartEnableInterrupt(nNum, UART_IER_RDA_IEN_Msk);

            /*
                Set up RTS mechanism.
                In uartReceiveChars(), if uRecCnt >= _uart_nMaxRxBuf then set RTS high to stop RX.
                In uartReadRxBuf(), if uRecCnt <= _uart_nMinRxBuf then set RTS low to re-start RX.
            */
            //_uart_nMaxRxBuf = (UARTRXBUFSIZE[nNum] * 3) / 4;
            //_uart_nMinRxBuf = UARTRXBUFSIZE[nNum] / 2;
            //FDEBUG("max[%d] min[%d]\n", _uart_nMaxRxBuf, _uart_nMinRxBuf);

            /* Set RTS high level trigger */
            outpw(REG_UART0_MCR+uOffset, (inpw(REG_UART0_MCR+uOffset) | UART_RTS_IS_HIGH_LEV_TRG) );
            /* Set RTS high level trigger */
            outpw(REG_UART0_MSR+uOffset, (inpw(REG_UART0_MSR+uOffset) | UART_CTS_IS_HIGH_LEV_TRG) );

            /* Set Auto CTS/RTS */
            outpw(REG_UART0_IER+uOffset, inpw(REG_UART0_IER+uOffset) | (0x3 << 12));

            /* Enable MODEM status interrupt */
            //_uartEnableInterrupt(nNum, UART_IER_MODEM_IEN_Msk);

            /*
                Maintain H/W flow control flag by read Modem Status Register.
                If CTS high, stop TX.
                If CTS low, start TX.
            */
            //if( inpw(REG_UART0_MSR+uOffset) & 0x10 )  /* CTS external signal is low  */
            //  _uart_cHWTXStopped = 0;       /* TX started                  */
            //else                              /* CTS external signal is high */
            //  _uart_cHWTXStopped = 1;       /* TX stopped                  */

            /* Set RTS as logic 0, RX re-start */
            //outpb(REG_UART0_MCR+uOffset, inpb(REG_UART0_MCR+uOffset) | 0x02);  /* set RTS signal low  */
            //_uart_cHWRXStopped = 0;  // RX started
            break;

        case UART_IOC_DISABLEHWFLOWCONTROL:

            /* Disable MODEM status interrupt */
            _uartDisableInterrupt(nNum, UART_IER_MODEM_IEN_Msk);
            _uart_cFlowControlMode = 0;
            _uart_cHWTXStopped = 0;
            _uart_cHWRXStopped = 0;
            break;

        case UART_IOC_FLUSH_TX_BUFFER:
            dev->uUartTxTail = 0;
            dev->uUartTxHead = 0;
            break;

        case UART_IOC_FLUSH_RX_BUFFER:
            dev->uUartRxTail = 0;
            dev->uUartRxHead = 0;
            break;

        case UART_IOC_SET_RS485_MODE:
            outpw((REG_UART0_FUN_SEL+uOffset), 0x3);
            outpw((REG_UART0_MCR+uOffset), 0x0);
            outpw((REG_UART0_LCR+uOffset), (UART_LCR_SPE_Msk | UART_LCR_EPE_Msk | UART_LCR_PBE_Msk | (0x3 << UART_LCR_WLS_Pos)));
            outpw((REG_UART0_ALT_CSR+uOffset), uArg0|(uArg1 << UART_ALT_CSR_ADDR_MATCH_Pos));
            break;

        case UART_IOC_SEND_RS485_ADDRESS:

            while(!((inpw(REG_UART0_FSR + uOffset)) & UART_FSR_TE_FLAG_Msk));
            uReg = inpw(REG_UART0_LCR + uOffset);
            outpw((REG_UART0_LCR+uOffset), (UART_LCR_SPE_Msk | UART_LCR_PBE_Msk | (0x3 << UART_LCR_WLS_Pos)));
            outpw((REG_UART0_THR+uOffset), uArg0);
            while(!((inpw(REG_UART0_FSR + uOffset)) & UART_FSR_TE_FLAG_Msk));

            outpw((REG_UART0_LCR+uOffset), uReg);

            break;

        case UART_IOC_SET_RS485_RXOFF:
            uReg = inpw(REG_UART0_FCR + uOffset);
            if(uArg0 == 1)
                uReg |= UART_FCR_RX_DIS_Msk;
            else
                uReg &= ~UART_FCR_RX_DIS_Msk;

            outpw((REG_UART0_FCR + uOffset), uReg);

            break;

        case UART_IOC_SET_ALTCTL_REG:

            outpw((REG_UART0_ALT_CSR + uOffset), uArg0);

            break;

        case UART_IOC_GET_ALTCTL_REG:

            *(PUINT32)uArg0 = inpw(REG_UART0_ALT_CSR + uOffset);

            break;

        case UART_IOC_SET_LIN_MODE:

            outpw((REG_UART0_FUN_SEL+uOffset), 0x1); // Select LIN function

            /* Select LIN function setting : Tx enable, Rx enable and break field length */
            uReg = inpw(REG_UART0_ALT_CSR + uOffset);
            uReg &= ~(UART_ALT_CSR_LIN_TX_EN_Msk | UART_ALT_CSR_LIN_RX_EN_Msk | UART_ALT_CSR_UA_LIN_BKFL_Msk);
            uReg |= (uArg0 | (uArg1 << UART_ALT_CSR_UA_LIN_BKFL_Pos));
            outpw((REG_UART0_ALT_CSR + uOffset), uReg);

            break;

        default:
            return UART_ENOTTY;
    }

    return Successful;
}

/**
  * @brief    Release memory resource, disable interrupt.
  *
  * @param[in]    nNum: UART channel. ( UART0 / UART1 / UART2 / UART3 / UART 4 /UART 5 /
  *                                     UART6 / UART7 / UART8 / UART9 / UARTA )
  *
  * @return   UART_ENODEV: UART channel out of range
  *           UART_EIO: No activated
  *           Successful: Success
  */
INT32 uartRelease(INT nNum)
{
    UART_BUFFER_T *dev;

    if((nNum < UART0) || (nNum > UARTA))
        return UART_ENODEV;

    dev = (UART_BUFFER_T *) &UART_DEV[nNum];

    /* Check UART initial status */
    if(dev->bIsUARTInitial == FALSE)
        return UART_EIO;

    /* Disable all interrupt of the specific UART */
    _uartDisableInterrupt(nNum, DISABLEALLIER);

    /* Free memory */
    free(dev->pucUartTxBuf);
    free(dev->pucUartRxBuf);
    free(dev->pucUARTFlag);

    /* Initial parameter */
    dev->bIsUARTInitial = FALSE;  /* it's important */

    return Successful;
}

/*@}*/ /* end of group N9H30_UART_EXPORTED_FUNCTIONS */

/*@}*/ /* end of group N9H30_UART_Driver */

/*@}*/ /* end of group N9H30_Device_Driver */



