/******************************************************************************
 * $Revision: 423 $
 * $Date:: 2017-04-07 16:03:30 +0900#$
 *****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************/
/** \file mfs_uart.c
 **
 ** A detailed description is available at
 ** @link UartGroup UART Module description @endlink
 **
 ** History:
 **   - 2014-06-04  0.01  HS  Initial version for Traveo
 **   - 2014-07-04  0.02  HS  Add interrupt and FIFO handling.
 **   - 2015-02-25  0.03  HS  Change the reception FIFO fill level
 **   - 2015-09-09  0.02  ST  Fixed interrupt enable/disable to use the bit band unit
 *****************************************************************************/

/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "common_include.h"
#include "mfs_uart.h"

/**
 *****************************************************************************
 ** \addtogroup UartGroup
 *****************************************************************************/
/*!@{ */

/*****************************************************************************/
/* Local pre-processor symbols/macros ('define')                             */
/*****************************************************************************/
#define UART_FIFO_MAX_VAL       (64)
#define UART_SSR_ERR_MASK       (0x38)
#define UART_SSR_RX_INT_MASK    (0x3C)
#define UART_BGR_MIN            (4)
#define UART_BFR_MAX            (0x7FFFu)


/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/


/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/

/**
 *****************************************************************************
 ** \brief UART module internal data,
 **        storing internal information for each enabled UART instance.
 *****************************************************************************/
typedef struct stc_mfs_hl_intern_data
{
    uart_tx_cb_func_ptr_t       pfnTransmitCbFunction;      /*!< Transmission complete callback function */
    uart_rx_cb_func_ptr_t       pfnReceiveCbFunction;       /*!< Receive callback function */
    stc_uart_buffer_t           stcRxBuffer;                /*!< Store location for receive Fifo buffer */
    stc_uart_buffer_t           stcTxBuffer;                /*!< Store location for transmission Fifo buffer */
    uint16_t                    u16RxCbFillLevel;           /*!< Unread counts of data buffer to call RX Callback function */
    boolean_t                   bFifoUsage;                 /*!< TRUE=use FIFO, FALSE=not use */
} stc_uart_intern_data_t;


/*****************************************************************************/
/* Local function prototypes ('static')                                      */
/*****************************************************************************/
static stc_uart_intern_data_t* MfsUartGetInternDataPtr(pstc_uart_type_t pstcUart);
static void MfsUartIrqHandlerRx(pstc_uart_type_t pstcUart,
                             stc_uart_intern_data_t *pstcUartInternData);
static void MfsUartIrqHandlerTx(pstc_uart_type_t pstcUart,
                             stc_uart_intern_data_t* pstcUartInternData);
static void MfsUartWriteBuf(pstc_uart_type_t pstcUart,
                         stc_uart_intern_data_t *pstcUartInternData);
static void MfsUartReadBuf(pstc_uart_type_t pstcUart,
                        stc_uart_intern_data_t *pstcUartInternData);
static void MfsUartFifoUseInit(pstc_uart_type_t pstcUart,
                                  uint16_t  u16RxCbFillLevel);
static void MfsUartFifoReset(pstc_uart_type_t pstcUart);


/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/


/*****************************************************************************/
/* Function implementation - global ('extern') and local ('static')          */
/*****************************************************************************/


/**
 *****************************************************************************
 ** \brief UART receive interrupt service routine.
 **
 ** This function is called on each Receive Interrupt set by the UART. It stores
 ** the data from the FIFO into the internal Ring Buffer an triggers a callback
 ** function if the specific Fill Level of the internal Ring Buffer is reached.
 *****************************************************************************/
static void MfsUartIrqHandlerRx(pstc_uart_type_t pstcUart,
                             stc_uart_intern_data_t *pstcUartInternData)
{
    un_cpg_mfsn_uart_ssr_t  unSSR;
    stc_uart_buffer_t       *pstcRxBuffer;

    /* Read status */
    unSSR.u8Register = pstcUart->unSSR.u8Register;

    /* Check if RX interrupt flags is set */
    if ((unSSR.u8Register & UART_SSR_RX_INT_MASK) == 0)
    {
        return;
    }

    /* If an error has occurred, error status is cleared by MfsUartReadBuf() */
    /* Store Received Data Register into buffer */
    MfsUartReadBuf(pstcUart, pstcUartInternData);

    /* Check if receive callback function is valid */
    if (pstcUartInternData->pfnReceiveCbFunction != NULL)
    {
        pstcRxBuffer = &pstcUartInternData->stcRxBuffer;
        if (pstcRxBuffer->u16FillCount >= pstcUartInternData->u16RxCbFillLevel)
        {
            /* Call receive callback function */
            pstcUartInternData->pfnReceiveCbFunction(pstcRxBuffer->u16FillCount);
        }
    }
}


/**
 *****************************************************************************
 ** \brief UART transmit interrupt service routine.
 **
 ** This function is called on each Transmit Interrupt set by the UART. This highly
 ** depends on the Configuration of the INT Sources. Interrupts might occur on
 ** TX FIFO empty, last bit has been shifted out, ...
 ** The data from the internal Ring Buffer is put into the FIFO until there's
 ** no more space left or all data has been transferred to the FIFO
 *****************************************************************************/
static void MfsUartIrqHandlerTx(pstc_uart_type_t pstcUart,
                             stc_uart_intern_data_t* pstcUartInternData)
{
    boolean_t bEnd = FALSE;
    stc_uart_buffer_t* pstcBuffer = &pstcUartInternData->stcTxBuffer;
    u8_uart_int_t*     pu8TxIntEnable;

    /* Check if TX interrupt flag is set, otherwise do nothing */
    if (pstcUart->unSSR.stcField.u1TDRE == 0)
    {
        return;
    }

    /* Write the contents of the buffer to Transmit Data Register */
    MfsUartWriteBuf(pstcUart, pstcUartInternData);
    if (pstcBuffer->u16FillCount == 0)
    {
        bEnd = TRUE;
    }
    if (bEnd != FALSE)
    {
        /* Get the pointer to TIE register(bit band unit) */
        pu8TxIntEnable = MfsUartGetTIE(pstcUart);

        /* If no more bytes to sent: Disable transmission interrupt(s) */
        /* Disable TX Interrupt */
        PDL_SAFELY_DISABLE_INTERRUPT(*pu8TxIntEnable, 0);

        if (pstcUartInternData->pfnTransmitCbFunction != NULL)
        {
            /* Call transmit callback function. */
            pstcUartInternData->pfnTransmitCbFunction();
        }
    }
}


/**
 ******************************************************************************
 ** \brief Write the contents of the buffer to Transmit Data Register.
 **
 ** The data from the internal Ring Buffer is put into the Fifo until there's
 ** no more space left or all data has been transferred to the Fifo.
 ** If the referenced MFS does not have a FIFO single data is put.
 **
 ** \param [in]       pstcMfs              Pointer to MFS instance register area
 ** \param [in,out]   pstcMfsHlInternData  Pointer to internal data
 **
 ******************************************************************************/
static void MfsUartWriteBuf(pstc_uart_type_t pstcUart,
                         stc_uart_intern_data_t *pstcUartInternData)
{
    stc_uart_buffer_t   *pstcBuffer;
    uint32_t            u16Data;
    uint_fast16_t       fu16Loop;

    pstcBuffer = &pstcUartInternData->stcTxBuffer;
    if (pstcUartInternData->bFifoUsage != FALSE)
    {
        /* Get number of Data to put into HW FIFO, whichever space is less */
        fu16Loop = (uint_fast16_t)MIN(pstcBuffer->u16FillCount,
                                      (uint16_t)(UART_FIFO_MAX_VAL - (pstcUart->unFBYTE.stcField.u8FBYTE1)));
    }
    else
    {
        fu16Loop = 1;
    }

    while (fu16Loop > 0u)
    {
        u16Data = (uint16_t)pstcBuffer->pu8Buffer[pstcBuffer->u16OutIndex];

        /* update tail */
        pstcBuffer->u16OutIndex = ((pstcBuffer->u16OutIndex + 1) % (pstcBuffer->u16BufferSize));
        pstcBuffer->u16FillCount--;
        fu16Loop--;

        /* Put data from Buffer into Transmit Data Register */
        /* Note: Transmit Data Register address is same as RDR */
        pstcUart->unRDR.u16Register = u16Data;
    }
}


/**
 ******************************************************************************
 ** \brief Store received data into buffer.
 **
 ** It stores the data from the FIFO into the internal Ring Buffer
 ** If the referenced MFS does not have a FIFO single data is read.
 **
 ** \param [in]       pstcUart              Pointer to Uart instance register area
 ** \param [in,out]   pstcUartInternData    Pointer to internal data
 ******************************************************************************/
static void MfsUartReadBuf(pstc_uart_type_t pstcUart,
                        stc_uart_intern_data_t *pstcUartInternData)
{
    stc_uart_buffer_t       *pstcRxBuffer;
    uint16_t                u16Data;
    un_cpg_mfsn_uart_ssr_t  unSSR;
    volatile uint16_t       u16Dummy;

    /* Pointer to the receive buffer   */
    pstcRxBuffer = &pstcUartInternData->stcRxBuffer;

    /* Use FIFO */
    if (pstcUartInternData->bFifoUsage != FALSE)
    {
        /* Now read all available bytes from hardware FIFO (if available) to buffer */
        while ((pstcUart->unFBYTE.stcField.u8FBYTE2 != 0u) &&
               ((pstcUart->unSSR.u8Register & UART_SSR_ERR_MASK) == 0))
        {
            /* Read reception data */
            u16Data = pstcUart->unRDR.u16Register;
            /* If there is empty space in RX buffer */
            if (pstcRxBuffer->u16FillCount < pstcRxBuffer->u16BufferSize)
            {
                /* Read data from RX FIFO, store in RX buffer */
                pstcRxBuffer->pu8Buffer[pstcRxBuffer->u16InIndex] = (uint8_t)u16Data;
                /* Increment in index */
                pstcRxBuffer->u16InIndex++;
                if (pstcRxBuffer->u16InIndex >= pstcRxBuffer->u16BufferSize)
                {
                    pstcRxBuffer->u16InIndex = 0u;
                }
                /* Count bytes in RX-FIFO */
                pstcRxBuffer->u16FillCount++;
            }
            else
            {
                /* We still need to do a dummy read to clear pending interrupts */
            }
        }
        /* If Error has occurred, Reset HW FIFO */
        if ((pstcUart->unSSR.u8Register & UART_SSR_ERR_MASK) != 0)
        {
            /* Clear possible reception errors */
            PDL_WRITE_REG_SYNC(pstcUart->unSSR.stcField.u1REC, 1);
            /* Reset FIFO */
            MfsUartFifoReset(pstcUart);
        }
    }
    /* Not use FIFO */
    else
    {
        /* Reception data exist? */
        if ((pstcUart->unSSR.stcField.u1RDRF != 0) &&
            ((pstcUart->unSSR.u8Register & UART_SSR_ERR_MASK) == 0))
        {
            /* Read reception data */
            u16Data = pstcUart->unRDR.u16Register;

            /* If there is empty space in RX buffer */
            if (pstcRxBuffer->u16FillCount < pstcRxBuffer->u16BufferSize)
            {
                /* Read data store in RX buffer */
                pstcRxBuffer->pu8Buffer[pstcRxBuffer->u16InIndex] = (uint8_t)u16Data;
                /* Increment in index */
                pstcRxBuffer->u16InIndex++;
                if (pstcRxBuffer->u16InIndex == pstcRxBuffer->u16BufferSize)
                {
                    pstcRxBuffer->u16InIndex = 0u;
                }
                /* Count bytes in RX-FIFO */
                pstcRxBuffer->u16FillCount++;
            }
            else
            {
                /* We still need to do a dummy read to clear pending interrupts */
            }
        }
        /* If Error has occurred, Clear error status */
        if ((pstcUart->unSSR.u8Register & UART_SSR_ERR_MASK) != 0)
        {
            /* Clear possible reception errors */
            PDL_WRITE_REG_SYNC(pstcUart->unSSR.stcField.u1REC, 1);
            /* Dummy read */
            u16Dummy = pstcUart->unRDR.u16Register;
        }
    }
}


/**
 ******************************************************************************
 ** \brief Uart Initialize FIFO to use
 **
 ** \param [in] pstcUart             Pointer to Uart instance register area
 ******************************************************************************/
static void MfsUartFifoUseInit(pstc_uart_type_t pstcUart,
                               uint16_t  u16RxCbFillLevel)
{
    un_cpg_mfsn_uart_fcr0_t unFCR0 = { 0 };
    un_cpg_mfsn_uart_fcr1_t unFCR1 = { 0 };
    un_cpg_mfsn_uart_fbyte_t unFBYTE = { 0 };
    un_cpg_mfsn_uart_fticr_t unFTICR = { 0 };

    /*
     *  Local Fifo Control Register 0 variable
     */
    unFCR0.stcField.u1FLD  = 0;    /* No reload (for transmission)   */
    unFCR0.stcField.u1FSET = 0;    /* FIFO2 read pointer not saved    */
    unFCR0.stcField.u1FCL2 = 1;    /* Reset FIFO2 */
    unFCR0.stcField.u1FCL1 = 1;    /* Reset FIFO1 */
    unFCR0.stcField.u1FE2  = 1;    /* Enable FIFO2 */
    unFCR0.stcField.u1FE1  = 1;    /* Enable FIFO1 */

    /*
     *  Local Fifo Control Register 1 variable
     */
    unFCR1.stcField.u1FLSTE = 0;   /* disable data lost detection */
    unFCR1.stcField.u1FRIIE = 0;   /* disable FIFO idle detection */
    unFCR1.stcField.u1FDRQ  = 0;   /* no request for transmission FIFO data */
    unFCR1.stcField.u1FTIE  = 0;   /* disable transmission FIFO interrupt */
    unFCR1.stcField.u1FSEL  = 0;   /* FIFO1: transmission FIFO, FIFO2: reception FIFO */

    /*
     *  Local Transmission Fifo interruption Control Register variable
     */
    unFTICR.stcField.u8FTICR1 = 0;  /* Trigger level = 0 */

    /*
     *  Local FIFO data bytes
     */
    /* FBYTE should not change when reception is enabled. */
    /* So, set to maximum value */
    if (u16RxCbFillLevel == 0)
    {
        unFBYTE.stcField.u8FBYTE2     = UART_FIFO_MAX_VAL;
    }
    else
    {
        unFBYTE.stcField.u8FBYTE2     = u16RxCbFillLevel;
    }

    /* Setup hardware */
    pstcUart->unFTICR.u16Register = unFTICR.u16Register;
    pstcUart->unFCR0.u8Register   = unFCR0.u8Register;
    pstcUart->unFCR1.u8Register   = unFCR1.u8Register;
    pstcUart->unFBYTE.u16Register = unFBYTE.u16Register;
}


/**
 ******************************************************************************
 ** \brief Uart reset FIFO (for error)
 **
 ** \param [in] pstcUart             Pointer to Uart instance register area
 ******************************************************************************/
static void MfsUartFifoReset(pstc_uart_type_t pstcUart)
{
    un_cpg_mfsn_uart_fcr0c_t unFCR0C = { 0 };
    un_cpg_mfsn_uart_fcr0s_t unFCR0S = { 0 };
    volatile uint16_t u16Dummy;

    /* Temp disable Rx */
    pstcUart->unSCR.stcField.u1RXE = 0;

    /* Disable reception FIFO */
    unFCR0C.stcField.u1FE2C = 1;
    pstcUart->unFCR0C.u8Register = unFCR0C.u8Register;

    /* Clear FIFO */
    unFCR0S.stcField.u1FCL2S = 1;
    pstcUart->unFCR0S.u8Register = unFCR0S.u8Register;
    unFCR0S.stcField.u1FCL2S = 0;

    /* Dummy read */
    u16Dummy = pstcUart->unRDR.u16Register;

    /* Re-enable RX and FIFO */
    unFCR0S.stcField.u1FE2S = 1;
    pstcUart->unFCR0S.u8Register = unFCR0S.u8Register;
    pstcUart->unSCR.stcField.u1RXE = 1;
}


/**
 *****************************************************************************
 ** \brief Initialization of a UART module.
 ** This Function initializes the  UART according the Options setup in the
 ** passed Config Struct. Several Checkings are done before that and an error
 ** is returned if invalid Modes are requested.
 **
 ** The required timing settings for data rate are calculated automatically
 ** with respect to the current peripheral clock.
 **
 ** \pre Peripheral Clock must be configured before to correctly set data rate.
 **      For UART asynchronous mode the peripheral clock must be at least
 **      4 times faster than the data rate to ensure proper oversampling.
 ** \pre The Application must configure corresponding UART pins (SIN, SOT)
 **       according to requirements and settings of UART instance!
 **
 ** \param [in]  pstcUart       Pointer to UART instance register area
 ** \param [in]  pstcConfig     UART module configuration. See #stc_uart_config_t.
 **
 ** \retval Ok                    Initializiation of UART module successfully done.
 ** \retval ErrorInvalidParameter If one of following conditions are met:
 **         - pstcUart == NULL
 **         - pstcConfig == NULL
 **         - pstcConfig->TxBuffer == NULL
 **         - pstcConfig->RxBuffer == NULL
 **         - pstcUartInternData == NULL (pstcUart is an invalid address)
 **            i.e. invalid or disabled UART unit (PDL_PERIPHERAL_ENABLE_UART)
 **         - u32ReloadValue == 0 (u32ReloadValue is calculated with: (SysCtrl_GetDistributedClockFrequencyHz(SysCtrlPeripheral0Clock) / pstcConfig->u32DataRate) - 1)
 **            i.E. data rate invalid (calculated timing with current peripheral clock is out of range)
 **         - pstcConfig->pfnRxCb != NULL & pstcConfig->u16RxCbFillLevel < 1
 *****************************************************************************/
en_result_t Mfs_Uart_Init(pstc_uart_type_t pstcUart,
                      const stc_uart_config_t* pstcConfig)
{
    stc_uart_intern_data_t* pstcUartInternData;
    uint32_t                u32ReloadValue;

    /* Preset local register variable to zero */
    un_cpg_mfsn_uart_scr_t   unSCR   = { 0 };
    un_cpg_mfsn_uart_smr_t   unSMR   = { 0 };
    un_cpg_mfsn_uart_escr_t  unESCR  = { 0 };
    un_cpg_mfsn_uart_ssr_t   unSSR   = { 0 };
    un_cpg_mfsn_uart_fcr0c_t unFCR0C = { 0 };
    un_cpg_mfsn_uart_bgr_t   unBGR   = { 0 };

    /* Check for valid pointers */
    if ((pstcUart  == NULL) ||
        (pstcConfig == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* Get ptr to internal data struct ... */
    pstcUartInternData = MfsUartGetInternDataPtr(pstcUart);
    /* ... and check */
    if (pstcUartInternData == NULL)
    {
        return ErrorInvalidParameter;
    }

    /* Buffer pointers are NULL */
    if ((pstcConfig->pu8TxBuf == NULL) ||
        (pstcConfig->pu8RxBuf == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* When rx callback is used, FillLevel must be greater than 1 */
    if((pstcConfig->pfnRxCb != NULL) &&
       (pstcConfig->u16RxCbFillLevel < 1))
    {
        return ErrorInvalidParameter;
    }

    /* Based on the UART we have to use different clocks for baudrate calculation */
    u32ReloadValue = SysCtrl_GetDistributedClockFrequencyHz(SysCtrlDistributedClockLCP0A);

    /* Check if data rate or Clock Frequency is 0 (div by zero not wanted) */
    if (pstcConfig->u32DataRate == 0)
    {
        return ErrorInvalidParameter;
    }
    /* Calc reload value based on data rate set */
    u32ReloadValue /= pstcConfig->u32DataRate;

    /* Check if reload value is 0 (to avoid underflow) */
    if (u32ReloadValue == 0)
    {
        return ErrorInvalidParameter;
    }
    u32ReloadValue -= 1;

    /* The reload value must be greater than or equal to 4 */
    if (u32ReloadValue < UART_BGR_MIN)
    {
        return ErrorInvalidParameter;
    }
    /* Maximum Reload value */
    else if (u32ReloadValue > UART_BFR_MAX)
    {
        return ErrorInvalidParameter;
    }
    else
    {
        /* valid */
    }

    /* First of all disable TX and RX for safe operation */
    pstcUart->unSCR.u8Register = 0;

    /* Then disable FIFOs */
    unFCR0C.stcField.u1FE1C = 1;
    unFCR0C.stcField.u1FE2C = 1;
    pstcUart->unFCR0C.u8Register = unFCR0C.u8Register;

    /* Next issue programmable clear */
    unSCR.stcField.u1UPCL = 1;
    pstcUart->unSCR.u8Register = unSCR.u8Register;
    /* Restore local variable for following settings */
    unSCR.stcField.u1UPCL = 0;

    /*
     * Serial Mode Register
     */
    /* Set Normal mode */
    unSMR.stcField.u3MD = 0;

    /* Bit order */
    if (pstcConfig->bTransferDirecton != TRUE)
    {
        /* LSB first */
        unSMR.stcField.u1BDS = 0;
    }
    else
    {
        /* MSB first */
        unSMR.stcField.u1BDS = 1;
    }

    /* Stop bit length */
    switch(pstcConfig->enStopBit)
    {
        case UartOneStopBit:
        case UartThreeStopBit:
            unSMR.stcField.u1SBL = 0;
            break;
        case UartTwoStopBit:
        case UartFourStopBit:
            unSMR.stcField.u1SBL = 1;
            break;
        default:
            return ErrorInvalidParameter;
    }

    /* Enable serial output */
    unSMR.stcField.u1SOE = 1;

    /* now setup hardware */
    pstcUart->unSMR.u8Register = unSMR.u8Register;

    /*
     * Extended Communication Control Register
     */
    /* Hardware flow control */
    if (pstcConfig->bHardwareFlow != TRUE)
    {
        /* disable hardware flow */
        unESCR.stcField.u1FLWEN =0;
    }
    else
    {
        /* enable hardware flow */
        unESCR.stcField.u1FLWEN =1;
    }

    /* Data format */
    unESCR.stcField.u1INV = pstcConfig->enDataFormat;

    /* Setup Parity (Effective only in normal mode)*/
    switch (pstcConfig->enParity)
    {
        case UartParityNone:
            unESCR.stcField.u1PEN  = 0; /* disable parity */
            unESCR.stcField.u1P    = 0; /* Even parity */
            break;
        case UartParityEven:
            unESCR.stcField.u1PEN  = 1; /* enable parity */
            unESCR.stcField.u1P    = 0; /* Even parity */
            break;
        case UartParityOdd:
            unESCR.stcField.u1PEN  = 1; /* enable parity */
            unESCR.stcField.u1P    = 1; /* Odd parity */
            break;
        default:
            return ErrorInvalidParameter;
    }

    /* Stop bit length */
    switch(pstcConfig->enStopBit)
    {
        case UartOneStopBit:
        case UartTwoStopBit:
            unESCR.stcField.u1ESBL = 0;
            break;
        case UartThreeStopBit:
        case UartFourStopBit:
            unESCR.stcField.u1ESBL = 1;
            break;
        default:
            return ErrorInvalidParameter;
    }

    /* Data length */
    switch (pstcConfig->enDataLength)
    {
        case UartLength5Bits:
            unESCR.stcField.u3L = 1;
            break;
        case UartLength6Bits:
            unESCR.stcField.u3L = 2;
            break;
        case UartLength7Bits:
            unESCR.stcField.u3L = 3;
            break;
        case UartLength8Bits:
            unESCR.stcField.u3L = 0;
            break;
        default:
            return ErrorInvalidParameter;
    }
    /* now setup hardware */
    pstcUart->unESCR.u8Register = unESCR.u8Register;

    /*
     * Serial Status Register
     */
    /* Clear Reception Error flags */
    unSSR.stcField.u1REC = 1;
    pstcUart->unSSR.u8Register = unSSR.u8Register;

    /*
     *  Local Fifo Control Register 0 variable
     */
    if (pstcConfig->bFifoUsage != FALSE)
    {
        /* Initialize FIFO to use */
        MfsUartFifoUseInit(pstcUart, pstcConfig->u16RxCbFillLevel);
    }
    /* Save FIFO usage */
    pstcUartInternData->bFifoUsage = pstcConfig->bFifoUsage;

    /* Set callback functions. */
    pstcUartInternData->pfnTransmitCbFunction = pstcConfig->pfnTxCb;
    pstcUartInternData->pfnReceiveCbFunction  = pstcConfig->pfnRxCb;

    /* Save RX Callback Buffer Fill Level */
    pstcUartInternData->u16RxCbFillLevel = pstcConfig->u16RxCbFillLevel;

    /* Initialize TX ring buffer */
    pstcUartInternData->stcTxBuffer.pu8Buffer     = pstcConfig->pu8TxBuf;
    pstcUartInternData->stcTxBuffer.u16BufferSize = pstcConfig->u16TxBufSize;
    pstcUartInternData->stcTxBuffer.u16InIndex    = 0u; /*  Reset Index */
    pstcUartInternData->stcTxBuffer.u16OutIndex   = 0u; /*  Reset Index */
    pstcUartInternData->stcTxBuffer.u16FillCount  = 0u; /*  Reset Counter */

    /* Initialize RX ring buffer */
    pstcUartInternData->stcRxBuffer.pu8Buffer     = pstcConfig->pu8RxBuf;
    pstcUartInternData->stcRxBuffer.u16BufferSize = pstcConfig->u16RxBufSize;
    pstcUartInternData->stcRxBuffer.u16InIndex    = 0u; /*  Reset Index */
    pstcUartInternData->stcRxBuffer.u16OutIndex   = 0u; /*  Reset Index */
    pstcUartInternData->stcRxBuffer.u16FillCount  = 0u; /*  Reset Counter */

    /* Set baud rate generation reload register */
    unBGR.u16Register |= (uint16_t)u32ReloadValue;
    pstcUart->unBGR.u16Register = unBGR.u16Register;
    /* Issue programmable clear to reload new value */
    unSCR.stcField.u1UPCL = 1;
    pstcUart->unSCR.u8Register = unSCR.u8Register;
    unSCR.stcField.u1UPCL = 0;

    /* Enable receiver and transmitter */
    unSCR.stcField.u1RXE = 1;       /* Enable receiver */
    unSCR.stcField.u1TXE = 1;       /* Enable transmitter */

    /* Use interruption */
    unSCR.stcField.u1RIE = 1;   /* Enable reception interrupt */
    pstcUart->unSCR.u8Register = unSCR.u8Register;

    return Ok;
}


/**
 ******************************************************************************
 ** \brief Write data to Uart synchronously or asynchronously
 **
 ** The data provided by Uart_Write#pu8Data is copied into the internal
 ** TX buffer and the transmission (via TX interrupt) is started.
 ** Depending on the Mfs_Uart_Write#bBlocking parameter, the function return behavior
 ** is different.
 **
 ** For an asynchronous (non-blocking) call (Mfs_Uart_Write#bBlocking = FALSE),
 ** the free size of the internal buffer must be sufficient to take all data
 ** (Mfs_Uart_Write#pu8Data) of length Mfs_Uart_Write#u16WriteCnt, otherwise
 ** the function will return ErrorBufferFull.
 ** After all data is copied into the internal buffer, the function will return
 ** immediately. The transmission may be pending when the function returns.
 **
 ** For a synchronous (blocking) call (Mfs_Uart_Write#bBlocking = TRUE),
 ** the function will wait until all data is transferred to the UART.
 ** The transmission  may be pending when the function returns.
 ** If the referenced UART does not have a FIFO, single data is written.
 **
 ** \param [in]  pstcUart      Pointer to Uart instance register area
 ** \param [in]  pu8Data       Transmit data buffer holding data to transmit
 ** \param [in]  u16WriteCnt   Number of characters to write, must be at least 1
 ** \param [in]  bBlocking     If TRUE, synchronously wait until all data is
 **                            transferred into the hardware buffer.
 **                            If FALSE, put data into internal TX buffer and return
 **                            immediately.
 ** \retval Ok                        Transmit data successfully done or started.
 ** \retval ErrorInvalidParameter     If one of following conditions are met:
 **             - pstcMfs == NULL
 **             - pu8Data == NULL
 **             - pstcUartInternData == NULL (invalid or disabled Uart unit
 **                                          (PDL_PERIPHERAL_ENABLE_UART))
 ** \retval ErrorBufferFull           Insufficient free size of TX buffer to
 **                                   take all data (in case of
 **                                   Mfs_Uart_Write#bBlocking = FALSE only)
 ******************************************************************************/
en_result_t Mfs_Uart_Write(pstc_uart_type_t pstcUart,
                       uint8_t  *pu8Data,
                       uint16_t  u16WriteCnt,
                       boolean_t bBlocking)
{
    stc_uart_intern_data_t *pstcUartInternData;
    stc_uart_buffer_t      *pstcBuffer;
    uint_fast16_t           fu16DataSent;
    uint_fast16_t           fu16Idx;
    u8_uart_int_t          *pu8TxIntEnable;

    /* Check for valid pointers and get pointer to internal data struct ... */
    pstcUartInternData = MfsUartGetInternDataPtr(pstcUart);
    /* ... and check */
    if ((pstcUartInternData == NULL) ||  (pu8Data == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* Check if nothing to do */
    if (u16WriteCnt == 0)
    {
        return Ok;
    }

    /* Get ptr to internal transmit Buffer */
    pstcBuffer = &pstcUartInternData->stcTxBuffer;

    /* Check if ring buffer can take all bytes (blocking only) */
    if ((bBlocking  == FALSE) &&
        (u16WriteCnt > (pstcBuffer->u16BufferSize - pstcBuffer->u16FillCount)))
    {
        /* not enough space left if non-blocking mode is requested */
        return ErrorBufferFull;
    }

    /* Loop until all data has been sent (blocking only) */
    /* If non-blocking mode is requested, it is guaranteed here that the */
    /* provided data will fit into the tx buffer */
    while (u16WriteCnt > 0)
    {
        /* Get the pointer to TIE register(bit band unit) */
        pu8TxIntEnable = MfsUartGetTIE(pstcUart);

        /* Disable transmission interrupt during copy (TIE) */
        PDL_SAFELY_DISABLE_INTERRUPT(*pu8TxIntEnable, 0);

        /* Copy data to provided destination buffer and save # bytes been read */
        /* determine free size in TX buffer */
        fu16DataSent = (uint_fast16_t)MIN(((pstcBuffer->u16BufferSize) - (pstcBuffer->u16FillCount)),
                                            u16WriteCnt);

        /* store bytes in TX buffer */
        for (fu16Idx = 0u; fu16Idx < fu16DataSent; fu16Idx++)
        {
            pstcBuffer->pu8Buffer[pstcBuffer->u16InIndex] = pu8Data[fu16Idx];

            /* Update in index */
            pstcBuffer->u16InIndex++;
            if (pstcBuffer->u16InIndex == pstcBuffer->u16BufferSize)
            {
                pstcBuffer->u16InIndex = 0; /* wrapped around */
            }
        }

        pstcBuffer->u16FillCount += (uint16_t)fu16DataSent;
        u16WriteCnt              -= (uint16_t)fu16DataSent;

        /* Now enable transmission interrupt to trigger send operation (TIE) */
        *pu8TxIntEnable = 1;

        /* None blocking mode */
        if (bBlocking  == FALSE)
        {
            break;
        }
        /* Blocking mode */
        else
        {
            /* Wait until all data has been transfered to the MFS HW FIFO (when blocking) */
            while (pstcBuffer->u16FillCount != 0)
            {
                PDL_WAIT_LOOP_HOOK();
            }
        }
    }

    return Ok;
}


/**
 ******************************************************************************
 ** \brief Read received data from UART module synchronously or asynchronously
 **
 ** The received data is copied from internal RX buffer
 ** into the provided data buffer Mfs_Uart_Read#pu8Data. The size is defined
 ** by Mfs_Uart_Read#pu16DataCnt. Depending on the Mfs_Uart_Read#bBlocking
 ** parameter, the function behavior is different.
 **
 ** For an asynchronous (non-blocking) call (Mfs_Uart_Read#bBlocking = FALSE),
 ** the function will return immediately after all currently available characters
 ** (in SW ring buffer and HW FIFO) are copied into the provided buffer
 ** (Mfs_Uart_Read#pu8Data) or the maximum count (Mfs_Uart_Read#u16ReadCnt)
 ** is reached. The value returned by Mfs_Uart_Read#pu16DataCnt gives the
 ** count of characters that was read actually.
 ** If the referenced UART does not have a FIFO, single data is read.
 **
 ** For a synchronous (blocking) call (Mfs_Uart_Read#bBlocking == TRUE),
 ** the function will return after the requested count of characters
 ** (Mfs_Uart_Read#pu16DataCnt) is received completely.
 ** This should be used with caution as the full application can get stuck
 ** if no further data is received.
 **
 ** \param [in]       pstcUart        Pointer to UART instance register area
 ** \param [in,out]   pu8Data         Buffer to store received data
 ** \param [in,out]   pu16DataCnt     Pointer to variable for number of bytes
 **                                   been read
 ** \param [in]       u16ReadCnt      Maximum number of characters to read
 **                                   (ensure sufficient size of Mfs_Uart_Read#pu8Data!)
 ** \param [in]       bBlocking       If TRUE, synchronously wait until
 **                                   Mfs_Uart_Read#u16ReadCnt bytes have been received.
 **                                   If FALSE, read all available and return immediately.
 **
 ** \retval Ok                        Read data successfully done or started.
 ** \retval ErrorInvalidParameter     If one of following conditions are met:
 **             - pstcMfs == NULL
 **             - pu8Data == NULL
 **             - pu16DataCnt == NULL
 **             - pstcUartInternData == NULL (invalid or disabled UART unit
 **                                   (PDL_PERIPHERAL_ENABLE_UART))
 ******************************************************************************/
en_result_t Mfs_Uart_Read(pstc_uart_type_t pstcUart,
                      uint8_t *pu8Data,
                      uint16_t *pu16DataCnt,
                      uint16_t u16ReadCnt,
                      boolean_t bBlocking)
{
    stc_uart_intern_data_t *pstcUartInternData;
    stc_uart_buffer_t      *pstcBuffer;
    uint_fast16_t           fu16Idx;
    uint_fast16_t           fu16Length;
    uint16_t                u16BytesToReadLeft;
    u8_uart_int_t          *pu8RxIntEnable;

    if (pu16DataCnt != NULL)
    {
        *pu16DataCnt = 0u;   /* Preset to default */
    }

    /* Check for valid pointers and get pointer to internal data struct ... */
    pstcUartInternData = MfsUartGetInternDataPtr(pstcUart);
    /* ... and check */
    if ((pstcUartInternData == NULL) ||
        (pu8Data == NULL)            ||
        (pu16DataCnt == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* Save Read Count for later use */
    u16BytesToReadLeft = u16ReadCnt;

    /* Check for nothing to do */
    if (u16ReadCnt == 0)
    {
        return Ok;
    }

    /* Get ptr to internal receive Buffer */
    pstcBuffer = &pstcUartInternData->stcRxBuffer;

    /* Read all available bytes from ring buffer, blocking. */
    while (u16BytesToReadLeft > 0)
    {
        /* Get the pointer to RIE register(bit band unit) */
        pu8RxIntEnable = MfsUartGetRIE(pstcUart);

        /* Disable reception interrupt */
        PDL_SAFELY_DISABLE_INTERRUPT(*pu8RxIntEnable, 0);

        /* Store Received Data Register into buffer */
        MfsUartReadBuf(pstcUart, pstcUartInternData);

        /* Copy data to destination buffer and save no. of bytes been read */
        /* get number of bytes to read */
        fu16Length = (uint_fast16_t)MIN(pstcBuffer->u16FillCount, u16BytesToReadLeft);

        /* if there are any bytes left to read */
        if (fu16Length  != 0)
        {
            /* read bytes out of RX buffer */
            for (fu16Idx = *pu16DataCnt; fu16Idx < (fu16Length + *pu16DataCnt); fu16Idx++)
            {
                pu8Data[fu16Idx] = pstcBuffer->pu8Buffer[pstcBuffer->u16OutIndex];
                /* Update out index */
                pstcBuffer->u16OutIndex++;
                if (pstcBuffer->u16OutIndex == pstcBuffer->u16BufferSize)
                {
                    pstcBuffer->u16OutIndex = 0u;
                }
            }
            pstcBuffer->u16FillCount -= (uint16_t)fu16Length; /* Update fill counter */
        }

        *pu16DataCnt       += (uint16_t)fu16Length; /* Provide no. of read to the caller */
        u16BytesToReadLeft -= (uint16_t)fu16Length; /* Some data processed */

        /* Re-enable reception interrupt (RIE) */
        *pu8RxIntEnable = 1;

        /* Not blocking read */
        if (bBlocking == FALSE)
        {
            break;
        }

        /* Call wait loop hook */
        PDL_WAIT_LOOP_HOOK();
    }

    return Ok;
}



/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
